<!-- AUTO-GENERATED by export-plugins.py — DO NOT EDIT -->
npx claudepluginhub frank-luongt/faos-skills-marketplace --plugin faos-security-engineerThis skill uses the workspace's default tool permissions.
Implements structured self-debugging workflow for AI agent failures: capture errors, diagnose patterns like loops or context overflow, apply contained recoveries, and generate introspection reports.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Containers introduce a unique attack surface spanning the image build pipeline, container runtime, orchestration layer, and host OS. Security must be applied at every stage of the container lifecycle:
| Stage | Threats | Key Controls |
|---|---|---|
| Build | Malicious base images, embedded secrets, bloat | Minimal images, scanning, signing |
| Registry | Image tampering, unauthorized access | Content trust, access control |
| Deploy | Misconfigured pods, excessive privileges | Admission control, PodSecurity |
| Runtime | Container escape, lateral movement, cryptomining | Seccomp, AppArmor, monitoring |
| Orchestration | RBAC bypass, API server exposure, secret leaks | NetworkPolicy, RBAC, external secrets |
This guide covers Docker image and build security, Kubernetes-specific hardening, and runtime protection. It is tailored for teams running containerized workloads on GKE but applies broadly to any Kubernetes distribution.
Scan images for known vulnerabilities before they reach production:
# Trivy: comprehensive vulnerability scanner
trivy image --severity HIGH,CRITICAL faos-api:latest
# Grype: fast alternative scanner
grype faos-api:latest --only-fixed --fail-on high
# Snyk: SaaS scanner with fix suggestions
snyk container test faos-api:latest --severity-threshold=high
# GCP Artifact Registry: automatic scanning
gcloud artifacts docker images list-vulnerabilities \
REGION-docker.pkg.dev/PROJECT/REPO/my-app:latest \
--format=json | jq '.[] | select(.vulnerability.effectiveSeverity == "CRITICAL")'
Integrate scanning into CI/CD pipelines with a gate:
# GitHub Actions: scan and fail on critical vulnerabilities
- name: Scan container image
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.IMAGE }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail the build on findings
Build minimal, secure container images following these principles:
Kubernetes PodSecurityStandards (PSS) define three progressive security profiles:
| Profile | Level of Security | Use Case |
|---|---|---|
| Privileged | None | System-level workloads (kube-system) |
| Baseline | Moderate | General workloads, prevents known escalations |
| Restricted | Strict | Security-sensitive and multi-tenant workloads |
Apply PSS enforcement at the namespace level:
# Enforce restricted profile on production namespace
kubectl label namespace faos-api \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
By default, all pods in a Kubernetes cluster can communicate with all other pods. NetworkPolicies implement microsegmentation:
Runtime security tools detect anomalous container behavior:
# Falco: runtime threat detection
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
--namespace falco-system \
--create-namespace \
--set falcosidekick.enabled=true \
--set falcosidekick.config.slack.webhookurl=$SLACK_WEBHOOK
# Falco detects:
# - Shell spawned in container
# - Sensitive file access (/etc/shadow, /etc/passwd)
# - Unexpected network connections
# - Privilege escalation attempts
# - Cryptocurrency miner signatures
Runtime protection layers:
| Technology | Protection Level | Performance Impact |
|---|---|---|
| Seccomp | System call filtering | Minimal |
| AppArmor | Mandatory access control profiles | Low |
| SELinux | Type enforcement and RBAC | Low |
| gVisor | User-space kernel (full syscall interception) | Moderate |
| Kata | VM-level isolation per container | Higher |
# ============================================
# Stage 1: Build dependencies
# ============================================
FROM python:3.12-slim AS builder
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# Create virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# ============================================
# Stage 2: Production image
# ============================================
FROM python:3.12-slim AS production
# Security: Create non-root user
RUN groupadd -r appuser && useradd -r -g appuser -d /app -s /sbin/nologin appuser
# Install only runtime dependencies (no gcc/build tools)
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
curl \
&& rm -rf /var/lib/apt/lists/*
# Copy virtual environment from builder
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Copy application code
WORKDIR /app
COPY --chown=appuser:appuser ./src ./src
# Security: No secrets in image (use env vars or mounted secrets)
# Security: Read-only filesystem compatible
# Security: Drop all capabilities in K8s securityContext
# Health check for Kubernetes probes
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Security: Run as non-root user
USER appuser
# Security: Use exec form (no shell injection risk)
ENTRYPOINT ["uvicorn", "src.main:app"]
CMD ["--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
Security features in this Dockerfile:
| Feature | Security Benefit |
|---|---|
| Multi-stage build | Build tools not in production image (smaller attack surface) |
python:3.12-slim | Minimal base image (fewer vulnerabilities) |
| Non-root user | Prevents container escape to host root |
--no-cache-dir | No pip cache (smaller image, no cached credentials) |
--no-install-recommends | Minimal OS packages |
rm -rf /var/lib/apt | No package manager cache |
| Exec form ENTRYPOINT | No shell injection via CMD override |
| HEALTHCHECK | Enables K8s liveness/readiness probes |
# 1. Default deny all ingress and egress traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: faos-api
spec:
podSelector: {} # Applies to all pods in namespace
policyTypes:
- Ingress
- Egress
---
# 2. Allow DNS resolution (required for service discovery)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: faos-api
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
---
# 3. Allow ingress from gateway to API pods only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-gateway-to-api
namespace: faos-api
spec:
podSelector:
matchLabels:
app: faos-api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: faos-gateway
podSelector:
matchLabels:
app: gateway
ports:
- protocol: TCP
port: 8000
---
# 4. Allow API pods to reach database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-to-database
namespace: faos-api
spec:
podSelector:
matchLabels:
app: faos-api
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/8 # Cloud SQL private IP range
ports:
- protocol: TCP
port: 5432 # PostgreSQL
---
# 5. Allow API pods to reach Redis
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-to-redis
namespace: faos-api
spec:
podSelector:
matchLabels:
app: faos-api
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: faos-cache
podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
# Pod specification compliant with PSS restricted profile
apiVersion: apps/v1
kind: Deployment
metadata:
name: faos-api
namespace: faos-api
spec:
replicas: 3
selector:
matchLabels:
app: faos-api
template:
metadata:
labels:
app: faos-api
spec:
# Security: Use dedicated service account (not default)
serviceAccountName: my-app-sa
automountServiceAccountToken: false # Disable unless needed
# Security: Prevent privilege escalation via hostPID/hostNetwork
hostPID: false
hostNetwork: false
hostIPC: false
# Security: Set filesystem group for shared volumes
securityContext:
runAsNonRoot: true
runAsUser: 65534 # nobody user
runAsGroup: 65534
fsGroup: 65534
seccompProfile:
type: RuntimeDefault
containers:
- name: faos-api
image: REGION-docker.pkg.dev/PROJECT/REPO/my-app@sha256:abc123...
ports:
- containerPort: 8000
protocol: TCP
# Security: Container-level security context
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
# Resource limits (prevent resource abuse)
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
# Health probes
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
# Mount secrets from external secret manager (not K8s Secrets)
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: faos-api-secrets
key: database-url
# Writable directories for temp files (read-only root filesystem)
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/.cache
volumes:
- name: tmp
emptyDir:
sizeLimit: 100Mi
- name: cache
emptyDir:
sizeLimit: 50Mi
runAsNonRoot: truecapabilities.drop: [ALL]readOnlyRootFilesystem: true and mount writable directories as emptyDir volumesrestricted level for production namespaces@sha256:...) rather than mutable tagsRuntimeDefault) for all containerslatest tag for production images -- it is mutable and unpredictableprivileged: true/var/run/docker.sock) to containershostPID, hostNetwork, or hostIPC unless absolutely requiredcluster-admin ClusterRole to application service accountsautomountServiceAccountToken: false by default)@sha256:...) not mutable tags.dockerignore excludes .env, .git, node_modules, and other sensitive filesrunAsNonRoot: true)capabilities.drop: [ALL])readOnlyRootFilesystem: true)RuntimeDefault or a custom profilerestricted or baseline level