Linux Security Baseline Checks in Practice: One-Click Server Configuration Audit
Build a one-click Linux security baseline script that auto-audits six categories—SSH config, account permissions, file permissions, kernel network parameters, firewall, and software updates—and emits a compliance report with hardening advice. Suited to MLPS assessments and routine inspections.
Use Cases
Security baseline checks are the starting point of server security management. This article centers on Linux security baseline checks and ships a one-click audit script. It inspects SSH configuration, account permissions, file permissions, kernel network parameters, firewall, and software updates, then emits a pass/fail report. Use it for pre-production checks, MLPS 2.0 compliance assessments, and routine security inspections.
Prerequisites
- Linux distros: CentOS 7/8, RHEL, Ubuntu 20.04+, Debian 11+
- Root privileges to run the check commands
- bash 4.0 or later
How It Works
Security baseline checks use a configuration compliance baseline: freeze security-ops best practices into rules, compare the host's current config against those values item by item, and emit a three-state result: pass / fail / risk. Common check dimensions:
- SSH security: disable direct root login, enable key auth, change the default port
- Accounts and permissions: empty-password accounts, UID=0 high-risk accounts, sensitive file permissions
- Kernel and network: IP forwarding, SYN Cookies, reverse-path filtering, and related parameters
- Services and patches: high-risk service exposure, security update status
The script wraps each check in a function with a unified output format, which makes extension and archival straightforward.
Procedure
Step 1: Create the audit directory and script file
mkdir -p /opt/security-audit
vim /opt/security-audit/baseline_check.sh
Step 2: Write the baseline check script (core code)
#!/bin/bash
# /opt/security-audit/baseline_check.sh - Linux security baseline check script
PASS=0; FAIL=0; WARN=0
report() { # $1=status $2=check item $3=description
case "$1" in
PASS) echo "[PASS] $2"; PASS=$((PASS+1));;
FAIL) echo "[FAIL] $2 - $3"; FAIL=$((FAIL+1));;
WARN) echo "[WARN] $2 - $3"; WARN=$((WARN+1));;
esac
}
# 1. SSH configuration checks
root_login=$(grep -E '^PermitRootLogin' /etc/ssh/sshd_config | awk '{print $2}')
[ "$root_login" = "no" ] && report PASS "SSH disable root login" || report FAIL "SSH disable root login" "current: ${root_login:-not set}"
pwd_auth=$(grep -E '^PasswordAuthentication' /etc/ssh/sshd_config | awk '{print $2}')
[ "$pwd_auth" = "no" ] && report PASS "SSH key-only auth" || report WARN "SSH key-only auth" "current: ${pwd_auth:-not set}"
# 2. Account security checks
empty_pwd=$(awk -F: '($2==""){print $1}' /etc/shadow)
[ -z "$empty_pwd" ] && report PASS "no empty-password accounts" || report FAIL "no empty-password accounts" "found: $empty_pwd"
uid0=$(awk -F: '($3==0){print $1}' /etc/passwd | grep -v '^root$')
[ -z "$uid0" ] && report PASS "no UID=0 non-root accounts" || report FAIL "UID=0 accounts" "found: $uid0"
# 3. File permission checks
perm=$(stat -c %a /etc/shadow)
[ "$perm" = "640" -o "$perm" = "600" ] && report PASS "/etc/shadow permissions compliant" || report FAIL "/etc/shadow permissions" "current: $perm"
# 4. Kernel network parameter checks
syn=$(sysctl -n net.ipv4.tcp_syncookies)
[ "$syn" = "1" ] && report PASS "SYN Cookies enabled" || report FAIL "SYN Cookies" "current: $syn"
fwd=$(sysctl -n net.ipv4.ip_forward)
[ "$fwd" = "0" ] && report PASS "IP forwarding disabled" || report WARN "IP forwarding" "current: $fwd (ignore on routers)"
# 5. Firewall enabled check
fw=0
command -v firewalld >/dev/null && systemctl is-active firewalld 2>/dev/null | grep -q active && fw=1
command -v ufw >/dev/null && ufw status 2>/dev/null | grep -q active && fw=1
[ $fw -eq 1 ] && report PASS "firewall enabled" || report WARN "firewall enabled" "neither firewalld nor ufw is running"
# 6. Security update checks
up=0
command -v yum >/dev/null && up=$(yum -q check-update 2>/dev/null | grep -c '^[a-z]')
command -v apt >/dev/null && up=$(apt list --upgradable 2>/dev/null | grep -c upgradable)
[ "${up:-0}" -gt 0 ] && report WARN "system updates" "${up} packages pending update" || report PASS "system updates"
# 7. World-writable file checks
ww=$(find / -xdev -type f -perm -0002 2>/dev/null | head -5)
[ -z "$ww" ] && report PASS "no world-writable files" || report WARN "world-writable files" "$ww"
echo "----------------------------------------"
echo "Check complete: PASS=$PASS FAIL=$FAIL WARN=$WARN"
Step 3: Run the check and generate a report
chmod +x /opt/security-audit/baseline_check.sh
bash /opt/security-audit/baseline_check.sh | tee /opt/security-audit/report_$(date +%F).log
Verify Configuration
Script output should look like:
[PASS] SSH disable root login
[FAIL] SSH key-only auth - current: yes
[WARN] firewall enabled - neither firewalld nor ufw is running
----------------------------------------
Check complete: PASS=6 FAIL=2 WARN=3
For FAIL/WARN items, harden the host: edit `/etc/ssh/sshd_config`, run `systemctl restart sshd`, then re-run the script until those items show PASS. Archive report files as audit evidence for MLPS assessments.
FAQ
Q1: Will the check script affect production?
No. The script is read-only. It only runs query commands such as grep, stat, sysctl, and find, and does not change any configuration. The only item that may generate modest I/O is the find world-writable scan; run it during off-peak hours.
Q2: What if a check does not fit the business scenario?
Baseline checks support exemptions. A router/gateway host, for example, must enable IP forwarding. Change the corresponding check from FAIL to WARN, or keep an `exclude.conf` that records exemptions and state the reason in the report so the audit remains traceable.
Q3: How do I audit multiple servers in bulk?
Use Ansible for bulk rollout: put the script in the playbook files directory, distribute it with the `copy` module, execute via the `shell` module, and collect the logs. You can also use `gather_facts` to aggregate results on the control node.
Summary
Security baseline checks are a routine security-ops task. The one-click Linux security baseline script in this article covers six core categories—SSH, accounts, permissions, kernel parameters, firewall, and patches—and produces an archivable compliance report. Pair it with Ansible to scale to hundreds of servers. Run it monthly and after every major configuration change to stop configuration drift from introducing security risk.