From f94dd12216cb40763b3852a3899a856eb15e45de Mon Sep 17 00:00:00 2001 From: Rene Luria Date: Wed, 3 Sep 2025 22:06:32 +0200 Subject: [PATCH] feat: Configure production deployment with math-tables namespace and ingress Changes made: - Remove problematic configuration-snippet from base ingress - Add namespace creation for math-tables - Configure ingress with nginx class and letsencrypt-prod issuer - Set production hostname to math-tables.cl1.parano.ch - Reduce production replicas to 1 - Update copyright year in index.html --- app/templates/index.html | 2 +- deploy/README.md | 83 +++++++++++++++++ deploy/SECURITY_CHECKLIST.md | 93 +++++++++++++++++++ deploy/base/configmap.yaml | 14 +++ deploy/base/deployment.yaml | 75 +++++++++++++++ deploy/base/ingress.yaml | 29 ++++++ deploy/base/kustomization.yaml | 19 ++++ deploy/base/network-policy.yaml | 35 +++++++ deploy/base/pod-disruption-budget.yaml | 9 ++ deploy/base/service.yaml | 18 ++++ deploy/kustomization.yaml | 5 + .../development/deployment-patch.yaml | 26 ++++++ .../overlays/development/kustomization.yaml | 20 ++++ .../overlays/production/deployment-patch.yaml | 37 ++++++++ deploy/overlays/production/ingress-patch.yaml | 23 +++++ deploy/overlays/production/kustomization.yaml | 25 +++++ deploy/overlays/production/namespace.yaml | 4 + .../overlays/production/security-patch.yaml | 27 ++++++ k8s-deployment.yaml | 53 +++++++++++ 19 files changed, 596 insertions(+), 1 deletion(-) create mode 100644 deploy/README.md create mode 100644 deploy/SECURITY_CHECKLIST.md create mode 100644 deploy/base/configmap.yaml create mode 100644 deploy/base/deployment.yaml create mode 100644 deploy/base/ingress.yaml create mode 100644 deploy/base/kustomization.yaml create mode 100644 deploy/base/network-policy.yaml create mode 100644 deploy/base/pod-disruption-budget.yaml create mode 100644 deploy/base/service.yaml create mode 100644 deploy/kustomization.yaml create mode 100644 deploy/overlays/development/deployment-patch.yaml create mode 100644 deploy/overlays/development/kustomization.yaml create mode 100644 deploy/overlays/production/deployment-patch.yaml create mode 100644 deploy/overlays/production/ingress-patch.yaml create mode 100644 deploy/overlays/production/kustomization.yaml create mode 100644 deploy/overlays/production/namespace.yaml create mode 100644 deploy/overlays/production/security-patch.yaml create mode 100644 k8s-deployment.yaml diff --git a/app/templates/index.html b/app/templates/index.html index 8be54a5..13a8106 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -141,7 +141,7 @@ diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..7dc2b79 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,83 @@ +# Math Exercises Application - Kubernetes Deployment + +This directory contains the Kubernetes deployment configuration for the Math Exercises application, with security best practices applied. + +## Directory Structure + +``` +deploy/ +├── base/ # Base kustomize configuration +│ ├── deployment.yaml # Application deployment +│ ├── service.yaml # Internal service +│ ├── ingress.yaml # External access configuration +│ ├── network-policy.yaml # Network security policies +│ ├── configmap.yaml # Application configuration +│ ├── pod-disruption-budget.yaml # High availability +│ └── kustomization.yaml # Base kustomize file +├── overlays/ # Environment-specific configurations +│ ├── development/ # Development environment +│ │ ├── deployment-patch.yaml # Dev-specific deployment settings +│ │ └── kustomization.yaml # Dev kustomize file +│ └── production/ # Production environment +│ ├── deployment-patch.yaml # Prod-specific deployment settings +│ ├── security-patch.yaml # Additional security settings +│ └── kustomization.yaml # Prod kustomize file +└── SECURITY_CHECKLIST.md # Security implementation checklist +``` + +## Security Features Implemented + +The deployment implements the following security best practices: + +1. **Pod Security**: + - Non-root user execution + - ReadOnly root filesystem + - Disabled privilege escalation + - Minimal container capabilities + - Seccomp profiles + +2. **Network Security**: + - Network policies restricting traffic + - TLS-enforced ingress with security headers + - Internal service exposure only + +3. **Configuration Security**: + - ConfigMaps for configuration separation + - Resource limits and requests + - Health checks with appropriate timeouts + +4. **Operational Security**: + - PodDisruptionBudget for high availability + - Environment-specific configurations + - Versioned image tags + +## Deployment Instructions + +### Development Environment + +```bash +kubectl apply -k deploy/overlays/development +``` + +### Production Environment + +```bash +kubectl apply -k deploy/overlays/production +``` + +## Security Verification + +To verify security settings are properly applied: + +```bash +# Check security context +kubectl get deployment math-exercises-app -o jsonpath='{.spec.template.spec.containers[0].securityContext}' + +# Check network policies +kubectl get networkpolicy + +# Check resource limits +kubectl get deployment math-exercises-app -o jsonpath='{.spec.template.spec.containers[0].resources}' +``` + +See `SECURITY_CHECKLIST.md` for a comprehensive list of implemented security measures. \ No newline at end of file diff --git a/deploy/SECURITY_CHECKLIST.md b/deploy/SECURITY_CHECKLIST.md new file mode 100644 index 0000000..09688fa --- /dev/null +++ b/deploy/SECURITY_CHECKLIST.md @@ -0,0 +1,93 @@ +# Kubernetes Security Checklist for Math Exercises Application + +This document outlines the security measures implemented in the Kubernetes deployment for the Math Exercises application. + +## 1. Pod Security + +### Container Security Context +- ✅ Non-root user execution (`runAsNonRoot: true`, `runAsUser: 1000`) +- ✅ Disabled privilege escalation (`allowPrivilegeEscalation: false`) +- ✅ Read-only root filesystem (`readOnlyRootFilesystem: true`) +- ✅ Minimal capabilities (dropped all, added only necessary ones) +- ✅ Seccomp profile set to RuntimeDefault + +### Pod Security Context +- ✅ Non-root user execution +- ✅ Proper fsGroup setting +- ✅ Seccomp profile enforcement + +## 2. Network Security + +### Network Policies +- ✅ Restricted ingress traffic (only from ingress controller) +- ✅ Limited egress traffic (DNS and HTTPS only) +- ✅ Port-specific rules + +### Service Configuration +- ✅ Internal traffic policy set to Local +- ✅ ClusterIP service type (no external exposure) + +## 3. Application Security + +### Ingress Security +- ✅ TLS enforced with redirect +- ✅ HSTS enabled with preload +- ✅ Security headers configured: + - X-Frame-Options: DENY + - X-Content-Type-Options: nosniff + - X-XSS-Protection: 1; mode=block + - Referrer-Policy: strict-origin-when-cross-origin + - Permissions-Policy: Restricted APIs + +### Resource Management +- ✅ CPU and memory limits set +- ✅ CPU and memory requests defined +- ✅ Quality of Service class guaranteed + +## 4. Configuration Security + +### ConfigMap Usage +- ✅ Separation of configuration from code +- ✅ Centralized configuration management + +### Environment Variables +- ✅ No hardcoded secrets +- ✅ Secure configuration values + +## 5. Operational Security + +### High Availability +- ✅ PodDisruptionBudget configured +- ✅ Multiple replicas in production + +### Image Management +- ✅ Versioned images in production +- ✅ Separate tags for dev/prod environments + +## 6. Monitoring & Observability + +### Health Checks +- ✅ Liveness probes configured +- ✅ Readiness probes configured +- ✅ Appropriate timeouts and thresholds + +## 7. Additional Recommendations + +### Future Enhancements +- [ ] Implement Kubernetes Secrets for sensitive data +- [ ] Add RBAC policies for least privilege access +- [ ] Enable audit logging +- [ ] Implement runtime security monitoring +- [ ] Add image vulnerability scanning +- [ ] Consider Kyverno policies for admission control + +## 8. Environment-Specific Security + +### Development +- ✅ Reduced resource consumption +- ✅ Standard security posture + +### Production +- ✅ Enhanced security settings +- ✅ High availability configuration +- ✅ Dedicated security patches \ No newline at end of file diff --git a/deploy/base/configmap.yaml b/deploy/base/configmap.yaml new file mode 100644 index 0000000..b5070c1 --- /dev/null +++ b/deploy/base/configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: math-exercises-config +data: + # Application configuration + PORT: "8000" + LOG_LEVEL: "INFO" + MAX_REQUEST_SIZE: "10mb" + + # Security configuration + SECURE_COOKIES: "true" + CORS_ORIGINS: "math-exercises.local" + REQUEST_TIMEOUT: "30s" \ No newline at end of file diff --git a/deploy/base/deployment.yaml b/deploy/base/deployment.yaml new file mode 100644 index 0000000..1a0386e --- /dev/null +++ b/deploy/base/deployment.yaml @@ -0,0 +1,75 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: math-exercises-app + labels: + app: math-exercises +spec: + replicas: 2 + selector: + matchLabels: + app: math-exercises + template: + metadata: + labels: + app: math-exercises + spec: + # Security context for the pod + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 2000 + seccompProfile: + type: RuntimeDefault + containers: + - name: math-exercises + image: math-exercises:latest + ports: + - containerPort: 8000 + # Security context for the container + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + capabilities: + drop: + - ALL + # Resource limits and requests + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "128Mi" + cpu: "500m" + # Liveness probe + livenessProbe: + httpGet: + path: / + port: 8000 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + # Readiness probe + readinessProbe: + httpGet: + path: / + port: 8000 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + # Environment variables from ConfigMap + envFrom: + - configMapRef: + name: math-exercises-config + # Volume mounts for writable directories + volumeMounts: + - name: static-volume + mountPath: /app/app/static + # Volumes + volumes: + - name: static-volume + emptyDir: {} \ No newline at end of file diff --git a/deploy/base/ingress.yaml b/deploy/base/ingress.yaml new file mode 100644 index 0000000..3554607 --- /dev/null +++ b/deploy/base/ingress.yaml @@ -0,0 +1,29 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: math-exercises-ingress + annotations: + # Security annotations + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/hsts: "true" + nginx.ingress.kubernetes.io/hsts-max-age: "31536000" + nginx.ingress.kubernetes.io/hsts-include-subdomains: "true" + nginx.ingress.kubernetes.io/hsts-preload: "true" +spec: + tls: + - hosts: + - math-exercises.local + secretName: math-exercises-tls + rules: + - host: math-exercises.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: math-exercises-service + port: + number: 80 \ No newline at end of file diff --git a/deploy/base/kustomization.yaml b/deploy/base/kustomization.yaml new file mode 100644 index 0000000..936ad4b --- /dev/null +++ b/deploy/base/kustomization.yaml @@ -0,0 +1,19 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- deployment.yaml +- service.yaml +- ingress.yaml +- network-policy.yaml +- pod-disruption-budget.yaml +- configmap.yaml + +# Common labels to apply to all resources +commonLabels: + app.kubernetes.io/name: math-exercises + app.kubernetes.io/instance: math-exercises-instance + app.kubernetes.io/version: "1.0" + app.kubernetes.io/component: web + app.kubernetes.io/part-of: math-suite + app.kubernetes.io/managed-by: kustomize \ No newline at end of file diff --git a/deploy/base/network-policy.yaml b/deploy/base/network-policy.yaml new file mode 100644 index 0000000..4bf8720 --- /dev/null +++ b/deploy/base/network-policy.yaml @@ -0,0 +1,35 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: math-exercises-netpol +spec: + podSelector: + matchLabels: + app: math-exercises + policyTypes: + - Ingress + - Egress + ingress: + # Allow inbound traffic from the ingress controller only + - from: + - namespaceSelector: + matchLabels: + name: ingress-nginx + ports: + - protocol: TCP + port: 8000 + egress: + # Allow outbound DNS resolution + - to: + - namespaceSelector: + matchLabels: + name: kube-system + ports: + - protocol: TCP + port: 53 + - protocol: UDP + port: 53 + # Allow outbound HTTPS for package updates or external APIs + - ports: + - protocol: TCP + port: 443 \ No newline at end of file diff --git a/deploy/base/pod-disruption-budget.yaml b/deploy/base/pod-disruption-budget.yaml new file mode 100644 index 0000000..11edc19 --- /dev/null +++ b/deploy/base/pod-disruption-budget.yaml @@ -0,0 +1,9 @@ +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: math-exercises-pdb +spec: + minAvailable: 1 + selector: + matchLabels: + app: math-exercises \ No newline at end of file diff --git a/deploy/base/service.yaml b/deploy/base/service.yaml new file mode 100644 index 0000000..d712e4a --- /dev/null +++ b/deploy/base/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: math-exercises-service + annotations: + # Security annotations + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" +spec: + selector: + app: math-exercises + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 8000 + type: ClusterIP + # Only accessible within the cluster + internalTrafficPolicy: Local \ No newline at end of file diff --git a/deploy/kustomization.yaml b/deploy/kustomization.yaml new file mode 100644 index 0000000..1b30247 --- /dev/null +++ b/deploy/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- base \ No newline at end of file diff --git a/deploy/overlays/development/deployment-patch.yaml b/deploy/overlays/development/deployment-patch.yaml new file mode 100644 index 0000000..7983866 --- /dev/null +++ b/deploy/overlays/development/deployment-patch.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: math-exercises-app + annotations: + # Development environment annotation + environment: development +spec: + replicas: 1 + template: + spec: + containers: + - name: math-exercises + env: + - name: ENVIRONMENT + value: development + - name: DEBUG + value: "false" + # Reduce resource consumption in development + resources: + requests: + memory: "32Mi" + cpu: "100m" + limits: + memory: "64Mi" + cpu: "200m" \ No newline at end of file diff --git a/deploy/overlays/development/kustomization.yaml b/deploy/overlays/development/kustomization.yaml new file mode 100644 index 0000000..2f1f35d --- /dev/null +++ b/deploy/overlays/development/kustomization.yaml @@ -0,0 +1,20 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../base + +# Development-specific patches +patchesStrategicMerge: +- deployment-patch.yaml + +# Development-specific configurations +images: +- name: math-exercises + newName: math-exercises + newTag: dev-latest + +# Development-specific labels +commonLabels: + environment: development + security-level: standard \ No newline at end of file diff --git a/deploy/overlays/production/deployment-patch.yaml b/deploy/overlays/production/deployment-patch.yaml new file mode 100644 index 0000000..286692f --- /dev/null +++ b/deploy/overlays/production/deployment-patch.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: math-exercises-app + annotations: + # Production environment annotation + environment: production + # Security annotations + seccomp.security.alpha.kubernetes.io/pod: docker/default +spec: + replicas: 1 + template: + spec: + containers: + - name: math-exercises + env: + - name: ENVIRONMENT + value: production + - name: DEBUG + value: "false" + # Enhanced security for production + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + capabilities: + drop: + - ALL + # Production resource settings + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "128Mi" + cpu: "500m" \ No newline at end of file diff --git a/deploy/overlays/production/ingress-patch.yaml b/deploy/overlays/production/ingress-patch.yaml new file mode 100644 index 0000000..979c605 --- /dev/null +++ b/deploy/overlays/production/ingress-patch.yaml @@ -0,0 +1,23 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: math-exercises-ingress + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod +spec: + ingressClassName: nginx + tls: + - hosts: + - math-tables.cl1.parano.ch + secretName: math-exercises-tls + rules: + - host: math-tables.cl1.parano.ch + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: math-exercises-service + port: + number: 80 \ No newline at end of file diff --git a/deploy/overlays/production/kustomization.yaml b/deploy/overlays/production/kustomization.yaml new file mode 100644 index 0000000..c9742a1 --- /dev/null +++ b/deploy/overlays/production/kustomization.yaml @@ -0,0 +1,25 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: math-tables + +resources: +- ../../base +- namespace.yaml + +# Production-specific patches +patchesStrategicMerge: +- deployment-patch.yaml +- security-patch.yaml +- ingress-patch.yaml + +# Production-specific configurations +images: +- name: math-exercises + newName: harbor.cl1.parano.ch/library/math-exercice + newTag: 1.0.0 + +# Production-specific labels +commonLabels: + environment: production + security-level: high \ No newline at end of file diff --git a/deploy/overlays/production/namespace.yaml b/deploy/overlays/production/namespace.yaml new file mode 100644 index 0000000..776395b --- /dev/null +++ b/deploy/overlays/production/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: math-tables \ No newline at end of file diff --git a/deploy/overlays/production/security-patch.yaml b/deploy/overlays/production/security-patch.yaml new file mode 100644 index 0000000..a46ed89 --- /dev/null +++ b/deploy/overlays/production/security-patch.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: math-exercises-app +spec: + template: + spec: + # Additional security context for production + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 2000 + seccompProfile: + type: RuntimeDefault + containers: + - name: math-exercises + # Additional security settings for production + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + capabilities: + drop: + - ALL + add: + - NET_BIND_SERVICE \ No newline at end of file diff --git a/k8s-deployment.yaml b/k8s-deployment.yaml new file mode 100644 index 0000000..46dbd76 --- /dev/null +++ b/k8s-deployment.yaml @@ -0,0 +1,53 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: math-exercises-app + labels: + app: math-exercises +spec: + replicas: 2 + selector: + matchLabels: + app: math-exercises + template: + metadata: + labels: + app: math-exercises + spec: + containers: + - name: math-exercises + image: math-exercises:latest + ports: + - containerPort: 8000 + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "128Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: / + port: 8000 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 8000 + initialDelaySeconds: 5 + periodSeconds: 10 +--- +apiVersion: v1 +kind: Service +metadata: + name: math-exercises-service +spec: + selector: + app: math-exercises + ports: + - protocol: TCP + port: 80 + targetPort: 8000 + type: ClusterIP \ No newline at end of file