08c. Security Hardening Guidelines
Audience: Security Engineers, Platform Teams, Compliance Officers
Prerequisites: Understanding of zero-trust architecture, PKI, and security compliance frameworks
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
- Infrastructure Templates - Secure-by-default infrastructure code
- Operational Runbooks - Security incident response procedures
- Threat Assessment - Strategic security context