Skip to main content

COMMUNITY KNOWLEDGE FORUM Independent Hosting Community • Not affiliated with netcup GmbH.

NETCUP GUIDE
FORUM · LIVE VOUCHER POOL
Tutorials LEVEL: Intermediate TIME: 25 minutes // 18 MIN READ • VERIFIED:

How to Secure a Linux VPS or Root Server on Netcup (5-Layer Defense-in-Depth Blueprint)

Relying on a single security control (such as UFW alone) leaves a production server brittle: a single docker run -p command can bypass UFW, and a single unpatched OpenSSL library in memory remains vulnerable even after apt upgrade unless the daemon is restarted. This guide implements a 5-layer defense-in-depth architecture specifically engineered for Netcup KVM servers across the Generation 11.5s and Generation 12.5 lineup.

THREAD OP // AUTHOR Marcus Vance Principal Systems Architect
MOD VERIFIED // CO-MAINTAINER Elena Rostova Senior Network & Storage Verification Editor
THREAD STATUS // UPDATED Community Standards →
MODERATOR NOTE // TEST ENVIRONMENT & TRANSPARENCY Verified against Ubuntu 24.04 LTS (systemd journal backend for Fail2ban, needrestart, and unattended-upgrades) and Netcup’s hypervisor-edge SCP Stateful Firewall.

PREREQUISITES // ENVIRONMENT REQUIREMENTS

CHECKLIST
  • ✓ A Netcup VPS or Root Server running Ubuntu 24.04 LTS or Debian 12
  • ✓ Ed25519 SSH key authentication already configured and tested

Key Takeaways

EXECUTIVE BRIEFING
  • 01 Layer 1 (Hypervisor Edge): Configure the Netcup SCP Stateful Firewall to drop unauthorized TCP/UDP ports outside the VM before packets hit your guest kernel or Docker PREROUTING chains.
  • 02 Layer 2 (Host Firewall): Enforce dual-stack UFW (IPV6=yes) inside the guest OS.
  • 03 Layer 3 (Intrusion Prevention): Configure Fail2ban with backend = systemd (mandatory on Ubuntu 24.04 where /var/log/auth.log is absent) and exponential ban multipliers (bantime.increment = true).
  • 04 Layer 4 & 5 (Kernel Hardening & Automated CVE Patching): Lock down /etc/sysctl.d/99-netcup-security.conf and configure unattended-upgrades + needrestart for automatic service restarts after library updates.
📑 Quick Jump Table of Contents (4 Sections) ▼
§ 01

Layer 1: Configure the Netcup SCP Hypervisor-Edge Stateful Firewall

Introduced across Generation 11, 11.5s, 12, and 12.5 in the Netcup Server Control Panel (servercontrolpanel.de), the SCP Stateful Firewall filters traffic at the host hypervisor virtual switch (tap interface) before packets ever enter your virtual machine’s virtio-net queue. This architecture provides two decisive operational advantages over relying solely on guest-OS iptables or ufw: first, volumetric TCP SYN or UDP port scans are dropped on the host bridge without consuming your VM’s vCPU softirq interrupt budget or conntrack table; second, because the filter sits outside your guest kernel, a misconfigured Docker 0.0.0.0:5432 port binding inside the VM cannot accidentally expose a database to the public internet.

Because the SCP Firewall is stateful (connection-tracking aware), an Egress: ACCEPT ALL rule automatically permits return packets (ESTABLISHED,RELATED) for any outbound DNS queries, apt repository downloads, or HTTPS API calls initiated by your server, while a default Ingress: DROP ALL policy at the bottom of the ruleset blocks all unsolicited inbound connections not explicitly whitelisted above it. You can also save your ruleset as a reusable Firewall Policy Template inside SCP and attach it across multiple Netcup VPS and Root Server instances in a single click or via the SCP REST API.

One critical IPv6 rule must never be overlooked when building an SCP Firewall policy: never block inbound ICMPv6 completely. Unlike IPv4, which uses Layer-2 ARP broadcasts, IPv6 relies entirely on ICMPv6 Neighbor Discovery Protocol (NDP, types 133–136) to resolve your link-local gateway (fe80::1) and relies on ICMPv6 Packet Too Big (type 2) for Path MTU Discovery. Blocking ICMPv6 in the SCP Firewall will cause your server’s IPv6 connectivity to drop as soon as the kernel neighbor cache expires. Configure the production ingress/egress policy below under SCP → Select Server → Firewall:

Recommended Production Netcup SCP Hypervisor Firewall Policy
DirectionProtocolSource IP / CIDRDestination PortAction & Engineering Purpose
Ingress (In)TCP0.0.0.0/0, ::/0 (or Admin IP)22 (or Custom SSH)ACCEPT — OpenSSH remote administration
Ingress (In)TCP0.0.0.0/0, ::/080, 443ACCEPT — HTTP/1.1, HTTP/2 TLS web traffic
Ingress (In)UDP0.0.0.0/0, ::/0443ACCEPT — HTTP/3 (QUIC) UDP transport
Ingress (In)ICMP / ICMPv60.0.0.0/0, ::/0All (Esp. Types 133–136)ACCEPT — Path MTU Discovery & IPv6 Neighbor Discovery
Ingress (In)ALL0.0.0.0/0, ::/01 – 65535DROP (Default Policy) — Blocks all internal DB/Docker ports
Egress (Out)ALL0.0.0.0/0, ::/01 – 65535ACCEPT — Outbound package updates, API calls & DNS
§ 02

Layer 2: Deploy Fail2ban with backend = systemd & Exponential Ban Multipliers

While the SCP Firewall restricts which ports are reachable, ports 22 (SSH) and 443 (HTTPS) must remain open to serve legitimate administrators and visitors. That makes host-level intrusion prevention essential. On Ubuntu 24.04 LTS and modern Debian 12 minimal cloud images, the legacy rsyslog daemon is no longer installed by default—meaning /var/log/auth.log and /var/log/syslog do not exist on disk! If you install fail2ban using old tutorials that rely on the auto or pyinotify file backends, the sshd jail will either crash on startup (Have not found any log file for sshd jail) or silently fail to detect attacks.

To integrate Fail2ban cleanly with systemd-journald on Ubuntu 24.04, always install the python3-systemd C-binding package alongside fail2ban and explicitly configure backend = systemd inside /etc/fail2ban/jail.local. Instead of polling text files, Fail2ban subscribes directly to structured kernel/daemon journal events (_SYSTEMD_UNIT=ssh.service). Setting mode = aggressive inside the [sshd] jail instructs Fail2ban’s regex filter to catch not only failed password or publickey attempts, but also pre-authentication scanner disconnects (Connection closed by authenticating user ... [preauth]) and banner-probing bots that waste sshd unprivileged child processes.

Rather than using a static 10-minute ban—which distributed botnets trivially wait out before resuming their dictionary sequence—enable exponential ban scaling (bantime.increment = true, bantime.factor = 2, bantime.maxtime = 5w). With this configuration, an attacking IP receives a 1-hour ban on its first offense, a 2-hour ban on its second, 4 hours on its third, and scales exponentially up to 5 weeks while Fail2ban persists the offender history across reboots in /var/lib/fail2ban/fail2ban.sqlite3:

Install Fail2ban & Configure Exponential Systemd Jails (/etc/fail2ban/jail.local)
bash
sudo apt update
sudo apt install -y fail2ban python3-systemd

cat << 'EOF' | sudo tee /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 5w
findtime = 15m
maxretry = 3
backend = systemd
banaction = ufw
banaction_allports = ufw
ignoreip = 127.0.0.1/8 ::1

[sshd]
enabled = true
mode = aggressive
port = ssh
logpath = %(sshd_log)s
backend = systemd
EOF

sudo systemctl enable --now fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Command Explanation: Installs python3-systemd so Fail2ban can query journald directly, integrates ban actions with ufw, enables mode = aggressive (which catches pre-auth scanner disconnects before a password/key attempt even completes), and activates exponential ban scaling.

EXPECTED OUTPUT
Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  `- Total failed:     0
`- Actions
   |- Currently banned: 0
   `- Total banned:     0
Install python3-systemd So Fail2ban Can Poll journald

On Ubuntu 24.04 LTS, always install python3-systemd alongside fail2ban so the systemd backend can subscribe to structured _SYSTEMD_UNIT=ssh.service journal events in real time.

§ 03

Layer 3: Kernel Network & Memory Exploit Mitigation (/etc/sysctl.d/)

Layer 3 hardens the Linux kernel’s network stack and userland memory boundaries via /etc/sysctl.d/99-netcup-security.conf. At the network layer, net.ipv4.tcp_syncookies = 1 protects your TCP listen queues when SYN backlog buffers saturate by encoding connection state into cryptographic sequence numbers, while net.ipv4.tcp_rfc1337 = 1 prevents TCP TIME_WAIT assassination attacks. Enabling strict Reverse Path Filtering (rp_filter = 1) instructs the kernel to verify that incoming packets arrive on the interface that holds the best return route to the source IP, dropping spoofed source packets immediately.

Disabling ICMP redirect acceptance (accept_redirects = 0, secure_redirects = 0) and source routing (accept_source_route = 0) across both IPv4 and IPv6 prevents malicious hosts from manipulating your kernel routing table. Crucially, notice that we do not disable IPv6 forwarding (net.ipv6.conf.all.forwarding) or IPv4 forwarding (net.ipv4.ip_forward) if the server hosts Docker containers or WireGuard tunnels, nor do we touch ICMPv6 Neighbor Discovery parameters—ensuring CIS-level network hardening that never breaks container networking.

At the kernel memory and filesystem boundary, setting kernel.kptr_restrict = 2 hides kernel symbol addresses in /proc/kallsyms from both unprivileged and privileged userland processes (defeating Return-Oriented Programming / KASLR bypasses), kernel.yama.ptrace_scope = 1 prevents a compromised web process from calling ptrace() to dump credentials from sibling processes, and fs.protected_hardlinks = 1 / fs.protected_symlinks = 1 block classic /tmp race-condition privilege escalations. For compliance-grade auditing, you can pair these sysctl settings with auditd (sudo apt install -y auditd) watching /etc/passwd, /etc/shadow, and /etc/ssh/sshd_config.d/:

/etc/sysctl.d/99-netcup-security.conf (Kernel Network & Memory Hardening)
bash
cat << 'EOF' | sudo tee /etc/sysctl.d/99-netcup-security.conf
# TCP SYN Flood & Spoofing Protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_rfc1337 = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Disable ICMP Redirect Acceptance & Source Routing (IPv4 & IPv6)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Kernel Pointer & ptrace Hardening
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.yama.ptrace_scope = 1
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
EOF

sudo sysctl --system

Command Explanation: Applies CIS Benchmark kernel network and memory protections without breaking Docker bridge forwarding or IPv6 Neighbor Discovery.

EXPECTED OUTPUT
* Applying /etc/sysctl.d/99-netcup-security.conf ...
§ 04

Layer 4: Automated Security Patching (unattended-upgrades) & Daemon Reloading (needrestart)

Understanding how Linux manages shared libraries in virtual memory reveals a subtle security pitfall that catches many sysadmins off guard. When unattended-upgrades downloads and installs an urgent security update for libssl3, libc6, or libsystemd0 at 04:00 UTC, dpkg atomically replaces the .so shared library file on the ext4 filesystem and unlinks the old inode. However, any long-running daemon that was already executing (nginx, php-fpm, postfix, mariadbd) keeps the old, unpatched shared library mapped in its virtual address space (/proc/<pid>/maps showing (deleted)) until that daemon process is restarted!

Ubuntu 24.04 LTS includes the needrestart utility, which hooks into apt / dpkg post-invoke triggers, scans /proc/*/maps across all running processes for unlinked shared library mappings, and identifies which systemd service units need to be restarted. By default, however, needrestart can pause in interactive mode (i) or skip certain restarts during non-interactive runs unless explicitly configured.

By creating /etc/needrestart/conf.d/99-netcup-auto.conf with $nrconf{restart} = 'a';, you instruct needrestart to automatically restart affected userland daemons (nginx.service, php8.3-fpm.service, cron.service) immediately after unattended-upgrades patches a shared library, while safely deferring critical display/session managers (dbus, systemd-logind) and kernel reboots. For kernel updates, we recommend keeping Unattended-Upgrade::Automatic-Reboot "false"; on single-node stateful servers and monitoring /var/run/reboot-required so you can take a quick SCP snapshot before rebooting during a planned maintenance window:

Configure Automated CVE Patching & Automatic Library-Linked Service Restarts
bash
sudo apt install -y unattended-upgrades needrestart

# Configure needrestart to automatically restart daemons when shared libraries are patched
sudo mkdir -p /etc/needrestart/conf.d
cat << 'EOF' | sudo tee /etc/needrestart/conf.d/99-netcup-auto.conf
# Restart services automatically ('a') instead of interactive prompt ('i')
$nrconf{restart} = 'a';
EOF

# Verify unattended-upgrades dry-run
sudo unattended-upgrade --dry-run --debug | tail -n 10

Command Explanation: Ensures that whenever unattended-upgrades patches libssl or glibc, dependent services (nginx, ssh, postfix) are automatically restarted with the patched library.

Common Troubleshooting Scenarios

DIAGNOSTIC RUNBOOK

[SYMPTOM] Fail2ban fails to start on Ubuntu 24.04 with Failed during configuration: Have not found any log file for sshd jail

Root Cause: Ubuntu 24.04 removed rsyslog and /var/log/auth.log in favor of systemd-journald.

Resolution: Install python3-systemd (sudo apt install -y python3-systemd) and set backend = systemd in /etc/fail2ban/jail.local as shown in Layer 2.

[SYMPTOM] How do I unban a developer IP address that accidentally triggered Fail2ban?

Root Cause: Three failed SSH key or port attempts within 15 minutes triggered the sshd jail.

Resolution: Run sudo fail2ban-client set sshd unbanip 198.51.100.42 and add trusted static office/VPN IPs to ignoreip in /etc/fail2ban/jail.local.

[SYMPTOM] Locked out of server after applying an overly strict SCP Hypervisor Firewall rule

Root Cause: Port 22/tcp (or your custom SSH port) was omitted before setting the default SCP Ingress policy to DROP.

Resolution: Log in to servercontrolpanel.de (SCP), open the Firewall tab, add the ACCEPT rule for TCP port 22, and click Apply Rules—changes take effect on the hypervisor virtual switch within 2 seconds without rebooting the VM.

Frequently Asked Questions

VERIFIED Q&A

If I use the Netcup SCP Stateful Firewall, do I still need UFW inside Ubuntu?

Yes. Defense-in-depth means never relying on a single control plane: the SCP Stateful Firewall protects the external hypervisor interface (including against Docker port bypasses), while UFW + Fail2ban dynamically block abusive IPs at the guest OS layer.

Should I install CrowdSec or Fail2ban on a Netcup VPS?

Both work well. Fail2ban (~25 MB RAM footprint) is ideal on compact 2 GB to 4 GB RAM instances (such as the VPS nano G11.5s or VPS 500 G12.5) if you want a zero-dependency local daemon with instant systemd journal parsing. CrowdSec (~120 MB RAM footprint) is a great fit on 8 GB+ VPS 1000 G12.5 or RS 1000 G12.5 servers if you want collaborative community blocklists and HTTP WAF bouncers.

Does the Netcup SCP Stateful Firewall cost extra on VPS or Root Server plans?

No! The SCP Stateful Firewall is included free of charge across all current Netcup VPS (G11.5s / G12.5) and Root Server (G12.5) plans, and supports reusable firewall policy templates across multiple servers.

Should I enable automatic reboots for kernel updates in unattended-upgrades?

For single-node production databases, we recommend leaving Unattended-Upgrade::Automatic-Reboot "false"; so needrestart restarts userland daemons automatically while allowing you to schedule a 30-second maintenance window (after taking an SCP snapshot) when /var/run/reboot-required appears.

REFERENCES // VERIFIED TECHNICAL SOURCES

FURTHER READING // ARCHITECTURE AUDITS & GUIDES

Active Coupons →