Skip to content

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen

Linux Server Hardening: CIS Benchmark, SSH, auditd and AppArmor

Comprehensive hardening guide for Linux servers based on the CIS Benchmark Level 2. SSH configuration, kernel parameters, auditd logging, AppArmor/SELinux, Fail2ban, automatic security updates, and compliance checks using Lynis.

Table of Contents (9 sections)

A freshly installed Linux server is not a secure Linux server. Default configurations are optimized for compatibility, not security: SSH root login enabled, no audit logs, no mandatory access controls, no automatic updates. This guide brings Ubuntu 22.04/24.04 LTS and RHEL 8/9 up to CIS Benchmark Level 1—the foundation for any production server.

Basic Hardening: First Steps

System Updates and Package Management

# Ubuntu/Debian: Full update
apt update && apt upgrade -y
apt autoremove -y

# RHEL/Rocky/AlmaLinux
dnf update -y
dnf autoremove -y

# Configure automatic security updates
# Ubuntu:
apt install unattended-upgrades -y
dpkg-reconfigure --priority=low unattended-upgrades

# /etc/apt/apt.conf.d/50unattended-upgrades
# Automatic security updates only:
cat >> /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF'
Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::Remove-Unused-Packages "true";
Unattended-Upgrade::Automatic-Reboot "false";  // Manual reboot after kernel updates
Unattended-Upgrade::Mail "security@firma.de";
EOF

# RHEL: dnf-automatic
dnf install dnf-automatic -y
sed -i 's/apply_updates = no/apply_updates = yes/' /etc/dnf/automatic.conf
sed -i 's/upgrade_type = default/upgrade_type = security/' /etc/dnf/automatic.conf
systemctl enable --now dnf-automatic.timer

Disable unnecessary services

# List all running services
systemctl list-units --type=service --state=active

# Typical services that are not necessary on production servers:
DISABLE_SERVICES=(
    "bluetooth"
    "cups"           # Print service
    "avahi-daemon"   # mDNS/Bonjour
    "rpcbind"        # NFS (if not needed)
    "postfix"        # Mail (if no local MTA is needed)
    "snapd"          # Ubuntu: Snap (if not used)
)

for service in "${DISABLE_SERVICES[@]}"; do
    if systemctl is-enabled "$service" 2>/dev/null | grep -q "enabled"; then
        systemctl disable --now "$service"
        echo "Disabled: $service"
    fi
done

# Remove unnecessary packages
apt purge --auto-remove telnet ftp rsh-client \
    finger talk ntpdate xinetd nis yp-tools -y 2>/dev/null || true

# Check sockets/network services
ss -tlnp  # Which services are listening on which ports?
# Every service listed here must be justified!

SSH hardening

# /etc/ssh/sshd_config - secure configuration

# Create a backup
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

cat > /etc/ssh/sshd_config.d/hardening.conf << 'EOF'
# Protocol and Port
Port 22                          # Recommendation: use a different port (security through obscurity, helps against script kiddies)
Protocol 2                       # SSH v2 only!

# Authentication
PermitRootLogin no               # Root login PROHIBITED
PasswordAuthentication no        # SSH keys only! (after key deployment)
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes

# Access control
AllowUsers deploy monitoring     # Only specific users
# OR: AllowGroups sshusers
MaxAuthTries 3                   # Make brute force attacks more difficult
MaxSessions 4                    # Max. concurrent sessions
LoginGraceTime 30                # 30s for login attempt

# Connection management
ClientAliveInterval 300          # 5-minute inactivity timeout
ClientAliveCountMax 2            # 2 retries → then disconnect
TCPKeepAlive no                  # Disable OS-level TCP keepalive

# Disable features
X11Forwarding no                 # X11 forwarding - unnecessary
AllowAgentForwarding no          # SSH agent forwarding
AllowTcpForwarding no            # TCP port forwarding (if not needed)
PermitTunnel no
GatewayPorts no

# Banner
Banner /etc/ssh/banner           # Legal notice

# Cryptography (CIS Benchmark Level 2)
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

# Logging
SyslogFacility AUTH
LogLevel VERBOSE                 # Log fingerprint during key-based authentication
EOF

# Create banner
cat > /etc/ssh/banner << 'EOF'
*******************************************************************
* WARNING: This system is for authorized users only!      *
* All activities are monitored and logged.           *
* Unauthorized access will be prosecuted (§ 202a StGB) *
*******************************************************************
EOF

# Restart SSH service
sshd -t  # Test configuration!
systemctl restart sshd

Kernel hardening (sysctl)

# /etc/sysctl.d/99-hardening.conf

cat > /etc/sysctl.d/99-hardening.conf << 'EOF'
# ===== Network hardening =====

# IP spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Reject ICMP redirects (Man-in-the-Middle prevention)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# No IP forwarding (except on routers/firewalls)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_syn_retries = 5
net.ipv4.tcp_synack_retries = 2

# ICMP Broadcast (Smurf Attack)
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Disable IPv6 (if not used)
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

# ===== Kernel Hardening =====

# ASLR (Address Space Layout Randomization) - maximum
kernel.randomize_va_space = 2

# Hide kernel pointers in /proc (make privilege escalation more difficult)
kernel.kptr_restrict = 2

# Restrict dmesg (no kernel leaks for normal users)
kernel.dmesg_restrict = 1

# Disable /proc/sysrq-trigger
kernel.sysrq = 0

# Disable core dumps (no passwords in core files)
kernel.core_pattern = |/bin/false
fs.suid_dumpable = 0

# Restrict ptrace (anti-debugging)
kernel.yama.ptrace_scope = 1

# ===== File System =====

# Hard links: only own (prevent symlink attacks)
fs.protected_hardlinks = 1
fs.protected_symlinks = 1

# Protect FIFOs / regular files
fs.protected_fifos = 2
fs.protected_regular = 2
EOF

sysctl -p /etc/sysctl.d/99-hardening.conf

Audit logging with auditd

# Install auditd
apt install auditd audispd-plugins -y  # Ubuntu
dnf install audit -y                    # RHEL

# /etc/audit/rules.d/hardening.rules
cat > /etc/audit/rules.d/99-hardening.rules << 'EOF'
# Audit log buffer (large enough for spikes)
-b 8192

# Deletions from the audit log should be noticeable
-e 2  # Immutable mode (rules can only be changed after a reboot)

# ===== Authentication =====
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d -p wa -k sudoers

# sudo usage
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid!=unset -k privileged

# SSH configuration
-w /etc/ssh/sshd_config -p wa -k sshd_config

# ===== System commands =====
# Privilege escalation
-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -k sudo_usage
-a always,exit -F path=/bin/su -F perm=x -F auid>=1000 -F auid!=unset -k su_usage

# Dangerous Commands
-a always,exit -F arch=b64 -S execve -F path=/bin/bash -k shell_access
-w /usr/bin/passwd -p x -k passwd_change
-w /usr/sbin/useradd -p x -k user_management
-w /usr/sbin/userdel -p x -k user_management
-w /usr/sbin/groupadd -p x -k group_management

# ===== Network =====
-a always,exit -F arch=b64 -S socket,connect,accept4 -k network_connection

# ===== File Integrity =====
-w /etc/cron.d -p wa -k cron
-w /etc/crontab -p wa -k cron
-w /var/spool/cron -p wa -k cron
-w /etc/init.d -p wa -k init
-w /etc/systemd/system -p wa -k systemd_service

# Kernel Modules
-a always,exit -F arch=b64 -S init_module,delete_module -k kernel_modules

# Mount operations
-a always,exit -F arch=b64 -S mount,umount2 -k mount
EOF

systemctl enable --now auditd
auditctl -l  # Check rules

# Log query examples:
ausearch -k sudo_usage -ts today         # All sudo usage today
ausearch -k identity -ts today          # Changes to /etc/passwd, etc.
aureport --summary                       # Summary

AppArmor (Ubuntu) / SELinux (RHEL)

# ===== AppArmor (Ubuntu/Debian) =====

# Check status
apparmor_status

# Enable AppArmor (if not active)
systemctl enable apparmor
systemctl start apparmor

# List profiles
aa-status  # Enforce vs. Complain Mode

# Create a new profile (example: Python web server)
apt install apparmor-utils -y

# Complain Mode (Logging without blocking - for profile development)
aa-genprof /usr/bin/python3
# Start the application, perform all necessary actions
# Then: Refine AppArmor profiles

# Set known services to Enforce Mode
aa-enforce /etc/apparmor.d/usr.sbin.nginx
aa-enforce /etc/apparmor.d/usr.lib.snapd.snap-confine.real

# ===== SELinux (RHEL/Rocky/AlmaLinux) =====

# Check status
sestatus

# SELinux mode (in /etc/selinux/config):
# SELINUX=enforcing   ← Production: always Enforcing!
# SELINUX=permissive  ← For debugging only
# SELINUX=disabled    ← NEVER in production!

# View AVC denials (what was blocked?)
ausearch -m AVC -ts today
audit2why -a  # Why was it blocked?

# Generate new rule from AVC log (emergency workaround)
audit2allow -a -M mymodule
semodule -i mymodule.pp

# Nginx SELinux adjustments
setsebool -P httpd_can_network_connect on
setsebool -P httpd_can_connect_ldap on
semanage port -a -t http_port_t -p tcp 8080

Fail2ban - Brute Force Protection

# Installation
apt install fail2ban -y

# /etc/fail2ban/jail.local
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime  = 3600    # Blocked for 1 hour
findtime  = 600    # Within 10 minutes
maxretry = 3       # 3 failed attempts → Ban
banaction = iptables-multiport

# Email upon ban
destemail = security@firma.de
action = %(action_mwl)s  # Ban + Email + Whois + Log

[sshd]
enabled = true
port = 22
filter = sshd
logpath = %(sshd_log)s
maxretry = 3
bantime = 86400    # 24h for SSH

[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 2

[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
EOF

systemctl enable --now fail2ban
fail2ban-client status         # Overview
fail2ban-client status sshd    # SSH jail status

Lynis - Compliance Check

# Lynis: Open-Source Security Auditing Tool
# Checks system against CIS Benchmark, NIST, etc.

# Installation
apt install lynis -y
# OR latest version:
git clone https://github.com/CISOfy/lynis
cd lynis

# System Audit
lynis audit system --quick

# Output (excerpt):
# [+] System Tools
#   [V] Hostname: server01
#   [V] Loaded kernel modules: 42
#
# [WARNING] Found malware scanner: none
# [SUGGESTION] Consider running a malware scanner (lynis suggestion ID: MALW-3280)
#
# Hardening Index: 68 / 100
# Tests Performed: 256
# Plugins Enabled: 0

# Score Interpretation:
#   0-49:  Poor - critical measures required
#  50-69:  Average - significant room for improvement
#  70-84:  Good - minor improvements recommended
#  85-100: Very good - CIS Level 1/2 compliant

# Detailed Report
lynis audit system --report-file /var/log/lynis-report.dat
lynis show details<test-id>

# Regular scan via Cron:
echo &quot;0 3 * * 0 root /usr/bin/lynis audit system --cronjob | mail -s &#x27;Lynis Report&#x27; security@firma.de&quot; &gt; /etc/cron.d/lynis

UFW Firewall (Ubuntu)

# UFW - Uncomplicated Firewall (simple, but sufficient for most servers)

ufw default deny incoming    # Default: block all incoming traffic
ufw default allow outgoing   # Default: allow all outgoing traffic

# Allow SSH (BEFORE enabling!)
ufw allow from 10.0.0.0/8 to any port 22  # Only from internal network
# OR:
ufw allow 22/tcp

# Web server
ufw allow 80/tcp
ufw allow 443/tcp

# Specific services
ufw allow from 192.168.1.0/24 to any port 5432  # PostgreSQL only internally
ufw allow from 10.0.0.5 to any port 3306        # MySQL only from specific server

# Enable UFW
ufw enable
ufw status verbose

# Enable logging
ufw logging on
# Logs in /var/log/ufw.log

Checklist: Hardening Acceptance

Basics:
  □ All packages up to date (apt upgrade / dnf update)
  □ Automatic security updates enabled
  □ Unnecessary services disabled
  □ No obsolete protocols (Telnet, FTP, rsh)

SSH:
  □ Root login disabled (PermitRootLogin no)
  □ Password authentication disabled (PasswordAuthentication no)
  □ SSH key authentication enabled
  □ Login banner configured
  □ Only allowed users (AllowUsers)

Kernel:
  □ ASLR enabled (randomize_va_space = 2)
  □ IP spoofing protection (rp_filter = 1)
  □ ICMP redirects disabled
  □ No IP forwarding (if no router)
  □ Kernel pointers hidden (kptr_restrict = 2)

Logging &amp; Monitoring:
  □ auditd running, rules loaded
  □ Audit log for auth, sudo, file changes
  □ Fail2ban configured for SSH
  □ Log rotation configured

Access Control:
  □ AppArmor (Enforce) / SELinux (Enforcing) active
  □ sudo only for authorized users (visudo)
  □ Minimal SUID/SGID files (find / -perm -4000)
  □ World-writable directories checked

Lynis Score:
  □ Lynis Hardening Index ≥ 75
  □ Critical findings resolved
  □ Report documented and filed
```</test-id>

Questions about this topic?

Our experts advise you free of charge and without obligation.

Free Consultation

About the Author

Chris Wojzechowski
Chris Wojzechowski

Geschäftsführender Gesellschafter

E-Mail

Geschäftsführender Gesellschafter der AWARE7 GmbH mit langjähriger Expertise in Informationssicherheit, Penetrationstesting und IT-Risikomanagement. Absolvent des Masterstudiengangs Internet-Sicherheit an der Westfälischen Hochschule (if(is), Prof. Norbert Pohlmann). Bestseller-Autor im Wiley-VCH Verlag und Lehrbeauftragter der ASW-Akademie. Einschätzungen zu Cybersecurity und digitaler Souveränität erschienen u.a. in Welt am Sonntag, WDR, Deutschlandfunk und Handelsblatt.

10 Publikationen
  • Einsatz von elektronischer Verschlüsselung - Hemmnisse für die Wirtschaft (2018)
  • Kompass IT-Verschlüsselung - Orientierungshilfen für KMU (2018)
  • IT Security Day 2025 - Live Hacking: KI in der Cybersicherheit (2025)
  • Live Hacking - Credential Stuffing: Finanzrisiken jenseits Ransomware (2025)
  • Keynote: Live Hacking Show - Ein Blick in die Welt der Cyberkriminalität (2025)
  • Analyse von Angriffsflächen bei Shared-Hosting-Anbietern (2024)
  • Gänsehaut garantiert: Die schaurigsten Funde aus dem Leben eines Pentesters (2022)
  • IT Security Zertifizierungen — CISSP, T.I.S.P. & Co (Live-Webinar) (2023)
  • Sicherheitsforum Online-Banking — Live Hacking (2021)
  • Nipster im Netz und das Ende der Kreidezeit (2017)
IT-Grundschutz-Praktiker (TÜV) IT Risk Manager (DGI) § 8a BSIG Prüfverfahrenskompetenz Ausbilderprüfung (IHK)
This article was last edited on 04.03.2026. Responsible: Chris Wojzechowski, Geschäftsführender Gesellschafter at AWARE7 GmbH. License: CC BY 4.0 - free use with attribution: "AWARE7 GmbH, https://a7.de"

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung