Xavi.app Operations Manual (outdated)

Prerequisites

  • Ubuntu 24.04 LTS, RHEL, or compatible Linux server instance

  • Domain A record (e.g.,cockpit.example.com) pointing to your server

  • A non-root user with sudo privileges (e.g.,yourusername)

  • Hardware virtualization enabled (VT-x/AMD-V) for VM management
    (verify with egrep -c '(vmx|svm)' /proc/cpuinfo )

Install Cockpit and Extensions (Ubuntu & RHEL)

Cockpit (plus cockpit-machines for KVM VMs and cockpit-podman for containers) is in the default repositories of both Ubuntu 24.04 and RHEL 9 / Alma 9. Libvirt and QEMU packages are required for full VM support.

Step

Ubuntu 24.04 LTS

RHEL 9 / Alma 9

Description

1 – Refresh repos

sudo apt update
sudo dnf update -y

Update package index

2 – Install Cockpit
+ Podman & Machines

sudo apt install -y \
cockpit cockpit-machines cockpit-podman
sudo dnf install -y \
cockpit cockpit-machines cockpit-podman

Web console, VM module, container module

3 – Install libvirt / QEMU

sudo apt install -y \
libvirt-daemon-system qemu-kvm
sudo dnf install -y \
libvirt qemu-kvm virt-install

Enables KVM hypervisor support

4 – Start services

sudo systemctl enable --now \
cockpit.socket libvirtd
sudo systemctl enable --now \
cockpit.socket libvirtd

Launch Cockpit & libvirt immediately
(and at boot)

5 – Open firewall
(optional but recommended)

# Ubuntu/nftables example
sudo nft add rule inet filter input \
tcp dport 9090 accept
# RHEL / firewalld
sudo firewall-cmd --add-service=cockpit
sudo firewall-cmd --add-service=cockpit --permanent
sudo firewall-cmd --reload

Allow Cockpit port 9090/TCP (optional)

6 – Verify

sudo systemctl status cockpit.socket
sudo systemctl status libvirtd

should showactive (running)


Access the Cockpit Web Dashboard

  1. Open https://localhost:9090in your browser (substitute your domain or IP).

  2. Log in with your sudo-enabled user (root login is disabled in the web UI).

  3. Click Turn on administrative access in the sidebar for privileged actions (requires re-entering your password).

Core Features in Cockpit

  • Overview:Monitor system health, CPU/memory usage, and basic configuration.

  • Storage:Manage disks, partitions, and file systems; view I/O stats and logs.

  • Services:Control systemd services and timers (start, stop, enable, etc.).

  • Networking:Configure interfaces, monitor bandwidth, and check logs.

  • Logs:Search and filter logs by severity, date, or source.

  • Accounts:Add, edit, or remove user accounts and groups.

  • Terminal:Access a web-based shell for CLI management.

  • Virtual Machines:Create and manage KVM/QEMU VMs with cockpit-machines.

  • Podman Containers:Visually manage containers (start, stop, logs) with cockpit-podman.


Enable Google Authenticator (TOTP) for Cockpit on Ubuntu 24.04

Why Add MFA?

Cockpit exposes full system-level controls (terminal, VM power-off, package installs, etc.). Adding time-based one-time passwords (TOTP) significantly reduces the risk of compromise if your primary password is leaked.

Prerequisites

  • Sudo-enabled account on the Ubuntu host running Cockpit.

  • Google Authenticator, FreeOTP, or any TOTP-compatible app on your phone.


Step1 — Install Required Packages

sudo apt update
sudo apt install -y libpam-google-authenticator libqrencode-dev


Step2 — Generate Your TOTP Secret & Scratch Codes

google-authenticator -t -d -f -r 3 -R 30 -W -Q UTF8
  • Scan the displayed QR code with your authenticator app.

  • Record the fivescratch codesin a safe place—they bypass MFA if you lose the phone.

Step3 — Require MFA for Cockpit via PAM

# Append the Google-Authenticator PAM module to Cockpit's stack
sudo bash -c 'echo "auth required pam_google_authenticator.so nullok" >> /etc/pam.d/cockpit'

Note: Add the linebeforerestarting Cockpit, otherwise existing sessions may be interrupted.

Step4 — Restart Cockpit

sudo systemctl restart cockpit

Step5 — Log In with MFA

  • Browse to https://your-server:9090and sign in with your Linux user credentials.

  • When prompted for the verification code, open your authentication app and enter the 6-digit TOTP.

Troubleshooting Tips

  • Locked Out? Log in via SSH and remove the last line from /etc/pam.d/cockpit, then restart Cockpit.

  • Multiple Admins? Each admin must run google-authenticator under their own account.

  • Scratch Codes Exhausted? Rerun the command in Step2 to regenerate a new secret.


Publish Cockpit Behind Nginx with Let’s Encrypt

The safest way to expose Cockpit publicly is to place it behind an Nginx reverse-proxy that terminates TLS on ports80/443 and forwards traffic to the local Cockpit service (TCP9090). The high-level workflow is:

  1. Install Nginx and the Certbot Nginx plugin.

  2. Create atemporaryHTTP-only virtual host (port 80) for cockpit.example.com.

  3. Run certbot --nginxto request a certificate & let Certbot update the vhost for SSL.

  4. Replace the generated location / block with the Cockpit reverse-proxy stanza shown below, then reload Nginx.

1. Install Prerequisites

Ubuntu 24.04

RHEL 9 / Alma 9

sudo apt update
sudo apt install -y nginx python3-certbot-nginx
sudo dnf install -y nginx python3-certbot-nginx
sudo systemctl enable --now nginx


2. Create a Minimal Port-80 vHost

Save /etc/nginx/sites-available/cockpit.temp (Ubuntu) or /etc/nginx/conf.d/cockpit.temp.conf (RHEL):

server {
 listen 80;
 server_name cockpit.example.com;
 root /var/www/html; # any directory; not used after TLS
}


Enable and test, then reload:

# Ubuntu
sudo ln -s /etc/nginx/sites-available/cockpit.temp \
 /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx


3. Obtain the Certificate

sudo certbot --nginx -d cockpit.example.com -m admin@example.com --agree-tos
  • Certbot inserts a new server block with listen 443 ssl, certificate paths, and a redirect from HTTP→HTTPS.

4. Add the Cockpit Proxy Stanza

  • Edit the new HTTPS server block (file path as created by Certbot) so that the location /looks like this:

location / {
 proxy_pass https://127.0.0.1:9090;
 proxy_set_header Host $host;
 proxy_set_header X-Forwarded-Proto $scheme;
 # WebSocket support
 proxy_http_version 1.1;
 proxy_buffering off;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection "upgrade";
 # Prevent gzip mangling of ETags (Cockpit bug #5239)
 gzip off;
}


When finished:

sudo nginx -t && sudo systemctl reload nginx


5. Firewall / SELinux Notes

  • Ubuntu nftables: ensure tcp dport {80,443}are accept.

  • RHEL firewalld: sudo firewall-cmd --permanent --add-service=http --add-service=https
    then sudo firewall-cmd --reload.

  • If SELinux is enforcing, allow Nginx to proxy:

sudo setsebool -P httpd_can_network_connect 1


6. Renewals & Maintenance

Certbot installs a systemd timer that runs twice daily. Verify:

sudo systemctl list-timers | grep certbot

You can test a dry-run any time:

sudo certbot renew –dry-run


 

Secure Web Servers with Let’s Encrypt SSL Certificates (Certbot)


This is a general description of the various command features involved in using the Lets Encrypt Certbot to generate SSL Certificates.

Command

Description

sudo apt install -y snapd

Install Snapd package manager

sudo snap install core; sudo snap refresh core

Update core snap

sudo snap install --classic certbot

Install Certbot

sudo ln -s /snap/bin/certbot /usr/bin/certbot

Create symlink for Certbot (if missing)

sudo certbot certonly --standalone -d example.com -m admin@example.com --agree-tos

Generate SSL certificate (default configuration)

sudo cat /etc/letsencrypt/live/example.com/fullchain.pem /etc/letsencrypt/live/
example.com/privkey.pem > cockpit.cert

Combine certificate and key

sudo certbot --nginx -d www.example.com -d example.com -m admin@example.com --agree-tos

Generate SSL certificate for NGINX reverse proxy. (suggested configuration)

sudo systemctl restart nginx

Restart nginx to apply the certificate

sudo certbot renew --dry-run

Test certificate renewal


Automate Renewal:Certbot includes a systemd timer, but you can add a cron job for redundancy:

sudo crontab -e
# Add: 0 0 * * * /usr/bin/certbot renew --quiet


Installing Certbot with Nginx Module

For servers running Nginx (e.g., hosting web apps), use Certbot’s Nginx plugin to automate SSL setup for Nginx-hosted domains.

Command

Description

sudo apt install -y python3-certbot-nginx

Install Certbot Nginx module

sudo certbot --nginx -d example.com -d www.example.com

Configure SSL for Nginx domains

Run sudo certbot renew to renew all certificates.


Managing Virtual Machines via Cockpit (KVM/QEMU)

  • Ensure libvirt-daemon-systemandcockpit-machines are installed.

  • In Cockpit, go to Virtual Machines.

  • ClickC reate New VM and fill in:

  • Name: Ubuntu-VM

  • Installation Source: /path/to/ubuntu.iso

  • OS:Ubuntu 24.04

  • Memory: 4096 MB

  • Storage: 20 GB

  • Start the VM and use the Console tab to install the OS.

  • Manage VM state and resources from the UI.

Troubleshooting: If VMs fail to start, verify hardware virtualization in BIOS and check sudo systemctl status libvirtd.

Managing Containers with Cockpit-Podman

  • In Cockpit, select Podman Containers.

  • Click Create Container and enter:

  • Name: nginx-test

  • Image: docker.io/library/nginx

  • Port Mapping: 8080 (host) to 80 (container)

  • Click Create and Run, then test at http://your-server-ip:8080.

  • View logs and manage the container in the UI.

Troubleshooting: If inaccessible, ensure port 8080 is open in your firewall.

Switch to Pure nftables (replace ufw / iptables wrappers)

Ubuntu 24.04 LTS

# 1 – Install native CLI (kernel modules already present)
sudo apt update
sudo apt install -y nftables

# 2 – Disable UFW if it’s running
sudo ufw disable
sudo systemctl mask ufw

# 3 – (Optional) Remove legacy iptables wrappers
sudo apt purge iptables iptables-persistent

# 4 – Enable nftables so your ruleset loads at boot
sudo systemctl enable --now nftables

RHEL 9 / Alma 9

# Install CLI and start service
sudo dnf install -y nftables
sudo systemctl enable --now nftables

# (Optional) remove iptables compatibility layer
sudo dnf remove iptables iptables-services

Optional Extras

Package Purpose
conntrack / conntrack-tools CLI to view or flush the connection-tracking table.
nftables-doc Offline man-pages and examples.
python3-nftables Python bindings for automated rule generation.

Key Take-aways

  • No extra kernel modulesnf_conntrack, nf_nat, etc., are built into the stock kernel and auto-load on demand.
  • nftables package = user-space CLI + systemd service; that’s all you install.
  • Removing ufw/iptables is optional but prevents confusion between legacy wrappers and native nft syntax.

 

Firewall Configuration with nftables

Configure nftables to allow Cockpit and other services:

Command

Description

sudo nano /etc/nftables.conf

Edit nftables rules

#!/usr/sbin/nft -f
###############################################################################
# COMPLETE EXAMPLE NFTABLES CONFIG
#───────────────────────────────────────────────────────────────
# • Host is a KVM hypervisor running libvirt’s default NAT network
# • Guest subnet: 10.0.0.0/24 (bridge interface: “virbr0”)
# • Public IPs :
# 203.0.113.2 ← MGMT_IP (SSH + Cockpit + HTTPS for the host)
# 203.0.113.3 ← NAT_IP1 (DNAT for multiple guests)
# 203.0.113.4 ← NAT_IP2 (DNAT for a single guest)
#
# Replace the addresses / interface names to suit your environment.
###############################################################################
###############################################################################
# FILTER TABLE (inet) – Host firewall, VM isolation, Fail2Ban hook
###############################################################################
table inet filter {
 ############################ Fail2Ban dynamic set #########################
 set f2b_blocklist { # Filled by Fail2Ban actions
 type ipv4_addr
 comment "Fail2Ban—blocked sources"
 }
 chain f2b-chain { # Executes early (priority -1)
 type filter hook input priority -1; policy accept;
 ip saddr @f2b_blocklist drop
 }
 ###########################################################################
 chain input {
 type filter hook input priority 0; policy drop;
 # ── Hygiene ────────────────────────────────────────────────────────
 ct state invalid drop
 ct state {established,related} accept
 iif lo accept
 ip protocol icmp icmp type echo-request accept
 ip6 nexthdr icmpv6 icmpv6 type echo-request accept
 # ── Host-level services (SSH/HTTP/HTTPS/Cockpit) ───────────────────
 ip daddr 203.0.113.2 tcp dport {22,80,443,9090} accept
 # ── UDP reflection / amplification guard ───────────────────────────
 ip daddr {203.0.113.3,203.0.113.4} udp dport {53,67} drop
 # ── Public services DNAT → VMs (NAT_IP1) ───────────────────────────
 ip daddr 203.0.113.3 tcp dport {23-28,80,443,1935,3306} accept
 # ── Public services DNAT → single VM (NAT_IP2) ─────────────────────
 ip daddr 203.0.113.4 tcp dport {22,80,443} accept
 # ── DNS / DHCP from guests (libvirt NAT) ───────────────────────────
 iif "virbr0" udp dport {53,67} accept
 # ── Default: log (rate-limited) then drop ──────────────────────────
 log prefix "NFT-INPUT-DROP: " group 1 limit rate 1/second drop
 }
 chain forward {
 type filter hook forward priority 0; policy drop;
 # Standard conn-tracking
 ct state invalid drop
 ct state {established,related} accept
 # ── Isolate VM 10.0.0.130 from other guests ────────────────────────
 ip saddr 10.0.0.130 ip daddr 10.0.0.0/24 iif "virbr0" oif "virbr0" drop
 ip daddr 10.0.0.130 ip saddr 10.0.0.0/24 iif "virbr0" oif "virbr0" drop
 # Guest ↔ Guest (except isolated VM)
 iif "virbr0" oif "virbr0" accept
 # Guest → Internet
 iif "virbr0" oif {"bridge0","eno1","eno2"} accept
 # Internet (post-DNAT) → Guest
 iif {"bridge0","eno1","eno2"} oif "virbr0" accept
 # ICMP inside libvirt network
 ip saddr 10.0.0.0/24 ip protocol icmp accept
 ip daddr 10.0.0.0/24 accept
 # Catch-all
 log prefix "NFT-FORWARD-DROP: " group 1 limit rate 1/second drop
 }
 chain output { # Host-originated traffic
 type filter hook output priority 0; policy accept;
 }
}
###############################################################################
# NAT TABLE (ip) – DNAT, SNAT, SYN-flood protection
###############################################################################
table ip nat {
 chain prerouting {
 type nat hook prerouting priority -100; policy accept;
 # ───────────────── 1) GLOBAL SYN caps (TLS) ───────────────────────
 ip daddr {203.0.113.3,203.0.113.4} tcp dport 443 tcp flags syn \
 limit rate over 100/second burst 200 packets \
 log prefix "NFT-SYNFLOOD-443-GLOBAL: " drop
 # ───────────────── 2) Per-source caps for HTTP/HTTPS ──────────────
 ip daddr 203.0.113.3 tcp dport {80,443} tcp flags syn \
 meter syn80_1133 size 10k { ip saddr limit rate over 10/second burst 20 packets } \
 log prefix "NFT-SYN-80/443-1133: " limit rate 1/second drop
 ip daddr 203.0.113.4 tcp dport {80,443} tcp flags syn \
 meter syn80_1134 size 10k { ip saddr limit rate over 10/second burst 20 packets } \
 log prefix "NFT-SYN-80/443-1134: " limit rate 1/second drop
 # ───────────────── 2b) Per-source caps for SSH ────────────────────
 ip daddr 203.0.113.2 tcp dport 22 tcp flags syn \
 meter synssh_host size 10k { ip saddr limit rate over 5/second burst 10 packets } \
 log prefix "NFT-SYN-SSH-HOST: " limit rate 1/second drop
 ip daddr 203.0.113.3 tcp dport 22-28 tcp flags syn \
 meter synssh_pub1 size 10k { ip saddr limit rate over 5/second burst 10 packets } \
 log prefix "NFT-SYN-SSH-PUB1: " limit rate 1/second drop
 ip daddr 203.0.113.4 tcp dport 22 tcp flags syn \
 meter synssh_pub2 size 10k { ip saddr limit rate over 5/second burst 10 packets } \
 log prefix "NFT-SYN-SSH-PUB2: " limit rate 1/second drop
 # ───────────────── 3) DNAT to internal guests ─────────────────────
 # NAT_IP1 (203.0.113.3) → Multiple VMs
 ip daddr 203.0.113.3 tcp dport 23 dnat to 10.0.0.209:22
 ip daddr 203.0.113.3 tcp dport 24 dnat to 10.0.0.37:22
 ip daddr 203.0.113.3 tcp dport 25 dnat to 10.0.0.215:22
 ip daddr 203.0.113.3 tcp dport 26 dnat to 10.0.0.168:22
 ip daddr 203.0.113.3 tcp dport 27 dnat to 10.0.0.128:22
 ip daddr 203.0.113.3 tcp dport 28 dnat to 10.0.0.45:22
 ip daddr 203.0.113.3 tcp dport 80 dnat to 10.0.0.209:80
 ip daddr 203.0.113.3 tcp dport 443 dnat to 10.0.0.209:443
 ip daddr 203.0.113.3 tcp dport 1935 dnat to 10.0.0.128:1935
 ip daddr 203.0.113.3 tcp dport 3306 dnat to 10.0.0.209:3306
 # NAT_IP2 (203.0.113.4) → Single VM
 ip daddr 203.0.113.4 tcp dport {22,80,443} dnat to 10.0.0.130
 }
 chain postrouting {
 type nat hook postrouting priority 100; policy accept;
 # SNAT / masquerade – Guests → Internet
 ip saddr 10.0.0.0/24 oif {"bridge0","eno1","eno2"} snat to 203.0.113.3
 ip saddr 10.0.0.130 oif {"bridge0","eno1","eno2"} snat to 203.0.113.4
 }
}
###############################################################################
# END OF FILE
###############################################################################

Example advanced nftables.conf


 

Key capabilities:

Fail2Ban integration
Dedicatedf2b_blocklistset and early-hook chain to drop IPs banned by Fail2Ban.

Baseline packet hygiene
Drops invalid connections; allows established/related traffic and local loopback.

ICMP/ICMPv6 echo-request (ping) accepted.

 

Host-level management access
Permits SSH (22), HTTP (80), HTTPS (443) and Cockpit (9090) on the management IP only.

 

UDP reflection / amplification guard
Blocks external UDP 53 & 67 traffic to public service Ips.

 

Public services to multiple guests (NAT IP-1)
DNAT rules for ports 23-28 (SSH variants), 80/443, 1935 (RTMP) and 3306 (MariaDB).

 

Public services to a single guest (NAT IP-2)
DNAT rules for SSH, HTTP and HTTPS to one designated VM.

 

Per-source & global SYN-flood protection
Global cap on TLS (443) for both public Ips.
Meters for HTTP/HTTPS and SSH on each IP with burst/pps limits.

 

Guest network isolation

Completely blocks traffic to/from a specific VM (10.0.0.130) from other guests.

Inter-guest & guest-Internet forwarding

Allows guest-to-guest traffic (except isolated VM) and guest-to-Internet via uplinks.

Accepts Internet traffic already DNATed to guests.

Guest DHCP/DNS passthrough
Permits UDP 53/67 fromvirbr0to the host.

SNAT / Source NAT
Outbound guest traffic is rewritten to the relevant public IP (different SNAT for isolated VM).

 

Logging & rate-limiting
Rate-limited logging for dropped packets in INPUT and FORWARD chains.

Logs SYN-flood drops with clear prefixes for easy syslog filtering.

IPv4 + IPv6 coverage
Uses a singleinetfilter table to handle both protocol families consistently.

Commented, modular structure
Clear section headers and comments simplify future editing or auditing.

sudo nft flush ruleset

Flushes the Ruleset

sudo nft -f /etc/nftables.conf

Applythe Ruleset Persistently, is loaded on system boot.

sudo nft list ruleset

Lists the active Ruleset

sudo systemctl restart fail2ban

Restart services that modify the nftables.conf
This must be carried out after every reload.


 

Install and Configure Fail2Ban on Ubuntu 24.04 & RHEL 9

1. Install Packages

OS / Repo

Command

Description

Ubuntu 24.04

sudo apt update
sudo apt install -y fail2ban

Install Fail2Ban from the main repo

RHEL 9 / Alma 9

sudo dnf install -y epel-release
sudo dnf install -y fail2ban

Enable EPEL and install Fail2Ban


 

2. Create jail.local

Enable basic SSH protection and integrate with nftables:

# Default banning action (e.g. iptables, iptables-new,
# iptables-multiport, shorewall, etc) It is used to define
# action_* variables. Can be overridden globally or per
# section within jail.local file
banaction = nftables-block
banaction_allports = nftables-block
# The simplest action to take: ban only
action_ = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report to the destemail.
action_mw = %(action_)s
 %(mta)s-whois[sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report and relevant log lines
# to the destemail.
action_mwl = %(action_)s
 %(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"]
#
# JAILS
#
[sshd]
#mode = normal
port = ssh,23,24,25,26,27,28
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 2
findtime = 10m
bantime = 1d
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd
action = %(action_)s
filter = sshd[mode=aggressive]
[nftables-synflood]
enabled = true
filter = nftables-synflood
logpath = /var/log/kern.log
maxretry = 5
findtime = 300
bantime = 3600
journalmatch = _SYSTEMD_UNIT=nftables.service + PRIORITY=5
action = %(action_mwl)s
[nftables-invalid]
enabled = true
filter = nft-invalid
logpath = /var/log/kern.log
maxretry = 5
findtime = 60
bantime = 6h
journalmatch = _SYSTEMD_UNIT=nftables.service + PRIORITY=5
action = %(action_mwl)s
[nftables-udpblock]
enabled = true
filter = nft-udpblock
logpath = /var/log/kern.log
maxretry = 10
findtime = 30
bantime = 1h
journalmatch = _SYSTEMD_UNIT=nftables.service + PRIORITY=5
action = %(action_mwl)s
[cockpit]
enabled = true
filter = cockpit
journalmatch = _SYSTEMD_UNIT=cockpit.service
port = 9090
maxretry = 3
findtime = 10m
bantime = 12h
action = %(action_mwl)s
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
journalmatch = _SYSTEMD_UNIT=fail2ban.service + PRIORITY=5
backend = systemd
bantime = 4w
findtime = 1d
maxretry = 3
action = %(action_mwl)s
[pam-generic]
enabled = true
filter = pam-generic
logpath = /var/log/auth.log # systemd backend will read journal anyway
backend = systemd
journalmatch = _SYSTEMD_UNIT=systemd-logind.service
maxretry = 5
findtime = 10m
bantime = 12h
action = %(action_mwl)s

3. Create /action.d/nftables-block.local

[Definition]
actionstart = nft add table inet filter 2>/dev/null || true
 nft add chain inet filter f2b-chain { type filter hook input priority -1 \; policy accept \; } 2>/dev/null || true
 nft add set inet filter blocklist { type ipv4_addr \; } 2>/dev/null || true
 nft add rule inet filter f2b-chain ip saddr @blocklist drop 2>/dev/null || true
actionflush =
actionstop = nft flush set inet filter blocklist 2>/dev/null || true
actioncheck = nft list chain inet filter f2b-chain | grep -q '@blocklist' 2>/dev/null || true
actionban = nft add element inet filter blocklist { <ip> } 2>/dev/null || true
actionunban = nft delete element inet filter blocklist { <ip> } 2>/dev/null || true
[Init]
table = filter
table_family = inet
chain = f2b-chain
chain_type = filter
chain_hook = input
chain_priority = -1
addr_type = ipv4_addr
addr_set = blocklist
blocktype = drop
nftables = nft


4. Start & Enable the Service

sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd # verify jail is active


Delayed Restart for Fail2Ban (ensures jails reload after nftables)

On Ubuntu 24.04 we add aone-shotsystemd service that waits until the network and nftables.service are up, then restarts Fail2Ban 30seconds after boot. This guarantees all persistent bans are re-inserted into the live nftables ruleset on server boot.

1. Create the helper script: /usr/local/bin/restart-fail2ban-delayed.sh

#!/usr/bin/env bash
# Wait 30 seconds to ensure network and nftables are up
sleep 30
# Restart Fail2Ban to restore banned IPs
systemctl restart fail2ban


Run:

sudo install -m 750 -o root -g root /usr/local/bin/restart-fail2ban-delayed.sh


2. Create the systemd unit

# /etc/systemd/system/restart-fail2ban-delayed.service
[Unit]
Description=Restart Fail2Ban 30 seconds after boot
After=network-online.target nftables.service
Wants=network-online.target nftables.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/restart-fail2ban-delayed.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target


3. Enable and start

sudo systemctl daemon-reload
sudo systemctl enable --now restart-fail2ban-delayed.service


4. Verify

# Should show “active (exited)”
systemctl status restart-fail2ban-delayed.service
# Confirm Fail2Ban was restarted once the 30 s elapsed
journalctl -u fail2ban -b | grep "Started Fail2Ban"


Why this matters:

  • fail2ban starts early in the boot sequence; if nftables loads afterward, latent bans are not present in the kernel firewall.

  • This service guarantees bans are re-applied after the nftables ruleset is fully loaded.

  • The solution is distro-neutral—works the same on RHEL 9/Alma 9 by copying the same unit and helper script.

Useful Commands

  • sudo fail2ban-client status – list all active jails.

  • sudo fail2ban-client set sshd unbanip <IP> – manually unban an IP.

  • Logs: /var/log/fail2ban.log.


Install PHP 8.3-FPM and Essential Modules

Add Repositories (if required)

  • Ubuntu 24.04:8.3 is already in the main archive.

  • RHEL 9:Enable Remi PHP 8.3:

sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo dnf module reset php -y
sudo dnf module enable php:remi-8.3 -y


2. Install FPM and Modules

OS

Command

Ubuntu 24.04

sudo apt install -y php8.3-fpm php8.3-{cli,common,mysql,gd,xml,curl,mbstring,zip,intl,imagick,opcache}

RHEL 9 / Alma 9

sudo dnf install -y php-fpm php-{cli,common,mysqlnd,gd,xml,curl,mbstring,zip,intl,imagick,opcache}


3. Enable & Tune FPM

# Start and enable:
sudo systemctl enable --now php-fpm
# Performance tuning (Ubuntu path shown):
sudo sed -i 's/^;?pm.max_children = .*/pm.max_children = 20/' \
 /etc/php/8.3/fpm/pool.d/www.conf
sudo systemctl restart php-fpm


Verify with php -v and systemctl status php8.3-fpm.


Install MariaDB 11 and phpMyAdmin

1. Add MariaDB Official Repository

# Ubuntu 24.04
sudo apt install -y software-properties-common curl
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | sudo bash -- --mariadb-server-version=11.5
# RHEL 9 / Alma 9
sudo curl -o /etc/yum.repos.d/MariaDB.repo \
https://r.mariadb.com/downloads/mariadb_repo_setup && \
sudo bash /etc/yum.repos.d/MariaDB.repo --mariadb-server-version=11.5

2. Install and Secure MariaDB

Command

Description

sudo apt install -y mariadb-server # Ubuntu
sudo dnf install -y MariaDB-server # RHEL

Install server package

sudo systemctl enable --now mariadb
sudo mysql_secure_installation

Start service & run security script


3. Install phpMyAdmin (Manual Deploy)

cd /opt
sudo wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz
sudo tar xzf phpMyAdmin-*-all-languages.tar.gz
sudo mv phpMyAdmin-*-all-languages /var/www/html/phpmyadmin
sudo cp /var/www/html/phpmyadmin/config.sample.inc.php \
 /var/www/html/phpmyadmin/config.inc.php
sudo nano /var/www/html/phpmyadmin/config.inc.php
/* set a random 32-char blowfish secret */
sudo chown -R www-data:www-data /var/www/html/phpmyadmin
sudo systemctl reload nginx
  1. Download latest release:

  2. Create a config.inc.php (quick setup):

  3. Set permissions and reload PHP-FPM / Nginx:

4. (Optional) Protect phpMyAdmin with Basic Auth

# Create password file
sudo htpasswd -c /etc/nginx/.htpasswd pmaadmin
# In your server block:
location /phpmyadmin {
 auth_basic "Restricted";
 auth_basic_user_file /etc/nginx/.htpasswd;
 # …existing PHP-FPM configuration…
}

Quick Reference: Components & Commands

Component / Tool

Ubuntu 24.04 Install

RHEL 9 / Alma 9 Install

Main Config / URL / Menu

Fail2Ban

sudo apt install fail2ban
sudo dnf install epel-release fail2ban

/etc/fail2ban/

PHP 8.3-FPM

sudo apt install php8.3-fpm
sudo dnf install php-fpm

/etc/php/8.3/fpm/

MariaDB 11

sudo apt install mariadb-server
sudo dnf install MariaDB-server

/etc/mysql/

phpMyAdmin

Download tarball →/var/www/html/phpmyadmin

config.inc.php

Cockpit

sudo apt install cockpit
sudo dnf install cockpit

https://your-server:9090

Cockpit-Machines

sudo apt install cockpit-machines
sudo dnf install cockpit-machines

“Virtual Machines” menu

Cockpit-Podman

sudo apt install cockpit-podman
sudo dnf install cockpit-podman

“Podman Containers” menu

Certbot (stand-alone)

sudo snap install --classic certbot
sudo dnf install certbot

/etc/letsencrypt/

Certbot Nginx Module

sudo apt install python3-certbot-nginx
sudo dnf install python3-certbot-nginx

/etc/nginx/sites-available/

Netplan (base)

sudo apt install netplan.io

/etc/netplan/*.yaml

Netplan Management Commands (Ubuntu)

  • sudo netplan apply — apply current YAML configuration.

  • sudo netplan try — test new config & auto-rollback on failure (press Enter to accept).

  • sudo netplan generate — regenerate backend configs (NetworkManager/systemd-networkd) without applying.

  • sudo netplan set <path.to.option>=<value> — change a single value in-place (useful for scripts).

  • sudo netplan info — show renderer backends and version details.

  • sudo netplan get — display the currently combined configuration tree.

Troubleshooting Common Issues

  • VM Fails to Start: Check BIOS virtualization settings and libvirtd status.

  • Container Not Accessible: Confirm nftables allows the mapped port.

  • Certificate Issues: Test renewal with sudo certbot renew –dry-run and check /var/log/letsencrypt.