This repository is designed so that the secure option is the default one. The controls below are all demonstrated by the manifests in this repo, and several are additionally enforced by policy.
Secrets are never committed in the clear. Two supported approaches, both with a
reference example under docs/examples/:
See docs/examples/sealedsecret.yaml. A
SealedSecret holds asymmetrically-encrypted data that only the in-cluster
sealed-secrets controller can decrypt. It is safe to commit to a public repo.
kubectl create secret generic sample-api-secrets \
--namespace sample-api-production \
--from-literal=DATABASE_URL='postgres://user:pass@db:5432/app' \
--dry-run=client -o yaml \
| kubeseal --controller-namespace kube-system --format yaml > sealedsecret.yamlSee docs/examples/externalsecret.yaml. Git
holds only a pointer to a secret in AWS Secrets Manager / GCP Secret Manager /
Vault; the operator materialises the real Kubernetes Secret. The service account
authenticates via workload identity (IRSA / Workload Identity) — no static
credentials anywhere in git. Rotation happens in the external store with no commit.
.gitignore additionally blocks *.dec.yaml, secrets/*.plain.yaml, and .env
to stop accidental commits of decrypted material.
Argo CD AppProjects constrain what each environment's apps may touch:
sample-api-nonprod— only this git repo as a source; only thesample-api-dev/sample-api-stagingnamespaces as destinations; a whitelist of allowed namespaced resource kinds; zero cluster-scoped resources.sample-api-prod— strictest: this repo only, thesample-api-productionnamespace only, and a deny sync window so automated sync can never touch production (manual sync only).
A compromised or misconfigured Application therefore cannot deploy an arbitrary repo, escape into another namespace, or create cluster-scoped objects.
Every namespace gets a default-deny-all NetworkPolicy (empty podSelector, both
Ingress and Egress) plus a narrow sample-api-allow policy that re-opens only:
- ingress to port 8000 from the ingress-controller namespace, the Prometheus
namespace (for
/metrics), and same-namespace pods; - egress to
kube-dnson port 53 for name resolution.
Nothing else can talk to or from the workload.
The container image and the Deployment both enforce:
runAsNonRoot: true,runAsUser: 10001(a dedicated unprivileged user baked into the image);allowPrivilegeEscalation: false;readOnlyRootFilesystem: true(with a writable/tmpemptyDir for scratch);capabilities.drop: [ALL];seccompProfile: RuntimeDefault;automountServiceAccountToken: false(the workload needs no API access).
Images are always pinned to an immutable tag. A mutable :latest would make the
git state a lie and break auditability and rollback. Promotion works by writing an
explicit tag with kustomize edit set image.
The controls above are enforced cluster-wide by the ClusterPolicy objects in
policies/, so a non-compliant manifest is rejected at admission (or
flagged in audit) even if someone forgets:
| Policy | Enforces | Action |
|---|---|---|
require-resource-limits |
CPU/memory requests and limits on every container | Enforce |
disallow-latest-tag |
explicit, non-:latest image tag |
Enforce |
require-non-root |
runAsNonRoot: true + allowPrivilegeEscalation: false |
Enforce |
require-pdb |
a PodDisruptionBudget for every multi-replica Deployment | Audit |
This is a portfolio/reference repository, not a production service. If you spot a security issue in the manifests or code, please open an issue describing it.