Pim de Greef

Debian webserver II: install LAMP stack

This tutorial, part of a series that will guide you in making a Debian webserver, will show you how to install the LAMP stack; Apache with PHP and MySQL (MariaDB). The tutorials work on Debian 10 (Buster) but will probably work on older versions and newer versions to come.

Always make sure your system is up to date!

$ sudo apt update && sudo apt upgrade

1. Apache
Install Apache and the Apache utilities and check its status.
$ sudo apt install apache2 apache2-utils
$ systemctl status apache2

If it doesn’t say ‘running’, start it manually. 
$ sudo systemctl start apache2

Enable apache at boot.
$ sudo systemctl enable apache2

Open the default port in ufw (you can check this with $ sudo ufw app info ‘Apache’ )
$ sudo ufw allow 80/tcp

Check to see if the servername is correct.
$ sudo apache2ctl -t

If it doesn’t say ‘Syntax OK’, change the name (best to go with the system’s hostname).
$ sudo vim /etc/apache2/conf-available/servername.conf

ServerName host.name.net

Activate the configuration and reload apache.
$ sudo a2enconf servername.conf
$ sudo systemctl reload apache2

2. MariaDB databaseserver (MySQL)
$ sudo apt install mariadb-server mariadb-client

Check the status.
$ sudo systemctl status mariadb

If it doesn’t say ‘running’, start it manually. 
$ sudo systemctl start mariadb

Make MariaDB start at boot.
$ sudo systemctl enable mariadb

Start the postinstallation script and choose a root password, in most cases you can answer all other questions with yes e.g. ‘Y’.
$ sudo mysql_secure_installation

Try to log in (no password needed).
$ sudo mariadb -u root or $ sudo mysql -u root

Log out with
> exit;

3. PHP (skip this step and go to 3.x if you want to use PHP-FPM)
Install the PHP modules. At the time of writing PHP 7.3 is the latest stable version.
$ sudo apt install php7.3 libapache2-mod-php7.3 php7.3-mysql php-common php7.3-cli php7.3-common php7.3-json php7.3-opcache php7.3-readline

Activate PHP en restart apache.
$ sudo a2enmod php7.3
$ sudo systemctl restart apache2
Test if PHP works with a test file.
$ sudo vim /var/www/html/info.php

<?php phpinfo(); ?>

Save the file and point your browser to it. You should see a long list of information. After that, delete the file, it is a potential security risk (but fine for testing).
$ sudo rm /var/www/html/info.php

3.x PHP-FPM (optional, instead step 3)
Install the PHP-FPM module. At the time of writing PHP 7.3 is the latest stable version.
$ sudo apt install php7.3-fpm

Activate the proxy_fcgi and setenvif modules.
$ sudo a2enmod proxy_fcgi setenvif

Activate PHP-FPM and restart apache.
$ sudo a2enconf php7.3-fpm
$ sudo systemctl restart apache2
Test if PHP works with a test file.
$ sudo vim /var/www/html/info.php

<?php phpinfo(); ?>

Save the file and point your browser to it. You should see a long list of information. After that, delete the file, it is a potential security risk (but fine for testing).
$ sudo rm /var/www/html/info.php

That’s it!

Debian webserver I: basic configuration

This tutorial, part of a series that will guide you in making a Debian webserver, will show you how to prepare your fresh Debian server installation. The tutorials work on Debian 10 (Buster) but will probably work on older versions and newer versions to come. Let’s start with some basic configuration: SSH, setting up an admin account, a firewall and fail2ban.

1. SSH
SSH into your server and update. Then, if needed (in case of a VPS you’ll get a root password from your provider) change the root password. 
# apt update && sudo apt upgrade
# passwd

Create a user with root privileges (sudo).
# adduser admin
# usermod -aG sudo admin

Switch to the new user (admin), whoami should return that this user is a Administrator. Check if it works by opening a new session with the new user in a new tab or screen (ssh admin@serveripordomain). Do not close the old connection untill you verify that the new user works!
# su – admin
$ sudo whoami

Logged in as the user, change the default SSH port by removing the comment (#) in the line before ‘port’ and change the port number (for example to 10000). Then change ‘PermitRootLogin’ to ‘no’.
$ sudo vim /etc/ssh/sshd_config

Restart the service again and then test if it works by making a new connection in a new screen or tab with ‘ssh admin@serveripordomain -p 10000’. Close the old connection if it works.
$ sudo systemctl restart ssh

2. SSH keygen (optional but strongly recommended!)
Create a ssh key on the client computer and choose a password.
$ ssh-keygen -b 4096

Copy the key to the server.
$ ssh-copy-id admin@server -p 1000 (the port you chose in step 3)

Log in with the new key. If this works, change the SSH config again to dissalow password login. Set ‘PasswordAuthentication’ to ‘no’.
$ sudo vim /etc/ssh/sshd_config

Now restart the service. You should test this again.
$ sudo systemctl restart ssh

3. ufw (firewall)
Install ufw (Uncomplicated Firewall), a simple firewall solution.
$ sudo apt install ufw

Add the SSH port from step 3.
$ sudo ufw allow 1000/tcp

Enable IPv6 if your provider supports it.
$ sudo sed -i ‘s/IPV6=no/IPV6=yes/g’ /etc/default/ufw

Set default rules.
$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing

Set logging to low.
$ sudo ufw logging low

Enable the firewall and check the status. You should again check if this works by making a new SSH connection.
$ sudo ufw enable
$ sudo ufw status

4. fail2ban (intrusion prevention)
5. Install fail2ban, enable it and active it at boot. This is fairly simple but effective intrusion prevention software.
$ sudo apt install fail2ban
$ sudo systemctl enable fail2ban
$ sudo systemctl start fail2ban

I recommend to make a portscan ‘definition’. This will help mitigate brute force attacks and (automated) scans. I recommend to learn more about fail2ban.

First let’s create a ‘action rule’. This will actually block perpetrators. If this file already exists, leave it as it is.
$ sudo vim /etc/fail2ban/action.d/ufw.conf

actionstart =
actionstop =
actioncheck =
actionban = ufw deny in from <ip>
actionunban = ufw delete deny in from <ip>

Then we’ll have to make a ‘filter’. This will scan the firewall logs for blocked connections.
$ sudo vim /etc/fail2ban/filter.d/portscan.conf

failregex = UFW BLOCK.* SRC=<HOST>
ignoreregex =

Create your own added configuration file. This will make up the configuration together with the basic configuration in ‘/etc/fail2ban/jail.conf’. This will also be persitent througout fail2ban updates. Enable the portscan jail and whitelist the local IP addresses (and your own?).
$ sudo vim /etc/fail2ban/jail.local

enabled = true
filter = portscan
logpath = /var/log/ufw.log
action = ufw
maxretry = 5

# local IPs
ignoreip =

I suggest that you add the following basic jails to the ‘/etc/fail2ban/jail.conf’ too, above the changes you made just now. We’ll talk about more fail2ban jails (for software we will install later) in a other tutorial.

enabled = true
port = 10000 # or whichever port you chose in step 3 

enabled = true
maxretry = 3

Now reload the configuration.
$ sudo systemctl reload fail2ban

Check the status of the fail2ban service and the status of the jails.
$ sudo systemctl status fail2ban.service
$ sudo fail2ban-client status
$ sudo fail2ban-client status sshd

That’s it for now.

Bufferbloat solution for pfSense

Everybody experienced ‘slow internet’ while someone was download on the same internet connection. This is due to something called ‘Bufferbloat’. The DNS response is slow, games lagg and other services as VoIP en SIP are easily affected too. This latency occurs because routers and other network equipment buffer too much data. Ironically, buffering was a solution to speed up the internet some decades ago. The scheduling algorithm CoDel (controlled delay) is the best solution we have for this problem. Luckily, the guys from pfSense implemented this protocol in there Traffic Shaper!

A more detailed explanation of Bufferbloat can be found here: https://www.bufferbloat.net/projects/bloat/wiki/Introduction/

This tutorial works for pfSense (>2.4.5) but the same principle can be achieved with OpenWRT, DDWRT and ‘gaming routers’ with more advanced options. More about that can be found here: https://www.bufferbloat.net/projects/bloat/wiki/Mitigations_and_solutions_for_Broadband/

You can test you connection, preferably at a quiet time on the network, on Bufferbloat (before and after this tutorial!) at https://www.dslreports.com/speedtest

These are my tests, before and after:

Tutorial (in pfSense 2.4.5)

This works in 2.5.0 (beta) as well and it will probably work in older versions too, which you obviously don’t use.

Step 1
Go to Firewall > Traffic Shaper > Limiters
Click ‘New Limiter’

Enable > select
Name > WANdown
Fill in your connections theoretical bandwith
Queue Management Algorithm > CoDel
Scheduler > FQ_CODEL
Queue length > 1000 (this is a default number, 2000 or 3000 could work better for you)
ECN > select


Step 2
Go to the end of the page and click ‘Add new Queue’

Select Enable
Name > WANdownQ
Queue Management Algorithm > CoDel
ECN > select

Save and Apply Changes

Step 3
Do the exact same but for the upload, with the names ‘WANup’ and ‘WANupQ’ and save it. Apply changes.

Step 4
Go to Firewall > Rules > Floating

Add a new rule above all other (button with arrow up)

Pass Quick > select
Interface > WAN
Direction > out
Protocol > Any
Click Display Advanced
Gateway > WAN_DHCP
In / Out pipe > WANupQ / WANdownQ

If you have a outgoing VPN connection tot a commercial VPN provider, you should not make a floating rule but instead change the LAN rule allowing traffic to the VPN and only make changes to the In/Out pipe. The gateway is probably set to that of the VPN and shouldn’t be changed.

Save and Apply Changes

The result should look like this:

And you’re done! You may ofcourse finetune the upload and download speed. I encourage you to fiddle with it and Google on the subject of Bufferfloat to learn more.

Setting up SpamAssassin

Setting up SpamAssassin is fairly easy. It is a free and open-source SPAM filter written in Perl from the same makers as Apache. This tutorial was made for Ubuntu 18.04 but should work on all (not too old) Ubuntu and Debian versions.

Postfix needs to be installed first.


Let’s begin with the installation.
apt-get install spamassassin spamc

And add a user.
adduser spamd --disabled-login

Configuration of SpamAssassin

Edit the configuration with your favorite editor at /etc/default/spamassassin

OPTIONS="--create-prefs --max-children 5 --username spamd --helper-home-dir /home/spamd/ -s /home/spamd/spamd.log"

Then edit /etc/spamassassin/local.cf to set up SPAM rules

rewrite_header Subject ***** SPAM _SCORE_ *****
report_safe             0
required_score          5.0
use_bayes               1
use_bayes_rules         1
bayes_auto_learn        1
skip_rbl_checks         0
use_razor2              0
use_dcc                 0
use_pyzor               0

Configuration of Postfix

Edit /etc/postfix/master.cf to add a content filter to the SMTP server

smtp      inet  n       -       -       -       -       smtpd
    -o content_filter=spamassassin
spamassassin unix -     n       n       -       -       pipe
    user=spamd argv=/usr/bin/spamc -f -e  
    /usr/sbin/sendmail -oi -f ${sender} ${recipient}

For the changes to take effect, use the following commands:

systemctl restart postfix.service
systemctl enable spamassassin.service
systemctl start spamassassin.service

Optional: train SpamAssassin with sa-learn

You can train SpamAssassin to learn what is SPAM or not (also known as ‘ham’) in your particular situation. You do this with the sa-learn command. It is said that it works the best if you let it scan 3000+ ham or SPAM mails but let’s start with what we’ve got. Use it regularly until all or most of the SPAM doesn’t reach your inbox but your spam folder and if that is set be sure to use it once in a while. Be sure to train (an equal amount of) both spam or ham or SpamAssassin wil be biased towards SPAM if you only let it train on that.

Be careful with this commands so you don’t let it think valid mails are SPAM or vice versa! Make sure your inbox is free of SPAM.

The basic command to train SPAM (for the mbox format) is:
sa-learn --no-sync --spam --mbox ~/mail/spam

This will scan all the mail in the folder /home/user/mail/spam and remember it to be SPAM. To train ham you can use the following commands:
sa-learn --no-sync --ham --mbox ~/mail/mailinglists or
sa-learn --no-sync --ham --mbox ~/mail/family

The inbox in the mbox format is mostly found at /var/spool/mail/[user]

After that you’ll need to let sa-learn rebuild it’s index and clean up after itself with
sa-learn --sync

If you want to see what is in the database now, use
sa-learn --dump magic

The command for the mbx format is:
sa-learn --no-sync [--spam or --ham] --mbx [folder]

the inbox would presumably be reached with:
sa-learn --no-sync --ham --mbx INBOX

And the command for the Maildir format is:
sa-learn --no-sync [--spam or --ham] [folder/{cur,new}]

for example:
sa-learn --no-sync --spam /folder/.INBOX.Spam/{cur,new}

sa-learn --no-sync --ham /folder/.INBOX.family/{cur,new}

This should cover it. Good luck with the setup! Questions? Go to contact or leave a comment.

Install Arch Linux with luks

Install ARCH Linux with encrypted file-system and UEFI

The official installation guide can be found here: https://wiki.archlinux.org/index.php/Installation_Guide

Download the archiso image from https://www.archlinux.org/ and copy to a usb-drive (on linux)
$ dd if=archlinux.img of=/dev/sdX bs=16M && sync

Boot from the usb. If the usb fails to boot, make sure that secure boot is disabled in the BIOS configuration.

This assumes a wifi only system, otherwise use dhcpcd
$ wifi-menu

Create partitions
$ cgdisk /dev/sdX
1st 256MB EFI partition with hex code ef00
2nd 512MB Boot partition with hex code 8300
3rd 100% size partiton (to be encrypted) with hex code 8300

Create filesystems
$ mkfs.vfat -F32 /dev/sdX1
$ mkfs.ext2 /dev/sdX2

Setup the encryption of the system
$ cryptsetup -c aes-xts-plain64 -y – -use-random luksFormat /dev/sdX3
$ cryptsetup luksOpen /dev/sdX3 luks

Create encrypted partitions
This creates one partions for root, modify if /home or other partitions should be on separate partitions
$ pvcreate /dev/mapper/luks
$ vgcreate vg0 /dev/mapper/luks
$ lvcreate – -size 8G vg0 – -name swap
$ lvcreate -l +100%FREE vg0 – -name root

Create filesystems on encrypted partitions
$ mkfs.ext4 /dev/mapper/vg0-root
$ mkswap /dev/mapper/vg0-swap

Mount the new system
$ mount /dev/mapper/vg0-root /mnt
$ swapon /dev/mapper/vg0-swap
$ mkdir /mnt/boot
$ mount /dev/sdX2 /mnt/boot
$ mkdir /mnt/boot/efi
$ mount /dev/sdX1 /mnt/boot/efi

Install the system, a text editor, wpa_supplicant for wifi and some other stuff
$ pacstrap /mnt base base-devel linux linux-firmware lvm2 grub-efi-x86_64 nano git efibootmgr dialog wpa_supplicant net-tools

Generate fstab
$ genfstab -pU /mnt >> /mnt/etc/fstab

Make /tmp a ramdisk (add the following line to /mnt/etc/fstab) and change relatime on all non-boot partitions to noatime (reduces wear if using an SSD)
tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0

Enter the new system
$ arch-chroot /mnt /bin/bash

Setup system clock
$ ln -s /usr/share/zoneinfo/Europe/Amsterdam /etc/localtime
$ hwclock – -systohc – -utc

Set the hostname
$ echo MYHOSTNAME > /etc/hostname

Update locale
$ echo LANG=en_US.UTF-8 >> /etc/locale.conf
$ echo LANGUAGE=en_US >> /etc/locale.conf
$ echo LC_ALL=C >> /etc/locale.conf

Set password for root
$ passwd

Add real user remove -s flag if you don’t whish to use zsh
$ useradd -m -g users -G wheel -s /bin/zsh USERNAME
$ passwd USERNAME

Configure mkinitcpio with modules needed for the initrd image
$ vim /etc/mkinitcpio.conf
Add ‘ext4’ to MODULES
Add HOOKS so it looks like this:
HOOKS=(base udev autodetect keyboard keymap consolefont modconf block lvm2 encrypt filesystems fsck)

Regenerate initrd image
$ mkinitcpio -p linux

Setup grub
$ grub-install

In /etc/default/grub edit the line GRUB_CMDLINE_LINUX to GRUB_CMDLINE_LINUX=”cryptdevice=/dev/sdX3:luks:allow-discards”
Now run:
$ grub-mkconfig -o /boot/grub/grub.cfg

It is safer to use the UUID in GRUB, alternatively you can use that instead of /dev/sdX3
Look up the UUID from /dev/sdX3
$ blkid
Use that in /etc/default/grub: GRUB_CMDLINE_LINUX=”cryptdevice=UUID=UUIDofdevice:vg0 root=/dev/mapper/vg0-root” and run:
$ grub-mkconfig -o /boot/grub/grub.cfg

OPTIONAL (but recommend) SETTINGS – you can skip this and go to ‘finish the installation’

Install a LTS kernel for more stability
$ pacman -Syu linux-headers linux-lts

Install Advanced Linux Sound Architecture (ALSA)
$ pacman -Syu alsa-utils pulseaudio
$ pacman -Syu alsa-oss alsa-lib

ALSA by default has all channels muted, all of which will need to be unmuted manually. This can be done using amixer:
$ amixer sset Master unmute

To check and make sure your speakers are working, just run:
$ speaker-test -c 2

Install graphical driver

First of all, we’ll install “xorg” and “mesa” packages utilities.
$ pacman -Syu mesa mesa-libgl
$ pacman -Syu xorg xorg-server

Install the driver you need

Install Radeon Drivers – or
$ pacman -Syu xf86-video-ati

Install Nvidia Drivers – or
$ pacman -Syu nvidia nvidia-utils nvidia-settings opencl-nvidia

Install Intel Drivers – or
$ pacman -Syu xf86-video-intel

Install Default Drivers
$ pacman -Syu xf86-video-vesa

Install a Desktop Environment

Install Gnome Desktop
$ pacman -Syu gnome gnome-extra
$ systemctl enable gdm.service

Install KDE Desktop
$ pacman -Syu plasma-meta plasma-wayland-session kde-applications-meta
$ systemctl enable sddm.service

Install XFCE Desktop
$ pacman -Syu xfce4 xfce4-goodies lightdm lightdm-gtk-greeter
$ systemctl enable lightdm.service

Install Mate Desktop
$ pacman -Syu mate mate-extra lightdm lightdm-gtk-greeter
$ systemctl enable lightdm.service

Install Cinnamon Desktop
$ pacman -Syu cinnamon lightdm lightdm-gtk-greeter
$ systemctl enable lightdm.service

Install LXDE Desktop
$ pacman -Syu lxde lxdm
$ systemctl enable lxdm.service


Exit new system and go into the cd shell
$ exit

Unmount all partitions
$ umount -R /mnt
$ swapoff -a

Reboot into the new system, don’t forget to remove the cd/usb
$ reboot