commit e9c35bdc8d1a39a7277c945dc750de6d83f76c0e Author: Tommy Parnell Date: Sun Aug 3 11:36:44 2025 -0400 init diff --git a/QUICK_START.md b/QUICK_START.md new file mode 100644 index 0000000..a48f140 --- /dev/null +++ b/QUICK_START.md @@ -0,0 +1,183 @@ +# Quick Start Guide + +## Prerequisites Checklist + +Before running the setup script, ensure you have: + +- [ ] Fresh Ubuntu 20.04+ or Debian 11+ server +- [ ] Root access to the server +- [ ] Domain name (e.g., `example.com`) +- [ ] Subdomain for mail server (e.g., `mail.example.com`) +- [ ] DNS A record: `mail.example.com` → Your server IP +- [ ] DNS MX record: `example.com` → `mail.example.com` +- [ ] Server ports 25, 587, 465, 143, 993, 110, 995, 80, 443 accessible + +## Quick Installation + +1. **Download the setup script:** + ```bash + wget https://raw.githubusercontent.com/your-repo/setup-email-server.sh + chmod +x setup-email-server.sh + ``` + +2. **Run as root:** + ```bash + sudo ./setup-email-server.sh + ``` + +3. **Follow the prompts to enter:** + - Domain name (e.g., `example.com`) + - Hostname (e.g., `mail.example.com`) + - Admin email + - Database password + - PostfixAdmin setup password + +4. **Wait for completion** (15-30 minutes) + +## Post-Installation Steps + +### 1. Add DNS Records + +Copy the DNS records displayed at the end of the script: + +```dns +; SPF Record +example.com. IN TXT "v=spf1 mx ~all" + +; DMARC Record +_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com" + +; DKIM Record (copy from script output) +mail._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=..." +``` + +### 2. Configure PostfixAdmin + +1. Visit: `https://mail.example.com/postfixadmin/setup.php` +2. Enter the setup password you created +3. Create admin user +4. Login at: `https://mail.example.com/postfixadmin/` + +### 3. Create Your First Domain and Mailbox + +1. In PostfixAdmin, add your domain (`example.com`) +2. Create a mailbox (e.g., `user@example.com`) +3. Test by sending an email + +## Testing + +Run the test script: +```bash +./test-email-server.sh +``` + +## Maintenance + +Set up automated maintenance: +```bash +# Add to crontab +sudo crontab -e + +# Add these lines: +0 2 * * * /path/to/maintenance-email-server.sh +0 5 1 * * /path/to/backup-email-server.sh +``` + +## Client Configuration + +### IMAP Settings: +- **Server:** mail.example.com +- **Port:** 993 +- **Security:** SSL/TLS +- **Authentication:** Normal password + +### SMTP Settings: +- **Server:** mail.example.com +- **Port:** 587 +- **Security:** STARTTLS +- **Authentication:** Normal password + +## Troubleshooting + +### Common Issues: + +1. **Can't connect to PostfixAdmin:** + - Check Apache status: `systemctl status apache2` + - Verify SSL certificate: `openssl s_client -connect mail.example.com:443` + +2. **Email not sending:** + - Check Postfix status: `systemctl status postfix` + - View logs: `tail -f /var/log/mail.log` + - Test SMTP: `telnet localhost 25` + +3. **Email not receiving:** + - Check MX record: `dig MX example.com` + - Check firewall: `ufw status` + - Test port 25: `telnet mail.example.com 25` + +4. **DKIM issues:** + - Test DKIM: `opendkim-testkey -d example.com -s mail -vvv` + - Check DNS: `dig TXT mail._domainkey.example.com` + +### Log Files: +- Main mail log: `/var/log/mail.log` +- Setup log: `/var/log/email-server-setup.log` +- Apache error log: `/var/log/apache2/error.log` + +### Useful Commands: +```bash +# Check all services +systemctl status postfix dovecot amavis spamassassin clamav-daemon opendkim apache2 + +# Test configuration +postfix check +dovecot -n + +# Check mail queue +mailq + +# Flush mail queue +postqueue -f + +# Check disk space +df -h + +# View active connections +ss -tulpn | grep :25 +``` + +## Security Recommendations + +1. **Keep system updated:** + ```bash + apt update && apt upgrade -y + ``` + +2. **Monitor logs regularly:** + ```bash + tail -f /var/log/mail.log | grep -i error + ``` + +3. **Backup regularly:** + ```bash + ./backup-email-server.sh --include-mail + ``` + +4. **Test SSL certificates:** + ```bash + openssl x509 -in /etc/letsencrypt/live/mail.example.com/fullchain.pem -noout -dates + ``` + +5. **Monitor disk space:** + ```bash + df -h /var/mail/vhosts + ``` + +## Getting Help + +1. Run the test script: `./test-email-server.sh` +2. Check the setup log: `/var/log/email-server-setup.log` +3. Use online testing tools: + - [MX Toolbox](https://mxtoolbox.com/) + - [Mail Tester](https://www.mail-tester.com/) + - [DKIM Validator](https://dkimvalidator.com/) diff --git a/README.md b/README.md new file mode 100644 index 0000000..186b755 --- /dev/null +++ b/README.md @@ -0,0 +1,309 @@ +# Email Server Setup Script + +This script automatically sets up a complete email server with the following components: + +- **Postfix** - SMTP server for sending/receiving emails +- **Dovecot** - IMAP/POP3 server for email retrieval +- **PostgreSQL** - Database backend for virtual domains and users +- **PostfixAdmin** - Web interface for managing domains and mailboxes +- **Amavis** - Content filter for spam and virus scanning +- **SpamAssassin** - Spam filtering +- **ClamAV** - Antivirus scanning +- **OpenDKIM** - DKIM signing for email authentication +- **Let's Encrypt** - SSL/TLS certificates for secure connections + +## Prerequisites + +1. **Fresh Ubuntu/Debian server** (tested on Ubuntu 20.04/22.04) +2. **Root access** to the server +3. **Domain name** pointing to your server +4. **Hostname** (e.g., mail.yourdomain.com) with A record pointing to server IP +5. **Ports 25, 587, 465, 110, 995, 143, 993, 80, 443** open in firewall + +## DNS Prerequisites + +Before running the script, ensure you have these DNS records: + +``` +A mail.yourdomain.com [Your Server IP] +MX yourdomain.com mail.yourdomain.com +``` + +## Installation + +1. **Download the script:** + ```bash + wget https://raw.githubusercontent.com/your-repo/setup-email-server.sh + chmod +x setup-email-server.sh + ``` + +2. **Run the script as root:** + ```bash + sudo ./setup-email-server.sh + ``` + +3. **Provide the required information when prompted:** + - Domain name (e.g., yourdomain.com) + - Hostname (e.g., mail.yourdomain.com) + - Admin email address + - PostgreSQL password for postfix user + - PostfixAdmin setup password + +## Post-Installation Steps + +### 1. Add DNS Records + +After the script completes, add these DNS records: + +#### SPF Record +``` +TXT yourdomain.com "v=spf1 mx ~all" +``` + +#### DMARC Record +``` +TXT _dmarc.yourdomain.com "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com" +``` + +#### DKIM Record +The script will display the DKIM DNS record. Add it to your DNS: +``` +TXT mail._domainkey.yourdomain.com "v=DKIM1; k=rsa; p=..." +``` + +### 2. Complete PostfixAdmin Setup + +1. Visit `https://mail.yourdomain.com/postfixadmin/setup.php` +2. Enter the setup password you provided during installation +3. Create an admin user +4. Login to PostfixAdmin at `https://mail.yourdomain.com/postfixadmin/` + +### 3. Create Domains and Mailboxes + +1. Add your domain in PostfixAdmin +2. Create mailboxes for your users +3. Test email functionality + +## Multiple Domain Support + +This email server fully supports unlimited virtual domains. Each domain can have its own mailboxes, aliases, quotas, and DKIM signing. + +### Adding Additional Domains + +#### Method 1: Using the Helper Script (Recommended) +```bash +# Add DKIM support for a new domain +sudo ./add-domain.sh newdomain.com + +# This will: +# - Generate DKIM keys +# - Update OpenDKIM configuration +# - Display DNS records to add +# - Show PostfixAdmin setup instructions +``` + +#### Method 2: Manual Process +1. **Add domain via PostfixAdmin:** + - Login to PostfixAdmin + - Go to "Domain List" → "New Domain" + - Enter domain details and save + +2. **Configure DKIM for the domain:** + ```bash + sudo ./add-domain.sh newdomain.com + ``` + +3. **Add DNS records for the new domain:** + ``` + MX newdomain.com mail.yourmainhost.com + TXT newdomain.com "v=spf1 mx ~all" + TXT _dmarc.newdomain.com "v=DMARC1; p=none; rua=mailto:dmarc@newdomain.com" + TXT mail._domainkey.newdomain.com "v=DKIM1; k=rsa; p=..." + ``` + +### Managing Multiple Domains + +Use the domain management script for comprehensive domain operations: + +```bash +# List all configured domains +sudo ./manage-domains.sh list + +# Show detailed information for a domain +sudo ./manage-domains.sh show example.com + +# Test domain configuration (DNS, DKIM, etc.) +sudo ./manage-domains.sh test example.com + +# Add a new domain +sudo ./manage-domains.sh add newdomain.com + +# Remove a domain (WARNING: deletes all data) +sudo ./manage-domains.sh remove olddomain.com + +# Show overall server status +sudo ./manage-domains.sh status +``` + +### Application SMTP for Multiple Domains + +Applications can send from any configured domain using the same SMTP server: + +```python +# Example: Send from different domains +domains = ['company.com', 'mysite.org', 'shop.net'] + +for domain in domains: + smtp_config = { + 'host': 'mail.yourmainhost.com', + 'port': 587, + 'username': f'noreply@{domain}', + 'password': 'domain_specific_password' + } + # Send email using this configuration +``` + +## Security Features + +- **SSL/TLS encryption** for all connections +- **DKIM signing** for email authentication +- **SPF and DMARC** policies for anti-spoofing +- **Spam filtering** with SpamAssassin +- **Virus scanning** with ClamAV +- **Secure authentication** with encrypted passwords +- **Firewall rules** restricting access to necessary ports + +## Email Ports + +- **25** - SMTP (incoming mail) +- **587** - Submission (authenticated sending) +- **465** - SMTPS (secure SMTP) +- **143** - IMAP +- **993** - IMAPS (secure IMAP) +- **110** - POP3 +- **995** - POP3S (secure POP3) + +## Client Configuration + +### IMAP Settings +- **Server**: mail.yourdomain.com +- **Port**: 993 (SSL) or 143 (STARTTLS) +- **Security**: SSL/TLS +- **Authentication**: Normal password + +### SMTP Settings +- **Server**: mail.yourdomain.com +- **Port**: 587 (STARTTLS) or 465 (SSL) +- **Security**: SSL/TLS +- **Authentication**: Normal password + +## Troubleshooting + +### Check Service Status +```bash +systemctl status postfix dovecot amavis spamassassin clamav-daemon opendkim apache2 +``` + +### View Logs +```bash +# Postfix logs +tail -f /var/log/mail.log + +# Dovecot logs +tail -f /var/log/dovecot.log + +# Amavis logs +tail -f /var/log/amavis.log + +# Apache logs +tail -f /var/log/apache2/error.log +``` + +### Test Email Sending +```bash +echo "Test email" | mail -s "Test Subject" user@yourdomain.com +``` + +### Test DKIM +```bash +opendkim-testkey -d yourdomain.com -s mail -vvv +``` + +### Test DNS Records +```bash +dig MX yourdomain.com +dig TXT yourdomain.com +dig TXT mail._domainkey.yourdomain.com +``` + +## Maintenance + +### Update SSL Certificates +Certificates are automatically renewed via cron. To test renewal: +```bash +certbot renew --dry-run +``` + +### Update Spam Rules +```bash +sa-update +systemctl restart spamassassin +``` + +### Update Virus Definitions +```bash +freshclam +systemctl restart clamav-daemon +``` + +### Backup Configuration +```bash +tar -czf email-backup-$(date +%Y%m%d).tar.gz \ + /etc/postfix \ + /etc/dovecot \ + /etc/amavis \ + /etc/opendkim \ + /var/www/postfixadmin/config.local.php \ + /etc/letsencrypt +``` + +## File Locations + +- **Postfix config**: `/etc/postfix/` +- **Dovecot config**: `/etc/dovecot/` +- **Amavis config**: `/etc/amavis/` +- **OpenDKIM config**: `/etc/opendkim/` +- **PostfixAdmin**: `/var/www/postfixadmin/` +- **Mail storage**: `/var/mail/vhosts/` +- **SSL certificates**: `/etc/letsencrypt/live/` +- **Setup log**: `/var/log/email-server-setup.log` + +## Advanced Configuration + +### Custom Spam Rules +Edit `/etc/spamassassin/local.cf` and restart SpamAssassin. + +### Additional Domains +Add domains through PostfixAdmin web interface. + +### Quota Management +Quotas are managed through PostfixAdmin and enforced by Dovecot. + +### Backup Strategy +Implement regular backups of: +- PostgreSQL database +- Configuration files +- SSL certificates +- Mail data + +## Support + +For issues and support: +1. Check the setup log: `/var/log/email-server-setup.log` +2. Review service logs +3. Verify DNS configuration +4. Test with online email testing tools + +## License + +This script is provided as-is under the MIT License. diff --git a/add-domain.sh b/add-domain.sh new file mode 100755 index 0000000..1745689 --- /dev/null +++ b/add-domain.sh @@ -0,0 +1,329 @@ +#!/bin/bash + +# Add Domain Helper Script +# Adds DKIM support and configuration for additional domains + +set -euo pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Check arguments +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " + echo "Example: $0 newdomain.com" + echo + echo "This script will:" + echo " - Generate DKIM keys for the new domain" + echo " - Update OpenDKIM configuration" + echo " - Display DNS records to add" + echo " - Show PostfixAdmin setup instructions" + exit 1 +fi + +DOMAIN=$1 + +# Logging +LOG_FILE="/var/log/email-server-setup.log" + +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" +} + +info() { + echo -e "${BLUE}INFO: $1${NC}" + log "INFO: $1" +} + +success() { + echo -e "${GREEN}SUCCESS: $1${NC}" + log "SUCCESS: $1" +} + +warning() { + echo -e "${YELLOW}WARNING: $1${NC}" + log "WARNING: $1" +} + +error() { + echo -e "${RED}ERROR: $1${NC}" >&2 + log "ERROR: $1" + exit 1 +} + +# Check if running as root +check_root() { + if [[ $EUID -ne 0 ]]; then + error "This script must be run as root" + fi +} + +# Validate domain format +validate_domain() { + if [[ ! "$DOMAIN" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.[a-zA-Z]{2,}$ ]]; then + error "Invalid domain format: $DOMAIN" + fi +} + +# Check if domain already exists in DKIM +check_existing_domain() { + if [[ -d "/etc/opendkim/keys/$DOMAIN" ]]; then + warning "DKIM keys already exist for $DOMAIN" + read -p "Do you want to regenerate them? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + info "Keeping existing DKIM keys for $DOMAIN" + return 1 + fi + fi + return 0 +} + +# Get main hostname from existing configuration +get_main_hostname() { + if [[ -f "/etc/postfix/main.cf" ]]; then + MAIN_HOSTNAME=$(grep "^myhostname" /etc/postfix/main.cf | cut -d= -f2 | tr -d ' ') + if [[ -z "$MAIN_HOSTNAME" ]]; then + error "Could not determine main hostname from Postfix configuration" + fi + else + error "Postfix configuration not found. Run the main setup script first." + fi +} + +# Generate DKIM keys for the new domain +generate_dkim_keys() { + info "Generating DKIM keys for domain: $DOMAIN" + + # Create directory + mkdir -p /etc/opendkim/keys/$DOMAIN + + # Generate DKIM key + opendkim-genkey -t -s mail -d $DOMAIN -D /etc/opendkim/keys/$DOMAIN + + # Set permissions + chown -R opendkim:opendkim /etc/opendkim/keys/$DOMAIN + chmod 600 /etc/opendkim/keys/$DOMAIN/mail.private + + success "DKIM keys generated for $DOMAIN" +} + +# Update OpenDKIM configuration +update_opendkim_config() { + info "Updating OpenDKIM configuration..." + + # Check if entry already exists in key.table + if ! grep -q "mail._domainkey.$DOMAIN" /etc/opendkim/key.table 2>/dev/null; then + echo "mail._domainkey.$DOMAIN $DOMAIN:mail:/etc/opendkim/keys/$DOMAIN/mail.private" >> /etc/opendkim/key.table + fi + + # Check if entry already exists in signing.table + if ! grep -q "*@$DOMAIN" /etc/opendkim/signing.table 2>/dev/null; then + echo "*@$DOMAIN mail._domainkey.$DOMAIN" >> /etc/opendkim/signing.table + fi + + # Check if domain already exists in trusted.hosts + if ! grep -q "^$DOMAIN$" /etc/opendkim/trusted.hosts 2>/dev/null; then + echo "$DOMAIN" >> /etc/opendkim/trusted.hosts + fi + + success "OpenDKIM configuration updated" +} + +# Restart OpenDKIM service +restart_opendkim() { + info "Restarting OpenDKIM service..." + if systemctl restart opendkim; then + success "OpenDKIM service restarted" + else + error "Failed to restart OpenDKIM service" + fi +} + +# Test DKIM configuration +test_dkim() { + info "Testing DKIM configuration..." + sleep 2 # Give OpenDKIM time to start + + if opendkim-testkey -d $DOMAIN -s mail -vvv 2>&1 | grep -q "key OK"; then + success "DKIM test passed for $DOMAIN" + else + warning "DKIM test failed. Check the DNS record below and try again later." + fi +} + +# Display DNS records to add +display_dns_records() { + get_main_hostname + + echo + echo -e "${YELLOW}=== DNS Records to Add for $DOMAIN ===${NC}" + echo + echo -e "${BLUE}1. MX Record:${NC}" + echo "MX $DOMAIN $MAIN_HOSTNAME" + echo + echo -e "${BLUE}2. SPF Record:${NC}" + echo "TXT $DOMAIN \"v=spf1 mx ~all\"" + echo + echo -e "${BLUE}3. DMARC Record:${NC}" + echo "TXT _dmarc.$DOMAIN \"v=DMARC1; p=none; rua=mailto:dmarc@$DOMAIN\"" + echo + echo -e "${BLUE}4. DKIM Record:${NC}" + echo "Record name: mail._domainkey.$DOMAIN" + echo "Record type: TXT" + echo "Record value:" + cat /etc/opendkim/keys/$DOMAIN/mail.txt 2>/dev/null || echo "Error: DKIM key file not found" + echo +} + +# Display PostfixAdmin instructions +display_postfixadmin_instructions() { + get_main_hostname + + echo -e "${YELLOW}=== PostfixAdmin Setup Instructions ===${NC}" + echo + echo "1. Visit: https://$MAIN_HOSTNAME/postfixadmin/" + echo "2. Login with your admin credentials" + echo "3. Go to 'Domain List' → 'New Domain'" + echo "4. Enter domain: $DOMAIN" + echo "5. Set desired limits and quotas" + echo "6. Click 'Add Domain'" + echo "7. Create mailboxes under 'Virtual List' → 'Add Mailbox'" + echo + echo -e "${BLUE}Suggested mailboxes for $DOMAIN:${NC}" + echo " - admin@$DOMAIN" + echo " - info@$DOMAIN" + echo " - noreply@$DOMAIN" + echo " - support@$DOMAIN" + echo +} + +# Display application configuration +display_app_config() { + get_main_hostname + + echo -e "${YELLOW}=== Application SMTP Configuration ===${NC}" + echo + echo "Your applications can now send email from $DOMAIN using these settings:" + echo + echo -e "${BLUE}SMTP Settings:${NC}" + echo " Host: $MAIN_HOSTNAME" + echo " Port: 587 (STARTTLS) or 465 (SSL/TLS)" + echo " Security: STARTTLS or SSL/TLS" + echo " Username: mailbox@$DOMAIN (full email address)" + echo " Password: [mailbox password from PostfixAdmin]" + echo + echo -e "${BLUE}Example Python code:${NC}" + cat << 'EOF' +import smtplib +from email.mime.text import MIMEText + +smtp_config = { + 'host': 'MAIN_HOSTNAME', + 'port': 587, + 'username': 'app@DOMAIN', + 'password': 'your_mailbox_password' +} + +msg = MIMEText("Hello from DOMAIN!") +msg['Subject'] = "Test Email" +msg['From'] = smtp_config['username'] +msg['To'] = "recipient@example.com" + +with smtplib.SMTP(smtp_config['host'], smtp_config['port']) as server: + server.starttls() + server.login(smtp_config['username'], smtp_config['password']) + server.send_message(msg) +EOF + echo +} + +# Verify email server is running +verify_email_server() { + info "Verifying email server status..." + + services=("postfix" "dovecot" "opendkim" "postgresql") + all_running=true + + for service in "${services[@]}"; do + if ! systemctl is-active --quiet $service; then + warning "$service is not running" + all_running=false + fi + done + + if $all_running; then + success "All required services are running" + else + warning "Some services are not running. You may need to restart them." + fi +} + +# Main function +main() { + echo -e "${BLUE}Add Domain Script for Email Server${NC}" + echo "==================================" + echo "Adding domain: $DOMAIN" + echo + + check_root + validate_domain + verify_email_server + + if check_existing_domain; then + generate_dkim_keys + fi + + update_opendkim_config + restart_opendkim + test_dkim + display_dns_records + display_postfixadmin_instructions + display_app_config + + echo + success "Domain $DOMAIN has been configured!" + echo + echo -e "${YELLOW}Next steps:${NC}" + echo "1. Add the DNS records shown above" + echo "2. Wait for DNS propagation (up to 24 hours)" + echo "3. Add the domain in PostfixAdmin" + echo "4. Create mailboxes for the domain" + echo "5. Test email sending and receiving" + echo + echo -e "${BLUE}To test the configuration later, run:${NC}" + echo "./test-email-server.sh" +} + +# Show help if requested +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then + echo "Add Domain Script for Email Server" + echo "=================================" + echo + echo "Usage: $0 " + echo + echo "This script adds DKIM support for additional domains to your email server." + echo "It will generate DKIM keys, update OpenDKIM configuration, and provide" + echo "DNS records and PostfixAdmin instructions." + echo + echo "Examples:" + echo " $0 newcompany.com" + echo " $0 mysite.org" + echo + echo "Prerequisites:" + echo " - Email server must be already set up with setup-email-server.sh" + echo " - Must be run as root" + echo " - Domain should point to your server" + echo + echo "After running this script:" + echo " 1. Add the provided DNS records" + echo " 2. Configure the domain in PostfixAdmin" + echo " 3. Create mailboxes for the new domain" + exit 0 +fi + +main "$@" diff --git a/backup-email-server.sh b/backup-email-server.sh new file mode 100755 index 0000000..3f49acb --- /dev/null +++ b/backup-email-server.sh @@ -0,0 +1,179 @@ +#!/bin/bash + +# Email Server Backup Script +# Creates backups of email server configuration and data + +set -euo pipefail + +# Configuration +BACKUP_DIR="/var/backups/email-server" +DATE=$(date +%Y%m%d_%H%M%S) +BACKUP_NAME="email-backup-$DATE" +RETENTION_DAYS=30 + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" +} + +info() { + echo -e "${BLUE}INFO: $1${NC}" + log "INFO: $1" +} + +success() { + echo -e "${GREEN}SUCCESS: $1${NC}" + log "SUCCESS: $1" +} + +error() { + echo -e "${RED}ERROR: $1${NC}" >&2 + log "ERROR: $1" + exit 1 +} + +# Create backup directory +create_backup_dir() { + info "Creating backup directory..." + mkdir -p "$BACKUP_DIR/$BACKUP_NAME" +} + +# Backup PostgreSQL database +backup_database() { + info "Backing up PostgreSQL database..." + sudo -u postgres pg_dump postfix > "$BACKUP_DIR/$BACKUP_NAME/postfix_db.sql" + success "Database backup completed" +} + +# Backup configuration files +backup_configs() { + info "Backing up configuration files..." + + # Postfix + cp -r /etc/postfix "$BACKUP_DIR/$BACKUP_NAME/" + + # Dovecot + cp -r /etc/dovecot "$BACKUP_DIR/$BACKUP_NAME/" + + # Amavis + cp -r /etc/amavis "$BACKUP_DIR/$BACKUP_NAME/" + + # OpenDKIM + cp -r /etc/opendkim "$BACKUP_DIR/$BACKUP_NAME/" + + # SpamAssassin + cp -r /etc/spamassassin "$BACKUP_DIR/$BACKUP_NAME/" + + # PostfixAdmin + if [[ -d "/var/www/postfixadmin" ]]; then + cp -r /var/www/postfixadmin "$BACKUP_DIR/$BACKUP_NAME/" + fi + + # Apache configuration + cp -r /etc/apache2/sites-available "$BACKUP_DIR/$BACKUP_NAME/apache2-sites/" + + success "Configuration backup completed" +} + +# Backup SSL certificates +backup_ssl() { + info "Backing up SSL certificates..." + if [[ -d "/etc/letsencrypt" ]]; then + cp -r /etc/letsencrypt "$BACKUP_DIR/$BACKUP_NAME/" + success "SSL certificates backup completed" + else + info "No SSL certificates found to backup" + fi +} + +# Backup mail data (optional - can be large) +backup_mail_data() { + if [[ "${1:-}" == "--include-mail" ]]; then + info "Backing up mail data (this may take a while)..." + if [[ -d "/var/mail/vhosts" ]]; then + tar -czf "$BACKUP_DIR/$BACKUP_NAME/mail_data.tar.gz" -C /var/mail vhosts + success "Mail data backup completed" + else + info "No mail data found to backup" + fi + else + info "Skipping mail data backup (use --include-mail to include)" + fi +} + +# Create compressed archive +create_archive() { + info "Creating compressed archive..." + cd "$BACKUP_DIR" + tar -czf "$BACKUP_NAME.tar.gz" "$BACKUP_NAME" + rm -rf "$BACKUP_NAME" + success "Archive created: $BACKUP_DIR/$BACKUP_NAME.tar.gz" +} + +# Cleanup old backups +cleanup_old_backups() { + info "Cleaning up backups older than $RETENTION_DAYS days..." + find "$BACKUP_DIR" -name "email-backup-*.tar.gz" -mtime +$RETENTION_DAYS -delete + success "Old backups cleaned up" +} + +# Display backup information +show_backup_info() { + echo + echo -e "${GREEN}Backup completed successfully!${NC}" + echo "Backup location: $BACKUP_DIR/$BACKUP_NAME.tar.gz" + echo "Backup size: $(du -h "$BACKUP_DIR/$BACKUP_NAME.tar.gz" | cut -f1)" + echo + echo "To restore this backup:" + echo "1. Extract: tar -xzf $BACKUP_NAME.tar.gz" + echo "2. Stop services: systemctl stop postfix dovecot amavis apache2" + echo "3. Restore configurations to their original locations" + echo "4. Restore database: sudo -u postgres psql postfix < postfix_db.sql" + echo "5. Restart services: systemctl start postfix dovecot amavis apache2" +} + +# Main function +main() { + echo -e "${BLUE}Email Server Backup Script${NC}" + echo "==========================" + echo + + # Check if running as root + if [[ $EUID -ne 0 ]]; then + error "This script must be run as root" + fi + + create_backup_dir + backup_database + backup_configs + backup_ssl + backup_mail_data "$@" + create_archive + cleanup_old_backups + show_backup_info +} + +# Show usage if help requested +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then + echo "Usage: $0 [--include-mail]" + echo + echo "Options:" + echo " --include-mail Include mail data in backup (can be large)" + echo " --help, -h Show this help message" + echo + echo "This script backs up:" + echo " - PostgreSQL database" + echo " - Configuration files (Postfix, Dovecot, Amavis, OpenDKIM, etc.)" + echo " - SSL certificates" + echo " - PostfixAdmin installation" + echo " - Mail data (if --include-mail is specified)" + exit 0 +fi + +main "$@" diff --git a/config.template b/config.template new file mode 100644 index 0000000..8fcef7a --- /dev/null +++ b/config.template @@ -0,0 +1,207 @@ +# Email Server Configuration Template +# Copy this file and customize for your environment + +# ============================================================================= +# BASIC CONFIGURATION +# ============================================================================= + +# Your domain name (e.g., example.com) +DOMAIN="yourdomain.com" + +# Your mail server hostname (e.g., mail.example.com) +HOSTNAME="mail.yourdomain.com" + +# Administrator email address +ADMIN_EMAIL="admin@yourdomain.com" + +# ============================================================================= +# DATABASE CONFIGURATION +# ============================================================================= + +# PostgreSQL database settings +DB_NAME="postfix" +DB_USER="postfix" +DB_PASSWORD="your_secure_database_password" + +# ============================================================================= +# POSTFIXADMIN CONFIGURATION +# ============================================================================= + +# PostfixAdmin setup password (used during initial setup) +POSTFIXADMIN_PASSWORD="your_secure_setup_password" + +# PostfixAdmin installation directory +WEBROOT="/var/www/postfixadmin" + +# ============================================================================= +# SSL/TLS CONFIGURATION +# ============================================================================= + +# Let's Encrypt email for certificate registration +LETSENCRYPT_EMAIL="admin@yourdomain.com" + +# ============================================================================= +# SECURITY SETTINGS +# ============================================================================= + +# Fail2ban configuration (optional) +ENABLE_FAIL2BAN="yes" + +# Maximum message size (in bytes) - 50MB default +MAX_MESSAGE_SIZE="52428800" + +# Maximum mailbox size (0 = unlimited) +MAX_MAILBOX_SIZE="0" + +# ============================================================================= +# SPAM FILTERING SETTINGS +# ============================================================================= + +# SpamAssassin score thresholds +SA_TAG_LEVEL="2.0" # Add spam headers +SA_TAG2_LEVEL="6.0" # Add more spam headers +SA_KILL_LEVEL="6.9" # Reject/quarantine + +# ============================================================================= +# ADVANCED SETTINGS +# ============================================================================= + +# Virtual mail user/group IDs +VMAIL_UID="5000" +VMAIL_GID="5000" + +# Mail storage location +MAIL_LOCATION="/var/mail/vhosts" + +# Dovecot authentication method +AUTH_MECHANISMS="plain login" + +# ============================================================================= +# NETWORK SETTINGS +# ============================================================================= + +# Listen on these interfaces (all = all interfaces) +INET_INTERFACES="all" + +# IP protocol (ipv4, ipv6, or all) +INET_PROTOCOLS="ipv4" + +# ============================================================================= +# BACKUP SETTINGS +# ============================================================================= + +# Backup directory +BACKUP_DIR="/var/backups/email-server" + +# Backup retention (days) +BACKUP_RETENTION="30" + +# Include mail data in backups (yes/no) +BACKUP_INCLUDE_MAIL="no" + +# ============================================================================= +# MONITORING SETTINGS +# ============================================================================= + +# Log file locations +MAIN_LOG="/var/log/mail.log" +SETUP_LOG="/var/log/email-server-setup.log" + +# Disk space warning threshold (percentage) +DISK_WARNING_THRESHOLD="85" + +# Certificate expiration warning (days) +CERT_WARNING_DAYS="30" + +# ============================================================================= +# DNS RECORDS REFERENCE +# ============================================================================= + +# Add these DNS records to your domain: +# +# A Record: +# mail.yourdomain.com IN A YOUR_SERVER_IP +# +# MX Record: +# yourdomain.com IN MX 10 mail.yourdomain.com +# +# SPF Record: +# yourdomain.com IN TXT "v=spf1 mx ~all" +# +# DMARC Record: +# _dmarc.yourdomain.com IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com" +# +# DKIM Record (generated by the script): +# mail._domainkey.yourdomain.com IN TXT "v=DKIM1; k=rsa; p=..." + +# ============================================================================= +# FIREWALL PORTS +# ============================================================================= + +# These ports will be opened in the firewall: +# 25 - SMTP (incoming mail) +# 587 - Submission (authenticated sending) +# 465 - SMTPS (secure SMTP) +# 143 - IMAP +# 993 - IMAPS (secure IMAP) +# 110 - POP3 +# 995 - POP3S (secure POP3) +# 80 - HTTP (for certificate validation) +# 443 - HTTPS (PostfixAdmin web interface) + +# ============================================================================= +# RECOMMENDED ADDITIONAL SECURITY +# ============================================================================= + +# Consider implementing: +# 1. Fail2ban for intrusion prevention +# 2. Regular security updates +# 3. Strong passwords for all accounts +# 4. Regular backups +# 5. Monitoring and alerting +# 6. Rate limiting +# 7. Greylisting (optional) + +# ============================================================================= +# MAINTENANCE SCHEDULE +# ============================================================================= + +# Recommended cron jobs: +# Daily virus definition updates: +# 0 2 * * * /usr/bin/freshclam + +# Weekly SpamAssassin rule updates: +# 0 3 * * 1 /usr/bin/sa-update && /bin/systemctl restart spamassassin + +# Weekly maintenance: +# 0 4 * * 1 /path/to/maintenance-email-server.sh + +# Monthly backups: +# 0 5 1 * * /path/to/backup-email-server.sh + +# ============================================================================= +# TROUBLESHOOTING COMMANDS +# ============================================================================= + +# Check service status: +# systemctl status postfix dovecot amavis spamassassin clamav-daemon opendkim + +# View logs: +# tail -f /var/log/mail.log +# tail -f /var/log/dovecot.log + +# Test configuration: +# postfix check +# dovecot -n +# amavisd-new testkeys + +# Check mail queue: +# mailq +# postqueue -p + +# Test DKIM: +# opendkim-testkey -d yourdomain.com -s mail -vvv + +# Test ports: +# telnet localhost 25 +# openssl s_client -connect localhost:993 diff --git a/maintenance-email-server.sh b/maintenance-email-server.sh new file mode 100755 index 0000000..054a2d1 --- /dev/null +++ b/maintenance-email-server.sh @@ -0,0 +1,290 @@ +#!/bin/bash + +# Email Server Maintenance Script +# Performs routine maintenance tasks + +set -euo pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" +} + +info() { + echo -e "${BLUE}INFO: $1${NC}" + log "INFO: $1" +} + +success() { + echo -e "${GREEN}SUCCESS: $1${NC}" + log "SUCCESS: $1" +} + +warning() { + echo -e "${YELLOW}WARNING: $1${NC}" + log "WARNING: $1" +} + +error() { + echo -e "${RED}ERROR: $1${NC}" >&2 + log "ERROR: $1" +} + +# Update virus definitions +update_virus_definitions() { + info "Updating virus definitions..." + if freshclam; then + success "Virus definitions updated" + systemctl restart clamav-daemon + else + error "Failed to update virus definitions" + fi +} + +# Update spam rules +update_spam_rules() { + info "Updating SpamAssassin rules..." + if sa-update; then + success "SpamAssassin rules updated" + systemctl restart spamassassin + else + warning "SpamAssassin update failed or no updates available" + fi +} + +# Clean mail logs +clean_mail_logs() { + info "Cleaning old mail logs..." + + # Rotate logs if they're getting large (>100MB) + if [[ -f "/var/log/mail.log" ]]; then + size=$(stat -f%z "/var/log/mail.log" 2>/dev/null || stat -c%s "/var/log/mail.log" 2>/dev/null || echo 0) + if [[ $size -gt 104857600 ]]; then # 100MB + logrotate -f /etc/logrotate.d/rsyslog + success "Mail logs rotated" + else + info "Mail logs are not large enough to rotate" + fi + fi +} + +# Clean temporary files +clean_temp_files() { + info "Cleaning temporary files..." + + # Clean Amavis temporary files + find /var/lib/amavis/tmp -type f -mtime +7 -delete 2>/dev/null || true + + # Clean SpamAssassin temporary files + find /var/lib/spamassassin -name "*.tmp" -mtime +7 -delete 2>/dev/null || true + + # Clean Postfix temporary files + find /var/spool/postfix -name "*" -type f -mtime +7 -path "*/tmp/*" -delete 2>/dev/null || true + + success "Temporary files cleaned" +} + +# Check disk space +check_disk_space() { + info "Checking disk space..." + + # Check root filesystem + root_usage=$(df / | awk 'NR==2 {print $5}' | sed 's/%//') + if [[ $root_usage -gt 85 ]]; then + warning "Root filesystem is ${root_usage}% full" + else + info "Root filesystem usage: ${root_usage}%" + fi + + # Check mail directory + if [[ -d "/var/mail/vhosts" ]]; then + mail_usage=$(df /var/mail/vhosts | awk 'NR==2 {print $5}' | sed 's/%//') + if [[ $mail_usage -gt 85 ]]; then + warning "Mail directory is ${mail_usage}% full" + else + info "Mail directory usage: ${mail_usage}%" + fi + fi +} + +# Check certificate expiration +check_ssl_expiration() { + info "Checking SSL certificate expiration..." + + cert_files=$(find /etc/letsencrypt/live -name "fullchain.pem" 2>/dev/null || true) + + for cert_file in $cert_files; do + domain=$(basename $(dirname $cert_file)) + expires=$(openssl x509 -in "$cert_file" -noout -enddate | cut -d= -f2) + expires_epoch=$(date -d "$expires" +%s) + current_epoch=$(date +%s) + days_left=$(( (expires_epoch - current_epoch) / 86400 )) + + if [[ $days_left -lt 30 ]]; then + warning "SSL certificate for $domain expires in $days_left days" + else + info "SSL certificate for $domain expires in $days_left days" + fi + done +} + +# Check service status +check_services() { + info "Checking service status..." + + services=("postfix" "dovecot" "amavis" "spamassassin" "clamav-daemon" "opendkim" "apache2" "postgresql") + + for service in "${services[@]}"; do + if systemctl is-active --quiet $service; then + success "$service is running" + else + error "$service is not running" + fi + done +} + +# Test basic email functionality +test_email_basic() { + info "Testing basic email functionality..." + + # Test SMTP port + if nc -z localhost 25; then + success "SMTP port 25 is accessible" + else + error "SMTP port 25 is not accessible" + fi + + # Test submission port + if nc -z localhost 587; then + success "Submission port 587 is accessible" + else + error "Submission port 587 is not accessible" + fi + + # Test IMAP port + if nc -z localhost 993; then + success "IMAPS port 993 is accessible" + else + error "IMAPS port 993 is not accessible" + fi +} + +# Database maintenance +database_maintenance() { + info "Performing database maintenance..." + + # Analyze and vacuum database + sudo -u postgres psql -d postfix -c "ANALYZE;" >/dev/null 2>&1 + sudo -u postgres psql -d postfix -c "VACUUM;" >/dev/null 2>&1 + + success "Database maintenance completed" +} + +# Check mail queue +check_mail_queue() { + info "Checking mail queue..." + + queue_count=$(mailq | tail -1 | awk '{print $5}' || echo "0") + if [[ "$queue_count" == "empty" ]]; then + queue_count=0 + fi + + if [[ $queue_count -gt 10 ]]; then + warning "Mail queue has $queue_count messages" + echo "Run 'mailq' to see queued messages" + echo "Run 'postqueue -f' to flush the queue" + else + info "Mail queue has $queue_count messages" + fi +} + +# Generate maintenance report +generate_report() { + info "Generating maintenance report..." + + report_file="/var/log/email-maintenance-$(date +%Y%m%d).log" + + { + echo "Email Server Maintenance Report - $(date)" + echo "============================================" + echo + echo "System Information:" + echo "- Hostname: $(hostname)" + echo "- Uptime: $(uptime | awk -F'up ' '{print $2}' | awk -F', load' '{print $1}')" + echo "- Load: $(uptime | awk -F'load average: ' '{print $2}')" + echo + echo "Disk Usage:" + df -h | grep -E "/$|/var" + echo + echo "Service Status:" + systemctl status postfix dovecot amavis --no-pager -l + echo + echo "Recent Mail Log Entries:" + tail -20 /var/log/mail.log | grep "$(date '+%b %d')" + } > "$report_file" + + success "Maintenance report saved to $report_file" +} + +# Main function +main() { + echo -e "${BLUE}Email Server Maintenance Script${NC}" + echo "===============================" + echo + + # Check if running as root + if [[ $EUID -ne 0 ]]; then + error "This script must be run as root" + exit 1 + fi + + update_virus_definitions + update_spam_rules + clean_mail_logs + clean_temp_files + check_disk_space + check_ssl_expiration + check_services + test_email_basic + database_maintenance + check_mail_queue + generate_report + + echo + success "Maintenance completed successfully!" + + echo + echo -e "${YELLOW}Recommended actions:${NC}" + echo "1. Review the maintenance report in /var/log/" + echo "2. Monitor disk space regularly" + echo "3. Check for any service issues" + echo "4. Verify SSL certificate renewal is working" +} + +# Show usage +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then + echo "Usage: $0" + echo + echo "This maintenance script performs:" + echo " - Updates virus definitions" + echo " - Updates SpamAssassin rules" + echo " - Cleans old logs and temporary files" + echo " - Checks disk space usage" + echo " - Verifies SSL certificate expiration" + echo " - Tests service status" + echo " - Performs database maintenance" + echo " - Checks mail queue status" + echo " - Generates maintenance report" + echo + echo "Run this script regularly (daily/weekly) via cron:" + echo "0 2 * * 0 /path/to/maintenance-email-server.sh" + exit 0 +fi + +main "$@" diff --git a/manage-domains.sh b/manage-domains.sh new file mode 100755 index 0000000..abdad1e --- /dev/null +++ b/manage-domains.sh @@ -0,0 +1,386 @@ +#!/bin/bash + +# Multiple Domain Management Script for Email Server +# Manage multiple domains, view status, and perform domain operations + +set -euo pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Functions +info() { + echo -e "${BLUE}INFO: $1${NC}" +} + +success() { + echo -e "${GREEN}SUCCESS: $1${NC}" +} + +warning() { + echo -e "${YELLOW}WARNING: $1${NC}" +} + +error() { + echo -e "${RED}ERROR: $1${NC}" >&2 + exit 1 +} + +# Check if running as root +check_root() { + if [[ $EUID -ne 0 ]]; then + error "This script must be run as root" + fi +} + +# List all configured domains +list_domains() { + echo -e "${BLUE}Configured Email Domains${NC}" + echo "========================" + echo + + # Get domains from database + echo -e "${YELLOW}Domains in Database:${NC}" + if sudo -u postgres psql -d postfix -t -c " + SELECT + domain, + CASE WHEN active = '1' THEN 'Active' ELSE 'Inactive' END as status, + aliases || ' aliases' as aliases, + mailboxes || ' mailboxes' as mailboxes, + CASE WHEN maxquota = 0 THEN 'Unlimited' ELSE (maxquota/1048576)::text || ' MB' END as quota + FROM domains + ORDER BY domain;" 2>/dev/null; then + echo + else + echo " Database connection failed" + echo + fi + + # Get domains with DKIM keys + echo -e "${YELLOW}Domains with DKIM Keys:${NC}" + if [[ -d "/etc/opendkim/keys" ]]; then + for domain_dir in /etc/opendkim/keys/*/; do + if [[ -d "$domain_dir" ]]; then + domain=$(basename "$domain_dir") + if [[ -f "$domain_dir/mail.private" ]]; then + echo " $domain (DKIM configured)" + else + echo " $domain (DKIM incomplete)" + fi + fi + done + else + echo " No DKIM directory found" + fi + echo +} + +# Show domain details +show_domain_details() { + local domain=$1 + + echo -e "${BLUE}Domain Details: $domain${NC}" + echo "===============================" + echo + + # Database information + echo -e "${YELLOW}Database Information:${NC}" + sudo -u postgres psql -d postfix -c " + SELECT + domain, + description, + aliases || ' aliases' as aliases, + mailboxes || ' mailboxes' as mailboxes, + CASE WHEN maxquota = 0 THEN 'Unlimited' ELSE (maxquota/1048576)::text || ' MB' END as max_quota, + CASE WHEN active = '1' THEN 'Active' ELSE 'Inactive' END as status, + created, + modified + FROM domains + WHERE domain = '$domain';" 2>/dev/null || echo "Domain not found in database" + + echo + + # Mailboxes for this domain + echo -e "${YELLOW}Mailboxes:${NC}" + sudo -u postgres psql -d postfix -c " + SELECT + username, + name, + CASE WHEN quota = 0 THEN 'Unlimited' ELSE (quota/1048576)::text || ' MB' END as quota, + CASE WHEN active = '1' THEN 'Active' ELSE 'Inactive' END as status, + created + FROM mailbox + WHERE domain = '$domain' + ORDER BY username;" 2>/dev/null || echo "No mailboxes found" + + echo + + # Aliases for this domain + echo -e "${YELLOW}Aliases:${NC}" + sudo -u postgres psql -d postfix -c " + SELECT + address, + goto, + CASE WHEN active = '1' THEN 'Active' ELSE 'Inactive' END as status, + created + FROM aliases + WHERE domain = '$domain' + ORDER BY address;" 2>/dev/null || echo "No aliases found" + + echo + + # DKIM status + echo -e "${YELLOW}DKIM Status:${NC}" + if [[ -d "/etc/opendkim/keys/$domain" ]]; then + if [[ -f "/etc/opendkim/keys/$domain/mail.private" && -f "/etc/opendkim/keys/$domain/mail.txt" ]]; then + echo " ✓ DKIM keys exist" + + # Test DKIM + if opendkim-testkey -d "$domain" -s mail 2>/dev/null | grep -q "key OK"; then + echo " ✓ DKIM test passed" + else + echo " ✗ DKIM test failed (check DNS record)" + fi + + echo + echo " DKIM DNS Record:" + echo " Record name: mail._domainkey.$domain" + echo " Record type: TXT" + echo " Record value:" + cat "/etc/opendkim/keys/$domain/mail.txt" 2>/dev/null | sed 's/^/ /' + else + echo " ✗ DKIM keys incomplete" + fi + else + echo " ✗ No DKIM configuration found" + fi + echo +} + +# Test domain configuration +test_domain() { + local domain=$1 + + echo -e "${BLUE}Testing Domain: $domain${NC}" + echo "=========================" + echo + + # DNS tests + echo -e "${YELLOW}DNS Configuration:${NC}" + + # MX record + if dig +short MX "$domain" | grep -q .; then + mx_record=$(dig +short MX "$domain" | head -1) + echo " ✓ MX record: $mx_record" + else + echo " ✗ MX record not found" + fi + + # SPF record + if dig +short TXT "$domain" | grep -q "v=spf1"; then + echo " ✓ SPF record found" + else + echo " ✗ SPF record not found" + fi + + # DMARC record + if dig +short TXT "_dmarc.$domain" | grep -q "v=DMARC1"; then + echo " ✓ DMARC record found" + else + echo " ✗ DMARC record not found" + fi + + # DKIM record + if dig +short TXT "mail._domainkey.$domain" | grep -q "v=DKIM1"; then + echo " ✓ DKIM record found" + + # Test DKIM key + if opendkim-testkey -d "$domain" -s mail 2>/dev/null | grep -q "key OK"; then + echo " ✓ DKIM key validation passed" + else + echo " ✗ DKIM key validation failed" + fi + else + echo " ✗ DKIM record not found" + fi + + echo + + # Database test + echo -e "${YELLOW}Database Status:${NC}" + if sudo -u postgres psql -d postfix -t -c "SELECT 1 FROM domains WHERE domain = '$domain' AND active = '1';" 2>/dev/null | grep -q 1; then + echo " ✓ Domain is active in database" + else + echo " ✗ Domain not found or inactive in database" + fi + + echo +} + +# Remove domain +remove_domain() { + local domain=$1 + + echo -e "${YELLOW}WARNING: This will remove all data for domain $domain${NC}" + echo "This includes:" + echo " - All mailboxes and their emails" + echo " - All aliases" + echo " - DKIM keys" + echo " - Database entries" + echo + read -p "Are you sure you want to continue? Type 'DELETE' to confirm: " confirm + + if [[ "$confirm" != "DELETE" ]]; then + echo "Operation cancelled" + return + fi + + info "Removing domain: $domain" + + # Remove from database + sudo -u postgres psql -d postfix -c "DELETE FROM aliases WHERE domain = '$domain';" 2>/dev/null + sudo -u postgres psql -d postfix -c "DELETE FROM mailbox WHERE domain = '$domain';" 2>/dev/null + sudo -u postgres psql -d postfix -c "DELETE FROM domain_admins WHERE domain = '$domain';" 2>/dev/null + sudo -u postgres psql -d postfix -c "DELETE FROM domains WHERE domain = '$domain';" 2>/dev/null + + # Remove DKIM configuration + if [[ -d "/etc/opendkim/keys/$domain" ]]; then + rm -rf "/etc/opendkim/keys/$domain" + fi + + # Remove from OpenDKIM config files + sed -i "/mail\._domainkey\.$domain/d" /etc/opendkim/key.table 2>/dev/null || true + sed -i "/*@$domain/d" /etc/opendkim/signing.table 2>/dev/null || true + sed -i "/^$domain$/d" /etc/opendkim/trusted.hosts 2>/dev/null || true + + # Remove mail data + if [[ -d "/var/mail/vhosts/$domain" ]]; then + warning "Removing mail data for $domain" + rm -rf "/var/mail/vhosts/$domain" + fi + + # Restart OpenDKIM + systemctl restart opendkim + + success "Domain $domain removed successfully" +} + +# Show usage +show_usage() { + echo "Multiple Domain Management Script" + echo "================================" + echo + echo "Usage: $0 [options]" + echo + echo "Commands:" + echo " list - List all configured domains" + echo " show - Show detailed information for a domain" + echo " test - Test domain configuration (DNS, DKIM, etc.)" + echo " add - Add a new domain (calls add-domain.sh)" + echo " remove - Remove a domain and all its data" + echo " status - Show overall email server status" + echo + echo "Examples:" + echo " $0 list" + echo " $0 show example.com" + echo " $0 test example.com" + echo " $0 add newdomain.com" + echo " $0 remove olddomain.com" + echo +} + +# Show server status +show_status() { + echo -e "${BLUE}Email Server Status${NC}" + echo "===================" + echo + + # Service status + echo -e "${YELLOW}Service Status:${NC}" + services=("postfix" "dovecot" "amavis" "spamassassin" "clamav-daemon" "opendkim" "apache2" "postgresql") + + for service in "${services[@]}"; do + if systemctl is-active --quiet "$service"; then + echo " ✓ $service is running" + else + echo " ✗ $service is not running" + fi + done + echo + + # Domain summary + echo -e "${YELLOW}Domain Summary:${NC}" + total_domains=$(sudo -u postgres psql -d postfix -t -c "SELECT COUNT(*) FROM domains;" 2>/dev/null | xargs || echo "0") + active_domains=$(sudo -u postgres psql -d postfix -t -c "SELECT COUNT(*) FROM domains WHERE active = '1';" 2>/dev/null | xargs || echo "0") + total_mailboxes=$(sudo -u postgres psql -d postfix -t -c "SELECT COUNT(*) FROM mailbox;" 2>/dev/null | xargs || echo "0") + active_mailboxes=$(sudo -u postgres psql -d postfix -t -c "SELECT COUNT(*) FROM mailbox WHERE active = '1';" 2>/dev/null | xargs || echo "0") + + echo " Total domains: $total_domains" + echo " Active domains: $active_domains" + echo " Total mailboxes: $total_mailboxes" + echo " Active mailboxes: $active_mailboxes" + echo + + # Mail queue + echo -e "${YELLOW}Mail Queue:${NC}" + queue_count=$(mailq 2>/dev/null | tail -1 | awk '{print $5}' || echo "0") + if [[ "$queue_count" == "empty" ]]; then + echo " Mail queue is empty" + else + echo " Mail queue has $queue_count messages" + fi + echo +} + +# Main logic +case "${1:-}" in + "list") + check_root + list_domains + ;; + "show") + if [[ -z "${2:-}" ]]; then + error "Domain name required. Usage: $0 show " + fi + check_root + show_domain_details "$2" + ;; + "test") + if [[ -z "${2:-}" ]]; then + error "Domain name required. Usage: $0 test " + fi + test_domain "$2" + ;; + "add") + if [[ -z "${2:-}" ]]; then + error "Domain name required. Usage: $0 add " + fi + check_root + if [[ -f "./add-domain.sh" ]]; then + ./add-domain.sh "$2" + else + error "add-domain.sh script not found in current directory" + fi + ;; + "remove") + if [[ -z "${2:-}" ]]; then + error "Domain name required. Usage: $0 remove " + fi + check_root + remove_domain "$2" + ;; + "status") + check_root + show_status + ;; + "--help"|"-h"|"help"|"") + show_usage + ;; + *) + error "Unknown command: $1. Use --help for usage information." + ;; +esac diff --git a/setup-email-server.sh b/setup-email-server.sh new file mode 100755 index 0000000..e2e2e13 --- /dev/null +++ b/setup-email-server.sh @@ -0,0 +1,921 @@ +#!/bin/bash + +# Email Server Setup Script +# Components: Postfix, Dovecot, Amavis, SpamAssassin, OpenDKIM, PostgreSQL, PostfixAdmin +# SSL: Let's Encrypt +# Author: Email Server Setup Script +# Date: $(date) + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration variables +DOMAIN="" +HOSTNAME="" +ADMIN_EMAIL="" +DB_NAME="postfix" +DB_USER="postfix" +DB_PASSWORD="" +POSTFIXADMIN_PASSWORD="" +WEBROOT="/var/www/postfixadmin" + +# Logging +LOG_FILE="/var/log/email-server-setup.log" + +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" +} + +error() { + echo -e "${RED}ERROR: $1${NC}" >&2 + log "ERROR: $1" + exit 1 +} + +info() { + echo -e "${BLUE}INFO: $1${NC}" + log "INFO: $1" +} + +success() { + echo -e "${GREEN}SUCCESS: $1${NC}" + log "SUCCESS: $1" +} + +warning() { + echo -e "${YELLOW}WARNING: $1${NC}" + log "WARNING: $1" +} + +# Check if running as root +check_root() { + if [[ $EUID -ne 0 ]]; then + error "This script must be run as root" + fi +} + +# Get user input +get_configuration() { + echo -e "${BLUE}Email Server Configuration${NC}" + echo "=================================" + + while [[ -z "$DOMAIN" ]]; do + read -p "Enter your domain name (e.g., example.com): " DOMAIN + done + + while [[ -z "$HOSTNAME" ]]; do + read -p "Enter your hostname (e.g., mail.example.com): " HOSTNAME + done + + while [[ -z "$ADMIN_EMAIL" ]]; do + read -p "Enter admin email address: " ADMIN_EMAIL + done + + while [[ -z "$DB_PASSWORD" ]]; do + read -s -p "Enter PostgreSQL password for postfix user: " DB_PASSWORD + echo + done + + while [[ -z "$POSTFIXADMIN_PASSWORD" ]]; do + read -s -p "Enter PostfixAdmin setup password: " POSTFIXADMIN_PASSWORD + echo + done + + info "Configuration completed" +} + +# Update system +update_system() { + info "Updating system packages..." + apt update && apt upgrade -y + success "System updated" +} + +# Install required packages +install_packages() { + info "Installing required packages..." + + # Install basic packages + apt install -y \ + curl \ + wget \ + gnupg \ + lsb-release \ + software-properties-common \ + certbot \ + nginx \ + ufw + + # Install PostgreSQL + apt install -y postgresql postgresql-contrib + + # Install Postfix and related packages + debconf-set-selections <<< "postfix postfix/mailname string $HOSTNAME" + debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Internet Site'" + apt install -y \ + postfix \ + postfix-pgsql \ + postfix-policyd-spf-python + + # Install Dovecot + apt install -y \ + dovecot-core \ + dovecot-imapd \ + dovecot-pop3d \ + dovecot-lmtpd \ + dovecot-pgsql + + # Install Amavis and SpamAssassin + apt install -y \ + amavisd-new \ + spamassassin \ + clamav \ + clamav-daemon \ + clamav-freshclam \ + clamav-unofficial-sigs + + # Install OpenDKIM + apt install -y opendkim opendkim-tools + + # Install PHP and Apache for PostfixAdmin + apt install -y \ + apache2 \ + php \ + php-fpm \ + php-cli \ + php-pgsql \ + php-mbstring \ + php-xml \ + php-curl \ + php-zip \ + php-gd + + success "All packages installed" +} + +# Configure PostgreSQL +configure_postgresql() { + info "Configuring PostgreSQL..." + + # Start PostgreSQL + systemctl start postgresql + systemctl enable postgresql + + # Create database and user + sudo -u postgres psql -c "CREATE DATABASE $DB_NAME;" + sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASSWORD';" + sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" + + # Create tables for Postfix + sudo -u postgres psql -d "$DB_NAME" << 'EOF' +CREATE TABLE domains ( + domain varchar(50) NOT NULL, + description varchar(255), + aliases int NOT NULL default '0', + mailboxes int NOT NULL default '0', + maxquota bigint NOT NULL default '0', + quota bigint NOT NULL default '0', + transport varchar(255) default NULL, + backupmx boolean NOT NULL default '0', + created timestamp NOT NULL default now(), + modified timestamp NOT NULL default now(), + active boolean NOT NULL default '1', + PRIMARY KEY (domain) +); + +CREATE TABLE aliases ( + address varchar(255) NOT NULL, + goto text NOT NULL, + domain varchar(255) NOT NULL, + created timestamp NOT NULL default now(), + modified timestamp NOT NULL default now(), + active boolean NOT NULL default '1', + PRIMARY KEY (address) +); + +CREATE TABLE mailbox ( + username varchar(255) NOT NULL, + password varchar(255) NOT NULL, + name varchar(255) NOT NULL default '', + maildir varchar(255) NOT NULL default '', + quota bigint NOT NULL default '0', + local_part varchar(255) NOT NULL, + domain varchar(255) NOT NULL, + created timestamp NOT NULL default now(), + modified timestamp NOT NULL default now(), + active boolean NOT NULL default '1', + PRIMARY KEY (username) +); + +CREATE TABLE domain_admins ( + username varchar(255) NOT NULL, + domain varchar(255) NOT NULL, + created timestamp NOT NULL default now(), + active boolean NOT NULL default '1', + PRIMARY KEY (username, domain) +); + +CREATE TABLE admin ( + username varchar(255) NOT NULL, + password varchar(255) NOT NULL, + created timestamp NOT NULL default now(), + modified timestamp NOT NULL default now(), + active boolean NOT NULL default '1', + PRIMARY KEY (username) +); + +CREATE TABLE log ( + timestamp timestamp NOT NULL default now(), + username varchar(255) NOT NULL default '', + domain varchar(255) NOT NULL default '', + action varchar(255) NOT NULL default '', + data text NOT NULL default '' +); + +CREATE TABLE vacation ( + email varchar(255) NOT NULL, + subject varchar(255) NOT NULL, + body text NOT NULL, + cache text NOT NULL default '', + domain varchar(255) NOT NULL, + created timestamp NOT NULL default now(), + active boolean NOT NULL default '1', + PRIMARY KEY (email) +); + +CREATE TABLE quota ( + username varchar(255) NOT NULL, + path varchar(100) NOT NULL, + current bigint NOT NULL default 0, + PRIMARY KEY (username, path) +); + +CREATE TABLE quota2 ( + username varchar(100) NOT NULL, + bytes bigint NOT NULL default 0, + messages int NOT NULL default 0, + PRIMARY KEY (username) +); +EOF + + # Grant permissions + sudo -u postgres psql -d "$DB_NAME" -c "GRANT SELECT ON ALL TABLES IN SCHEMA public TO $DB_USER;" + sudo -u postgres psql -d "$DB_NAME" -c "GRANT INSERT, UPDATE, DELETE ON aliases, mailbox, domain_admins, domains TO $DB_USER;" + sudo -u postgres psql -d "$DB_NAME" -c "GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO $DB_USER;" + + # Insert the primary domain + sudo -u postgres psql -d "$DB_NAME" -c "INSERT INTO domains (domain, description, active) VALUES ('$DOMAIN', 'Primary domain configured during setup', '1') ON CONFLICT (domain) DO NOTHING;" + + success "PostgreSQL configured" +} + +# Configure Postfix +configure_postfix() { + info "Configuring Postfix..." + + # Backup original configuration + cp /etc/postfix/main.cf /etc/postfix/main.cf.backup + + # Create main.cf + cat > /etc/postfix/main.cf << EOF +# Basic configuration +myhostname = $HOSTNAME +mydomain = $DOMAIN +myorigin = \$mydomain +inet_interfaces = all +inet_protocols = ipv4 +mydestination = localhost + +# Virtual domains +virtual_mailbox_domains = pgsql:/etc/postfix/pgsql-virtual-mailbox-domains.cf +virtual_mailbox_maps = pgsql:/etc/postfix/pgsql-virtual-mailbox-maps.cf +virtual_alias_maps = pgsql:/etc/postfix/pgsql-virtual-alias-maps.cf +virtual_mailbox_base = /var/mail/vhosts +virtual_minimum_uid = 100 +virtual_uid_maps = static:5000 +virtual_gid_maps = static:5000 + +# SSL/TLS configuration +smtpd_tls_cert_file = /etc/letsencrypt/live/$HOSTNAME/fullchain.pem +smtpd_tls_key_file = /etc/letsencrypt/live/$HOSTNAME/privkey.pem +smtpd_tls_security_level = may +smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 +smtp_tls_security_level = may +smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 + +# SASL authentication +smtpd_sasl_type = dovecot +smtpd_sasl_path = private/auth +smtpd_sasl_auth_enable = yes + +# Restrictions +smtpd_helo_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_pipelining, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname +smtpd_sender_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_sender, reject_unknown_sender_domain +smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unknown_recipient_domain, reject_non_fqdn_recipient +smtpd_data_restrictions = reject_unauth_pipelining + +# Content filter (Amavis) +content_filter = smtp-amavis:[127.0.0.1]:10024 + +# Milters (OpenDKIM) +milter_protocol = 2 +milter_default_action = accept +smtpd_milters = inet:localhost:8891 +non_smtpd_milters = inet:localhost:8891 + +# Miscellaneous +message_size_limit = 52428800 +mailbox_size_limit = 0 +biff = no +append_dot_mydomain = no +readme_directory = no +compatibility_level = 2 +EOF + + # Create PostgreSQL lookup files + cat > /etc/postfix/pgsql-virtual-mailbox-domains.cf << EOF +user = $DB_USER +password = $DB_PASSWORD +hosts = localhost +dbname = $DB_NAME +query = SELECT 1 FROM domains WHERE domain='%s' AND active='1' +EOF + + cat > /etc/postfix/pgsql-virtual-mailbox-maps.cf << EOF +user = $DB_USER +password = $DB_PASSWORD +hosts = localhost +dbname = $DB_NAME +query = SELECT 1 FROM mailbox WHERE username='%s' AND active='1' +EOF + + cat > /etc/postfix/pgsql-virtual-alias-maps.cf << EOF +user = $DB_USER +password = $DB_PASSWORD +hosts = localhost +dbname = $DB_NAME +query = SELECT goto FROM aliases WHERE address='%s' AND active='1' +EOF + + # Set permissions + chmod 640 /etc/postfix/pgsql-*.cf + chown root:postfix /etc/postfix/pgsql-*.cf + + # Configure master.cf for submission and smtps + cat >> /etc/postfix/master.cf << EOF + +# Submission port 587 +submission inet n - y - - smtpd + -o syslog_name=postfix/submission + -o smtpd_tls_security_level=encrypt + -o smtpd_sasl_auth_enable=yes + -o smtpd_sasl_type=dovecot + -o smtpd_sasl_path=private/auth + -o smtpd_reject_unlisted_recipient=no + -o smtpd_client_restrictions=permit_sasl_authenticated,reject + -o milter_macro_daemon_name=ORIGINATING + +# SMTPS port 465 +smtps inet n - y - - smtpd + -o syslog_name=postfix/smtps + -o smtpd_tls_wrappermode=yes + -o smtpd_sasl_auth_enable=yes + -o smtpd_sasl_type=dovecot + -o smtpd_sasl_path=private/auth + -o smtpd_client_restrictions=permit_sasl_authenticated,reject + -o milter_macro_daemon_name=ORIGINATING + +# Amavis +smtp-amavis unix - - - - 2 smtp + -o smtp_data_done_timeout=1200 + -o smtp_send_xforward_command=yes + -o disable_dns_lookups=yes + -o max_use=20 + +127.0.0.1:10025 inet n - - - - smtpd + -o content_filter= + -o local_recipient_maps= + -o relay_recipient_maps= + -o smtpd_restriction_classes= + -o smtpd_delay_reject=no + -o smtpd_client_restrictions=permit_mynetworks,reject + -o smtpd_helo_restrictions= + -o smtpd_sender_restrictions= + -o smtpd_recipient_restrictions=permit_mynetworks,reject + -o smtpd_data_restrictions=reject_unauth_pipelining + -o smtpd_end_of_data_restrictions= + -o mynetworks=127.0.0.0/8 + -o smtpd_error_sleep_time=0 + -o smtpd_soft_error_limit=1001 + -o smtpd_hard_error_limit=1000 + -o smtpd_client_connection_count_limit=0 + -o smtpd_client_connection_rate_limit=0 + -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks +EOF + + # Create virtual mailbox directory + mkdir -p /var/mail/vhosts + groupadd -g 5000 vmail 2>/dev/null || true + useradd -g vmail -u 5000 vmail -d /var/mail/vhosts -m 2>/dev/null || true + chown -R vmail:vmail /var/mail/vhosts + + success "Postfix configured" +} + +# Configure Dovecot +configure_dovecot() { + info "Configuring Dovecot..." + + # Backup configuration + cp -r /etc/dovecot /etc/dovecot.backup + + # Main configuration + cat > /etc/dovecot/dovecot.conf << EOF +!include_try /usr/share/dovecot/protocols.d/*.protocol +protocols = imap pop3 lmtp +!include conf.d/*.conf +!include_try local.conf +EOF + + # 10-mail.conf + cat > /etc/dovecot/conf.d/10-mail.conf << EOF +mail_location = maildir:/var/mail/vhosts/%d/%n +namespace inbox { + inbox = yes +} +mail_privileged_group = mail +mail_uid = vmail +mail_gid = vmail +first_valid_uid = 5000 +last_valid_uid = 5000 +first_valid_gid = 5000 +last_valid_gid = 5000 +EOF + + # 10-auth.conf + cat > /etc/dovecot/conf.d/10-auth.conf << EOF +disable_plaintext_auth = yes +auth_mechanisms = plain login +!include auth-sql.conf.ext +EOF + + # auth-sql.conf.ext + cat > /etc/dovecot/conf.d/auth-sql.conf.ext << EOF +passdb { + driver = sql + args = /etc/dovecot/dovecot-sql.conf.ext +} +userdb { + driver = static + args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n +} +EOF + + # dovecot-sql.conf.ext + cat > /etc/dovecot/dovecot-sql.conf.ext << EOF +driver = pgsql +connect = host=localhost dbname=$DB_NAME user=$DB_USER password=$DB_PASSWORD +default_pass_scheme = SHA512-CRYPT +password_query = SELECT username as user, password FROM mailbox WHERE username='%u' AND active='1' +EOF + + # 10-master.conf + cat > /etc/dovecot/conf.d/10-master.conf << EOF +service imap-login { + inet_listener imap { + port = 143 + } + inet_listener imaps { + port = 993 + ssl = yes + } +} + +service pop3-login { + inet_listener pop3 { + port = 110 + } + inet_listener pop3s { + port = 995 + ssl = yes + } +} + +service lmtp { + unix_listener /var/spool/postfix/private/dovecot-lmtp { + mode = 0600 + user = postfix + group = postfix + } +} + +service auth { + unix_listener /var/spool/postfix/private/auth { + mode = 0666 + user = postfix + group = postfix + } + unix_listener auth-userdb { + mode = 0600 + user = vmail + group = vmail + } + user = dovecot +} + +service auth-worker { + user = vmail +} +EOF + + # 10-ssl.conf + cat > /etc/dovecot/conf.d/10-ssl.conf << EOF +ssl = required +ssl_cert = /etc/opendkim.conf << EOF +Syslog yes +UMask 007 +Socket inet:8891@localhost +PidFile /var/run/opendkim/opendkim.pid +OversignHeaders From +TrustAnchorFile /usr/share/dns/root.key +UserID opendkim +KeyTable /etc/opendkim/key.table +SigningTable /etc/opendkim/signing.table +ExternalIgnoreList /etc/opendkim/trusted.hosts +InternalHosts /etc/opendkim/trusted.hosts +EOF + + # Key table + echo "mail._domainkey.$DOMAIN $DOMAIN:mail:/etc/opendkim/keys/$DOMAIN/mail.private" > /etc/opendkim/key.table + + # Signing table + echo "*@$DOMAIN mail._domainkey.$DOMAIN" > /etc/opendkim/signing.table + + # Trusted hosts + cat > /etc/opendkim/trusted.hosts << EOF +127.0.0.1 +localhost +$HOSTNAME +$DOMAIN +EOF + + success "OpenDKIM configured" + + # Display DNS record + info "Add this TXT record to your DNS:" + echo "mail._domainkey.$DOMAIN" + cat /etc/opendkim/keys/$DOMAIN/mail.txt +} + +# Configure Amavis and SpamAssassin +configure_amavis() { + info "Configuring Amavis and SpamAssassin..." + + # Update ClamAV signatures + freshclam + + # Configure Amavis + cat > /etc/amavis/conf.d/15-content_filter_mode << 'EOF' +use strict; +@bypass_virus_checks_maps = ( + \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re); +@bypass_spam_checks_maps = ( + \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re); +1; # ensure a defined return +EOF + + cat > /etc/amavis/conf.d/20-debian_defaults << EOF +use strict; +\$MYHOME = '/var/lib/amavis'; +\$mydomain = '$DOMAIN'; +\$myhostname = '$HOSTNAME'; +\$max_servers = 3; +\$daemon_user = 'amavis'; +\$daemon_group = 'amavis'; +\$inet_socket_port = 10024; +\$policy_bank{'MYNETS'} = { + originating => 1, + os_fingerprint_method => undef, +}; +\$interface_policy{'10024'} = 'ORIGINATING'; +\$policy_bank{'ORIGINATING'} = { + originating => 1, + allow_disclaimers => 1, + virus_admin_maps => ["virusalert\\\@\$mydomain"], + spam_admin_maps => ["virusalert\\\@\$mydomain"], + warnbadhsender => 1, + forward_method => 'smtp:[127.0.0.1]:10025', + smtpd_discard_ehlo_keywords => ['8BITMIME'], + bypass_banned_checks_maps => [1], + terminate_dsn_on_notify_success => 0, +}; +\$sa_tag_level_deflt = 2.0; +\$sa_tag2_level_deflt = 6.0; +\$sa_kill_level_deflt = 6.9; +\$sa_dsn_cutoff_level = 10; +\$penpals_bonus_score = 8; +\$penpals_threshold_high = \$sa_kill_level_deflt; +\$bounce_killer_score = 100; +\$virus_admin = "postmaster\\\@\$mydomain"; +\$mailfrom_notify_admin = "postmaster\\\@\$mydomain"; +\$mailfrom_notify_recip = "postmaster\\\@\$mydomain"; +\$mailfrom_notify_spamadmin = "postmaster\\\@\$mydomain"; +\$mailfrom_to_quarantine = ''; +\@lookup_sql_dsn = ( ['DBI:Pg:database=$DB_NAME;host=127.0.0.1;port=5432', '$DB_USER', '$DB_PASSWORD'] ); +\$enable_db = 1; +\$enable_global_cache = 1; +\$inet_socket_bind = '127.0.0.1'; +@inet_acl = qw(127.0.0.1); +1; +EOF + + # Configure SpamAssassin + cat > /etc/spamassassin/local.cf << EOF +rewrite_header Subject *****SPAM***** +report_safe 0 +required_score 5.0 +use_bayes 1 +use_bayes_rules 1 +bayes_auto_learn 1 +skip_rbl_checks 0 +use_razor2 1 +use_dcc 1 +use_pyzor 1 +EOF + + # Add amavis user to clamav group + usermod -a -G clamav amavis + + success "Amavis and SpamAssassin configured" +} + +# Get Let's Encrypt certificates +get_ssl_certificates() { + info "Obtaining Let's Encrypt certificates..." + + # Stop services that might be using port 80 + systemctl stop apache2 2>/dev/null || true + systemctl stop nginx 2>/dev/null || true + + # Get certificate + certbot certonly --standalone -d $HOSTNAME --email $ADMIN_EMAIL --agree-tos --non-interactive + + if [[ ! -f "/etc/letsencrypt/live/$HOSTNAME/fullchain.pem" ]]; then + error "Failed to obtain SSL certificate" + fi + + # Set up auto-renewal + (crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab - + + success "SSL certificates obtained" +} + +# Install PostfixAdmin +install_postfixadmin() { + info "Installing PostfixAdmin..." + + # Download PostfixAdmin + cd /tmp + wget https://github.com/postfixadmin/postfixadmin/archive/postfixadmin-3.3.11.tar.gz + tar -xzf postfixadmin-3.3.11.tar.gz + mv postfixadmin-postfixadmin-3.3.11 $WEBROOT + + # Set permissions + chown -R www-data:www-data $WEBROOT + chmod -R 755 $WEBROOT + + # Create PostfixAdmin configuration + cat > $WEBROOT/config.local.php << EOF + 'abuse@change-this-to-your.domain.tld', + 'hostmaster' => 'hostmaster@change-this-to-your.domain.tld', + 'postmaster' => 'postmaster@change-this-to-your.domain.tld', + 'webmaster' => 'webmaster@change-this-to-your.domain.tld' +); +\$CONF['domain_path'] = 'YES'; +\$CONF['domain_in_mailbox'] = 'YES'; +\$CONF['maildir_name_hook'] = 'maildir_name_hook'; +function maildir_name_hook(\$domain, \$user) { + return \$domain . '/' . \$user . '/'; +} +?> +EOF + + # Configure Apache virtual host + cat > /etc/apache2/sites-available/postfixadmin.conf << EOF + + ServerName $HOSTNAME + Redirect permanent / https://$HOSTNAME/ + + + + ServerName $HOSTNAME + DocumentRoot $WEBROOT/public + + SSLEngine on + SSLCertificateFile /etc/letsencrypt/live/$HOSTNAME/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/$HOSTNAME/privkey.pem + + + Options -Indexes + AllowOverride All + Require all granted + + + ErrorLog \${APACHE_LOG_DIR}/postfixadmin_error.log + CustomLog \${APACHE_LOG_DIR}/postfixadmin_access.log combined + +EOF + + # Enable Apache modules and site + a2enmod ssl + a2enmod rewrite + a2ensite postfixadmin + a2dissite 000-default + + success "PostfixAdmin installed" +} + +# Configure firewall +configure_firewall() { + info "Configuring firewall..." + + # Reset UFW + ufw --force reset + + # Default policies + ufw default deny incoming + ufw default allow outgoing + + # Allow SSH + ufw allow ssh + + # Allow email ports + ufw allow 25/tcp # SMTP + ufw allow 587/tcp # Submission + ufw allow 465/tcp # SMTPS + ufw allow 110/tcp # POP3 + ufw allow 995/tcp # POP3S + ufw allow 143/tcp # IMAP + ufw allow 993/tcp # IMAPS + + # Allow web ports + ufw allow 80/tcp # HTTP + ufw allow 443/tcp # HTTPS + + # Enable UFW + ufw --force enable + + success "Firewall configured" +} + +# Start and enable services +start_services() { + info "Starting and enabling services..." + + # Reload systemd + systemctl daemon-reload + + # Start and enable services + systemctl enable --now postgresql + systemctl enable --now postfix + systemctl enable --now dovecot + systemctl enable --now amavis + systemctl enable --now spamassassin + systemctl enable --now clamav-daemon + systemctl enable --now clamav-freshclam + systemctl enable --now opendkim + systemctl enable --now apache2 + + success "All services started and enabled" +} + +# Display final information +display_final_info() { + success "Email server setup completed!" + echo + echo -e "${BLUE}=== Setup Summary ===${NC}" + echo "Primary Domain: $DOMAIN" + echo "Hostname: $HOSTNAME" + echo "Admin Email: $ADMIN_EMAIL" + echo "PostfixAdmin URL: https://$HOSTNAME/postfixadmin/" + echo + echo -e "${YELLOW}=== DNS Records to Add for $DOMAIN ===${NC}" + echo "A $HOSTNAME [Your server IP]" + echo "MX $DOMAIN $HOSTNAME" + echo "TXT $DOMAIN \"v=spf1 mx ~all\"" + echo "TXT _dmarc.$DOMAIN \"v=DMARC1; p=none; rua=mailto:dmarc@$DOMAIN\"" + echo + echo -e "${YELLOW}=== DKIM DNS Record for $DOMAIN ===${NC}" + cat /etc/opendkim/keys/$DOMAIN/mail.txt + echo + echo -e "${YELLOW}=== Next Steps ===${NC}" + echo "1. Add the DNS records shown above" + echo "2. Visit https://$HOSTNAME/postfixadmin/setup.php to complete PostfixAdmin setup" + echo "3. Create your first domain and mailbox in PostfixAdmin" + echo "4. Test email sending and receiving" + echo + echo -e "${BLUE}=== Multiple Domain Support ===${NC}" + echo "This server supports unlimited virtual domains!" + echo + echo "To add additional domains:" + echo "1. Run: ./add-domain.sh newdomain.com" + echo "2. Add the provided DNS records" + echo "3. Configure the domain in PostfixAdmin" + echo "4. Create mailboxes for the new domain" + echo + echo "Each domain can have its own:" + echo " • Independent mailboxes and aliases" + echo " • DKIM signing for authentication" + echo " • Custom quotas and limits" + echo " • Application SMTP access" + echo + echo -e "${BLUE}=== Application SMTP Configuration ===${NC}" + echo "Your applications can send email using these settings:" + echo " Host: $HOSTNAME" + echo " Port: 587 (STARTTLS) or 465 (SSL/TLS)" + echo " Username: mailbox@yourdomain.com (full email address)" + echo " Password: [mailbox password from PostfixAdmin]" + echo " Security: STARTTLS or SSL/TLS" + echo + echo -e "${GREEN}Setup completed successfully!${NC}" +} + +# Main execution +main() { + echo -e "${BLUE}Email Server Setup Script${NC}" + echo "=========================" + echo + + check_root + get_configuration + update_system + install_packages + configure_postgresql + get_ssl_certificates + configure_postfix + configure_dovecot + configure_opendkim + configure_amavis + install_postfixadmin + configure_firewall + start_services + display_final_info +} + +# Run main function +main "$@" diff --git a/test-email-server.sh b/test-email-server.sh new file mode 100755 index 0000000..3eff267 --- /dev/null +++ b/test-email-server.sh @@ -0,0 +1,262 @@ +#!/bin/bash + +# Email Server Test Script +# This script helps test various components of your email server + +set -euo pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Configuration +DOMAIN="" +HOSTNAME="" +TEST_EMAIL="" + +# Get configuration +get_config() { + if [[ -z "$DOMAIN" ]]; then + read -p "Enter your domain (e.g., example.com): " DOMAIN + fi + if [[ -z "$HOSTNAME" ]]; then + read -p "Enter your hostname (e.g., mail.example.com): " HOSTNAME + fi + if [[ -z "$TEST_EMAIL" ]]; then + read -p "Enter test email address: " TEST_EMAIL + fi +} + +# Test functions +test_dns() { + echo -e "${BLUE}Testing DNS Configuration...${NC}" + + echo "Checking MX record for $DOMAIN:" + if dig +short MX $DOMAIN | grep -q $HOSTNAME; then + echo -e "${GREEN}✓ MX record found${NC}" + else + echo -e "${RED}✗ MX record not found or incorrect${NC}" + fi + + echo "Checking A record for $HOSTNAME:" + if dig +short A $HOSTNAME | grep -q .; then + echo -e "${GREEN}✓ A record found${NC}" + else + echo -e "${RED}✗ A record not found${NC}" + fi + + echo "Checking SPF record for $DOMAIN:" + if dig +short TXT $DOMAIN | grep -q "v=spf1"; then + echo -e "${GREEN}✓ SPF record found${NC}" + else + echo -e "${RED}✗ SPF record not found${NC}" + fi + + echo "Checking DKIM record for $DOMAIN:" + if dig +short TXT mail._domainkey.$DOMAIN | grep -q "v=DKIM1"; then + echo -e "${GREEN}✓ DKIM record found${NC}" + else + echo -e "${RED}✗ DKIM record not found${NC}" + fi + + echo "Checking DMARC record for $DOMAIN:" + if dig +short TXT _dmarc.$DOMAIN | grep -q "v=DMARC1"; then + echo -e "${GREEN}✓ DMARC record found${NC}" + else + echo -e "${RED}✗ DMARC record not found${NC}" + fi + echo +} + +test_services() { + echo -e "${BLUE}Testing Service Status...${NC}" + + services=("postfix" "dovecot" "amavis" "spamassassin" "clamav-daemon" "opendkim" "apache2" "postgresql") + + for service in "${services[@]}"; do + if systemctl is-active --quiet $service; then + echo -e "${GREEN}✓ $service is running${NC}" + else + echo -e "${RED}✗ $service is not running${NC}" + fi + done + echo +} + +test_ports() { + echo -e "${BLUE}Testing Port Connectivity...${NC}" + + ports=("25:SMTP" "587:Submission" "465:SMTPS" "143:IMAP" "993:IMAPS" "110:POP3" "995:POP3S") + + for port_info in "${ports[@]}"; do + port=$(echo $port_info | cut -d: -f1) + name=$(echo $port_info | cut -d: -f2) + + if nc -z localhost $port 2>/dev/null; then + echo -e "${GREEN}✓ Port $port ($name) is open${NC}" + else + echo -e "${RED}✗ Port $port ($name) is closed${NC}" + fi + done + echo +} + +test_ssl() { + echo -e "${BLUE}Testing SSL Certificates...${NC}" + + if [[ -f "/etc/letsencrypt/live/$HOSTNAME/fullchain.pem" ]]; then + echo -e "${GREEN}✓ SSL certificate found${NC}" + + # Check certificate validity + if openssl x509 -in /etc/letsencrypt/live/$HOSTNAME/fullchain.pem -noout -checkend 86400; then + echo -e "${GREEN}✓ SSL certificate is valid${NC}" + else + echo -e "${RED}✗ SSL certificate is expired or expiring soon${NC}" + fi + + # Check certificate CN + cn=$(openssl x509 -in /etc/letsencrypt/live/$HOSTNAME/fullchain.pem -noout -subject | grep -o "CN=[^,]*" | cut -d= -f2) + if [[ "$cn" == "$HOSTNAME" ]]; then + echo -e "${GREEN}✓ SSL certificate CN matches hostname${NC}" + else + echo -e "${RED}✗ SSL certificate CN ($cn) doesn't match hostname ($HOSTNAME)${NC}" + fi + else + echo -e "${RED}✗ SSL certificate not found${NC}" + fi + echo +} + +test_dkim() { + echo -e "${BLUE}Testing DKIM Configuration...${NC}" + + # Find all domains with DKIM keys + dkim_domains=() + if [[ -d "/etc/opendkim/keys" ]]; then + while IFS= read -r -d '' domain_dir; do + domain=$(basename "$domain_dir") + dkim_domains+=("$domain") + done < <(find /etc/opendkim/keys -mindepth 1 -maxdepth 1 -type d -print0) + fi + + if [[ ${#dkim_domains[@]} -eq 0 ]]; then + echo -e "${RED}✗ No DKIM domains found${NC}" + return + fi + + for domain in "${dkim_domains[@]}"; do + if opendkim-testkey -d "$domain" -s mail -vvv 2>&1 | grep -q "key OK"; then + echo -e "${GREEN}✓ DKIM key test passed for $domain${NC}" + else + echo -e "${RED}✗ DKIM key test failed for $domain${NC}" + echo " Run: opendkim-testkey -d $domain -s mail -vvv" + fi + done + echo +} + +test_authentication() { + echo -e "${BLUE}Testing SMTP Authentication...${NC}" + + if echo "quit" | telnet localhost 587 2>/dev/null | grep -q "250-AUTH"; then + echo -e "${GREEN}✓ SMTP AUTH is available${NC}" + else + echo -e "${RED}✗ SMTP AUTH is not available${NC}" + fi + echo +} + +test_database() { + echo -e "${BLUE}Testing Database Connection...${NC}" + + if sudo -u postgres psql -d postfix -c "SELECT 1;" >/dev/null 2>&1; then + echo -e "${GREEN}✓ Database connection successful${NC}" + + # Check tables + tables=$(sudo -u postgres psql -d postfix -t -c "SELECT tablename FROM pg_tables WHERE schemaname='public';" | xargs) + if [[ "$tables" == *"domains"* && "$tables" == *"mailbox"* && "$tables" == *"aliases"* ]]; then + echo -e "${GREEN}✓ Required database tables exist${NC}" + + # Check configured domains + domain_count=$(sudo -u postgres psql -d postfix -t -c "SELECT COUNT(*) FROM domains WHERE active='1';" | xargs) + echo -e "${GREEN}✓ Database has $domain_count active domain(s)${NC}" + + if [[ $domain_count -gt 0 ]]; then + echo "Active domains:" + sudo -u postgres psql -d postfix -t -c "SELECT ' - ' || domain FROM domains WHERE active='1';" | grep -v "^$" + fi + else + echo -e "${RED}✗ Required database tables missing${NC}" + fi + else + echo -e "${RED}✗ Database connection failed${NC}" + fi + echo +} + +test_email_flow() { + echo -e "${BLUE}Testing Email Flow...${NC}" + + if [[ -n "$TEST_EMAIL" ]]; then + echo "Sending test email to $TEST_EMAIL..." + if echo "This is a test email from your email server." | mail -s "Email Server Test" $TEST_EMAIL; then + echo -e "${GREEN}✓ Test email sent${NC}" + echo "Check your inbox for the test email" + else + echo -e "${RED}✗ Failed to send test email${NC}" + fi + else + echo -e "${YELLOW}! No test email provided, skipping email flow test${NC}" + fi + echo +} + +check_logs() { + echo -e "${BLUE}Recent Log Entries...${NC}" + + echo "=== Postfix Logs (last 10 lines) ===" + tail -10 /var/log/mail.log | grep postfix || echo "No recent postfix logs" + + echo + echo "=== Dovecot Logs (last 5 lines) ===" + tail -5 /var/log/mail.log | grep dovecot || echo "No recent dovecot logs" + + echo + echo "=== Setup Log (last 5 lines) ===" + if [[ -f "/var/log/email-server-setup.log" ]]; then + tail -5 /var/log/email-server-setup.log + else + echo "Setup log not found" + fi + echo +} + +main() { + echo -e "${BLUE}Email Server Test Suite${NC}" + echo "======================" + echo + + get_config + + test_dns + test_services + test_ports + test_ssl + test_dkim + test_authentication + test_database + test_email_flow + check_logs + + echo -e "${GREEN}Testing completed!${NC}" + echo + echo -e "${YELLOW}Online testing tools:${NC}" + echo "- MX Toolbox: https://mxtoolbox.com/" + echo "- Mail Tester: https://www.mail-tester.com/" + echo "- DKIM Validator: https://dkimvalidator.com/" +} + +main "$@"