feat(mailinabox): Major enhancement to mail server package v2.0.0

Complete rewrite of mailinaboxctl with comprehensive features:

Container Management:
- install, check, update, status, logs, shell commands
- Better prerequisite checking and Docker integration

Email Account Management:
- user-add/del/list/passwd for email accounts
- alias-add/del/list for email aliases
- Uses docker-mailserver setup command

Domain & SSL:
- domain-add/list for virtual domains
- ssl-status/renew for certificate management
- Let's Encrypt integration

Backup & Restore:
- Full backup with automatic container stop
- Restore with confirmation prompt

Diagnostics:
- health: comprehensive health check
- dns-check: verify MX, SPF, DMARC records
- ports: check listening ports
- config: show current configuration
- test-email: send test message

Updated configuration with:
- Separate hostname and domain options
- Feature flags for ClamAV, SpamAssassin, Fail2ban, POP3
- SSL type selection (letsencrypt, manual, self-signed)
- Complete port mapping options

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-22 06:12:23 +01:00
parent 13d7e8641f
commit b671843132
4 changed files with 668 additions and 131 deletions

View File

@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-mailinabox
PKG_RELEASE:=2
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_VERSION:=2.0.0
PKG_ARCH:=all
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
PKG_LICENSE:=CC0-1.0
@ -14,14 +14,26 @@ define Package/secubox-app-mailinabox
CATEGORY:=Utilities
PKGARCH:=all
SUBMENU:=SecuBox Apps
TITLE:=SecuBox Mail-in-a-Box docker app
TITLE:=SecuBox Mail Server (docker-mailserver)
DEPENDS:=+uci +libuci
endef
define Package/secubox-app-mailinabox/description
Installer, configuration, and service manager for running Mail-in-a-Box
inside Docker on SecuBox-powered OpenWrt systems. Complete email server
solution with webmail, calendar, contacts, spam filtering, and DNS.
Complete email server solution using docker-mailserver for SecuBox.
Features:
- Full email server (SMTP, IMAP, POP3)
- User account management (add/remove/list)
- Email aliases support
- SpamAssassin spam filtering
- ClamAV antivirus (optional)
- Fail2ban intrusion prevention
- Let's Encrypt SSL certificates
- Backup and restore functionality
- DNS configuration verification
- Health monitoring and diagnostics
Commands: mailinaboxctl --help
endef
define Package/secubox-app-mailinabox/conffiles

View File

@ -1,22 +1,30 @@
config mailinabox 'main'
option enabled '0'
option image 'docker-mailserver/docker-mailserver:latest'
option data_path '/srv/mailinabox'
option image 'ghcr.io/docker-mailserver/docker-mailserver:latest'
option data_path '/srv/mailserver'
# Domain configuration (MUST be configured before use)
option hostname 'mail.example.com'
option admin_email 'admin@example.com'
option domain 'example.com'
option timezone 'UTC'
# Port mappings
option smtp_port '25'
option dns_port '53'
option http_port '80'
option https_port '443'
option submission_port '587'
option submissions_port '465'
option imap_port '143'
option imaps_port '993'
option pop3_port '110'
option pop3s_port '995'
option sieve_port '4190'
# Feature flags
option enable_dns '1'
option enable_webmail '1'
option letsencrypt '1'
option enable_pop3 '0'
option enable_clamav '0'
option enable_spamassassin '1'
option enable_fail2ban '1'
# SSL configuration
# Options: letsencrypt, manual, self-signed
option ssl_type 'letsencrypt'
# Email for Let's Encrypt notifications (optional but recommended)
#option letsencrypt_email 'admin@example.com'

View File

@ -4,12 +4,26 @@ START=95
STOP=10
USE_PROCD=1
EXTRA_COMMANDS="status"
EXTRA_HELP=" status Show mail server status"
SERVICE_BIN="/usr/sbin/mailinaboxctl"
start_service() {
local enabled
config_load mailinabox
config_get enabled main enabled 0
[ "$enabled" != "1" ] && {
echo "Mail server is disabled. Enable with: uci set mailinabox.main.enabled=1"
return 0
}
procd_open_instance
procd_set_param command "$SERVICE_BIN" service-run
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
@ -19,5 +33,10 @@ stop_service() {
restart_service() {
stop_service
sleep 2
start_service
}
status() {
"$SERVICE_BIN" status
}

View File

@ -1,68 +1,116 @@
#!/bin/sh
# SecuBox Mail-in-a-Box manager
# SecuBox Mail-in-a-Box manager - Docker Mailserver Edition
# Copyright (C) 2024 CyberMind.fr
#
# Based on docker-mailserver for lightweight email hosting
CONFIG="mailinabox"
CONTAINER="secbx-mailinabox"
CONTAINER_NAME="secbx-mailserver"
OPKG_UPDATED=0
# Paths
DATA_BASE="/srv/mailserver"
usage() {
cat <<'USAGE'
cat <<'EOF'
Usage: mailinaboxctl <command>
Commands:
Container Management:
install Install prerequisites, prepare directories, pull image
check Run prerequisite checks
check Run prerequisite checks (ports, DNS, storage)
update Pull new image and restart
status Show container status
status Show container and service status
logs Show container logs (use -f to follow)
admin Open admin interface in browser (shows URL)
shell Open shell in container
service-run Internal: run container via procd
service-stop Stop container
Post-Installation:
1. Configure hostname and admin_email in /etc/config/mailinabox
2. Ensure proper DNS configuration (A, MX, SPF, DKIM, DMARC records)
3. Start with: /etc/init.d/mailinabox start
4. Access admin panel at https://your-hostname/admin
Email Account Management:
user-add <email> [password] Add email account
user-del <email> Remove email account
user-list List all email accounts
user-passwd <email> Change user password
alias-add <alias> <target> Add email alias
alias-del <alias> Remove email alias
alias-list List all aliases
Important Notes:
- Requires public IP and proper DNS configuration
- Port 25 must be open (some ISPs block it)
- Valid domain name required for SSL certificates
- Initial setup may take 10-15 minutes
USAGE
Domain & SSL:
domain-add <domain> Add email domain
domain-list List configured domains
ssl-status Show SSL certificate status
ssl-renew Force SSL certificate renewal
Backup & Restore:
backup [path] Backup mail data and config
restore <backup-file> Restore from backup
Diagnostics:
health Run health checks
dns-check [domain] Verify DNS records for domain
ports Check required ports
config Show current configuration
test-email <to> Send test email
Post-Installation:
1. Configure hostname and domain in /etc/config/mailinabox
2. Set proper DNS records (A, MX, SPF, DKIM, DMARC)
3. Start with: /etc/init.d/mailinabox start
4. Add users with: mailinaboxctl user-add admin@yourdomain.com
Required DNS Records:
A mail.domain.com -> your-public-ip
MX domain.com -> mail.domain.com (priority 10)
TXT domain.com -> "v=spf1 mx -all"
TXT _dmarc.domain.com -> "v=DMARC1; p=quarantine"
TXT mail._domainkey.domain.com -> (DKIM key from container)
EOF
}
require_root() { [ "$(id -u)" -eq 0 ]; }
require_root() { [ "$(id -u)" -eq 0 ] || { echo "Root required" >&2; exit 1; }; }
log_info() { echo "[INFO] $*"; }
log_warn() { echo "[WARN] $*" >&2; }
log_error() { echo "[ERROR] $*" >&2; }
uci_get() { uci -q get ${CONFIG}.main.$1; }
uci_set() { uci set ${CONFIG}.main.$1="$2" && uci commit ${CONFIG}; }
defaults() {
image="$(uci_get image || echo docker-mailserver/docker-mailserver:latest)"
data_path="$(uci_get data_path || echo /srv/mailinabox)"
# Load configuration with defaults
load_config() {
enabled="$(uci_get enabled || echo 0)"
image="$(uci_get image || echo ghcr.io/docker-mailserver/docker-mailserver:latest)"
data_path="$(uci_get data_path || echo /srv/mailserver)"
hostname="$(uci_get hostname || echo mail.example.com)"
admin_email="$(uci_get admin_email || echo admin@example.com)"
timezone="$(uci_get timezone || echo UTC)"
domain="$(uci_get domain || echo example.com)"
timezone="$(uci_get timezone || cat /etc/TZ 2>/dev/null || echo UTC)"
# Ports
smtp_port="$(uci_get smtp_port || echo 25)"
dns_port="$(uci_get dns_port || echo 53)"
http_port="$(uci_get http_port || echo 80)"
https_port="$(uci_get https_port || echo 443)"
submission_port="$(uci_get submission_port || echo 587)"
submissions_port="$(uci_get submissions_port || echo 465)"
imap_port="$(uci_get imap_port || echo 143)"
imaps_port="$(uci_get imaps_port || echo 993)"
pop3_port="$(uci_get pop3_port || echo 110)"
pop3s_port="$(uci_get pop3s_port || echo 995)"
sieve_port="$(uci_get sieve_port || echo 4190)"
enable_dns="$(uci_get enable_dns || echo 1)"
enable_webmail="$(uci_get enable_webmail || echo 1)"
letsencrypt="$(uci_get letsencrypt || echo 1)"
# Features
enable_pop3="$(uci_get enable_pop3 || echo 0)"
enable_clamav="$(uci_get enable_clamav || echo 0)"
enable_spamassassin="$(uci_get enable_spamassassin || echo 1)"
enable_fail2ban="$(uci_get enable_fail2ban || echo 1)"
ssl_type="$(uci_get ssl_type || echo letsencrypt)"
letsencrypt_email="$(uci_get letsencrypt_email)"
}
ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }
# =============================================================================
# Docker Functions
# =============================================================================
ensure_packages() {
for pkg in "$@"; do
if ! opkg status "$pkg" >/dev/null 2>&1; then
if ! opkg list-installed 2>/dev/null | grep -q "^$pkg "; then
if [ "$OPKG_UPDATED" -eq 0 ]; then
opkg update || return 1
OPKG_UPDATED=1
@ -72,135 +120,553 @@ ensure_packages() {
done
}
docker_ready() {
command -v docker >/dev/null 2>&1 && [ -S /var/run/docker.sock ]
}
check_prereqs() {
defaults
load_config
log_info "Checking prerequisites..."
# Check hostname configuration
if [ "$hostname" = "mail.example.com" ]; then
echo "[WARNING] Please configure hostname in /etc/config/mailinabox" >&2
echo "[WARNING] Mail-in-a-Box requires a valid domain name" >&2
if [ "$hostname" = "mail.example.com" ] || [ "$domain" = "example.com" ]; then
log_warn "Please configure hostname and domain in /etc/config/mailinabox"
log_warn "docker-mailserver requires a valid domain name"
fi
# Check cgroups
[ -d /sys/fs/cgroup ] || { log_error "/sys/fs/cgroup missing"; return 1; }
# Install Docker
ensure_packages dockerd docker containerd || return 1
# Enable and start Docker
/etc/init.d/dockerd enable >/dev/null 2>&1
if ! /etc/init.d/dockerd status >/dev/null 2>&1; then
/etc/init.d/dockerd start || return 1
sleep 3
fi
# Wait for Docker socket
local retry=0
while [ ! -S /var/run/docker.sock ] && [ $retry -lt 30 ]; do
sleep 1
retry=$((retry + 1))
done
[ -S /var/run/docker.sock ] || { log_error "Docker socket not available"; return 1; }
# Create data directories
ensure_dir "$data_path"
ensure_dir "$data_path/mail"
ensure_dir "$data_path/ssl"
ensure_dir "$data_path/data"
ensure_dir "$data_path/dns"
ensure_dir "$data_path/mail-data"
ensure_dir "$data_path/mail-state"
ensure_dir "$data_path/mail-logs"
ensure_dir "$data_path/config"
# Check system requirements
[ -d /sys/fs/cgroup ] || { echo "[ERROR] /sys/fs/cgroup missing" >&2; return 1; }
# Install Docker
ensure_packages dockerd docker containerd
/etc/init.d/dockerd enable >/dev/null 2>&1
/etc/init.d/dockerd start >/dev/null 2>&1
# Port conflict checks
if [ "$smtp_port" = "25" ]; then
if netstat -tln 2>/dev/null | grep -q ":25 "; then
echo "[WARNING] Port 25 already in use - potential conflict" >&2
fi
fi
if [ "$dns_port" = "53" ] && [ "$enable_dns" = "1" ]; then
if netstat -uln 2>/dev/null | grep -q ":53 "; then
echo "[WARNING] Port 53 already in use - DNS may conflict with dnsmasq" >&2
echo "[WARNING] Consider disabling Mail-in-a-Box DNS or moving dnsmasq" >&2
fi
fi
log_info "Docker ready, directories created"
return 0
}
pull_image() { defaults; docker pull "$image"; }
pull_image() {
load_config
log_info "Pulling Docker image: $image"
docker pull "$image"
}
stop_container() {
docker stop "$CONTAINER" >/dev/null 2>&1 || true
docker rm "$CONTAINER" >/dev/null 2>&1 || true
docker stop "$CONTAINER_NAME" >/dev/null 2>&1 || true
docker rm "$CONTAINER_NAME" >/dev/null 2>&1 || true
}
container_running() {
docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER_NAME}$"
}
# Execute setup.sh in container
docker_setup() {
if ! container_running; then
log_error "Container not running. Start with: /etc/init.d/mailinabox start"
return 1
fi
docker exec -it "$CONTAINER_NAME" setup "$@"
}
# =============================================================================
# User Management Commands
# =============================================================================
cmd_user_add() {
local email="$1"
local password="$2"
[ -z "$email" ] && { log_error "Usage: mailinaboxctl user-add <email> [password]"; return 1; }
if [ -n "$password" ]; then
docker_setup email add "$email" "$password"
else
docker_setup email add "$email"
fi
}
cmd_user_del() {
local email="$1"
[ -z "$email" ] && { log_error "Usage: mailinaboxctl user-del <email>"; return 1; }
docker_setup email del "$email"
}
cmd_user_list() {
docker_setup email list
}
cmd_user_passwd() {
local email="$1"
[ -z "$email" ] && { log_error "Usage: mailinaboxctl user-passwd <email>"; return 1; }
docker_setup email update "$email"
}
cmd_alias_add() {
local alias="$1"
local target="$2"
[ -z "$alias" ] || [ -z "$target" ] && { log_error "Usage: mailinaboxctl alias-add <alias> <target>"; return 1; }
docker_setup alias add "$alias" "$target"
}
cmd_alias_del() {
local alias="$1"
[ -z "$alias" ] && { log_error "Usage: mailinaboxctl alias-del <alias>"; return 1; }
docker_setup alias del "$alias"
}
cmd_alias_list() {
docker_setup alias list
}
# =============================================================================
# Domain & SSL Commands
# =============================================================================
cmd_domain_add() {
local domain="$1"
[ -z "$domain" ] && { log_error "Usage: mailinaboxctl domain-add <domain>"; return 1; }
# Create virtual domain entry
load_config
local vhost_file="$data_path/config/postfix-virtual.cf"
if ! grep -q "^$domain" "$vhost_file" 2>/dev/null; then
echo "$domain" >> "$vhost_file"
log_info "Domain $domain added. Restart service to apply."
else
log_warn "Domain $domain already exists"
fi
}
cmd_domain_list() {
load_config
log_info "Configured domains:"
if [ -f "$data_path/config/postfix-virtual.cf" ]; then
cat "$data_path/config/postfix-virtual.cf" | grep -v "^#" | grep -v "^$"
fi
echo ""
echo "Primary domain: $domain"
echo "Mail hostname: $hostname"
}
cmd_ssl_status() {
load_config
log_info "SSL Configuration:"
echo " Type: $ssl_type"
if container_running; then
docker_setup debug show-mail-logs | grep -i "ssl\|cert\|tls" | tail -20
fi
# Check certificate files
if [ -d "$data_path/config/ssl" ]; then
echo ""
echo "Certificate files:"
ls -la "$data_path/config/ssl/" 2>/dev/null || echo " No certificates found"
fi
}
cmd_ssl_renew() {
load_config
if [ "$ssl_type" = "letsencrypt" ]; then
log_info "Triggering Let's Encrypt renewal..."
if container_running; then
docker exec "$CONTAINER_NAME" certbot renew
else
log_error "Container not running"
fi
else
log_warn "SSL type is not letsencrypt"
fi
}
# =============================================================================
# Backup & Restore Commands
# =============================================================================
cmd_backup() {
require_root
load_config
local backup_path="${1:-/tmp}"
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="$backup_path/mailserver_backup_$timestamp.tar.gz"
log_info "Creating backup..."
# Stop container for consistent backup
local was_running=0
if container_running; then
was_running=1
log_info "Stopping container for backup..."
stop_container
fi
# Create backup
tar czf "$backup_file" -C "$(dirname $data_path)" "$(basename $data_path)" 2>/dev/null || {
log_error "Backup failed"
[ $was_running -eq 1 ] && /etc/init.d/mailinabox start
return 1
}
# Restart if was running
[ $was_running -eq 1 ] && /etc/init.d/mailinabox start
log_info "Backup created: $backup_file"
ls -lh "$backup_file"
}
cmd_restore() {
require_root
load_config
local backup_file="$1"
[ -z "$backup_file" ] || [ ! -f "$backup_file" ] && {
log_error "Usage: mailinaboxctl restore <backup-file>"
return 1
}
log_warn "This will OVERWRITE existing mail data!"
echo -n "Continue? [y/N] "
read answer
[ "$answer" != "y" ] && [ "$answer" != "Y" ] && { echo "Aborted"; return 1; }
# Stop container
if container_running; then
log_info "Stopping container..."
stop_container
fi
# Remove existing data
log_info "Removing existing data..."
rm -rf "$data_path"
# Restore
log_info "Restoring from backup..."
tar xzf "$backup_file" -C "$(dirname $data_path)" || {
log_error "Restore failed"
return 1
}
log_info "Restore complete. Start service with: /etc/init.d/mailinabox start"
}
# =============================================================================
# Diagnostic Commands
# =============================================================================
cmd_health() {
load_config
echo "=== Mail Server Health Check ==="
echo ""
# Container status
echo "Container Status:"
if container_running; then
echo " [OK] Container is running"
local uptime=$(docker inspect --format='{{.State.StartedAt}}' "$CONTAINER_NAME" 2>/dev/null)
echo " Started: $uptime"
else
echo " [FAIL] Container is not running"
fi
echo ""
# Port checks
echo "Port Status:"
for port in $smtp_port $submission_port $imaps_port; do
if netstat -tln 2>/dev/null | grep -q ":$port "; then
echo " [OK] Port $port is listening"
else
echo " [WARN] Port $port is not listening"
fi
done
echo ""
# Service checks inside container
if container_running; then
echo "Services Status:"
docker exec "$CONTAINER_NAME" supervisorctl status 2>/dev/null || echo " Unable to check services"
fi
echo ""
# Disk usage
echo "Disk Usage:"
if [ -d "$data_path" ]; then
du -sh "$data_path" 2>/dev/null
du -sh "$data_path"/* 2>/dev/null | head -10
fi
}
cmd_dns_check() {
load_config
local check_domain="${1:-$domain}"
echo "=== DNS Check for $check_domain ==="
echo ""
# A record
echo "A Record (mail.$check_domain):"
local a_record=$(nslookup "mail.$check_domain" 2>/dev/null | grep -A1 "Name:" | tail -1)
if [ -n "$a_record" ]; then
echo " [OK] $a_record"
else
echo " [FAIL] No A record found"
fi
echo ""
# MX record
echo "MX Record ($check_domain):"
local mx_record=$(nslookup -type=mx "$check_domain" 2>/dev/null | grep "mail exchanger")
if [ -n "$mx_record" ]; then
echo " [OK] $mx_record"
else
echo " [FAIL] No MX record found"
fi
echo ""
# SPF record
echo "SPF Record ($check_domain):"
local spf=$(nslookup -type=txt "$check_domain" 2>/dev/null | grep "v=spf1")
if [ -n "$spf" ]; then
echo " [OK] $spf"
else
echo " [WARN] No SPF record found"
echo " Recommended: \"v=spf1 mx -all\""
fi
echo ""
# DMARC record
echo "DMARC Record (_dmarc.$check_domain):"
local dmarc=$(nslookup -type=txt "_dmarc.$check_domain" 2>/dev/null | grep "v=DMARC1")
if [ -n "$dmarc" ]; then
echo " [OK] $dmarc"
else
echo " [WARN] No DMARC record found"
echo " Recommended: \"v=DMARC1; p=quarantine; rua=mailto:postmaster@$check_domain\""
fi
}
cmd_ports() {
load_config
echo "=== Port Status ==="
echo ""
echo "Required ports for mail server:"
echo ""
local ports="25:SMTP 587:Submission 465:SMTPS 143:IMAP 993:IMAPS"
[ "$enable_pop3" = "1" ] && ports="$ports 110:POP3 995:POP3S"
for entry in $ports; do
local port=$(echo "$entry" | cut -d: -f1)
local name=$(echo "$entry" | cut -d: -f2)
printf " %-6s %-12s " "$port" "$name"
if netstat -tln 2>/dev/null | grep -q ":$port "; then
echo "[LISTENING]"
else
echo "[NOT LISTENING]"
fi
done
echo ""
echo "Note: Port 25 may be blocked by some ISPs"
}
cmd_config() {
load_config
echo "=== Mail Server Configuration ==="
echo ""
echo "General:"
echo " Enabled: $enabled"
echo " Image: $image"
echo " Hostname: $hostname"
echo " Domain: $domain"
echo " Data path: $data_path"
echo " Timezone: $timezone"
echo ""
echo "Features:"
echo " SpamAssassin: $enable_spamassassin"
echo " ClamAV: $enable_clamav"
echo " Fail2ban: $enable_fail2ban"
echo " POP3: $enable_pop3"
echo " SSL Type: $ssl_type"
echo ""
echo "Ports:"
echo " SMTP: $smtp_port"
echo " Submission: $submission_port"
echo " IMAPS: $imaps_port"
}
cmd_test_email() {
local to="$1"
[ -z "$to" ] && { log_error "Usage: mailinaboxctl test-email <to-address>"; return 1; }
load_config
if ! container_running; then
log_error "Container not running"
return 1
fi
log_info "Sending test email to $to..."
docker exec "$CONTAINER_NAME" sh -c "echo 'Test email from SecuBox Mail Server' | mail -s 'Test from $hostname' $to"
log_info "Test email sent (check spam folder if not received)"
}
# =============================================================================
# Main Container Commands
# =============================================================================
cmd_install() {
require_root || { echo Root required >&2; exit 1; }
require_root
check_prereqs || exit 1
pull_image || exit 1
uci set ${CONFIG}.main.enabled='1'
uci_set enabled '1'
uci commit ${CONFIG}
/etc/init.d/mailinabox enable
load_config
echo ""
echo "Mail-in-a-Box prerequisites installed."
log_info "Mail server installed successfully!"
echo ""
echo "CRITICAL NEXT STEPS:"
echo " 1. Edit /etc/config/mailinabox and set:"
echo " - hostname (must be a valid FQDN)"
echo " - admin_email"
echo " 2. Configure DNS records for your domain:"
echo " - A record: $hostname -> your-public-ip"
echo " - MX record: @ -> $hostname"
echo " 3. Ensure port 25 is not blocked by your ISP"
echo " 4. Start with: /etc/init.d/mailinabox start"
echo " 5. Access admin at: https://$hostname/admin"
echo "NEXT STEPS:"
echo " 1. Edit /etc/config/mailinabox and configure:"
echo " - hostname (e.g., mail.yourdomain.com)"
echo " - domain (e.g., yourdomain.com)"
echo " 2. Set up DNS records (see 'mailinaboxctl dns-check')"
echo " 3. Start: /etc/init.d/mailinabox start"
echo " 4. Add first user: mailinaboxctl user-add admin@$domain"
echo ""
}
cmd_check() {
check_prereqs
echo "Prerequisite check completed."
echo ""
defaults
echo "Current configuration:"
echo " Hostname: $hostname"
echo " Admin email: $admin_email"
echo " Data path: $data_path"
echo " DNS enabled: $enable_dns"
cmd_config
echo ""
cmd_ports
}
cmd_update() {
require_root || { echo Root required >&2; exit 1; }
require_root
pull_image || exit 1
if container_running; then
/etc/init.d/mailinabox restart
else
log_info "Image updated. Start manually when ready."
fi
}
cmd_status() { docker ps -a --filter "name=$CONTAINER"; }
cmd_logs() { docker logs "$@" "$CONTAINER"; }
cmd_admin() {
defaults
echo "Admin interface: https://$hostname/admin"
cmd_status() {
echo "=== Container Status ==="
docker ps -a --filter "name=$CONTAINER_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
echo "If accessing locally, you may need to:"
echo " - Add '$hostname' to your hosts file"
echo " - Or use: https://$(uci get network.lan.ipaddr 2>/dev/null || echo 'router-ip')/admin"
if container_running; then
echo "=== Service Status ==="
docker exec "$CONTAINER_NAME" supervisorctl status 2>/dev/null || true
fi
}
cmd_logs() {
docker logs "$@" "$CONTAINER_NAME"
}
cmd_shell() {
if container_running; then
docker exec -it "$CONTAINER_NAME" /bin/bash
else
log_error "Container not running"
fi
}
cmd_service_run() {
require_root || { echo Root required >&2; exit 1; }
require_root
check_prereqs || exit 1
defaults
load_config
stop_container
local docker_args="--name $CONTAINER"
log_info "Starting mail server container..."
# Network mode: host for mail server functionality
docker_args="$docker_args --network host"
# Build docker run command
local docker_args="--name $CONTAINER_NAME"
# Volume mounts
docker_args="$docker_args -v $data_path/mail:/var/mail"
docker_args="$docker_args -v $data_path/ssl:/etc/ssl/mail"
docker_args="$docker_args -v $data_path/data:/var/mail-state"
docker_args="$docker_args -v $data_path/dns:/etc/bind"
# Hostname
docker_args="$docker_args --hostname $hostname"
docker_args="$docker_args --domainname $domain"
# Environment variables for docker-mailserver
# Ports
docker_args="$docker_args -p $smtp_port:25"
docker_args="$docker_args -p $submission_port:587"
docker_args="$docker_args -p $submissions_port:465"
docker_args="$docker_args -p $imap_port:143"
docker_args="$docker_args -p $imaps_port:993"
if [ "$enable_pop3" = "1" ]; then
docker_args="$docker_args -p $pop3_port:110"
docker_args="$docker_args -p $pop3s_port:995"
fi
# Volumes
docker_args="$docker_args -v $data_path/mail-data:/var/mail"
docker_args="$docker_args -v $data_path/mail-state:/var/mail-state"
docker_args="$docker_args -v $data_path/mail-logs:/var/log/mail"
docker_args="$docker_args -v $data_path/config:/tmp/docker-mailserver"
# Let's Encrypt volume if using certbot
if [ "$ssl_type" = "letsencrypt" ]; then
ensure_dir "$data_path/letsencrypt"
docker_args="$docker_args -v $data_path/letsencrypt:/etc/letsencrypt"
fi
# Environment variables
docker_args="$docker_args -e TZ=$timezone"
docker_args="$docker_args -e OVERRIDE_HOSTNAME=$hostname"
docker_args="$docker_args -e ENABLE_SPAMASSASSIN=1"
docker_args="$docker_args -e ENABLE_CLAMAV=1"
docker_args="$docker_args -e ENABLE_FAIL2BAN=1"
docker_args="$docker_args -e SSL_TYPE=letsencrypt"
docker_args="$docker_args -e ENABLE_SPAMASSASSIN=$enable_spamassassin"
docker_args="$docker_args -e ENABLE_CLAMAV=$enable_clamav"
docker_args="$docker_args -e ENABLE_FAIL2BAN=$enable_fail2ban"
docker_args="$docker_args -e ENABLE_POP3=$enable_pop3"
docker_args="$docker_args -e SSL_TYPE=$ssl_type"
docker_args="$docker_args -e PERMIT_DOCKER=network"
docker_args="$docker_args -e ONE_DIR=1"
docker_args="$docker_args -e POSTMASTER_ADDRESS=postmaster@$domain"
# Capabilities for mail server
if [ -n "$letsencrypt_email" ]; then
docker_args="$docker_args -e LETSENCRYPT_EMAIL=$letsencrypt_email"
fi
# Capabilities
docker_args="$docker_args --cap-add=NET_ADMIN"
docker_args="$docker_args --cap-add=NET_BIND_SERVICE"
docker_args="$docker_args --cap-add=SYS_PTRACE"
# Restart policy
docker_args="$docker_args --restart=unless-stopped"
@ -209,19 +675,51 @@ cmd_service_run() {
}
cmd_service_stop() {
require_root || { echo Root required >&2; exit 1; }
require_root
stop_container
}
# =============================================================================
# Main Entry Point
# =============================================================================
case "${1:-}" in
# Container management
install) shift; cmd_install "$@" ;;
check) shift; cmd_check "$@" ;;
update) shift; cmd_update "$@" ;;
status) shift; cmd_status "$@" ;;
logs) shift; cmd_logs "$@" ;;
admin) shift; cmd_admin "$@" ;;
shell) shift; cmd_shell "$@" ;;
service-run) shift; cmd_service_run "$@" ;;
service-stop) shift; cmd_service_stop "$@" ;;
# User management
user-add) shift; cmd_user_add "$@" ;;
user-del) shift; cmd_user_del "$@" ;;
user-list) shift; cmd_user_list "$@" ;;
user-passwd) shift; cmd_user_passwd "$@" ;;
alias-add) shift; cmd_alias_add "$@" ;;
alias-del) shift; cmd_alias_del "$@" ;;
alias-list) shift; cmd_alias_list "$@" ;;
# Domain & SSL
domain-add) shift; cmd_domain_add "$@" ;;
domain-list) shift; cmd_domain_list "$@" ;;
ssl-status) shift; cmd_ssl_status "$@" ;;
ssl-renew) shift; cmd_ssl_renew "$@" ;;
# Backup & restore
backup) shift; cmd_backup "$@" ;;
restore) shift; cmd_restore "$@" ;;
# Diagnostics
health) shift; cmd_health "$@" ;;
dns-check) shift; cmd_dns_check "$@" ;;
ports) shift; cmd_ports "$@" ;;
config) shift; cmd_config "$@" ;;
test-email) shift; cmd_test_email "$@" ;;
help|--help|-h|'') usage ;;
*) echo "Unknown command: $1" >&2; usage >&2; exit 1 ;;
esac