Distro support
Two-family skill. SSH and kernel/sysctl hardening are largely identical; the
big difference is mandatory access control — Debian/Ubuntu use AppArmor,
the RHEL family (Fedora, RHEL, CentOS Stream, Rocky, Alma, Oracle) enforces
SELinux by default. Full SELinux detail is in
references/selinux-reference.md.
| Concept | Debian/Ubuntu | RHEL family |
|---|---|---|
| Mandatory access control | AppArmor (aa-status, /etc/apparmor.d/) |
SELinux (getenforce, sestatus) — enforcing by default |
| Firewall | ufw |
firewalld |
| Auto security updates | unattended-upgrades |
dnf-automatic |
| Sudo admin group | sudo |
wheel |
| SSH hardening | /etc/ssh/sshd_config[.d] |
identical |
| sysctl hardening | /etc/sysctl.d/ |
identical |
| Audit daemon | auditd |
auditd (same) |
Golden rule on RHEL: never setenforce 0 to "fix" a problem — diagnose the
denial and set the right context/boolean/port. See
references/selinux-reference.md and
docs/multi-distro/plan.md. In sk-* scripts
use the common.sh primitives (pkg_install, firewall_allow, svc_name).
Use when
- Applying security fixes after an audit or known hardening gap.
- Locking down SSH, UFW, fail2ban, sysctl, web stack, database, or file-permission posture.
- Converting audit findings into confirmed, staged changes on a live server.
Do not use when
- You only need a read-only findings report; use
linux-security-analysis. - The task is a narrow, non-security operational change better handled by a specialist skill.
Required Inputs
| Artefact | Source | Required? | If absent |
|---|---|---|---|
| Verified finding and evidence | linux-security-analysis or specialist assessment | yes | Inspect and propose; do not mutate |
| Host role, distro, dependencies, uptime | Inventory and service owner | yes | Stop before changing service behaviour |
| Per-change authority and rollback window | Change owner | for mutation | Return a sequenced hardening plan |
| Recovery access | Console or independent privileged session | for access controls | Do not change SSH, firewall, PAM, or MAC policy |
Capability Contract
Read/search confirmation precedes every change. Root-level edits, installs, service reloads, firewall changes, and enforcement changes require explicit authority. Preserve an independent recovery path and never broaden scope from one approved finding to an entire baseline.
Degraded Mode
Without audit evidence, root, recovery access, a maintenance window, or validation tooling, produce the narrowest qualified plan and exact commands for an operator. Leave the finding open; do not claim hardening is complete.
Decision Rules
| Condition | Action | Failure avoided |
|---|---|---|
| Change can break remote access | Test syntax and preserve a second session/console | Lockout |
| SELinux/AppArmor denial is unexplained | Diagnose labels/profile before policy exception | Disabling mandatory access control |
| Several high-risk changes are requested | Apply and validate one control at a time | Untraceable outage |
| Verification contradicts expected result | Roll back and stop the cohort | Compounding a failed change |
Workflow
- Reproduce the finding, identify distro/role dependencies, and confirm scope.
- Define success, rollback, recovery access, and a syntax/config test; stop if any is missing.
- Back up the affected config and preview the smallest change.
- Apply one authorised control and validate access, syntax, service health, and application behaviour.
- Roll back immediately on failed validation; preserve output and stop related changes.
- Re-run the original audit check and record closure or residual risk.
Evidence Produced
| Artefact | Acceptance |
|---|---|
| Hardening evidence pack | Includes finding, approval, backup/diff, syntax checks, commands, service/access validation, rollback if used, and repeated audit check |
Quality standards
- Every hardening step must be deliberate, reversible, and verified.
- Preserve operator access while tightening controls.
- Close findings with evidence, not intention.
Anti-patterns
- Hardening blind. Fix: reproduce a verified finding or define a precise target.
- Bundling high-risk changes. Fix: validate and checkpoint each control.
- Calling a control fixed before re-checking. Fix: repeat the original audit test.
- Disabling SELinux or AppArmor to clear a denial. Fix: fix labels, context, or narrow policy.
- Editing SSH without recovery access. Fix: preserve a second session or console and run
sshd -t. - Assuming rollback exists. Fix: create and test the config backup and restore command.
Outputs
| Artefact | Consumer | Acceptance condition |
|---|---|---|
| Hardening change record | Change owner | Links finding, approval, exact diff, and rollback |
| Validation evidence | Service owner | Proves access, syntax, service, and original check |
| Residual-risk register | Risk owner | Names deferred or failed controls and next action |
Worked Example
For password SSH still enabled on Ubuntu, first verify key login in a second session, back up sshd_config, change only PasswordAuthentication, run sshd -t, reload SSH, and prove a new key session works before closing the old one. Restore the backup if validation fails.
Book-derived additions
For an NGO or civil-society programme that must join host controls to mission,
identity, staff, incident, and recovery risk, load
../linux-ngo-cyber-resilience/SKILL.md.
References
references/hardening-checklist.mdreferences/sysctl-reference.mdreferences/selinux-reference.md../../01-provisioning-and-bootstrap/linux-package-management/SKILL.md— package hygiene, Flatpak/snap, and workstation AppImage runtime support.
This skill is self-contained. Every hardening step below uses standard
tools on both Debian/Ubuntu and the RHEL family (Fedora, RHEL, CentOS Stream,
Rocky, Alma, Oracle); see the Distro support table above for the
family-specific equivalents. The sk-* scripts in the Optional fast path
section are convenience wrappers — never required.
Applies security fixes interactively. Runs the audit first — never applies a change without your confirmation.
For a full picture first: run linux-security-analysis before hardening.
Desktop/workstation exception: AppImage runtime packages such as fuse3,
desktop-file-utils, and app-specific media libraries (mpv-libs on Fedora)
belong to workstation package setup, not production server hardening. Keep them
off headless servers unless a real desktop app requirement exists; use
linux-package-management when such a requirement is present.
Step 1: Run The Audit
sudo bash ~/.claude/skills/scripts/server-audit.sh
Fix FAIL items first, then WARN. Use references/hardening-checklist.md
for the complete commands for each area.
Hardening Areas (In Priority Order)
1. SSH
- Disable password auth, disable root login, set MaxAuthTries 3
- WARNING: Keep existing SSH session open. Test login in a second terminal before closing the first session.
- Config:
/etc/ssh/sshd_config.d/99-hardening.conf - No SSH-hardening wrapper is shipped. Use the reviewed drop-in procedure in
references/hardening-checklist.md; validate withsshd -tand keep the existing SSH session open until a second login succeeds.
2. Firewall (UFW)
- Default deny incoming, allow 22/80/443 only
sudo ufw status verboseto check current state- No UFW profile wrapper is shipped. Route firewall changes through
linux-firewall-ssland its native UFW/firewalld procedures.
3. Kernel (sysctl)
- Network stack hardening + ASLR + kernel pointer restriction
- Config:
/etc/sysctl.d/99-linux-skills.conf - No sysctl-hardening wrapper is shipped. Use the reviewed file and apply
procedure in
references/hardening-checklist.md.
4. Nginx
server_tokens offin nginx.conf- Security headers on all vhosts (or global security.conf)
- Dotfile blocking snippet included in all vhosts
5. PHP-FPM
expose_php = Off,display_errors = Off,allow_url_include = Off- Session cookie security settings
disable_functionsfor dangerous functions- Config:
/etc/php/8.3/fpm/php.ini - No PHP-hardening wrapper is shipped. Use the installed PHP-FPM paths and
validator documented in
references/hardening-checklist.md.
6. MySQL
bind-address = 127.0.0.1(never expose to network)- Run
mysql_secure_installation - Application users: least-privilege only
7. Redis
- Bound to 127.0.0.1, password set, dangerous commands renamed
8. File Permissions
/etc/shadow→ 640, credential files → 600- No world-writable files in
/var/www - SSH keys → 600
9. Automatic Updates
unattended-upgradesinstalled and enabled
Verify After Hardening
sudo bash ~/.claude/skills/scripts/server-audit.sh
# All previous FAIL items should now be PASS
Full configs and commands for each area: references/hardening-checklist.md
Reference guide: ~/.claude/skills/notes/server-security.md
Optional fast path (when sk-* scripts are installed)
Running sudo install-skills-bin linux-server-hardening installs:
| Task | Fast-path script |
|---|---|
| Triage SELinux denials, build reviewed policy module (RHEL family) | sudo sk-selinux-denials |
These wrap the manual steps in references/hardening-checklist.md.
Scripts
This skill installs the following scripts to /usr/local/bin/. To install:
sudo install-skills-bin linux-server-hardening
| Script | Source | Core? | Purpose |
|---|---|---|---|
| sk-selinux-denials | scripts/sk-selinux-denials.sh | no | RHEL family only. Summarize recent AVC denials (ausearch/aureport --avc), explain with audit2why, then — only after you confirm — build a reviewed local policy module (audit2allow -M + semodule -i). Never disables SELinux. See references/selinux-reference.md. |