OS Hardening as Code, Episode 4
Cloud AMI Security Risks · Linux Hardening as Code · Multi-Cloud OS Hardening · Automated OpenSCAP Compliance**
Focus Keyphrase: automated OpenSCAP compliance
Search Intent: Navigational
Meta Description: Get an A-F compliance grade on every AMI before it deploys — automated OpenSCAP scanning, SARIF export, and drift detection built into the image build process. (158 chars)
TL;DR
- “We use CIS L1” means nothing without a verified grade — automated OpenSCAP compliance provides one before any instance is deployed
- Stratum runs OpenSCAP after every build and attaches the grade to the image metadata:
cis-l1-A-98 - Grades are A through F based on percentage of controls passing, with explicit accounting for documented overrides
- SARIF output is machine-readable — importable directly into GitHub Advanced Security, Jira, or any SIEM
- Drift detection: rescan any running instance against the original blueprint and see exactly which controls changed since the image was built
- An image that scores below your minimum grade threshold doesn’t get snapshotted — it doesn’t exist
The Problem: A Grade That’s Never Been Verified Is Not a Grade
Security audit request:
"Provide CIS L1 compliance evidence for all production instances"
Team response:
Instance A: "CIS L1 hardened" — OpenSCAP last run: 4 months ago
Instance B: "CIS L1 hardened" — OpenSCAP last run: never
Instance C: "CIS L1 hardened" — OpenSCAP version: 1.2 (current: 1.3.8)
Instance D: "CIS L1 hardened" — manual scan output: "87% passing"
Instance E: "CIS L1 hardened" — manual scan output: "91% passing"
"Which profile was used for D and E? Are they comparable?"
"Were they scanned before or after a recent kernel update?"
"Why is C running an old OpenSCAP version?"
Automated OpenSCAP compliance means the grade is generated the same way, on every image, every time, before the image is ever deployed.
EP03 showed that the same HardeningBlueprint YAML builds consistent OS images across six cloud providers. What it left open is the question every auditor eventually asks: how do you know the Ansible hardening actually did what you think it did? Running Ansible-Lockdown successfully means the tasks ran. It does not mean every CIS control is satisfied — some controls can’t be applied by Ansible alone, some require manual verification, and some interact with the environment in unexpected ways.
A compliance team requested CIS L2 evidence for a SOC 2 Type II audit. The security team had been running OpenSCAP scans — but manually, on-demand, using slightly different profiles across teams, with no standard for how to store or compare results.
The audit found four problems:
1. Two instances had been scanned with CIS L1, not L2, despite being labeled “CIS L2”
2. Three instances hadn’t been scanned in over six months
3. The scan outputs from different teams were in different formats (HTML vs XML vs text)
4. Two instances showed “91% passing” and “89% passing” — with no documentation of whether those were acceptable thresholds or what the failing controls were
The audit took two weeks to resolve. The finding wasn’t a security failure — it was a documentation and process failure. But it consumed two weeks of engineering time and appeared in the audit report as a gap.
The root cause: compliance scanning was a manual step that produced inconsistent output in an inconsistent format.
How Automated OpenSCAP Compliance Works
Every Stratum build ends with an automated OpenSCAP scan:
stratum build --blueprint ubuntu22-cis-l1.yaml --provider aws
│
├─ Provisions build instance
│
├─ Runs Ansible-Lockdown (144 tasks)
│
├─ Runs post-build OpenSCAP scan
│ ├── Profile: CIS Ubuntu 22.04 L1 (from blueprint)
│ ├── OpenSCAP version: pinned in blueprint (default: latest)
│ └── 100 controls checked
│
├─ Calculates grade
│ ├── Passing: 92 controls
│ ├── Failing: 6 controls
│ ├── Overrides: 2 (documented in blueprint)
│ └── Grade: A (94/100 effective, 98% pass rate)
│
├─ Writes to image metadata:
│ compliance_grade=cis-l1-A-94
│ compliance_scan_date=2026-04-19
│ [email protected]
│
└─ Snapshots AMI (or fails if grade < min_grade)
The grade is written into the AMI (or GCP/Azure image) metadata at creation time. It travels with the image. Any instance launched from this AMI carries the provenance of what was applied and what grade was achieved.
The A-F Grade Calculation
The grade is not a simple percentage. It accounts for documented overrides and applies a threshold-based letter scale:
Total CIS controls: 100
Passing: 92
Failing: 6 (genuine failures)
Overrides (compliant): 2 (documented in blueprint, counted as passing)
Effective passing: 94 / 100
Grade: A
Grade thresholds (configurable per blueprint):
| Grade | Default threshold | Meaning |
|---|---|---|
| A | ≥ 95% effective | Production-ready, minimal exceptions |
| B | 85–94% | Acceptable with documented exceptions |
| C | 70–84% | Below standard — deploy with caution |
| D | 55–69% | Significant gaps — do not deploy to production |
| F | < 55% | Hardening failed — image not snapshotted |
The thresholds are configurable in the blueprint:
compliance:
benchmark: cis-l1
controls: all
min_grade: B # Build fails if grade < B
grade_thresholds:
A: 95
B: 85
C: 70
D: 55
If the build produces a grade below min_grade, the instance is terminated and no image is created. The failure is logged with the full list of controls that blocked the grade.
Reading the Scan Output
# Show the last build's scan results
stratum scan --show-last --blueprint ubuntu22-cis-l1.yaml
# Output:
# Build: ubuntu22-cis-l1 @ 2026-04-19T15:42:01Z
# Provider: aws (ap-south-1)
# Grade: A (94/100 effective controls)
#
# Passing controls: 92
# Failing controls: 6
# ──────────────────────────────────────────────
# FAIL 1.1.7 Ensure separate partition for /var/log/audit
# Reason: tmpfs used — separate block device not configured
# Remediation: Add /var/log/audit to separate EBS volume
#
# FAIL 1.6.1.3 Ensure AppArmor is enabled in bootloader config
# Reason: GRUB_CMDLINE_LINUX missing apparmor=1 security=apparmor
# Remediation: Update /etc/default/grub, run update-grub, reboot
#
# FAIL 3.1.1 Ensure IPv6 is disabled if not needed
# Reason: net.ipv6.conf.all.disable_ipv6=0
# Remediation: Set in /etc/sysctl.d/60-kernel-hardening.conf
# ...
#
# Overrides (compliant): 2
# ──────────────────────────────────────────────
# OVERRIDE 1.1.2 tmpfs /tmp via systemd unit — equivalent control
# OVERRIDE 5.2.4 SSH timeout managed by session manager policy
The failing controls tell you exactly what to fix and how to fix it. This is the difference between “87% passing” as a number and “87% passing” as an actionable gap list.
SARIF Export
Every scan produces a SARIF (Static Analysis Results Interchange Format) file:
# Export scan results to SARIF
stratum scan \
--instance i-0abc123 \
--benchmark cis-l1 \
--output sarif \
--out-file scan-results/i-0abc123-cis-l1.sarif
SARIF is the standard format for security scan results. It’s directly importable into:
- GitHub Advanced Security — upload via
actions/upload-sarif, results appear in the Security tab - Jira — import as security findings, linked to the image or instance ID
- Splunk / SIEM — structured JSON, parseable as events
- AWS Security Hub — importable as findings via the Security Hub API
For audit purposes, the SARIF file is the evidence artifact. It contains the full scan profile, every control result, the OpenSCAP version, the scan timestamp, and the machine it was run against.
# Upload to GitHub Advanced Security
stratum scan \
--instance i-0abc123 \
--benchmark cis-l1 \
--output sarif \
--github-upload \
--github-ref $GITHUB_REF \
--github-sha $GITHUB_SHA
Drift Detection
The grade at build time is the baseline. Any instance can be rescanned against the blueprint that built it:
# Rescan a running instance
stratum scan --instance i-0abc123 --blueprint ubuntu22-cis-l1.yaml
# Output:
# Instance: i-0abc123 (launched from ami-0a7f3c9e82d1b4c05)
# Original grade (build): A (94/100) — 2026-01-15
# Current grade (rescan): B (87/100) — 2026-04-19
#
# Drifted controls (7):
# 3.3.2 TCP SYN cookies: FAIL — net.ipv4.tcp_syncookies=0
# Last passing: 2026-01-15 (build)
# Current value: 0 (expected: 1)
#
# 5.3.2 sudo log_input: FAIL — rule removed from /etc/sudoers.d/
# Last passing: 2026-01-15 (build)
# Current value: [rule absent] (expected: Defaults log_input)
Drift detection is how you find the instances that were “temporarily” modified and never reverted. The scan compares the current state against the baseline — not against a generic CIS profile, but against the specific blueprint version that built the image.
Scanning Without a Build: Assessing Existing Instances
For instances not built with Stratum, you can run a standalone scan:
# Assess an existing instance against CIS L1
stratum scan --instance i-0legacy123 --benchmark cis-l1
# No blueprint comparison — just the raw CIS grade
# Output:
# Grade: C (72/100)
# 28 controls failing
# ...
This is useful for assessing the state of instances built before Stratum was in use, or for comparing a manual hardening approach against the benchmark.
What Controls Typically Block an A Grade
For Ubuntu 22.04 CIS L1 builds in most cloud environments, these are the controls that most commonly prevent an A grade:
| Control | Why it often fails | Fix |
|---|---|---|
1.1.7 /var/log/audit separate partition |
Cloud images don’t have separate volumes at build time | Add EBS volume, configure at launch |
| 1.6.1 AppArmor bootloader config | GRUB parameters not set correctly | Update /etc/default/grub, run update-grub |
| 3.1.1 Disable IPv6 | Cloud networking sometimes requires IPv6 | Override with documented reason if intentional |
| 5.2.21 SSH MaxStartups | Default sshd_config not updated | Add MaxStartups 10:30:60 to sshd_config |
| 6.1.10 World-writable files | Some package installations leave world-writable files | Post-install cleanup in Ansible role |
The first two (separate audit partition, AppArmor bootloader) are the most common A→B blockers and often require architecture decisions about how volumes are provisioned at launch versus build time.
Key Takeaways
- Automated OpenSCAP compliance means every image has a verified, reproducible grade generated by the same scanner with the same profile, before it’s ever deployed
- The A-F grade accounts for documented overrides from the blueprint — the failing controls in the output are genuine gaps, not known exceptions
- SARIF export makes scan results importable into GitHub Advanced Security, Jira, SIEM, and audit tooling
- Drift detection catches configuration changes that happen after the image is deployed — the grade at build time is the baseline
- Images that score below
min_gradedon’t get snapshotted — the failed build tells you exactly which controls to fix
What’s Next
Automated OpenSCAP compliance gives every image a verified grade before deployment. What EP04 left open is what happens after the grade is known — specifically, what prevents an engineer from deploying a C-grade image to production “just this once.”
The Pipeline API is the answer. EP05 covers the CI/CD compliance gate: POST /api/pipeline/scan fails the build if the image grade is below threshold. The unhardened image never reaches production — not because engineers are disciplined, but because the pipeline won’t let it through.
Next: CI/CD compliance gate — block unhardened images before they reach production
Get EP05 in your inbox when it publishes → linuxcent.com/subscribe