AID-M-005.001: Secure Configuration Template Development
Pillars: infra
Lifecycle: scoping
Scope boundary
Owns authoring, versioning, review, and static validation of secure configuration templates, including immutable external sources, digest-pinned base images, dependency locks, and resolved placeholders. It does not deploy, enforce, or score runtime conformance.
AID-M-005.002: AID-M-005.001 creates and statically validates the secure templates; AID-M-005.002 converts approved requirements into signed policy-as-code, measurable posture criteria, and build or release gates. A reviewed template is an input to the gate, not evidence that a release or deployed environment conforms.
Implementation guidance
- AID-M-005.001-G001: Author, version, and statically validate secure Infrastructure-as-Code baseline templates.
- AID-M-005.001-G002: Author and review a digest-pinned, dependency-locked minimal container template.
- AID-M-005.001-G003: Author and review benchmark-traceable cluster policy templates without performing deployment enforcement.
- AID-M-005.001-G004: Author and validate a digest-pinnable, dependency-locked secure Jupyter Server image template.
AID-M-005.001-G001
Concept
Author a reusable Terraform module template that expresses encryption, no public IPs, least-privilege IAM, and restricted egress. Review and statically validate the template here; mandatory deployment and conformance enforcement belong to AID-M-005.002.
Terraform module (minimal, runnable)
# File: terraform/modules/secure_ml_instance/variables.tf
variable "instance_type" {
type = string
}
variable "vpc_id" {
type = string
}
variable "subnet_id" {
type = string
}
variable "kms_key_id" {
type = string
}
variable "name_prefix" {
type = string
}
variable "ami_id" {
type = string
validation {
condition = can(regex("^ami-[0-9a-f]{8,17}$", var.ami_id))
error_message = "ami_id must be an explicitly reviewed immutable AMI ID"
}
}
# File: terraform/modules/secure_ml_instance/main.tf
resource "aws_security_group" "ml_training_sg" {
name_prefix = "${var.name_prefix}-sg-"
vpc_id = var.vpc_id
# No inbound by default
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
}
resource "aws_iam_role" "ml_training_role" {
name = "${var.name_prefix}-role"
assume_role_policy = data.aws_iam_policy_document.ec2_assume.json
}
data "aws_iam_policy_document" "ec2_assume" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
resource "aws_iam_instance_profile" "profile" {
name = "${var.name_prefix}-profile"
role = aws_iam_role.ml_training_role.name
}
resource "aws_instance" "ml_training" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = var.subnet_id
vpc_security_group_ids = [aws_security_group.ml_training_sg.id]
associate_public_ip_address = false
monitoring = true
iam_instance_profile = aws_iam_instance_profile.profile.name
root_block_device {
encrypted = true
kms_key_id = var.kms_key_id
}
metadata_options {
http_tokens = "required" # IMDSv2
}
}
# File: terraform/envs/prod/main.tf (usage)
module "secure_ml_instance" {
source = "../../modules/secure_ml_instance"
name_prefix = "fraud-train"
instance_type = "m6i.xlarge"
vpc_id = var.vpc_id
subnet_id = var.subnet_id
kms_key_id = var.kms_key_id
ami_id = "ami-0123456789abcdef0" # replace through reviewed region-specific lock data
}
Action: Record the reviewed module version, provider lock file, region-specific AMI ID, and static-validation result as authoring evidence. Placeholder IDs, most_recent lookups, floating module sources, or missing .terraform.lock.hcl make the template INSUFFICIENT_DATA; AID-M-005.002 owns publication and enforcement.
AID-M-005.001-G002
Concept
Author a multi-stage, non-root template that copies only runtime artifacts. Every base must be supplied as a reviewed full digest reference and every Python dependency must come from a hash-locked file; missing locks are an authoring validation failure.
Multi-stage Dockerfile (runnable)
# File: Dockerfile
ARG PYTHON_BUILD_IMAGE
ARG DISTROLESS_RUNTIME_IMAGE
FROM ${PYTHON_BUILD_IMAGE} AS build
WORKDIR /app
COPY requirements.lock .
RUN pip install --no-cache-dir --require-hashes -r requirements.lock
COPY src/ src/
FROM ${DISTROLESS_RUNTIME_IMAGE}
WORKDIR /app
COPY --from=build /usr/local/lib/python3.10 /usr/local/lib/python3.10
COPY --from=build /app/src /app/src
USER 65532:65532
ENV PYTHONPATH=/usr/local/lib/python3.10/site-packages
ENTRYPOINT ["/usr/bin/python3","/app/src/main.py"]
Authoring validation: Require both build arguments to match ^[^@]+@sha256:[a-f0-9]{64}$, require every line in requirements.lock to carry an approved hash, build the template, and record the resulting image digest and static scan. A tag-only base, missing argument, floating dependency, or unhashed requirement is INSUFFICIENT_DATA. AID-M-005.002 owns registry publication, admission enforcement, and release measurement.
AID-M-005.001-G003
Concept
Translate an explicitly versioned benchmark requirement into a reviewed candidate policy template and maintain its control-to-policy traceability. Applying the template, evaluating live resources, and measuring conformance belong to AID-M-005.002.
Kyverno policy to drop NET_RAW (maps to CIS K8s 5.2.x)
# File: k8s/policies/deny-net-raw.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: drop-net-raw
spec:
rules:
- name: require-drop-net-raw
match:
any:
- resources:
kinds: [Pod]
validate:
message: "Containers must drop NET_RAW capability"
pattern:
spec:
containers:
- securityContext:
capabilities:
drop: ["NET_RAW"]
Pod Security Admission namespace template
# File: k8s/templates/ai-workloads-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: ai-workloads
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: "v1.30"
Action: Record the exact benchmark edition, Kubernetes API/minor version, template digest, policy-engine schema validation, and reviewer decision. A floating latest version, missing benchmark clause, unsupported cluster version, or unresolved value is INSUFFICIENT_DATA. AID-M-005.002 owns applying these templates and collecting live enforcement evidence.
AID-M-005.001-G004
Concept:
Jupyter Server 2 moved authentication settings from legacy NotebookApp and deprecated ServerApp.password/ServerApp.token fields to identity-provider configuration. Author the template with authentication enabled, a runtime-mounted password hash, localhost binding behind an authenticated same-pod proxy, and no baked credential. Deployment and admission enforcement remain in AID-M-005.002.
Step 1: Configure the current Jupyter Server identity provider
# File: jupyter/jupyter_server_config.py
from pathlib import Path
from traitlets.config import get_config
c = get_config()
password_hash_path = Path("/run/secrets/jupyter_password_hash")
if not password_hash_path.is_file():
raise RuntimeError("Jupyter password hash secret is missing")
password_hash = password_hash_path.read_text(encoding="utf-8").strip()
if not password_hash.startswith("argon2:"):
raise RuntimeError("Jupyter password hash must use Argon2")
c.ServerApp.identity_provider_class = "jupyter_server.auth.identity.PasswordIdentityProvider"
c.PasswordIdentityProvider.hashed_password = password_hash
c.PasswordIdentityProvider.password_required = True
c.PasswordIdentityProvider.allow_password_change = False
c.ServerApp.ip = "127.0.0.1"
c.ServerApp.port = 8888
c.ServerApp.open_browser = False
c.ServerApp.allow_remote_access = False
c.ServerApp.allow_root = False
c.ServerApp.allow_origin = ""
c.ServerApp.allow_credentials = False
c.ServerApp.disable_check_xsrf = False
c.ServerApp.trust_xheaders = False
c.ServerApp.terminals_enabled = False
c.ServerApp.root_dir = "/home/nbuser/work"
Generate the Argon2 hash interactively with jupyter server password or jupyter_server.auth.passwd(), then place only the resulting hash in the runtime secret. Do not set any token or password field to an empty string; that can disable authentication.
Step 2: Validate a non-root image template without credentials
# File: Dockerfile
ARG JUPYTER_BASE_IMAGE
FROM ${JUPYTER_BASE_IMAGE}
COPY jupyter/jupyter-requirements.lock /tmp/jupyter-requirements.lock
RUN pip install --no-cache-dir --require-hashes -r /tmp/jupyter-requirements.lock && rm /tmp/jupyter-requirements.lock && useradd --create-home --uid 10001 nbuser && install -d -o 10001 -g 10001 /home/nbuser/.jupyter /home/nbuser/work
COPY --chown=10001:10001 jupyter/jupyter_server_config.py /home/nbuser/.jupyter/jupyter_server_config.py
USER 10001:10001
WORKDIR /home/nbuser/work
EXPOSE 8888
CMD ["jupyter", "server", "--config=/home/nbuser/.jupyter/jupyter_server_config.py"]
Step 3: Author the deployment boundary and acceptance tests
Template a read-only mount for /run/secrets/jupyter_password_hash, a read-only root filesystem, dropped Linux capabilities, and an authenticated same-pod proxy or JupyterHub path to the localhost listener. Author an admission rule that rejects arbitrary notebook images and root containers, but leave its enforcement to AID-M-005.002.
Authoring validation: Require JUPYTER_BASE_IMAGE to be a full @sha256: reference, require every Jupyter dependency and transitive dependency in jupyter-requirements.lock to have an approved hash, build the template, inspect the effective Jupyter configuration, and define tests for direct-port isolation, unauthenticated challenge, and XSRF rejection. A mutable tag, unresolved base argument, unlocked dependency, legacy auth trait, or missing test is INSUFFICIENT_DATA.
Action: Version and review the image template, dependency lock, expected effective configuration, and acceptance-test definitions. AID-M-005.002 owns publication, admission, execution of release tests, and live conformance evidence.
Open-source tools
- OpenTofu
- Ansible Core
- Pulumi
- Docker Engine
- Podman
- OpenSCAP
- Kyverno
Source-available / open-weight tools
- TFLint release binaries (MPL-2.0 and Business Source License 1.1; source-available)
- HashiCorp Packer (Business Source License 1.1; source-available)
- HashiCorp Terraform Community (BUSL-1.1; source-available)
Commercial / hosted tools
- AWS CloudFormation
- HashiCorp Terraform Enterprise
- Red Hat Ansible Automation Platform
- Puppet Enterprise
Threat mappings
MITRE ATLAS
- N/A
MAESTRO
- N/A
OWASP LLM Top 10 2026
- N/A
OWASP ML Top 10 2023
- N/A
OWASP Top 10 for Agentic Applications 2026
- N/A
NIST Adversarial Machine Learning 2025
- N/A
Cisco Integrated AI Security and Safety Framework
- N/A
Google Secure AI Framework 2.0 - Risks
- N/A
Databricks AI Security Framework 3.0
- Platform 12.5: Poor security in the software development lifecycle