Security Hardening¶
Advanced security hardening for Mailborder deployments.
System Hardening¶
Operating System Security¶
Update system packages:
Enable automatic security updates:
Configure automatic updates:
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESM:${distro_codename}-infra-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
Disable unnecessary services:
# List enabled services
systemctl list-unit-files --state=enabled
# Disable unnecessary ones
sudo systemctl disable bluetooth
sudo systemctl disable cups
sudo systemctl disable avahi-daemon
Remove unnecessary packages:
Firewall Configuration¶
Install and configure UFW:
sudo apt install ufw
# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (configure before enabling!)
sudo ufw allow 22/tcp
# Allow email services
sudo ufw allow 25/tcp # SMTP
sudo ufw allow 465/tcp # SMTPS
sudo ufw allow 587/tcp # Submission
# Allow web interface
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Rate limit SSH
sudo ufw limit 22/tcp
# Enable firewall
sudo ufw enable
Advanced UFW rules:
# Allow from specific IP
sudo ufw allow from 192.168.1.0/24 to any port 22
# Block specific country (using ipset)
sudo apt install ipset xtables-addons-common
sudo ipset create blocklist hash:net
sudo ipset add blocklist 1.2.3.0/24
# Add to UFW
sudo iptables -I INPUT -m set --match-set blocklist src -j DROP
SSH Hardening¶
Configure SSH securely:
# Disable root login
PermitRootLogin no
# Use key-based authentication only
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Limit login attempts
MaxAuthTries 3
MaxSessions 5
# Disable X11 forwarding
X11Forwarding no
# Use strong ciphers
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
# Idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2
# Banner
Banner /etc/issue.net
Restart SSH:
Setup SSH key authentication:
# Generate key (on client)
ssh-keygen -t ed25519 -C "admin@mailborder"
# Copy to server
ssh-copy-id user@mailborder-server
# Test before disabling password auth!
ssh user@mailborder-server
Fail2ban Configuration¶
Install Fail2ban:
Configure Fail2ban:
sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
destemail = admin@example.com
sendername = Fail2Ban
action = %(action_mwl)s
[sshd]
enabled = true
port = 22
logpath = /var/log/auth.log
[nginx-http-auth]
enabled = true
port = 80,443
logpath = /var/log/nginx/error.log
[nginx-limit-req]
enabled = true
port = 80,443
logpath = /var/log/nginx/error.log
[postfix-sasl]
enabled = true
port = smtp,465,submission
logpath = /var/log/mail.log
maxretry = 3
[mailborder-auth]
enabled = true
port = 80,443
logpath = /var/log/mailborder/auth.log
maxretry = 5
findtime = 600
bantime = 7200
EOF
Create custom filter:
sudo tee /etc/fail2ban/filter.d/mailborder-auth.conf << 'EOF'
[Definition]
failregex = ^.*Failed login attempt from <HOST>.*$
^.*Authentication failed for user .* from <HOST>.*$
ignoreregex =
EOF
Restart Fail2ban:
Check status:
sudo fail2ban-client status
sudo fail2ban-client status sshd
sudo fail2ban-client status mailborder-auth
Application Hardening¶
Mailborder Configuration¶
Security settings:
[security]
# Enforce HTTPS
force_https = true
https_only = true
# Security headers
hsts_enabled = true
hsts_max_age = 31536000
frame_options = DENY
content_type_nosniff = true
xss_protection = true
# Session security
session_secure = true
session_httponly = true
session_samesite = Strict
session_lifetime = 3600
session_regenerate = true
# Rate limiting
rate_limit_enabled = true
rate_limit_requests = 100
rate_limit_window = 60
# CSRF protection
csrf_protection = true
csrf_token_lifetime = 3600
# Password policy
password_min_length = 12
password_require_uppercase = true
password_require_lowercase = true
password_require_numbers = true
password_require_special = true
password_history = 5
password_expiry_days = 90
# Account lockout
lockout_enabled = true
lockout_attempts = 5
lockout_duration = 900
lockout_window = 300
# 2FA enforcement
require_2fa = true
require_2fa_for_admin = true
# Audit logging
audit_enabled = true
audit_log_file = /var/log/mailborder/audit.log
PHP Hardening¶
Secure PHP configuration:
; Disable dangerous functions
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
; Hide PHP version
expose_php = Off
; Error handling
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/error.log
; File uploads
file_uploads = On
upload_max_filesize = 50M
max_file_uploads = 10
; Resource limits
max_execution_time = 60
max_input_time = 60
memory_limit = 256M
post_max_size = 50M
; Session security
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = Strict
session.use_strict_mode = 1
session.use_only_cookies = 1
; Disable dangerous wrappers
allow_url_fopen = Off
allow_url_include = Off
Restart PHP-FPM:
Nginx Security Headers¶
Add security headers:
server {
listen 443 ssl http2;
server_name mailborder.example.com;
# SSL configuration
ssl_certificate /etc/ssl/certs/mailborder.crt;
ssl_certificate_key /etc/ssl/private/mailborder.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5:!3DES;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Hide server version
server_tokens off;
more_clear_headers Server;
# Rate limiting
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
# Client body size
client_max_body_size 50M;
client_body_timeout 30s;
client_header_timeout 30s;
# Disable unwanted HTTP methods
if ($request_method !~ ^(GET|POST|PUT|DELETE|HEAD|OPTIONS)$) {
return 405;
}
# Block common exploit attempts
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~* \.(git|svn|htaccess|htpasswd)$ {
deny all;
}
# Main application
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP processing
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Security parameters
fastcgi_hide_header X-Powered-By;
fastcgi_intercept_errors on;
fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name mailborder.example.com;
return 301 https://$server_name$request_uri;
}
Reload Nginx:
Database Security¶
MariaDB Hardening¶
Run mysql_secure_installation:
Secure MariaDB configuration:
[mysqld]
# Bind to localhost only
bind-address = 127.0.0.1
# Disable LOAD DATA LOCAL
local-infile = 0
# Enable SSL
ssl-ca = /etc/mysql/ssl/ca-cert.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
# Logging
log-error = /var/log/mysql/error.log
general_log = 0
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
# Secure file operations
secure-file-priv = /var/lib/mysql-files
# User validation
plugin-load-add = simple_password_check.so
simple_password_check_minimal_length = 12
Create restricted database user:
-- Create user with limited privileges
CREATE USER 'mailborder'@'localhost' IDENTIFIED BY 'secure_password';
-- Grant only necessary privileges
GRANT SELECT, INSERT, UPDATE, DELETE ON mailborder.* TO 'mailborder'@'localhost';
-- No GRANT privilege
FLUSH PRIVILEGES;
Encrypt backups:
# Backup with encryption
sudo mysqldump mailborder | gzip | \
openssl enc -aes-256-cbc -salt -pbkdf2 -out /backups/db.sql.gz.enc
Redis Security¶
Secure Redis:
# Bind to localhost only
bind 127.0.0.1 ::1
# Require authentication
requirepass your_strong_redis_password
# Rename dangerous commands
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command CONFIG "CONFIG_a1b2c3d4"
rename-command SHUTDOWN ""
# Disable protected mode (when bind is set)
protected-mode yes
# Maximum clients
maxclients 10000
# Snapshotting disabled (if using as cache only)
save ""
# Logging
loglevel notice
logfile /var/log/redis/redis-server.log
Restart Redis:
Email Security¶
Postfix Hardening¶
Secure Postfix configuration:
# SMTP restrictions
smtpd_helo_required = yes
smtpd_helo_restrictions =
permit_mynetworks,
reject_invalid_helo_hostname,
reject_non_fqdn_helo_hostname,
reject_unknown_helo_hostname
smtpd_sender_restrictions =
permit_mynetworks,
reject_non_fqdn_sender,
reject_unknown_sender_domain
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination,
reject_invalid_hostname,
reject_non_fqdn_recipient,
reject_unknown_recipient_domain,
reject_unauth_pipelining,
reject_rbl_client zen.spamhaus.org,
reject_rbl_client bl.spamcop.net
# Rate limiting
smtpd_client_connection_count_limit = 10
smtpd_client_connection_rate_limit = 30
smtpd_client_message_rate_limit = 100
smtpd_client_recipient_rate_limit = 100
# Size limits
message_size_limit = 52428800
mailbox_size_limit = 0
# Disable VRFY and EXPN
disable_vrfy_command = yes
# TLS security
smtpd_tls_security_level = may
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_ciphers = high
smtpd_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA, KRB5-DES, CBC3-SHA
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_ciphers = high
smtp_tls_security_level = may
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_ciphers = high
# SASL authentication
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous, noplaintext
smtpd_sasl_tls_security_options = noanonymous
Reload Postfix:
SPF, DKIM, DMARC¶
Ensure authentication is enforced:
# Check SPF records
dig +short TXT example.com | grep spf
# Check DKIM signing
echo "Test" | mail -s "DKIM Test" test@gmail.com
# Check headers in received email
# Check DMARC policy
dig +short TXT _dmarc.example.com
Strict DMARC policy:
v=DMARC1; p=reject; pct=100; rua=mailto:dmarc@example.com; ruf=mailto:dmarc@example.com; fo=1; adkim=s; aspf=s
Monitoring and Auditing¶
Security Monitoring¶
Install monitoring tools:
Configure auditd:
# Monitor configuration changes
-w /etc/mailborder/ -p wa -k mailborder_config
-w /etc/postfix/ -p wa -k postfix_config
-w /etc/nginx/ -p wa -k nginx_config
# Monitor authentication
-w /var/log/auth.log -p wa -k auth_log
-w /var/log/mailborder/auth.log -p wa -k mailborder_auth
# Monitor user changes
-w /etc/passwd -p wa -k passwd_changes
-w /etc/group -p wa -k group_changes
-w /etc/shadow -p wa -k shadow_changes
# Monitor sudoers
-w /etc/sudoers -p wa -k sudoers_changes
Restart auditd:
Initialize AIDE:
Run security scan:
Log Monitoring¶
Configure centralized logging:
Forward logs to remote server:
Monitor for security events:
sudo tee /usr/local/bin/mb-security-monitor.sh << 'EOF'
#!/bin/bash
# Check for failed logins
FAILED_LOGINS=$(grep "Failed password" /var/log/auth.log | wc -l)
if [ $FAILED_LOGINS -gt 10 ]; then
echo "ALERT: $FAILED_LOGINS failed login attempts" | \
mail -s "Security Alert" admin@example.com
fi
# Check for privilege escalation
SUDO_FAILURES=$(grep "sudo.*COMMAND" /var/log/auth.log | grep "NOT" | wc -l)
if [ $SUDO_FAILURES -gt 0 ]; then
echo "ALERT: $SUDO_FAILURES sudo failures" | \
mail -s "Security Alert" admin@example.com
fi
# Check for file modifications
if [ -f /tmp/aide-changes.txt ]; then
mail -s "AIDE File Changes" admin@example.com < /tmp/aide-changes.txt
fi
EOF
sudo chmod +x /usr/local/bin/mb-security-monitor.sh
Schedule monitoring:
Compliance and Best Practices¶
Security Checklist¶
System Security:
□ OS packages up to date
□ Automatic security updates enabled
□ Unnecessary services disabled
□ Firewall configured and enabled
□ SSH hardened (key-only auth)
□ Fail2ban configured
□ SELinux/AppArmor enabled
Application Security:
□ HTTPS enforced
□ Security headers configured
□ CSRF protection enabled
□ Rate limiting enabled
□ 2FA required for admin
□ Password policy enforced
□ Session security configured
□ Audit logging enabled
Database Security:
□ Database users restricted
□ SSL/TLS enabled
□ Bind to localhost
□ Dangerous commands disabled
□ Backups encrypted
Email Security:
□ SPF configured
□ DKIM signing enabled
□ DMARC policy enforced
□ TLS encryption required
□ Authentication required
□ Rate limiting configured
□ RBL checking enabled
Monitoring:
□ System monitoring enabled
□ Log aggregation configured
□ Security alerts configured
□ File integrity monitoring
□ Regular security audits
Regular Security Tasks¶
Daily: - Review authentication logs - Check failed login attempts - Monitor system alerts
Weekly: - Review firewall logs - Check for security updates - Analyze access patterns
Monthly: - Run security audit (Lynis) - Review user accounts - Check file integrity (AIDE) - Review SSL certificates - Test backup restoration
Quarterly: - Penetration testing - Security policy review - Compliance audit - Disaster recovery test