diff --git a/deployments/base/README.md b/deployments/base/README.md new file mode 100644 index 0000000..c503811 --- /dev/null +++ b/deployments/base/README.md @@ -0,0 +1,61 @@ +# Deployment Base Template + +Production-ready Kubernetes deployment with **Pod Security Standards (PSS) restricted** compliance. + +## Security Features + +This template enforces the most restrictive Pod Security Standard: + +- ✅ **Non-root execution** - Pods run as UID 1000 +- ✅ **Read-only root filesystem** - Prevents runtime modifications +- ✅ **No privilege escalation** - `allowPrivilegeEscalation: false` +- ✅ **All capabilities dropped** - Minimal Linux capabilities +- ✅ **Seccomp profile** - RuntimeDefault seccomp filtering +- ✅ **Resource limits** - CPU and memory constraints + +## Usage + +### Deploy directly +```bash +kubectl apply -k . +``` + +### Use as a base with overlays +```yaml +# overlays/production/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ../../base +patches: + - path: replicas-patch.yaml +``` + +## Customization Points + +| Field | Default | Description | +|-------|---------|-------------| +| `replicas` | 2 | Number of pod replicas | +| `image` | nginx:1.27-alpine | Container image | +| `resources.requests.cpu` | 100m | CPU request | +| `resources.requests.memory` | 128Mi | Memory request | +| `resources.limits.cpu` | 500m | CPU limit | +| `resources.limits.memory` | 256Mi | Memory limit | + +## Pod Security Standards Reference + +The namespace is configured with PSS labels: + +```yaml +pod-security.kubernetes.io/enforce: restricted +pod-security.kubernetes.io/audit: restricted +pod-security.kubernetes.io/warn: restricted +``` + +See: https://kubernetes.io/docs/concepts/security/pod-security-standards/ + +## Health Probes + +- **Liveness**: `/healthz` - Restart if unhealthy +- **Readiness**: `/ready` - Remove from service if not ready +- **Startup**: `/healthz` - Allow up to 150s for startup diff --git a/deployments/base/deployment.yaml b/deployments/base/deployment.yaml new file mode 100644 index 0000000..b6734f5 --- /dev/null +++ b/deployments/base/deployment.yaml @@ -0,0 +1,116 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app + labels: + app.kubernetes.io/name: app + app.kubernetes.io/component: server +spec: + replicas: 2 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + selector: + matchLabels: + app.kubernetes.io/name: app + template: + metadata: + labels: + app.kubernetes.io/name: app + app.kubernetes.io/component: server + spec: + # Pod-level security context for PSS restricted compliance + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + + # Graceful shutdown + terminationGracePeriodSeconds: 30 + + containers: + - name: app + image: nginx:1.27-alpine + imagePullPolicy: IfNotPresent + + ports: + - name: http + containerPort: 8080 + protocol: TCP + + # Container-level security context (PSS restricted) + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + capabilities: + drop: + - ALL + + # Resource management - ALWAYS set these + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 256Mi + + # Health probes + livenessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + + readinessProbe: + httpGet: + path: /ready + port: http + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + + # Startup probe for slow-starting apps + startupProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 0 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 30 # 150 seconds max startup + + # Volume mounts for read-only root filesystem + volumeMounts: + - name: tmp + mountPath: /tmp + - name: cache + mountPath: /var/cache/nginx + - name: run + mountPath: /var/run + + volumes: + - name: tmp + emptyDir: {} + - name: cache + emptyDir: {} + - name: run + emptyDir: {} + + # Spread pods across nodes + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app.kubernetes.io/name: app diff --git a/deployments/base/kustomization.yaml b/deployments/base/kustomization.yaml new file mode 100644 index 0000000..f4eec9d --- /dev/null +++ b/deployments/base/kustomization.yaml @@ -0,0 +1,15 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - namespace.yaml + - deployment.yaml + - service.yaml + +labels: + - pairs: + app.kubernetes.io/managed-by: kustomize + includeSelectors: false + +# Default namespace for all resources +namespace: app-production diff --git a/deployments/base/namespace.yaml b/deployments/base/namespace.yaml new file mode 100644 index 0000000..9e4bcd1 --- /dev/null +++ b/deployments/base/namespace.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: app-production + labels: + # Pod Security Standards - enforce restricted profile + # See: https://kubernetes.io/docs/concepts/security/pod-security-standards/ + pod-security.kubernetes.io/enforce: restricted + pod-security.kubernetes.io/enforce-version: latest + pod-security.kubernetes.io/audit: restricted + pod-security.kubernetes.io/audit-version: latest + pod-security.kubernetes.io/warn: restricted + pod-security.kubernetes.io/warn-version: latest diff --git a/deployments/base/service.yaml b/deployments/base/service.yaml new file mode 100644 index 0000000..b8ba3b2 --- /dev/null +++ b/deployments/base/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: app + labels: + app.kubernetes.io/name: app +spec: + type: ClusterIP + selector: + app.kubernetes.io/name: app + ports: + - name: http + port: 80 + targetPort: http + protocol: TCP