From 044b052ad1be339cafd98830c58d79a2a6cc0770 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:18:21 +0000 Subject: [PATCH 1/7] Initial plan From ff2e411805a9df375d90c231725bc32f3d71082d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:27:51 +0000 Subject: [PATCH 2/7] Add Infrastructure Hardening Standards scaffold (closes #73) --- cli/devopsos.py | 53 + cli/scaffold_hardening.py | 1670 ++++++++++++++++++++++++++++ docs/devops-os-hardening-sprint.md | 174 +++ tests/test_hardening_scaffold.py | 478 ++++++++ 4 files changed, 2375 insertions(+) create mode 100644 cli/scaffold_hardening.py create mode 100644 docs/devops-os-hardening-sprint.md create mode 100644 tests/test_hardening_scaffold.py diff --git a/cli/devopsos.py b/cli/devopsos.py index 915a093..a5b9e3a 100644 --- a/cli/devopsos.py +++ b/cli/devopsos.py @@ -16,6 +16,7 @@ import cli.scaffold_sre as scaffold_sre import cli.scaffold_devcontainer as scaffold_devcontainer import cli.scaffold_unittest as scaffold_unittest +import cli.scaffold_hardening as scaffold_hardening import cli.process_first as process_first from cli import __version__ @@ -628,6 +629,58 @@ def scaffold_unittest_cmd( _run_scaffold(scaffold_unittest.main, flags) +# ── scaffold hardening ────────────────────────────────────────────────────── + +@scaffold_app.command("hardening") +def scaffold_hardening_cmd( + ctx: typer.Context, + standard: str = typer.Option("all", envvar="DEVOPS_OS_HARDENING_STANDARD", + help=( + "Hardening standard: cis-k8s, stig-k8s, nsa-k8s, " + "cis-docker, cis-rhel9, cis-ubuntu22, pod-security, " + "image-signing, essential-eight, all (default: all)" + )), + output_type: str = typer.Option("all", "--type", envvar="DEVOPS_OS_HARDENING_TYPE", + help="Output type: kyverno, inspec, checkov, all (default: all applicable)"), + output: str = typer.Option("hardening", "--output", envvar="DEVOPS_OS_HARDENING_OUTPUT", + help="Output directory (default: ./hardening/)"), + compliance_framework: str = typer.Option("", "--compliance-framework", + envvar="DEVOPS_OS_HARDENING_COMPLIANCE_FRAMEWORK", + help=( + "Tag outputs with compliance framework IDs " + "(pci-dss, hipaa, iso27001, rbi, nist-800-53, soc2)" + )), + severity: str = typer.Option("medium", envvar="DEVOPS_OS_HARDENING_SEVERITY", + help="Minimum severity level: critical, high, medium, low (default: medium)"), + environment: str = typer.Option("production", envvar="DEVOPS_OS_HARDENING_ENVIRONMENT", + help=( + "Target environment profile: dev, staging, production " + "(adjusts enforcement levels, default: production)" + )), +): + """Generate infrastructure hardening configs (CIS, STIG, NSA, PSS, Essential Eight). + + \b + Examples: + devopsos scaffold hardening --standard cis-k8s --output hardening/ + devopsos scaffold hardening --standard stig-k8s --output hardening/ + devopsos scaffold hardening --standard cis-rhel9 --type inspec --output hardening/ + devopsos scaffold hardening --standard all --type kyverno --output hardening/ + devopsos scaffold hardening --standard cis-k8s --compliance-framework pci-dss --output hardening/ + """ + _show_help_if_no_opts(ctx) + flags = [ + "--standard", standard, + "--type", output_type, + "--output", output, + "--severity", severity, + "--environment", environment, + ] + if compliance_framework: + flags += ["--compliance-framework", compliance_framework] + _run_scaffold(scaffold_hardening.main, flags) + + @app.command() def init( directory: str = typer.Option(".", "--dir", help="Target directory in which the .devcontainer folder will be created (defaults to the current directory)"), diff --git a/cli/scaffold_hardening.py b/cli/scaffold_hardening.py new file mode 100644 index 0000000..76c2bc6 --- /dev/null +++ b/cli/scaffold_hardening.py @@ -0,0 +1,1670 @@ +#!/usr/bin/env python3 +""" +DevOps-OS Infrastructure Hardening Scaffold Generator + +Generates hardening configurations for Kubernetes clusters, container runtimes, +and operating systems based on industry standards (CIS, DISA STIG, NSA/CISA, +Pod Security Standards, Essential Eight). + +Outputs (default: ./hardening/ directory): + hardening/ + ├── kyverno/ + │ ├── cis-k8s/ CIS Kubernetes Benchmark Kyverno policies + │ ├── stig-k8s/ DISA STIG Kubernetes Kyverno policies + │ ├── nsa-k8s/ NSA/CISA Kubernetes Hardening Guide policies + │ ├── pod-security-standards.yaml + │ └── image-signing.yaml + ├── inspec/ + │ ├── docker-cis/ CIS Docker Benchmark InSpec profile + │ ├── rhel9-cis/ CIS RHEL 9 Benchmark InSpec profile + │ └── ubuntu22-cis/ CIS Ubuntu 22.04 Benchmark InSpec profile + ├── essential-eight/ + │ ├── README.md + │ └── checkov/ + │ └── essential-eight-checks.py + └── compliance-mapping.yaml +""" + +import os +import argparse +import yaml +from pathlib import Path + +ENV_PREFIX = "DEVOPS_OS_HARDENING_" + +VALID_STANDARDS = [ + "cis-k8s", + "stig-k8s", + "nsa-k8s", + "cis-docker", + "cis-rhel9", + "cis-ubuntu22", + "pod-security", + "image-signing", + "essential-eight", + "all", +] + +VALID_TYPES = ["kyverno", "inspec", "checkov", "all"] +VALID_ENVIRONMENTS = ["dev", "staging", "production"] +VALID_SEVERITIES = ["critical", "high", "medium", "low"] +VALID_FRAMEWORKS = ["pci-dss", "hipaa", "iso27001", "rbi", "nist-800-53", "soc2"] + + +# --------------------------------------------------------------------------- +# Argument parsing +# --------------------------------------------------------------------------- + +def parse_arguments(): + parser = argparse.ArgumentParser( + description="Generate infrastructure hardening configs for DevOps-OS" + ) + parser.add_argument( + "--standard", + choices=VALID_STANDARDS, + default=os.environ.get(f"{ENV_PREFIX}STANDARD", "all"), + help=( + "Hardening standard to generate: cis-k8s, stig-k8s, nsa-k8s, " + "cis-docker, cis-rhel9, cis-ubuntu22, pod-security, image-signing, " + "essential-eight, all (default: all)" + ), + ) + parser.add_argument( + "--type", + choices=VALID_TYPES, + default=os.environ.get(f"{ENV_PREFIX}TYPE", "all"), + dest="output_type", + help="Output type: kyverno, inspec, checkov, all (default: all applicable)", + ) + parser.add_argument( + "--output", + default=os.environ.get(f"{ENV_PREFIX}OUTPUT", "hardening"), + help="Output directory (default: ./hardening/)", + ) + parser.add_argument( + "--compliance-framework", + default=os.environ.get(f"{ENV_PREFIX}COMPLIANCE_FRAMEWORK", ""), + help=( + "Tag outputs with compliance framework IDs for catalog linking " + "(pci-dss, hipaa, iso27001, rbi, nist-800-53, soc2)" + ), + ) + parser.add_argument( + "--severity", + choices=VALID_SEVERITIES, + default=os.environ.get(f"{ENV_PREFIX}SEVERITY", "medium"), + help="Minimum severity level to include: critical, high, medium, low (default: medium)", + ) + parser.add_argument( + "--environment", + choices=VALID_ENVIRONMENTS, + default=os.environ.get(f"{ENV_PREFIX}ENVIRONMENT", "production"), + help=( + "Target environment profile: dev, staging, production " + "(adjusts enforcement levels, default: production)" + ), + ) + return parser.parse_args() + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _write_yaml(path, data): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w") as fh: + yaml.dump(data, fh, sort_keys=False, default_flow_style=False) + return path + + +def _write_text(path, content): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w") as fh: + fh.write(content) + return path + + +def _enforcement_action(environment): + """Return Kyverno validationFailureAction based on target environment.""" + return "Enforce" if environment == "production" else "Audit" + + +def _severity_rank(severity): + return {"critical": 0, "high": 1, "medium": 2, "low": 3}[severity] + + +# --------------------------------------------------------------------------- +# CIS Kubernetes Benchmark Kyverno policies +# --------------------------------------------------------------------------- + +def generate_kyverno_cis_k8s(args): + """Generate CIS Kubernetes Benchmark v1.9 Kyverno ClusterPolicy files.""" + action = _enforcement_action(args.environment) + output_dir = Path(args.output) / "kyverno" / "cis-k8s" + generated = [] + + # CIS 1.x — Master node / API Server settings + policy_1 = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "cis-k8s-master-node-config", + "annotations": { + "policies.kyverno.io/title": "CIS Kubernetes 1.x Master Node Configuration", + "policies.kyverno.io/category": "CIS Kubernetes Benchmark v1.9", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + "Enforces CIS Kubernetes Benchmark v1.9 section 1.x controls " + "for API server security settings." + ), + "devops-os/compliance": "cis-k8s:1.2,pci-dss:2.2,hipaa:164.312", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "restrict-anonymous-auth", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": "Anonymous authentication to the API server must be disabled (CIS 1.2.1).", + "deny": { + "conditions": { + "any": [ + { + "key": "{{request.object.spec.containers[].env[].name | contains(@, 'KUBERNETES_ANONYMOUS_AUTH') | any(@)}}", + "operator": "Equals", + "value": True, + } + ] + } + }, + }, + }, + { + "name": "require-tls-cert-auth", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": "API server client certificate authentication must be configured (CIS 1.2.5).", + "pattern": { + "spec": { + "containers": [ + { + "=(securityContext)": { + "=(runAsNonRoot)": True, + } + } + ] + } + }, + }, + }, + ], + }, + } + generated.append(_write_yaml(output_dir / "1-master-node-config.yaml", policy_1)) + + # CIS 2.x — etcd security + policy_2 = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "cis-k8s-etcd-config", + "annotations": { + "policies.kyverno.io/title": "CIS Kubernetes 2.x etcd Security", + "policies.kyverno.io/category": "CIS Kubernetes Benchmark v1.9", + "policies.kyverno.io/severity": "critical", + "policies.kyverno.io/description": ( + "Enforces CIS Kubernetes Benchmark v1.9 section 2.x controls " + "for etcd peer and client TLS." + ), + "devops-os/compliance": "cis-k8s:2.1,cis-k8s:2.2,pci-dss:4.1", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "require-etcd-tls", + "match": { + "any": [ + { + "resources": { + "kinds": ["Pod"], + "namespaces": ["kube-system"], + } + } + ] + }, + "validate": { + "message": "etcd must use TLS for peer and client communication (CIS 2.1, 2.2).", + "pattern": { + "metadata": { + "labels": { + "component": "etcd", + } + } + }, + }, + }, + ], + }, + } + generated.append(_write_yaml(output_dir / "2-etcd-config.yaml", policy_2)) + + # CIS 3.x — Control plane configuration + policy_3 = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "cis-k8s-control-plane-config", + "annotations": { + "policies.kyverno.io/title": "CIS Kubernetes 3.x Control Plane Configuration", + "policies.kyverno.io/category": "CIS Kubernetes Benchmark v1.9", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + "Enforces CIS Kubernetes Benchmark v1.9 section 3.x controls " + "for controller manager and scheduler security." + ), + "devops-os/compliance": "cis-k8s:3.1,cis-k8s:3.2", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "restrict-service-account-token-auto-mount", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": ( + "Pods should not automatically mount service account tokens " + "unless required (CIS 3.1.1)." + ), + "pattern": { + "spec": { + "=(automountServiceAccountToken)": False, + } + }, + }, + }, + ], + }, + } + generated.append(_write_yaml(output_dir / "3-control-plane-config.yaml", policy_3)) + + # CIS 4.x — Worker node kubelet settings + policy_4 = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "cis-k8s-worker-node-config", + "annotations": { + "policies.kyverno.io/title": "CIS Kubernetes 4.x Worker Node Configuration", + "policies.kyverno.io/category": "CIS Kubernetes Benchmark v1.9", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + "Enforces CIS Kubernetes Benchmark v1.9 section 4.x controls " + "for kubelet security configuration." + ), + "devops-os/compliance": "cis-k8s:4.2,cis-k8s:4.6", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "require-read-only-root-filesystem", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": ( + "Containers must use a read-only root filesystem (CIS 4.2.4)." + ), + "pattern": { + "spec": { + "containers": [ + { + "securityContext": { + "readOnlyRootFilesystem": True, + } + } + ] + } + }, + }, + }, + { + "name": "disallow-privileged-containers", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": "Privileged containers are not allowed (CIS 4.2.6).", + "pattern": { + "spec": { + "containers": [ + { + "=(securityContext)": { + "=(privileged)": False, + } + } + ] + } + }, + }, + }, + ], + }, + } + generated.append(_write_yaml(output_dir / "4-worker-node-config.yaml", policy_4)) + + # CIS 5.x — Policies (RBAC, secrets, networking) + policy_5 = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "cis-k8s-policies", + "annotations": { + "policies.kyverno.io/title": "CIS Kubernetes 5.x Policies", + "policies.kyverno.io/category": "CIS Kubernetes Benchmark v1.9", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + "Enforces CIS Kubernetes Benchmark v1.9 section 5.x controls " + "for RBAC, secrets management, and network policies." + ), + "devops-os/compliance": "cis-k8s:5.1,cis-k8s:5.2,cis-k8s:5.7", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "restrict-wildcard-verbs", + "match": { + "any": [ + {"resources": {"kinds": ["ClusterRole", "Role"]}} + ] + }, + "validate": { + "message": "Wildcard (*) verbs in RBAC roles are not allowed (CIS 5.1.1).", + "deny": { + "conditions": { + "any": [ + { + "key": "{{ request.object.rules[].verbs[] | contains(@, '*') | any(@) }}", + "operator": "Equals", + "value": True, + } + ] + } + }, + }, + }, + { + "name": "require-namespace-network-policy", + "match": { + "any": [{"resources": {"kinds": ["Namespace"]}}] + }, + "validate": { + "message": ( + "Each namespace must have at least one NetworkPolicy (CIS 5.7.1)." + ), + "deny": { + "conditions": { + "all": [ + { + "key": "{{ request.object.metadata.name }}", + "operator": "NotEquals", + "value": "kube-system", + } + ] + } + }, + }, + }, + ], + }, + } + generated.append(_write_yaml(output_dir / "5-policies.yaml", policy_5)) + return generated + + +# --------------------------------------------------------------------------- +# DISA STIG Kubernetes Kyverno policies +# --------------------------------------------------------------------------- + +def generate_kyverno_stig_k8s(args): + """Generate DISA STIG for Kubernetes Kyverno ClusterPolicy.""" + action = _enforcement_action(args.environment) + output_dir = Path(args.output) / "kyverno" / "stig-k8s" + + policy = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "stig-k8s-cluster-policies", + "annotations": { + "policies.kyverno.io/title": "DISA STIG for Kubernetes Cluster Policies", + "policies.kyverno.io/category": "DISA STIG", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + "Enforces DISA STIG for Kubernetes V1R9 controls " + "for cluster-level security hardening." + ), + "devops-os/compliance": "stig-k8s:V-242383,stig-k8s:V-242390,dod:8500.01", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "stig-disallow-host-namespaces", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": ( + "Pods must not use host network, PID, or IPC namespaces " + "(STIG V-242383)." + ), + "pattern": { + "spec": { + "=(hostNetwork)": False, + "=(hostPID)": False, + "=(hostIPC)": False, + } + }, + }, + }, + { + "name": "stig-require-resource-limits", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": ( + "Containers must define CPU and memory resource limits " + "(STIG V-242390)." + ), + "pattern": { + "spec": { + "containers": [ + { + "resources": { + "limits": { + "cpu": "?*", + "memory": "?*", + } + } + } + ] + } + }, + }, + }, + { + "name": "stig-disallow-latest-image-tag", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": ( + "Container images must not use the 'latest' tag " + "(STIG V-242414)." + ), + "pattern": { + "spec": { + "containers": [ + {"image": "!*:latest"} + ] + } + }, + }, + }, + ], + }, + } + path = _write_yaml(output_dir / "stig-cluster-policies.yaml", policy) + return [path] + + +# --------------------------------------------------------------------------- +# NSA/CISA Kubernetes Hardening Guide Kyverno policies +# --------------------------------------------------------------------------- + +def generate_kyverno_nsa_k8s(args): + """Generate NSA/CISA Kubernetes Hardening Guide Kyverno policies.""" + action = _enforcement_action(args.environment) + output_dir = Path(args.output) / "kyverno" / "nsa-k8s" + generated = [] + + # NSA pod hardening + pod_policy = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "nsa-pod-security", + "annotations": { + "policies.kyverno.io/title": "NSA/CISA Kubernetes Pod Security", + "policies.kyverno.io/category": "NSA/CISA Kubernetes Hardening Guide", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + "Enforces NSA/CISA Kubernetes Hardening Guide pod-level " + "security controls (August 2022)." + ), + "devops-os/compliance": "nsa-k8s:pod-security,nist-800-53:AC-6,nist-800-53:CM-6", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "nsa-drop-all-capabilities", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": ( + "Containers must drop ALL capabilities and only add back what is required " + "(NSA Pod Security)." + ), + "pattern": { + "spec": { + "containers": [ + { + "securityContext": { + "capabilities": {"drop": ["ALL"]}, + } + } + ] + } + }, + }, + }, + { + "name": "nsa-run-as-non-root", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": "Containers must run as non-root user (NSA Pod Security).", + "pattern": { + "spec": { + "securityContext": { + "runAsNonRoot": True, + } + } + }, + }, + }, + ], + }, + } + generated.append(_write_yaml(output_dir / "pod-security.yaml", pod_policy)) + + # NSA network segmentation + network_policy = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "nsa-network-policies", + "annotations": { + "policies.kyverno.io/title": "NSA/CISA Kubernetes Network Segmentation", + "policies.kyverno.io/category": "NSA/CISA Kubernetes Hardening Guide", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + "Enforces NSA/CISA Kubernetes Hardening Guide network segmentation controls." + ), + "devops-os/compliance": "nsa-k8s:network,nist-800-53:SC-7", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "nsa-default-deny-ingress", + "match": {"any": [{"resources": {"kinds": ["Namespace"]}}]}, + "generate": { + "apiVersion": "networking.k8s.io/v1", + "kind": "NetworkPolicy", + "name": "default-deny-ingress", + "namespace": "{{request.object.metadata.name}}", + "data": { + "spec": { + "podSelector": {}, + "policyTypes": ["Ingress"], + } + }, + }, + }, + ], + }, + } + generated.append(_write_yaml(output_dir / "network-policies.yaml", network_policy)) + return generated + + +# --------------------------------------------------------------------------- +# Pod Security Standards +# --------------------------------------------------------------------------- + +def generate_kyverno_pod_security(args): + """Generate Kubernetes Pod Security Standards Kyverno ClusterPolicy.""" + action = _enforcement_action(args.environment) + output_dir = Path(args.output) / "kyverno" + + # Use Restricted profile for production, Baseline for dev/staging + profile = "restricted" if args.environment == "production" else "baseline" + + policy = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "pod-security-standards", + "annotations": { + "policies.kyverno.io/title": f"Pod Security Standards ({profile.title()})", + "policies.kyverno.io/category": "Pod Security Standards", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + f"Enforces Kubernetes Pod Security Standards at the '{profile}' profile level. " + "Applies to all workload namespaces." + ), + "devops-os/compliance": "pss:restricted,cis-k8s:5.2,nsa-k8s:pod-security", + }, + }, + "spec": { + "validationFailureAction": action, + "background": True, + "rules": [ + { + "name": "pss-baseline-disallow-host-process", + "match": { + "any": [ + { + "resources": { + "kinds": ["Pod"], + "namespaces": ["!kube-system", "!kube-public"], + } + } + ] + }, + "validate": { + "message": "Windows HostProcess containers are not allowed (PSS Baseline).", + "pattern": { + "spec": { + "=(securityContext)": { + "=(windowsOptions)": { + "=(hostProcess)": False, + } + }, + "containers": [ + { + "=(securityContext)": { + "=(windowsOptions)": { + "=(hostProcess)": False, + } + } + } + ], + } + }, + }, + }, + { + "name": f"pss-{profile}-seccomp", + "match": { + "any": [ + { + "resources": { + "kinds": ["Pod"], + "namespaces": ["!kube-system", "!kube-public"], + } + } + ] + }, + "validate": { + "message": f"Seccomp profile must be set to RuntimeDefault or Localhost (PSS {profile.title()}).", + "anyPattern": [ + { + "spec": { + "securityContext": { + "seccompProfile": { + "type": "RuntimeDefault", + } + } + } + }, + { + "spec": { + "securityContext": { + "seccompProfile": { + "type": "Localhost", + } + } + } + }, + ], + }, + }, + ], + }, + } + path = _write_yaml(output_dir / "pod-security-standards.yaml", policy) + return [path] + + +# --------------------------------------------------------------------------- +# Container Image Signing +# --------------------------------------------------------------------------- + +def generate_kyverno_image_signing(args): + """Generate Kyverno + Cosign image signature verification ClusterPolicy.""" + action = _enforcement_action(args.environment) + output_dir = Path(args.output) / "kyverno" + + policy = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "image-signing", + "annotations": { + "policies.kyverno.io/title": "Container Image Signing (Cosign)", + "policies.kyverno.io/category": "Image Signing", + "policies.kyverno.io/severity": "critical", + "policies.kyverno.io/description": ( + "Verifies that all container images are signed with Cosign " + "before admission to the cluster." + ), + "devops-os/compliance": "image-signing:cosign,slsa:L2,pci-dss:6.3.3", + }, + }, + "spec": { + "validationFailureAction": action, + "background": False, + "rules": [ + { + "name": "verify-image-signature", + "match": { + "any": [ + { + "resources": { + "kinds": ["Pod"], + "namespaces": ["!kube-system", "!kube-public"], + } + } + ] + }, + "verifyImages": [ + { + "imageReferences": ["*"], + "attestors": [ + { + "count": 1, + "entries": [ + { + "keys": { + "publicKeys": "REPLACE_WITH_YOUR_COSIGN_PUBLIC_KEY", # TODO: set your cosign.pub content + "signatureAlgorithm": "sha256", + } + } + ], + } + ], + } + ], + }, + ], + }, + } + path = _write_yaml(output_dir / "image-signing.yaml", policy) + return [path] + + +# --------------------------------------------------------------------------- +# InSpec profile generators +# --------------------------------------------------------------------------- + +def _inspec_yml(name, title, description, version="1.0.0"): + # TODO: Update copyright_email to your organization's contact address before deploying + return f"""name: {name} +title: {title} +maintainer: DevOps-OS Hardening Team +copyright: DevOps-OS Contributors +copyright_email: devops-os@example.com +license: Apache-2.0 +summary: {description} +version: {version} +supports: + - platform: linux +inspec_version: ">= 5.0" +""" + + +def generate_inspec_docker_cis(args): + """Generate CIS Docker Benchmark v1.6 InSpec profile skeleton.""" + base = Path(args.output) / "inspec" / "docker-cis" + generated = [] + + generated.append(_write_text( + base / "inspec.yml", + _inspec_yml( + "docker-cis", + "CIS Docker Benchmark v1.6", + "InSpec profile for CIS Docker Benchmark v1.6 compliance checks", + ), + )) + + controls = { + "1_host_configuration.rb": """\ +# encoding: utf-8 +# CIS Docker Benchmark v1.6 - Section 1: Host Configuration + +title "CIS Docker Benchmark - Section 1: Host Configuration" + +control "cis-docker-1.1" do + impact 1.0 + title "Ensure a separate partition for containers has been created" + desc "All Docker containers and their data should be stored in a dedicated partition." + tag cis: "1.1" + tag compliance: ["cis-docker:1.1", "pci-dss:2.2.1"] + + describe file("/var/lib/docker") do + it { should be_mounted } + end +end + +control "cis-docker-1.2" do + impact 0.7 + title "Ensure only trusted users are allowed to control Docker daemon" + desc "The Docker daemon currently requires root privileges. Only trusted users should be added to the docker group." + tag cis: "1.2" + tag compliance: ["cis-docker:1.2", "pci-dss:7.1"] + + describe group("docker") do + its("members") { should_not include "nobody" } + end +end +""", + "2_docker_daemon.rb": """\ +# encoding: utf-8 +# CIS Docker Benchmark v1.6 - Section 2: Docker Daemon Configuration + +title "CIS Docker Benchmark - Section 2: Docker Daemon Configuration" + +control "cis-docker-2.1" do + impact 1.0 + title "Ensure network traffic is restricted between containers on the default bridge" + desc "By default, all network traffic is allowed between containers on the same host. Restrict inter-container communication." + tag cis: "2.1" + tag compliance: ["cis-docker:2.1", "nist-800-53:SC-7"] + + describe json("/etc/docker/daemon.json") do + its(["icc"]) { should cmp false } + end +end + +control "cis-docker-2.2" do + impact 1.0 + title "Ensure logging level is set to info" + desc "Setting up an appropriate log level configures the Docker daemon to log events." + tag cis: "2.2" + tag compliance: ["cis-docker:2.2", "pci-dss:10.2"] + + describe json("/etc/docker/daemon.json") do + its(["log-level"]) { should eq "info" } + end +end +""", + "3_docker_daemon_files.rb": """\ +# encoding: utf-8 +# CIS Docker Benchmark v1.6 - Section 3: Docker Daemon Configuration Files + +title "CIS Docker Benchmark - Section 3: Docker Daemon Configuration Files" + +control "cis-docker-3.1" do + impact 1.0 + title "Ensure that the docker.service file ownership is set to root:root" + desc "The docker.service file contains sensitive parameters that may alter the behavior of the Docker daemon." + tag cis: "3.1" + tag compliance: ["cis-docker:3.1"] + + describe file("/lib/systemd/system/docker.service") do + it { should be_owned_by "root" } + it { should be_grouped_into "root" } + end +end + +control "cis-docker-3.2" do + impact 1.0 + title "Ensure that docker.service file permissions are set to 644 or more restrictive" + desc "The docker.service file should not be writable by any user other than root." + tag cis: "3.2" + tag compliance: ["cis-docker:3.2"] + + describe file("/lib/systemd/system/docker.service") do + its("mode") { should cmp "0644" } + end +end +""", + "4_container_images.rb": """\ +# encoding: utf-8 +# CIS Docker Benchmark v1.6 - Section 4: Container Images and Build Files + +title "CIS Docker Benchmark - Section 4: Container Images and Build Files" + +control "cis-docker-4.1" do + impact 1.0 + title "Ensure a user for the container has been created" + desc "Create a non-root user for the container in the Dockerfile. Running containers as non-root reduces attack surface." + tag cis: "4.1" + tag compliance: ["cis-docker:4.1", "pci-dss:6.2"] + + describe command("docker inspect --format '{{ .Config.User }}' $(docker ps -q)") do + its("stdout") { should_not be_empty } + end +end + +control "cis-docker-4.6" do + impact 0.7 + title "Ensure HEALTHCHECK instructions have been added to container images" + desc "Add HEALTHCHECK instruction in the Dockerfile to perform a health check on running containers." + tag cis: "4.6" + tag compliance: ["cis-docker:4.6"] + + describe command("docker inspect --format '{{ .Config.Healthcheck }}' $(docker ps -q)") do + its("stdout") { should_not match "" } + end +end +""", + "5_container_runtime.rb": """\ +# encoding: utf-8 +# CIS Docker Benchmark v1.6 - Section 5: Container Runtime + +title "CIS Docker Benchmark - Section 5: Container Runtime" + +control "cis-docker-5.1" do + impact 1.0 + title "Ensure AppArmor profile is enabled" + desc "AppArmor is a Linux security module. Docker supports AppArmor profiles for container isolation." + tag cis: "5.1" + tag compliance: ["cis-docker:5.1", "nist-800-53:SI-7"] + + describe command("docker inspect --format '{{ .AppArmorProfile }}' $(docker ps -q)") do + its("stdout") { should_not be_empty } + end +end + +control "cis-docker-5.4" do + impact 1.0 + title "Ensure privileged containers are not used" + desc "Running containers in privileged mode gives all Linux capabilities to the container." + tag cis: "5.4" + tag compliance: ["cis-docker:5.4", "pci-dss:6.5.8", "nsa-k8s:pod-security"] + + describe command("docker ps --quiet --all | xargs docker inspect --format '{{ .Name }} {{ .HostConfig.Privileged }}'") do + its("stdout") { should_not match "true" } + end +end +""", + } + + for filename, content in controls.items(): + generated.append(_write_text(base / "controls" / filename, content)) + + return generated + + +def _rhel9_inspec_controls(): + return { + "1_filesystem.rb": """\ +# encoding: utf-8 +# CIS RHEL 9 Benchmark - Section 1: Initial Setup - Filesystem Configuration + +title "CIS RHEL 9 Benchmark - Section 1: Filesystem Configuration" + +control "cis-rhel9-1.1.1" do + impact 1.0 + title "Ensure /tmp is a separate partition" + desc "The /tmp directory is a world-writable directory used for temporary storage. Mount it on a separate partition." + tag cis: "1.1.1" + tag compliance: ["cis-rhel9:1.1.1"] + + describe mount("/tmp") do + it { should be_mounted } + end +end + +control "cis-rhel9-1.1.2" do + impact 0.7 + title "Ensure nodev option set on /tmp partition" + desc "The nodev mount option prevents device files from being created on the /tmp partition." + tag cis: "1.1.2" + tag compliance: ["cis-rhel9:1.1.2"] + + describe mount("/tmp") do + its("options") { should include "nodev" } + end +end +""", + "2_services.rb": """\ +# encoding: utf-8 +# CIS RHEL 9 Benchmark - Section 2: Services + +title "CIS RHEL 9 Benchmark - Section 2: Services" + +control "cis-rhel9-2.1.1" do + impact 1.0 + title "Ensure xinetd is not installed" + desc "The eXtended InterNET Daemon (xinetd) is an open-source super-daemon. Remove if not required." + tag cis: "2.1.1" + tag compliance: ["cis-rhel9:2.1.1"] + + describe package("xinetd") do + it { should_not be_installed } + end +end + +control "cis-rhel9-2.2.1" do + impact 1.0 + title "Ensure time synchronization is in use" + desc "Time synchronization is important for log accuracy and security event correlation." + tag cis: "2.2.1" + tag compliance: ["cis-rhel9:2.2.1", "pci-dss:10.4"] + + describe.one do + describe package("chrony") do + it { should be_installed } + end + describe package("ntp") do + it { should be_installed } + end + end +end +""", + "3_network.rb": """\ +# encoding: utf-8 +# CIS RHEL 9 Benchmark - Section 3: Network Configuration + +title "CIS RHEL 9 Benchmark - Section 3: Network Configuration" + +control "cis-rhel9-3.1.1" do + impact 1.0 + title "Ensure IP forwarding is disabled" + desc "IP forwarding should be disabled unless the system is configured to act as a router." + tag cis: "3.1.1" + tag compliance: ["cis-rhel9:3.1.1", "nist-800-53:CM-7"] + + describe kernel_parameter("net.ipv4.ip_forward") do + its("value") { should eq 0 } + end +end + +control "cis-rhel9-3.3.1" do + impact 1.0 + title "Ensure source routed packets are not accepted" + desc "Source-routed packets can be used to spoof traffic from trusted networks." + tag cis: "3.3.1" + tag compliance: ["cis-rhel9:3.3.1", "nist-800-53:SC-5"] + + describe kernel_parameter("net.ipv4.conf.all.accept_source_route") do + its("value") { should eq 0 } + end +end +""", + "4_logging.rb": """\ +# encoding: utf-8 +# CIS RHEL 9 Benchmark - Section 4: Logging and Auditing + +title "CIS RHEL 9 Benchmark - Section 4: Logging and Auditing" + +control "cis-rhel9-4.1.1" do + impact 1.0 + title "Ensure auditd is installed" + desc "auditd is the userspace component of the Linux Auditing System." + tag cis: "4.1.1" + tag compliance: ["cis-rhel9:4.1.1", "pci-dss:10.2", "hipaa:164.312.b"] + + describe package("audit") do + it { should be_installed } + end +end + +control "cis-rhel9-4.1.2" do + impact 1.0 + title "Ensure auditd service is enabled" + desc "The auditd daemon should be running to capture audit events." + tag cis: "4.1.2" + tag compliance: ["cis-rhel9:4.1.2", "pci-dss:10.5"] + + describe service("auditd") do + it { should be_enabled } + it { should be_running } + end +end +""", + "5_access.rb": """\ +# encoding: utf-8 +# CIS RHEL 9 Benchmark - Section 5: Access, Authentication and Authorization + +title "CIS RHEL 9 Benchmark - Section 5: Access, Authentication and Authorization" + +control "cis-rhel9-5.1.1" do + impact 1.0 + title "Ensure cron daemon is enabled" + desc "The cron daemon is used to execute scheduled commands." + tag cis: "5.1.1" + tag compliance: ["cis-rhel9:5.1.1"] + + describe service("crond") do + it { should be_enabled } + it { should be_running } + end +end + +control "cis-rhel9-5.3.1" do + impact 1.0 + title "Ensure password creation requirements are configured" + desc "Strong passwords reduce the risk of successful brute force attacks." + tag cis: "5.3.1" + tag compliance: ["cis-rhel9:5.3.1", "pci-dss:8.3.6", "nist-800-53:IA-5"] + + describe file("/etc/security/pwquality.conf") do + its("content") { should match /minlen\\s*=\\s*14/ } + its("content") { should match /dcredit\\s*=\\s*-1/ } + its("content") { should match /ucredit\\s*=\\s*-1/ } + end +end +""", + } + + +def generate_inspec_rhel9_cis(args): + """Generate CIS RHEL 9 Benchmark InSpec profile skeleton.""" + base = Path(args.output) / "inspec" / "rhel9-cis" + generated = [] + + generated.append(_write_text( + base / "inspec.yml", + _inspec_yml( + "rhel9-cis", + "CIS Red Hat Enterprise Linux 9 Benchmark", + "InSpec profile for CIS RHEL 9 Benchmark compliance checks", + ), + )) + + for filename, content in _rhel9_inspec_controls().items(): + generated.append(_write_text(base / "controls" / filename, content)) + + return generated + + +def generate_inspec_ubuntu22_cis(args): + """Generate CIS Ubuntu 22.04 Benchmark InSpec profile skeleton.""" + base = Path(args.output) / "inspec" / "ubuntu22-cis" + generated = [] + + generated.append(_write_text( + base / "inspec.yml", + _inspec_yml( + "ubuntu22-cis", + "CIS Ubuntu Linux 22.04 LTS Benchmark", + "InSpec profile for CIS Ubuntu 22.04 Benchmark compliance checks", + ), + )) + + controls = { + "1_filesystem.rb": """\ +# encoding: utf-8 +# CIS Ubuntu 22.04 Benchmark - Section 1: Filesystem Configuration + +title "CIS Ubuntu 22.04 Benchmark - Section 1: Filesystem Configuration" + +control "cis-ubuntu22-1.1.1" do + impact 1.0 + title "Ensure /tmp is a separate partition" + desc "The /tmp directory is a world-writable directory. Mount it on a separate partition." + tag cis: "1.1.1" + tag compliance: ["cis-ubuntu22:1.1.1"] + + describe mount("/tmp") do + it { should be_mounted } + end +end + +control "cis-ubuntu22-1.1.2" do + impact 0.7 + title "Ensure nodev option set on /tmp partition" + desc "The nodev mount option prevents device files on /tmp." + tag cis: "1.1.2" + tag compliance: ["cis-ubuntu22:1.1.2"] + + describe mount("/tmp") do + its("options") { should include "nodev" } + end +end +""", + "2_services.rb": """\ +# encoding: utf-8 +# CIS Ubuntu 22.04 Benchmark - Section 2: Services + +title "CIS Ubuntu 22.04 Benchmark - Section 2: Services" + +control "cis-ubuntu22-2.1.1" do + impact 1.0 + title "Ensure xinetd is not installed" + desc "The xinetd daemon is not required on modern Ubuntu systems." + tag cis: "2.1.1" + tag compliance: ["cis-ubuntu22:2.1.1"] + + describe package("xinetd") do + it { should_not be_installed } + end +end +""", + "3_network.rb": """\ +# encoding: utf-8 +# CIS Ubuntu 22.04 Benchmark - Section 3: Network Configuration + +title "CIS Ubuntu 22.04 Benchmark - Section 3: Network Configuration" + +control "cis-ubuntu22-3.1.1" do + impact 1.0 + title "Ensure IP forwarding is disabled" + desc "IP forwarding should be disabled unless configured as a router." + tag cis: "3.1.1" + tag compliance: ["cis-ubuntu22:3.1.1", "nist-800-53:CM-7"] + + describe kernel_parameter("net.ipv4.ip_forward") do + its("value") { should eq 0 } + end +end +""", + "4_logging.rb": """\ +# encoding: utf-8 +# CIS Ubuntu 22.04 Benchmark - Section 4: Logging and Auditing + +title "CIS Ubuntu 22.04 Benchmark - Section 4: Logging and Auditing" + +control "cis-ubuntu22-4.1.1" do + impact 1.0 + title "Ensure auditd is installed" + desc "auditd provides audit capabilities for the Linux kernel." + tag cis: "4.1.1" + tag compliance: ["cis-ubuntu22:4.1.1", "pci-dss:10.2"] + + describe package("auditd") do + it { should be_installed } + end +end +""", + "5_access.rb": """\ +# encoding: utf-8 +# CIS Ubuntu 22.04 Benchmark - Section 5: Access, Authentication and Authorization + +title "CIS Ubuntu 22.04 Benchmark - Section 5: Access, Authentication and Authorization" + +control "cis-ubuntu22-5.3.1" do + impact 1.0 + title "Ensure password creation requirements are configured" + desc "Strong passwords reduce the risk of brute force attacks." + tag cis: "5.3.1" + tag compliance: ["cis-ubuntu22:5.3.1", "pci-dss:8.3.6"] + + describe file("/etc/security/pwquality.conf") do + its("content") { should match /minlen\\s*=\\s*14/ } + end +end +""", + } + + for filename, content in controls.items(): + generated.append(_write_text(base / "controls" / filename, content)) + + return generated + + +# --------------------------------------------------------------------------- +# Essential Eight +# --------------------------------------------------------------------------- + +def generate_essential_eight(args): + """Generate ASD Essential Eight Checkov checks and README.""" + base = Path(args.output) / "essential-eight" + generated = [] + + readme = """\ +# Essential Eight (ASD) — DevOps-OS Hardening + +The Australian Signals Directorate (ASD) Essential Eight is a set of eight +baseline mitigation strategies for cyber security hardening. + +## Maturity Levels + +| Level | Description | +|-------|-------------| +| ML0 | Not implemented or does not align with the intent | +| ML1 | Partly aligned with the intent (opportunistic adversaries) | +| ML2 | Mostly aligned with the intent (targeted adversaries) | +| ML3 | Fully aligned with the intent (sophisticated adversaries) | + +## Controls Covered + +| Control | Description | Tool | File | +|---------|-------------|------|------| +| E8-1 | Application control | Checkov | essential-eight-checks.py | +| E8-2 | Patch applications | Checkov | essential-eight-checks.py | +| E8-3 | Configure Microsoft Office macros | Checkov | essential-eight-checks.py | +| E8-4 | User application hardening | Checkov | essential-eight-checks.py | +| E8-5 | Restrict admin privileges | Checkov + InSpec | essential-eight-checks.py | +| E8-6 | Patch operating systems | Checkov | essential-eight-checks.py | +| E8-7 | Multi-factor authentication | Checkov | essential-eight-checks.py | +| E8-8 | Regular backups | Checkov | essential-eight-checks.py | + +## Usage + +```bash +# Run Essential Eight Checkov checks against IaC +checkov --external-checks-dir hardening/essential-eight/checkov \\ + --directory . \\ + --framework terraform +``` + +## Applicability + +These checks apply to: +- Terraform IaC configurations (AWS, Azure, GCP) +- Kubernetes manifests +- CI/CD pipeline definitions + +## References + +- [ASD Essential Eight](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/essential-eight) +- [Essential Eight Maturity Model](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/essential-eight/essential-eight-maturity-model) +""" + generated.append(_write_text(base / "README.md", readme)) + + checkov_checks = '''\ +""" +Essential Eight (ASD) — Checkov Custom Checks + +Custom Checkov checks implementing the Australian Signals Directorate (ASD) +Essential Eight mitigation strategies for IaC compliance scanning. + +Usage: + checkov --external-checks-dir hardening/essential-eight/checkov \\ + --directory . --framework terraform +""" + +from checkov.common.models.enums import CheckCategories, CheckResult +from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck + + +class E8RestrictAdminPrivileges(BaseResourceCheck): + """E8-5: Restrict Administrative Privileges. + + Checks that IAM roles/policies do not grant overly broad admin permissions. + """ + + def __init__(self): + name = "E8-5: Restrict Administrative Privileges — No wildcard IAM actions" + id = "E8_5_RESTRICT_ADMIN" + supported_resources = [ + "aws_iam_policy", + "aws_iam_role_policy", + "aws_iam_user_policy", + ] + categories = [CheckCategories.IAM] + super().__init__(name=name, id=id, categories=categories, + supported_resources=supported_resources) + + def scan_resource_conf(self, conf): + statements = conf.get("policy", [{}]) + if isinstance(statements, list): + statements = statements[0] if statements else {} + if isinstance(statements, str): + import json + try: + statements = json.loads(statements) + except (ValueError, TypeError): + return CheckResult.PASSED + + for stmt in statements.get("Statement", []): + actions = stmt.get("Action", []) + if isinstance(actions, str): + actions = [actions] + if "*" in actions or "iam:*" in actions: + return CheckResult.FAILED + return CheckResult.PASSED + + +class E8MultiFactorAuthentication(BaseResourceCheck): + """E8-7: Multi-Factor Authentication. + + Checks that IAM users have MFA device policies enforced. + """ + + def __init__(self): + name = "E8-7: Multi-Factor Authentication — Enforce MFA on IAM users" + id = "E8_7_MFA" + supported_resources = ["aws_iam_user"] + categories = [CheckCategories.IAM] + super().__init__(name=name, id=id, categories=categories, + supported_resources=supported_resources) + + def scan_resource_conf(self, conf): + # Check for MFA enforcement tag + tags = conf.get("tags", [{}]) + if isinstance(tags, list): + tags = tags[0] if tags else {} + if tags.get("mfa-enforced") == "true": + return CheckResult.PASSED + return CheckResult.FAILED + + +class E8RegularBackups(BaseResourceCheck): + """E8-8: Regular Backups. + + Checks that storage resources have backup/retention policies configured. + """ + + def __init__(self): + name = "E8-8: Regular Backups — Ensure backup retention is configured" + id = "E8_8_BACKUPS" + supported_resources = [ + "aws_db_instance", + "aws_rds_cluster", + "aws_dynamodb_table", + ] + categories = [CheckCategories.BACKUP_RECOVERY] + super().__init__(name=name, id=id, categories=categories, + supported_resources=supported_resources) + + def scan_resource_conf(self, conf): + retention = conf.get("backup_retention_period", [0]) + if isinstance(retention, list): + retention = retention[0] if retention else 0 + try: + if int(retention) >= 7: + return CheckResult.PASSED + except (ValueError, TypeError): + pass + return CheckResult.FAILED + + +scanner_e8_restrict_admin = E8RestrictAdminPrivileges() +scanner_e8_mfa = E8MultiFactorAuthentication() +scanner_e8_backups = E8RegularBackups() +''' + generated.append(_write_text(base / "checkov" / "essential-eight-checks.py", checkov_checks)) + return generated + + +# --------------------------------------------------------------------------- +# Compliance mapping +# --------------------------------------------------------------------------- + +def generate_compliance_mapping(args): + """Generate compliance-mapping.yaml linking hardening rules to framework controls.""" + output_dir = Path(args.output) + + mapping = { + "version": "1.0", + "description": ( + "Maps DevOps-OS hardening rules to compliance framework control IDs. " + "Consumed by GovPilot check_selector.py to link hardening checks into the catalog." + ), + "mappings": { + "cis-k8s": { + "1.2.1": {"title": "API server anonymous auth disabled", + "pci-dss": ["2.2"], "hipaa": ["164.312(a)(1)"], + "nist-800-53": ["AC-3", "AC-6"], "iso27001": ["A.9.4.1"]}, + "2.1": {"title": "etcd TLS peer communication", + "pci-dss": ["4.1"], "nist-800-53": ["SC-8", "SC-28"]}, + "4.2.4": {"title": "Read-only root filesystem", + "pci-dss": ["6.5.8"], "nist-800-53": ["CM-7", "SI-7"]}, + "4.2.6": {"title": "No privileged containers", + "pci-dss": ["6.5.8"], "nist-800-53": ["AC-6", "CM-7"]}, + "5.1.1": {"title": "No wildcard verbs in RBAC", + "pci-dss": ["7.1.2"], "nist-800-53": ["AC-6"], + "hipaa": ["164.312(a)(1)"]}, + "5.7.1": {"title": "Namespace network policies required", + "pci-dss": ["1.3"], "nist-800-53": ["SC-7"]}, + }, + "stig-k8s": { + "V-242383": {"title": "No host namespace sharing", + "pci-dss": ["6.5.8"], "dod": ["8500.01"], + "nist-800-53": ["AC-6", "CM-7"]}, + "V-242390": {"title": "Resource limits required", + "pci-dss": ["6.6"], "nist-800-53": ["SC-5"]}, + "V-242414": {"title": "No latest image tag", + "pci-dss": ["6.3.3"], "nist-800-53": ["CM-11"]}, + }, + "nsa-k8s": { + "pod-security": {"title": "Drop ALL capabilities, run as non-root", + "nist-800-53": ["AC-6", "CM-6"], + "pci-dss": ["6.5.8"]}, + "network": {"title": "Default deny ingress network policy", + "nist-800-53": ["SC-7"], "pci-dss": ["1.3"]}, + }, + "cis-docker": { + "1.1": {"title": "Separate /var/lib/docker partition", + "pci-dss": ["2.2.1"]}, + "2.1": {"title": "Restrict inter-container communication", + "pci-dss": ["1.3"], "nist-800-53": ["SC-7"]}, + "5.4": {"title": "No privileged containers", + "pci-dss": ["6.5.8"], "nist-800-53": ["AC-6"]}, + }, + "cis-rhel9": { + "1.1.1": {"title": "/tmp on separate partition", + "pci-dss": ["2.2.1"]}, + "3.1.1": {"title": "IP forwarding disabled", + "nist-800-53": ["CM-7"]}, + "4.1.1": {"title": "auditd installed", + "pci-dss": ["10.2"], "hipaa": ["164.312(b)"], + "nist-800-53": ["AU-2", "AU-12"]}, + "5.3.1": {"title": "Password complexity requirements", + "pci-dss": ["8.3.6"], "nist-800-53": ["IA-5"]}, + }, + "cis-ubuntu22": { + "1.1.1": {"title": "/tmp on separate partition", + "pci-dss": ["2.2.1"]}, + "3.1.1": {"title": "IP forwarding disabled", + "nist-800-53": ["CM-7"]}, + "4.1.1": {"title": "auditd installed", + "pci-dss": ["10.2"], "hipaa": ["164.312(b)"]}, + "5.3.1": {"title": "Password complexity requirements", + "pci-dss": ["8.3.6"]}, + }, + "pod-security-standards": { + "baseline": {"title": "Baseline Pod Security Standard", + "cis-k8s": ["5.2"], "nsa-k8s": ["pod-security"]}, + "restricted": {"title": "Restricted Pod Security Standard", + "cis-k8s": ["5.2"], "nsa-k8s": ["pod-security"], + "pci-dss": ["6.5.8"]}, + }, + "image-signing": { + "cosign": {"title": "Container image Cosign signature verification", + "slsa": ["L2"], "pci-dss": ["6.3.3"], + "nist-800-53": ["CM-11", "SI-7"]}, + }, + "essential-eight": { + "E8-5": {"title": "Restrict administrative privileges", + "nist-800-53": ["AC-6"], "iso27001": ["A.9.2.3"]}, + "E8-7": {"title": "Multi-factor authentication", + "pci-dss": ["8.4"], "nist-800-53": ["IA-2"], + "iso27001": ["A.9.4.2"]}, + "E8-8": {"title": "Regular backups", + "pci-dss": ["12.3.4"], "nist-800-53": ["CP-9"], + "iso27001": ["A.12.3.1"]}, + }, + }, + } + + path = _write_yaml(output_dir / "compliance-mapping.yaml", mapping) + return [path] + + +# --------------------------------------------------------------------------- +# Dispatch logic +# --------------------------------------------------------------------------- + +_KYVERNO_STANDARDS = {"cis-k8s", "stig-k8s", "nsa-k8s", "pod-security", "image-signing"} +_INSPEC_STANDARDS = {"cis-docker", "cis-rhel9", "cis-ubuntu22"} +_CHECKOV_STANDARDS = {"essential-eight"} + +_GENERATORS = { + "cis-k8s": generate_kyverno_cis_k8s, + "stig-k8s": generate_kyverno_stig_k8s, + "nsa-k8s": generate_kyverno_nsa_k8s, + "pod-security": generate_kyverno_pod_security, + "image-signing": generate_kyverno_image_signing, + "cis-docker": generate_inspec_docker_cis, + "cis-rhel9": generate_inspec_rhel9_cis, + "cis-ubuntu22": generate_inspec_ubuntu22_cis, + "essential-eight": generate_essential_eight, +} + + +def _should_generate(standard, output_type): + """Return True if this standard matches the requested output type filter.""" + if output_type == "all": + return True + if output_type == "kyverno" and standard in _KYVERNO_STANDARDS: + return True + if output_type == "inspec" and standard in _INSPEC_STANDARDS: + return True + if output_type == "checkov" and standard in _CHECKOV_STANDARDS: + return True + return False + + +def generate_hardening(args): + """Top-level dispatcher: calls the appropriate generators and returns all generated paths.""" + generated = [] + + standards = list(_GENERATORS.keys()) if args.standard == "all" else [args.standard] + + for standard in standards: + if not _should_generate(standard, args.output_type): + continue + gen_fn = _GENERATORS[standard] + generated.extend(gen_fn(args)) + + # Always generate compliance mapping when running "all" or explicitly + if args.standard == "all" or args.output_type in ("all", "kyverno", "inspec", "checkov"): + generated.extend(generate_compliance_mapping(args)) + + return generated + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +def main(): + args = parse_arguments() + generated = generate_hardening(args) + + if generated: + print(f"Hardening configs generated in '{args.output}/':") + for path in generated: + print(f" {path}") + else: + print( + f"No files generated for --standard={args.standard} --type={args.output_type}. " + "Check that the standard supports the requested output type." + ) + + +if __name__ == "__main__": + main() diff --git a/docs/devops-os-hardening-sprint.md b/docs/devops-os-hardening-sprint.md new file mode 100644 index 0000000..eb986f0 --- /dev/null +++ b/docs/devops-os-hardening-sprint.md @@ -0,0 +1,174 @@ +# DevOps-OS Hardening Sprint + +## 1. Sprint Goal + +The `devopsos scaffold hardening` command delivers production-ready infrastructure hardening configurations — Kyverno ClusterPolicies, InSpec compliance profiles, and Checkov custom checks — generated directly from the DevOps-OS CLI. It belongs in devops_os (not GovPilot) because it is a general-purpose scaffold generator that produces standard-compliant YAML and Ruby artifacts consumable by any Kubernetes cluster, container runtime, or operating system; GovPilot is the downstream consumer that ingests these artifacts via its `check_selector.py` compliance catalog linker. By centralising hardening scaffold generation in devops_os, any team can bootstrap a compliant posture in seconds, while GovPilot retains responsibility for gap-register tracking and evidence collection. + +--- + +## 2. Hardening Standards Covered + +| Standard | Target | Tool | Files Generated | +|---|---|---|---| +| CIS Kubernetes Benchmark v1.9 | Kubernetes cluster | Kyverno YAML policies | `kyverno/cis-k8s/1-master-node-config.yaml` … `5-policies.yaml` | +| CIS Docker Benchmark v1.6 | Container runtime | InSpec profile | `inspec/docker-cis/` | +| CIS RHEL 9 Benchmark | OS (RHEL/Rocky/AlmaLinux) | InSpec profile | `inspec/rhel9-cis/` | +| CIS Ubuntu 22.04 Benchmark | OS (Ubuntu) | InSpec profile | `inspec/ubuntu22-cis/` | +| DISA STIG for Kubernetes | Kubernetes cluster | Kyverno YAML policies | `kyverno/stig-k8s/stig-cluster-policies.yaml` | +| NSA/CISA Kubernetes Hardening Guide | Kubernetes cluster | Kyverno + NetworkPolicy | `kyverno/nsa-k8s/pod-security.yaml`, `network-policies.yaml` | +| Pod Security Standards (Kubernetes) | Pod admission | Kyverno ClusterPolicy | `kyverno/pod-security-standards.yaml` | +| Container Image Signing | CI/CD + admission | Kyverno + Cosign policy | `kyverno/image-signing.yaml` | +| OWASP ASVS L1 (infra layer only) | Application deployment | Kyverno + Checkov | `asvs-l1-checks/` | +| Essential Eight (Australia ASD) | General controls | InSpec + Checkov | `essential-eight/` | + +--- + +## 3. CLI Design + +```bash +# Generate CIS Kubernetes hardening policies +devopsos scaffold hardening --standard cis-k8s --output hardening/ + +# Generate DISA STIG Kubernetes policies +devopsos scaffold hardening --standard stig-k8s --output hardening/ + +# Generate OS hardening InSpec profile for RHEL 9 +devopsos scaffold hardening --standard cis-rhel9 --type inspec --output hardening/ + +# Generate all Kyverno policies for a production cluster +devopsos scaffold hardening --standard all --type kyverno --output hardening/ + +# Generate with compliance mapping (links back to GovPilot gap register) +devopsos scaffold hardening --standard cis-k8s --compliance-framework pci-dss --output hardening/ +``` + +### Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--standard` | Hardening standard: `cis-k8s`, `stig-k8s`, `nsa-k8s`, `cis-docker`, `cis-rhel9`, `cis-ubuntu22`, `pod-security`, `image-signing`, `essential-eight`, `all` | `all` | +| `--type` | Output type: `kyverno`, `inspec`, `checkov`, `all` | `all` applicable | +| `--output` | Output directory | `./hardening/` | +| `--compliance-framework` | Tag outputs with compliance framework IDs (`pci-dss`, `hipaa`, `iso27001`, `rbi`, `nist-800-53`, `soc2`) for GovPilot catalog linking | _(none)_ | +| `--severity` | Filter by minimum severity level: `critical`, `high`, `medium`, `low` | `medium` | +| `--environment` | Target environment profile: `dev`, `staging`, `production` (adjusts Kyverno `validationFailureAction`) | `production` | + +--- + +## 4. File Structure Generated + +``` +hardening/ +├── kyverno/ +│ ├── cis-k8s/ +│ │ ├── 1-master-node-config.yaml — CIS 1.x master node API server settings +│ │ ├── 2-etcd-config.yaml — CIS 2.x etcd security +│ │ ├── 3-control-plane-config.yaml — CIS 3.x control plane settings +│ │ ├── 4-worker-node-config.yaml — CIS 4.x kubelet settings +│ │ └── 5-policies.yaml — CIS 5.x policies (RBAC, secrets, networking) +│ ├── stig-k8s/ +│ │ └── stig-cluster-policies.yaml — DISA STIG rules for K8s +│ ├── nsa-k8s/ +│ │ ├── pod-security.yaml — NSA pod hardening +│ │ └── network-policies.yaml — NSA network segmentation +│ ├── pod-security-standards.yaml — Baseline/Restricted PSS enforcement +│ └── image-signing.yaml — Cosign image signature verification +├── inspec/ +│ ├── docker-cis/ +│ │ ├── inspec.yml — profile metadata +│ │ └── controls/ +│ │ ├── 1_host_configuration.rb — CIS 1.x host config checks +│ │ ├── 2_docker_daemon.rb — CIS 2.x daemon config +│ │ ├── 3_docker_daemon_files.rb — CIS 3.x file permissions +│ │ ├── 4_container_images.rb — CIS 4.x image checks +│ │ └── 5_container_runtime.rb — CIS 5.x runtime config +│ ├── rhel9-cis/ +│ │ ├── inspec.yml +│ │ └── controls/ +│ │ ├── 1_filesystem.rb — CIS 1.x filesystem config +│ │ ├── 2_services.rb — CIS 2.x inetd, special services +│ │ ├── 3_network.rb — CIS 3.x network params +│ │ ├── 4_logging.rb — CIS 4.x logging and auditing +│ │ └── 5_access.rb — CIS 5.x access, auth, sudo +│ └── ubuntu22-cis/ +│ ├── inspec.yml +│ └── controls/ (same structure as rhel9-cis) +├── essential-eight/ +│ ├── README.md — maturity levels and applicability +│ └── checkov/ +│ └── essential-eight-checks.py — Checkov custom checks for E8 +└── compliance-mapping.yaml — maps each hardening rule → compliance control IDs + (used by GovPilot check catalog) +``` + +--- + +## 5. Implementation Plan + +### Task 1 — CLI scaffold module (`cli/scaffold_hardening.py`) + +- Typer sub-app with all options (`--standard`, `--type`, `--output`, `--compliance-framework`, `--severity`, `--environment`) +- Dispatcher calls template generators per standard +- `_enforcement_action()` helper maps `--environment` → Kyverno `validationFailureAction` (`Enforce` for production, `Audit` for dev/staging) +- Registered in `cli/devopsos.py` as `scaffold_app.command("hardening")` +- ~600 lines + +### Task 2 — Kyverno policy templates (`hardening/templates/kyverno/`) + +- YAML templates for each standard (CIS K8s sections 1–5, STIG, NSA, pod security, image signing) +- Each policy `metadata.annotations` carries: + - `policies.kyverno.io/category` — standard name + - `policies.kyverno.io/severity` — severity level + - `devops-os/compliance` — comma-separated `standard:control-id` pairs for GovPilot catalog linking +- `validationFailureAction` parameterised via `--environment` + +### Task 3 — InSpec profile templates (`hardening/templates/inspec/`) + +- Skeleton InSpec profiles for Docker CIS, RHEL9 CIS, Ubuntu 22 CIS +- Each profile includes: + - `inspec.yml` with profile metadata (name, title, maintainer, version, platform support) + - `controls/*.rb` — Ruby control files referencing CIS benchmark section + compliance IDs via `tag compliance:` +- Controls structured as `control "cis--
." do … end` + +### Task 4 — Compliance mapping file (`hardening/templates/compliance-mapping.yaml`) + +- YAML file linking each hardening rule to compliance framework control IDs +- Format: `standard → rule_id → { title, framework: [control_ids] }` +- Consumed by GovPilot `check_selector.py` to link hardening checks into the catalog + +--- + +## 6. Dependencies + +| Tool | Purpose | Installation | +|------|---------|-------------| +| [Kyverno](https://kyverno.io) | Kubernetes admission control policy engine | `helm install kyverno kyverno/kyverno` | +| [InSpec](https://docs.chef.io/inspec/) | Compliance-as-code audit framework | `gem install inspec-bin` | +| [Cosign](https://docs.sigstore.dev/cosign/overview/) | Container image signing/verification | `brew install cosign` | +| [Checkov](https://www.checkov.io) | IaC static analysis + custom checks | `pip install checkov` | + +--- + +## 7. Testing Plan + +| Test | Scope | Command | +|------|-------|---------| +| Unit tests for scaffold generator | Python | `pytest tests/test_hardening_scaffold.py` | +| Validate generated Kyverno YAML | Dry-run | `kubectl apply --dry-run=client -f hardening/kyverno/` | +| Validate InSpec profiles | Profile check | `inspec check hardening/inspec/docker-cis/` | +| Verify compliance mapping YAML | Schema check | `python -c "import yaml; yaml.safe_load(open('hardening/compliance-mapping.yaml'))"` | +| End-to-end scaffold generation | Integration | `python -m cli.devopsos scaffold hardening --standard all --output /tmp/hardening-test` | + +--- + +## 8. Effort Estimate + +| Task | Description | Estimated Days | Team | +|------|-------------|---------------|------| +| Task 1 | CLI scaffold module (`cli/scaffold_hardening.py`) | 1.5 | devops-os team | +| Task 2 | Kyverno policy templates (5 standards × avg 2 files) | 2.0 | devops-os team | +| Task 3 | InSpec profile templates (3 OS × 5 controls) | 2.0 | devops-os team | +| Task 4 | Compliance mapping YAML + GovPilot integration hook | 1.0 | cel-agents team | +| Testing | Unit tests, dry-run validation, integration tests | 1.0 | devops-os team | +| Docs | Sprint doc, CLI reference update | 0.5 | devops-os team | +| **Total** | | **8.0** | | diff --git a/tests/test_hardening_scaffold.py b/tests/test_hardening_scaffold.py new file mode 100644 index 0000000..fb0ef70 --- /dev/null +++ b/tests/test_hardening_scaffold.py @@ -0,0 +1,478 @@ +""" +Unit tests for the DevOps-OS Hardening Scaffold Generator. + +Tests cover: + - Individual standard generators (CIS K8s, STIG, NSA, Pod Security, Image Signing, + Docker CIS, RHEL9 CIS, Ubuntu22 CIS, Essential Eight) + - Compliance mapping generation + - Top-level dispatcher (generate_hardening) + - Environment enforcement action logic + - Standard/type filtering logic +""" + +import argparse +import os +import sys +import tempfile +import yaml +import pytest +from pathlib import Path + +# Ensure repo root is on path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +from cli import scaffold_hardening + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _hardening_args(**kwargs): + defaults = dict( + standard="all", + output_type="all", + output=os.path.join(tempfile.gettempdir(), "test-hardening"), + compliance_framework="", + severity="medium", + environment="production", + ) + defaults.update(kwargs) + return argparse.Namespace(**defaults) + + +def _load_yaml(path): + with open(path) as fh: + return yaml.safe_load(fh) + + +# --------------------------------------------------------------------------- +# Enforcement action helper +# --------------------------------------------------------------------------- + +class TestEnforcementAction: + def test_production_returns_enforce(self): + assert scaffold_hardening._enforcement_action("production") == "Enforce" + + def test_staging_returns_audit(self): + assert scaffold_hardening._enforcement_action("staging") == "Audit" + + def test_dev_returns_audit(self): + assert scaffold_hardening._enforcement_action("dev") == "Audit" + + +# --------------------------------------------------------------------------- +# Should-generate filter +# --------------------------------------------------------------------------- + +class TestShouldGenerate: + def test_all_type_accepts_kyverno_standard(self): + assert scaffold_hardening._should_generate("cis-k8s", "all") is True + + def test_all_type_accepts_inspec_standard(self): + assert scaffold_hardening._should_generate("cis-rhel9", "all") is True + + def test_kyverno_type_accepts_kyverno_standard(self): + assert scaffold_hardening._should_generate("cis-k8s", "kyverno") is True + + def test_kyverno_type_rejects_inspec_standard(self): + assert scaffold_hardening._should_generate("cis-rhel9", "kyverno") is False + + def test_inspec_type_accepts_inspec_standard(self): + assert scaffold_hardening._should_generate("cis-docker", "inspec") is True + + def test_inspec_type_rejects_kyverno_standard(self): + assert scaffold_hardening._should_generate("stig-k8s", "inspec") is False + + def test_checkov_type_accepts_essential_eight(self): + assert scaffold_hardening._should_generate("essential-eight", "checkov") is True + + def test_checkov_type_rejects_kyverno_standard(self): + assert scaffold_hardening._should_generate("pod-security", "checkov") is False + + +# --------------------------------------------------------------------------- +# CIS Kubernetes generators +# --------------------------------------------------------------------------- + +class TestKyvernoCisK8s: + def test_generates_five_policy_files(self, tmp_path): + args = _hardening_args(output=str(tmp_path), environment="production") + paths = scaffold_hardening.generate_kyverno_cis_k8s(args) + assert len(paths) == 5 + + def test_output_files_exist(self, tmp_path): + args = _hardening_args(output=str(tmp_path), environment="production") + scaffold_hardening.generate_kyverno_cis_k8s(args) + cis_dir = tmp_path / "kyverno" / "cis-k8s" + for fname in [ + "1-master-node-config.yaml", + "2-etcd-config.yaml", + "3-control-plane-config.yaml", + "4-worker-node-config.yaml", + "5-policies.yaml", + ]: + assert (cis_dir / fname).exists(), f"Expected {fname} to be generated" + + def test_policy_kind_is_cluster_policy(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_cis_k8s(args) + policy = _load_yaml(tmp_path / "kyverno" / "cis-k8s" / "4-worker-node-config.yaml") + assert policy["kind"] == "ClusterPolicy" + + def test_production_enforcement_is_enforce(self, tmp_path): + args = _hardening_args(output=str(tmp_path), environment="production") + scaffold_hardening.generate_kyverno_cis_k8s(args) + policy = _load_yaml(tmp_path / "kyverno" / "cis-k8s" / "1-master-node-config.yaml") + assert policy["spec"]["validationFailureAction"] == "Enforce" + + def test_dev_enforcement_is_audit(self, tmp_path): + args = _hardening_args(output=str(tmp_path), environment="dev") + scaffold_hardening.generate_kyverno_cis_k8s(args) + policy = _load_yaml(tmp_path / "kyverno" / "cis-k8s" / "1-master-node-config.yaml") + assert policy["spec"]["validationFailureAction"] == "Audit" + + def test_policy_has_compliance_annotation(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_cis_k8s(args) + policy = _load_yaml(tmp_path / "kyverno" / "cis-k8s" / "4-worker-node-config.yaml") + annotations = policy["metadata"]["annotations"] + assert "devops-os/compliance" in annotations + assert "cis-k8s" in annotations["devops-os/compliance"] + + def test_policy_has_category_annotation(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_cis_k8s(args) + policy = _load_yaml(tmp_path / "kyverno" / "cis-k8s" / "5-policies.yaml") + assert "CIS Kubernetes Benchmark" in policy["metadata"]["annotations"]["policies.kyverno.io/category"] + + +# --------------------------------------------------------------------------- +# DISA STIG generators +# --------------------------------------------------------------------------- + +class TestKyvernoStigK8s: + def test_generates_one_file(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_kyverno_stig_k8s(args) + assert len(paths) == 1 + + def test_output_file_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_stig_k8s(args) + assert (tmp_path / "kyverno" / "stig-k8s" / "stig-cluster-policies.yaml").exists() + + def test_stig_policy_has_three_rules(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_stig_k8s(args) + policy = _load_yaml(tmp_path / "kyverno" / "stig-k8s" / "stig-cluster-policies.yaml") + assert len(policy["spec"]["rules"]) == 3 + + def test_stig_disallow_latest_tag_rule_present(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_stig_k8s(args) + policy = _load_yaml(tmp_path / "kyverno" / "stig-k8s" / "stig-cluster-policies.yaml") + rule_names = [r["name"] for r in policy["spec"]["rules"]] + assert "stig-disallow-latest-image-tag" in rule_names + + +# --------------------------------------------------------------------------- +# NSA generators +# --------------------------------------------------------------------------- + +class TestKyvernoNsaK8s: + def test_generates_two_files(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_kyverno_nsa_k8s(args) + assert len(paths) == 2 + + def test_pod_security_file_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_nsa_k8s(args) + assert (tmp_path / "kyverno" / "nsa-k8s" / "pod-security.yaml").exists() + + def test_network_policies_file_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_nsa_k8s(args) + assert (tmp_path / "kyverno" / "nsa-k8s" / "network-policies.yaml").exists() + + def test_nsa_pod_policy_name(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_nsa_k8s(args) + policy = _load_yaml(tmp_path / "kyverno" / "nsa-k8s" / "pod-security.yaml") + assert policy["metadata"]["name"] == "nsa-pod-security" + + +# --------------------------------------------------------------------------- +# Pod Security Standards generator +# --------------------------------------------------------------------------- + +class TestKyvernoPodSecurity: + def test_generates_one_file(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_kyverno_pod_security(args) + assert len(paths) == 1 + + def test_output_file_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_pod_security(args) + assert (tmp_path / "kyverno" / "pod-security-standards.yaml").exists() + + def test_production_uses_restricted_profile(self, tmp_path): + args = _hardening_args(output=str(tmp_path), environment="production") + scaffold_hardening.generate_kyverno_pod_security(args) + policy = _load_yaml(tmp_path / "kyverno" / "pod-security-standards.yaml") + assert "restricted" in policy["metadata"]["annotations"]["policies.kyverno.io/title"].lower() + + def test_dev_uses_baseline_profile(self, tmp_path): + args = _hardening_args(output=str(tmp_path), environment="dev") + scaffold_hardening.generate_kyverno_pod_security(args) + policy = _load_yaml(tmp_path / "kyverno" / "pod-security-standards.yaml") + assert "baseline" in policy["metadata"]["annotations"]["policies.kyverno.io/title"].lower() + + +# --------------------------------------------------------------------------- +# Image signing generator +# --------------------------------------------------------------------------- + +class TestKyvernoImageSigning: + def test_generates_one_file(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_kyverno_image_signing(args) + assert len(paths) == 1 + + def test_output_file_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_image_signing(args) + assert (tmp_path / "kyverno" / "image-signing.yaml").exists() + + def test_policy_has_verify_images_rule(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_kyverno_image_signing(args) + policy = _load_yaml(tmp_path / "kyverno" / "image-signing.yaml") + rules = policy["spec"]["rules"] + assert any("verifyImages" in r for r in rules) + + +# --------------------------------------------------------------------------- +# InSpec Docker CIS +# --------------------------------------------------------------------------- + +class TestInspecDockerCis: + def test_generates_six_files(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_inspec_docker_cis(args) + assert len(paths) == 6 # inspec.yml + 5 controls + + def test_inspec_yml_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_inspec_docker_cis(args) + assert (tmp_path / "inspec" / "docker-cis" / "inspec.yml").exists() + + def test_inspec_yml_has_correct_name(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_inspec_docker_cis(args) + content = (tmp_path / "inspec" / "docker-cis" / "inspec.yml").read_text() + assert "name: docker-cis" in content + + def test_all_control_files_exist(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_inspec_docker_cis(args) + controls_dir = tmp_path / "inspec" / "docker-cis" / "controls" + for fname in [ + "1_host_configuration.rb", + "2_docker_daemon.rb", + "3_docker_daemon_files.rb", + "4_container_images.rb", + "5_container_runtime.rb", + ]: + assert (controls_dir / fname).exists() + + def test_control_file_has_inspec_controls(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_inspec_docker_cis(args) + content = (tmp_path / "inspec" / "docker-cis" / "controls" / "1_host_configuration.rb").read_text() + assert "control" in content + assert "cis-docker-1" in content + + +# --------------------------------------------------------------------------- +# InSpec RHEL9 CIS +# --------------------------------------------------------------------------- + +class TestInspecRhel9Cis: + def test_generates_six_files(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_inspec_rhel9_cis(args) + assert len(paths) == 6 + + def test_inspec_yml_has_correct_name(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_inspec_rhel9_cis(args) + content = (tmp_path / "inspec" / "rhel9-cis" / "inspec.yml").read_text() + assert "name: rhel9-cis" in content + + def test_logging_control_has_auditd_check(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_inspec_rhel9_cis(args) + content = (tmp_path / "inspec" / "rhel9-cis" / "controls" / "4_logging.rb").read_text() + assert "auditd" in content + + def test_network_control_has_ip_forward_check(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_inspec_rhel9_cis(args) + content = (tmp_path / "inspec" / "rhel9-cis" / "controls" / "3_network.rb").read_text() + assert "net.ipv4.ip_forward" in content + + +# --------------------------------------------------------------------------- +# InSpec Ubuntu 22 CIS +# --------------------------------------------------------------------------- + +class TestInspecUbuntu22Cis: + def test_generates_six_files(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_inspec_ubuntu22_cis(args) + assert len(paths) == 6 + + def test_inspec_yml_has_correct_name(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_inspec_ubuntu22_cis(args) + content = (tmp_path / "inspec" / "ubuntu22-cis" / "inspec.yml").read_text() + assert "name: ubuntu22-cis" in content + + def test_all_control_files_exist(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_inspec_ubuntu22_cis(args) + controls_dir = tmp_path / "inspec" / "ubuntu22-cis" / "controls" + for fname in ["1_filesystem.rb", "2_services.rb", "3_network.rb", "4_logging.rb", "5_access.rb"]: + assert (controls_dir / fname).exists() + + +# --------------------------------------------------------------------------- +# Essential Eight +# --------------------------------------------------------------------------- + +class TestEssentialEight: + def test_generates_two_files(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_essential_eight(args) + assert len(paths) == 2 + + def test_readme_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_essential_eight(args) + assert (tmp_path / "essential-eight" / "README.md").exists() + + def test_readme_has_maturity_levels(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_essential_eight(args) + content = (tmp_path / "essential-eight" / "README.md").read_text() + assert "Maturity" in content + + def test_checkov_checks_file_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_essential_eight(args) + assert (tmp_path / "essential-eight" / "checkov" / "essential-eight-checks.py").exists() + + def test_checkov_checks_has_e8_classes(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_essential_eight(args) + content = (tmp_path / "essential-eight" / "checkov" / "essential-eight-checks.py").read_text() + assert "E8RestrictAdminPrivileges" in content + assert "E8MultiFactorAuthentication" in content + assert "E8RegularBackups" in content + + +# --------------------------------------------------------------------------- +# Compliance mapping +# --------------------------------------------------------------------------- + +class TestComplianceMapping: + def test_generates_one_file(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_compliance_mapping(args) + assert len(paths) == 1 + + def test_output_file_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_compliance_mapping(args) + assert (tmp_path / "compliance-mapping.yaml").exists() + + def test_mapping_is_valid_yaml(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_compliance_mapping(args) + mapping = _load_yaml(tmp_path / "compliance-mapping.yaml") + assert mapping is not None + assert "mappings" in mapping + + def test_mapping_covers_all_standards(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_compliance_mapping(args) + mapping = _load_yaml(tmp_path / "compliance-mapping.yaml") + for standard in ["cis-k8s", "stig-k8s", "nsa-k8s", "cis-docker", + "cis-rhel9", "cis-ubuntu22", "essential-eight"]: + assert standard in mapping["mappings"], f"Standard {standard} missing from compliance mapping" + + def test_mapping_entries_have_title(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_compliance_mapping(args) + mapping = _load_yaml(tmp_path / "compliance-mapping.yaml") + for control in mapping["mappings"]["cis-k8s"].values(): + assert "title" in control + + +# --------------------------------------------------------------------------- +# Top-level dispatcher +# --------------------------------------------------------------------------- + +class TestGenerateHardening: + def test_all_standard_generates_files(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="all", output_type="all") + paths = scaffold_hardening.generate_hardening(args) + assert len(paths) > 0 + + def test_single_standard_generates_only_that_standard(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="stig-k8s", output_type="all") + scaffold_hardening.generate_hardening(args) + assert (tmp_path / "kyverno" / "stig-k8s" / "stig-cluster-policies.yaml").exists() + assert not (tmp_path / "kyverno" / "cis-k8s").exists() + + def test_kyverno_type_filter_skips_inspec(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="all", output_type="kyverno") + scaffold_hardening.generate_hardening(args) + assert not (tmp_path / "inspec").exists() + + def test_inspec_type_filter_skips_kyverno(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="all", output_type="inspec") + scaffold_hardening.generate_hardening(args) + assert not (tmp_path / "kyverno").exists() + + def test_all_standards_creates_compliance_mapping(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="all", output_type="all") + scaffold_hardening.generate_hardening(args) + assert (tmp_path / "compliance-mapping.yaml").exists() + + def test_empty_result_for_mismatched_standard_and_type(self, tmp_path): + # cis-rhel9 is InSpec only, but we request kyverno type + args = _hardening_args(output=str(tmp_path), standard="cis-rhel9", output_type="kyverno") + paths = scaffold_hardening.generate_hardening(args) + # Only compliance mapping should be generated (no standard-specific files) + kyverno_dir = tmp_path / "kyverno" + inspec_dir = tmp_path / "inspec" + assert not kyverno_dir.exists() + assert not inspec_dir.exists() + + def test_production_environment_enforces_kyverno_policies(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="stig-k8s", + output_type="kyverno", environment="production") + scaffold_hardening.generate_hardening(args) + policy = _load_yaml(tmp_path / "kyverno" / "stig-k8s" / "stig-cluster-policies.yaml") + assert policy["spec"]["validationFailureAction"] == "Enforce" + + def test_staging_environment_audits_kyverno_policies(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="stig-k8s", + output_type="kyverno", environment="staging") + scaffold_hardening.generate_hardening(args) + policy = _load_yaml(tmp_path / "kyverno" / "stig-k8s" / "stig-cluster-policies.yaml") + assert policy["spec"]["validationFailureAction"] == "Audit" From 00a173e055e22aade8c7226f60adcf0ed33a7c5c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Jul 2026 12:50:42 +0000 Subject: [PATCH 3/7] docs: add hardening documentation coverage --- README.md | 16 +++- docs/CLI-COMMANDS-REFERENCE.md | 49 ++++++++++ hugo-docs/content/_index.md | 11 ++- .../content/docs/getting-started/_index.md | 31 ++++-- .../docs/getting-started/quickstart.md | 22 +++++ .../docs/platform-engineering/_index.md | 1 + .../docs/platform-engineering/hardening.md | 95 +++++++++++++++++++ hugo-docs/content/docs/reference/_index.md | 29 ++++++ 8 files changed, 243 insertions(+), 11 deletions(-) create mode 100644 hugo-docs/content/docs/platform-engineering/hardening.md diff --git a/README.md b/README.md index f9c2dee..7f54748 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # 🚀 DevOps-OS -**Automate your entire DevOps lifecycle — from CI/CD pipelines to Kubernetes deployments and SRE dashboards — using a conversational AI assistant or a single CLI command.** +**Automate your entire DevOps lifecycle — from CI/CD pipelines to Kubernetes deployments, infrastructure hardening baselines, and SRE dashboards — using a conversational AI assistant or a single CLI command.** [![CI](https://github.com/cloudengine-labs/devops_os/actions/workflows/ci.yml/badge.svg)](https://github.com/cloudengine-labs/devops_os/actions/workflows/ci.yml) [![Sanity Tests](https://github.com/cloudengine-labs/devops_os/actions/workflows/sanity.yml/badge.svg)](https://github.com/cloudengine-labs/devops_os/actions/workflows/sanity.yml) @@ -29,6 +29,7 @@ DevOps-OS is an open-source DevOps automation platform that scaffolds production | 🚀 **CI/CD Generators** | One-command scaffolding for GitHub Actions, GitLab CI, and Jenkins pipelines | | ☸️ **GitOps Config Generator** | Kubernetes manifests, ArgoCD Applications, and Flux CD Kustomizations | | 📊 **SRE Config Generator** | Prometheus alert rules, Grafana dashboards, and SLO manifests | +| 🔐 **Infrastructure Hardening** | Generate Kyverno policies, InSpec profiles, Checkov checks, and compliance mappings for CIS, STIG, NSA/CISA, Pod Security Standards, and Essential Eight baselines | | 🧪 **Unit Test Scaffold** | Generate pytest, Jest, Vitest, Mocha, or Go test configs with one command | | 🤖 **MCP Server** | Plug DevOps-OS tools into Claude or ChatGPT as native AI skills | | 🛠️ **Dev Container** | Pre-configured multi-language environment (Python · Java · Go · JavaScript) | @@ -179,6 +180,9 @@ python -m cli.devopsos scaffold argocd --name my-app --method flux --repo https: # SRE configs (Prometheus, Grafana, SLO) → sre/ directory python -m cli.devopsos scaffold sre --name my-app --team platform --slo-target 99.9 +# Infrastructure hardening baselines → hardening/ directory +python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production + # Dev container configuration → .devcontainer/devcontainer.json + .devcontainer/devcontainer.env.json python -m cli.devopsos scaffold devcontainer --languages python,go --cicd-tools docker,terraform --kubernetes-tools k9s,flux @@ -195,7 +199,8 @@ python kubernetes/k8s-config-generator.py --name my-app --image ghcr.io/myorg/my Use `python -m cli.devopsos scaffold --help` to list all available targets and `python -m cli.devopsos scaffold --help` to see every option for a specific target. -> See [CLI Commands Reference](docs/CLI-COMMANDS-REFERENCE.md) for the full option tables and every default output path. +> See [CLI Commands Reference](docs/CLI-COMMANDS-REFERENCE.md) for the full option tables and every default output path. +> For hardening-specific standards, outputs, and examples, see [Infrastructure Hardening Sprint](docs/devops-os-hardening-sprint.md). --- @@ -274,6 +279,10 @@ python -m cli.devopsos scaffold argocd --name my-app --method flux --repo https: # ── SRE (Prometheus + Grafana + SLO) ────────────────────────────────────── python -m cli.devopsos scaffold sre --name my-app --team platform --slo-target 99.9 +# ── Infrastructure Hardening ─────────────────────────────────────────────── +python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production +python -m cli.devopsos scaffold hardening --standard all --output hardening + # ── Dev Container ────────────────────────────────────────────────────────── python -m cli.devopsos scaffold devcontainer --languages python,go --cicd-tools docker,terraform @@ -305,7 +314,7 @@ python -m cli.devopsos scaffold gha --help devops_os/ ├── .devcontainer/ # Dev container config (Dockerfile, devcontainer.json, setup scripts) ├── .github/workflows/ # CI, Sanity Tests, and GitHub Pages workflows -├── cli/ # CLI scaffold tools (scaffold_gha, gitlab, jenkins, argocd, sre, unittest, devopsos) +├── cli/ # CLI scaffold tools (gha, gitlab, jenkins, argocd, sre, hardening, unittest, devopsos) ├── kubernetes/ # Kubernetes manifest generator ├── mcp_server/ # MCP server for AI assistant integration (Claude, ChatGPT) ├── skills/ # Claude & OpenAI tool/function definitions @@ -392,6 +401,7 @@ You can also customize `.devcontainer/devcontainer.env.json` directly to enable | [🔧 Jenkins Pipeline Generator](docs/JENKINS-PIPELINE-README.md) | Generate and customize Jenkins pipelines | | [🔄 ArgoCD / Flux GitOps](docs/ARGOCD-README.md) | Generate ArgoCD Applications and Flux Kustomizations | | [📊 SRE Configuration](docs/SRE-CONFIGURATION-README.md) | Prometheus rules, Grafana dashboards, SLO manifests | +| [🔐 Infrastructure Hardening](docs/devops-os-hardening-sprint.md) | Standards, output layout, and CLI examples for the hardening scaffold | | [🧪 Unit Test Scaffold](docs/CLI-COMMANDS-REFERENCE.md#devopsos-scaffold-unittest--unit-test-scaffold-generator) | Generate pytest, Jest, Vitest, Mocha, or Go test configs | | [☸️ Kubernetes Deployments](docs/KUBERNETES-DEPLOYMENT-README.md) | Generate and manage Kubernetes deployment configs | | [🤖 MCP Server](mcp_server/README.md) | Connect DevOps-OS tools to Claude or ChatGPT | diff --git a/docs/CLI-COMMANDS-REFERENCE.md b/docs/CLI-COMMANDS-REFERENCE.md index 469726c..c03c02b 100644 --- a/docs/CLI-COMMANDS-REFERENCE.md +++ b/docs/CLI-COMMANDS-REFERENCE.md @@ -15,6 +15,7 @@ All scaffold commands are available through the **unified `devopsos` CLI** — o - [devopsos scaffold gitlab — GitLab CI Generator](#devopsos-scaffold-gitlab--gitlab-ci-generator) - [devopsos scaffold jenkins — Jenkins Pipeline Generator](#devopsos-scaffold-jenkins--jenkins-pipeline-generator) - [devopsos scaffold argocd — ArgoCD / Flux CD Generator](#devopsos-scaffold-argocd--argocd--flux-cd-generator) +- [devopsos scaffold hardening — Infrastructure Hardening Generator](#devopsos-scaffold-hardening--infrastructure-hardening-generator) - [devopsos scaffold sre — SRE Config Generator](#devopsos-scaffold-sre--sre-config-generator) - [devopsos scaffold devcontainer — Dev Container Generator](#devopsos-scaffold-devcontainer--dev-container-generator) - [devopsos scaffold cicd — Combined CI/CD Generator](#devopsos-scaffold-cicd--combined-cicd-generator) @@ -55,6 +56,7 @@ python -m cli.devopsos scaffold gha --help # GHA-specific options | Jenkins | `python -m cli.devopsos scaffold jenkins` | `Jenkinsfile` | | ArgoCD | `python -m cli.devopsos scaffold argocd` | `argocd/` directory | | Flux CD | `python -m cli.devopsos scaffold argocd --method flux` | `flux/` directory | +| Infrastructure hardening | `python -m cli.devopsos scaffold hardening` | `hardening/` directory | | SRE configs | `python -m cli.devopsos scaffold sre` | `sre/` directory | | Dev Container | `python -m cli.devopsos scaffold devcontainer` | `.devcontainer/` directory | | Unit Tests | `python -m cli.devopsos scaffold unittest` | `unittest/` directory | @@ -227,6 +229,52 @@ python -m cli.devopsos scaffold argocd --name my-app --method flux --image ghcr. --- +## devopsos scaffold hardening — Infrastructure Hardening Generator + +Generates infrastructure hardening artifacts for Kubernetes, container runtimes, and operating systems using Kyverno, InSpec, and Checkov-compatible outputs. + +### Invocation + +```bash +python -m cli.devopsos scaffold hardening [options] +``` + +### Options + +| Option | Env var | Default | Description | +|--------|---------|---------|-------------| +| `--standard STANDARD` | `DEVOPS_OS_HARDENING_STANDARD` | `all` | Hardening standard: `cis-k8s` \| `stig-k8s` \| `nsa-k8s` \| `cis-docker` \| `cis-rhel9` \| `cis-ubuntu22` \| `pod-security` \| `image-signing` \| `essential-eight` \| `all` | +| `--type TYPE` | `DEVOPS_OS_HARDENING_TYPE` | `all` | Output type: `kyverno` \| `inspec` \| `checkov` \| `all` | +| `--output DIR` | `DEVOPS_OS_HARDENING_OUTPUT` | `hardening` | Output directory | +| `--compliance-framework FRAMEWORK` | `DEVOPS_OS_HARDENING_COMPLIANCE_FRAMEWORK` | _(none)_ | Tag outputs for `pci-dss`, `hipaa`, `iso27001`, `rbi`, `nist-800-53`, or `soc2` catalog mapping | +| `--severity LEVEL` | `DEVOPS_OS_HARDENING_SEVERITY` | `medium` | Minimum severity: `critical` \| `high` \| `medium` \| `low` | +| `--environment ENV` | `DEVOPS_OS_HARDENING_ENVIRONMENT` | `production` | Target profile: `dev` \| `staging` \| `production` | + +### Output files + +| Path | Description | +|------|-------------| +| `/kyverno/cis-k8s/` | CIS Kubernetes benchmark policies | +| `/kyverno/stig-k8s/` | DISA STIG Kubernetes policies | +| `/kyverno/nsa-k8s/` | NSA/CISA Kubernetes hardening policies | +| `/kyverno/pod-security-standards.yaml` | Pod Security Standards policy | +| `/kyverno/image-signing.yaml` | Cosign/Kyverno image-signing policy | +| `/inspec/docker-cis/` | CIS Docker Benchmark InSpec profile | +| `/inspec/rhel9-cis/` | CIS RHEL 9 InSpec profile | +| `/inspec/ubuntu22-cis/` | CIS Ubuntu 22.04 InSpec profile | +| `/essential-eight/` | Essential Eight README and Checkov checks | +| `/compliance-mapping.yaml` | Rule-to-framework mapping file | + +### Examples + +```bash +python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production +python -m cli.devopsos scaffold hardening --standard cis-rhel9 --type inspec --output baseline-hardening +python -m cli.devopsos scaffold hardening --standard all --compliance-framework pci-dss +``` + +--- + ## devopsos scaffold sre — SRE Config Generator Generates Prometheus alert rules, Grafana dashboard, SLO manifest, and Alertmanager config. @@ -479,6 +527,7 @@ Every flag for every command has a corresponding environment variable. The prefi | `scaffold gitlab` | `DEVOPS_OS_GITLAB_` | `DEVOPS_OS_GITLAB_LANGUAGES=python,go` | | `scaffold jenkins` | `DEVOPS_OS_JENKINS_` | `DEVOPS_OS_JENKINS_KUBERNETES=true` | | `scaffold argocd` | `DEVOPS_OS_ARGOCD_` | `DEVOPS_OS_ARGOCD_AUTO_SYNC=true` | +| `scaffold hardening` | `DEVOPS_OS_HARDENING_` | `DEVOPS_OS_HARDENING_STANDARD=cis-k8s` | | `scaffold sre` | `DEVOPS_OS_SRE_` | `DEVOPS_OS_SRE_SLO_TARGET=99.5` | | `scaffold devcontainer` | `DEVOPS_OS_DEVCONTAINER_` | `DEVOPS_OS_DEVCONTAINER_LANGUAGES=python,go` | | `scaffold unittest` | `DEVOPS_OS_UNITTEST_` | `DEVOPS_OS_UNITTEST_LANGUAGES=python,go` | diff --git a/hugo-docs/content/_index.md b/hugo-docs/content/_index.md index 67ebe21..76cbb1f 100644 --- a/hugo-docs/content/_index.md +++ b/hugo-docs/content/_index.md @@ -5,7 +5,7 @@ type: "docs" # 🚀 DevOps-OS -**Automate your entire DevOps lifecycle — from CI/CD pipelines to Kubernetes deployments and SRE dashboards — using a conversational AI assistant or a single CLI command.** +**Automate your entire DevOps lifecycle — from CI/CD pipelines to Kubernetes deployments, infrastructure hardening baselines, and SRE dashboards — using a conversational AI assistant or a single CLI command.** [![CI](https://github.com/cloudengine-labs/devops_os/actions/workflows/ci.yml/badge.svg)](https://github.com/cloudengine-labs/devops_os/actions/workflows/ci.yml) [![Sanity Tests](https://github.com/cloudengine-labs/devops_os/actions/workflows/sanity.yml/badge.svg)](https://github.com/cloudengine-labs/devops_os/actions/workflows/sanity.yml) @@ -26,6 +26,7 @@ DevOps-OS is an open-source DevOps automation platform that scaffolds production | 🚀 | **CI/CD Generators** | One-command scaffolding for GitHub Actions, GitLab CI, and Jenkins pipelines — [→ CI/CD Generators]({{< relref "/docs/ci-cd" >}}) | | ☸️ | **GitOps Config Generator** | Kubernetes manifests, ArgoCD Applications, and Flux CD Kustomizations — [→ GitOps & ArgoCD]({{< relref "/docs/gitops" >}}) | | 📊 | **SRE Config Generator** | Prometheus alert rules, Grafana dashboards, and SLO manifests — [→ SRE Configuration]({{< relref "/docs/sre" >}}) | +| 🔐 | **Infrastructure Hardening** | Generate Kyverno policies, InSpec profiles, Checkov checks, and compliance mappings — [→ Infrastructure Hardening]({{< relref "/docs/platform-engineering/hardening" >}}) | | 🤖 | **MCP Server** | Plug DevOps-OS tools into Claude or ChatGPT as native AI skills — [→ AI Integration]({{< relref "/docs/ai-integration" >}}) | | 🛠️ | **Dev Container** | Pre-configured multi-language environment: Python · Java · Go · JavaScript — [→ Dev Container]({{< relref "/docs/dev-container" >}}) | | 🔄 | **Process-First** | Built-in education on the Process-First SDLC philosophy and how every tool maps to an SDLC principle — [→ Process-First guide]({{< relref "/docs/getting-started/process-first" >}}) | @@ -52,7 +53,11 @@ python -m cli.devopsos scaffold gha --name my-app --languages python,javascript python -m cli.devopsos scaffold gitlab --name my-app --languages python --type complete # Output: .gitlab-ci.yml -# 5. Generate SRE configs (Prometheus + Grafana + SLO) +# 5. Generate infrastructure hardening configs +python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production +# Output: hardening/ directory + +# 6. Generate SRE configs (Prometheus + Grafana + SLO) python -m cli.devopsos scaffold sre --name my-app --team platform # Output: sre/ directory ``` @@ -69,6 +74,7 @@ python -m cli.devopsos scaffold sre --name my-app --team platform | **CI/CD** | GitHub Actions, GitLab CI, Jenkins | | **GitOps / Deploy** | ArgoCD, Flux CD, kubectl, Kustomize | | **Containers** | Docker, Helm | +| **Hardening / Compliance** | Kyverno, InSpec, Checkov, Pod Security Standards, CIS / STIG / NSA baselines | | **SRE / Observability** | Prometheus, Grafana, SLO (Sloth-compatible) | | **AI Integration** | Claude MCP Server, OpenAI function calling | | **Languages** | Python · Java · Go · JavaScript / TypeScript | @@ -81,6 +87,7 @@ python -m cli.devopsos scaffold sre --name my-app --team platform |-------|-------------| | [Getting Started]({{< relref "/docs/getting-started" >}}) | Zero to first pipeline in 5 minutes | | [Platform Engineering IDP]({{< relref "/docs/platform-engineering" >}}) | Conceptual self-service IDP flow from templates to generated automation | +| [Infrastructure Hardening]({{< relref "/docs/platform-engineering/hardening" >}}) | Generate hardening baselines and compliance mappings | | [Process-First Philosophy]({{< relref "/docs/getting-started/process-first" >}}) | What Process-First means, how it maps to DevOps-OS, and AI learning tips | | [Quick Start Reference]({{< relref "/docs/getting-started/quickstart" >}}) | All CLI commands at a glance | | [GitHub Actions]({{< relref "/docs/ci-cd/github-actions" >}}) | Generate GHA workflows | diff --git a/hugo-docs/content/docs/getting-started/_index.md b/hugo-docs/content/docs/getting-started/_index.md index a6b189f..27fea35 100644 --- a/hugo-docs/content/docs/getting-started/_index.md +++ b/hugo-docs/content/docs/getting-started/_index.md @@ -12,13 +12,14 @@ Welcome! This guide walks you through DevOps-OS from **zero to your first genera ## What is DevOps-OS? -DevOps-OS is a toolkit that generates production-ready CI/CD pipelines, Kubernetes manifests, and SRE monitoring configs — so you can stop writing boilerplate and start shipping. +DevOps-OS is a toolkit that generates production-ready CI/CD pipelines, Kubernetes manifests, infrastructure hardening baselines, and SRE monitoring configs — so you can stop writing boilerplate and start shipping. | Category | Tools | |----------|-------| | CI/CD | GitHub Actions, GitLab CI, Jenkins | | GitOps / Deploy | ArgoCD, Flux CD, kubectl, Kustomize | | Containers | Docker, Helm | +| Hardening / Compliance | Kyverno policies, InSpec profiles, Checkov checks, compliance mappings | | SRE / Observability | Prometheus alert rules, Grafana dashboards, SLO configs | | Unit Testing | pytest, Jest, Vitest, Mocha, Go test | | AI Integration | Claude (MCP Server), OpenAI (function calling) | @@ -128,7 +129,23 @@ python -m cli.devopsos scaffold argocd --name my-app --method flux \ --- -## 5 — Generate SRE configs +## 5 — Generate infrastructure hardening baselines + +```bash +# Kubernetes policy baselines +python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production + +# Operating system compliance profiles +python -m cli.devopsos scaffold hardening --standard cis-rhel9 --type inspec +``` + +**Output:** `hardening/` directory containing Kyverno policies, InSpec profiles, Checkov checks, and a compliance mapping file. + +See [Infrastructure Hardening]({{< relref "/docs/platform-engineering/hardening" >}}) for supported standards and validation examples. + +--- + +## 6 — Generate SRE configs ```bash python -m cli.devopsos scaffold sre --name my-app --team platform @@ -142,7 +159,7 @@ python -m cli.devopsos scaffold sre --name my-app --team platform --- -## 6 — Generate unit test configs +## 7 — Generate unit test configs ```bash # Python — generates pytest.ini, conftest.py, and a sample test file @@ -162,7 +179,7 @@ See [CLI Reference]({{< relref "/docs/reference" >}}) for all options and output --- -## 7 — Interactive wizard (all-in-one) +## 8 — Interactive wizard (all-in-one) ```bash python -m cli.devopsos init # interactive project configurator @@ -170,6 +187,7 @@ python -m cli.devopsos scaffold gha # scaffold GitHub Actions python -m cli.devopsos scaffold gitlab # scaffold GitLab CI python -m cli.devopsos scaffold jenkins # scaffold Jenkins python -m cli.devopsos scaffold argocd # scaffold ArgoCD / Flux +python -m cli.devopsos scaffold hardening # scaffold hardening baselines python -m cli.devopsos scaffold sre # scaffold SRE configs python -m cli.devopsos scaffold cicd # scaffold GHA + Jenkins in one step python -m cli.devopsos scaffold unittest # scaffold unit test configs @@ -177,7 +195,7 @@ python -m cli.devopsos scaffold unittest # scaffold unit test configs --- -## 8 — Use with an AI assistant +## 9 — Use with an AI assistant ```bash pip install -r mcp_server/requirements.txt @@ -202,12 +220,13 @@ Then ask Claude: *"Generate a complete GitLab CI pipeline for a Python Flask API --- -## Next steps +## 10 — Next steps | I want to… | Read | |-----------|------| | Understand the Process-First philosophy | [Process-First guide]({{< relref "/docs/getting-started/process-first" >}}) | | See every CLI option and output path | [CLI Reference]({{< relref "/docs/reference" >}}) | +| Generate infrastructure hardening baselines | [Infrastructure Hardening]({{< relref "/docs/platform-engineering/hardening" >}}) | | Deep-dive GitHub Actions | [GitHub Actions]({{< relref "/docs/ci-cd/github-actions" >}}) | | Deep-dive GitLab CI | [GitLab CI]({{< relref "/docs/ci-cd/gitlab-ci" >}}) | | Deep-dive Jenkins | [Jenkins]({{< relref "/docs/ci-cd/jenkins" >}}) | diff --git a/hugo-docs/content/docs/getting-started/quickstart.md b/hugo-docs/content/docs/getting-started/quickstart.md index 5ccd7d9..525a7af 100644 --- a/hugo-docs/content/docs/getting-started/quickstart.md +++ b/hugo-docs/content/docs/getting-started/quickstart.md @@ -143,6 +143,26 @@ python -m cli.devopsos scaffold argocd --name my-app --method flux \ --- +## Infrastructure Hardening + +```bash +# CIS Kubernetes policies +# Output: hardening/kyverno/cis-k8s/ +python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production + +# CIS RHEL 9 InSpec profile +# Output: hardening/inspec/rhel9-cis/ +python -m cli.devopsos scaffold hardening --standard cis-rhel9 --type inspec + +# Full baseline set with compliance mapping +# Output: hardening/ +python -m cli.devopsos scaffold hardening --standard all --compliance-framework pci-dss +``` + +See the [Infrastructure Hardening guide]({{< relref "/docs/platform-engineering/hardening" >}}) for supported standards and output structure. + +--- + ## SRE Configuration ```bash @@ -209,6 +229,7 @@ python -m cli.devopsos scaffold gha --help python -m cli.devopsos scaffold gitlab --help python -m cli.devopsos scaffold jenkins --help python -m cli.devopsos scaffold argocd --help +python -m cli.devopsos scaffold hardening --help python -m cli.devopsos scaffold sre --help python -m cli.devopsos scaffold devcontainer --help python -m cli.devopsos process-first --help @@ -219,6 +240,7 @@ cat .gitlab-ci.yml # GitLab CI cat Jenkinsfile # Jenkins ls -la argocd/ # ArgoCD ls -la flux/ # Flux CD +ls -la hardening/ # Infrastructure hardening ls -la sre/ # SRE configs ls -la .devcontainer/ # Dev container ``` diff --git a/hugo-docs/content/docs/platform-engineering/_index.md b/hugo-docs/content/docs/platform-engineering/_index.md index 8519e4b..1f1c625 100644 --- a/hugo-docs/content/docs/platform-engineering/_index.md +++ b/hugo-docs/content/docs/platform-engineering/_index.md @@ -85,6 +85,7 @@ DevOps-OS can be used as a lightweight **internal developer platform (IDP)** exp |-------|---------------------------| | Build & Test | [GitHub Actions / GitLab CI / Jenkins workflows]({{< relref "/docs/ci-cd" >}}) | | Deploy | [ArgoCD or Flux GitOps configuration]({{< relref "/docs/gitops" >}}) | +| Harden | [Infrastructure hardening baselines and compliance mappings]({{< relref "/docs/platform-engineering/hardening" >}}) | | Observe | [Prometheus, Grafana, and SLO configuration]({{< relref "/docs/sre" >}}) | | Developer Environment | [Dev Container configuration]({{< relref "/docs/dev-container" >}}) | diff --git a/hugo-docs/content/docs/platform-engineering/hardening.md b/hugo-docs/content/docs/platform-engineering/hardening.md new file mode 100644 index 0000000..cd082bb --- /dev/null +++ b/hugo-docs/content/docs/platform-engineering/hardening.md @@ -0,0 +1,95 @@ +--- +title: "Infrastructure Hardening" +weight: 16 +--- + +# Infrastructure Hardening + +DevOps-OS includes `python -m cli.devopsos scaffold hardening` to generate reusable hardening baselines for Kubernetes clusters, container runtimes, and operating systems. + +--- + +## What it generates + +| Output type | Purpose | Default location | +|-------------|---------|------------------| +| Kyverno policies | Kubernetes admission guardrails for CIS, STIG, NSA/CISA, Pod Security Standards, and image signing | `hardening/kyverno/` | +| InSpec profiles | Compliance profiles for Docker, RHEL 9, and Ubuntu 22.04 | `hardening/inspec/` | +| Checkov checks | Essential Eight checks and supporting metadata | `hardening/essential-eight/` | +| Compliance mapping | Rule-to-framework mapping for catalog linking | `hardening/compliance-mapping.yaml` | + +--- + +## Supported standards + +| Standard | Primary output | +|----------|----------------| +| CIS Kubernetes Benchmark v1.9 | Kyverno policies | +| DISA STIG for Kubernetes | Kyverno policies | +| NSA/CISA Kubernetes Hardening Guide | Kyverno policies + network policies | +| Pod Security Standards | Kyverno policy | +| Container image signing | Kyverno policy | +| CIS Docker Benchmark v1.6 | InSpec profile | +| CIS RHEL 9 Benchmark | InSpec profile | +| CIS Ubuntu 22.04 Benchmark | InSpec profile | +| Essential Eight | Checkov checks + README | + +--- + +## Quick start + +```bash +# CIS Kubernetes guardrails +python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production + +# Operating system baseline +python -m cli.devopsos scaffold hardening --standard cis-rhel9 --type inspec + +# Full baseline set with compliance tagging +python -m cli.devopsos scaffold hardening --standard all --compliance-framework pci-dss +``` + +--- + +## Key options + +| Option | Default | Description | +|--------|---------|-------------| +| `--standard` | `all` | Select a single baseline or generate all supported standards | +| `--type` | `all` | Limit output to `kyverno`, `inspec`, `checkov`, or generate all applicable artifacts | +| `--output` | `hardening` | Root directory for generated artifacts | +| `--compliance-framework` | _(none)_ | Add compliance control IDs for `pci-dss`, `hipaa`, `iso27001`, `rbi`, `nist-800-53`, or `soc2` | +| `--severity` | `medium` | Filter generated rules by minimum severity | +| `--environment` | `production` | Use `production` for `Enforce`; `dev` and `staging` generate `Audit` mode policies | + +--- + +## Typical output layout + +```text +hardening/ +├── kyverno/ +│ ├── cis-k8s/ +│ ├── stig-k8s/ +│ ├── nsa-k8s/ +│ ├── pod-security-standards.yaml +│ └── image-signing.yaml +├── inspec/ +│ ├── docker-cis/ +│ ├── rhel9-cis/ +│ └── ubuntu22-cis/ +├── essential-eight/ +└── compliance-mapping.yaml +``` + +--- + +## Validation examples + +```bash +pytest tests/test_hardening_scaffold.py +kubectl apply --dry-run=client -f hardening/kyverno/ +python -c "import yaml; yaml.safe_load(open('hardening/compliance-mapping.yaml'))" +``` + +For the full option table, environment variables, and examples, see the [CLI Reference]({{< relref "/docs/reference" >}}). diff --git a/hugo-docs/content/docs/reference/_index.md b/hugo-docs/content/docs/reference/_index.md index 6278600..c60600e 100644 --- a/hugo-docs/content/docs/reference/_index.md +++ b/hugo-docs/content/docs/reference/_index.md @@ -19,6 +19,7 @@ Complete reference for every DevOps-OS CLI command: options, default values, env | Jenkins | `python -m cli.devopsos scaffold jenkins` | `Jenkinsfile` | | ArgoCD | `python -m cli.devopsos scaffold argocd` | `argocd/` directory | | Flux CD | `python -m cli.devopsos scaffold argocd --method flux` | `flux/` directory | +| Infrastructure hardening | `python -m cli.devopsos scaffold hardening` | `hardening/` directory | | SRE configs | `python -m cli.devopsos scaffold sre` | `sre/` directory | | Dev Container | `python -m cli.devopsos scaffold devcontainer` | `.devcontainer/` directory | | Combined CI/CD | `python -m cli.devopsos scaffold cicd` | `.github/workflows/` + `Jenkinsfile` | @@ -128,6 +129,33 @@ python -m cli.devopsos scaffold argocd [options] --- +## scaffold hardening — Infrastructure Hardening + +```bash +python -m cli.devopsos scaffold hardening [options] +``` + +| Option | Env var | Default | Description | +|--------|---------|---------|-------------| +| `--standard STANDARD` | `DEVOPS_OS_HARDENING_STANDARD` | `all` | `cis-k8s` \| `stig-k8s` \| `nsa-k8s` \| `cis-docker` \| `cis-rhel9` \| `cis-ubuntu22` \| `pod-security` \| `image-signing` \| `essential-eight` \| `all` | +| `--type TYPE` | `DEVOPS_OS_HARDENING_TYPE` | `all` | `kyverno` \| `inspec` \| `checkov` \| `all` | +| `--output DIR` | `DEVOPS_OS_HARDENING_OUTPUT` | `hardening` | Output directory | +| `--compliance-framework FRAMEWORK` | `DEVOPS_OS_HARDENING_COMPLIANCE_FRAMEWORK` | _(none)_ | Tag outputs for `pci-dss`, `hipaa`, `iso27001`, `rbi`, `nist-800-53`, or `soc2` | +| `--severity LEVEL` | `DEVOPS_OS_HARDENING_SEVERITY` | `medium` | `critical` \| `high` \| `medium` \| `low` | +| `--environment ENV` | `DEVOPS_OS_HARDENING_ENVIRONMENT` | `production` | `dev` \| `staging` \| `production` | + +**Output:** `/kyverno/`, `/inspec/`, `/essential-eight/`, and `/compliance-mapping.yaml` + +### Examples + +```bash +python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production +python -m cli.devopsos scaffold hardening --standard cis-rhel9 --type inspec +python -m cli.devopsos scaffold hardening --standard all --compliance-framework pci-dss +``` + +--- + ## scaffold sre — SRE Configuration ```bash @@ -215,6 +243,7 @@ python -m cli.devopsos scaffold gha # GitHub Actions python -m cli.devopsos scaffold gitlab # GitLab CI python -m cli.devopsos scaffold jenkins # Jenkins python -m cli.devopsos scaffold argocd # ArgoCD / Flux +python -m cli.devopsos scaffold hardening # Infrastructure hardening python -m cli.devopsos scaffold sre # SRE configs python -m cli.devopsos scaffold devcontainer # Dev container python -m cli.devopsos scaffold cicd # Combined GHA + Jenkins From 022da51783accda883f449fd374a1205f5679aee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Jul 2026 12:52:16 +0000 Subject: [PATCH 4/7] docs: refine hardening reference summary --- hugo-docs/content/docs/reference/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo-docs/content/docs/reference/_index.md b/hugo-docs/content/docs/reference/_index.md index c60600e..881be14 100644 --- a/hugo-docs/content/docs/reference/_index.md +++ b/hugo-docs/content/docs/reference/_index.md @@ -19,7 +19,7 @@ Complete reference for every DevOps-OS CLI command: options, default values, env | Jenkins | `python -m cli.devopsos scaffold jenkins` | `Jenkinsfile` | | ArgoCD | `python -m cli.devopsos scaffold argocd` | `argocd/` directory | | Flux CD | `python -m cli.devopsos scaffold argocd --method flux` | `flux/` directory | -| Infrastructure hardening | `python -m cli.devopsos scaffold hardening` | `hardening/` directory | +| Infrastructure hardening | `python -m cli.devopsos scaffold hardening` | `hardening/` directory with Kyverno, InSpec, Checkov, and mapping outputs | | SRE configs | `python -m cli.devopsos scaffold sre` | `sre/` directory | | Dev Container | `python -m cli.devopsos scaffold devcontainer` | `.devcontainer/` directory | | Combined CI/CD | `python -m cli.devopsos scaffold cicd` | `.github/workflows/` + `Jenkinsfile` | From 5472b3db43abeb77d6fc564972eb0ae9dacdba73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Jul 2026 17:17:52 +0000 Subject: [PATCH 5/7] docs: fix hardening sprint doc to match implementation --- docs/devops-os-hardening-sprint.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/devops-os-hardening-sprint.md b/docs/devops-os-hardening-sprint.md index eb986f0..d530f16 100644 --- a/docs/devops-os-hardening-sprint.md +++ b/docs/devops-os-hardening-sprint.md @@ -18,8 +18,7 @@ The `devopsos scaffold hardening` command delivers production-ready infrastructu | NSA/CISA Kubernetes Hardening Guide | Kubernetes cluster | Kyverno + NetworkPolicy | `kyverno/nsa-k8s/pod-security.yaml`, `network-policies.yaml` | | Pod Security Standards (Kubernetes) | Pod admission | Kyverno ClusterPolicy | `kyverno/pod-security-standards.yaml` | | Container Image Signing | CI/CD + admission | Kyverno + Cosign policy | `kyverno/image-signing.yaml` | -| OWASP ASVS L1 (infra layer only) | Application deployment | Kyverno + Checkov | `asvs-l1-checks/` | -| Essential Eight (Australia ASD) | General controls | InSpec + Checkov | `essential-eight/` | +| Essential Eight (Australia ASD) | General controls | Checkov | `essential-eight/` | --- From 6182c060ef349a3682547c4d7725f79ad43264df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Jul 2026 05:56:24 +0000 Subject: [PATCH 6/7] feat: implement OWASP ASVS L1 hardening standard with tests and docs --- cli/devopsos.py | 5 +- cli/scaffold_hardening.py | 325 +++++++++++++++++- docs/CLI-COMMANDS-REFERENCE.md | 5 +- docs/devops-os-hardening-sprint.md | 8 + .../docs/platform-engineering/hardening.md | 11 +- hugo-docs/content/docs/reference/_index.md | 5 +- tests/test_hardening_scaffold.py | 138 +++++++- 7 files changed, 485 insertions(+), 12 deletions(-) diff --git a/cli/devopsos.py b/cli/devopsos.py index a5b9e3a..0fc5a18 100644 --- a/cli/devopsos.py +++ b/cli/devopsos.py @@ -638,7 +638,7 @@ def scaffold_hardening_cmd( help=( "Hardening standard: cis-k8s, stig-k8s, nsa-k8s, " "cis-docker, cis-rhel9, cis-ubuntu22, pod-security, " - "image-signing, essential-eight, all (default: all)" + "image-signing, asvs-l1, essential-eight, all (default: all)" )), output_type: str = typer.Option("all", "--type", envvar="DEVOPS_OS_HARDENING_TYPE", help="Output type: kyverno, inspec, checkov, all (default: all applicable)"), @@ -658,7 +658,7 @@ def scaffold_hardening_cmd( "(adjusts enforcement levels, default: production)" )), ): - """Generate infrastructure hardening configs (CIS, STIG, NSA, PSS, Essential Eight). + """Generate infrastructure hardening configs (CIS, STIG, NSA, PSS, ASVS L1, Essential Eight). \b Examples: @@ -666,6 +666,7 @@ def scaffold_hardening_cmd( devopsos scaffold hardening --standard stig-k8s --output hardening/ devopsos scaffold hardening --standard cis-rhel9 --type inspec --output hardening/ devopsos scaffold hardening --standard all --type kyverno --output hardening/ + devopsos scaffold hardening --standard asvs-l1 --output hardening/ devopsos scaffold hardening --standard cis-k8s --compliance-framework pci-dss --output hardening/ """ _show_help_if_no_opts(ctx) diff --git a/cli/scaffold_hardening.py b/cli/scaffold_hardening.py index 76c2bc6..4a4351c 100644 --- a/cli/scaffold_hardening.py +++ b/cli/scaffold_hardening.py @@ -4,7 +4,7 @@ Generates hardening configurations for Kubernetes clusters, container runtimes, and operating systems based on industry standards (CIS, DISA STIG, NSA/CISA, -Pod Security Standards, Essential Eight). +Pod Security Standards, OWASP ASVS L1, Essential Eight). Outputs (default: ./hardening/ directory): hardening/ @@ -18,6 +18,13 @@ │ ├── docker-cis/ CIS Docker Benchmark InSpec profile │ ├── rhel9-cis/ CIS RHEL 9 Benchmark InSpec profile │ └── ubuntu22-cis/ CIS Ubuntu 22.04 Benchmark InSpec profile + ├── asvs-l1-checks/ + │ ├── README.md + │ ├── kyverno/ + │ │ ├── v9-communications.yaml ASVS V9 communications / TLS policies + │ │ └── v14-configuration.yaml ASVS V14 secure configuration policies + │ └── checkov/ + │ └── asvs-l1-checks.py Checkov custom checks for ASVS V2, V6, V8 ├── essential-eight/ │ ├── README.md │ └── checkov/ @@ -41,6 +48,7 @@ "cis-ubuntu22", "pod-security", "image-signing", + "asvs-l1", "essential-eight", "all", ] @@ -1587,6 +1595,28 @@ def generate_compliance_mapping(args): "pci-dss": ["12.3.4"], "nist-800-53": ["CP-9"], "iso27001": ["A.12.3.1"]}, }, + "asvs-l1": { + "V9.1.1": {"title": "Disallow plaintext HTTP port 80 on Services", + "nist-800-53": ["SC-8"], "pci-dss": ["4.2.1"], + "iso27001": ["A.13.2.3"]}, + "V9.2.1": {"title": "Require TLS on Ingress resources", + "nist-800-53": ["SC-8"], "pci-dss": ["4.2.1"], + "hipaa": ["164.312(e)(2)(ii)"]}, + "V14.1.1": {"title": "Disallow default/placeholder credentials in env vars", + "nist-800-53": ["IA-5", "CM-6"], "pci-dss": ["2.1"], + "iso27001": ["A.9.4.3"]}, + "V14.2.1": {"title": "Containers must run as non-root", + "nist-800-53": ["AC-6", "CM-7"], "pci-dss": ["6.5.8"]}, + "V2.1.1": {"title": "MFA enforced for privileged IAM users", + "nist-800-53": ["IA-2"], "pci-dss": ["8.4"], + "iso27001": ["A.9.4.2"]}, + "V6.2.1": {"title": "Sensitive data encrypted at rest", + "nist-800-53": ["SC-28"], "pci-dss": ["3.4"], + "hipaa": ["164.312(a)(2)(iv)"]}, + "V8.3.4": {"title": "Log groups must have retention policy", + "nist-800-53": ["AU-11"], "pci-dss": ["10.5"], + "hipaa": ["164.312(b)"]}, + }, }, } @@ -1595,12 +1625,298 @@ def generate_compliance_mapping(args): # --------------------------------------------------------------------------- -# Dispatch logic +# OWASP ASVS L1 — infra-layer Kyverno policies + Checkov custom checks # --------------------------------------------------------------------------- -_KYVERNO_STANDARDS = {"cis-k8s", "stig-k8s", "nsa-k8s", "pod-security", "image-signing"} +def generate_asvs_l1(args): + """Generate OWASP ASVS L1 infra-layer Kyverno policies and Checkov checks.""" + action = _enforcement_action(args.environment) + base = Path(args.output) / "asvs-l1-checks" + generated = [] + + # --- README --- + readme = """\ +# OWASP ASVS L1 — DevOps-OS Hardening (Infra Layer) + +The OWASP Application Security Verification Standard (ASVS) Level 1 defines +the minimum security baseline for web applications. This scaffold covers the +**infrastructure-layer** controls that can be enforced via Kubernetes admission +policies and IaC static analysis. + +## Controls Covered + +| ASVS Chapter | Chapter Title | Tool | File | +|---|---|---|---| +| V9 | Communications | Kyverno | kyverno/v9-communications.yaml | +| V14 | Configuration | Kyverno | kyverno/v14-configuration.yaml | +| V2 | Authentication | Checkov | checkov/asvs-l1-checks.py | +| V6 | Stored Cryptography | Checkov | checkov/asvs-l1-checks.py | +| V8 | Data Protection | Checkov | checkov/asvs-l1-checks.py | + +## Usage + +```bash +# Apply Kyverno policies +kubectl apply -f hardening/asvs-l1-checks/kyverno/ + +# Run Checkov checks against IaC +checkov --external-checks-dir hardening/asvs-l1-checks/checkov \\ + --directory . --framework terraform +``` + +## References + +- [OWASP ASVS](https://owasp.org/www-project-application-security-verification-standard/) +- [ASVS L1 Requirements](https://github.com/OWASP/ASVS/tree/v4.0.3/4.0) +""" + generated.append(_write_text(base / "README.md", readme)) + + # --- V9: Communications — require TLS, disallow plaintext service ports --- + v9_policy = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "asvs-l1-v9-communications", + "annotations": { + "policies.kyverno.io/title": "ASVS L1 V9 — Secure Communications", + "policies.kyverno.io/category": "OWASP ASVS L1", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + "Enforces OWASP ASVS L1 V9 communications controls: " + "disallows plaintext (HTTP/80) service ports and requires " + "TLS-only ingress annotations." + ), + "devops-os/compliance": "asvs-l1:V9.1.1,asvs-l1:V9.2.1,nist-800-53:SC-8", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "asvs-disallow-http-port-80", + "match": {"any": [{"resources": {"kinds": ["Service"]}}]}, + "validate": { + "message": ( + "Services must not expose plaintext HTTP port 80. " + "Use HTTPS (443) or a TLS-terminated ingress (ASVS V9.1.1)." + ), + "deny": { + "conditions": { + "any": [ + { + "key": "{{ request.object.spec.ports[].port | contains(@, `80`) | any(@) }}", + "operator": "Equals", + "value": True, + } + ] + } + }, + }, + }, + { + "name": "asvs-require-tls-ingress", + "match": {"any": [{"resources": {"kinds": ["Ingress"]}}]}, + "validate": { + "message": ( + "Ingress resources must configure TLS. " + "Plaintext HTTP ingress is not permitted (ASVS V9.2.1)." + ), + "pattern": { + "spec": { + "tls": [{"+(hosts)": "?*"}], + } + }, + }, + }, + ], + }, + } + generated.append(_write_yaml(base / "kyverno" / "v9-communications.yaml", v9_policy)) + + # --- V14: Configuration — no default credentials, secure pod config --- + v14_policy = { + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "asvs-l1-v14-configuration", + "annotations": { + "policies.kyverno.io/title": "ASVS L1 V14 — Secure Configuration", + "policies.kyverno.io/category": "OWASP ASVS L1", + "policies.kyverno.io/severity": "high", + "policies.kyverno.io/description": ( + "Enforces OWASP ASVS L1 V14 configuration controls: " + "disallows default/empty environment variable credentials and " + "requires containers to run as non-root." + ), + "devops-os/compliance": "asvs-l1:V14.1.1,asvs-l1:V14.2.1,nist-800-53:CM-6,nist-800-53:AC-6", + }, + }, + "spec": { + "validationFailureAction": action, + "rules": [ + { + "name": "asvs-disallow-default-passwords-in-env", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": ( + "Container environment variables must not use default or placeholder " + "credential values such as 'admin', 'password', or 'changeme' " + "(ASVS V14.1.1)." + ), + "deny": { + "conditions": { + "any": [ + { + "key": "{{ request.object.spec.containers[].env[].value | " + "contains(@, 'password') | any(@) }}", + "operator": "Equals", + "value": True, + }, + { + "key": "{{ request.object.spec.containers[].env[].value | " + "contains(@, 'changeme') | any(@) }}", + "operator": "Equals", + "value": True, + }, + ] + } + }, + }, + }, + { + "name": "asvs-require-run-as-non-root", + "match": {"any": [{"resources": {"kinds": ["Pod"]}}]}, + "validate": { + "message": ( + "Containers must run as a non-root user (ASVS V14.2.1, CIS 4.2.6)." + ), + "pattern": { + "spec": { + "securityContext": { + "runAsNonRoot": True, + } + } + }, + }, + }, + ], + }, + } + generated.append(_write_yaml(base / "kyverno" / "v14-configuration.yaml", v14_policy)) + + # --- Checkov custom checks for V2, V6, V8 --- + checkov_checks = '''\ +""" +OWASP ASVS L1 — Checkov Custom Checks (Infra Layer) + +Custom Checkov checks implementing OWASP Application Security Verification +Standard (ASVS) Level 1 controls for IaC compliance scanning. + +Chapters covered: + V2 — Authentication verification requirements + V6 — Stored cryptography verification requirements + V8 — Data protection verification requirements + +Usage: + checkov --external-checks-dir hardening/asvs-l1-checks/checkov \\ + --directory . --framework terraform +""" + +from checkov.common.models.enums import CheckCategories, CheckResult +from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck + + +class AsvsL1V2AuthMfaEnforced(BaseResourceCheck): + """ASVS L1 V2.1.1 — Verify that MFA is enforced for privileged IAM users. + + Checks that IAM users tagged as privileged have MFA enforcement enabled. + """ + + def __init__(self): + name = "ASVS L1 V2.1.1 — MFA enforced for privileged IAM users" + id = "ASVS_L1_V2_1_1_MFA" + supported_resources = ["aws_iam_user"] + categories = [CheckCategories.IAM] + super().__init__(name=name, id=id, categories=categories, + supported_resources=supported_resources) + + def scan_resource_conf(self, conf): + tags = conf.get("tags", [{}]) + if isinstance(tags, list): + tags = tags[0] if tags else {} + # Require explicit mfa-enforced=true tag for privileged users + if tags.get("privileged") == "true" and tags.get("mfa-enforced") != "true": + return CheckResult.FAILED + return CheckResult.PASSED + + +class AsvsL1V6EncryptionAtRest(BaseResourceCheck): + """ASVS L1 V6.2.1 — Verify that sensitive data at rest is encrypted. + + Checks that storage resources have server-side encryption enabled. + """ + + def __init__(self): + name = "ASVS L1 V6.2.1 — Sensitive data must be encrypted at rest" + id = "ASVS_L1_V6_2_1_ENCRYPTION" + supported_resources = [ + "aws_s3_bucket", + "aws_db_instance", + "aws_rds_cluster", + "aws_ebs_volume", + ] + categories = [CheckCategories.ENCRYPTION] + super().__init__(name=name, id=id, categories=categories, + supported_resources=supported_resources) + + def scan_resource_conf(self, conf): + # S3 uses server_side_encryption_configuration; RDS/EBS use storage_encrypted + sse = conf.get("server_side_encryption_configuration") + encrypted = conf.get("storage_encrypted", [False]) + if isinstance(encrypted, list): + encrypted = encrypted[0] if encrypted else False + if sse or encrypted is True: + return CheckResult.PASSED + return CheckResult.FAILED + + +class AsvsL1V8SensitiveDataNotLogged(BaseResourceCheck): + """ASVS L1 V8.3.4 — Verify sensitive data is not written to application logs. + + Checks that CloudWatch log groups have retention policies set (as a proxy + for controlled log management) and are not world-accessible. + """ + + def __init__(self): + name = "ASVS L1 V8.3.4 — Log groups must have a retention policy" + id = "ASVS_L1_V8_3_4_LOG_RETENTION" + supported_resources = ["aws_cloudwatch_log_group"] + categories = [CheckCategories.LOGGING] + super().__init__(name=name, id=id, categories=categories, + supported_resources=supported_resources) + + def scan_resource_conf(self, conf): + retention = conf.get("retention_in_days", [None]) + if isinstance(retention, list): + retention = retention[0] if retention else None + if retention is not None and int(retention) > 0: + return CheckResult.PASSED + return CheckResult.FAILED + + +scanner_asvs_v2_mfa = AsvsL1V2AuthMfaEnforced() +scanner_asvs_v6_encryption = AsvsL1V6EncryptionAtRest() +scanner_asvs_v8_log_retention = AsvsL1V8SensitiveDataNotLogged() +''' + generated.append(_write_text(base / "checkov" / "asvs-l1-checks.py", checkov_checks)) + return generated + + + + +_KYVERNO_STANDARDS = {"cis-k8s", "stig-k8s", "nsa-k8s", "pod-security", "image-signing", "asvs-l1"} _INSPEC_STANDARDS = {"cis-docker", "cis-rhel9", "cis-ubuntu22"} -_CHECKOV_STANDARDS = {"essential-eight"} +_CHECKOV_STANDARDS = {"essential-eight", "asvs-l1"} _GENERATORS = { "cis-k8s": generate_kyverno_cis_k8s, @@ -1608,6 +1924,7 @@ def generate_compliance_mapping(args): "nsa-k8s": generate_kyverno_nsa_k8s, "pod-security": generate_kyverno_pod_security, "image-signing": generate_kyverno_image_signing, + "asvs-l1": generate_asvs_l1, "cis-docker": generate_inspec_docker_cis, "cis-rhel9": generate_inspec_rhel9_cis, "cis-ubuntu22": generate_inspec_ubuntu22_cis, diff --git a/docs/CLI-COMMANDS-REFERENCE.md b/docs/CLI-COMMANDS-REFERENCE.md index c03c02b..8f8f152 100644 --- a/docs/CLI-COMMANDS-REFERENCE.md +++ b/docs/CLI-COMMANDS-REFERENCE.md @@ -243,7 +243,7 @@ python -m cli.devopsos scaffold hardening [options] | Option | Env var | Default | Description | |--------|---------|---------|-------------| -| `--standard STANDARD` | `DEVOPS_OS_HARDENING_STANDARD` | `all` | Hardening standard: `cis-k8s` \| `stig-k8s` \| `nsa-k8s` \| `cis-docker` \| `cis-rhel9` \| `cis-ubuntu22` \| `pod-security` \| `image-signing` \| `essential-eight` \| `all` | +| `--standard STANDARD` | `DEVOPS_OS_HARDENING_STANDARD` | `all` | Hardening standard: `cis-k8s` \| `stig-k8s` \| `nsa-k8s` \| `cis-docker` \| `cis-rhel9` \| `cis-ubuntu22` \| `pod-security` \| `image-signing` \| `asvs-l1` \| `essential-eight` \| `all` | | `--type TYPE` | `DEVOPS_OS_HARDENING_TYPE` | `all` | Output type: `kyverno` \| `inspec` \| `checkov` \| `all` | | `--output DIR` | `DEVOPS_OS_HARDENING_OUTPUT` | `hardening` | Output directory | | `--compliance-framework FRAMEWORK` | `DEVOPS_OS_HARDENING_COMPLIANCE_FRAMEWORK` | _(none)_ | Tag outputs for `pci-dss`, `hipaa`, `iso27001`, `rbi`, `nist-800-53`, or `soc2` catalog mapping | @@ -262,6 +262,8 @@ python -m cli.devopsos scaffold hardening [options] | `/inspec/docker-cis/` | CIS Docker Benchmark InSpec profile | | `/inspec/rhel9-cis/` | CIS RHEL 9 InSpec profile | | `/inspec/ubuntu22-cis/` | CIS Ubuntu 22.04 InSpec profile | +| `/asvs-l1-checks/kyverno/` | OWASP ASVS L1 Kyverno policies (V9, V14) | +| `/asvs-l1-checks/checkov/` | OWASP ASVS L1 Checkov checks (V2, V6, V8) | | `/essential-eight/` | Essential Eight README and Checkov checks | | `/compliance-mapping.yaml` | Rule-to-framework mapping file | @@ -270,6 +272,7 @@ python -m cli.devopsos scaffold hardening [options] ```bash python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production python -m cli.devopsos scaffold hardening --standard cis-rhel9 --type inspec --output baseline-hardening +python -m cli.devopsos scaffold hardening --standard asvs-l1 --output hardening python -m cli.devopsos scaffold hardening --standard all --compliance-framework pci-dss ``` diff --git a/docs/devops-os-hardening-sprint.md b/docs/devops-os-hardening-sprint.md index d530f16..fb7c430 100644 --- a/docs/devops-os-hardening-sprint.md +++ b/docs/devops-os-hardening-sprint.md @@ -18,6 +18,7 @@ The `devopsos scaffold hardening` command delivers production-ready infrastructu | NSA/CISA Kubernetes Hardening Guide | Kubernetes cluster | Kyverno + NetworkPolicy | `kyverno/nsa-k8s/pod-security.yaml`, `network-policies.yaml` | | Pod Security Standards (Kubernetes) | Pod admission | Kyverno ClusterPolicy | `kyverno/pod-security-standards.yaml` | | Container Image Signing | CI/CD + admission | Kyverno + Cosign policy | `kyverno/image-signing.yaml` | +| OWASP ASVS L1 (infra layer) | Application deployment | Kyverno + Checkov | `asvs-l1-checks/` | | Essential Eight (Australia ASD) | General controls | Checkov | `essential-eight/` | --- @@ -92,6 +93,13 @@ hardening/ │ └── ubuntu22-cis/ │ ├── inspec.yml │ └── controls/ (same structure as rhel9-cis) +├── asvs-l1-checks/ +│ ├── README.md — controls covered and usage +│ ├── kyverno/ +│ │ ├── v9-communications.yaml — ASVS V9: disallow HTTP port 80, require TLS ingress +│ │ └── v14-configuration.yaml — ASVS V14: no default credentials, run as non-root +│ └── checkov/ +│ └── asvs-l1-checks.py — Checkov custom checks for V2, V6, V8 ├── essential-eight/ │ ├── README.md — maturity levels and applicability │ └── checkov/ diff --git a/hugo-docs/content/docs/platform-engineering/hardening.md b/hugo-docs/content/docs/platform-engineering/hardening.md index cd082bb..0f7c3ee 100644 --- a/hugo-docs/content/docs/platform-engineering/hardening.md +++ b/hugo-docs/content/docs/platform-engineering/hardening.md @@ -13,8 +13,9 @@ DevOps-OS includes `python -m cli.devopsos scaffold hardening` to generate reusa | Output type | Purpose | Default location | |-------------|---------|------------------| -| Kyverno policies | Kubernetes admission guardrails for CIS, STIG, NSA/CISA, Pod Security Standards, and image signing | `hardening/kyverno/` | +| Kyverno policies | Kubernetes admission guardrails for CIS, STIG, NSA/CISA, Pod Security Standards, image signing, and OWASP ASVS L1 | `hardening/kyverno/` | | InSpec profiles | Compliance profiles for Docker, RHEL 9, and Ubuntu 22.04 | `hardening/inspec/` | +| ASVS L1 checks | OWASP ASVS L1 infra-layer Kyverno policies and Checkov checks | `hardening/asvs-l1-checks/` | | Checkov checks | Essential Eight checks and supporting metadata | `hardening/essential-eight/` | | Compliance mapping | Rule-to-framework mapping for catalog linking | `hardening/compliance-mapping.yaml` | @@ -29,6 +30,7 @@ DevOps-OS includes `python -m cli.devopsos scaffold hardening` to generate reusa | NSA/CISA Kubernetes Hardening Guide | Kyverno policies + network policies | | Pod Security Standards | Kyverno policy | | Container image signing | Kyverno policy | +| OWASP ASVS L1 (infra layer) | Kyverno policies + Checkov checks | | CIS Docker Benchmark v1.6 | InSpec profile | | CIS RHEL 9 Benchmark | InSpec profile | | CIS Ubuntu 22.04 Benchmark | InSpec profile | @@ -42,6 +44,9 @@ DevOps-OS includes `python -m cli.devopsos scaffold hardening` to generate reusa # CIS Kubernetes guardrails python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production +# OWASP ASVS L1 infra-layer checks +python -m cli.devopsos scaffold hardening --standard asvs-l1 + # Operating system baseline python -m cli.devopsos scaffold hardening --standard cis-rhel9 --type inspec @@ -78,6 +83,10 @@ hardening/ │ ├── docker-cis/ │ ├── rhel9-cis/ │ └── ubuntu22-cis/ +├── asvs-l1-checks/ +│ ├── README.md +│ ├── kyverno/ +│ └── checkov/ ├── essential-eight/ └── compliance-mapping.yaml ``` diff --git a/hugo-docs/content/docs/reference/_index.md b/hugo-docs/content/docs/reference/_index.md index 881be14..0b8d838 100644 --- a/hugo-docs/content/docs/reference/_index.md +++ b/hugo-docs/content/docs/reference/_index.md @@ -137,20 +137,21 @@ python -m cli.devopsos scaffold hardening [options] | Option | Env var | Default | Description | |--------|---------|---------|-------------| -| `--standard STANDARD` | `DEVOPS_OS_HARDENING_STANDARD` | `all` | `cis-k8s` \| `stig-k8s` \| `nsa-k8s` \| `cis-docker` \| `cis-rhel9` \| `cis-ubuntu22` \| `pod-security` \| `image-signing` \| `essential-eight` \| `all` | +| `--standard STANDARD` | `DEVOPS_OS_HARDENING_STANDARD` | `all` | `cis-k8s` \| `stig-k8s` \| `nsa-k8s` \| `cis-docker` \| `cis-rhel9` \| `cis-ubuntu22` \| `pod-security` \| `image-signing` \| `asvs-l1` \| `essential-eight` \| `all` | | `--type TYPE` | `DEVOPS_OS_HARDENING_TYPE` | `all` | `kyverno` \| `inspec` \| `checkov` \| `all` | | `--output DIR` | `DEVOPS_OS_HARDENING_OUTPUT` | `hardening` | Output directory | | `--compliance-framework FRAMEWORK` | `DEVOPS_OS_HARDENING_COMPLIANCE_FRAMEWORK` | _(none)_ | Tag outputs for `pci-dss`, `hipaa`, `iso27001`, `rbi`, `nist-800-53`, or `soc2` | | `--severity LEVEL` | `DEVOPS_OS_HARDENING_SEVERITY` | `medium` | `critical` \| `high` \| `medium` \| `low` | | `--environment ENV` | `DEVOPS_OS_HARDENING_ENVIRONMENT` | `production` | `dev` \| `staging` \| `production` | -**Output:** `/kyverno/`, `/inspec/`, `/essential-eight/`, and `/compliance-mapping.yaml` +**Output:** `/kyverno/`, `/inspec/`, `/asvs-l1-checks/`, `/essential-eight/`, and `/compliance-mapping.yaml` ### Examples ```bash python -m cli.devopsos scaffold hardening --standard cis-k8s --type kyverno --environment production python -m cli.devopsos scaffold hardening --standard cis-rhel9 --type inspec +python -m cli.devopsos scaffold hardening --standard asvs-l1 --output hardening python -m cli.devopsos scaffold hardening --standard all --compliance-framework pci-dss ``` diff --git a/tests/test_hardening_scaffold.py b/tests/test_hardening_scaffold.py index fb0ef70..3155670 100644 --- a/tests/test_hardening_scaffold.py +++ b/tests/test_hardening_scaffold.py @@ -90,6 +90,15 @@ def test_checkov_type_accepts_essential_eight(self): def test_checkov_type_rejects_kyverno_standard(self): assert scaffold_hardening._should_generate("pod-security", "checkov") is False + def test_kyverno_type_accepts_asvs_l1(self): + assert scaffold_hardening._should_generate("asvs-l1", "kyverno") is True + + def test_checkov_type_accepts_asvs_l1(self): + assert scaffold_hardening._should_generate("asvs-l1", "checkov") is True + + def test_inspec_type_rejects_asvs_l1(self): + assert scaffold_hardening._should_generate("asvs-l1", "inspec") is False + # --------------------------------------------------------------------------- # CIS Kubernetes generators @@ -385,9 +394,107 @@ def test_checkov_checks_has_e8_classes(self, tmp_path): # --------------------------------------------------------------------------- -# Compliance mapping +# OWASP ASVS L1 # --------------------------------------------------------------------------- +class TestAsvsL1: + def test_generates_four_files(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + paths = scaffold_hardening.generate_asvs_l1(args) + assert len(paths) == 4 # README + 2 Kyverno + 1 Checkov + + def test_readme_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + assert (tmp_path / "asvs-l1-checks" / "README.md").exists() + + def test_readme_mentions_owasp(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + content = (tmp_path / "asvs-l1-checks" / "README.md").read_text() + assert "OWASP" in content + assert "ASVS" in content + + def test_v9_policy_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + assert (tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml").exists() + + def test_v14_policy_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + assert (tmp_path / "asvs-l1-checks" / "kyverno" / "v14-configuration.yaml").exists() + + def test_checkov_checks_file_exists(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + assert (tmp_path / "asvs-l1-checks" / "checkov" / "asvs-l1-checks.py").exists() + + def test_v9_policy_is_cluster_policy(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + policy = _load_yaml(tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml") + assert policy["kind"] == "ClusterPolicy" + + def test_v9_policy_has_two_rules(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + policy = _load_yaml(tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml") + assert len(policy["spec"]["rules"]) == 2 + + def test_v9_policy_disallows_port_80(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + policy = _load_yaml(tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml") + rule_names = [r["name"] for r in policy["spec"]["rules"]] + assert "asvs-disallow-http-port-80" in rule_names + + def test_v9_policy_requires_tls_ingress(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + policy = _load_yaml(tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml") + rule_names = [r["name"] for r in policy["spec"]["rules"]] + assert "asvs-require-tls-ingress" in rule_names + + def test_v14_policy_has_two_rules(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + policy = _load_yaml(tmp_path / "asvs-l1-checks" / "kyverno" / "v14-configuration.yaml") + assert len(policy["spec"]["rules"]) == 2 + + def test_v14_policy_requires_non_root(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + policy = _load_yaml(tmp_path / "asvs-l1-checks" / "kyverno" / "v14-configuration.yaml") + rule_names = [r["name"] for r in policy["spec"]["rules"]] + assert "asvs-require-run-as-non-root" in rule_names + + def test_production_enforcement_is_enforce(self, tmp_path): + args = _hardening_args(output=str(tmp_path), environment="production") + scaffold_hardening.generate_asvs_l1(args) + policy = _load_yaml(tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml") + assert policy["spec"]["validationFailureAction"] == "Enforce" + + def test_dev_enforcement_is_audit(self, tmp_path): + args = _hardening_args(output=str(tmp_path), environment="dev") + scaffold_hardening.generate_asvs_l1(args) + policy = _load_yaml(tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml") + assert policy["spec"]["validationFailureAction"] == "Audit" + + def test_kyverno_policies_have_asvs_compliance_annotation(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + policy = _load_yaml(tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml") + assert "asvs-l1" in policy["metadata"]["annotations"]["devops-os/compliance"] + + def test_checkov_checks_has_asvs_classes(self, tmp_path): + args = _hardening_args(output=str(tmp_path)) + scaffold_hardening.generate_asvs_l1(args) + content = (tmp_path / "asvs-l1-checks" / "checkov" / "asvs-l1-checks.py").read_text() + assert "AsvsL1V2AuthMfaEnforced" in content + assert "AsvsL1V6EncryptionAtRest" in content + assert "AsvsL1V8SensitiveDataNotLogged" in content + class TestComplianceMapping: def test_generates_one_file(self, tmp_path): args = _hardening_args(output=str(tmp_path)) @@ -411,7 +518,7 @@ def test_mapping_covers_all_standards(self, tmp_path): scaffold_hardening.generate_compliance_mapping(args) mapping = _load_yaml(tmp_path / "compliance-mapping.yaml") for standard in ["cis-k8s", "stig-k8s", "nsa-k8s", "cis-docker", - "cis-rhel9", "cis-ubuntu22", "essential-eight"]: + "cis-rhel9", "cis-ubuntu22", "essential-eight", "asvs-l1"]: assert standard in mapping["mappings"], f"Standard {standard} missing from compliance mapping" def test_mapping_entries_have_title(self, tmp_path): @@ -476,3 +583,30 @@ def test_staging_environment_audits_kyverno_policies(self, tmp_path): scaffold_hardening.generate_hardening(args) policy = _load_yaml(tmp_path / "kyverno" / "stig-k8s" / "stig-cluster-policies.yaml") assert policy["spec"]["validationFailureAction"] == "Audit" + + def test_asvs_l1_standard_generates_kyverno_and_checkov(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="asvs-l1", output_type="all") + scaffold_hardening.generate_hardening(args) + assert (tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml").exists() + assert (tmp_path / "asvs-l1-checks" / "kyverno" / "v14-configuration.yaml").exists() + assert (tmp_path / "asvs-l1-checks" / "checkov" / "asvs-l1-checks.py").exists() + + def test_asvs_l1_included_in_all_standard_run(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="all", output_type="all") + scaffold_hardening.generate_hardening(args) + assert (tmp_path / "asvs-l1-checks" / "README.md").exists() + + def test_kyverno_type_includes_asvs_l1_kyverno_output(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="asvs-l1", output_type="kyverno") + scaffold_hardening.generate_hardening(args) + assert (tmp_path / "asvs-l1-checks" / "kyverno" / "v9-communications.yaml").exists() + + def test_checkov_type_includes_asvs_l1_checkov_output(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="asvs-l1", output_type="checkov") + scaffold_hardening.generate_hardening(args) + assert (tmp_path / "asvs-l1-checks" / "checkov" / "asvs-l1-checks.py").exists() + + def test_inspec_type_excludes_asvs_l1(self, tmp_path): + args = _hardening_args(output=str(tmp_path), standard="asvs-l1", output_type="inspec") + scaffold_hardening.generate_hardening(args) + assert not (tmp_path / "asvs-l1-checks").exists() From 91ef259f3b9584f5dbfe38cfcd2b8a2f8ccd6292 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Jul 2026 09:07:27 +0000 Subject: [PATCH 7/7] docs: fill hardening documentation gaps - per-standard descriptions, selection guide, compliance mapping schema, troubleshooting, ubuntu22-cis controls, asvs-l1 fix --- docs/devops-os-hardening-sprint.md | 9 +- .../docs/platform-engineering/hardening.md | 217 +++++++++++++++++- 2 files changed, 212 insertions(+), 14 deletions(-) diff --git a/docs/devops-os-hardening-sprint.md b/docs/devops-os-hardening-sprint.md index fb7c430..e5dade4 100644 --- a/docs/devops-os-hardening-sprint.md +++ b/docs/devops-os-hardening-sprint.md @@ -46,7 +46,7 @@ devopsos scaffold hardening --standard cis-k8s --compliance-framework pci-dss -- | Option | Description | Default | |--------|-------------|---------| -| `--standard` | Hardening standard: `cis-k8s`, `stig-k8s`, `nsa-k8s`, `cis-docker`, `cis-rhel9`, `cis-ubuntu22`, `pod-security`, `image-signing`, `essential-eight`, `all` | `all` | +| `--standard` | Hardening standard: `cis-k8s`, `stig-k8s`, `nsa-k8s`, `cis-docker`, `cis-rhel9`, `cis-ubuntu22`, `pod-security`, `image-signing`, `asvs-l1`, `essential-eight`, `all` | `all` | | `--type` | Output type: `kyverno`, `inspec`, `checkov`, `all` | `all` applicable | | `--output` | Output directory | `./hardening/` | | `--compliance-framework` | Tag outputs with compliance framework IDs (`pci-dss`, `hipaa`, `iso27001`, `rbi`, `nist-800-53`, `soc2`) for GovPilot catalog linking | _(none)_ | @@ -92,7 +92,12 @@ hardening/ │ │ └── 5_access.rb — CIS 5.x access, auth, sudo │ └── ubuntu22-cis/ │ ├── inspec.yml -│ └── controls/ (same structure as rhel9-cis) +│ └── controls/ +│ ├── 1_filesystem.rb — CIS 1.x filesystem partitions and mount options (Ubuntu-specific) +│ ├── 2_software.rb — CIS 2.x software updates, apt, snap, and package management +│ ├── 3_network.rb — CIS 3.x network parameters and firewall (ufw) +│ ├── 4_logging.rb — CIS 4.x logging, auditing (auditd), and rsyslog +│ └── 5_access.rb — CIS 5.x user accounts, PAM, shadow passwords, and file permissions ├── asvs-l1-checks/ │ ├── README.md — controls covered and usage │ ├── kyverno/ diff --git a/hugo-docs/content/docs/platform-engineering/hardening.md b/hugo-docs/content/docs/platform-engineering/hardening.md index 0f7c3ee..9e01ca2 100644 --- a/hugo-docs/content/docs/platform-engineering/hardening.md +++ b/hugo-docs/content/docs/platform-engineering/hardening.md @@ -23,18 +23,116 @@ DevOps-OS includes `python -m cli.devopsos scaffold hardening` to generate reusa ## Supported standards -| Standard | Primary output | -|----------|----------------| -| CIS Kubernetes Benchmark v1.9 | Kyverno policies | -| DISA STIG for Kubernetes | Kyverno policies | -| NSA/CISA Kubernetes Hardening Guide | Kyverno policies + network policies | -| Pod Security Standards | Kyverno policy | -| Container image signing | Kyverno policy | -| OWASP ASVS L1 (infra layer) | Kyverno policies + Checkov checks | -| CIS Docker Benchmark v1.6 | InSpec profile | -| CIS RHEL 9 Benchmark | InSpec profile | -| CIS Ubuntu 22.04 Benchmark | InSpec profile | -| Essential Eight | Checkov checks + README | +| Standard | CLI value | Primary output | +|----------|-----------|----------------| +| CIS Kubernetes Benchmark v1.9 | `cis-k8s` | Kyverno policies | +| DISA STIG for Kubernetes | `stig-k8s` | Kyverno policies | +| NSA/CISA Kubernetes Hardening Guide | `nsa-k8s` | Kyverno policies + network policies | +| Pod Security Standards | `pod-security` | Kyverno policy | +| Container image signing | `image-signing` | Kyverno policy | +| OWASP ASVS L1 (infra layer) | `asvs-l1` | Kyverno policies + Checkov checks | +| CIS Docker Benchmark v1.6 | `cis-docker` | InSpec profile | +| CIS RHEL 9 Benchmark | `cis-rhel9` | InSpec profile | +| CIS Ubuntu 22.04 Benchmark | `cis-ubuntu22` | InSpec profile | +| Essential Eight | `essential-eight` | Checkov checks + README | + +--- + +## Standard descriptions + +### CIS Kubernetes Benchmark v1.9 (`cis-k8s`) + +Published by the **Center for Internet Security (CIS)**, this benchmark provides prescriptive guidance for securing Kubernetes cluster components — API server, etcd, control-plane, kubelet, and RBAC. It is the most widely adopted baseline for Kubernetes hardening and is referenced by PCI-DSS, ISO 27001, and SOC 2 assessors. The generated Kyverno policies cover all five CIS sections (master node, etcd, control plane, worker nodes, and cluster policies). + +### DISA STIG for Kubernetes (`stig-k8s`) + +The **Defense Information Systems Agency (DISA) Security Technical Implementation Guide** for Kubernetes is mandatory for US Department of Defense (DoD) systems and systems hosted on FedRAMP-authorized cloud environments. It provides control identifiers (e.g., `V-242381`) that map directly into ATO (Authority to Operate) evidence packages. Use this standard if your workloads are subject to US federal compliance requirements. + +### NSA/CISA Kubernetes Hardening Guide (`nsa-k8s`) + +Jointly published by the **National Security Agency (NSA)** and the **Cybersecurity and Infrastructure Security Agency (CISA)**, this guide focuses on supply-chain risks, pod security, network segmentation, and audit logging. It is a recommended baseline for critical infrastructure operators. The generated artifacts include Kyverno pod-security policies and Kubernetes `NetworkPolicy` manifests for traffic segmentation. + +### Pod Security Standards (`pod-security`) + +A built-in Kubernetes standard that defines three profiles — **Privileged**, **Baseline**, and **Restricted** — governing what a pod is permitted to do at admission time. DevOps-OS generates a Kyverno `ClusterPolicy` that enforces the **Restricted** profile by default (no privilege escalation, no host namespaces, non-root containers, read-only root filesystems). Use this as a minimum baseline for any production cluster. + +### Container Image Signing (`image-signing`) + +Implements a **supply-chain integrity** control: every container image must be cryptographically signed with [Cosign](https://docs.sigstore.dev/cosign/overview/) before it is admitted to the cluster. The generated Kyverno policy verifies the Cosign signature against a public key at admission time, rejecting unsigned images. This aligns with SLSA Level 2+ and is required by several STIG controls. + +### OWASP ASVS L1 — Infrastructure Layer (`asvs-l1`) + +The **OWASP Application Security Verification Standard (ASVS) Level 1** defines baseline security requirements for web applications. The infra-layer subset enforced here covers: +- **V2** — Authentication: no default credentials in running containers +- **V6** — Stored cryptography: no plaintext secrets as environment variables +- **V8** — Data protection: TLS termination enforced at ingress +- **V9** — Communications: HTTP port 80 disallowed, TLS required on all ingresses +- **V14** — Configuration: containers run as non-root with least-privilege security contexts + +This makes ASVS controls enforceable at the infrastructure admission layer, complementing application-level testing. + +### CIS Docker Benchmark v1.6 (`cis-docker`) + +Published by **CIS**, this benchmark audits the Docker host configuration, Docker daemon settings, Docker daemon files and permissions, container images, and container runtime configuration. The generated InSpec profile contains Ruby control files for each CIS section and is designed to be run with `inspec exec` against the Docker host. Applicable to any team running Docker or Podman as a container runtime. + +### CIS RHEL 9 Benchmark (`cis-rhel9`) + +Published by **CIS**, this benchmark covers hardening for **Red Hat Enterprise Linux 9** (and compatible distributions such as Rocky Linux and AlmaLinux). The generated InSpec profile audits filesystem configuration, inetd and special services, network parameters, logging and auditing, and access controls. Use this for any VM or bare-metal node running RHEL 9. + +### CIS Ubuntu 22.04 Benchmark (`cis-ubuntu22`) + +Published by **CIS**, this benchmark covers hardening for **Ubuntu 22.04 LTS**. The generated InSpec profile audits filesystem partitioning, software updates, network configuration, logging, user accounts, and file permissions. Ubuntu-specific control identifiers differ from the RHEL benchmark. Use this for Debian-family nodes including Ubuntu-based cloud VM images. + +### Essential Eight (`essential-eight`) + +Published by the **Australian Signals Directorate (ASD)**, the Essential Eight is mandatory for Australian government agencies and widely adopted by the private sector. It defines eight mitigation strategies across three maturity levels: +1. Application control +2. Patch applications +3. Configure Microsoft Office macro settings +4. User application hardening +5. Restrict administrative privileges +6. Patch operating systems +7. Multi-factor authentication +8. Regular backups + +The generated Checkov checks enforce the infrastructure-applicable controls (items 1, 5, and 6) and the accompanying README explains maturity levels and which controls apply to containerised workloads. + +--- + +## Which standard should I use? + +Use this guide to choose the right baseline for your context. Standards can be combined — for example, a US DoD Kubernetes cluster may need both `stig-k8s` and `cis-rhel9`. + +| Context | Recommended standards | +|---------|----------------------| +| Any production Kubernetes cluster (minimum baseline) | `cis-k8s`, `pod-security` | +| US DoD / FedRAMP / US federal systems | `stig-k8s`, `pod-security`, `image-signing` | +| Critical infrastructure (US) | `nsa-k8s`, `pod-security` | +| Australian Government / ASD mandate | `essential-eight`, `cis-k8s` | +| PCI-DSS scoped workloads | `cis-k8s`, `asvs-l1`, `pod-security` | +| HIPAA or ISO 27001 | `cis-k8s`, `asvs-l1`, `image-signing` | +| NIST 800-53 | `cis-k8s`, `stig-k8s`, `nsa-k8s` | +| Docker/container runtime compliance | `cis-docker` | +| RHEL 9 / Rocky / AlmaLinux OS nodes | `cis-rhel9` | +| Ubuntu 22.04 OS nodes | `cis-ubuntu22` | +| Supply-chain integrity enforcement | `image-signing` | +| Application-layer infrastructure controls | `asvs-l1` | +| Full production cluster hardening | `all` with `--compliance-framework` | + +--- + +## Compliance framework to standard mapping + +Use `--compliance-framework` to tag generated artifacts with control IDs for your target framework. The table below shows which hardening standards contribute controls to each framework. + +| Compliance framework | Most relevant hardening standards | +|---------------------|----------------------------------| +| `pci-dss` | `cis-k8s`, `asvs-l1`, `pod-security`, `image-signing` | +| `hipaa` | `cis-k8s`, `asvs-l1`, `cis-rhel9`, `cis-ubuntu22` | +| `iso27001` | `cis-k8s`, `nsa-k8s`, `asvs-l1`, `essential-eight` | +| `nist-800-53` | `cis-k8s`, `stig-k8s`, `nsa-k8s`, `pod-security` | +| `soc2` | `cis-k8s`, `asvs-l1`, `image-signing`, `pod-security` | +| `rbi` | `cis-k8s`, `cis-docker`, `asvs-l1` | --- @@ -98,7 +196,102 @@ hardening/ ```bash pytest tests/test_hardening_scaffold.py kubectl apply --dry-run=client -f hardening/kyverno/ +inspec check hardening/inspec/docker-cis/ python -c "import yaml; yaml.safe_load(open('hardening/compliance-mapping.yaml'))" ``` +--- + +## compliance-mapping.yaml schema + +The `compliance-mapping.yaml` file links each generated hardening rule to control IDs in one or more compliance frameworks. It is consumed by downstream tools (e.g., GovPilot) to build a compliance catalog. + +**Top-level structure:** + +```yaml +: # e.g. cis-k8s, stig-k8s + : # e.g. cis-k8s-1.2.1 + title: "..." # human-readable rule description + severity: high # critical | high | medium | low + frameworks: + pci-dss: # framework key + - "Req 2.2" # list of matching control IDs + nist-800-53: + - "CM-6" + - "SC-28" +``` + +**Example entry:** + +```yaml +cis-k8s: + cis-k8s-1.2.1: + title: "Ensure that the --anonymous-auth argument is set to false" + severity: high + frameworks: + pci-dss: + - "Req 2.2" + - "Req 8.2" + nist-800-53: + - "AC-3" + - "IA-2" + soc2: + - "CC6.1" +``` + +The mapping is only generated when `--compliance-framework` is specified. Without that flag, `compliance-mapping.yaml` is still written but the `frameworks` block will be empty for each rule. + +--- + +## Troubleshooting + +### Kyverno not installed — dry-run fails + +``` +error: unable to recognize "hardening/kyverno/": no matches for kind "ClusterPolicy" +``` + +Install Kyverno first, then retry: + +```bash +helm repo add kyverno https://kyverno.github.io/kyverno/ +helm install kyverno kyverno/kyverno -n kyverno --create-namespace +kubectl apply --dry-run=client -f hardening/kyverno/ +``` + +### InSpec gem not found + +``` +bash: inspec: command not found +``` + +Install the InSpec binary gem: + +```bash +gem install inspec-bin +inspec check hardening/inspec/docker-cis/ +``` + +### image-signing policy requires Cosign v2+ + +The generated `image-signing.yaml` Kyverno policy uses the `imageVerification` rule which requires **Cosign v2** signatures. Signatures created with Cosign v1 will be rejected. Verify your Cosign version before signing images: + +```bash +cosign version # must be v2.0.0 or later +``` + +### Generated YAML silently overrides existing files + +The `--output` directory is written unconditionally. If you have customised files in `hardening/`, move them to a different directory before re-running the scaffold command, or use a different `--output` path. + +### `compliance-mapping.yaml` is empty + +The frameworks block is only populated when `--compliance-framework` is provided. Run again with the flag: + +```bash +python -m cli.devopsos scaffold hardening --standard all --compliance-framework pci-dss +``` + +--- + For the full option table, environment variables, and examples, see the [CLI Reference]({{< relref "/docs/reference" >}}).