netsapiensis

rocky-security-hardening

0
0
# Install this skill:
npx skills add netsapiensis/claude-code-skills --skill "rocky-security-hardening"

Install specific skill from multi-skill repository

# Description

Rocky Linux 8/9 security hardening including CIS benchmarks with OpenSCAP, SSH hardening, fail2ban, auditd rules, PAM configuration with authselect, and system-wide crypto policies. Use when hardening servers, configuring SSH security, setting up intrusion prevention, or compliance scanning.

# SKILL.md


name: rocky-security-hardening
description: Rocky Linux 8/9 security hardening including CIS benchmarks with OpenSCAP, SSH hardening, fail2ban, auditd rules, PAM configuration with authselect, and system-wide crypto policies. Use when hardening servers, configuring SSH security, setting up intrusion prevention, or compliance scanning.


Rocky Linux Security Hardening

CIS benchmarks, SSH hardening, fail2ban, auditd, PAM/authselect, and crypto policies for Rocky Linux 8/9.

Prerequisite: See rocky-foundation for OS detection and safety tier definitions.

CIS Benchmarks and OpenSCAP

Install OpenSCAP

# Install scanner and security guide  # [CONFIRM]
dnf install -y openscap-scanner scap-security-guide

# List available profiles  # [READ-ONLY]
oscap info /usr/share/xml/scap/ssg/content/ssg-rl8-ds.xml   # Rocky 8
oscap info /usr/share/xml/scap/ssg/content/ssg-rl9-ds.xml   # Rocky 9

Common Profiles

Profile ID Description
xccdf_org.ssgproject.content_profile_cis CIS Level 1 Server
xccdf_org.ssgproject.content_profile_cis_server_l1 CIS Level 1 Server
xccdf_org.ssgproject.content_profile_cis_workstation_l1 CIS Level 1 Workstation
xccdf_org.ssgproject.content_profile_stig DISA STIG
xccdf_org.ssgproject.content_profile_pci-dss PCI-DSS

Run Scans

# Determine correct datastream file  # [READ-ONLY]
ROCKY_VERSION=$(source /etc/os-release && echo "${VERSION_ID%%.*}")
DS_FILE="/usr/share/xml/scap/ssg/content/ssg-rl${ROCKY_VERSION}-ds.xml"

# Run CIS scan and generate HTML report  # [READ-ONLY]
oscap xccdf eval \
  --profile xccdf_org.ssgproject.content_profile_cis \
  --report /tmp/cis-report.html \
  --results /tmp/cis-results.xml \
  "$DS_FILE"

# Generate remediation script (review before running!)  # [READ-ONLY]
oscap xccdf generate fix \
  --profile xccdf_org.ssgproject.content_profile_cis \
  --fix-type bash \
  --output /tmp/cis-remediation.sh \
  --result-id "" \
  /tmp/cis-results.xml

# Review remediation script before executing  # [READ-ONLY]
less /tmp/cis-remediation.sh

WARNING: Never run auto-generated remediation scripts without reviewing them. They can break services.

SSH Hardening

Version Differences: SSH Config

Feature Rocky 8 Rocky 9
Main config /etc/ssh/sshd_config /etc/ssh/sshd_config
Drop-in configs Not default /etc/ssh/sshd_config.d/*.conf
Default crypto Wider compatibility Stricter (crypto policies)
Include directive Manual Pre-configured

SSH Configuration

CORRECT -- use drop-in configs on Rocky 9, main config on Rocky 8:

ROCKY_VERSION=$(source /etc/os-release && echo "${VERSION_ID%%.*}")

if [[ "$ROCKY_VERSION" == "9" ]]; then
    # Rocky 9: Use drop-in config  # [CONFIRM]
    cat > /etc/ssh/sshd_config.d/50-hardening.conf << 'EOF'
# Key-only authentication
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey

# Disable root login
PermitRootLogin no

# Protocol hardening
X11Forwarding no
MaxAuthTries 3
MaxSessions 5
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30

# Restrict users (uncomment and customize)
# AllowUsers deploy admin
# AllowGroups sshusers

# Logging
LogLevel VERBOSE
EOF
else
    # Rocky 8: Edit main config (back up first!)  # [CONFIRM]
    cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%Y%m%d%H%M%S)
    # Then edit /etc/ssh/sshd_config with the same directives
fi

ALWAYS validate before restarting SSH:

# Validate SSH config  # [READ-ONLY]
sshd -t

# If validation passes, restart  # [CONFIRM]
systemctl restart sshd

WRONG -- restarting SSH without validation:

# WRONG: If config has errors, you lose SSH access
systemctl restart sshd

# CORRECT: Always validate first
sshd -t && systemctl restart sshd

SSH Key Management

# Generate key pair (on client)  # [READ-ONLY]
ssh-keygen -t ed25519 -C "user@hostname"

# Copy public key to server  # [CONFIRM]
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

# Verify key permissions on server  # [READ-ONLY]
ls -la ~/.ssh/
# Should be: ~/.ssh (700), ~/.ssh/authorized_keys (600)

# Fix permissions  # [CONFIRM]
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Non-Standard SSH Port

# 1. Update SELinux  # [CONFIRM]
semanage port -a -t ssh_port_t -p tcp 2222

# 2. Update firewall  # [CONFIRM]
firewall-cmd --add-port=2222/tcp --permanent
firewall-cmd --reload

# 3. Update SSH config  # [CONFIRM]
# Rocky 9: /etc/ssh/sshd_config.d/50-port.conf
# Port 2222
# Rocky 8: /etc/ssh/sshd_config
# Port 2222

# 4. Validate and restart  # [CONFIRM]
sshd -t && systemctl restart sshd

# 5. Test BEFORE closing current session
# Open new terminal: ssh -p 2222 user@server

fail2ban

Installation and Setup

# Install from EPEL  # [CONFIRM]
dnf install -y epel-release
dnf install -y fail2ban fail2ban-firewalld

# Enable service  # [CONFIRM]
systemctl enable --now fail2ban

Configuration

CORRECT -- use local overrides, never edit main config:

# WRONG: Editing /etc/fail2ban/jail.conf (overwritten on update)
# CORRECT: Create /etc/fail2ban/jail.local  # [CONFIRM]

cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
banaction = firewallcmd-rich-rules[actiontype=<multiport>]
banaction_allports = firewallcmd-rich-rules[actiontype=<allports>]

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/secure
maxretry = 3
bantime = 86400

[sshd-ddos]
enabled = true
port = ssh
filter = sshd-ddos
logpath = /var/log/secure
maxretry = 5
bantime = 3600
EOF

fail2ban Operations

# Status  # [READ-ONLY]
fail2ban-client status
fail2ban-client status sshd

# Banned IPs  # [READ-ONLY]
fail2ban-client get sshd banned

# Manually ban/unban  # [CONFIRM]
fail2ban-client set sshd banip 192.168.1.100
fail2ban-client set sshd unbanip 192.168.1.100

# Reload config  # [CONFIRM]
fail2ban-client reload

# Test regex against log  # [READ-ONLY]
fail2ban-regex /var/log/secure /etc/fail2ban/filter.d/sshd.conf

Custom Jail Example (Nginx)

# /etc/fail2ban/jail.d/nginx.conf  # [CONFIRM]
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600

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

Audit Framework (auditd)

Basic Configuration

# Check audit status  # [READ-ONLY]
auditctl -s
auditctl -l                              # List current rules

# View audit logs  # [READ-ONLY]
ausearch -m USER_AUTH --start today
ausearch -m AVC --start today
aureport --summary
aureport --login
aureport --failed

Audit Rules

# Rule file: /etc/audit/rules.d/99-custom.rules  # [CONFIRM]

# Watch sensitive files for changes
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
-w /etc/ssh/sshd_config -p wa -k sshd_config

# Monitor user commands
-a always,exit -F arch=b64 -S execve -F uid=0 -k root_commands

# Monitor file deletions
-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F uid>=1000 -k file_deletion

# Monitor mount operations
-a always,exit -F arch=b64 -S mount -k mounts

# Monitor kernel module loading
-a always,exit -F arch=b64 -S init_module,finit_module -k module_load
-a always,exit -F arch=b64 -S delete_module -k module_unload

# Monitor network configuration changes
-w /etc/sysconfig/network -p wa -k network_config
-w /etc/NetworkManager/ -p wa -k network_config

# Make rules immutable (requires reboot to change)
# WARNING: Only add this when rules are finalized
# -e 2
# Load rules  # [CONFIRM]
augenrules --load

# Verify rules loaded  # [READ-ONLY]
auditctl -l

# Search audit events  # [READ-ONLY]
ausearch -k identity --start today
ausearch -k sudoers --start today
ausearch -k root_commands -ts recent

PAM Configuration

authselect (Rocky 8/9)

CRITICAL: On Rocky 8/9, NEVER edit PAM files directly. Use authselect.

# View current profile  # [READ-ONLY]
authselect current
authselect list-features

# List available profiles  # [READ-ONLY]
authselect list

# Select profile with features  # [CONFIRM]
authselect select sssd with-faillock with-mkhomedir --force

# Enable/disable features  # [CONFIRM]
authselect enable-feature with-faillock
authselect disable-feature with-mkhomedir

# Available features  # [READ-ONLY]
authselect list-features sssd

WRONG -- direct PAM editing:

# WRONG: Editing PAM files directly (authselect will overwrite)
vim /etc/pam.d/system-auth
vim /etc/pam.d/password-auth

# CORRECT: Use authselect
authselect select sssd with-faillock --force

Account Lockout (pam_faillock)

# Enable faillock via authselect  # [CONFIRM]
authselect enable-feature with-faillock

# Configure faillock  # [CONFIRM]
# /etc/security/faillock.conf
cat > /etc/security/faillock.conf << 'EOF'
deny = 5
unlock_time = 900
fail_interval = 900
audit
even_deny_root
root_unlock_time = 60
EOF

# Check locked accounts  # [READ-ONLY]
faillock --user username

# Unlock a locked account  # [CONFIRM]
faillock --user username --reset

Password Quality (pam_pwquality)

# Configure password requirements  # [CONFIRM]
# /etc/security/pwquality.conf
cat > /etc/security/pwquality.conf << 'EOF'
minlen = 14
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1
minclass = 3
maxrepeat = 3
maxclassrepeat = 4
retry = 3
enforce_for_root
EOF

Password Aging

# Set defaults for new users  # [CONFIRM]
# /etc/login.defs
# PASS_MAX_DAYS 90
# PASS_MIN_DAYS 7
# PASS_WARN_AGE 14
# PASS_MIN_LEN 14

# Set for existing user  # [CONFIRM]
chage -M 90 -m 7 -W 14 username

# Check password aging  # [READ-ONLY]
chage -l username

Cryptographic Policies

System-wide crypto policies control TLS, SSH, IPSec, and other crypto defaults.

# Check current policy  # [READ-ONLY]
update-crypto-policies --show

# Available policies  # [READ-ONLY]
update-crypto-policies --show
# DEFAULT  -- reasonable defaults
# FUTURE   -- conservative, may break older clients
# FIPS     -- FIPS 140-2 compliance
# LEGACY   -- allows old protocols (NOT recommended)
# NEXT     -- Rocky 9 only, between DEFAULT and FUTURE

Set Crypto Policy

# Set policy  # [CONFIRM]
update-crypto-policies --set FUTURE

# Rocky 9 only: policy submodules  # [CONFIRM]
update-crypto-policies --set DEFAULT:NO-SHA1

# Verify  # [READ-ONLY]
update-crypto-policies --show

# View what the policy affects  # [READ-ONLY]
update-crypto-policies --check

Version Differences: Crypto Policies

Feature Rocky 8 Rocky 9
Policies DEFAULT, LEGACY, FUTURE, FIPS Same + NEXT
Submodules Not supported Supported (e.g., :NO-SHA1)
Default OpenSSL 1.1.1 3.0 (different defaults)
SHA-1 Allowed in DEFAULT Restricted in some contexts

Additional Hardening

Kernel Hardening (sysctl)

# /etc/sysctl.d/99-security.conf  # [CONFIRM]
cat > /etc/sysctl.d/99-security.conf << 'EOF'
# Network hardening
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_syncookies = 1

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

# Kernel hardening
kernel.randomize_va_space = 2
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
fs.suid_dumpable = 0
EOF

# Apply  # [CONFIRM]
sysctl -p /etc/sysctl.d/99-security.conf

Disable Unnecessary Services

# Find and disable unnecessary services  # [READ-ONLY]
systemctl list-units --type=service --state=running

# Common services to disable if not needed  # [CONFIRM]
systemctl disable --now postfix        # If no local mail
systemctl disable --now rpcbind        # If no NFS/NIS
systemctl disable --now avahi-daemon   # If no mDNS
systemctl mask ctrl-alt-del.target     # Prevent accidental reboot

File Permission Hardening

# Check world-writable files  # [READ-ONLY]
find / -xdev -type f -perm -0002 -ls 2>/dev/null

# Check unowned files  # [READ-ONLY]
find / -xdev -nouser -o -nogroup 2>/dev/null

# Check SUID/SGID files  # [READ-ONLY]
find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/null

# Secure boot loader  # [CONFIRM]
chmod 600 /boot/grub2/grub.cfg

USB Storage Disable

# Disable USB storage  # [CONFIRM]
echo "install usb-storage /bin/true" > /etc/modprobe.d/disable-usb-storage.conf
echo "blacklist usb-storage" >> /etc/modprobe.d/disable-usb-storage.conf

Checklist: Server Hardening

  • [ ] Set crypto policy (update-crypto-policies --set FUTURE)
  • [ ] SSH: Key-only auth, no root login, validate config
  • [ ] Configure fail2ban for SSH (and web services if applicable)
  • [ ] Set password policies (pwquality, faillock, aging)
  • [ ] Configure auditd rules for critical files
  • [ ] Apply kernel hardening sysctl parameters
  • [ ] Disable unnecessary services
  • [ ] Check file permissions (world-writable, SUID/SGID)
  • [ ] Run OpenSCAP CIS scan and review results
  • [ ] SELinux in Enforcing mode (getenforce)
  • [ ] Firewall configured with minimal open ports

When to Use This Skill

  • Initial server hardening after OS installation
  • Preparing for compliance audits (CIS, STIG, PCI-DSS)
  • Configuring SSH security
  • Setting up intrusion prevention (fail2ban)
  • Configuring audit logging
  • Managing PAM authentication settings
  • Setting system-wide cryptographic policies
  • rocky-foundation -- OS detection, safety tiers
  • rocky-selinux -- SELinux is part of security posture
  • rocky-core-system -- Service management, user admin
  • rocky-networking -- Firewall hardening
  • rocky-webstack -- Web server security, TLS

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.