Skip to main content
CleanStart

Knowledge Hub

DISA STIG: Military-Grade Container Hardening

Applying DISA STIG Controls to Containers

DISA Security Technical Implementation Guides (STIGs) are the most stringent security hardening standards, required for Department of Defense systems and military contractors. CleanStart supports STIG compliance through additional hardening layers beyond CIS.

STIG vs CIS

Aspect

CIS

STIG

Source

Industry (CIS)

Military (DISA)

Stringency

Balanced

Maximum

Controls

94

150+

Override

Some flexibility

Minimal

Environment

General IT

DoD/Military only

Compliance Required

Industry

DoD Contracts

CleanStart implements both; STIG is a superset.

STIG Container Controls

GEN000080: Configurable Accounts

# STIG: Lock system accounts, use application accountFROM ubuntu:24.04 # Create application userRUN useradd -m -u 1000 -d /app appuser && \    usermod -L appuser  # Lock account (no direct login) # All processes run as appuserUSER appuser

GEN001080: Account Lockout Policy

STIG requires implementing account lockout policies at the host level. These policies enforce the use of PAM (Pluggable Authentication Modules) to lock accounts after a specified number of failed login attempts. The pam_tally2.so module is the mechanism for enforcement, which locks an account for a 15-minute duration after 5 failed authentication attempts. This policy applies to all containers since they share the host kernel and authentication system.

GEN002870: Remove Unnecessary Packages

STIG mandates minimal packages in container images. Lightweight base images like Alpine at approximately 5MB should be used instead of full distributions like Ubuntu at approximately 70MB. The number of installed packages should remain minimal—ideally less than 20 packages. Unnecessary packages like apk-tools and ca-certificates should be removed if they are not required for application functionality.

GEN006100: File Permissions

STIG requires strict file permissions on all files in the container. The owner of application files should be the application user rather than root. Directories should have 700 permissions, which grants rwx access to the owner with nothing for group or others. Configuration files should have 600 permissions, which grants rw access to the owner with nothing for group or others. Executable binaries should have 755 permissions, which grants rwx access to the owner and rx access to group and others. This implements the principle of least privilege by ensuring no group or world-readable files exist in the container.

GEN006360: Audit Logging

STIG requires comprehensive audit logging of all file modifications and security-relevant events. The host-level audit daemon should be configured to log all writes and attribute changes on critical configuration files. The auditctl utility specifies watches on sensitive files and directories, recording all modifications. Verification that auditd is running and active ensures audit logs are continuously captured. These logs provide forensic evidence in the event of a security incident and demonstrate compliance with STIG requirements.

GEN006680: SSH Hardening (if SSH in container)

# NOT RECOMMENDED: SSH in containers# Instead use 'docker exec' or 'kubectl exec'# But if required for STIG: RUN apt-get install -y openssh-server && \    mkdir /var/run/sshd && \    echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \    echo "PasswordAuthentication no" >> /etc/ssh/sshd_config && \    echo "X11Forwarding no" >> /etc/ssh/sshd_config && \    ssh-keygen -t ed25519 -N "" -f /etc/ssh/ssh_host_ed25519_key

STIG Application Controls

APP000080: Configuration Management

# Store configuration in read-only volumesFROM alpine:3.19 # Config is provided via ConfigMap/Secret (Kubernetes)RUN mkdir -p /etc/app# /etc/app is mounted read-only from ConfigMap # Application reads from /etc/app but cannot modifyRUN chmod 555 /etc/app

APP000400: Authentication

# Strong authentication only (no hardcoded credentials)FROM alpine:3.19 # Credentials from environment variables or secretsENV DB_PASSWORD=""  # Must be set at runtimeENV API_KEY="" # Never log credentialsRUN export DB_PASSWORD=****

APP000480: Data Protection

# Encrypt all sensitive dataFROM alpine:3.19 RUN apk add --no-cache openssl # Application must use AES-256 encryption for sensitive dataCOPY app /appRUN chmod 755 /app/bin/encrypt.sh  # Encryption helper

APP000500: Error Handling

# No sensitive info in error messagesFROM alpine:3.19 # Application must:# - Not expose stack traces# - Not reveal system paths# - Not reveal database details# - Log errors securely RUN sed -i 's/debug=true/debug=false/' /app/config.conf

APP000700: Input Validation

# Application code must validate all inputfrom flask import Flask, requestimport bleach app = Flask(__name__) @app.route('/api/data', methods=['POST'])def handle_data():    # STIG: Validate and sanitize all input    data = request.json.get('input', '')     # Validate length    if len(data) > 1000:        return {'error': 'Input too long'}, 400     # Sanitize dangerous characters    safe_data = bleach.clean(data)     # Validate format    if not safe_data.isalnum():        return {'error': 'Invalid characters'}, 400     return {'status': 'ok'}

STIG Dockerfile Template

# Dockerfile: STIG-CompliantFROM alpine:3.19  # Minimal base # GEN000080: Non-root userRUN useradd -m -u 1000 -d /app appuser && \    usermod -L appuser # GEN002870: Remove unnecessary packagesRUN apk del apk-tools ca-certificates # GEN006100: Strict permissionsCOPY --chown=appuser:appuser app /appRUN chmod 700 /app && \    find /app -type f -exec chmod 600 {} \; # GEN000840: Disable unnecessary servicesRUN rm -rf /etc/init.d/sshd /etc/init.d/httpd # Configuration (STIG)ENV APP_MODE=productionENV LOG_LEVEL=warn  # Minimal loggingENV ENABLE_DEBUG=false  # Never enable debug in production # Security labels (STIG requirement)LABEL security.stig="true"LABEL compliance="DoD-Required"LABEL maintainer="security@company.com" # Health check (GEN000700)HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \  CMD /app/bin/healthcheck.sh || exit 1 USER appuserENTRYPOINT ["/app/bin/myapp"]

STIG Kubernetes Manifest

apiVersion: v1kind: Podmetadata:  name: myapp-stig  labels:    security.stig: "true"spec:  serviceAccountName: myapp-sa   securityContext:    runAsNonRoot: true    runAsUser: 1000    fsGroup: 1000    seLinuxOptions:      level: "s0:c123,c456"  # STIG level assignment   containers:  - name: myapp    image: myapp:stig-1.0.0    imagePullPolicy: Always     # GEN000080: Resource limits    resources:      limits:        cpu: "1"        memory: "512Mi"      requests:        cpu: "100m"        memory: "128Mi"     # GEN002800: Security context    securityContext:      privileged: false      allowPrivilegeEscalation: false      capabilities:        drop:        - ALL      readOnlyRootFilesystem: true     # GEN006100: Volumes    volumeMounts:    - name: app-config      mountPath: /etc/app      readOnly: true    - name: tmp      mountPath: /tmp    - name: logs      mountPath: /var/log     # GEN006360: Probes (monitoring)    livenessProbe:      exec:        command:        - /app/bin/healthcheck.sh      initialDelaySeconds: 10      periodSeconds: 30      failureThreshold: 3     readinessProbe:      exec:        command:        - /app/bin/readiness.sh      initialDelaySeconds: 5      periodSeconds: 10   # GEN001080: Node affinity (run on hardened nodes)  nodeSelector:    node.kubernetes.io/stig-hardened: "true"   # GEN002100: Pod security policy  affinity:    nodeAffinity:      requiredDuringSchedulingIgnoredDuringExecution:        nodeSelectorTerms:        - matchExpressions:          - key: security.stig            operator: In            values:            - "true"   volumes:  - name: app-config    configMap:      name: myapp-config      defaultMode: 0400  # Read-only for owner  - name: tmp    emptyDir:      medium: Memory      sizeLimit: 100Mi  - name: logs    emptyDir:      sizeLimit: 1Gi   # GEN003150: Audit logging  auditAnnotations:    kubernetes.io/audit-log: "true"   # GEN006500: Network policy  # (See networkPolicy in CIS hardening)   # GEN006700: No privileged namespace  # (Run in non-privileged namespace only)

STIG Compliance Verification

# Check image for STIG compliancecleanimg-init --stig-check --image myapp:stig-1.0.0 # Output:# GEN000080: Non-root user - PASS# GEN002870: Minimal packages (15 packages) - PASS# GEN006100: Strict file permissions - PASS# GEN006360: Audit logging capable - PASS# GEN001080: SSH hardening (no SSH - acceptable) - EXEMPTED# Overall STIG Level: 2 (Military-Grade)

STIG Levels

STIG compliance is measured across five maturity levels. Level 0 represents non-compliant images that run as root with unnecessary packages and no hardening. Level 1 achieves basic compliance with non-root user, minimal packages, and some hardening measures. Level 2 (Standard) implements STIG controls, strict file permissions, and audit logging—this is production-ready and recommended for most organizations. Level 3 (Enhanced) adds DoD-specific requirements and NSA guidelines. Level 4 (Maximum) achieves the strictest compliance with air-gapped systems, no network access, and full isolation.

CleanStart achieves Level 2 compliance by default, providing a balance between strict security controls and operational practicality suitable for production deployments.

Common STIG Violations

Violation: Running as Root

# BADENTRYPOINT ["/app/bin/myapp"]  # Runs as root # GOODRUN useradd -m appuserUSER appuserENTRYPOINT ["/app/bin/myapp"]

Violation: Unnecessary Packages

# BADFROM ubuntu:24.04RUN apt-get update && apt-get install -y \  curl wget git vim emacs ssh openssh-server # GOODFROM alpine:3.19RUN apk add --no-cache openssl

Violation: Debug Enabled

# BADENV DEBUG=trueENV LOG_LEVEL=debug # GOODENV DEBUG=falseENV LOG_LEVEL=warn

DoD Contractor Checklist

DoD contractors must ensure that they use the STIG Dockerfile template, run the STIG compliance check in their CI/CD pipeline, enforce policies via a Kubernetes admission controller, and enable audit logging. They must verify that no hardcoded secrets exist in any images, that encryption is applied for all sensitive data, and that input validation is implemented on all endpoints. Regular STIG re-assessments should be scheduled, and any exceptions must be documented with formal risk acceptance.

See Also

For further reading, see cis-hardening.md for industry standard CIS hardening, openscap-reference.md for automated STIG checking with OpenSCAP, and ../regulatory/fedramp-high.md for government compliance under FedRAMP.