Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-SkillsFramework mappings
MITRE ATT&CK
When to Use
Use this skill when:
- Hardening Linux servers (Ubuntu, RHEL, CentOS, Debian) against CIS benchmarks
- Automating Linux security baselines using Ansible, OpenSCAP, or shell scripts
- Meeting compliance requirements (PCI DSS, HIPAA, SOC 2) for Linux endpoints
- Remediating findings from vulnerability scans or security audits
Do not use for Windows hardening (use hardening-windows-endpoint-with-cis-benchmark).
Prerequisites
- Root or sudo access on target Linux endpoints
- CIS Benchmark PDF for target distribution (from cisecurity.org)
- OpenSCAP or CIS-CAT for automated assessment
- Ansible for enterprise-scale remediation (optional)
Workflow
Step 1: Filesystem Configuration (Section 1)
# 1.1.1 Disable unused filesystems
cat >> /etc/modprobe.d/CIS.conf << 'EOF'
install cramfs /bin/true
install freevxfs /bin/true
install jffs2 /bin/true
install hfs /bin/true
install hfsplus /bin/true
install squashfs /bin/true
install udf /bin/true
EOF
# 1.1.2 Ensure /tmp is a separate partition with nodev,nosuid,noexec
# /etc/fstab entry:
# tmpfs /tmp tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0
systemctl unmask tmp.mount
systemctl enable tmp.mount
# 1.1.8 Ensure nodev option on /dev/shm
mount -o remount,nodev,nosuid,noexec /dev/shm
echo "tmpfs /dev/shm tmpfs defaults,nodev,nosuid,noexec 0 0" >> /etc/fstab
# 1.4 Secure boot settings
chown root:root /boot/grub/grub.cfg
chmod 600 /boot/grub/grub.cfg
# Set GRUB password
grub-mkpasswd-pbkdf2 # Generate hash, add to /etc/grub.d/40_customStep 2: Services and Network (Sections 2-3)
# 2.1 Disable unnecessary services
systemctl disable --now avahi-daemon
systemctl disable --now cups
systemctl disable --now rpcbind
systemctl disable --now xinetd
# 2.2 Ensure NTP is configured
apt install chrony -y # or systemd-timesyncd
systemctl enable --now chrony
# 3.1 Network parameters (host only, not router)
cat >> /etc/sysctl.d/99-cis.conf << 'EOF'
net.ipv4.ip_forward = 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.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.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
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
EOF
sysctl --system
# 3.4 Configure firewall (UFW or firewalld)
ufw enable
ufw default deny incoming
ufw default allow outgoing
ufw allow sshStep 3: Access Control (Sections 4-5)
# 5.2 SSH Server Configuration (/etc/ssh/sshd_config)
sed -i 's/#Protocol 2/Protocol 2/' /etc/ssh/sshd_config
cat >> /etc/ssh/sshd_config << 'EOF'
LogLevel VERBOSE
MaxAuthTries 4
PermitRootLogin no
PermitEmptyPasswords no
PasswordAuthentication no
X11Forwarding no
MaxStartups 10:30:60
LoginGraceTime 60
AllowTcpForwarding no
ClientAliveInterval 300
ClientAliveCountMax 3
EOF
systemctl restart sshd
# 5.3 Password policy (PAM)
# /etc/security/pwquality.conf
minlen = 14
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
# 5.4 User account settings
# /etc/login.defs
PASS_MAX_DAYS 365
PASS_MIN_DAYS 1
PASS_WARN_AGE 7
# Lock inactive accounts
useradd -D -f 30Step 4: Audit and Logging (Section 4)
# Install and configure auditd
apt install auditd audispd-plugins -y
systemctl enable --now auditd
# /etc/audit/rules.d/cis.rules
cat > /etc/audit/rules.d/cis.rules << 'EOF'
-w /etc/sudoers -p wa -k scope
-w /etc/sudoers.d/ -p wa -k scope
-w /var/log/sudo.log -p wa -k actions
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change
-a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale
-w /etc/group -p wa -k identity
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -k perm_mod
-a always,exit -F arch=b64 -S unlink -S rmdir -S rename -k delete
-w /sbin/insmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-e 2
EOF
augenrules --load
# Configure rsyslog for remote logging
echo "*.* @@syslog-server.corp.com:514" >> /etc/rsyslog.d/50-remote.conf
systemctl restart rsyslogStep 5: Assess with OpenSCAP
# Install OpenSCAP
apt install openscap-scanner scap-security-guide -y
# Run CIS benchmark assessment
oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis_level1_server \
--results /tmp/cis_results.xml \
--report /tmp/cis_report.html \
/usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml
# View HTML report in browser for detailed resultsKey Concepts
| Term | Definition |
|---|---|
| OpenSCAP | Open-source SCAP (Security Content Automation Protocol) scanner for automated compliance |
| auditd | Linux audit framework for monitoring system calls and file access |
| PAM | Pluggable Authentication Modules; configurable authentication framework for Linux |
| sysctl | Linux kernel parameter configuration for network and system security tuning |
| AIDE | Advanced Intrusion Detection Environment; file integrity checker for Linux |
Tools & Systems
- OpenSCAP: Automated CIS benchmark assessment for Linux
- Ansible Lockdown: Ansible roles for automated CIS benchmark remediation
- Lynis: Open-source security auditing tool for Linux/Unix systems
- AIDE: File integrity monitoring for Linux endpoints
- auditd: Linux audit framework for system call monitoring
Common Pitfalls
- Applying server benchmarks to workstations: CIS provides separate benchmarks for server and workstation profiles. Server benchmarks disable desktop services.
- Breaking SSH access: Misconfiguring sshd_config (especially PermitRootLogin, PasswordAuthentication) can lock out administrators. Always test SSH configuration changes from a second session.
- Not testing firewall rules: Enabling UFW without allowing SSH first will disconnect remote sessions permanently.
- Kernel parameter changes without testing: Some sysctl settings can break application networking. Test in staging first.
Source materials
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md2.2 KB
API Reference: Linux CIS Benchmark Hardening
CIS Benchmark Sections
| Section | Topic |
|---|---|
| 1 | Initial Setup (filesystem, updates, secure boot) |
| 2 | Services (inetd, special purpose) |
| 3 | Network Configuration (parameters, firewall) |
| 4 | Logging and Auditing (auditd, rsyslog) |
| 5 | Access, Authentication, Authorization (SSH, PAM) |
| 6 | System Maintenance (file permissions) |
Key sysctl Parameters
Network Hardening
sysctl -w net.ipv4.ip_forward=0
sysctl -w net.ipv4.conf.all.send_redirects=0
sysctl -w net.ipv4.conf.all.accept_source_route=0
sysctl -w net.ipv4.conf.all.accept_redirects=0
sysctl -w net.ipv4.conf.all.log_martians=1
sysctl -w net.ipv4.tcp_syncookies=1Persistent Configuration
# /etc/sysctl.d/99-hardening.conf
net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0SSH Hardening (/etc/ssh/sshd_config)
| Parameter | Recommended Value |
|---|---|
| PermitRootLogin | no |
| PasswordAuthentication | no |
| Protocol | 2 |
| MaxAuthTries | 4 |
| ClientAliveInterval | 300 |
| ClientAliveCountMax | 3 |
| X11Forwarding | no |
| AllowTcpForwarding | no |
Service Management
Disable unnecessary services
systemctl disable avahi-daemon
systemctl disable cups
systemctl disable rpcbind
systemctl mask service_nameCheck enabled services
systemctl list-unit-files --type=service --state=enabledAudit Rules (/etc/audit/rules.d/)
Monitor critical files
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/sudoers -p wa -k sudoersMonitor system calls
-a always,exit -F arch=b64 -S execve -k exec
-a always,exit -F arch=b64 -S mount -k mountsFile Permissions
| File | Owner | Permissions |
|---|---|---|
/etc/passwd |
root:root | 644 |
/etc/shadow |
root:shadow | 000 or 640 |
/etc/group |
root:root | 644 |
/etc/gshadow |
root:shadow | 000 or 640 |
Automated Tools
OpenSCAP
oscap xccdf eval --profile cis \
--results results.xml \
/usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xmlLynis
lynis audit system --cronjob --quietstandards.md1.0 KB
Standards & References
Primary Standards
- CIS Ubuntu Linux 22.04 LTS Benchmark v2.0.0: https://www.cisecurity.org/benchmark/ubuntu_linux
- CIS Red Hat Enterprise Linux 9 Benchmark v2.0.0: https://www.cisecurity.org/benchmark/red_hat_linux
- NIST SP 800-123: Guide to General Server Security
- DISA STIG: Security Technical Implementation Guide for RHEL/Ubuntu
Compliance Mappings
| Framework | Requirement | Linux Hardening Coverage |
|---|---|---|
| PCI DSS 4.0 | 2.2 - Configuration standards | CIS benchmark application |
| NIST 800-53 | CM-6 Configuration Settings | Kernel, service, and auth hardening |
| NIST 800-53 | AU-2 Audit Events | auditd configuration |
| HIPAA | 164.312(a)(1) Access Control | SSH hardening, PAM configuration |
Supporting References
- Ansible Lockdown: https://github.com/ansible-lockdown
- OpenSCAP: https://www.open-scap.org/
- Lynis: https://cisofy.com/lynis/
- SCAP Security Guide: https://github.com/ComplianceAsCode/content
workflows.md0.7 KB
Workflows
Workflow 1: Linux CIS Hardening Deployment
[Select CIS Benchmark for distro/version] → [Choose L1 or L2 profile]
→ [Run OpenSCAP baseline assessment] → [Review initial compliance score]
→ [Apply remediations (Ansible/manual)] → [Re-assess with OpenSCAP]
→ [Document exceptions] → [Deploy to production fleet]
→ [Schedule quarterly reassessment]Workflow 2: Automated Remediation with Ansible
[Clone Ansible Lockdown role for target distro]
→ [Configure variables (skip list, exceptions)]
→ [Test against staging servers]
→ [Review changes and application compatibility]
→ [Deploy to production in rolling batches]
→ [Run OpenSCAP validation after each batch]Scripts 2
agent.py6.2 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for auditing Linux endpoints against CIS Benchmark hardening controls."""
import argparse
import json
import os
import re
import shlex
import subprocess
from datetime import datetime, timezone
def run_cmd(cmd, timeout=10):
"""Run a command and return stdout."""
try:
# Strip shell stderr redirects since we use subprocess.DEVNULL
clean_cmd = cmd.replace("2>/dev/null", "").strip()
# Use shell only for commands with shell operators
needs_shell = any(op in clean_cmd for op in ("|", ";", "&&", "||"))
return subprocess.check_output(
clean_cmd if needs_shell else shlex.split(clean_cmd),
shell=needs_shell, text=True, errors="replace",
timeout=timeout, stderr=subprocess.DEVNULL
).strip()
except subprocess.SubprocessError:
return ""
def check_filesystem_config():
"""CIS Section 1 — Filesystem Configuration."""
findings = []
cramfs = run_cmd("modprobe -n -v cramfs 2>/dev/null")
if "install /bin/true" not in cramfs and "install /bin/false" not in cramfs:
findings.append({"check": "1.1.1.1", "issue": "cramfs module not disabled", "severity": "LOW"})
tmp_mount = run_cmd("findmnt /tmp")
if not tmp_mount:
findings.append({"check": "1.1.2", "issue": "/tmp not a separate partition", "severity": "MEDIUM"})
elif "nodev" not in tmp_mount or "nosuid" not in tmp_mount:
findings.append({"check": "1.1.3", "issue": "/tmp missing nodev/nosuid options", "severity": "MEDIUM"})
return findings
def check_services():
"""CIS Section 2 — Services."""
findings = []
unnecessary = ["avahi-daemon", "cups", "dhcpd", "slapd", "nfs-server",
"rpcbind", "named", "vsftpd", "httpd", "dovecot", "smb", "squid"]
for svc in unnecessary:
status = run_cmd(f"systemctl is-enabled {svc} 2>/dev/null")
if status == "enabled":
findings.append({"check": "2.x", "issue": f"Unnecessary service enabled: {svc}", "severity": "MEDIUM"})
return findings
def check_network_parameters():
"""CIS Section 3 — Network Parameters."""
findings = []
params = {
"net.ipv4.ip_forward": ("0", "3.1.1", "IP forwarding enabled"),
"net.ipv4.conf.all.send_redirects": ("0", "3.1.2", "ICMP redirects enabled"),
"net.ipv4.conf.all.accept_source_route": ("0", "3.2.1", "Source routing accepted"),
"net.ipv4.conf.all.accept_redirects": ("0", "3.2.2", "ICMP redirects accepted"),
"net.ipv4.conf.all.log_martians": ("1", "3.2.4", "Martian logging disabled"),
"net.ipv4.tcp_syncookies": ("1", "3.2.8", "SYN cookies disabled"),
}
for param, (expected, cis_id, desc) in params.items():
value = run_cmd(f"sysctl -n {param} 2>/dev/null")
if value != expected:
findings.append({"check": cis_id, "issue": desc, "current": value, "expected": expected, "severity": "MEDIUM"})
return findings
def check_access_auth():
"""CIS Section 5 — Access, Authentication, Authorization."""
findings = []
sshd_config = run_cmd("cat /etc/ssh/sshd_config 2>/dev/null")
if "PermitRootLogin yes" in sshd_config:
findings.append({"check": "5.2.10", "issue": "SSH root login permitted", "severity": "HIGH"})
if "PasswordAuthentication yes" in sshd_config:
findings.append({"check": "5.2.12", "issue": "SSH password authentication enabled", "severity": "MEDIUM"})
if "Protocol 1" in sshd_config:
findings.append({"check": "5.2.4", "issue": "SSH Protocol 1 enabled", "severity": "HIGH"})
passwd_maxdays = run_cmd("grep PASS_MAX_DAYS /etc/login.defs 2>/dev/null")
if passwd_maxdays:
match = re.search(r'PASS_MAX_DAYS\s+(\d+)', passwd_maxdays)
if match and int(match.group(1)) > 365:
findings.append({"check": "5.4.1.1", "issue": f"Password max age: {match.group(1)} days", "severity": "MEDIUM"})
return findings
def check_audit_logging():
"""CIS Section 4 — Logging and Auditing."""
findings = []
auditd = run_cmd("systemctl is-active auditd 2>/dev/null")
if auditd != "active":
findings.append({"check": "4.1.1", "issue": "auditd not active", "severity": "HIGH"})
rsyslog = run_cmd("systemctl is-active rsyslog 2>/dev/null")
if rsyslog != "active":
findings.append({"check": "4.2.1", "issue": "rsyslog not active", "severity": "MEDIUM"})
return findings
def check_file_permissions():
"""CIS Section 6 — System File Permissions."""
findings = []
critical_files = {
"/etc/passwd": "644",
"/etc/shadow": "000",
"/etc/group": "644",
"/etc/gshadow": "000",
}
for fpath, expected in critical_files.items():
if os.path.isfile(fpath):
mode = oct(os.stat(fpath).st_mode)[-3:]
if mode > expected:
findings.append({"check": "6.1.x", "issue": f"{fpath}: mode {mode} > {expected}", "severity": "MEDIUM"})
return findings
def main():
parser = argparse.ArgumentParser(
description="Audit Linux endpoint against CIS Benchmark"
)
parser.add_argument("--output", "-o", help="Output JSON report")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()
print("[*] Linux CIS Benchmark Hardening Audit Agent")
all_findings = []
all_findings.extend(check_filesystem_config())
all_findings.extend(check_services())
all_findings.extend(check_network_parameters())
all_findings.extend(check_access_auth())
all_findings.extend(check_audit_logging())
all_findings.extend(check_file_permissions())
high = sum(1 for f in all_findings if f["severity"] == "HIGH")
report = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"findings": all_findings,
"total": len(all_findings),
"high_severity": high,
"compliance_score": max(0, 100 - len(all_findings) * 5),
}
print(f"[*] Findings: {len(all_findings)} (HIGH: {high})")
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"[*] Report saved to {args.output}")
else:
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()
process.py2.5 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Linux CIS Benchmark Compliance Processor - Parses OpenSCAP XCCDF results."""
import xml.etree.ElementTree as ET
import json
import csv
import sys
import os
from datetime import datetime
def parse_oscap_results(xml_path: str) -> dict:
"""Parse OpenSCAP XCCDF results XML."""
tree = ET.parse(xml_path)
root = tree.getroot()
ns = {"xccdf": "http://checklists.nist.gov/xccdf/1.2"}
results = {"total": 0, "passed": 0, "failed": 0, "notapplicable": 0,
"findings": [], "score": 0.0}
for rr in root.iter():
if rr.tag.endswith("}rule-result"):
results["total"] += 1
result_elem = None
for child in rr:
if child.tag.endswith("}result"):
result_elem = child
if result_elem is not None:
val = result_elem.text
if val == "pass":
results["passed"] += 1
elif val == "fail":
results["failed"] += 1
results["findings"].append({
"rule_id": rr.get("idref", ""),
"severity": rr.get("severity", ""),
"result": "FAIL",
})
elif val == "notapplicable":
results["notapplicable"] += 1
scored = results["passed"] + results["failed"]
if scored > 0:
results["score"] = round((results["passed"] / scored) * 100, 2)
return results
def generate_report(results: dict, output_path: str) -> None:
"""Generate compliance report."""
report = {
"report_generated": datetime.utcnow().isoformat() + "Z",
"score": results["score"],
"total": results["total"],
"passed": results["passed"],
"failed": results["failed"],
"not_applicable": results["notapplicable"],
"status": "COMPLIANT" if results["score"] >= 95 else "NON-COMPLIANT",
"failed_controls": results["findings"][:100],
}
with open(output_path, "w") as f:
json.dump(report, f, indent=2)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python process.py <oscap_results.xml>")
sys.exit(1)
results = parse_oscap_results(sys.argv[1])
out = os.path.join(os.path.dirname(sys.argv[1]) or ".", "linux_cis_report.json")
generate_report(results, out)
print(f"Score: {results['score']}% | Passed: {results['passed']} | Failed: {results['failed']}")
Assets 1
template.mdtext/markdown · 0.6 KBKeep exploring