#!/bin/sh # SecuBox Mail Server Controller VERSION="1.0.0" CONFIG="mailserver" LIB_DIR="/usr/lib/mailserver" # Load libraries . "$LIB_DIR/container.sh" . "$LIB_DIR/users.sh" . "$LIB_DIR/mesh.sh" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${GREEN}[MAIL]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; } uci_get() { uci -q get ${CONFIG}.$1; } # ============================================================================ # Install / Setup # ============================================================================ cmd_install() { local container=$(uci_get main.container) container="${container:-mailserver}" local data_path=$(uci_get main.data_path) data_path="${data_path:-/srv/mailserver}" log "Installing mail server..." # Create container container_create "$container" "$data_path" local rc=$? [ $rc -ne 0 ] && return $rc # Start container log "Starting container..." lxc-start -n "$container" sleep 5 # Run setup log "Installing mail packages..." lxc-attach -n "$container" -- /root/setup.sh log "Installation complete!" echo "" echo "Next steps:" echo " 1. Configure domain: uci set mailserver.main.domain='yourdomain.com'" echo " 2. Set up DNS: dnsctl mail-setup" echo " 3. Add SSL cert: mailctl ssl-setup" echo " 4. Add users: mailctl user-add user@yourdomain.com" echo " 5. Enable: uci set mailserver.main.enabled=1 && uci commit" } cmd_uninstall() { local container=$(uci_get main.container) container="${container:-mailserver}" warn "This will remove the mail server container and all data!" echo "Continue? (yes/no)" read confirm [ "$confirm" != "yes" ] && { echo "Aborted"; return 1; } log "Stopping container..." lxc-stop -n "$container" 2>/dev/null log "Removing container..." rm -rf "/srv/lxc/$container" log "Mail server removed. Data in $(uci_get main.data_path) preserved." } # ============================================================================ # Service Control # ============================================================================ cmd_start() { /etc/init.d/mailserver start } cmd_stop() { /etc/init.d/mailserver stop } cmd_restart() { /etc/init.d/mailserver stop sleep 2 /etc/init.d/mailserver start } cmd_status() { local container=$(uci_get main.container) container="${container:-mailserver}" local domain=$(uci_get main.domain) local hostname=$(uci_get main.hostname) echo "" echo "========================================" echo " SecuBox Mail Server v$VERSION" echo "========================================" echo "" # Container status local state="stopped" lxc-info -n "$container" 2>/dev/null | grep -q "RUNNING" && state="running" echo " Container: $container ($state)" echo " Domain: ${domain:-not set}" echo " Hostname: ${hostname:-mail}.${domain:-example.com}" echo "" # Port status - check inside container echo " Ports:" local ports="25 587 465 993 995" local container_ports="" if [ "$state" = "running" ]; then container_ports=$(lxc-attach -n "$container" -- netstat -tln 2>/dev/null) fi for port in $ports; do if echo "$container_ports" | grep -q ":$port "; then echo -e " $port: ${GREEN}listening${NC}" else echo -e " $port: ${RED}closed${NC}" fi done echo "" # User count local data_path=$(uci_get main.data_path) data_path="${data_path:-/srv/mailserver}" local user_count=$(wc -l < "$data_path/config/users" 2>/dev/null || echo "0") echo " Users: $user_count" # Storage local storage=$(du -sh "$data_path" 2>/dev/null | awk '{print $1}') echo " Storage: ${storage:-0}" echo "" } # ============================================================================ # User Commands # ============================================================================ cmd_user() { local action="$1" shift case "$action" in add) user_add "$@" ;; del) user_del "$@" ;; list) user_list ;; passwd) user_passwd "$@" ;; *) echo "User commands:" echo " user add Add mail user" echo " user del Delete mail user" echo " user list List all users" echo " user passwd Change password" ;; esac } cmd_alias() { local action="$1" shift case "$action" in add) alias_add "$@" ;; list) alias_list ;; *) echo "Alias commands:" echo " alias add Add email alias" echo " alias list List aliases" ;; esac } # ============================================================================ # SSL Certificate # ============================================================================ cmd_ssl_setup() { local container=$(uci_get main.container) container="${container:-mailserver}" local domain=$(uci_get main.domain) local hostname=$(uci_get main.hostname) hostname="${hostname:-mail}" local fqdn="${hostname}.${domain}" log "Setting up SSL for $fqdn..." # Check if acme.sh available if ! command -v acme.sh >/dev/null 2>&1; then error "acme.sh not installed. Install with: opkg install acme acme-dnsapi" return 1 fi # Use DNS-01 via dnsctl log "Requesting certificate via DNS-01..." dnsctl acme-dns01 "$fqdn" local cert_dir="/root/.acme.sh/${fqdn}" local data_path=$(uci_get main.data_path) data_path="${data_path:-/srv/mailserver}" if [ -f "$cert_dir/fullchain.cer" ]; then cp "$cert_dir/fullchain.cer" "$data_path/ssl/fullchain.pem" cp "$cert_dir/${fqdn}.key" "$data_path/ssl/privkey.pem" chmod 600 "$data_path/ssl/"*.pem log "SSL certificates installed to $data_path/ssl/" log "Restarting mail services..." cmd_restart else error "Certificate generation failed" return 1 fi } cmd_ssl_status() { local data_path=$(uci_get main.data_path) data_path="${data_path:-/srv/mailserver}" if [ -f "$data_path/ssl/fullchain.pem" ]; then echo "SSL Certificate:" openssl x509 -in "$data_path/ssl/fullchain.pem" -noout -subject -dates 2>/dev/null | sed 's/^/ /' else echo "No SSL certificate installed" fi } # ============================================================================ # DNS Integration # ============================================================================ cmd_dns_setup() { local hostname=$(uci_get main.hostname) hostname="${hostname:-mail}" log "Setting up DNS records..." dnsctl mail-setup "$hostname" } # ============================================================================ # Mesh Commands # ============================================================================ cmd_mesh() { local action="$1" shift case "$action" in backup) mesh_backup ;; restore) mesh_restore "$@" ;; sync) mesh_sync "$@" ;; add-peer) mesh_add_peer "$@" ;; peers) mesh_list_peers ;; enable) uci set $CONFIG.mesh.enabled=1 uci commit $CONFIG log "Mesh sync enabled" ;; disable) uci set $CONFIG.mesh.enabled=0 uci commit $CONFIG log "Mesh sync disabled" ;; *) echo "Mesh commands:" echo " mesh backup Create backup for mesh" echo " mesh restore Restore from backup" echo " mesh sync [push|pull] Sync with peers" echo " mesh add-peer Add mesh peer" echo " mesh peers List mesh peers" echo " mesh enable Enable mesh sync" echo " mesh disable Disable mesh sync" ;; esac } # ============================================================================ # Fix / Repair # ============================================================================ cmd_fix_postfix() { local container=$(uci_get main.container) container="${container:-mailserver}" log "Fixing Postfix configuration..." # Fix LMDB maps (Alpine doesn't support hash maps) lxc-attach -n "$container" -- sh -c ' postconf -e "virtual_mailbox_maps = lmdb:/etc/postfix/vmailbox" postconf -e "virtual_alias_maps = lmdb:/etc/postfix/virtual" # Regenerate LMDB databases [ -f /etc/postfix/vmailbox ] && postmap lmdb:/etc/postfix/vmailbox [ -f /etc/postfix/virtual ] && postmap lmdb:/etc/postfix/virtual # Fix Postfix chroot DNS resolution mkdir -p /var/spool/postfix/etc cp /etc/resolv.conf /var/spool/postfix/etc/ # Reload Postfix postfix reload ' log "Postfix configuration fixed" log "If you still see 'Temporary lookup failure', restart the container: mailctl restart" } # ============================================================================ # Logs & Diagnostics # ============================================================================ cmd_logs() { local lines="${1:-50}" local container=$(uci_get main.container) container="${container:-mailserver}" lxc-attach -n "$container" -- tail -n "$lines" /var/log/mail.log 2>/dev/null } cmd_test() { local email="$1" [ -z "$email" ] && { echo "Usage: mailctl test "; return 1; } local container=$(uci_get main.container) container="${container:-mailserver}" local domain=$(uci_get main.domain) local hostname=$(uci_get main.hostname) log "Sending test email to $email..." lxc-attach -n "$container" -- sh -c "echo 'Test from SecuBox Mail Server' | mail -s 'Test Email' '$email'" log "Test email sent. Check inbox." } # ============================================================================ # Webmail Integration # ============================================================================ cmd_webmail() { local action="${1:-status}" case "$action" in status) local webmail=$(uci_get webmail.container) webmail="${webmail:-secubox-webmail}" if docker ps 2>/dev/null | grep -q "$webmail"; then local port=$(uci_get webmail.port) echo "Webmail: Running on port ${port:-8026}" else echo "Webmail: Not running" fi ;; configure) local domain=$(uci_get main.domain) local hostname=$(uci_get main.hostname) hostname="${hostname:-mail}" local fqdn="${hostname}.${domain}" log "Configuring webmail to connect via socat proxy..." local webmail=$(uci_get webmail.container) webmail="${webmail:-secubox-webmail}" # Docker containers reach host via gateway IP 172.17.0.1 # socat proxies IMAP on 10143 and SMTP on 10025 local docker_gateway="172.17.0.1" local imap_proxy_port="10143" local smtp_proxy_port="10025" # Update Roundcube config to use socat proxy (plaintext, no SSL) # SSL termination happens at HAProxy level for external access docker exec "$webmail" sh -c "cat > /var/www/html/config/config.docker.inc.php << EOF "$report_file" << EOF ======================================== SecuBox Mail Server Daily Report Date: $report_date Domain: ${domain:-not set} ======================================== === Server Status === $(cmd_status 2>/dev/null | grep -v '====') === Mail Queue === $(lxc-attach -n "$container" -- mailq 2>/dev/null | head -20) === Recent Mail Log (last 50 lines) === $(lxc-attach -n "$container" -- tail -50 /var/log/mail.log 2>/dev/null) === Storage Usage === $(du -sh /srv/mailserver/* 2>/dev/null) === Active Connections === $(lxc-attach -n "$container" -- netstat -tn 2>/dev/null | grep -E ':25|:143|:993|:587' | head -20) EOF echo "Report generated: $report_file" echo "$report_file" ;; send) local report_file=$(cmd_report generate | tail -1) if [ -f "$report_file" ]; then log "Sending daily report to $admin_email..." lxc-attach -n "$container" -- sh -c "cat '$report_file' | mail -s 'SecuBox Mail Daily Report' '$admin_email'" log "Report sent to $admin_email" else error "Failed to generate report" return 1 fi ;; enable) # Setup cron job for daily report local cron_file="/etc/cron.d/secubox-mail-report" cat > "$cron_file" << 'EOF' # SecuBox Mail Server Daily Report # Runs at 7:00 AM daily 0 7 * * * root /usr/sbin/mailctl report send >/dev/null 2>&1 EOF chmod 644 "$cron_file" log "Daily report enabled (7:00 AM)" ;; disable) rm -f /etc/cron.d/secubox-mail-report log "Daily report disabled" ;; *) echo "Report commands:" echo " report generate Generate report to file" echo " report send Generate and send to admin" echo " report enable Enable daily report (7 AM)" echo " report disable Disable daily report" ;; esac } # ============================================================================ # Help # ============================================================================ show_help() { cat << EOF SecuBox Mail Server v$VERSION Usage: mailctl [options] Setup: install Create and configure mail server uninstall Remove mail server dns-setup Set up MX/SPF/DKIM/DMARC via dnsctl ssl-setup Obtain SSL certificate Service: start Start mail server stop Stop mail server restart Restart mail server status Show server status Users: user add Add mail user user del Delete mail user user list List all users user passwd Change password Aliases: alias add Add email alias alias list List aliases Mesh Backup: mesh backup Create backup mesh restore Restore from backup mesh sync Sync with peers mesh peers List mesh peers Webmail: webmail status Show webmail status webmail configure Configure for this server Reports: report generate Generate status report report send Send report to admin report enable Enable daily report (7 AM) report disable Disable daily report Diagnostics: logs [lines] View mail logs test Send test email ssl-status Show SSL cert info fix-postfix Fix LMDB maps and DNS resolution Examples: mailctl install mailctl user add user@example.com mailctl dns-setup mailctl mesh sync push EOF } # ============================================================================ # Main # ============================================================================ case "${1:-}" in install) shift; cmd_install "$@" ;; uninstall) shift; cmd_uninstall "$@" ;; start) shift; cmd_start "$@" ;; stop) shift; cmd_stop "$@" ;; restart) shift; cmd_restart "$@" ;; status) shift; cmd_status "$@" ;; user) shift; cmd_user "$@" ;; alias) shift; cmd_alias "$@" ;; ssl-setup) shift; cmd_ssl_setup "$@" ;; ssl-status) shift; cmd_ssl_status "$@" ;; dns-setup) shift; cmd_dns_setup "$@" ;; mesh) shift; cmd_mesh "$@" ;; webmail) shift; cmd_webmail "$@" ;; logs) shift; cmd_logs "$@" ;; test) shift; cmd_test "$@" ;; report) shift; cmd_report "$@" ;; fix-postfix) shift; cmd_fix_postfix "$@" ;; help|--help|-h|'') show_help ;; *) error "Unknown command: $1"; show_help >&2; exit 1 ;; esac exit 0