BT
Privacy ToolboxJournalProjectsResumeBookmarks
Feed
Privacy Toolbox
Journal
Projects
Resume
Bookmarks
Intel
CIPHER
Threat Actors
Privacy Threats
Dashboard
CVEs
Tags
Intel
CIPHERThreat ActorsPrivacy ThreatsDashboardCVEsTags

Intel

  • Feed
  • Threat Actors
  • Privacy Threats
  • Dashboard
  • Privacy Toolbox
  • CVEs

Personal

  • Journal
  • Projects

Resources

  • Subscribe
  • Bookmarks
  • Developers
  • Tags
Cybersecurity News & Analysis
github
defconxt
•
© 2026
•
blacktemple.net
  • Overview
  • Synthesis
  • Hardening Guides
  • SIEM & SOC
  • Sigma Detection
  • Threat Hunting
  • Logging & Monitoring
  • EDR & AV Internals
  • Windows Event Logs
  • PowerShell Security
  • SecOps Runbooks
  • Security Automation
  • Insider Threat & DLP
  • AI Defense
  • Evasion vs Detection
  • Overview
  • Synthesis
  • Hardening Guides
  • SIEM & SOC
  • Sigma Detection
  • Threat Hunting
  • Logging & Monitoring
  • EDR & AV Internals
  • Windows Event Logs
  • PowerShell Security
  • SecOps Runbooks
  • Security Automation
  • Insider Threat & DLP
  • AI Defense
  • Evasion vs Detection
  1. CIPHER
  2. /Defensive
  3. /CIPHER Ultimate Hardening Reference Guide

CIPHER Ultimate Hardening Reference Guide

CIPHER Ultimate Hardening Reference Guide

Classification: TRAINING REFERENCE — Definitive hardening configurations for production systems Version: 1.0 — 2026-03-14 Sources: CIS Benchmarks, DISA STIGs, Mozilla TLS Guidelines, OWASP Cheat Sheets, madaidans-insecurities, ANSSI, NIST SP 800-series, NSA Cybersecurity Advisories


Table of Contents

  1. Linux Server Hardening (Ubuntu/RHEL)
  2. Windows Server Hardening
  3. SSH Hardening
  4. TLS/SSL Hardening
  5. Web Server Hardening (NGINX/Apache)
  6. Database Hardening (MySQL/PostgreSQL)
  7. Application Framework Hardening
  8. Docker Container Hardening
  9. Kubernetes Cluster Hardening

1. Linux Server Hardening

Applicable to: Ubuntu 22.04/24.04 LTS, RHEL 8/9, Debian 12 Standards: CIS Benchmark L1/L2, DISA STIG, ANSSI Configuration Recommendations

1.1 Kernel Hardening — sysctl Parameters

Place in /etc/sysctl.d/99-hardening.conf:

# === Kernel Self-Protection ===
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.printk = 3 3 3 3
kernel.unprivileged_bpf_disabled = 1
net.core.bpf_jit_harden = 2
dev.tty.ldisc_autoload = 0
vm.unprivileged_userfaultfd = 0
kernel.kexec_load_disabled = 1
kernel.sysrq = 4
kernel.unprivileged_userns_clone = 0
kernel.perf_event_paranoid = 3
kernel.yama.ptrace_scope = 2
kernel.core_pattern = |/bin/false
fs.suid_dumpable = 0

# === Memory Protections ===
vm.mmap_rnd_bits = 32
vm.mmap_rnd_compat_bits = 16
vm.swappiness = 1
fs.protected_symlinks = 1
fs.protected_hardlinks = 1
fs.protected_fifos = 2
fs.protected_regular = 2

# === Network Hardening — IPv4 ===
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_rfc1337 = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.icmp_echo_ignore_all = 1
net.ipv4.tcp_sack = 0
net.ipv4.tcp_dsack = 0
net.ipv4.tcp_fack = 0
net.ipv4.tcp_timestamps = 0

# === Network Hardening — IPv6 ===
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
net.ipv6.conf.all.use_tempaddr = 2
net.ipv6.conf.default.use_tempaddr = 2

Apply: sysctl --system

1.2 Boot Parameters (GRUB)

Edit /etc/default/grub, append to GRUB_CMDLINE_LINUX:

slab_nomerge init_on_alloc=1 init_on_free=1 page_alloc.shuffle=1 pti=on randomize_kstack_offset=on vsyscall=none debugfs=off oops=panic module.sig_enforce=1 lockdown=confidentiality mce=0 quiet loglevel=0

CPU mitigations (append for maximum security, accept ~5-30% performance cost):

spectre_v2=on spec_store_bypass_disable=on tsx=off tsx_async_abort=full,nosmt mds=full,nosmt l1tf=full,force nosmt=force kvm.nx_huge_pages=force

DMA protection (if applicable):

intel_iommu=on amd_iommu=on efi=disable_early_pci_dma

Regenerate:

# Ubuntu/Debian
update-grub
# RHEL
grub2-mkconfig -o /boot/grub2/grub.cfg

1.3 GRUB Bootloader Password

grub-mkpasswd-pbkdf2
# Record hash output

Create /etc/grub.d/40_password:

#!/bin/sh
cat <<EOF
set superusers="grubadmin"
password_pbkdf2 grubadmin grub.pbkdf2.sha512.10000.<hash>
EOF
chmod 700 /etc/grub.d/40_password
update-grub

1.4 Kernel Module Blacklisting

Create /etc/modprobe.d/hardening-blacklist.conf:

# Unnecessary network protocols
install dccp /bin/false
install sctp /bin/false
install rds /bin/false
install tipc /bin/false
install n-hdlc /bin/false
install ax25 /bin/false
install netrom /bin/false
install x25 /bin/false
install rose /bin/false
install decnet /bin/false
install econet /bin/false
install af_802154 /bin/false
install ipx /bin/false
install appletalk /bin/false
install psnap /bin/false
install p8023 /bin/false
install p8022 /bin/false
install can /bin/false
install atm /bin/false

# Unnecessary filesystems
install cramfs /bin/false
install freevxfs /bin/false
install jffs2 /bin/false
install hfs /bin/false
install hfsplus /bin/false
install squashfs /bin/false
install udf /bin/false

# Network filesystems (if not needed)
install cifs /bin/true
install nfs /bin/true
install nfsv3 /bin/true
install nfsv4 /bin/true
install ksmbd /bin/true
install gfs2 /bin/true

# Wireless/Bluetooth (if server)
install bluetooth /bin/false
install btusb /bin/false

# FireWire/Thunderbolt (attack surface)
install firewire-core /bin/false
install thunderbolt /bin/false

# Camera (servers)
install uvcvideo /bin/false

1.5 Filesystem Hardening

/etc/fstab mount options:

proc       /proc    proc    nosuid,nodev,noexec,hidepid=2,gid=proc   0 0
/dev/sda2  /        ext4    defaults                                  1 1
/dev/sda3  /home    ext4    defaults,nosuid,noexec,nodev              1 2
/dev/sda4  /tmp     ext4    defaults,nosuid,noexec,nodev              1 2
/dev/sda5  /var     ext4    defaults,nosuid                           1 2
/dev/sda1  /boot    ext4    defaults,nosuid,noexec,nodev              1 2
tmpfs      /dev/shm tmpfs   defaults,noexec,nodev,nosuid              0 0
tmpfs      /run     tmpfs   defaults,noexec,nodev,nosuid,size=512M    0 0

Key mount options:

  • nosuid — ignore SUID/SGID bits
  • noexec — prevent binary execution
  • nodev — ignore device files
  • hidepid=2 — users can only see their own processes

Directory permissions:

chmod 700 /home/*
chmod 700 /boot /usr/src /lib/modules /usr/lib/modules
chmod 600 /etc/shadow /etc/gshadow
chmod 644 /etc/passwd /etc/group
chmod 600 /etc/crontab
chmod 700 /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly

Default umask — set in /etc/profile and /etc/login.defs:

umask 0077

1.6 Core Dumps Disabled

/etc/security/limits.conf:

* hard core 0

/etc/systemd/coredump.conf.d/disable.conf:

[Coredump]
Storage=none

/etc/sysctl.d/99-hardening.conf (already included above):

fs.suid_dumpable = 0
kernel.core_pattern = |/bin/false

1.7 User Account Security

Lock root login:

passwd -l root

Empty /etc/securetty to prevent root TTY login.

Restrict su — /etc/pam.d/su and /etc/pam.d/su-l:

auth required pam_wheel.so use_uid

Login delay — /etc/pam.d/system-login:

auth optional pam_faildelay.so delay=4000000

Password complexity — /etc/pam.d/passwd or /etc/security/pwquality.conf:

password required pam_pwquality.so retry=2 minlen=16 difok=6 dcredit=-3 ucredit=-2 lcredit=-2 ocredit=-3 enforce_for_root
password required pam_unix.so use_authtok sha512 shadow rounds=65536

/etc/login.defs:

PASS_MAX_DAYS   90
PASS_MIN_DAYS   7
PASS_WARN_AGE   14
LOGIN_RETRIES   3
LOGIN_TIMEOUT   60
ENCRYPT_METHOD  SHA512
SHA_CRYPT_ROUNDS 65536
UMASK           077

Account lockout — /etc/pam.d/common-auth (Ubuntu) or /etc/pam.d/system-auth (RHEL):

auth required pam_faillock.so preauth silent deny=5 unlock_time=900 fail_interval=900
auth required pam_faillock.so authfail deny=5 unlock_time=900 fail_interval=900

1.8 Automatic Security Updates

Ubuntu/Debian — /etc/apt/apt.conf.d/20auto-upgrades:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";

/etc/apt/apt.conf.d/50unattended-upgrades:

Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Remove-Unused-Dependencies "true";

APT sandboxing — /etc/apt/apt.conf.d/40sandbox:

APT::Sandbox::Seccomp "true";

RHEL — enable automatic security updates:

dnf install dnf-automatic

/etc/dnf/automatic.conf:

[commands]
upgrade_type = security
apply_updates = yes
systemctl enable --now dnf-automatic.timer

1.9 Firewall (UFW / firewalld)

UFW (Ubuntu):

ufw default deny incoming
ufw default allow outgoing
ufw limit ssh
ufw allow 443/tcp
ufw enable
ufw logging on

firewalld (RHEL):

firewall-cmd --set-default-zone=drop
firewall-cmd --zone=drop --add-service=ssh --permanent
firewall-cmd --zone=drop --add-service=https --permanent
firewall-cmd --reload

nftables (modern replacement) — /etc/nftables.conf:

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        iif lo accept
        ct state established,related accept
        ct state invalid drop
        tcp dport 22 ct state new limit rate 4/minute accept
        tcp dport 443 ct state new accept
        icmp type echo-request limit rate 1/second accept
        counter drop
    }
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

1.10 Fail2Ban

/etc/fail2ban/jail.local:

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
banaction = nftables-multiport
backend = systemd
ignoreip = 127.0.0.1/8 ::1

[sshd]
enabled = true
port = ssh
maxretry = 3
bantime = 86400

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
maxretry = 3
bantime = 3600

[nginx-limit-req]
enabled = true
filter = nginx-limit-req
port = http,https
maxretry = 5
bantime = 7200

1.11 Auditd Rules

/etc/audit/rules.d/hardening.rules:

# Delete all existing rules
-D

# Buffer size
-b 8192

# Failure mode (2 = panic, 1 = printk)
-f 1

# Time changes
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change
-a always,exit -F arch=b64 -S clock_settime -k time-change
-w /etc/localtime -p wa -k time-change

# User/group changes
-w /etc/group -p wa -k identity
-w /etc/passwd -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/security/opasswd -p wa -k identity

# Network configuration
-a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale
-w /etc/issue -p wa -k system-locale
-w /etc/issue.net -p wa -k system-locale
-w /etc/hosts -p wa -k system-locale
-w /etc/hostname -p wa -k system-locale

# Login/logout events
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock/ -p wa -k logins
-w /var/log/wtmp -p wa -k logins
-w /var/log/btmp -p wa -k logins

# Session initiation
-w /var/run/utmp -p wa -k session
-w /var/log/wtmp -p wa -k session
-w /var/log/btmp -p wa -k session

# Discretionary access control changes
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -k perm_mod
-a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -k perm_mod
-a always,exit -F arch=b64 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -k perm_mod

# Unauthorized access attempts
-a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -k access
-a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -k access

# Privileged commands (generate per system)
# find / -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null
-a always,exit -F path=/usr/bin/sudo -F perm=x -k privileged
-a always,exit -F path=/usr/bin/su -F perm=x -k privileged
-a always,exit -F path=/usr/bin/passwd -F perm=x -k privileged
-a always,exit -F path=/usr/bin/chage -F perm=x -k privileged
-a always,exit -F path=/usr/bin/gpasswd -F perm=x -k privileged
-a always,exit -F path=/usr/bin/newgrp -F perm=x -k privileged

# Kernel module loading
-a always,exit -F arch=b64 -S init_module -S finit_module -S delete_module -k modules
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules

# Sudoers changes
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers

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

# Cron changes
-w /etc/crontab -p wa -k cron
-w /etc/cron.d/ -p wa -k cron
-w /etc/cron.daily/ -p wa -k cron
-w /etc/cron.hourly/ -p wa -k cron
-w /var/spool/cron/ -p wa -k cron

# Make configuration immutable (must be last)
-e 2

Load: augenrules --load

1.12 Mandatory Access Control

AppArmor (Ubuntu/Debian):

apt install apparmor apparmor-utils apparmor-profiles apparmor-profiles-extra
# Boot parameters:
# apparmor=1 security=apparmor

# Enforce all loaded profiles:
aa-enforce /etc/apparmor.d/*

# Generate profile for new application:
aa-genprof /path/to/application

SELinux (RHEL):

# /etc/selinux/config
SELINUX=enforcing
SELINUXTYPE=targeted

# Boot parameters:
# selinux=1 security=selinux

# Verify:
getenforce   # Should return "Enforcing"
sestatus     # Full status

# Troubleshoot:
sealert -a /var/log/audit/audit.log

1.13 File Integrity Monitoring (AIDE)

# Ubuntu
apt install aide
aideinit
cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db

# RHEL
dnf install aide
aide --init
cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz

# Check:
aide --check

# Cron (daily):
0 5 * * * /usr/bin/aide --check | mail -s "AIDE Report" admin@example.com

1.14 Systemd Service Sandboxing Template

Apply to all custom services — /etc/systemd/system/<service>.service.d/hardening.conf:

[Service]
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ProtectKernelLogs=true
ProtectHostname=true
ProtectClock=true
ProtectProc=invisible
ProcSubset=pid
PrivateTmp=true
PrivateUsers=true
PrivateDevices=true
PrivateIPC=true
MemoryDenyWriteExecute=true
NoNewPrivileges=true
LockPersonality=true
RestrictRealtime=true
RestrictSUIDSGID=true
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=true
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources
UMask=0077
IPAddressDeny=any

Verify hardening score: systemd-analyze security <service>

1.15 USBGuard (Physical Attack Surface)

# Install
apt install usbguard   # or dnf install usbguard

# Generate initial policy from currently connected devices
usbguard generate-policy > /etc/usbguard/rules.conf

# Enable
systemctl enable --now usbguard

# Example policy — allow only keyboard and storage
allow id 1d6b:0002 name "xHCI Host Controller"
reject via-port "*"

1.16 Hardened Memory Allocator

System-wide — /etc/ld.so.preload:

/usr/lib/libhardened_malloc.so

Per-application:

LD_PRELOAD="/usr/lib/libhardened_malloc.so" /usr/bin/application

1.17 Intrusion Detection Stack

Tool Purpose Install
AIDE File integrity monitoring apt install aide
Fail2Ban Brute-force prevention apt install fail2ban
CrowdSec Community threat intelligence curl -s https://install.crowdsec.net | bash
OSSEC/Wazuh HIDS + log analysis Agent-based deployment
Lynis Security auditing apt install lynis && lynis audit system
rkhunter Rootkit detection apt install rkhunter && rkhunter --check
chkrootkit Rootkit detection apt install chkrootkit && chkrootkit
ClamAV Malware scanning apt install clamav clamav-daemon

1.18 Automation with Ansible Lockdown

# CIS Benchmark automation
ansible-galaxy install ansible-lockdown.UBUNTU22-CIS
ansible-galaxy install ansible-lockdown.RHEL9-CIS

# DISA STIG automation
ansible-galaxy install ansible-lockdown.UBUNTU22-STIG
ansible-galaxy install ansible-lockdown.RHEL9-STIG

# Audit with Goss
ansible-galaxy install ansible-lockdown.UBUNTU22-CIS-Audit
ansible-galaxy install ansible-lockdown.RHEL9-CIS-Audit

2. Windows Server Hardening

Applicable to: Windows Server 2019/2022/2025 Standards: CIS Benchmark, Microsoft Security Baselines, DISA STIGs, NSA Guidance

2.1 Account Policies

Password Policy (via Group Policy or secpol.msc):

Enforce password history: 24 passwords
Maximum password age: 60 days
Minimum password age: 1 day
Minimum password length: 14 characters
Password must meet complexity requirements: Enabled
Store passwords using reversible encryption: Disabled

Account Lockout Policy:

Account lockout duration: 30 minutes
Account lockout threshold: 5 invalid logon attempts
Reset account lockout counter after: 30 minutes

2.2 Audit Policies

Advanced Audit Policy (via auditpol /set):

auditpol /set /subcategory:"Credential Validation" /success:enable /failure:enable
auditpol /set /subcategory:"Logon" /success:enable /failure:enable
auditpol /set /subcategory:"Logoff" /success:enable
auditpol /set /subcategory:"Account Lockout" /success:enable /failure:enable
auditpol /set /subcategory:"Special Logon" /success:enable
auditpol /set /subcategory:"Process Creation" /success:enable
auditpol /set /subcategory:"Audit Policy Change" /success:enable /failure:enable
auditpol /set /subcategory:"Authentication Policy Change" /success:enable
auditpol /set /subcategory:"Security Group Management" /success:enable
auditpol /set /subcategory:"User Account Management" /success:enable /failure:enable
auditpol /set /subcategory:"Sensitive Privilege Use" /success:enable /failure:enable
auditpol /set /subcategory:"Security System Extension" /success:enable
auditpol /set /subcategory:"System Integrity" /success:enable /failure:enable

Enable command-line process auditing:

Computer Configuration > Administrative Templates > System > Audit Process Creation > Include command line in process creation events: Enabled

2.3 User Rights Assignment

Access this computer from the network: Administrators, Authenticated Users
Allow log on locally: Administrators
Deny access to this computer from the network: Guests, Local account
Deny log on through Remote Desktop Services: Guests, Local account

2.4 Security Options

Accounts: Administrator account status: Disabled (rename first)
Accounts: Guest account status: Disabled
Accounts: Rename administrator account: <custom_name>
Accounts: Rename guest account: <custom_name>
Interactive logon: Do not display last user name: Enabled
Interactive logon: Machine inactivity limit: 900 seconds
Network access: Do not allow anonymous enumeration of SAM accounts: Enabled
Network access: Do not allow anonymous enumeration of SAM accounts and shares: Enabled
Network security: LAN Manager authentication level: Send NTLMv2 response only. Refuse LM & NTLM
Network security: Minimum session security for NTLM SSP: Require NTLMv2 session security, Require 128-bit encryption
User Account Control: Admin Approval Mode for Built-in Administrator: Enabled
User Account Control: Behavior of elevation prompt for administrators: Prompt for consent on the secure desktop
User Account Control: Run all administrators in Admin Approval Mode: Enabled

2.5 Windows Firewall

# Enable for all profiles
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True -DefaultInboundAction Block -DefaultOutboundAction Allow -LogAllowed True -LogBlocked True -LogMaxSizeKilobytes 16384

# Block inbound by default
Set-NetFirewallProfile -Profile Public -AllowInboundRules False

# Allow specific services
New-NetFirewallRule -DisplayName "RDP" -Direction Inbound -Protocol TCP -LocalPort 3389 -Action Allow -Profile Domain -RemoteAddress "10.0.0.0/8"
New-NetFirewallRule -DisplayName "WinRM" -Direction Inbound -Protocol TCP -LocalPort 5985,5986 -Action Allow -Profile Domain -RemoteAddress "10.0.0.0/8"

2.6 SMB Hardening

# Disable SMBv1
Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -NoRestart
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force

# Enforce SMB signing
Set-SmbServerConfiguration -RequireSecuritySignature $true -Force
Set-SmbClientConfiguration -RequireSecuritySignature $true -Force

# Enforce SMB encryption (SMBv3)
Set-SmbServerConfiguration -EncryptData $true -Force
Set-SmbServerConfiguration -RejectUnencryptedAccess $true -Force

2.7 PowerShell Security

# Enable script block logging
# Group Policy: Administrative Templates > Windows Components > Windows PowerShell
# Turn on PowerShell Script Block Logging: Enabled

# Enable module logging
# Turn on Module Logging: Enabled (all modules: *)

# Enable transcription
# Turn on PowerShell Transcription: Enabled
# Include invocation headers: Enabled

# Constrained Language Mode (for non-admin users)
[Environment]::SetEnvironmentVariable('__PSLockdownPolicy', '4', 'Machine')

# Execution policy
Set-ExecutionPolicy RemoteSigned -Force

2.8 AppLocker / WDAC

# AppLocker — default deny with whitelisting
# Export baseline rules:
Get-AppLockerPolicy -Effective -Xml > C:\AppLockerPolicy.xml

# WDAC (Windows Defender Application Control) — preferred over AppLocker
New-CIPolicy -Level Publisher -FilePath "C:\WDAC\BasePolicy.xml" -UserPEs -Fallback Hash
ConvertFrom-CIPolicy "C:\WDAC\BasePolicy.xml" "C:\WDAC\BasePolicy.bin"

2.9 Windows Defender Configuration

Set-MpPreference -DisableRealtimeMonitoring $false
Set-MpPreference -MAPSReporting Advanced
Set-MpPreference -SubmitSamplesConsent SendAllSamples
Set-MpPreference -PUAProtection Enabled
Set-MpPreference -EnableNetworkProtection Enabled
Set-MpPreference -AttackSurfaceReductionRules_Ids @(
    "BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550",  # Block executable content from email
    "D4F940AB-401B-4EFC-AADC-AD5F3C50688A",  # Block Office child processes
    "3B576869-A4EC-4529-8536-B80A7769E899",  # Block Office from creating executables
    "75668C1F-73B5-4CF0-BB93-3ECF5CB7CC84",  # Block Office injection
    "D3E037E1-3EB8-44C8-A917-57927947596D",  # Block JS/VBS launching executables
    "5BEB7EFE-FD9A-4556-801D-275E5FFC04CC",  # Block execution of obfuscated scripts
    "92E97FA1-2EDF-4476-BDD6-9DD0B4DDDC7B",  # Block Win32 API from Office macros
    "01443614-CD74-433A-B99E-2ECDC07BFC25"   # Block executable files from running unless they meet criteria
) -AttackSurfaceReductionRules_Actions @(1,1,1,1,1,1,1,1)

# Enable Credential Guard
reg add "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard" /v EnableVirtualizationBasedSecurity /t REG_DWORD /d 1 /f
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LsaCfgFlags /t REG_DWORD /d 1 /f

2.10 Registry Hardening

# Disable LLMNR
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" -Name "EnableMulticast" -Value 0 -PropertyType DWord -Force

# Disable NetBIOS over TCP/IP (per adapter)
$adapters = Get-WmiObject Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True"
$adapters | ForEach-Object { $_.SetTcpipNetbios(2) }

# Disable WPAD
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad" -Name "WpadOverride" -Value 1 -PropertyType DWord -Force

# Disable WDigest (prevent plaintext credential caching)
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest" -Name "UseLogonCredential" -Value 0

# Disable remote assistance
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Remote Assistance" -Name "fAllowToGetHelp" -Value 0

# Enable LSA protection
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "RunAsPPL" -Value 1 -PropertyType DWord -Force

2.11 Windows Event Log Sizing

wevtutil sl Security /ms:1073741824    # 1 GB
wevtutil sl Application /ms:134217728   # 128 MB
wevtutil sl System /ms:134217728        # 128 MB
wevtutil sl "Windows PowerShell" /ms:134217728
wevtutil sl "Microsoft-Windows-PowerShell/Operational" /ms:134217728
wevtutil sl "Microsoft-Windows-Sysmon/Operational" /ms:1073741824

2.12 BitLocker

Enable-BitLocker -MountPoint "C:" -EncryptionMethod XtsAes256 -UsedSpaceOnly -TpmProtector
Add-BitLockerKeyProtector -MountPoint "C:" -RecoveryPasswordProtector

2.13 Automation

# Ansible Lockdown for Windows
ansible-galaxy install ansible-lockdown.Windows-2022-CIS
ansible-galaxy install ansible-lockdown.Windows-2022-STIG

3. SSH Hardening

Standards: NIST IR 7966, ANSSI OpenSSH Recommendations, Mozilla Guidelines

3.1 Server Configuration — /etc/ssh/sshd_config

# === Protocol ===
Protocol 2

# === Network ===
Port 22
AddressFamily inet
ListenAddress 0.0.0.0

# === Authentication ===
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
UsePAM yes
AuthenticationMethods publickey
PubkeyAuthentication yes
PermitEmptyPasswords no
MaxAuthTries 3
MaxSessions 2
LoginGraceTime 30

# === Key Exchange ===
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256

# === Host Keys ===
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key

HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256

# === Ciphers ===
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr

# === MACs ===
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

# === Forwarding ===
AllowTcpForwarding no
AllowAgentForwarding no
X11Forwarding no
PermitTunnel no
GatewayPorts no
DisableForwarding yes

# === Access Control ===
AllowGroups ssh-users
DenyUsers root
DenyGroups root

# === Logging ===
SyslogFacility AUTH
LogLevel VERBOSE

# === Security ===
StrictModes yes
PermitUserEnvironment no
IgnoreRhosts yes
HostbasedAuthentication no
RekeyLimit 512M 1h

# === Session ===
ClientAliveInterval 300
ClientAliveCountMax 2
TCPKeepAlive no

# === Banners ===
Banner /etc/issue.net
PrintMotd no
PrintLastLog yes

# === Misc ===
Compression no
UseDNS no
AcceptEnv LANG LC_*
Subsystem sftp internal-sftp

3.2 Remove Weak DH Keys

awk '$5 >= 3071' /etc/ssh/moduli > /etc/ssh/moduli.safe
mv /etc/ssh/moduli.safe /etc/ssh/moduli

3.3 Regenerate Host Keys (Ed25519 only for max security)

rm -f /etc/ssh/ssh_host_*
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
chmod 600 /etc/ssh/ssh_host_*_key
chmod 644 /etc/ssh/ssh_host_*_key.pub

3.4 Client Configuration — ~/.ssh/config

Host *
    HashKnownHosts yes
    IdentitiesOnly yes
    ServerAliveInterval 300
    ServerAliveCountMax 2
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519
    HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256
    KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
    Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
    MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
    VisualHostKey yes

3.5 SSH Key Generation

# Preferred
ssh-keygen -t ed25519 -a 100 -C "user@host"

# RSA fallback (when Ed25519 not supported)
ssh-keygen -t rsa -b 4096 -a 100 -C "user@host"

3.6 SSH Certificate Authority (Enterprise)

# Generate CA key
ssh-keygen -t ed25519 -f /etc/ssh/ca_key -C "SSH CA"

# Sign host key
ssh-keygen -s /etc/ssh/ca_key -I "hostname" -h -n hostname.example.com -V +52w /etc/ssh/ssh_host_ed25519_key.pub

# Sign user key
ssh-keygen -s /etc/ssh/ca_key -I "username" -n username -V +24h ~/.ssh/id_ed25519.pub

# sshd_config:
TrustedUserCAKeys /etc/ssh/ca_key.pub

# Client known_hosts:
@cert-authority *.example.com <ca_public_key>

4. TLS/SSL Hardening

Standards: Mozilla Server Side TLS, NIST SP 800-52r2, ANSSI TLS Recommendations, Netherlands NCSC TLS Guidelines

4.1 Mozilla Modern Configuration (TLS 1.3 Only)

Parameter Value
Protocols TLS 1.3
Cipher Suites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
Certificate ECDSA P-256
Curves X25519, prime256v1, secp384r1
HSTS max-age=63072000 (2 years)
Cert Lifespan 90 days
Cipher Preference Client chooses

4.2 Mozilla Intermediate Configuration (Recommended)

Parameter Value
Protocols TLS 1.2 + TLS 1.3
TLS 1.3 Suites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
TLS 1.2 Suites ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
Certificate ECDSA P-256 (preferred) or RSA 2048-bit
Curves X25519, prime256v1, secp384r1
DH Params ffdhe2048 (RFC 7919), 2048-bit minimum
HSTS max-age=63072000
Cert Lifespan 90-366 days
Cipher Preference Client chooses

4.3 NGINX TLS Configuration (Intermediate)

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Protocols
    ssl_protocols TLSv1.2 TLSv1.3;

    # Ciphers (Intermediate)
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;

    # DH Parameters
    ssl_dhparam /etc/ssl/dhparam.pem;

    # Curves
    ssl_ecdh_curve X25519:prime256v1:secp384r1;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    resolver 1.1.1.1 8.8.8.8 valid=300s;
    resolver_timeout 5s;

    # Session
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}

4.4 Apache TLS Configuration (Intermediate)

<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    # Protocols
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

    # Ciphers
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
    SSLHonorCipherOrder off

    # DH Parameters
    SSLOpenSSLConfCmd DHParameters "/etc/ssl/dhparam.pem"

    # Curves
    SSLOpenSSLConfCmd Curves X25519:prime256v1:secp384r1

    # OCSP Stapling
    SSLUseStapling on
    SSLStaplingResponderTimeout 5
    SSLStaplingReturnResponderErrors off

    # HSTS
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</VirtualHost>

SSLStaplingCache shmcb:/var/run/ocsp(128000)

4.5 Generate DH Parameters

openssl dhparam -out /etc/ssl/dhparam.pem 2048
chmod 600 /etc/ssl/dhparam.pem

4.6 Certificate Best Practices

  • Use ECDSA P-256 certificates (faster, smaller, modern)
  • Automate with ACME (Let's Encrypt / certbot)
  • 90-day maximum certificate lifespan
  • Enable CAA DNS records: example.com. CAA 0 issue "letsencrypt.org"
  • Enable Certificate Transparency monitoring
  • Pin backup keys via Expect-CT header (deprecated but useful for monitoring)

5. Web Server Hardening

5.1 NGINX Hardening

/etc/nginx/conf.d/hardening.conf:

# === Hide version ===
server_tokens off;
more_clear_headers Server;

# === Security Headers ===
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "0" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;

# === Request Limits ===
client_max_body_size 10m;
client_body_buffer_size 1k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 15;
send_timeout 10;

# === Rate Limiting ===
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;

# === Buffer Overflow Protection ===
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;

# === Disable unwanted HTTP methods ===
# In server block:
# if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|PATCH)$ ) {
#     return 405;
# }

Per-location rate limiting:

location /login {
    limit_req zone=login burst=3 nodelay;
    limit_conn addr 5;
    proxy_pass http://backend;
}

location / {
    limit_req zone=general burst=20 nodelay;
    proxy_pass http://backend;
}

Deny access to hidden files:

location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

5.2 Apache Hardening

/etc/apache2/conf-enabled/hardening.conf or /etc/httpd/conf.d/hardening.conf:

# === Hide version ===
ServerTokens Prod
ServerSignature Off
TraceEnable Off

# === Security Headers ===
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "0"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"
Header always set Cross-Origin-Embedder-Policy "require-corp"
Header always set Cross-Origin-Opener-Policy "same-origin"
Header always set Cross-Origin-Resource-Policy "same-origin"
Header always unset X-Powered-By

# === Disable directory listing ===
Options -Indexes -Includes -ExecCGI
<Directory />
    Options None
    AllowOverride None
    Require all denied
</Directory>

# === Restrict HTTP methods ===
<LimitExcept GET POST HEAD>
    Require all denied
</LimitExcept>

# === Request limits ===
LimitRequestBody 10485760
LimitRequestFields 50
LimitRequestFieldSize 8190
LimitRequestLine 8190
Timeout 60
KeepAliveTimeout 5
MaxKeepAliveRequests 100

# === Prevent clickjacking ===
Header always append X-Frame-Options DENY

# === Disable ETag (information leak) ===
FileETag None

# === Deny access to hidden files ===
<FilesMatch "^\.">
    Require all denied
</FilesMatch>

# === Disable unnecessary modules ===
# a2dismod autoindex status info cgi

mod_security (WAF):

<IfModule security2_module>
    SecRuleEngine On
    SecRequestBodyAccess On
    SecResponseBodyAccess Off
    SecRequestBodyLimit 13107200
    SecRequestBodyNoFilesLimit 131072
    SecRequestBodyLimitAction Reject
    SecAuditEngine RelevantOnly
    SecAuditLogRelevantStatus "^(?:5|4(?!04))"
    SecAuditLogType Serial
    SecAuditLog /var/log/modsec_audit.log
    IncludeOptional /etc/modsecurity/crs/*.conf
</IfModule>

5.3 Shared Configuration

Redirect HTTP to HTTPS (NGINX):

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    return 301 https://$host$request_uri;
}

Redirect HTTP to HTTPS (Apache):

<VirtualHost *:80>
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

6. Database Hardening

6.1 PostgreSQL Hardening

postgresql.conf:

# === Network ===
listen_addresses = 'localhost'          # Only local connections, or specific IPs
port = 5432

# === Authentication ===
password_encryption = scram-sha-256     # Never md5

# === SSL ===
ssl = on
ssl_cert_file = '/etc/ssl/certs/server.crt'
ssl_key_file = '/etc/ssl/private/server.key'
ssl_ca_file = '/etc/ssl/certs/ca.crt'
ssl_min_protocol_version = 'TLSv1.2'
ssl_ciphers = 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'
ssl_prefer_server_ciphers = on

# === Logging ===
logging_collector = on
log_directory = '/var/log/postgresql'
log_filename = 'postgresql-%Y-%m-%d.log'
log_file_mode = 0600
log_connections = on
log_disconnections = on
log_duration = on
log_line_prefix = '%m [%p] %q%u@%d '
log_statement = 'ddl'                   # Log DDL; use 'all' for high-security
log_min_duration_statement = 1000       # Log queries > 1s
log_checkpoints = on
log_lock_waits = on
log_temp_files = 0

# === Resource Limits ===
max_connections = 100
statement_timeout = 60000               # 60s query timeout
idle_in_transaction_session_timeout = 600000  # 10min

# === Row-Level Security ===
# Enable per-table with: ALTER TABLE t ENABLE ROW LEVEL SECURITY;

pg_hba.conf (host-based authentication):

# TYPE  DATABASE  USER       ADDRESS        METHOD
local   all       postgres                  peer
local   all       all                       scram-sha-256
host    all       all        127.0.0.1/32   scram-sha-256
host    all       all        ::1/128        scram-sha-256
hostssl app_db    app_user   10.0.0.0/24    scram-sha-256
# Deny everything else
host    all       all        0.0.0.0/0      reject

User privilege hardening:

-- Revoke default public schema permissions
REVOKE ALL ON SCHEMA public FROM PUBLIC;
REVOKE CREATE ON SCHEMA public FROM PUBLIC;

-- Application user with minimal privileges
CREATE ROLE app_user WITH LOGIN PASSWORD 'strong_password_here' VALID UNTIL '2027-01-01';
GRANT CONNECT ON DATABASE app_db TO app_user;
GRANT USAGE ON SCHEMA app_schema TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app_schema TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA app_schema GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;

-- Read-only user
CREATE ROLE readonly_user WITH LOGIN PASSWORD 'strong_password_here';
GRANT CONNECT ON DATABASE app_db TO readonly_user;
GRANT USAGE ON SCHEMA app_schema TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA app_schema TO readonly_user;

-- Prevent superuser creation by non-superusers
-- (default behavior, verify)

pgaudit extension (audit logging):

# postgresql.conf
shared_preload_libraries = 'pgaudit'
pgaudit.log = 'write,ddl,role'
pgaudit.log_catalog = off
pgaudit.log_parameter = on
pgaudit.log_statement_once = on

6.2 MySQL/MariaDB Hardening

/etc/mysql/mysql.conf.d/hardening.cnf or /etc/my.cnf.d/hardening.cnf:

[mysqld]
# === Network ===
bind-address = 127.0.0.1
port = 3306
skip-networking = 0               # Set to 1 if only socket connections needed
socket = /var/run/mysqld/mysqld.sock

# === Authentication ===
default_authentication_plugin = caching_sha2_password
# MySQL 8.4+: authentication_policy = caching_sha2_password

# === SSL/TLS ===
require_secure_transport = ON
ssl-ca = /etc/mysql/ssl/ca.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
tls_version = TLSv1.2,TLSv1.3
ssl_cipher = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384

# === Security ===
local-infile = 0
skip-symbolic-links = 1
secure-file-priv = /var/lib/mysql-files
log-raw = OFF

# === Logging ===
general_log = OFF
general_log_file = /var/log/mysql/general.log
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_error = /var/log/mysql/error.log
log_error_verbosity = 3
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW

# === Audit (Enterprise or plugin) ===
# plugin-load-add = audit_log.so
# audit_log_policy = ALL
# audit_log_format = JSON

# === Limits ===
max_connections = 100
max_connect_errors = 10
wait_timeout = 600
interactive_timeout = 600
max_allowed_packet = 16M

# === Performance Schema (for monitoring) ===
performance_schema = ON

Post-install hardening:

mysql_secure_installation
# Answer yes to all prompts:
# - Set root password
# - Remove anonymous users
# - Disallow root login remotely
# - Remove test database
# - Reload privilege tables

User privilege hardening:

-- Remove default accounts
DROP USER IF EXISTS ''@'localhost';
DROP USER IF EXISTS ''@'%';
DROP USER IF EXISTS 'root'@'%';

-- Application user
CREATE USER 'app_user'@'10.0.0.%' IDENTIFIED BY 'strong_password_here' REQUIRE SSL;
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'10.0.0.%';
FLUSH PRIVILEGES;

-- Verify no accounts without passwords
SELECT User, Host FROM mysql.user WHERE authentication_string = '' OR authentication_string IS NULL;

-- Verify no wildcard hosts
SELECT User, Host FROM mysql.user WHERE Host = '%';

7. Application Framework Hardening

7.1 Node.js Security

Express.js hardened setup:

"use strict";

const express = require("express");
const helmet = require("helmet");
const hpp = require("hpp");
const rateLimit = require("express-rate-limit");
const session = require("express-session");

const app = express();

// === Helmet — Security Headers ===
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
    directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'"],
        styleSrc: ["'self'"],
        imgSrc: ["'self'", "data:"],
        fontSrc: ["'self'"],
        connectSrc: ["'self'"],
        frameAncestors: ["'none'"],
        objectSrc: ["'none'"],
        baseUri: ["'self'"],
        formAction: ["'self'"]
    }
}));
app.use(helmet.hsts({ maxAge: 63072000, includeSubDomains: true, preload: true }));

// === Request Size Limits ===
app.use(express.json({ limit: "1kb" }));
app.use(express.urlencoded({ extended: true, limit: "1kb" }));

// === HTTP Parameter Pollution ===
app.use(hpp());

// === Rate Limiting ===
const generalLimiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100,
    standardHeaders: true,
    legacyHeaders: false
});
app.use(generalLimiter);

const loginLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 5,
    message: "Too many login attempts"
});
app.use("/login", loginLimiter);

// === Session Configuration ===
app.use(session({
    secret: process.env.SESSION_SECRET,
    name: "__Host-sessionId",
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: true,
        httpOnly: true,
        sameSite: "strict",
        maxAge: 1800000, // 30 minutes
        path: "/",
        domain: "example.com"
    }
}));

// === Disable fingerprinting ===
app.disable("x-powered-by");
app.disable("etag");

// === Trust proxy (if behind reverse proxy) ===
app.set("trust proxy", 1);

Event loop protection:

const toobusy = require("toobusy-js");
app.use(function(req, res, next) {
    if (toobusy()) {
        res.status(503).send("Server Too Busy");
    } else {
        next();
    }
});

Uncaught exception handler:

process.on("uncaughtException", function(err) {
    console.error("Uncaught Exception:", err);
    process.exit(1);
});

process.on("unhandledRejection", function(reason, promise) {
    console.error("Unhandled Rejection at:", promise, "reason:", reason);
    process.exit(1);
});

Node.js Permissions Model (v20+):

node --permission index.js
node --permission --allow-fs-read=/app/uploads/ --allow-fs-write=/app/uploads/ index.js
# Restrict child processes, workers, addons
# --allow-child-process --allow-worker --allow-addons --allow-wasi

Key packages:

Package Purpose
helmet Security headers
hpp HTTP parameter pollution
express-rate-limit Rate limiting
express-mongo-sanitize NoSQL injection prevention
validator Input validation
escape-html Output escaping
node-esapi OWASP ESAPI for Node
toobusy-js Event loop protection
winston / pino Structured logging

Dangerous functions to NEVER use with user input:

  • eval()
  • child_process.exec() (use execFile() with argument arrays)
  • new Function() with user data
  • vm.runInNewContext() without sandbox
  • Unsanitized fs operations (path traversal)

7.2 Ruby on Rails Security

config/environments/production.rb:

Rails.application.configure do
  config.force_ssl = true
  config.ssl_options = { hsts: { subdomains: true, preload: true, expires: 63072000 } }

  # Session store — use database, not cookies for sensitive apps
  config.session_store :active_record_store

  # Log level
  config.log_level = :warn

  # Filter sensitive parameters from logs
  config.filter_parameters += [:password, :password_confirmation, :credit_card, :ssn, :token, :secret]
end

app/controllers/application_controller.rb:

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  # Security headers
  before_action :set_security_headers

  private

  def set_security_headers
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-XSS-Protection'] = '0'
    response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
    response.headers['Permissions-Policy'] = 'camera=(), microphone=(), geolocation=()'
  end
end

Security headers gem — Gemfile:

gem 'secure_headers'

config/initializers/secure_headers.rb:

SecureHeaders::Configuration.default do |config|
  config.csp = {
    default_src: %w('self'),
    script_src: %w('self'),
    style_src: %w('self'),
    img_src: %w('self' data:),
    font_src: %w('self'),
    connect_src: %w('self'),
    frame_ancestors: %w('none'),
    base_uri: %w('self'),
    form_action: %w('self')
  }
end

SQL injection prevention — always use parameterized queries:

# SAFE
Project.where("name LIKE ?", "%#{ActiveRecord::Base.sanitize_sql_like(params[:name])}%")
Project.where(name: params[:name])

# VULNERABLE — never do this
Project.where("name LIKE '#{params[:name]}'")

Mass assignment protection:

class UserController < ApplicationController
  def create
    @user = User.new(user_params)
  end

  private

  def user_params
    params.require(:user).permit(:name, :email)  # Whitelist only
  end
end

Password complexity with zxcvbn — config/initializers/devise.rb:

config.min_password_score = 4
config.stretches = Rails.env.test? ? 1 : 12

CORS — config/application.rb:

config.middleware.use Rack::Cors do
  allow do
    origins 'app.example.com'
    resource '/api/*',
      headers: %w[Origin Accept Content-Type Authorization],
      methods: [:get, :post, :put, :patch, :delete],
      max_age: 600
  end
end

Security scanning tools:

  • brakeman — static analysis for Rails
  • bundler-audit — dependency vulnerability checking
  • bearer — code security scanner

7.3 Django Security

settings.py production hardening:

import os

# === Core ===
DEBUG = False
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')  # 50+ chars, never hardcode
ALLOWED_HOSTS = ['www.example.com', 'example.com']

# === HTTPS ===
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

# === HSTS ===
SECURE_HSTS_SECONDS = 63072000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

# === Cookies ===
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Strict'
SESSION_COOKIE_AGE = 1800  # 30 minutes
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = 'Strict'

# === Content Security ===
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
SECURE_REFERRER_POLICY = 'strict-origin-when-cross-origin'

# === Middleware (order matters) ===
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'csp.middleware.CSPMiddleware',  # django-csp package
]

# === Password Validation ===
AUTH_PASSWORD_VALIDATORS = [
    {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
    {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
     'OPTIONS': {'min_length': 12}},
    {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
    {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]

# === File Uploads ===
FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440  # 2.5 MB
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440
DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000

# === Logging ===
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'WARNING',
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/django.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'WARNING',
            'propagate': True,
        },
        'django.security': {
            'handlers': ['file'],
            'level': 'INFO',
            'propagate': True,
        },
    },
}

# === Database ===
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'OPTIONS': {
            'sslmode': 'verify-full',
        },
    }
}

# === CSP (django-csp) ===
CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'",)
CSP_STYLE_SRC = ("'self'",)
CSP_IMG_SRC = ("'self'", "data:")
CSP_FONT_SRC = ("'self'",)
CSP_CONNECT_SRC = ("'self'",)
CSP_FRAME_ANCESTORS = ("'none'",)
CSP_BASE_URI = ("'self'",)
CSP_FORM_ACTION = ("'self'",)

Deployment check:

python manage.py check --deploy

7.4 .NET Security

Program.cs / Startup.cs hardening (ASP.NET Core):

var builder = WebApplication.CreateBuilder(args);

// === Identity/Password Policy ===
builder.Services.Configure<IdentityOptions>(options => {
    options.Password.RequireDigit = true;
    options.Password.RequiredLength = 12;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequireLowercase = true;
    options.Password.RequiredUniqueChars = 6;
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
    options.Lockout.MaxFailedAccessAttempts = 3;
    options.SignIn.RequireConfirmedEmail = true;
    options.User.RequireUniqueEmail = true;
});

// === Anti-Forgery (global) ===
builder.Services.AddMvc(options => {
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

// === CORS ===
builder.Services.AddCors(options => {
    options.AddPolicy("Strict", policy => {
        policy.WithOrigins("https://www.example.com")
              .AllowCredentials()
              .WithMethods("GET", "POST", "PUT", "DELETE")
              .WithHeaders("Content-Type", "Authorization");
    });
});

var app = builder.Build();

// === HTTPS ===
app.UseHttpsRedirection();
app.UseHsts();

// === Security Headers ===
app.Use(async (context, next) => {
    context.Response.Headers.Append("X-Content-Type-Options", "nosniff");
    context.Response.Headers.Append("X-Frame-Options", "DENY");
    context.Response.Headers.Append("Referrer-Policy", "strict-origin-when-cross-origin");
    context.Response.Headers.Append("Content-Security-Policy",
        "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';");
    context.Response.Headers.Append("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
    context.Response.Headers.Append("Cross-Origin-Opener-Policy", "same-origin");
    context.Response.Headers.Remove("X-Powered-By");
    context.Response.Headers.Remove("Server");
    await next();
});

// === Exception Handling ===
if (app.Environment.IsDevelopment()) {
    app.UseDeveloperExceptionPage();
} else {
    app.UseExceptionHandler("/Error");
}

app.UseCors("Strict");
app.UseAuthentication();
app.UseAuthorization();

web.config hardening:

<system.web>
    <httpRuntime enableVersionHeader="false" maxRequestLength="4096" />
    <httpCookies requireSSL="true" httpOnlyCookies="true" />
    <compilation debug="false" />
    <customErrors mode="RemoteOnly" defaultRedirect="~/Error" />
    <authentication>
        <forms requireSSL="true" />
    </authentication>
</system.web>
<system.webServer>
    <security>
        <requestFiltering removeServerHeader="true">
            <requestLimits maxAllowedContentLength="4194304" />
        </requestFiltering>
    </security>
    <httpProtocol>
        <customHeaders>
            <remove name="X-Powered-By" />
            <add name="X-Content-Type-Options" value="nosniff" />
            <add name="X-Frame-Options" value="DENY" />
            <add name="Content-Security-Policy" value="default-src 'self'" />
            <add name="Strict-Transport-Security" value="max-age=63072000; includeSubDomains; preload" />
            <add name="Referrer-Policy" value="strict-origin-when-cross-origin" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

Cryptography — always use:

// AES-256-GCM
var key = new byte[32];
RandomNumberGenerator.Fill(key);
var nonce = new byte[AesGcm.NonceByteSizes.MaxSize];
RandomNumberGenerator.Fill(nonce);
var tag = new byte[AesGcm.TagByteSizes.MaxSize];
using var aes = new AesGcm(key);
aes.Encrypt(nonce, plaintextBytes, ciphertext, tag);

// Password hashing — use PBKDF2 from Microsoft.AspNetCore.Cryptography.KeyDerivation
// Or Argon2 via Konscious.Security.Cryptography

SQL Injection prevention — always parameterized:

var sql = "SELECT * FROM Users WHERE Email = @Email";
context.Database.ExecuteSqlRaw(sql, new SqlParameter("@Email", email));
// Or use Entity Framework LINQ (safe by default)

7.5 PHP Hardening

php.ini production configuration:

; === Error Handling ===
expose_php = Off
error_reporting = E_ALL
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/php_error.log
ignore_repeated_errors = Off
html_errors = Off

; === Filesystem Restrictions ===
open_basedir = /var/www/html/:/tmp/
doc_root = /var/www/html/
include_path = /usr/share/php/
allow_url_fopen = Off
allow_url_include = Off

; === Dangerous Functions (disable all) ===
disable_functions = system,exec,shell_exec,passthru,phpinfo,show_source,highlight_file,popen,proc_open,fopen_with_path,dbmopen,dbase_open,putenv,move_uploaded_file,chdir,mkdir,rmdir,chmod,rename,filepro,filepro_rowcount,filepro_retrieve,posix_mkfifo,posix_getpwuid,posix_kill,posix_setuid,posix_setgid,posix_setsid,posix_setpgid,posix_seteuid,posix_setegid,pcntl_exec

; === Session Security ===
session.save_path = /var/lib/php/sessions
session.name = __Host-PHPSESSID
session.auto_start = Off
session.use_trans_sid = 0
session.use_strict_mode = 1
session.use_cookies = 1
session.use_only_cookies = 1
session.cookie_lifetime = 14400
session.cookie_secure = 1
session.cookie_httponly = 1
session.cookie_samesite = Strict
session.cache_expire = 30
session.sid_length = 256
session.sid_bits_per_character = 6
session.gc_maxlifetime = 600

; === File Uploads ===
file_uploads = On
upload_tmp_dir = /var/lib/php/uploads
upload_max_filesize = 2M
max_file_uploads = 2

; === Resource Limits ===
memory_limit = 128M
post_max_size = 20M
max_execution_time = 30
max_input_time = 60
max_input_vars = 1000

; === Miscellaneous ===
enable_dl = Off
variables_order = "GPCS"
report_memleaks = On
zend.exception_ignore_args = On

7.6 Laravel Hardening

.env production:

APP_ENV=production
APP_DEBUG=false
APP_KEY=base64:...  # Generated via: php artisan key:generate
APP_URL=https://www.example.com
SESSION_DRIVER=database
SESSION_LIFETIME=15
SESSION_SECURE_COOKIE=true
SESSION_HTTP_ONLY=true
SESSION_SAME_SITE=strict

config/session.php:

return [
    'driver' => env('SESSION_DRIVER', 'database'),
    'lifetime' => env('SESSION_LIFETIME', 15),
    'expire_on_close' => false,
    'encrypt' => true,
    'cookie' => env('SESSION_COOKIE', '__Host-laravel_session'),
    'path' => '/',
    'domain' => env('SESSION_DOMAIN', null),
    'secure' => true,
    'http_only' => true,
    'same_site' => 'strict',
];

app/Http/Kernel.php:

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'throttle:30,1',  // Global rate limit
    ],
    'api' => [
        'throttle:60,1',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

Mass assignment — always whitelist:

// Controller — use validated() or only()
$user = User::create($request->validated());
// OR
$user = User::create($request->only(['name', 'email']));

// NEVER use $request->all() or forceFill() with raw input

SQL injection prevention:

// SAFE — Eloquent parameterizes automatically
User::where('email', $request->input('email'))->first();

// SAFE — Raw with bindings
User::whereRaw('email = ?', [$request->input('email')])->first();

// NEVER use raw string interpolation
User::whereRaw("email = '{$request->input('email')}'")->first(); // VULNERABLE

File upload validation:

$request->validate([
    'photo' => 'required|file|max:2048|mimes:jpg,png,gif',
]);
// Store with safe filename
$request->file('photo')->storeAs(auth()->id(), basename($request->input('filename')));

Rate limiting — app/Providers/RouteServiceProvider.php:

RateLimiter::for('login', function ($request) {
    return Limit::perMinute(5)->by($request->input('email') . '|' . $request->ip());
});

RateLimiter::for('api', function ($request) {
    return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});

Command injection prevention:

exec('whois ' . escapeshellarg($request->input('domain')));

File permissions:

# Directories: 755 max (775 if web server needs write)
# Files: 644 max
# Storage/cache: 775
find /var/www/app -type f -exec chmod 644 {} \;
find /var/www/app -type d -exec chmod 755 {} \;
chmod -R 775 /var/www/app/storage /var/www/app/bootstrap/cache

8. Docker Container Hardening

Standards: CIS Docker Benchmark, NIST SP 800-190, ANSSI Docker Recommendations

8.1 Docker Daemon Configuration

/etc/docker/daemon.json:

{
    "icc": false,
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "10m",
        "max-file": "5"
    },
    "live-restore": true,
    "userns-remap": "default",
    "no-new-privileges": true,
    "seccomp-profile": "/etc/docker/seccomp/default.json",
    "storage-driver": "overlay2",
    "tls": true,
    "tlscacert": "/etc/docker/tls/ca.pem",
    "tlscert": "/etc/docker/tls/server-cert.pem",
    "tlskey": "/etc/docker/tls/server-key.pem",
    "tlsverify": true,
    "default-ulimits": {
        "nofile": {
            "Name": "nofile",
            "Hard": 64000,
            "Soft": 64000
        },
        "nproc": {
            "Name": "nproc",
            "Hard": 4096,
            "Soft": 4096
        }
    },
    "userns-remap": "dockremap:dockremap"
}

8.2 Hardened Dockerfile Template

# Use specific digest, not :latest
FROM python:3.12-slim@sha256:<digest>

# Labels
LABEL maintainer="security@example.com"
LABEL org.opencontainers.image.source="https://github.com/org/repo"

# Install security updates
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y --no-install-recommends \
        ca-certificates \
        tini && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Create non-root user
RUN groupadd -r appuser && \
    useradd -r -g appuser -d /app -s /sbin/nologin appuser

# Set working directory
WORKDIR /app

# Copy and install dependencies first (layer caching)
COPY --chown=appuser:appuser requirements.txt .
RUN pip install --no-cache-dir --no-compile -r requirements.txt

# Copy application code
COPY --chown=appuser:appuser . .

# Remove setuid/setgid binaries
RUN find / -perm /6000 -type f -exec chmod a-s {} + 2>/dev/null || true

# Set filesystem to read-only where possible
RUN chmod -R 555 /app

# Switch to non-root
USER appuser

# Use tini as PID 1
ENTRYPOINT ["tini", "--"]

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

# Expose only necessary ports
EXPOSE 8080

CMD ["python", "app.py"]

8.3 Docker Run Security Flags

docker run \
    --name app \
    --read-only \
    --tmpfs /tmp:noexec,nosuid,size=64m \
    --tmpfs /run:noexec,nosuid,size=32m \
    --cap-drop ALL \
    --cap-add NET_BIND_SERVICE \
    --security-opt no-new-privileges:true \
    --security-opt apparmor=docker-default \
    --security-opt seccomp=/etc/docker/seccomp/default.json \
    --pids-limit 100 \
    --memory 512m \
    --memory-swap 512m \
    --cpus 1.0 \
    --ulimit nofile=64000:64000 \
    --ulimit nproc=4096:4096 \
    --network app-net \
    --restart on-failure:5 \
    --health-cmd "curl -f http://localhost:8080/health || exit 1" \
    --health-interval 30s \
    --health-timeout 5s \
    --user 1000:1000 \
    -e "APP_ENV=production" \
    app:latest

8.4 Docker Compose Security

version: '3.8'

services:
  app:
    image: app:latest
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=64m
      - /run:noexec,nosuid,size=32m
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    security_opt:
      - no-new-privileges:true
      - apparmor:docker-default
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
          pids: 100
        reservations:
          cpus: '0.25'
          memory: 128M
    user: "1000:1000"
    networks:
      - app-internal
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"
    environment:
      - APP_ENV=production
    secrets:
      - db_password
    restart: on-failure

networks:
  app-internal:
    driver: bridge
    internal: true

secrets:
  db_password:
    file: ./secrets/db_password.txt

8.5 Image Scanning

# Trivy (recommended)
trivy image --severity HIGH,CRITICAL app:latest

# Grype
grype app:latest

# Docker Scout
docker scout cves app:latest

# Hadolint for Dockerfile linting
hadolint Dockerfile

8.6 Docker Bench Security

git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
./docker-bench-security.sh

9. Kubernetes Cluster Hardening

Standards: CIS Kubernetes Benchmark, NSA/CISA Kubernetes Hardening Guide, Kubernetes Security Checklist

9.1 API Server Hardening

/etc/kubernetes/manifests/kube-apiserver.yaml critical flags:

spec:
  containers:
  - command:
    - kube-apiserver
    # === Authentication ===
    - --anonymous-auth=false
    - --authentication-token-webhook-config-file=/etc/kubernetes/webhook-config.yaml

    # === Authorization ===
    - --authorization-mode=Node,RBAC
    - --enable-admission-plugins=NodeRestriction,PodSecurityAdmission,AlwaysPullImages,EventRateLimit,ResourceQuota,LimitRanger

    # === TLS ===
    - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
    - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
    - --tls-min-version=VersionTLS12
    - --tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    - --client-ca-file=/etc/kubernetes/pki/ca.crt

    # === Audit ===
    - --audit-log-path=/var/log/kubernetes/audit.log
    - --audit-log-maxage=30
    - --audit-log-maxbackup=10
    - --audit-log-maxsize=100
    - --audit-policy-file=/etc/kubernetes/audit-policy.yaml

    # === etcd ===
    - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
    - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
    - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key

    # === Encryption at rest ===
    - --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

    # === Disable insecure options ===
    - --insecure-port=0
    - --profiling=false
    - --enable-bootstrap-token-auth=false

    # === Request limits ===
    - --request-timeout=60s
    - --service-account-lookup=true

9.2 Encryption at Rest

/etc/kubernetes/encryption-config.yaml:

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-32-byte-key>
      - identity: {}

9.3 Audit Policy

/etc/kubernetes/audit-policy.yaml:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  # Don't log read-only endpoints
  - level: None
    nonResourceURLs:
      - /healthz*
      - /version
      - /readyz*
      - /livez*

  # Log secret access at Metadata level
  - level: Metadata
    resources:
      - group: ""
        resources: ["secrets", "configmaps"]

  # Log authentication
  - level: RequestResponse
    resources:
      - group: "authentication.k8s.io"

  # Log RBAC changes
  - level: RequestResponse
    resources:
      - group: "rbac.authorization.k8s.io"

  # Log pod exec/attach
  - level: RequestResponse
    resources:
      - group: ""
        resources: ["pods/exec", "pods/attach", "pods/portforward"]

  # Log all write operations
  - level: Request
    verbs: ["create", "update", "patch", "delete", "deletecollection"]

  # Default — log metadata
  - level: Metadata
    omitStages:
      - RequestReceived

9.4 Pod Security Standards (Restricted)

Namespace enforcement:

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest

9.5 Hardened Pod Template

apiVersion: v1
kind: Pod
metadata:
  name: hardened-app
  namespace: production
spec:
  automountServiceAccountToken: false
  hostNetwork: false
  hostPID: false
  hostIPC: false
  securityContext:
    runAsNonRoot: true
    runAsUser: 10000
    runAsGroup: 10000
    fsGroup: 10000
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: registry.example.com/app:v1.0.0@sha256:<digest>
      imagePullPolicy: Always
      ports:
        - containerPort: 8080
          protocol: TCP
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        runAsNonRoot: true
        runAsUser: 10000
        capabilities:
          drop:
            - ALL
        seccompProfile:
          type: RuntimeDefault
      resources:
        limits:
          cpu: "500m"
          memory: "256Mi"
          ephemeral-storage: "128Mi"
        requests:
          cpu: "100m"
          memory: "128Mi"
      volumeMounts:
        - name: tmp
          mountPath: /tmp
      livenessProbe:
        httpGet:
          path: /health
          port: 8080
        initialDelaySeconds: 10
        periodSeconds: 15
      readinessProbe:
        httpGet:
          path: /ready
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 10
  volumes:
    - name: tmp
      emptyDir:
        medium: Memory
        sizeLimit: 64Mi
  serviceAccountName: app-sa

9.6 Network Policies

Default deny all:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Allow specific traffic:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-app-traffic
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
        - podSelector:
            matchLabels:
              app: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 5432
    - to:  # DNS
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

9.7 RBAC — Least Privilege

# Service account for application
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
  namespace: production
automountServiceAccountToken: false

---
# Role with minimal permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: production
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["app-config"]
    verbs: ["get"]

---
# Bind role to service account
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-rolebinding
  namespace: production
subjects:
  - kind: ServiceAccount
    name: app-sa
    namespace: production
roleRef:
  kind: Role
  name: app-role
  apiGroup: rbac.authorization.k8s.io

Dangerous ClusterRoles to audit:

# Find overprivileged bindings
kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name == "cluster-admin") | .subjects'

# Audit all roles for wildcard permissions
kubectl get roles,clusterroles -A -o json | jq '.items[] | select(.rules[]?.resources[]? == "*" or .rules[]?.verbs[]? == "*") | .metadata.name'

9.8 Resource Quotas

apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "10"
    requests.memory: 20Gi
    limits.cpu: "20"
    limits.memory: 40Gi
    pods: "50"
    services: "10"
    secrets: "20"
    configmaps: "20"
    persistentvolumeclaims: "10"
    services.loadbalancers: "2"
    services.nodeports: "0"

---
apiVersion: v1
kind: LimitRange
metadata:
  name: production-limits
  namespace: production
spec:
  limits:
    - type: Container
      default:
        cpu: "500m"
        memory: "256Mi"
      defaultRequest:
        cpu: "100m"
        memory: "128Mi"
      max:
        cpu: "2"
        memory: "2Gi"
      min:
        cpu: "50m"
        memory: "64Mi"
    - type: Pod
      max:
        cpu: "4"
        memory: "4Gi"

9.9 etcd Hardening

# Verify etcd uses TLS
etcdctl --endpoints=https://127.0.0.1:2379 \
    --cacert=/etc/kubernetes/pki/etcd/ca.crt \
    --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
    --key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
    endpoint health

# File permissions
chmod 600 /etc/kubernetes/pki/etcd/*.key
chmod 644 /etc/kubernetes/pki/etcd/*.crt
chown root:root /etc/kubernetes/pki/etcd/*

# etcd data directory
chmod 700 /var/lib/etcd
chown etcd:etcd /var/lib/etcd

9.10 Kubelet Hardening

/var/lib/kubelet/config.yaml:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
readOnlyPort: 0
protectKernelDefaults: true
makeIPTablesUtilChains: true
eventRecordQPS: 5
tlsCipherSuites:
  - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
tlsMinVersion: VersionTLS12
rotateCertificates: true
serverTLSBootstrap: true
streamingConnectionIdleTimeout: 5m0s

9.11 Image Security

# ImagePolicyWebhook admission controller
# OR use OPA Gatekeeper / Kyverno

# Kyverno — require signed images from trusted registry
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: Enforce
  rules:
    - name: require-trusted-registry
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Images must come from registry.example.com"
        pattern:
          spec:
            containers:
              - image: "registry.example.com/*"
            initContainers:
              - image: "registry.example.com/*"

9.12 Runtime Security Tools

Tool Purpose
Falco Runtime threat detection via syscall monitoring
Trivy Image vulnerability scanning
kube-bench CIS benchmark audit
Kyverno Policy engine (admission control)
OPA Gatekeeper Policy engine (Rego-based)
Cilium eBPF-based network policies + observability
Tetragon eBPF security observability
kubeaudit Cluster security audit

Run kube-bench:

docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro \
    aquasec/kube-bench run --targets=master,node

Appendix A: Security Scanning & Compliance Automation

Tool Platform Purpose
Ansible Lockdown Linux/Windows CIS/STIG automated remediation
OpenSCAP Linux SCAP-based compliance scanning
Lynis Linux Security auditing
kube-bench Kubernetes CIS benchmark
Docker Bench Docker CIS Docker benchmark
Trivy Containers Vulnerability scanning
Sigma SIEM Generic detection rules
osquery Endpoint SQL-based OS telemetry
Wazuh Multi HIDS + compliance monitoring
Falco Kubernetes Runtime threat detection

Appendix B: Detection Engineering Stack

Layer Tool Detection Source
Network Zeek/Suricata Full packet analysis, IDS signatures
Endpoint osquery/Sysmon Process, file, registry, network events
SIEM Elastic/Splunk Log correlation, Sigma rules
Fingerprinting JA3/JA3S, HASSH, JARM TLS/SSH client/server profiling
Threat Intel MITRE ATT&CK, MITRE CAR TTP mapping, analytics
Hunting ThreatHunter-Playbook Hypothesis-driven detection development
ML/Analytics msticpy Jupyter-based investigation

Appendix C: Quick Reference — Hardening Verification Commands

# === Linux ===
lynis audit system                              # Full system audit
aide --check                                    # File integrity check
sysctl -a | grep -E "kptr|dmesg|ptrace"        # Verify kernel params
ss -tulnp                                       # Check listening services
systemd-analyze security                        # Service hardening scores
ausearch -m AVC -ts recent                      # SELinux denials
aa-status                                       # AppArmor profile status
find / -perm -4000 -type f 2>/dev/null         # Find SUID binaries
grep -r "PermitRootLogin" /etc/ssh/            # SSH root login check
awk -F: '($2 == "") {print $1}' /etc/shadow   # Accounts without passwords

# === Docker ===
docker run --rm aquasec/trivy image <image>     # Scan image
./docker-bench-security.sh                      # CIS Docker benchmark

# === Kubernetes ===
kubectl auth can-i --list --as=system:anonymous # Check anonymous access
kubectl get netpol -A                           # List network policies
kubectl get psp -A 2>/dev/null                  # Check pod security policies
kube-bench run                                  # CIS K8s benchmark

# === TLS ===
nmap --script ssl-enum-ciphers -p 443 host      # Enumerate TLS ciphers
testssl.sh https://example.com                   # Full TLS audit
curl -sI https://example.com | grep -i strict    # HSTS check

# === SSH ===
ssh-audit hostname                               # Full SSH audit
nmap --script ssh2-enum-algos -p 22 host         # Enumerate SSH algorithms

Generated by CIPHER — Claude Integrated Privacy & Hardening Expert Resource Sources: CIS Benchmarks, DISA STIGs, Mozilla Server Side TLS, OWASP Cheat Sheet Series, madaidans-insecurities Linux Hardening Guide, ANSSI Configuration Recommendations, NIST SP 800-series, NSA Cybersecurity Advisories, Ansible Lockdown Project

PreviousSynthesis
NextSIEM & SOC

On this page

  • Table of Contents
  • 1. Linux Server Hardening
  • 1.1 Kernel Hardening — sysctl Parameters
  • 1.2 Boot Parameters (GRUB)
  • 1.3 GRUB Bootloader Password
  • 1.4 Kernel Module Blacklisting
  • 1.5 Filesystem Hardening
  • 1.6 Core Dumps Disabled
  • 1.7 User Account Security
  • 1.8 Automatic Security Updates
  • 1.9 Firewall (UFW / firewalld)
  • 1.10 Fail2Ban
  • 1.11 Auditd Rules
  • 1.12 Mandatory Access Control
  • 1.13 File Integrity Monitoring (AIDE)
  • 1.14 Systemd Service Sandboxing Template
  • 1.15 USBGuard (Physical Attack Surface)
  • 1.16 Hardened Memory Allocator
  • 1.17 Intrusion Detection Stack
  • 1.18 Automation with Ansible Lockdown
  • 2. Windows Server Hardening
  • 2.1 Account Policies
  • 2.2 Audit Policies
  • 2.3 User Rights Assignment
  • 2.4 Security Options
  • 2.5 Windows Firewall
  • 2.6 SMB Hardening
  • 2.7 PowerShell Security
  • 2.8 AppLocker / WDAC
  • 2.9 Windows Defender Configuration
  • 2.10 Registry Hardening
  • 2.11 Windows Event Log Sizing
  • 2.12 BitLocker
  • 2.13 Automation
  • 3. SSH Hardening
  • 3.1 Server Configuration — /etc/ssh/sshd_config
  • 3.2 Remove Weak DH Keys
  • 3.3 Regenerate Host Keys (Ed25519 only for max security)
  • 3.4 Client Configuration — ~/.ssh/config
  • 3.5 SSH Key Generation
  • 3.6 SSH Certificate Authority (Enterprise)
  • 4. TLS/SSL Hardening
  • 4.1 Mozilla Modern Configuration (TLS 1.3 Only)
  • 4.2 Mozilla Intermediate Configuration (Recommended)
  • 4.3 NGINX TLS Configuration (Intermediate)
  • 4.4 Apache TLS Configuration (Intermediate)
  • 4.5 Generate DH Parameters
  • 4.6 Certificate Best Practices
  • 5. Web Server Hardening
  • 5.1 NGINX Hardening
  • 5.2 Apache Hardening
  • 5.3 Shared Configuration
  • 6. Database Hardening
  • 6.1 PostgreSQL Hardening
  • 6.2 MySQL/MariaDB Hardening
  • 7. Application Framework Hardening
  • 7.1 Node.js Security
  • 7.2 Ruby on Rails Security
  • 7.3 Django Security
  • 7.4 .NET Security
  • 7.5 PHP Hardening
  • 7.6 Laravel Hardening
  • 8. Docker Container Hardening
  • 8.1 Docker Daemon Configuration
  • 8.2 Hardened Dockerfile Template
  • 8.3 Docker Run Security Flags
  • 8.4 Docker Compose Security
  • 8.5 Image Scanning
  • 8.6 Docker Bench Security
  • 9. Kubernetes Cluster Hardening
  • 9.1 API Server Hardening
  • 9.2 Encryption at Rest
  • 9.3 Audit Policy
  • 9.4 Pod Security Standards (Restricted)
  • 9.5 Hardened Pod Template
  • 9.6 Network Policies
  • 9.7 RBAC — Least Privilege
  • 9.8 Resource Quotas
  • 9.9 etcd Hardening
  • 9.10 Kubelet Hardening
  • 9.11 Image Security
  • 9.12 Runtime Security Tools
  • Appendix A: Security Scanning & Compliance Automation
  • Appendix B: Detection Engineering Stack
  • Appendix C: Quick Reference — Hardening Verification Commands