#!/bin/sh # SecuBox Roundcube Webmail Controller # LXC-based nginx + PHP-FPM + Roundcube VERSION="1.0.0" CONFIG="roundcube" CONTAINER="roundcube" LXC_PATH="/srv/lxc/roundcube" ROUNDCUBE_VERSION="1.6.12" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${GREEN}[ROUNDCUBE]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; } # ============================================================================ # Configuration # ============================================================================ uci_get() { uci -q get ${CONFIG}.$1; } uci_set() { uci -q set ${CONFIG}.$1="$2"; } require_root() { [ "$(id -u)" -eq 0 ] || { error "Root required"; exit 1; } } defaults() { port="$(uci_get main.port)" [ -z "$port" ] && port="8027" mail_host="$(uci_get main.mail_host)" [ -z "$mail_host" ] && mail_host="192.168.255.30" domain="$(uci_get main.domain)" [ -z "$domain" ] && domain="webmail.gk2.secubox.in" } ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; } # ============================================================================ # LXC Helpers # ============================================================================ lxc_running() { lxc-info -n "$CONTAINER" 2>/dev/null | grep -q "State:.*RUNNING" } lxc_exists() { [ -d "$LXC_PATH/rootfs" ] } create_lxc_config() { defaults cat > "$LXC_PATH/config" << EOF lxc.uts.name = roundcube lxc.rootfs.path = dir:${LXC_PATH}/rootfs lxc.net.0.type = none lxc.mount.auto = proc:mixed sys:ro cgroup:mixed lxc.cap.drop = sys_module mac_admin mac_override sys_time lxc.seccomp.profile = lxc.tty.max = 0 lxc.pty.max = 256 lxc.cgroup2.memory.max = 128000000 lxc.init.cmd = /opt/start-roundcube.sh EOF } create_startup_script() { cat > "$LXC_PATH/rootfs/opt/start-roundcube.sh" << 'EOF' #!/bin/sh set -e # Initialize SQLite database if not exists if [ ! -f /var/www/roundcube/db/roundcube.db ]; then echo "Initializing Roundcube database..." mkdir -p /var/www/roundcube/db cd /var/www/roundcube sqlite3 db/roundcube.db < SQL/sqlite.initial.sql chown nginx:nginx db/roundcube.db chmod 640 db/roundcube.db echo "Database initialized" fi # Start PHP-FPM echo "Starting PHP-FPM..." php-fpm84 -D # Start nginx in foreground echo "Starting nginx..." exec nginx -g "daemon off;" EOF chmod +x "$LXC_PATH/rootfs/opt/start-roundcube.sh" } # ============================================================================ # Installation # ============================================================================ bootstrap_alpine() { require_root log "Bootstrapping Alpine Linux rootfs..." ensure_dir "$LXC_PATH" cd "$LXC_PATH" if [ ! -f sbin/apk.static ]; then log "Downloading apk-tools-static..." curl -L -o apk-tools-static.apk \ "https://dl-cdn.alpinelinux.org/alpine/v3.21/main/aarch64/apk-tools-static-2.14.6-r3.apk" tar -xzf apk-tools-static.apk sbin/apk.static rm -f apk-tools-static.apk fi log "Installing base system..." ./sbin/apk.static -X https://dl-cdn.alpinelinux.org/alpine/v3.21/main \ -U --allow-untrusted --root rootfs --initdb add \ alpine-base alpine-baselayout busybox musl mkdir -p rootfs/etc/apk cat > rootfs/etc/apk/repositories << 'EOF' https://dl-cdn.alpinelinux.org/alpine/v3.21/main https://dl-cdn.alpinelinux.org/alpine/v3.21/community EOF cat > rootfs/etc/resolv.conf << 'EOF' nameserver 8.8.8.8 nameserver 1.1.1.1 EOF log "Base system installed" } install_packages() { require_root if ! lxc_running; then log "Starting container for package installation..." lxc-start -n "$CONTAINER" -d sleep 3 fi log "Installing packages..." lxc-attach -n "$CONTAINER" -- apk update lxc-attach -n "$CONTAINER" -- apk add --no-cache \ nginx \ php84 php84-fpm php84-imap php84-mbstring php84-openssl \ php84-session php84-pdo php84-pdo_sqlite php84-sqlite3 \ php84-xml php84-dom php84-intl php84-zip php84-gd \ php84-ctype php84-json php84-fileinfo php84-ldap \ sqlite curl log "Packages installed" } download_roundcube() { local rootfs="$LXC_PATH/rootfs" log "Downloading Roundcube $ROUNDCUBE_VERSION..." mkdir -p "$rootfs/var/www" curl -L -o "$rootfs/tmp/roundcube.tar.gz" \ "https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBE_VERSION}/roundcubemail-${ROUNDCUBE_VERSION}-complete.tar.gz" tar -xzf "$rootfs/tmp/roundcube.tar.gz" -C "$rootfs/var/www/" mv "$rootfs/var/www/roundcubemail-${ROUNDCUBE_VERSION}" "$rootfs/var/www/roundcube" rm -f "$rootfs/tmp/roundcube.tar.gz" log "Roundcube downloaded" } configure_roundcube() { defaults local rootfs="$LXC_PATH/rootfs" log "Configuring Roundcube..." cat > "$rootfs/var/www/roundcube/config/config.inc.php" << EOF ["verify_peer" => false, "verify_peer_name" => false] ]; \$config["smtp_conn_options"] = [ "ssl" => ["verify_peer" => false, "verify_peer_name" => false] ]; \$config["support_url"] = ""; \$config["product_name"] = "SecuBox Webmail"; \$config["des_key"] = "rcmail-!24ByteDESKey*Sym"; \$config["plugins"] = ["archive", "zipdownload"]; \$config["skin"] = "elastic"; \$config["language"] = "fr_FR"; EOF # Configure nginx cat > "$rootfs/etc/nginx/http.d/roundcube.conf" << EOF server { listen ${port}; server_name _; root /var/www/roundcube; index index.php; location / { try_files \$uri \$uri/ /index.php?\$args; } location ~ \.php\$ { fastcgi_pass unix:/run/php-fpm.sock; fastcgi_index index.php; include fastcgi.conf; } location ~ /\. { deny all; } } EOF # Configure PHP-FPM mkdir -p "$rootfs/etc/php84/php-fpm.d" cat > "$rootfs/etc/php84/php-fpm.d/www.conf" << 'EOF' [www] user = nginx group = nginx listen = /run/php-fpm.sock listen.owner = nginx listen.group = nginx pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 EOF # Set permissions lxc-attach -n "$CONTAINER" -- chown -R nginx:nginx /var/www/roundcube 2>/dev/null || true log "Roundcube configured" } configure_haproxy() { defaults if ! command -v haproxyctl >/dev/null 2>&1; then warn "haproxyctl not found" return 0 fi local vhost_name=$(echo "$domain" | tr '.' '_') if ! uci -q get haproxy.roundcube >/dev/null 2>&1; then log "Creating HAProxy backend..." uci set haproxy.roundcube=backend uci set haproxy.roundcube.name='roundcube' uci set haproxy.roundcube.mode='http' uci set haproxy.roundcube.balance='roundrobin' uci set haproxy.roundcube.enabled='1' uci set haproxy.roundcube.option='forwardfor' uci add_list haproxy.roundcube.http_request='set-header X-Forwarded-Proto https' uci add_list haproxy.roundcube.http_request='set-header X-Real-IP %[src]' uci set haproxy.roundcube_srv=server uci set haproxy.roundcube_srv.backend='roundcube' uci set haproxy.roundcube_srv.name='roundcube' uci set haproxy.roundcube_srv.address='192.168.255.1' uci set haproxy.roundcube_srv.port="$port" uci set haproxy.roundcube_srv.weight='100' uci set haproxy.roundcube_srv.check='1' uci set haproxy.roundcube_srv.enabled='1' fi if ! uci -q get haproxy.${vhost_name} >/dev/null 2>&1; then log "Creating HAProxy vhost for $domain..." uci set haproxy.${vhost_name}=vhost uci set haproxy.${vhost_name}.domain="$domain" uci set haproxy.${vhost_name}.backend='roundcube' uci set haproxy.${vhost_name}.ssl='1' uci set haproxy.${vhost_name}.ssl_redirect='1' uci set haproxy.${vhost_name}.acme='1' uci set haproxy.${vhost_name}.waf_bypass='1' uci set haproxy.${vhost_name}.enabled='1' fi uci commit haproxy haproxyctl generate 2>/dev/null haproxyctl reload 2>/dev/null log "HAProxy configured for $domain" } cmd_install() { require_root log "Installing Roundcube LXC..." defaults if ! lxc_exists; then bootstrap_alpine fi create_lxc_config create_startup_script lxc-start -n "$CONTAINER" -d sleep 3 if lxc_running; then install_packages download_roundcube configure_roundcube lxc-stop -n "$CONTAINER" else error "Failed to start container" return 1 fi configure_haproxy uci_set main.enabled '1' uci commit ${CONFIG} log "Roundcube installed!" log "Start with: roundcubectl start" } cmd_start() { require_root if lxc_running; then log "Roundcube already running" return 0 fi if ! lxc_exists; then error "Roundcube not installed" return 1 fi defaults create_lxc_config log "Starting Roundcube LXC..." lxc-start -n "$CONTAINER" -d sleep 3 if lxc_running; then log "Roundcube started on port $port" else error "Failed to start Roundcube" return 1 fi } cmd_stop() { require_root if ! lxc_running; then log "Roundcube is not running" return 0 fi log "Stopping Roundcube..." lxc-stop -n "$CONTAINER" log "Roundcube stopped" } cmd_restart() { cmd_stop sleep 2 cmd_start } cmd_status() { defaults echo "" echo "========================================" echo " SecuBox Roundcube v$VERSION (LXC)" echo "========================================" echo "" echo "Configuration:" echo " Port: $port" echo " Mail Host: $mail_host" echo " Domain: $domain" echo "" echo "Container:" if lxc_running; then echo -e " Status: ${GREEN}Running${NC}" local test=$(curl -sI "http://127.0.0.1:$port/" 2>/dev/null | head -1) if echo "$test" | grep -q "200"; then echo -e " Web: ${GREEN}OK${NC}" else echo -e " Web: ${YELLOW}Starting...${NC}" fi elif lxc_exists; then echo -e " Status: ${YELLOW}Stopped${NC}" else echo -e " Status: ${RED}Not installed${NC}" fi echo "" } show_help() { cat << EOF SecuBox Roundcube Webmail v$VERSION (LXC) Usage: roundcubectl Commands: install Install LXC container start Start Roundcube stop Stop Roundcube restart Restart Roundcube status Show status EOF } case "${1:-}" in install) shift; cmd_install "$@" ;; start) shift; cmd_start "$@" ;; stop) shift; cmd_stop "$@" ;; restart) shift; cmd_restart "$@" ;; status) shift; cmd_status "$@" ;; service-run) cmd_start ;; service-stop) cmd_stop ;; help|--help|-h|'') show_help ;; *) error "Unknown command: $1"; show_help >&2; exit 1 ;; esac exit 0