Production Hardening¶
Extra hardening and tuning to apply after the Setup Server guide. These steps are optional but strongly recommended for any server exposed to the internet.
Automatic Security Updates¶
Enable unattended security patches so critical vulnerabilities are patched without manual intervention.
sudo apt update && sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Select Yes when prompted. Verify it's enabled:
Expected output:
To also enable automatic reboots when a kernel update requires it (e.g. overnight at 4 AM):
Uncomment and set:
Warning
Automatic reboots will restart your K3s node and all game server pods on it. Agones will reschedule pods on other nodes if available. For single-node setups, schedule reboots during maintenance windows.
Fail2Ban¶
Blocks IPs that repeatedly fail SSH login attempts. Even with password auth disabled, this reduces log noise and resource waste from brute-force bots.
Create a local config (survives package upgrades):
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
Start and enable:
Check banned IPs:
SSH Hardening¶
Additional SSH lockdown beyond disabling password auth.
MaxAuthTries 3
MaxSessions 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers ubuntu
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
| Setting | Effect |
|---|---|
MaxAuthTries 3 |
Disconnect after 3 failed auth attempts per connection |
MaxSessions 3 |
Limit multiplexed sessions per connection |
LoginGraceTime 30 |
30 seconds to authenticate before disconnect |
ClientAliveInterval 300 |
Send keepalive every 5 minutes |
ClientAliveCountMax 2 |
Drop connection after 2 missed keepalives (10 min idle) |
AllowUsers ubuntu |
Only ubuntu can SSH in — blocks all other usernames |
X11Forwarding no |
Disable X11 forwarding (not needed on a headless server) |
Restart SSH:
Warning
Test in a new terminal before closing your current session.
Swap¶
Game servers and the backend can spike in memory usage. A small swap file prevents the OOM killer from terminating processes during spikes.
Check if swap exists:
If empty, create a 2 GB swap file:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Set swappiness low so the kernel prefers RAM and only uses swap under pressure:
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.d/99-mip.conf
sudo sysctl -p /etc/sysctl.d/99-mip.conf
Kernel Tuning for Game Servers¶
Tune network and file descriptor limits for high-throughput UDP game traffic.
# Increase UDP buffer sizes for game server traffic
net.core.rmem_max = 26214400
net.core.rmem_default = 1048576
net.core.wmem_max = 26214400
net.core.wmem_default = 1048576
# Increase connection tracking for many concurrent players
net.netfilter.nf_conntrack_max = 131072
# Increase the backlog queue for incoming packets
net.core.netdev_max_backlog = 5000
# Increase max open files system-wide
fs.file-max = 2097152
# Reduce TIME_WAIT socket accumulation
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
# Ignore ICMP redirects (prevents MITM routing attacks)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
# Ignore source-routed packets
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Log martian packets (packets with impossible source addresses)
net.ipv4.conf.all.log_martians = 1
Apply:
File Descriptor Limits¶
Game servers and the backend open many connections. Raise the per-process limit:
Log out and back in for limits to take effect. Verify with ulimit -n.
Time Synchronization¶
Accurate time is critical for JWT expiry, Redis TTLs, and log correlation across nodes. Ubuntu uses systemd-timesyncd by default, but chrony is more reliable for production.
Verify sync:
Check that Leap status shows Normal and System time offset is sub-millisecond.
Info
All nodes in the cluster (master + workers) should use the same NTP source. Chrony handles this automatically with Ubuntu's default NTP pool.
Log Rotation¶
Prevent logs from filling the disk. Most services use journald, but UE server logs inside pods write to files.
journald¶
Cap the journal to 500 MB:
Docker logs¶
Docker container logs can grow unbounded. Set a global default:
Disable Unused Services¶
Reduce the attack surface by disabling services you don't need:
sudo systemctl disable --now snapd snapd.socket 2>/dev/null || true
sudo systemctl disable --now ModemManager 2>/dev/null || true
sudo systemctl disable --now cups cups-browsed 2>/dev/null || true
sudo systemctl disable --now avahi-daemon 2>/dev/null || true
List what's still listening:
Investigate anything unexpected. On a properly configured MIP server, you should only see K3s (6443), Kubelet (10250), and your backend ports.
Shared Memory Hardening¶
Prevent shared memory exploits:
echo 'tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0' | sudo tee -a /etc/fstab
sudo mount -o remount /run/shm
Login Banners¶
Warn unauthorized users. Useful for compliance and legal protection:
*********************************************************************
WARNING: Unauthorized access to this system is prohibited.
All connections are monitored and recorded.
Disconnect IMMEDIATELY if you are not an authorized user.
*********************************************************************
Enable the banner in SSH:
Checklist¶
| Step | Status |
|---|---|
| Unattended security upgrades | dpkg-reconfigure unattended-upgrades |
| Fail2Ban installed and enabled | fail2ban-client status sshd |
| SSH hardened (MaxAuthTries, AllowUsers) | Review /etc/ssh/sshd_config |
| Swap configured | swapon --show |
| Kernel tuning applied | sysctl -p /etc/sysctl.d/99-mip.conf |
| File descriptor limits raised | ulimit -n → 65536 |
| Time sync (chrony) | chronyc tracking |
| Log rotation configured | journald + Docker log limits |
| Unused services disabled | ss -tlnp — nothing unexpected |
| Shared memory hardened | /run/shm mounted noexec |
| Monitoring in place | See Monitoring |