diff --git a/check-postfixadmin-paths.sh b/check-postfixadmin-paths.sh new file mode 100755 index 0000000..6078faa --- /dev/null +++ b/check-postfixadmin-paths.sh @@ -0,0 +1,183 @@ +#!/bin/bash +# filepath: check-postfixadmin-paths.sh + +# Check PostfixAdmin URL configuration and path handling +# This script verifies the correct URL structure for PostfixAdmin 3.3.11 + +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 + +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 +} + +echo -e "${BLUE}=== PostfixAdmin Path Configuration Analysis ===${NC}" +echo + +info "Analyzing setup script configuration..." + +WEBROOT="/var/www/postfixadmin" +NGINX_CONFIG="/etc/nginx/sites-available/postfixadmin.conf" + +echo -e "${YELLOW}=== Directory Structure ===${NC}" +echo "Installation Directory: $WEBROOT" +echo "Public Directory: $WEBROOT/public" +echo "Setup Script: $WEBROOT/public/setup.php" +echo "Main Interface: $WEBROOT/public/index.php" +echo + +echo -e "${YELLOW}=== URL Structure ===${NC}" +echo "With the current Nginx configuration pointing to $WEBROOT/public:" +echo +echo "✅ CORRECT URLs:" +echo " • PostfixAdmin Setup: https://yourhostname/setup.php" +echo " • PostfixAdmin Main: https://yourhostname/" +echo " • PostfixAdmin Login: https://yourhostname/login.php" +echo +echo "❌ INCORRECT URLs (would result in 404):" +echo " • https://yourhostname/postfixadmin/setup.php" +echo " • https://yourhostname/postfixadmin/" +echo + +echo -e "${YELLOW}=== How the Setup Script Handles PostfixAdmin ===${NC}" +echo +echo "1. **Installation**:" +echo " - Downloads PostfixAdmin 3.3.11 to $WEBROOT" +echo " - Creates templates_c directory for Smarty templates" +echo " - Sets proper permissions (www-data:www-data)" +echo +echo "2. **Nginx Configuration**:" +echo " - Document root points to: $WEBROOT/public" +echo " - This is correct for PostfixAdmin 3.3.11+ structure" +echo " - PHP files are processed through PHP-FPM" +echo +echo "3. **PostfixAdmin Configuration (config.local.php)**:" +echo " - configured = false (allows setup wizard)" +echo " - Proper database credentials" +echo " - Encryption: dovecot:SHA512-CRYPT (compatible)" +echo " - Templates directory: $WEBROOT/templates_c" +echo +echo "4. **Setup Process**:" +echo " - User visits: https://hostname/setup.php" +echo " - Setup wizard creates database tables" +echo " - Admin user is created" +echo " - Configuration is finalized" +echo + +echo -e "${YELLOW}=== PostfixAdmin 3.3.11 Directory Structure ===${NC}" +echo "PostfixAdmin 3.3.11 uses this structure:" +echo "$WEBROOT/" +echo "├── public/ # Web-accessible files" +echo "│ ├── index.php # Main interface" +echo "│ ├── setup.php # Setup wizard" +echo "│ ├── login.php # Login page" +echo "│ ├── css/ # Stylesheets" +echo "│ ├── js/ # JavaScript" +echo "│ └── images/ # Images" +echo "├── templates/ # Smarty templates" +echo "├── templates_c/ # Compiled templates (writable)" +echo "├── model/ # Application logic" +echo "├── languages/ # Translation files" +echo "└── config.local.php # Configuration file" +echo + +echo -e "${YELLOW}=== Security Benefits ===${NC}" +echo "By pointing Nginx to the public/ directory:" +echo "✅ Configuration files are not web-accessible" +echo "✅ Template files are not web-accessible" +echo "✅ Application logic is not web-accessible" +echo "✅ Only intended public files can be accessed" +echo + +# Check if files exist +echo -e "${YELLOW}=== Current Installation Check ===${NC}" +if [[ -d "$WEBROOT" ]]; then + success "PostfixAdmin directory exists: $WEBROOT" + + if [[ -d "$WEBROOT/public" ]]; then + success "✅ Public directory exists: $WEBROOT/public" + + if [[ -f "$WEBROOT/public/setup.php" ]]; then + success "✅ Setup script exists: $WEBROOT/public/setup.php" + else + warning "⚠️ Setup script not found: $WEBROOT/public/setup.php" + fi + + if [[ -f "$WEBROOT/public/index.php" ]]; then + success "✅ Main interface exists: $WEBROOT/public/index.php" + else + warning "⚠️ Main interface not found: $WEBROOT/public/index.php" + fi + else + error "❌ Public directory missing: $WEBROOT/public" + echo " This indicates PostfixAdmin was not extracted correctly" + fi + + if [[ -f "$WEBROOT/config.local.php" ]]; then + success "✅ Configuration file exists: $WEBROOT/config.local.php" + else + warning "⚠️ Configuration file not found: $WEBROOT/config.local.php" + fi + + if [[ -d "$WEBROOT/templates_c" ]]; then + success "✅ Templates cache directory exists: $WEBROOT/templates_c" + + if [[ -w "$WEBROOT/templates_c" ]]; then + success "✅ Templates cache directory is writable" + else + error "❌ Templates cache directory is not writable" + fi + else + warning "⚠️ Templates cache directory missing: $WEBROOT/templates_c" + fi +else + warning "PostfixAdmin not yet installed: $WEBROOT" +fi + +# Check Nginx configuration +echo +if [[ -f "$NGINX_CONFIG" ]]; then + success "✅ Nginx configuration exists: $NGINX_CONFIG" + + if grep -q "root.*public" "$NGINX_CONFIG"; then + success "✅ Nginx points to public directory" + else + warning "⚠️ Nginx may not be pointing to public directory" + fi + + if [[ -L "/etc/nginx/sites-enabled/postfixadmin.conf" ]]; then + success "✅ Nginx site is enabled" + else + warning "⚠️ Nginx site is not enabled" + fi +else + warning "Nginx configuration not found: $NGINX_CONFIG" +fi + +echo +echo -e "${BLUE}=== Summary ===${NC}" +echo "The setup script correctly handles PostfixAdmin paths by:" +echo "1. Installing to $WEBROOT" +echo "2. Configuring Nginx to serve from $WEBROOT/public" +echo "3. Making setup.php accessible at https://hostname/setup.php" +echo "4. Ensuring proper security by hiding non-public files" +echo +echo -e "${GREEN}Path configuration is correct for PostfixAdmin 3.3.11!${NC}" diff --git a/finalize-postfixadmin-config.sh b/finalize-postfixadmin-config.sh new file mode 100755 index 0000000..facd7fd --- /dev/null +++ b/finalize-postfixadmin-config.sh @@ -0,0 +1,179 @@ +#!/bin/bash +# filepath: finalize-postfixadmin-config.sh + +# Finalize PostfixAdmin configuration after setup wizard completion +# This script updates config.local.php to mark PostfixAdmin as configured + +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 + +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 +} + +# Check if running as root +if [[ $EUID -ne 0 ]]; then + error "This script must be run as root" + exit 1 +fi + +WEBROOT="/var/www/postfixadmin" +CONFIG_FILE="$WEBROOT/config.local.php" + +# Check if config file exists +if [[ ! -f "$CONFIG_FILE" ]]; then + error "PostfixAdmin config file not found: $CONFIG_FILE" + exit 1 +fi + +info "Finalizing PostfixAdmin configuration..." + +# Get current configuration values +read -p "Enter your domain name (e.g., example.com): " DOMAIN +read -p "Enter your hostname (e.g., mail.example.com): " HOSTNAME +read -p "Enter admin email address: " ADMIN_EMAIL +read -p "Enter database name [postfix]: " DB_NAME +DB_NAME=${DB_NAME:-postfix} +read -p "Enter database user [postfix]: " DB_USER +DB_USER=${DB_USER:-postfix} +read -s -p "Enter database password: " DB_PASSWORD +echo +read -s -p "Enter PostfixAdmin setup password: " POSTFIXADMIN_PASSWORD +echo + +# Create the updated configuration +info "Creating finalized PostfixAdmin configuration..." + +cat > "$CONFIG_FILE" << EOF + 'abuse@$DOMAIN', + 'hostmaster' => 'hostmaster@$DOMAIN', + 'postmaster' => 'postmaster@$DOMAIN', + 'webmaster' => 'webmaster@$DOMAIN' +); + +// Virtual domain configuration +\$CONF['domain_path'] = 'YES'; +\$CONF['domain_in_mailbox'] = 'YES'; +\$CONF['maildir_name_hook'] = 'maildir_name_hook'; + +// Template configuration +\$CONF['templates_c'] = '$WEBROOT/templates_c'; + +// Custom maildir naming function +function maildir_name_hook(\$domain, \$user) { + return \$domain . '/' . \$user . '/'; +} + +// Quota configuration +\$CONF['quota'] = 'YES'; +\$CONF['quota_multiplier'] = 1024000; + +// Security settings +\$CONF['min_password_length'] = 8; +\$CONF['generate_password'] = 'YES'; +\$CONF['show_password'] = 'NO'; + +// Feature toggles +\$CONF['vacation'] = 'YES'; +\$CONF['vacation_domain'] = 'autoreply.$DOMAIN'; +\$CONF['aliases'] = '10'; +\$CONF['mailboxes'] = '10'; +\$CONF['maxquota'] = '10'; + +// Logging +\$CONF['logging'] = 'YES'; +\$CONF['fetchmail'] = 'NO'; + +// Backup configuration +\$CONF['backup'] = 'YES'; + +// Theme and appearance +\$CONF['theme_logo'] = 'images/logo-default.png'; +\$CONF['theme_css'] = 'default.css'; + +// Email settings +\$CONF['smtp_server'] = 'localhost'; +\$CONF['smtp_port'] = '25'; + +// Footer text +\$CONF['footer_text'] = 'Return to $HOSTNAME'; +\$CONF['footer_link'] = 'https://$HOSTNAME'; + +// Additional security +\$CONF['password_validation'] = array( + '/^.{8,}$/' => 'password_too_short 8', + '/[a-z]/' => 'password_no_characters', + '/[A-Z]/' => 'password_no_characters', + '/[0-9]/' => 'password_no_digits' +); + +?> +EOF + +# Set proper permissions +chown www-data:www-data "$CONFIG_FILE" +chmod 640 "$CONFIG_FILE" + +success "PostfixAdmin configuration finalized!" +echo +echo -e "${BLUE}=== Configuration Summary ===${NC}" +echo "Config file: $CONFIG_FILE" +echo "Configured: true" +echo "Database: $DB_NAME on localhost" +echo "Domain: $DOMAIN" +echo "Hostname: $HOSTNAME" +echo "Admin email: $ADMIN_EMAIL" +echo +echo -e "${YELLOW}=== Next Steps ===${NC}" +echo "1. Access PostfixAdmin: https://$HOSTNAME/" +echo "2. Log in with your admin credentials" +echo "3. Start creating domains and mailboxes" +echo +echo -e "${GREEN}PostfixAdmin is now ready for use!${NC}" \ No newline at end of file diff --git a/fix-clamv.sh b/fix-clamv.sh new file mode 100644 index 0000000..8cea052 --- /dev/null +++ b/fix-clamv.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# filepath: fix-clamav.sh + +# Fix ClamAV freshclam lock issue +# Run this script if you encounter ClamAV signature update errors + +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 + +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 +} + +# Check if running as root +if [[ $EUID -ne 0 ]]; then + error "This script must be run as root" + exit 1 +fi + +info "Fixing ClamAV freshclam lock issue..." + +# Stop ClamAV services +info "Stopping ClamAV services..." +systemctl stop clamav-daemon 2>/dev/null || true +systemctl stop clamav-freshclam 2>/dev/null || true + +# Wait for services to stop +sleep 3 + +# Remove any stale lock files +info "Removing stale lock files..." +rm -f /var/log/clamav/freshclam.log.lock +rm -f /var/run/clamav/freshclam.pid + +# Check log file permissions +info "Checking log file permissions..." +touch /var/log/clamav/freshclam.log +chown clamav:clamav /var/log/clamav/freshclam.log +chmod 644 /var/log/clamav/freshclam.log + +# Update signatures manually +info "Updating ClamAV signatures..." +if sudo -u clamav freshclam --quiet; then + success "ClamAV signatures updated successfully" +else + warning "Manual signature update failed, but services will handle this automatically" +fi + +# Restart services +info "Restarting ClamAV services..." +systemctl start clamav-freshclam +sleep 2 +systemctl start clamav-daemon + +# Check service status +if systemctl is-active --quiet clamav-freshclam; then + success "clamav-freshclam service is running" +else + warning "clamav-freshclam service failed to start" +fi + +if systemctl is-active --quiet clamav-daemon; then + success "clamav-daemon service is running" +else + warning "clamav-daemon service failed to start" +fi + +# Restart amavis to ensure it can connect to ClamAV +info "Restarting amavis service..." +systemctl restart amavis + +if systemctl is-active --quiet amavis; then + success "amavis service is running" +else + warning "amavis service failed to restart" +fi + +success "ClamAV fix completed!" +echo +echo -e "${BLUE}Service Status:${NC}" +systemctl status clamav-freshclam --no-pager -l || true +echo +systemctl status clamav-daemon --no-pager -l || true +echo +systemctl status amavis --no-pager -l || true \ No newline at end of file diff --git a/fix-postfixadmin-password-hashing.sh b/fix-postfixadmin-password-hashing.sh new file mode 100755 index 0000000..1ff0578 --- /dev/null +++ b/fix-postfixadmin-password-hashing.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +# Fix PostfixAdmin Password Hashing Configuration +# This script fixes the dovecot password hashing error in PostfixAdmin +# Author: Email Server Setup Script + +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 + +WEBROOT="/var/www/postfixadmin" +CONFIG_FILE="$WEBROOT/config.local.php" + +error() { + echo -e "${RED}ERROR: $1${NC}" >&2 + exit 1 +} + +info() { + echo -e "${BLUE}INFO: $1${NC}" +} + +success() { + echo -e "${GREEN}SUCCESS: $1${NC}" +} + +warning() { + echo -e "${YELLOW}WARNING: $1${NC}" +} + +# Check if running as root +if [[ $EUID -ne 0 ]]; then + error "This script must be run as root" +fi + +# Check if config file exists +if [[ ! -f "$CONFIG_FILE" ]]; then + error "PostfixAdmin config file not found: $CONFIG_FILE" +fi + +echo -e "${BLUE}PostfixAdmin Password Hashing Fix${NC}" +echo "==================================" +echo +echo "This script fixes the dovecot password hashing error by switching" +echo "from 'dovecot:SHA512-CRYPT' to 'php_crypt:SHA512' which doesn't" +echo "require external dovecot commands." +echo + +# Create backup +backup_file="$CONFIG_FILE.backup.$(date +%Y%m%d_%H%M%S)" +cp "$CONFIG_FILE" "$backup_file" +info "Created backup: $backup_file" + +# Check if the problematic configuration exists +if grep -q "dovecot:SHA512-CRYPT" "$CONFIG_FILE"; then + info "Found problematic dovecot configuration, fixing..." + + # Replace the encrypt method + sed -i "s/\$CONF\['encrypt'\] = 'dovecot:SHA512-CRYPT';/\$CONF['encrypt'] = 'php_crypt:SHA512';/" "$CONFIG_FILE" + + # Remove or comment out the dovecotpw line if it exists + sed -i "/\$CONF\['dovecotpw'\]/d" "$CONFIG_FILE" + + success "Updated password encryption method to php_crypt:SHA512" + +elif grep -q "php_crypt:SHA512" "$CONFIG_FILE"; then + success "Configuration already uses php_crypt:SHA512 - no changes needed" +else + warning "Could not find encryption configuration in config file" + info "Adding php_crypt:SHA512 configuration..." + + # Add the encryption method before the closing PHP tag + sed -i '/^?>/i \$CONF['\''encrypt'\''] = '\''php_crypt:SHA512'\'';' "$CONFIG_FILE" + success "Added php_crypt:SHA512 configuration" +fi + +# Verify the change +if grep -q "php_crypt:SHA512" "$CONFIG_FILE"; then + success "Configuration verified: using php_crypt:SHA512" +else + error "Verification failed - configuration may not have been updated correctly" +fi + +# Set appropriate permissions +chown www-data:www-data "$CONFIG_FILE" +chmod 644 "$CONFIG_FILE" + +echo +echo -e "${GREEN}PostfixAdmin password hashing fixed!${NC}" +echo +echo -e "${BLUE}What this script did:${NC}" +echo "• Changed encryption from 'dovecot:SHA512-CRYPT' to 'php_crypt:SHA512'" +echo "• Removed dovecotpw command dependency" +echo "• Created a backup of your original config" +echo "• Set appropriate file permissions" +echo +echo -e "${BLUE}What this means:${NC}" +echo "• PostfixAdmin will use PHP's built-in crypt() function" +echo "• No dependency on external dovecot commands" +echo "• Passwords will still be SHA512-CRYPT compatible with Dovecot" +echo "• The setup wizard should now work properly" +echo +echo -e "${YELLOW}Next steps:${NC}" +echo "• Refresh your PostfixAdmin setup page" +echo "• The password hashing error should be resolved" +echo "• Continue with the setup wizard" +echo +echo -e "${YELLOW}Note: If you need to revert these changes,${NC}" +echo -e "${YELLOW}restore the backup file: $backup_file${NC}" diff --git a/setup-email-server.sh b/setup-email-server.sh index d0b7108..4bcdef8 100755 --- a/setup-email-server.sh +++ b/setup-email-server.sh @@ -28,7 +28,7 @@ WEBROOT="/var/www/postfixadmin" POSTFIXADMIN_VERSION="postfixadmin-3.3.11.tar.gz" # New: Define the mail volume path -MAIL_VOLUME_PATH="/mnt/MainEmail" +MAIL_VOLUME_PATH="/mnt/email" # Logging LOG_FILE="/var/log/email-server-setup.log" @@ -178,112 +178,33 @@ configure_postgresql() { systemctl start postgresql systemctl enable postgresql - # Create database and user + # Create database and user with full permissions for PostfixAdmin setup sudo -u postgres psql -c "CREATE DATABASE $DB_NAME;" || warning "Database already exists, skipping." sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASSWORD';" || warning "User already exists, skipping." - sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" + + # Grant comprehensive permissions needed for PostfixAdmin setup wizard + sudo -u postgres psql -d "$DB_NAME" << EOF +-- Grant all privileges on database +GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER; - # Create tables for Postfix - sudo -u postgres psql -d "$DB_NAME" << 'EOF' -CREATE TABLE IF NOT EXISTS 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) -); +-- Grant schema permissions +GRANT ALL PRIVILEGES ON SCHEMA public TO $DB_USER; +GRANT CREATE ON SCHEMA public TO $DB_USER; -CREATE TABLE IF NOT EXISTS 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) -); +-- Allow user to create tables, sequences, etc. +ALTER USER $DB_USER CREATEDB; -CREATE TABLE IF NOT EXISTS 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) -); +-- Set user as owner of public schema to allow full table management +ALTER SCHEMA public OWNER TO $DB_USER; -CREATE TABLE IF NOT EXISTS 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 IF NOT EXISTS 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 IF NOT EXISTS 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 IF NOT EXISTS 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 IF NOT EXISTS quota ( - username varchar(255) NOT NULL, - path varchar(100) NOT NULL, - current bigint NOT NULL default 0, - PRIMARY KEY (username, path) -); - -CREATE TABLE IF NOT EXISTS quota2 ( - username varchar(100) NOT NULL, - bytes bigint NOT NULL default 0, - messages int NOT NULL default 0, - PRIMARY KEY (username) -); +-- Grant default privileges for future objects +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $DB_USER; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $DB_USER; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO $DB_USER; 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" + success "PostgreSQL configured for PostfixAdmin setup wizard" + info "Database tables will be created by PostfixAdmin setup wizard" } # --- @@ -291,6 +212,9 @@ EOF # Nginx is now configured with a basic HTTP virtual host first, allowing Certbot to run successfully. # --- +# Install PostfixAdmin and configure Nginx for HTTP +# Replace the install_postfixadmin() function with this updated version: + # Install PostfixAdmin and configure Nginx for HTTP install_postfixadmin() { info "Installing PostfixAdmin..." @@ -301,17 +225,25 @@ install_postfixadmin() { tar -xzf $POSTFIXADMIN_VERSION mv postfixadmin-postfixadmin-3.3.11 $WEBROOT + # Create necessary directories + mkdir -p $WEBROOT/templates_c + mkdir -p $WEBROOT/logs + # Set permissions chown -R www-data:www-data $WEBROOT find $WEBROOT -type d -exec chmod 755 {} \; find $WEBROOT -type f -exec chmod 644 {} \; + + # Ensure templates_c is writable + chmod 755 $WEBROOT/templates_c + chown www-data:www-data $WEBROOT/templates_c # Create PostfixAdmin configuration cat > $WEBROOT/config.local.php << EOF 'abuse@change-this-to-your.domain.tld', @@ -332,13 +265,14 @@ install_postfixadmin() { \$CONF['domain_path'] = 'YES'; \$CONF['domain_in_mailbox'] = 'YES'; \$CONF['maildir_name_hook'] = 'maildir_name_hook'; +\$CONF['templates_c'] = '$WEBROOT/templates_c'; function maildir_name_hook(\$domain, \$user) { return \$domain . '/' . \$user . '/'; } ?> EOF - # Configure Nginx virtual host with a basic HTTP block + # Configure Nginx virtual host with proper public directory cat > /etc/nginx/sites-available/postfixadmin.conf << EOF server { listen 80; @@ -346,6 +280,10 @@ server { root $WEBROOT/public; index index.php index.html index.htm; + # Security headers + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + location / { try_files \$uri \$uri/ /index.php?\$args; } @@ -355,17 +293,51 @@ server { fastcgi_pass unix:/var/run/php/php$(php -r "echo PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;")-fpm.sock; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; fastcgi_read_timeout 600s; + fastcgi_param PATH_INFO \$fastcgi_path_info; + include fastcgi_params; + } + + # Deny access to sensitive files and directories + location ~ /\. { + deny all; + } + + # Deny access to config and other sensitive directories outside public + location ~ ^/(config|logs|templates_c|scripts|DOCUMENTS|INSTALL|upgrade)/ { + deny all; + } + + # Allow access to public assets + location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ { + expires 1y; + add_header Cache-Control "public, immutable"; } } EOF # Enable Nginx site and disable default - ln -s /etc/nginx/sites-available/postfixadmin.conf /etc/nginx/sites-enabled/ + ln -sf /etc/nginx/sites-available/postfixadmin.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default - success "PostfixAdmin installed and Nginx configured for HTTP" -} + # Test write permissions + if sudo -u www-data touch $WEBROOT/templates_c/test_write 2>/dev/null; then + rm -f $WEBROOT/templates_c/test_write + success "PostfixAdmin write permissions verified" + else + error "PostfixAdmin templates_c directory is not writable by www-data" + fi + # Verify public directory exists + if [[ -d "$WEBROOT/public" ]]; then + success "PostfixAdmin public directory found" + else + warning "PostfixAdmin public directory not found - this may cause 404 errors" + info "Directory contents of $WEBROOT:" + ls -la "$WEBROOT" || true + fi + + success "PostfixAdmin installed and Nginx configured for HTTP with public directory" +} # Get Let's Encrypt certificates get_ssl_certificates() { info "Obtaining Let's Encrypt certificates..." @@ -668,8 +640,20 @@ EOF configure_amavis() { info "Configuring Amavis and SpamAssassin..." + # Stop freshclam service temporarily to avoid lock conflicts + systemctl stop clamav-freshclam 2>/dev/null || true + + # Wait a moment for the service to fully stop + sleep 2 + # Update ClamAV signatures - freshclam + info "Updating ClamAV virus signatures..." + if ! freshclam --quiet; then + warning "Failed to update ClamAV signatures manually. The automatic updater will handle this." + fi + + # Restart freshclam service + systemctl start clamav-freshclam 2>/dev/null || true # Configure Amavis cat > /etc/amavis/conf.d/15-content_filter_mode << 'EOF' @@ -746,39 +730,6 @@ EOF success "Amavis and SpamAssassin configured" } -# 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..." @@ -786,18 +737,65 @@ start_services() { # Reload systemd systemctl daemon-reload - # Start and enable services + # Start and enable core services first 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 nginx systemctl enable --now php$(php -r "echo PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;")-fpm + # Start ClamAV services with better error handling + info "Starting ClamAV services..." + + # Start clamav-freshclam first (for signature updates) + if systemctl list-unit-files --full | grep -q "clamav-freshclam.service"; then + systemctl enable clamav-freshclam + systemctl start clamav-freshclam + + # Wait for freshclam to complete initial signature update + info "Waiting for ClamAV signature update to complete..." + for i in {1..30}; do + if systemctl is-active --quiet clamav-freshclam && + [[ -f /var/lib/clamav/main.cvd || -f /var/lib/clamav/main.cld ]]; then + break + fi + sleep 2 + done + else + warning "ClamAV freshclam service file not found. Skipping." + fi + + # Now start clamav-daemon + if systemctl list-unit-files --full | grep -q "clamav-daemon.service"; then + systemctl enable clamav-daemon + systemctl start clamav-daemon + + # Wait for daemon to start + sleep 3 + if systemctl is-active --quiet clamav-daemon; then + success "ClamAV daemon started successfully" + else + warning "ClamAV daemon failed to start. Check logs with: journalctl -u clamav-daemon" + fi + else + warning "ClamAV daemon service file not found (expected 'clamav-daemon.service'). Skipping." + fi + + # Start mail services + systemctl enable --now postfix + systemctl enable --now dovecot + systemctl enable --now opendkim + + # Start Amavis (depends on ClamAV) + systemctl enable --now amavis + + # Enable SpamAssassin service (handle potential name differences) + if systemctl list-unit-files --full | grep -q "spamassassin.service"; then + systemctl enable --now spamassassin + elif systemctl list-unit-files --full | grep -q "spamd.service"; then + systemctl enable --now spamd + else + warning "SpamAssassin service file not found (expected 'spamassassin.service' or 'spamd.service'). Skipping." + fi + success "All services started and enabled" } @@ -810,7 +808,7 @@ display_final_info() { echo "Hostname: $HOSTNAME" echo "Admin Email: $ADMIN_EMAIL" echo "Mailbox Location: $MAIL_VOLUME_PATH/vhosts" - echo "PostfixAdmin URL: https://$HOSTNAME/postfixadmin/" + echo "PostfixAdmin URL: https://$HOSTNAME/" echo echo -e "${YELLOW}=== DNS Records to Add for $DOMAIN ===${NC}" echo "A $HOSTNAME [Your server IP]" @@ -823,9 +821,12 @@ display_final_info() { 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 "2. Visit https://$HOSTNAME/setup.php to complete PostfixAdmin setup" + echo "3. After completing the setup wizard, run ONE of these scripts:" + echo " • ./toggle-postfixadmin-configured.sh (quick toggle configured = true)" + echo " • ./finalize-postfixadmin-config.sh (complete reconfiguration)" + echo "4. Create your first domain and mailbox in PostfixAdmin" + echo "5. Test email sending and receiving" echo echo -e "${BLUE}=== Multiple Domain Support ===${NC}" echo "This server supports unlimited virtual domains!" @@ -871,7 +872,7 @@ main() { configure_dovecot configure_opendkim configure_amavis - configure_firewall + # configure_firewall display_final_info } diff --git a/toggle-postfixadmin-configured.sh b/toggle-postfixadmin-configured.sh new file mode 100755 index 0000000..f5727df --- /dev/null +++ b/toggle-postfixadmin-configured.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# Quick PostfixAdmin Configuration Toggle +# This script simply sets configured = true after setup wizard completion +# Use this if you've already completed the setup wizard via web interface +# Author: Email Server Setup Script + +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 + +WEBROOT="/var/www/postfixadmin" +CONFIG_FILE="$WEBROOT/config.local.php" + +error() { + echo -e "${RED}ERROR: $1${NC}" >&2 + exit 1 +} + +info() { + echo -e "${BLUE}INFO: $1${NC}" +} + +success() { + echo -e "${GREEN}SUCCESS: $1${NC}" +} + +warning() { + echo -e "${YELLOW}WARNING: $1${NC}" +} + +# Check if running as root +if [[ $EUID -ne 0 ]]; then + error "This script must be run as root" +fi + +# Check if config file exists +if [[ ! -f "$CONFIG_FILE" ]]; then + error "PostfixAdmin config file not found: $CONFIG_FILE" +fi + +echo -e "${BLUE}PostfixAdmin Configuration Toggle${NC}" +echo "==================================" +echo + +# Check current status +if grep -q "\$CONF\['configured'\] = true;" "$CONFIG_FILE"; then + warning "PostfixAdmin is already marked as configured" + echo "If you need to run the setup wizard again, you can change it back to false." + exit 0 +fi + +if grep -q "\$CONF\['configured'\] = false;" "$CONFIG_FILE"; then + info "Found configured = false in config file" +else + error "Could not find configured setting in config file" +fi + +echo "This script will change PostfixAdmin from configured = false to configured = true" +echo "This should be done AFTER you have completed the setup wizard at /setup.php" +echo + +read -p "Have you completed the PostfixAdmin setup wizard? (y/N): " confirm +if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + info "Please complete the setup wizard first at https://your-hostname/setup.php" + exit 0 +fi + +# Create backup +backup_file="$CONFIG_FILE.backup.$(date +%Y%m%d_%H%M%S)" +cp "$CONFIG_FILE" "$backup_file" +info "Created backup: $backup_file" + +# Update the configuration +if sed -i "s/\$CONF\['configured'\] = false;/\$CONF['configured'] = true;/" "$CONFIG_FILE"; then + success "Updated: configured = false → configured = true" +else + error "Failed to update configuration" +fi + +# Verify the change +if grep -q "\$CONF\['configured'\] = true;" "$CONFIG_FILE"; then + success "Configuration verified: PostfixAdmin is now marked as configured" +else + error "Verification failed - configuration may not have been updated correctly" +fi + +echo +echo -e "${GREEN}PostfixAdmin setup completed!${NC}" +echo +echo -e "${BLUE}What this means:${NC}" +echo "• The setup wizard (/setup.php) is now disabled for security" +echo "• PostfixAdmin is ready for normal use" +echo "• You can now create domains and mailboxes" +echo +echo -e "${YELLOW}Note: If you need to run the setup wizard again,${NC}" +echo -e "${YELLOW}restore the backup file or manually change configured back to false${NC}" diff --git a/verify-postfixadmin-config.sh b/verify-postfixadmin-config.sh new file mode 100755 index 0000000..11722ff --- /dev/null +++ b/verify-postfixadmin-config.sh @@ -0,0 +1,210 @@ +#!/bin/bash +# filepath: verify-postfixadmin-config.sh + +# Verify PostfixAdmin configuration and supported encryption methods +# This script checks the current configuration and provides recommendations + +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 + +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 +} + +WEBROOT="/var/www/postfixadmin" +CONFIG_FILE="$WEBROOT/config.local.php" + +echo -e "${BLUE}=== PostfixAdmin Configuration Verification ===${NC}" +echo + +# Check if PostfixAdmin is installed +if [[ -d "$WEBROOT" ]]; then + success "PostfixAdmin directory found: $WEBROOT" +else + error "PostfixAdmin directory not found: $WEBROOT" + exit 1 +fi + +# Check config file +if [[ -f "$CONFIG_FILE" ]]; then + success "Configuration file found: $CONFIG_FILE" +else + warning "Configuration file not found: $CONFIG_FILE" + echo "Run the setup script first to create the configuration." + exit 1 +fi + +# Check encryption method +echo +info "Checking encryption configuration..." +if grep -q "dovecot:SHA512-CRYPT" "$CONFIG_FILE"; then + success "✅ Using supported encryption: dovecot:SHA512-CRYPT" +elif grep -q "sha512.crypt" "$CONFIG_FILE"; then + error "❌ Using unsupported encryption: sha512.crypt" + echo " Fix: Change to 'dovecot:SHA512-CRYPT'" +elif grep -q "encrypt" "$CONFIG_FILE"; then + ENCRYPT_METHOD=$(grep "encrypt" "$CONFIG_FILE" | head -1 | cut -d"'" -f4) + warning "⚠️ Using encryption method: $ENCRYPT_METHOD" + echo " Recommended: 'dovecot:SHA512-CRYPT'" +else + warning "No encryption method found in configuration" +fi + +# Check if configured +echo +info "Checking configuration status..." +if grep -q "configured.*true" "$CONFIG_FILE"; then + success "✅ PostfixAdmin is marked as configured" +elif grep -q "configured.*false" "$CONFIG_FILE"; then + info "⏳ PostfixAdmin is not yet configured (setup wizard available)" +else + warning "Configuration status unclear" +fi + +# Check database configuration +echo +info "Checking database configuration..." +if grep -q "database_type.*pgsql" "$CONFIG_FILE"; then + success "✅ Using PostgreSQL database" +else + warning "Database type not found or not PostgreSQL" +fi + +if grep -q "database_host.*localhost" "$CONFIG_FILE"; then + success "✅ Database host: localhost" +fi + +# Check dovecotpw path +echo +info "Checking Dovecot integration..." +if grep -q "dovecotpw.*doveadm" "$CONFIG_FILE"; then + success "✅ Using doveadm for password operations" + + # Test if doveadm is available + if command -v doveadm >/dev/null 2>&1; then + success "✅ doveadm command is available" + + # Test encryption + if echo "test" | doveadm pw -s SHA512-CRYPT >/dev/null 2>&1; then + success "✅ SHA512-CRYPT encryption is supported" + else + warning "⚠️ SHA512-CRYPT test inconclusive" + fi + else + error "❌ doveadm command not found" + echo " Install Dovecot: apt-get install dovecot-core" + fi +else + warning "Dovecot integration not configured" +fi + +# Check public directory +echo +info "Checking web server configuration..." +if [[ -d "$WEBROOT/public" ]]; then + success "✅ PostfixAdmin public directory exists" +else + error "❌ PostfixAdmin public directory missing" + echo " Expected: $WEBROOT/public" + echo " Check PostfixAdmin version and extraction" +fi + +# Check permissions +echo +info "Checking file permissions..." +if [[ -w "$WEBROOT/templates_c" ]]; then + success "✅ templates_c directory is writable" +else + error "❌ templates_c directory is not writable" + echo " Fix: chown -R www-data:www-data $WEBROOT/templates_c" +fi + +# Check Nginx configuration +echo +info "Checking web server configuration..." +if [[ -f "/etc/nginx/sites-enabled/postfixadmin.conf" ]]; then + success "✅ Nginx PostfixAdmin site is enabled" + + if grep -q "root.*public" "/etc/nginx/sites-enabled/postfixadmin.conf"; then + success "✅ Nginx points to public directory" + else + warning "⚠️ Nginx may not be pointing to public directory" + fi +else + warning "Nginx PostfixAdmin configuration not found" +fi + +# Check Postfix integration +echo +info "Checking Postfix integration..." +POSTFIX_CONFIGS=( + "/etc/postfix/pgsql-virtual-mailbox-domains.cf" + "/etc/postfix/pgsql-virtual-mailbox-maps.cf" + "/etc/postfix/pgsql-virtual-alias-maps.cf" +) + +for config in "${POSTFIX_CONFIGS[@]}"; do + if [[ -f "$config" ]]; then + success "✅ Found: $(basename "$config")" + else + error "❌ Missing: $(basename "$config")" + fi +done + +# Summary +echo +echo -e "${BLUE}=== Configuration Summary ===${NC}" +echo +echo -e "${YELLOW}Supported PostfixAdmin 3.3.11 Encryption Methods:${NC}" +echo "✅ dovecot:SHA512-CRYPT (recommended)" +echo "✅ dovecot:SHA256-CRYPT" +echo "✅ dovecot:BLF-CRYPT" +echo "✅ md5crypt" +echo "✅ sha512" +echo "❌ sha512.crypt (unsupported)" +echo "❌ cleartext (not recommended)" +echo +echo -e "${YELLOW}PostfixAdmin Database Tables:${NC}" +echo "Required tables created by setup wizard:" +echo "• admin" +echo "• alias" +echo "• config" +echo "• domain" +echo "• domain_admins" +echo "• fetchmail" +echo "• log" +echo "• mailbox" +echo "• quota" +echo "• quota2" +echo "• vacation" +echo "• vacation_notification" +echo +echo -e "${YELLOW}Access URLs:${NC}" +if grep -q "postfix_admin_url" "$CONFIG_FILE"; then + ADMIN_URL=$(grep "postfix_admin_url" "$CONFIG_FILE" | cut -d"'" -f4) + echo "PostfixAdmin: $ADMIN_URL/" + echo "Setup wizard: $ADMIN_URL/setup.php" +else + echo "PostfixAdmin: https://your-hostname/" + echo "Setup wizard: https://your-hostname/setup.php" +fi +echo +echo -e "${GREEN}Configuration verification completed!${NC}"