Virtualmin LAMP Server (Ubuntu)

Facebook
Google+
https://www.neblink.net/virtualmin-lamp-server/
Twitter

Building on the basic Ubuntu Cloud Server (with Emerging Threats Protection) we will create an all-in-one internet hosting server using the Virtualmin web hosting control panel.

Add Support for TLSv1.3

The Apache package from Ubuntu 18.04, 16.04, repository isn’t built with OpenSSL 1.1.1. You can manually compile Apache with OpenSSL 1.1.1, but it takes extra time and you have to re-compile when a new version of Apache comes out. Luckily, we can install Apache from a PPA (personal package archive) by Ondřej Surý, who is a Debian developer and an important figure in the DNS community. He maintains many packages for Debian repository, including Apache, BIND, MariaDB, PHP etc. He is also one of the maintainers of the official certbot PPA. So I have trust in his PPA and use it on my servers.

add-apt-repository ppa:ondrej/apache2
apt update


Download & Install Virtualmin

cd /tmp
wget http://software.virtualmin.com/gpl/scripts/install.sh
chmod +rx /tmp/install.sh

Before running the installation script, decide if you will locally host mail for end users (including IMAP/POP clients). The minimal install will exclude the full mail processing stack (SpamAssassin and ClamAV). This will save about 1GB of system resources.

If you will NOT host email, then do a minimal install

/tmp/install.sh --minimal


If you WILL host email, then do the standard full install

/tmp/install.sh

Verify the installation completed successfully.

Setup Firewall

If the UFW firewall was previously setup, uninstall it

apt -y purge ufw


Modify Blocklist Scripts for Firewalld

sed -i '/ipset create/D' /usr/local/bin/country-block.sh 
sed -i '/ipset create/D' /usr/local/bin/emerging-threats-update.sh


Modify firewall startup script

cat > /etc/rc.local <<EOF
#!/bin/sh -e
# rc.local
#
/usr/local/bin/emerging-threats-update.sh
/usr/local/bin/country-block.sh
exit 0
EOF
chmod +rx /etc/rc.local


Setup IP Blocklists

firewall-cmd --permanent --new-ipset=geo-block --type=hash:net
firewall-cmd --permanent --new-ipset=threat-ip --type=hash:ip
firewall-cmd --permanent --new-ipset=threat-net --type=hash:net
firewall-cmd --reload


Create Blocklist Rules

firewall-cmd --permanent --add-rich-rule='rule source ipset=geo-block log prefix="GEO BLOCK:" drop'
firewall-cmd --permanent --add-rich-rule='rule source ipset=threat-ip log prefix="THREAT BLOCK:" drop'
firewall-cmd --permanent --add-rich-rule='rule source ipset=threat-net log prefix="THREAT BLOCK:" drop'
firewall-cmd --reload
service firewalld restart


Move MySQL Data Directory

I like to locate the MySQL data under the /home directory so everything for Virtualmin is under a single path. This can be helpful if you later need to add disk space my moving /home to a separate drive.

Stop the MySQL service

service mysql stop


Create new data directory

mkdir /home/mysql
chown mysql:mysql /home/mysql


Define new data directory in MySQL config file

sed -i '/datadir/c\datadir = /home/mysql' /etc/mysql/mysql.conf.d/mysqld.cnf


Update AppArmor

echo "alias /var/lib/mysql/ -> /home/mysql/," >> /etc/apparmor.d/tunables/alias
service apparmor restart


Initialize New Data Directory (don’t worry, we will add a password during the Virtualmin setup later)

mysqld --initialize-insecure


Start MySQL

service mysql start


Create SQL System Maintenance User. (This will include a new longer random password for the maintenance user)

RANDOM1=`< /dev/urandom tr -dc '[:alnum:]' | head -c${1:-64}`
cp /etc/mysql/debian.cnf /etc/mysql/debian.cnf.bak
cat > /etc/mysql/debian.cnf <<EOF
# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host     = localhost
user     = debian-sys-maint
password = $RANDOM1
socket   = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
host     = localhost
user     = debian-sys-maint
password = $RANDOM1
socket   = /var/run/mysqld/mysqld.sock
basedir  = /usr
EOF
echo "GRANT ALL PRIVILEGES ON *.* TO 'debian-sys-maint'@'localhost' IDENTIFIED BY '$RANDOM1';" | mysql -u root
echo "GRANT PROXY ON ''@'' TO 'debian-sys-maint'@'localhost' WITH GRANT OPTION;" | mysql -u root


Apache Modifications

Enable mod_rewrite

a2enmod rewrite
service apache2 restart


Customization of HTTP request and response headers

cat > /etc/apache2/conf-enabled/security.conf <<EOF
ServerTokens Prod
ServerSignature Off
TraceEnable Off
Header unset ETag
FileETag None
EOF


Enable the mod_headers

a2enmod headers
service apache2 restart


Instruct browsers to allow cacheable content to be fetched from the browser’s cache for up to a week

cat > /etc/apache2/mods-available/expires.conf <<EOF
<IfModule mod_expires.c>
    ExpiresActive On 
    ExpiresDefault "access plus 1 week"
</IfModule>
EOF


Enable the mod_expires

a2enmod expires
service apache2 restart


Allow output from your server to be compressed before being sent to the browser

cat > /etc/apache2/mods-available/deflate.conf <<EOF
<IfModule mod_deflate.c>
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font
AddOutputFilterByType DEFLATE application/x-font-opentype
AddOutputFilterByType DEFLATE application/x-font-otf
AddOutputFilterByType DEFLATE application/x-font-truetype
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
</IfModule>
</IfModule>
EOF


Enable mod_deflate

a2enmod deflate
service apache2 restart


Download and install Google’s Pagespeed Module

cd /tmp
wget https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_amd64.deb
dpkg -i mod-pagespeed-stable_current_amd64.deb
apt-get -f install


Configure Pagespeed

cat > /etc/apache2/mods-available/pagespeed.conf <<EOF
<IfModule pagespeed_module>
ModPagespeed on
AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER text/html
ModPagespeedFileCachePath "/home/.cache/mod_pagespeed/"
ModPagespeedLogDir "/var/log/pagespeed"
ModPagespeedSslCertDirectory "/etc/ssl/certs"
ModPagespeedEnableFilters rewrite_javascript,rewrite_css
ModPagespeedEnableFilters collapse_whitespace,elide_attributes
ModPagespeedFileCacheInodeLimit 500000
ModPagespeedEnableCachePurge on
ModPagespeedPurgeMethod PURGE
<Location /pagespeed_admin>
Order allow,deny
Allow from localhost
Allow from 127.0.0.1
SetHandler pagespeed_admin
</Location>
<Location /pagespeed_global_admin>
Order allow,deny
Allow from localhost
Allow from 127.0.0.1
SetHandler pagespeed_global_admin
</Location>
ModPagespeedStatisticsLogging on
ModPagespeedMessageBufferSize 100000
</IfModule>
EOF


Enable Pagespeed Module

a2enmod pagespeed
service apache2 restart


Install additional PHP Packages

apt -y install php-curl php-zip php-mbstring php-gettext php-zip php-bz2 php-gd


Enable new PHP modules

phpenmod mbstring gettext zip bz2 gd


Virtualmin Post-Installation Wizard

From a web browser, log in to the Virtualmin console at port 10000, using the root user credentials, and complete the Post-Installation Wizard. (https://myserver:10000)

Select System Settings on the left menu, then select the Re-check and refresh configuration button (this may fail if the system was not rebooted after installation of Virtualmin).

Disable Unnecessary Services

If you plan to host DNS elsewhere, disable Bind DNS

systemctl mask bind9


If you plan to host email elsewhere, disable Dovecot Mail Server

systemctl mask dovecot


Disable Proftp server (I strongly encourage the use of ssh-based sftp instead of ftp/ftps)

systemctl mask proftpd


Restrict Mail protocols

If you are not using the Virtualmin’s mail services, then let’s lock down the Postfix SMTP server so it cannot be an attack target. We cannot disable it completely as it will be needed to send outbound email from your server. We configure it so connections are only accepted from the server itself.

From the Virtualmin Console:

  • Select Webmin on the left menu bar
  • Expand the Servers menu and select Postfix Mail Server
  • Select General Options.
  • For the setting Network Interfaces for receiving mail select the second radio button and enter 127.0.0.1 in the box.
  • Select Save and Apply button at the bottom of the page.


SSL Security

The default SSL/TLS configuration for Apache lacks good security. These changes will apply Best Industry Practices and pass compliance checking for PCI, HIPAA, and NIST standards.

Create Diffie-Hellman Key Pairs

openssl dhparam -out /etc/ssl/dhparam.pem 2048


Create New Apache SSL Config File

cp /etc/apache2/mods-available/ssl.conf /etc/apache2/mods-available/ssl.conf.save
cat > /etc/apache2/mods-available/ssl.conf <<EOF
<IfModule mod_ssl.c>
SSLRandomSeed startup builtin
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect builtin
SSLRandomSeed connect file:/dev/urandom 512
AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl .crl
SSLPassPhraseDialog exec:/usr/share/apache2/ask-for-passphrase
SSLSessionCache         shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)
SSLSessionCacheTimeout  300
SSLProtocol -All +TLSv1.2 +TLSv1.3
SSLCipherSuite ECDH+AESGCM+AES128:ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES128:ECDH+AES:DHE+AES128:DHE+AES:!aNULL:!SHA1:!DSS
SSLCipherSuite TLSv1.3 TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
SSLHonorCipherOrder On
SSLOpenSSLConfCmd DHParameters "/etc/ssl/dhparam.pem"
SSLUseStapling on
SSLStaplingCache "shmcb:logs/stapling-cache(150000)"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</IfModule>
EOF


Modify Apache2 Config File

sed -i '/SSLProtocol/D' /etc/apache2/apache2.conf
sed -i '/SSLCipherSuite/D' /etc/apache2/apache2.conf


Restart Apache

service apache2 restart


Setup Default Apache Site to Block IP URL Requests

a2dissite 000-default
openssl req -new -x509 -nodes -out /etc/ssl/snakeoil.pem -keyout /etc/ssl/snakeoil.key -days 3650 -subj '/CN=*'
VirtualHost1=""
VirtualHost2=""
ServerName="ServerName"
ServerAlias="ServerAlias"
AliasFlag=0
for i in `hostname -I`
do
VirtualHost1="$VirtualHost1 $i:80"
VirtualHost2="$VirtualHost2 $i:443"
if [ $AliasFlag = 0 ] ; then
        ServerName="$ServerName $i"
        AliasFlag=1
else
        ServerAlias="$ServerAlias $i"
        AliasFlag=2
fi
done
if [ $AliasFlag = 1 ] ; then
        ServerAlias=""
fi
cat > /etc/apache2/sites-available/000-default.conf << EOF
<VirtualHost $VirtualHost1>
$ServerName
$ServerAlias
DocumentRoot /var/www/html/
RedirectMatch 400 /(.*)\$
ErrorLog /var/log/apache2/default_error_log
CustomLog /var/log/apache2/default_access_log combined
</VirtualHost>
<VirtualHost $VirtualHost2>
$ServerName
$ServerAlias
DocumentRoot /var/www/html/
RedirectMatch 400 /(.*)\$
ErrorLog /var/log/apache2/default_error_log
CustomLog /var/log/apache2/default_access_log combined
SSLEngine on
SSLCertificateFile /etc/ssl/snakeoil.pem
SSLCertificateKeyFile /etc/ssl/snakeoil.key
SSLCACertificateFile /etc/ssl/snakeoil.pem
</VirtualHost>
EOF
a2ensite 000-default
service apache2 restart


Finished – Reboot

reboot


NOTE: After adding a new SSL enabled virtual server in Virtualmin, execute these commands to make sure the new server stays compliant and doesn’t override the SSL Protocol and SSL Ciphersuite that is defined by Apache SSL Config File.

sed -i '/SSLProtocol/D' /etc/apache2/sites-available/*
sed -i '/SSLCipherSuite/D' /etc/apache2/sites-available/*
service apache2 restart
Facebook
Google+
https://www.neblink.net/virtualmin-lamp-server/
Twitter