Security Basics
Security Hardening Basics
Hardening is the process of reducing a system's attack surface by removing everything that doesn't need to be there and locking down everything that does. A freshly installed OS is built for compatibility and convenience — not security. Hardening is what turns it into something you'd actually trust in production.
This lesson covers
What hardening actually means → CIS benchmarks → sysctl kernel hardening → Service minimisation → SSH hardening → User and privilege hardening → Automated hardening with Lynis → Building a repeatable baseline
Hardening vs fixing misconfigurations
Misconfiguration fixing is reactive — you find something wrong and correct it. Hardening is proactive — you systematically reduce attack surface before anything goes wrong. The distinction matters because hardening covers things that aren't technically misconfigured. A default Ubuntu install isn't misconfigured. It just has services running that you don't need, kernel parameters set for compatibility rather than security, and features enabled that create unnecessary exposure.
The goal of hardening is to reach a state where every running service, every open port, every enabled feature, and every user account has a documented reason for existing. Anything without that reason gets removed. What remains is a minimal, intentional attack surface — and that's a fundamentally stronger position than one that relies on nothing going wrong with everything that's running.
CIS benchmarks — the industry standard
The Center for Internet Security publishes hardening benchmarks for virtually every major operating system, cloud platform, database, and application. A CIS benchmark is a detailed checklist — hundreds of specific configuration checks, each with a rationale, a remediation command, and an audit command to verify the setting is in place.
CIS benchmarks have two levels. Level 1 covers settings that are broadly applicable, low-risk to implement, and have a clear security benefit — they're the baseline that every system should meet. Level 2 goes further with settings that trade some functionality or performance for stronger security — appropriate for high-sensitivity environments. For most production systems, Level 1 compliance is the target.
CIS Level 1
Baseline security. Minimal functionality impact. Every production system should comply. Covers SSH config, filesystem permissions, logging, service minimisation, and kernel parameters.
CIS Level 2
Defence in depth. Some settings may impact performance or require application changes. Appropriate for high-value targets, regulated environments, and systems handling sensitive data.
sysctl — hardening the kernel
The Linux kernel exposes hundreds of runtime parameters through the /proc/sys filesystem — and you can tune them with sysctl. Several of these parameters have direct security implications: whether the kernel accepts source-routed packets, whether it logs suspicious activity, whether it allows core dumps that could leak sensitive memory contents.
These settings survive service restarts but not reboots unless written to /etc/sysctl.conf or a file in /etc/sysctl.d/. Always persist them — a reboot that undoes your hardening is an incident waiting to happen.
# Create a hardening sysctl config file
sudo tee /etc/sysctl.d/99-hardening.conf << 'EOF'
# --- NETWORK HARDENING ---
# Disable IP source routing (prevents traffic redirection attacks)
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Disable ICMP redirects (prevents routing table manipulation)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Enable reverse path filtering (drops packets with impossible source IPs)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Log suspicious packets (martians = packets with impossible addresses)
net.ipv4.conf.all.log_martians = 1
# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1
# Ignore ICMP broadcast requests (Smurf attack mitigation)
net.ipv4.icmp_echo_ignore_broadcasts = 1
# --- KERNEL HARDENING ---
# Restrict access to kernel logs (dmesg) to root only
kernel.dmesg_restrict = 1
# Restrict kernel pointer exposure in /proc (hides KASLR bypass info)
kernel.kptr_restrict = 2
# Disable core dumps for setuid programs (prevents memory leaks)
fs.suid_dumpable = 0
# Randomise memory layout (ASLR — makes buffer overflows harder)
kernel.randomize_va_space = 2
EOF
# Apply immediately without rebooting
sudo sysctl -p /etc/sysctl.d/99-hardening.conf
# Verify a specific setting is active
sysctl net.ipv4.tcp_syncookies
sysctl kernel.randomize_va_space
# sudo sysctl -p /etc/sysctl.d/99-hardening.conf net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.all.log_martians = 1 net.ipv4.tcp_syncookies = 1 net.ipv4.icmp_echo_ignore_broadcasts = 1 kernel.dmesg_restrict = 1 kernel.kptr_restrict = 2 fs.suid_dumpable = 0 kernel.randomize_va_space = 2 # sysctl net.ipv4.tcp_syncookies net.ipv4.tcp_syncookies = 1 # sysctl kernel.randomize_va_space kernel.randomize_va_space = 2
What just happened
Each line is a kernel parameter now actively enforced. tcp_syncookies = 1 enables SYN cookie protection — the kernel now handles SYN flood attacks without exhausting connection state tables. randomize_va_space = 2 enables full ASLR — memory addresses are randomised on every execution, making buffer overflow exploits significantly harder to reliably execute. kptr_restrict = 2 hides kernel symbol addresses from all users including root — removing information attackers use to bypass kernel security mechanisms.
SSH hardening
SSH is the primary remote access method for Linux servers — which makes it the primary target for brute-force attacks and credential theft. The default SSH configuration is functional but not hardened. Every internet-facing server should have these settings applied.
# Edit SSH daemon configuration
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo tee /etc/ssh/sshd_config.d/99-hardening.conf << 'EOF'
# Disable password authentication — key-based only
PasswordAuthentication no
ChallengeResponseAuthentication no
# Disable root login entirely
PermitRootLogin no
# Only allow specific users to SSH in
AllowUsers deployuser adminuser
# Use only modern, strong key exchange algorithms
KexAlgorithms curve25519-sha256,diffie-hellman-group16-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# Disconnect idle sessions after 5 minutes
ClientAliveInterval 300
ClientAliveCountMax 0
# Disable X11 forwarding (not needed on servers)
X11Forwarding no
# Disable unused authentication methods
PermitEmptyPasswords no
KerberosAuthentication no
GSSAPIAuthentication no
# Limit authentication attempts per connection
MaxAuthTries 3
# Change default port (obscures from automated scanners — not a security control)
# Port 2222
EOF
# Validate config before restarting — never restart without validating
sudo sshd -t && echo "Config valid" || echo "Config error — do not restart"
# Apply if valid
sudo systemctl reload sshd
# sudo sshd -t && echo "Config valid" Config valid # sudo systemctl reload sshd (no output — reload successful) # Verify password auth is now rejected $ ssh -o PasswordAuthentication=yes adminuser@203.0.113.10 adminuser@203.0.113.10: Permission denied (publickey). # Verify root login is blocked $ ssh root@203.0.113.10 root@203.0.113.10: Permission denied (publickey,gssapi-keyex,gssapi-with-mic). # Check active SSH hardening settings $ sudo sshd -T | grep -E "passwordauth|permitroot|maxauthtries|x11forwarding" passwordauthentication no permitrootlogin no maxauthtries 3 x11forwarding no
Always validate before reloading SSH
Run sudo sshd -t before every SSH config change. A syntax error in sshd_config that gets applied will disconnect your current session and prevent new ones — locking you out completely. The backup file created with cp sshd_config sshd_config.bak is your recovery path if you need console access to fix it.
Automated hardening audit with Lynis
Lynis is an open-source security auditing tool that scans a Linux system against hundreds of hardening checks — covering the kernel, filesystem, authentication, networking, logging, and installed software. It gives you a hardening index score and a prioritised list of findings to address. It's the fastest way to see where a system stands against a hardening baseline.
# Install Lynis
sudo apt install lynis -y # Debian/Ubuntu
sudo yum install lynis -y # RHEL/CentOS
# Run a full system audit
sudo lynis audit system
# Run audit and save full report to file
sudo lynis audit system --log-file /var/log/lynis-audit.log
# Show only warnings and suggestions from last scan
sudo grep -E "^(WARNING|SUGGESTION)" /var/log/lynis.log | head -30
# Check current hardening index score only
sudo lynis audit system --quick 2>/dev/null | grep "Hardening index"
# sudo lynis audit system (abbreviated output)
[ Lynis 3.0.8 ]
###############################################
Lynis comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome
to redistribute it under the GPL license.
###############################################
[+] Initializing program
[+] System tools scan
[+] Boot and services
- Boot loader: GRUB2 [FOUND]
- Service manager: systemd
[+] Kernel
- Kernel version: 5.15.0-91-generic
- Loaded kernel modules: 67
- /proc/sys/kernel/randomize_va_space: 2 [OK]
- /proc/sys/kernel/kptr_restrict: 2 [OK]
[+] SSH
- PermitRootLogin: no [OK]
- PasswordAuthentication: no [OK]
- MaxAuthTries: 3 [OK]
WARNING: Default umask in /etc/login.defs could be more strict [auth-9328]
WARNING: No password set for single user mode [boot-5262]
SUGGESTION: Install libpam-tmpdir to set $TMP and $TMPDIR for PAM sessions [PKGS-7345]
SUGGESTION: Enable process accounting to track all executed commands [ACCT-9622]
SUGGESTION: Disable USB storage if not required [USB-1000]
SUGGESTION: Consider restricting compiler access to root only [HRDN-7222]
Hardening index : 67 [############# ]
Tests performed : 241
Plugins enabled : 2
What just happened
241 checks run, hardening index of 67 out of 100. The sysctl and SSH hardening applied earlier shows up as green — randomize_va_space, kptr_restrict, PermitRootLogin no, and PasswordAuthentication no all confirmed. The warnings and suggestions are the next items to address — each one has a test ID you can look up in the Lynis documentation for the exact remediation command. A score of 67 on a fresh install that's had basic hardening applied is typical. A well-hardened production server should be in the 80s.
Service minimisation — remove what you don't need
Every running service is an attack surface. A web server doesn't need a mail daemon. A database server doesn't need a print spooler. Every service that's running but not needed is a potential vulnerability waiting to be discovered — and one more thing that needs patching when a CVE comes out for it.
List everything that's running and why
Run systemctl list-units --type=service --state=running and go through every entry. If you can't answer "what does this do and why does this server need it" — it's a candidate for removal. Common candidates: avahi-daemon (network discovery — useless on a server), cups (printing), bluetooth, ModemManager.
Disable and stop unused services
sudo systemctl disable --now avahi-daemon cups bluetooth ModemManager — disable prevents it starting on boot, now stops it immediately. Both are required. Stopping without disabling means it comes back after a reboot.
Remove packages entirely where possible
Disabling a service leaves the software installed and patchable but not running. For software you're certain you'll never need — compilers on production servers, development tools, legacy protocol daemons — remove the package entirely with apt purge or yum remove. Less software means fewer CVEs to track.
Instructor's Note
The right time to build a hardening baseline is before you deploy anything on top of it — not after. Create a hardened base image, run Lynis against it, document the score and the remaining findings, and use that image for every new server you spin up. That way every server in your fleet starts from the same secure foundation. Hardening a server after it's been running in production for six months — with applications depending on its current configuration — is significantly harder and riskier than starting from a clean, hardened image.
Practice Questions
What Linux command is used to read and write kernel runtime parameters — including security-relevant settings like ASLR and SYN cookie protection?
What command should you always run before reloading the SSH daemon to verify there are no syntax errors in the config?
What open-source security auditing tool scans a Linux system against hundreds of hardening checks and produces a hardening index score?
Quiz
What is the key difference between security hardening and fixing misconfigurations?
What does setting kernel.randomize_va_space = 2 protect against?
Why is running systemctl disable alone not sufficient when removing an unnecessary service?
Up Next · Lesson 21
Malware Types & Analysis
Going deeper on malware — how each type works at a technical level, how analysts identify and classify samples, and the tools used to safely examine malicious code without getting infected.