secubox-openwrt/package/secubox/secubox-app-roundcube/files/usr/sbin/roundcubectl
CyberMind-FR 2b8fb1cd62 feat(apps): Convert Docker-based apps to LXC
Converted secubox-app-jellyfin, secubox-app-mailserver, and added
secubox-app-roundcube to use LXC containers instead of Docker.

Changes:
- jellyfinctl: Now uses LXC at 192.168.255.31
- mailserverctl: New controller for Alpine LXC with Postfix/Dovecot
- roundcubectl: New package for Roundcube webmail LXC

All controllers support:
- Bootstrap Alpine rootfs using static apk
- LXC configuration generation
- HAProxy integration with waf_bypass
- Start/stop/status commands

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 09:07:33 +01:00

438 lines
10 KiB
Bash

#!/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
<?php
\$config["db_dsnw"] = "sqlite:////var/www/roundcube/db/roundcube.db?mode=0640";
\$config["imap_host"] = "ssl://${mail_host}:993";
\$config["smtp_host"] = "ssl://${mail_host}:465";
\$config["imap_conn_options"] = [
"ssl" => ["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 <command>
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