Post-Steps: Ubuntu 24.04 Desktop (20240730)
This is a more opinionated post than usual: I live on the command line on Linux. As such, I perform a few extra steps after a fresh installation to get to a “usable” system.
Hosting on a VPS is a great option to run a blogging service, but installing services that might expose ports needs to be done with some precaution (or not at all if the service is only to be used by the server itself).
init
: 20240616)VLE-4
), but the instructions below easily adapt to Linode or Digital Ocean. We will not cover the initial host creation of a Ubuntu 24.04 server VM on your cloud provider of choice.ubuntu
account will get your ssh
key added for login or you will get a temporary password to change at initial login.ubuntu
user password and host details, initialize a new ~/.ssh/config.d/vps
file (you can include all files from the .config.d
directory by adding Include config.d/*
at the top of the ~/.ssh/config
file) and add access details to it:Host vps HostName <VPS-DETAILS> User ubuntu
ssh vps
, change it to a secure one (stored in a password manager if needed) using passwd
. sudo apt update && sudo apt upgrade
followed by a reboot using sudo reboot -h now
ssh-keygen -t ed25519 -c "vps" -f ~/.ssh/id_ed25519-vps
to create one and armor it with a passphrase if preferred (you will want to make sure to add it to your ssh-add
or keychain
if you do). Then copy the public key to the remote host’s .ssh/authorized_keys
:rsync -avR ~/./.ssh/id_ed25519-vps.pub vps:./.ssh/authorized_keys
vps
host.sudo
-capable vpsu
user:sudo adduser vpsu sudo usermod -aG sudo vpsu sudo rsync --archive --chown=vpsu:vpsu ~/.ssh /home/vpsu
vpsu
user: ssh vpsu@vps
sudo nano /etc/ssh/sshd_config
. Port 1327
PermitRootLogin no
PasswordAuthentication no
PasswordAuthentication no
in sudo nano /etc/ssh/sshd_config.d/50-cloud-init.conf
(or other files in that directories, if it is on), see https://askubuntu.com/a/1440509 for detailsvpsu
user: AllowUsers vpsu
(see https://linux.die.net/man/5/sshd_config for more details on this option)sshd
(so you can make modifications in case of issues): sudo service ssh restart
.ssh/config.d/vps
on your host (not the VM) accordingly:Host vps HostName <VPS-DETAILS> User vpsu Port 1327 IdentityFile ~/.ssh/id_ed25519-vps IdentitiesOnly yes
ssh ubuntu@vps
again. You should not only be denied the option to enter a password, but your ubuntu
user should also get Permission denied (publickey)
vps
on the VM, on your host, confirm you can ssh vps
sudo sshd -T | grep "\(ciphers\|macs\|kexalgorithms\)"
. The ciphers
line should only contain chacha
and aes
answers, which are considered secure.ctos
and stoc
( client to server, server to client) supported ciphers by using ssh -vv vps
. The content will be in some of the debug2
lines./etc/ssh/sshd_config
file and add (or alter if one is already present) ciphers
line. For example:ciphers [email protected],aes256-ctr,[email protected]
sudo service ssh restart
sudo sshd -T | grep "\(ciphers\|macs\|kexalgorithms\)"
cloudflared
is the recommended solution. For additional details, see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/cloudflared
's alternate configuration as a ”malware blocking” DNS. cd /tmp wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb sudo dpkg -i cloudflared-linux-amd64.deb rm -f cloudflared-linux-amd64.deb
tmux
(or have another ssh
into the host). In one terminal, start the client:cloudflared proxy-dns --port 5053 --upstream https://1.1.1.2/dns-query --upstream https://1.0.0.2/dns-query
dig @127.0.0.1 -p 5053 cloudflare.com AAAA
cloudflared
command and let’s make this configuration a system service; sudo nano /etc/systemd/system/cloudflared-proxy-dns.service
and use the following:[Unit] Description=DNS over HTTPS (DoH) proxy client Wants=network-online.target nss-lookup.target Before=nss-lookup.target [Service] AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE DynamicUser=yes ExecStart=/usr/local/bin/cloudflared proxy-dns --upstream https://1.1.1.2/dns-query --upstream https://1.0.0.2/dns-query [Install] WantedBy=multi-user.target
sudo systemctl enable --now cloudflared-proxy-dns
sudo service cloudflared-proxy-dns status
/etc/systemd/resolved.conf
 having it use DNS=127.0.0.1
as the only active entry in the [Resolve]
section, then restart the service:sudo service systemd-resolved restart
/etc/systemd/resolved.conf
file and enter the following [Resolve]
section:[Resolve] DNS=9.9.9.9#dns.quad9.net DNS=2620:fe::fe#dns.quad9.net DNS=149.112.112.112#dns.quad9.net DNS=2620:fe::9#dns.quad9.net DNSOverTLS=yes
sudo service systemd-resolved restart
dig +short txt on.quad9.net.
yes.quad9.net.
ufw
(Uncomplicated Firewall) acts as a first line of defense by filtering incoming and outgoing traffic based on predefined rules. fail2ban
monitors the system logs for suspicious activity and automatically bans IP addresses showing malicious behavior. Both ufw
and fail2ban
interact with the underlying iptables
firewall to manage rules and actions. The combination of ufw
and fail2ban
helps protect against various threats, including unauthorized access, brute-force attacks, and other automated attacks.ufw
to deny all incoming connections and allow traffic out:sudo ufw default deny incoming sudo ufw default allow outgoing
ssh
connections using our alternate port:sudo ufw allow 1327
ssh
is added by using sudo ufw show added
ufw
on the host:sudo ufw enable
sudo ufw status numbered sudo udw delete <RULE_NUMBER>
iptables
entry added:sudo iptables -S
docker
commands on this VPS, we want to make sure our firewall rules are not bypassed by Docker. This is currently the case; if we run a service that expose a port, that port is not blocked by ufw
as Docker modified alternate rules with iptables
.iptables
rules to fix this issue.# Download the command sudo wget -O /usr/local/bin/ufw-docker \ https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker sudo chmod +x /usr/local/bin/ufw-docker # Install the iptables sudo ufw-docker install # Restart ufw for those to take effect sudo systemctl restart ufw
ufw
rules is handled differently. From their example:Expose the443
port of the containerhttpd
and the protocol istcp ufw-docker allow httpd 443/tcp
sudo ufw-docker allow containername port/protocol
wg_easy
(”The easiest way to run WireGuard VPN + Web-based Admin UI”), to expose the Wireguard port (51820
) only through our ufw
setup (denying access by default), we would use:# Add the wg-easy (running container name)'s port 51820 with udp to ufw sudo ufw-docker allow wg-easy 51280/udp # if all goes well, we will see an output prompt ending in: Rule added # To confirm the rule is active, use ufw sudo ufw status # will display a new entry with the docker container's IP; for example: # 172.20.0.2 51280/udp ALLOW FWD Anywhere
ufw-docker
as found from their issue board might be https://gist.github.com/deploy595/205ea7985fbf41fe66ab9a082021ed6ajail.local
file:sudo apt install fail2ban cd /etc/fail2ban sudo cp jail.conf jail.local
sudo nano jail.local
:[DEFAULT]
section, modify the bantime
, findtime
and maxretry
to match how aggressively you want to block attempts:# "bantime" is the number of seconds that a host is banned. bantime = 15m # A host is banned if it has generated "maxretry" during the last "findtime" seconds. findtime = 15m # "maxretry" is the number of failures before a host get banned. maxretry = 4
JAILS
section, enable the ones you want. Here we will enable the [sshd]
section by adding enabled=true
in that section, modify the mode
, and change the default port
to match our ssh port change for iptables
to work properly[sshd] enabled=true mode = aggressive port = 1327 logpath = %(sshd_log)s backend = %(sshd_backend)s
sudo systemctl enable fail2ban sudo systemctl start fail2ban sudo systemctl status fail2ban
sudo systemctl status fail2ban â—Ź fail2ban.service - LSB: Start/stop fail2ban Loaded: loaded (/etc/init.d/fail2ban; generated) Active: active (running) since ...
sudo tail -f /var/log/fail2ban.log
you should see the status and services loaded by fail2ban
bantime
of 15 minutes), you can attempt to get it banned to test the tool, ssh fakeuser@<VPS-DETAILS> -p 1327
a few times, and you will see a [sshd] Ban <IP>
entry in the tail
as well as new content when using sudo iptables -S | grep f2b
:-N f2b-sshd -A INPUT -p tcp -m multiport --dports 1327 -j f2b-sshd -A f2b-sshd -s <IP>/32 -j REJECT --reject-with icmp-port-unreachable -A f2b-sshd -j RETURN
bantime
, you will see a [sshd] Unban <IP>
in the log.sudo reboot -h now
then, after the VPS reboots, confirm that ufw
, fail2ban
and iptables
are configured and running:sudo ufw status sudo service fail2ban status sudo iptables -S
logwatch
(which works best if the host can send emails).lynis
and rkhunter
for examples. The details below provide limited details, as the configuration complexity you require depends on your needs. Please refer to each tool's documentation for further instructions. It is also possible to use a tool such as tripwire
, a file integrity monitoring tool, to detect and alert you to unauthorized changes in files or directories, helping you maintain system security and compliance. sudo apt install logwatch sudo mkdir /var/cache/logwatch sudo cp /usr/share/logwatch/default.conf/logwatch.conf /etc/logwatch/conf/
sudo nano /etc/logwatch/conf/logwatch.conf
and alter the file according to your needs (please see the comments below to help you decide) :## if your VPS can send emails out, set your preferred from and to accordingly Output = mail Format = HTML MailTo = [email protected] MailFrom = [email protected] # comment the "mailer" line when using "postfix" ## otherwise go through the configuration file to store results in files that can be consulted manually Output = file Format = text Filename = /tmp/logwatch ## Common # Adapt after testing if you want more detailed content Detail = Low Service = All
sudo logwatch --detail High --range today
lynis
sudo apt install lynis sudo lynis audit system
rkhunter
sudo apt install rkhunter sudo rkhunter --check
ufw-docker
contentciphers
and logwatch