Level 2 Technical Implementation Documentation

08c. Security Hardening Guidelines

Audience: Security Engineers, Platform Teams, Compliance Officers
Prerequisites: Understanding of zero-trust architecture, PKI, and security compliance frameworks

This section provides comprehensive security hardening configurations for sovereign cloud infrastructure, ensuring compliance with national security requirements while maintaining operational efficiency.

Sovereignty Principle: Security controls must not depend on any US-controlled infrastructure, certificate authorities, or security services. All cryptographic operations must use jurisdiction-controlled HSMs.

Compliance Framework Mapping

Control Domain UK (Cyber Essentials Plus) EU (NIS2) Canada (ITSG-33) Australia (ISM)
Access Control AC-1 to AC-5 Art. 21(2)(a) AC-1 to AC-25 ISM-0432 to ISM-0445
Encryption CE-3 Art. 21(2)(h) SC-8, SC-13 ISM-0457 to ISM-0494
Logging & Monitoring CE-5 Art. 21(2)(b) AU-1 to AU-16 ISM-0580 to ISM-0620
Incident Response CE-6 Art. 23 IR-1 to IR-10 ISM-0123 to ISM-0140
Supply Chain N/A Art. 21(2)(d) SA-12 ISM-1406 to ISM-1430

Security Control Categories

Network Security

Zero-trust network architecture with microsegmentation.

  • Default-deny network policies
  • Service mesh with mTLS (Istio/Linkerd)
  • Egress filtering with explicit allowlists
  • Network intrusion detection (Suricata)
  • DDoS protection at edge

Identity & Access

Centralised identity with least-privilege enforcement.

  • OIDC/SAML federation via Keycloak
  • Hardware MFA enforcement (FIDO2)
  • Just-in-time access provisioning
  • Privileged access management
  • Session recording for admin access

Data Protection

Encryption at rest and in transit with sovereign key management.

  • AES-256-GCM for data at rest
  • TLS 1.3 for data in transit
  • Sovereign HSM key storage (FIPS 140-2 L3)
  • Client-side encryption for sensitive data
  • Cryptographic key rotation

Container Security

Secure container runtime with image verification.

  • Image signing with Sigstore/Cosign
  • Vulnerability scanning (Trivy)
  • Runtime security (Falco)
  • Read-only root filesystems
  • Non-root container execution

Secrets Management

Centralised secrets with automatic rotation.

  • OpenBao deployment
  • Dynamic database credentials
  • PKI certificate automation
  • Kubernetes secrets encryption
  • External Secrets Operator

Audit & Compliance

Comprehensive logging with tamper-evident storage.

  • Centralised audit logging
  • Immutable log storage (WORM)
  • 7-year retention compliance
  • Automated compliance scanning
  • Security information & event management

Kubernetes Security Hardening

Pod Security Standards

# Restricted Pod Security Standard (most secure)
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

---
# Example compliant deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: secure-app
  template:
    metadata:
      labels:
        app: secure-app
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 10000
        runAsGroup: 10000
        fsGroup: 10000
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: app
          image: registry.sovereign.gov.uk/app:v1.2.3@sha256:abc123...
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop:
                - ALL
          resources:
            limits:
              memory: "512Mi"
              cpu: "500m"
            requests:
              memory: "256Mi"
              cpu: "250m"
          volumeMounts:
            - name: tmp
              mountPath: /tmp
            - name: cache
              mountPath: /var/cache
      volumes:
        - name: tmp
          emptyDir: {}
        - name: cache
          emptyDir: {}

Network Policies

# Default deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

---
# Allow specific application traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-app-traffic
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web-frontend
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
        - podSelector:
            matchLabels:
              app: ingress-controller
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: api-backend
      ports:
        - protocol: TCP
          port: 8080
    - to:
        - namespaceSelector:
            matchLabels:
              name: kube-system
        - podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53

OpenBao Configuration

Sovereign HSM Auto-Unseal

# vault-config.hcl

storage "raft" {
  path = "/vault/data"
  node_id = "vault-0"
}

listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_cert_file = "/vault/tls/tls.crt"
  tls_key_file  = "/vault/tls/tls.key"
  tls_min_version = "tls13"
}

# Sovereign HSM configuration (example: Thales Luna)
seal "pkcs11" {
  lib            = "/usr/lib/libCryptoki2_64.so"
  slot           = "0"
  pin            = "env:HSM_PIN"
  key_label      = "vault-unseal-key"
  mechanism      = "0x1085"  # CKM_AES_GCM
  hmac_mechanism = "0x0251"  # CKM_SHA256_HMAC
}

api_addr = "https://vault.sovereign.gov.uk:8200"
cluster_addr = "https://vault-0.vault-internal:8201"

ui = true

telemetry {
  prometheus_retention_time = "30s"
  disable_hostname = true
}

PKI Secrets Engine

# Enable PKI secrets engine for sovereign certificates
vault secrets enable pki

# Configure maximum TTL
vault secrets tune -max-lease-ttl=87600h pki

# Generate root CA (stored in HSM)
vault write pki/root/generate/internal \
    common_name="Sovereign Cloud Root CA" \
    issuer_name="sovereign-root-2024" \
    ttl=87600h \
    key_type="ec" \
    key_bits=384

# Configure CA and CRL URLs
vault write pki/config/urls \
    issuing_certificates="https://vault.sovereign.gov.uk:8200/v1/pki/ca" \
    crl_distribution_points="https://vault.sovereign.gov.uk:8200/v1/pki/crl"

# Create intermediate CA for workloads
vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=43800h pki_int

# Generate intermediate CSR
vault write -format=json pki_int/intermediate/generate/internal \
    common_name="Sovereign Cloud Intermediate CA" \
    issuer_name="sovereign-intermediate-2024" \
    key_type="ec" \
    key_bits=384 \
    | jq -r '.data.csr' > pki_intermediate.csr

# Sign intermediate with root
vault write -format=json pki/root/sign-intermediate \
    issuer_ref="sovereign-root-2024" \
    csr=@pki_intermediate.csr \
    format=pem_bundle \
    ttl="43800h" \
    | jq -r '.data.certificate' > intermediate.cert.pem

# Set signed intermediate
vault write pki_int/intermediate/set-signed \
    certificate=@intermediate.cert.pem

# Create role for issuing certificates
vault write pki_int/roles/sovereign-workloads \
    allowed_domains="sovereign.gov.uk,internal.sovereign" \
    allow_subdomains=true \
    max_ttl="720h" \
    key_type="ec" \
    key_bits=256

Audit Logging Configuration

Comprehensive Audit Policy

# Kubernetes Audit Policy
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  # Log all requests at the Metadata level
  - level: Metadata
    resources:
      - group: ""
        resources: ["secrets", "configmaps"]

  # Log pod exec/attach at RequestResponse level
  - level: RequestResponse
    resources:
      - group: ""
        resources: ["pods/exec", "pods/attach", "pods/portforward"]

  # Log changes to RBAC
  - level: RequestResponse
    resources:
      - group: "rbac.authorization.k8s.io"
        resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"]

  # Log authentication events
  - level: Metadata
    nonResourceURLs:
      - "/api*"
      - "/apis*"
      - "/healthz*"
      - "/livez*"
      - "/readyz*"

  # Log all other requests at Request level
  - level: Request
    omitStages:
      - RequestReceived

Immutable Log Storage

# Loki configuration for immutable audit logs
# loki-config.yaml

auth_enabled: true

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

common:
  path_prefix: /loki
  storage:
    s3:
      endpoint: minio.sovereign.gov.uk:9000
      bucketnames: audit-logs
      access_key_id: ${MINIO_ACCESS_KEY}
      secret_access_key: ${MINIO_SECRET_KEY}
      s3forcepathstyle: true
      insecure: false

schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: audit_index_
        period: 24h

storage_config:
  tsdb_shipper:
    active_index_directory: /loki/tsdb-index
    cache_location: /loki/tsdb-cache
    cache_ttl: 24h

compactor:
  working_directory: /loki/compactor
  compaction_interval: 10m
  retention_enabled: true
  retention_delete_delay: 2h
  retention_delete_worker_count: 150
  delete_request_store: s3

limits_config:
  retention_period: 2557d  # 7 years for compliance
  enforce_metric_name: false
  reject_old_samples: true
  reject_old_samples_max_age: 168h
  max_entries_limit_per_query: 5000

# MinIO bucket policy for WORM compliance
# Apply via mc admin policy
# {
#   "Version": "2012-10-17",
#   "Statement": [
#     {
#       "Effect": "Allow",
#       "Action": ["s3:PutObject"],
#       "Resource": "arn:aws:s3:::audit-logs/*"
#     },
#     {
#       "Effect": "Deny",
#       "Action": ["s3:DeleteObject", "s3:DeleteObjectVersion"],
#       "Resource": "arn:aws:s3:::audit-logs/*"
#     }
#   ]
# }

Security Scanning Pipeline

# .github/workflows/security-scan.yml

name: Security Scanning Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * *'  # Daily at 6 AM

jobs:
  container-scan:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4

      - name: Build container image
        run: docker build -t app:${{ github.sha }} .

      - name: Scan for vulnerabilities (Trivy)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'app:${{ github.sha }}'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'

      - name: Upload scan results
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'

  infrastructure-scan:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4

      - name: Scan OpenTofu (tfsec)
        uses: aquasecurity/tfsec-action@v1.0.0
        with:
          working_directory: opentofu/
          soft_fail: false

      - name: Scan Kubernetes manifests (kubesec)
        run: |
          curl -sSX POST --data-binary @kubernetes/deployment.yaml \
            https://v2.kubesec.io/scan | jq '.[] | select(.score < 5)'

  secrets-scan:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Scan for secrets (Gitleaks)
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  dependency-scan:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4

      - name: Check for vulnerable dependencies
        run: |
          # Python
          pip-audit --require-hashes --strict

          # Node.js
          npm audit --audit-level=high

          # Go
          govulncheck ./...

Related Documentation