DirectAdmin: Improve performance and security

Hardened DirectAdmin
Hardened DirectAdmin

Introduction

When you set up a fresh DirectAdmin server, the default settings are functional but not secure or optimized. The first thing you should do is harden the server and improve its performance. Here’s what I did to optimize my DirectAdmin server for hosting my customers' websites.

Prerequisites

At the time of writing, I was using DirectAdmin v1.67 on Ubuntu 20.04, running on an Ampere ARM-based compute instance.

Solutions

  • Prepare envs

    export SMTP_USER="YOUR_SMTP_USER"
    export SMTP_PASS="YOUR_SMTP_PASS"
    export SMTP_HOST="YOUR_SMTP_HOST"
    export SMTP_PORT="YOUR_SMTP_PORT"
    export AWS_S3_KEY="YOUR_AWS_S3_KEY"
    export AWS_S3_SECERT="YOUR_AWS_S3_SECERT"
    export AWS_S3_BUCKET="YOUR_AWS_S3_BUCKET"
    export AWS_S3_REGION="s3.ap-northeast-1.amazonaws.com"
    export ADMIN_NAME="XXX Admin"
    export DA_HOSTNAME=$(hostname -f)
    export DA_EMAIL=admin@$(hostname -f)
    export DA_NS1=ns0.hostcp.xyz
    export DA_NS2=ns1.hostcp.xyz
    
  • Increase the memory allocation for the /tmp directory by editing the /etc/fstab file

    tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev,size=1G 0 0
    
  • Disable potentially dangerous PHP functions for improved security

    cd /usr/local/directadmin/custombuild
    ./build secure_php
    
  • Set clean_old_webapps to true to remove outdated scripts and minimize potential issues

    cd /usr/local/directadmin/custombuild
    ./build set clean_old_webapps yes    
    
  • Turn off remote MySQL access to enhance security add the following to the [mysqld] section of the /etc/my.cnf

    bind-address = 127.0.0.1
    
  • Use php-fpm mode

    /usr/local/directadmin/custombuild/build set php1_release 8.1
    /usr/local/directadmin/custombuild/build set php2_release 8.0
    /usr/local/directadmin/custombuild/build set php3_release 7.4
    /usr/local/directadmin/custombuild/build set php4_release 5.6
    /usr/local/directadmin/custombuild/build set php1_mode php-fpm
    /usr/local/directadmin/custombuild/build set php2_mode php-fpm
    /usr/local/directadmin/custombuild/build set php3_mode php-fpm
    /usr/local/directadmin/custombuild/build set php4_mode php-fpm
    /usr/local/directadmin/custombuild/build php n
    /usr/local/directadmin/custombuild/build rewrite_confs    
    
  • Use Nginx reverse proxy

    You can boost performance by serving static files with Nginx and handling PHP files with Apache:

    cd /usr/local/directadmin/custombuild
    ./build update
    ./build set webserver nginx_apache
    ./build nginx_apache
    
  • Install ModSecurity for enhanced web application protection

    /usr/local/directadmin/custombuild/build set modsecurity yes
    /usr/local/directadmin/custombuild/build set modsecurity_ruleset owasp
    /usr/local/directadmin/custombuild/build modsecurity
    /usr/local/directadmin/custombuild/build modsecurity_rules
    /usr/local/directadmin/custombuild/build rewrite_confs    
    
  • Enable the jail system for improved security and isolation

    /usr/local/directadmin/custombuild/build jailshell
    
  • Install Rspamd for advanced spam filtering and email protection

    /usr/local/directadmin/custombuild/build set spamd rspamd
    /usr/local/directadmin/custombuild/build rspamd
    /usr/local/directadmin/custombuild/build exim_conf    
    
  • Install ClamAV for reliable antivirus protection on the server

    /usr/local/directadmin/custombuild/build set clamav yes
    /usr/local/directadmin/custombuild/build set modsecurity_uploadscan yes
    /usr/local/directadmin/custombuild/build set pureftpd_uploadscan yes
    /usr/local/directadmin/custombuild/build clamav
    
  • Enable common PHP extensions for better compatibility and functionality

    /usr/local/directadmin/custombuild/build set_php "opcache" yes
    /usr/local/directadmin/custombuild/build set_php "gmp" yes
    /usr/local/directadmin/custombuild/build set_php "imagick" yes
    /usr/local/directadmin/custombuild/build set_php "redis" yes
    /usr/local/directadmin/custombuild/build set_php "snuffleupagus" yes
    
    /usr/local/directadmin/custombuild/build "php_opcache"
    /usr/local/directadmin/custombuild/build "php_gmp"
    /usr/local/directadmin/custombuild/build "php_imagick"
    /usr/local/directadmin/custombuild/build "php_redis"
    /usr/local/directadmin/custombuild/build "php_snuffleupagus"    
    
  • Schedule a daily ClamAV scan with an automated script

    Create the script:

    mkdir -p /root/scripts
    mkdir -p /var/log/clamav
    vi /root/scripts/clamscan.sh
    
    #!/bin/bash
    LOGFILE="/var/log/clamav/clamav-$(date +'%Y-%m-%d').log";
    EMAIL_MSG="Please see the log file attached.";
    EMAIL_FROM="root@$(hostname -f)";
    EMAIL_TO="root@$(hostname -f)";
    DIRTOSCAN="/home";
    
    for S in ${DIRTOSCAN}; do
    DIRSIZE=$(du -sh "$S" 2>/dev/null | cut -f1);
    
    echo "Starting AV scan of "$S" directory.
    Amount of data to be scanned is "$DIRSIZE".";
    
    clamscan -ri --remove "$S" >> "$LOGFILE";
    
    # get the value of "Infected lines"
    MALWARE=$(tail "$LOGFILE"|grep Infected|cut -d" " -f3);
    
    # if the value is not equal to zero, send an email with the log file attached
    if [ "$MALWARE" -ne "0" ];then
    # using heirloom-mailx below
    echo "$EMAIL_MSG"|mail -a "$LOGFILE" -s "Malware Found" -r "$EMAIL_FROM" "$EMAIL_TO";
    fi
    done
    
    exit 0;    
    

    Next, add the script to the server's cron job for automated execution:

    chmod +x /root/scripts/clamscan.sh
    ln /root/scripts/clamscan.sh /etc/cron.daily/clamscan_daily    
    
  • Fine-tune DirectAdmin configuration for optimal performance and security

    /usr/local/directadmin/directadmin set force_hostname $(hostname -f)
    /usr/local/directadmin/directadmin set dkim 1
    /usr/local/directadmin/directadmin set letsencrypt_renewal_notice_to_admins 0
    /usr/local/directadmin/directadmin set clear_blacklist_ip_time 1440
    /usr/local/directadmin/directadmin set unblock_brute_ip_time 1440
    /usr/local/directadmin/directadmin set ip_brutecount 10
    /usr/local/directadmin/directadmin set user_brutecount 10
    /usr/local/directadmin/directadmin set enforce_difficult_passwords 1
    /usr/local/directadmin/directadmin set hide_brute_force_notifications 1
    /usr/local/directadmin/directadmin set purge_spam_days 7
    /usr/local/directadmin/directadmin set default_ttl 14400
    /usr/local/directadmin/directadmin set brute_force_scan_apache_logs 2
    /usr/local/directadmin/directadmin set one_click_webmail_login 1
    /usr/local/directadmin/directadmin set one_click_pma_login 1
    /usr/local/directadmin/directadmin set webmail_link roundcube
    /usr/local/directadmin/directadmin set one_click_webmail_link /webmail    
    
  • Set up SSL for email and applications to ensure secure communication

    Edit /etc/httpd/conf/extra/httpd-includes.conf using the configuration below:

     ################################################################################
     #                          Mod_Rewrite
     ################################################################################
     <location /phpMyAdmin>
       RewriteEngine On
       RewriteCond %{HTTPS} off
       RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
     </location>
     <location /webmail>
       RewriteEngine On
       RewriteCond %{HTTPS} off
       RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
     </location>
     <location /squirrelmail>
       RewriteEngine On
       RewriteCond %{HTTPS} off
       RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
     </location>
     <location /roundcube>
       RewriteEngine On
       RewriteCond %{HTTPS} off
       RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
     </location>
     <location /atmail>
       RewriteEngine On
       RewriteCond %{HTTPS} off
       RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
     </location>   
    

    Next, set disable_plaintext_auth to yes in the /etc/dovecot.conf file.

    Prevent unencrypted email submissions:

     # Prevents unencrypted mail submission.
       drop
               !encrypted     = *
               authenticated  = *
               message        = Your connection must be encrypted.
               log_message  = Connection from \
                               [$sender_host_address]($authenticated_id) was \
                               not encrypted.   
    

    Update webmail to use encrypted connections in /var/www/html/roundcube/config/config.inc.php:

    $config['smtp_host'] = 'tls://ser1.hostname.xyz:587';
    

    Preserve the configuration by copying config.inc.php to /usr/local/directadmin/custombuild/custom/roundcube/config.inc.php.

  • Set the admin name

    sed -i "0,/name=admin/s//name=${ADMIN_NAME}/" /usr/local/directadmin/data/users/admin/user.conf
    
  • Update CustomBuild settings

    /usr/local/directadmin/custombuild/build set email root@$(hostname -f)
    /usr/local/directadmin/custombuild/build set notifications yes
    /usr/local/directadmin/custombuild/build set updates yes
    /usr/local/directadmin/custombuild/build set webapps_updates yes
    /usr/local/directadmin/custombuild/build set cron_frequency weekly
    /usr/local/directadmin/custombuild/build set use_hostname_for_alias yes
    /usr/local/directadmin/custombuild/build set redirect_host_https yes
    /usr/local/directadmin/custombuild/build set phpmyadmin_public no
    /usr/local/directadmin/custombuild/build phpmyadmin
    /usr/local/directadmin/custombuild/build roundcube
    /usr/local/directadmin/custombuild/build dovecot_conf
    /usr/local/directadmin/custombuild/build rewrite_confs    
    
  • Disable outdated DirectAdmin skin

    touch /usr/local/directadmin/data/skins/enhanced/.disabled    
    
  • Configure CSF Settings

    Set SMTP_BLOCK = "0" in /etc/csf/csf.conf.

  • Custome email templates

    mkdir -p /usr/local/directadmin/data/templates/custom
    wget -O /usr/local/directadmin/data/templates/custom/a_welcome.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/a_welcome.html
    wget -O /usr/local/directadmin/data/templates/custom/u_welcome.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/u_welcome.html
    wget -O /usr/local/directadmin/data/templates/custom/r_welcome.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/r_welcome.html
    
    wget -O /usr/local/directadmin/data/admin/a_welcome.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/a_welcome.html
    wget -O /usr/local/directadmin/data/admin/r_welcome.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/r_welcome.html
    wget -O /usr/local/directadmin/data/users/admin/u_welcome.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/u_welcome.html
    
    wget -O /usr/local/directadmin/data/templates/custom/message_footer.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/message_footer.txt
    wget -O /usr/local/directadmin/data/templates/custom/message_tech.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/message_tech.html
    wget -O /usr/local/directadmin/data/templates/custom/message_user.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/message_user.html
    wget -O /usr/local/directadmin/data/templates/custom/lost_password_email.txt https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/email-templates/lost_password_email.html
    
    wget -O /usr/local/directadmin/data/templates/custom/mail_settings.html https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/misc/mail_settings.html
    wget -O /usr/local/directadmin/data/templates/custom/outlook_setup.reg https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/misc/outlook_setup.reg    
    
  • Configure STMP for admin account

    wget -O /etc/exim.routers.pre.conf http://files.directadmin.com/services/SpamBlocker/smart_route/exim.routers.pre.conf
    wget -O /etc/exim.transports.pre.conf http://files.directadmin.com/services/SpamBlocker/smart_route/exim.transports.pre.conf
    wget -O /etc/exim.authenticators.post.conf http://files.directadmin.com/services/SpamBlocker/smart_route/exim.authenticators.post.conf
    sed -i "s/your@email.com : yourpass/$SMTP_USER : $SMTP_PASS/g" /etc/exim.authenticators.post.conf
    sed -i "s/smtp.yourisp.com/$SMTP_HOST::$SMTP_PORT/g" /etc/exim.routers.pre.conf
    sed -i "s/manualroute/manualroute\n     senders = *@$(hostname -f)/g" /etc/exim.routers.pre.conf    
    
  • Disable the email feature for new accounts to help prevent spam

    mkdir -p /usr/local/directadmin/scripts/custom
    wget -O /usr/local/directadmin/scripts/custom/user_create_post.sh https://raw.githubusercontent.com/powerkernel/directadmin-conf/main/scripts/user_create_post.sh
    chmod 755 /usr/local/directadmin/scripts/custom/user_create_post.sh
    
  • Configure S3 backup

    Create a file /root/.aws/credentials with:

    [default]
    region = nl-ams
    endpoint_url = https://s3.nl-ams.scw.cloud
    aws_access_key_id = yyy
    aws_secret_access_key = xxx
    

    Add an s3 sync script at /usr/local/directadmin/scripts/custom/all_backups_post/s3.sh:

    #!/bin/bash
    
    # This script moves successful Admin backups to an S3 bucket and deletes the local copies.
    
    # Replace with your actual bucket name
    S3_BUCKET_NAME="sg2-hostcp-xyz"
    
    LOCAL_BACKUP_PATH="/home/admin/admin_backups"
    
    # Ensure the script runs only for 'admin' type backups that were successful and where backup is local
    if [ "$type" == "admin" ] && [ "$success" -eq 1 ] && [ "$where" == "local" ]; then
        echo "Backup type is 'admin', the operation was successful, and the location is local."
    
        # Check if the backup directory exists
        if [ -d "$LOCAL_BACKUP_PATH" ]; then
            # Sync the local backup directory to the S3 bucket
            aws s3 sync "$LOCAL_BACKUP_PATH" "s3://$S3_BUCKET_NAME/" --quiet
    
            # Check if AWS sync command was successful
            if [ $? -eq 0 ]; then
                echo "Backup successfully synced to S3 bucket: $S3_BUCKET_NAME"
    
                # Delete local backup files after successful upload to S3
                echo "Deleting local backup files $LOCAL_BACKUP_PATH"
                find "$LOCAL_BACKUP_PATH" -mindepth 1 -delete
                echo "Local backup files deleted."
    
            else
                echo "Error syncing backup to S3 bucket."
                exit 1
            fi
    
        else
            echo "Local backup directory does not exist: $LOCAL_BACKUP_PATH"
            exit 1
        fi
    
    else
        echo "This script only runs for successful 'admin' type backups with local storage."
    fi    
    

    Make the script executable:

    chmod +x /usr/local/directadmin/scripts/custom/all_backups_post/s3.sh
    
  • Set up the CSF Allow and Ignore lists

    Add the following text to /etc/csf/csf.pignore:

    # system whitelist
    user:ubuntu
    user:snap_daemon
    exe:/usr/lib/systemd/systemd
    exe:/usr/lib/systemd/systemd-networkd
    exe:/usr/lib/systemd/systemd-resolved
    exe:/lib/systemd/systemd
    exe:/lib/systemd/systemd-networkd
    exe:/lib/systemd/systemd-resolved
    exe:/usr/bin/clamd
    exe:/usr/bin/freshclam
    exe:/usr/sbin/clamd
    exe:/usr/sbin/rpcbind
    exe:/usr/sbin/uuidd
    exe:/opt/datadog-agent/bin/agent/agent
    exe:/opt/datadog-agent/embedded/bin/trace-agent
    exe:/opt/datadog-agent/embedded/bin/process-agent    
    
  • Custome DNS template

    touch /usr/local/directadmin/data/templates/custom/dns_a.conf
    touch /usr/local/directadmin/data/templates/custom/dns_aaaa.conf
    touch /usr/local/directadmin/data/templates/custom/dns_cname.conf
    touch /usr/local/directadmin/data/templates/custom/dns_mx.conf
    touch /usr/local/directadmin/data/templates/custom/dns_txt.conf    
    

    Edit /usr/local/directadmin/data/templates/custom/dns_a.conf:

    |*if IS_IPV6!="yes"|
    |DOMAIN|.=|IP|
    |*endif|    
    

    Edit /usr/local/directadmin/data/templates/custom/dns_aaaa.conf:

    |*if IS_IPV6="yes"|
    |DOMAIN|.=|IP|
    |*endif|    
    

    Edit /usr/local/directadmin/data/templates/custom/dns_cname.conf:

    www.|DOMAIN|.=|DOMAIN|.
    

    Edit /usr/local/directadmin/data/templates/custom/dns_mx.conf:

    |HOSTNAME|.=10
    

    Edit /usr/local/directadmin/data/templates/custom/dns_txt.conf:

    |DOMAIN|.="v=spf1 include:_spf.|HOSTNAME| ~all"
    _dmarc="v=DMARC1; p=quarantine; rua=mailto:|USERNAME|@|DOMAIN|; ruf=mailto:|USERNAME|@|DOMAIN|"    
    

    Add an SPF record for the server's hostname:

    v=spf1 ip4:x.x.x.x ip6:xxxx ~all
    
  • Finally, rebuild the configuration and restart the server

    /usr/local/directadmin/custombuild/build rewrite_confs
    reboot
    

Conclusion

Now you have a hardened and better-performing DirectAdmin server. The next step could be setting up multiple DNS servers using DirectSlave.

References