243 lines
7.5 KiB
Bash
243 lines
7.5 KiB
Bash
#!/bin/bash
|
|
|
|
# ==========================================
|
|
# PROXMOX LXC MAILSERVER WRAPPER (FINAL)
|
|
# ==========================================
|
|
|
|
# --- CONFIGURATION ---
|
|
IP=${STATIC_IP:-"192.168.0.124/24"}
|
|
CONFIG_DIR="/tmp/docker-mailserver"
|
|
ACCOUNTS_FILE="$CONFIG_DIR/postfix-accounts.cf"
|
|
SECRETS_FILE="/mnt/secrets/mailserver.env"
|
|
|
|
echo "--- Mailserver Proxmox Init ---"
|
|
|
|
# 0. LOAD SECRETS
|
|
if [ -f "$SECRETS_FILE" ]; then
|
|
echo "🔐 Loading secrets from $SECRETS_FILE..."
|
|
set -a
|
|
source "$SECRETS_FILE"
|
|
set +a
|
|
|
|
# CRITICAL: Explicitly export SSL variables for the vendor script
|
|
export SSL_TYPE="${SSL_TYPE:-manual}"
|
|
export SSL_CERT_PATH="${SSL_CERT_PATH:-/tmp/docker-mailserver/ssl/cert.pem}"
|
|
export SSL_KEY_PATH="${SSL_KEY_PATH:-/tmp/docker-mailserver/ssl/key.pem}"
|
|
|
|
echo " -> SSL Mode: $SSL_TYPE"
|
|
else
|
|
echo "⚠️ Warning: Secrets file not found at $SECRETS_FILE"
|
|
fi
|
|
|
|
# 1. NETWORK SETUP
|
|
CURRENT_IP=$(ip -o -4 addr show eth0 | awk '{print $4}')
|
|
if [[ "$CURRENT_IP" == *"$IP"* ]]; then
|
|
echo "Network active ($CURRENT_IP). Skipping config."
|
|
else
|
|
echo "Configuring Network: $IP..."
|
|
cat <<EOF > /etc/network/interfaces
|
|
auto lo
|
|
iface lo inet loopback
|
|
|
|
auto eth0
|
|
iface eth0 inet static
|
|
address $IP
|
|
EOF
|
|
ifup eth0 || ip addr add $IP dev eth0 || true
|
|
fi
|
|
|
|
# 2. STATE & MIGRATION REPAIR
|
|
echo "--- Ensuring State Integrity ---"
|
|
rm -f /CONTAINER_START
|
|
rm -f /var/mail-state/container-id
|
|
|
|
migrate_to_state() {
|
|
NAME=$1
|
|
ORIGIN=$2
|
|
DEST="/var/mail-state/$NAME"
|
|
|
|
if [ ! -L "$ORIGIN" ]; then
|
|
echo "Migrating $NAME to persistent storage..."
|
|
mkdir -p "$DEST"
|
|
if [ -d "$ORIGIN" ]; then
|
|
cp -a "$ORIGIN/." "$DEST/"
|
|
rm -rf "$ORIGIN"
|
|
fi
|
|
ln -s "$DEST" "$ORIGIN"
|
|
fi
|
|
}
|
|
|
|
migrate_to_state "lib-amavis" "/var/lib/amavis"
|
|
migrate_to_state "lib-dovecot" "/var/lib/dovecot"
|
|
migrate_to_state "lib-logrotate" "/var/lib/logrotate"
|
|
migrate_to_state "lib-postfix" "/var/lib/postfix"
|
|
migrate_to_state "spool-postfix" "/var/spool/postfix"
|
|
|
|
# Fix Missing SSL (Snakeoil) for initial boot safety
|
|
if [ ! -f /etc/ssl/private/ssl-cert-snakeoil.key ]; then
|
|
echo "Generating default SSL certificate..."
|
|
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
|
|
-keyout /etc/ssl/private/ssl-cert-snakeoil.key \
|
|
-out /etc/ssl/certs/ssl-cert-snakeoil.pem \
|
|
-subj '/CN=localhost'
|
|
fi
|
|
|
|
# Clean stale sockets
|
|
rm -f /dev/shm/supervisor.sock
|
|
rm -rf /var/run/supervisor/*
|
|
mkdir -p /dev/shm
|
|
chmod 1777 /dev/shm
|
|
mkdir -p /var/run/supervisor /var/log/supervisor /var/run/saslauthd
|
|
chmod 755 /var/run/supervisor /var/log/supervisor
|
|
|
|
# 3. SUPERVISOR SHIM v2
|
|
if [ ! -f /usr/bin/supervisorctl_original ]; then
|
|
echo "--- Shiming supervisorctl ---"
|
|
mv /usr/bin/supervisorctl /usr/bin/supervisorctl_original
|
|
cat <<'EOF' > /usr/bin/supervisorctl
|
|
#!/bin/bash
|
|
if ! pgrep -x "supervisord" > /dev/null; then
|
|
echo "Shim: Supervisord died. Kickstarting..."
|
|
/usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
|
fi
|
|
for i in {1..10}; do
|
|
if [ -S /dev/shm/supervisor.sock ]; then break; fi
|
|
sleep 1
|
|
done
|
|
exec /usr/bin/supervisorctl_original "$@"
|
|
EOF
|
|
chmod +x /usr/bin/supervisorctl
|
|
fi
|
|
|
|
# 4. ACCOUNT CREATION (Admin + USERS_JSON)
|
|
if [ -f "$ACCOUNTS_FILE" ]; then
|
|
echo "✅ Accounts file exists. Skipping creation."
|
|
else
|
|
echo "⚠️ Accounts file missing. Initializing..."
|
|
mkdir -p "$CONFIG_DIR"
|
|
|
|
# --- A. Create Admin Account ---
|
|
if [[ -n "$DMS_ADMIN_EMAIL" ]] && [[ -n "$DMS_ADMIN_PASSWORD" ]]; then
|
|
echo "📝 Creating Admin: $DMS_ADMIN_EMAIL..."
|
|
HASH=$(doveadm pw -s SHA512-CRYPT -p "$DMS_ADMIN_PASSWORD")
|
|
echo "$DMS_ADMIN_EMAIL|$HASH" > "$ACCOUNTS_FILE"
|
|
else
|
|
echo "❌ No DMS_ADMIN_EMAIL found. Creating empty accounts file."
|
|
touch "$ACCOUNTS_FILE"
|
|
fi
|
|
|
|
# --- B. Process USERS_JSON ---
|
|
if [[ -n "${USERS_JSON:-}" ]]; then
|
|
echo "📋 Found USERS_JSON. Parsing..."
|
|
|
|
# Determine Domain (from Admin Email or default)
|
|
DOMAIN_SUFFIX=$(echo "$DMS_ADMIN_EMAIL" | cut -d'@' -f2)
|
|
if [[ -z "$DOMAIN_SUFFIX" ]]; then DOMAIN_SUFFIX="poppyglen.cc"; fi
|
|
|
|
# Loop through users and append to file
|
|
echo "$USERS_JSON" | jq -c '.[]' | while read -r user_entry; do
|
|
U=$(echo "$user_entry" | jq -r '.username')
|
|
P=$(echo "$user_entry" | jq -r '.password')
|
|
|
|
if [[ -n "$U" && -n "$P" ]]; then
|
|
# Hash using local doveadm
|
|
UHASH=$(doveadm pw -s SHA512-CRYPT -p "$P")
|
|
|
|
# Check if username already has @domain, if not add it
|
|
if [[ "$U" != *"@"* ]]; then
|
|
FULL_USER="$U@$DOMAIN_SUFFIX"
|
|
else
|
|
FULL_USER="$U"
|
|
fi
|
|
|
|
echo "$FULL_USER|$UHASH" >> "$ACCOUNTS_FILE"
|
|
echo " -> Added User: $FULL_USER"
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
|
|
# 5. NEW: PRE-FLIGHT FIXES (Permissions & Auth)
|
|
echo "🔧 Applying Security & Auth Patches..."
|
|
|
|
# A. Fix Certificate Permissions
|
|
# Ensures the 'dovecot' user (non-root in LXC) can read the host-mounted keys
|
|
if [ -f "$SSL_KEY_PATH" ]; then
|
|
chmod 644 "$SSL_KEY_PATH"
|
|
chmod 644 "$SSL_CERT_PATH"
|
|
echo " -> Fixed SSL certificate permissions (644)"
|
|
fi
|
|
|
|
# B. Configure SASL Auth Socket
|
|
# Creates the missing link allowing Postfix to ask Dovecot for password verification
|
|
echo " -> Creating SASL Auth Socket config..."
|
|
cat > /etc/dovecot/conf.d/99-postfix-auth.conf <<EOF
|
|
service auth {
|
|
unix_listener /var/spool/postfix/private/auth {
|
|
mode = 0660
|
|
user = postfix
|
|
group = postfix
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# 6. START THE SERVER (Backgrounded)
|
|
echo "🚀 Starting Docker Mailserver..."
|
|
|
|
# Run in background (&) so script continues to step 7
|
|
if [ $# -eq 0 ]; then
|
|
/usr/local/bin/start-mailserver.sh supervisord -c /etc/supervisor/supervisord.conf &
|
|
else
|
|
/usr/local/bin/start-mailserver.sh "$@" &
|
|
fi
|
|
PID=$!
|
|
|
|
# 7. POST-START CONFIG ENFORCEMENT
|
|
echo "⏳ Waiting 15s for config generation..."
|
|
sleep 15
|
|
|
|
echo "🔧 Enforcing Postfix SASL & Dovecot SSL..."
|
|
|
|
# --- A. Force Dovecot SSL Required ---
|
|
if [ -f /etc/dovecot/conf.d/10-ssl.conf ]; then
|
|
sed -i 's/^ssl =.*/ssl = required/' /etc/dovecot/conf.d/10-ssl.conf
|
|
fi
|
|
|
|
# --- B. Configure PHONE Authentication (Incoming) ---
|
|
# This allows your phone to log in to port 587/465
|
|
echo " -> Enabling Phone Authentication (smtpd)..."
|
|
postconf -e 'smtpd_sasl_type = dovecot'
|
|
postconf -e 'smtpd_sasl_path = private/auth'
|
|
postconf -e 'smtpd_sasl_auth_enable = yes' # <--- THIS IS THE FIX
|
|
postconf -e 'smtpd_tls_auth_only = yes'
|
|
|
|
# --- C. Configure BREVO Authentication (Outgoing) ---
|
|
# This allows the server to log in to Brevo to send mail
|
|
if [[ -n "$RELAY_HOST" ]]; then
|
|
echo "🔗 Configuring SMTP Relay: $RELAY_HOST..."
|
|
|
|
# Configure Relay Host
|
|
postconf -e "relayhost = [$RELAY_HOST]:$RELAY_PORT"
|
|
|
|
# Configure Relay Auth
|
|
if [[ -n "$RELAY_USER" ]] && [[ -n "$RELAY_PASSWORD" ]]; then
|
|
echo " -> Setting Relay Authentication..."
|
|
|
|
# Create password file
|
|
echo "[$RELAY_HOST]:$RELAY_PORT $RELAY_USER:$RELAY_PASSWORD" > /etc/postfix/sasl_passwd
|
|
chmod 600 /etc/postfix/sasl_passwd
|
|
postmap /etc/postfix/sasl_passwd
|
|
|
|
# Enable Client Auth
|
|
postconf -e 'smtp_sasl_auth_enable = yes'
|
|
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
|
|
postconf -e 'smtp_sasl_security_options = noanonymous'
|
|
fi
|
|
fi
|
|
|
|
echo "🔄 Reloading services..."
|
|
supervisorctl restart dovecot
|
|
supervisorctl restart postfix
|
|
|
|
wait $PID
|