This commit is contained in:
Tommy Parnell
2025-08-03 11:36:44 -04:00
commit e9c35bdc8d
9 changed files with 3066 additions and 0 deletions

183
QUICK_START.md Normal file
View File

@@ -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/)

309
README.md Normal file
View File

@@ -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.

329
add-domain.sh Executable file
View File

@@ -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 <domain>"
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 <domain>"
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 "$@"

179
backup-email-server.sh Executable file
View File

@@ -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 "$@"

207
config.template Normal file
View File

@@ -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

290
maintenance-email-server.sh Executable file
View File

@@ -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 "$@"

386
manage-domains.sh Executable file
View File

@@ -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 <command> [options]"
echo
echo "Commands:"
echo " list - List all configured domains"
echo " show <domain> - Show detailed information for a domain"
echo " test <domain> - Test domain configuration (DNS, DKIM, etc.)"
echo " add <domain> - Add a new domain (calls add-domain.sh)"
echo " remove <domain> - 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 <domain>"
fi
check_root
show_domain_details "$2"
;;
"test")
if [[ -z "${2:-}" ]]; then
error "Domain name required. Usage: $0 test <domain>"
fi
test_domain "$2"
;;
"add")
if [[ -z "${2:-}" ]]; then
error "Domain name required. Usage: $0 add <domain>"
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 <domain>"
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

921
setup-email-server.sh Executable file
View File

@@ -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/letsencrypt/live/$HOSTNAME/fullchain.pem
ssl_key = </etc/letsencrypt/live/$HOSTNAME/privkey.pem
ssl_protocols = !SSLv2 !SSLv3 !TLSv1 !TLSv1.1
ssl_cipher_list = ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384
ssl_prefer_server_ciphers = yes
ssl_dh = </etc/dovecot/dh.pem
EOF
# Generate DH parameters
openssl dhparam -out /etc/dovecot/dh.pem 2048
# Set permissions
chmod 600 /etc/dovecot/dovecot-sql.conf.ext
chown vmail:dovecot /etc/dovecot/dovecot-sql.conf.ext
success "Dovecot configured"
}
# Configure OpenDKIM
configure_opendkim() {
info "Configuring OpenDKIM..."
# Create directories
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
chmod 600 /etc/opendkim/keys/$DOMAIN/mail.private
# OpenDKIM configuration
cat > /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
<?php
\$CONF['configured'] = true;
\$CONF['setup_password'] = '$(php -r "echo password_hash('$POSTFIXADMIN_PASSWORD', PASSWORD_DEFAULT);")';
\$CONF['postfix_admin_url'] = 'https://$HOSTNAME/postfixadmin';
\$CONF['postfix_admin_path'] = '$WEBROOT';
\$CONF['default_language'] = 'en';
\$CONF['database_type'] = 'pgsql';
\$CONF['database_host'] = 'localhost';
\$CONF['database_user'] = '$DB_USER';
\$CONF['database_password'] = '$DB_PASSWORD';
\$CONF['database_name'] = '$DB_NAME';
\$CONF['admin_email'] = '$ADMIN_EMAIL';
\$CONF['encrypt'] = 'sha512.crypt';
\$CONF['dovecotpw'] = "/usr/bin/doveadm pw";
\$CONF['page_size'] = '10';
\$CONF['default_aliases'] = array (
'abuse' => '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
<VirtualHost *:80>
ServerName $HOSTNAME
Redirect permanent / https://$HOSTNAME/
</VirtualHost>
<VirtualHost *:443>
ServerName $HOSTNAME
DocumentRoot $WEBROOT/public
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/$HOSTNAME/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/$HOSTNAME/privkey.pem
<Directory $WEBROOT/public>
Options -Indexes
AllowOverride All
Require all granted
</Directory>
ErrorLog \${APACHE_LOG_DIR}/postfixadmin_error.log
CustomLog \${APACHE_LOG_DIR}/postfixadmin_access.log combined
</VirtualHost>
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 "$@"

262
test-email-server.sh Executable file
View File

@@ -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 "$@"