diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fc40aab2f..f94162097 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -74,6 +74,8 @@ jobs: ksail-version: "7.12.2" init: "false" validate: "true" + scan: "true" + scan-framework: "nsa" sops-age-key: ${{ secrets.SOPS_AGE_KEY }} hosts-file: ${{ vars.HOSTS_FILE }} root-ca-cert-file: ${{ vars.ROOT_CA_CERT_FILE }} diff --git a/k8s/bases/apps/fleetdm/helm-release.yaml b/k8s/bases/apps/fleetdm/helm-release.yaml index ab4a8df06..0f30bef01 100644 --- a/k8s/bases/apps/fleetdm/helm-release.yaml +++ b/k8s/bases/apps/fleetdm/helm-release.yaml @@ -13,6 +13,7 @@ spec: remediation: retries: -1 upgrade: + force: true remediation: retries: -1 remediateLastFailure: true @@ -152,6 +153,9 @@ spec: existingSecret: mysql secondary: replicaCount: ${fleetdm_mysql_secondary_replicas:=2} + persistence: + enabled: true + size: 20Gi primary: persistence: enabled: true @@ -179,11 +183,13 @@ spec: persistence: enabled: true size: 8Gi + storageClass: hcloud replica: replicaCount: ${fleetdm_redis_replicas:=2} persistence: enabled: true size: 8Gi + storageClass: hcloud metrics: enabled: true image: diff --git a/k8s/bases/apps/fleetdm/kustomization.yaml b/k8s/bases/apps/fleetdm/kustomization.yaml index 3a25cbb81..17abf8fe8 100644 --- a/k8s/bases/apps/fleetdm/kustomization.yaml +++ b/k8s/bases/apps/fleetdm/kustomization.yaml @@ -8,5 +8,6 @@ resources: - http-scaled-object.yaml - license-secret.yaml - mysql-secret.yaml + - networkpolicy.yaml - pod-disruption-budget.yaml - redis-secret.yaml diff --git a/k8s/bases/apps/fleetdm/networkpolicy.yaml b/k8s/bases/apps/fleetdm/networkpolicy.yaml new file mode 100644 index 000000000..0d3e47dd5 --- /dev/null +++ b/k8s/bases/apps/fleetdm/networkpolicy.yaml @@ -0,0 +1,46 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-fleetdm + namespace: fleetdm +spec: + endpointSelector: {} + ingress: + # Gateway ingress (direct from Cilium envoy) + - fromEntities: + - ingress + toPorts: + - ports: + - port: "8080" + protocol: TCP + # KEDA HTTP interceptor forwards traffic from gateway + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: keda + toPorts: + - ports: + - port: "8080" + protocol: TCP + # Intra-namespace (fleet → mysql, fleet → redis) + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: fleetdm + egress: + # Intra-namespace (fleet → mysql, fleet → redis) + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: fleetdm + # Kube API + - toEntities: + - kube-apiserver + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/apps/headlamp/kustomization.yaml b/k8s/bases/apps/headlamp/kustomization.yaml index 55de23346..e3d70b333 100644 --- a/k8s/bases/apps/headlamp/kustomization.yaml +++ b/k8s/bases/apps/headlamp/kustomization.yaml @@ -6,3 +6,4 @@ resources: - helm-repository.yaml - httproute.yaml - http-scaled-object.yaml + - networkpolicy.yaml diff --git a/k8s/bases/apps/headlamp/networkpolicy.yaml b/k8s/bases/apps/headlamp/networkpolicy.yaml new file mode 100644 index 000000000..52962c7a1 --- /dev/null +++ b/k8s/bases/apps/headlamp/networkpolicy.yaml @@ -0,0 +1,31 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-headlamp + namespace: headlamp +spec: + endpointSelector: {} + ingress: + # KEDA interceptor proxy routes traffic from gateway + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: keda + toPorts: + - ports: + - port: "4466" + protocol: TCP + egress: + # Kube API for dashboard + - toEntities: + - kube-apiserver + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/apps/homepage/kustomization.yaml b/k8s/bases/apps/homepage/kustomization.yaml index 80baa373b..fea85fe91 100644 --- a/k8s/bases/apps/homepage/kustomization.yaml +++ b/k8s/bases/apps/homepage/kustomization.yaml @@ -7,3 +7,4 @@ resources: - httproute.yaml - namespace.yaml - pod-disruption-budget.yaml + - networkpolicy.yaml diff --git a/k8s/bases/apps/homepage/networkpolicy.yaml b/k8s/bases/apps/homepage/networkpolicy.yaml new file mode 100644 index 000000000..857c75c2a --- /dev/null +++ b/k8s/bases/apps/homepage/networkpolicy.yaml @@ -0,0 +1,31 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-homepage + namespace: homepage +spec: + endpointSelector: {} + ingress: + # Traffic from oauth2-proxy (via gateway → oauth2-proxy → homepage) + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: oauth2-proxy + toPorts: + - ports: + - port: "3000" + protocol: TCP + egress: + # Kube API for widget data + - toEntities: + - kube-apiserver + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/apps/wedding-app/kustomization.yaml b/k8s/bases/apps/wedding-app/kustomization.yaml index 242684561..5b3feaba0 100644 --- a/k8s/bases/apps/wedding-app/kustomization.yaml +++ b/k8s/bases/apps/wedding-app/kustomization.yaml @@ -7,3 +7,4 @@ resources: - serviceaccount.yaml - sops-age-secret.enc.yaml - sync.yaml + - networkpolicy.yaml diff --git a/k8s/bases/apps/wedding-app/networkpolicy.yaml b/k8s/bases/apps/wedding-app/networkpolicy.yaml new file mode 100644 index 000000000..296f97a3b --- /dev/null +++ b/k8s/bases/apps/wedding-app/networkpolicy.yaml @@ -0,0 +1,56 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-wedding-app + namespace: wedding-app +spec: + endpointSelector: {} + ingress: + # Gateway ingress + - fromEntities: + - ingress + toPorts: + - ports: + - port: "3000" + protocol: TCP + # Intra-namespace (app → db, db replication) + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: wedding-app + # CNPG operator needs to reach DB status endpoint (port 8000) + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: cnpg-system + toPorts: + - ports: + - port: "8000" + protocol: TCP + - port: "5432" + protocol: TCP + # Metrics scraping from monitoring namespace + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + toPorts: + - ports: + - port: "9187" + protocol: TCP + egress: + # Intra-namespace (app → db) + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: wedding-app + # Kube API (for CNPG operator managing the cluster) + - toEntities: + - kube-apiserver + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/apps/whoami/kustomization.yaml b/k8s/bases/apps/whoami/kustomization.yaml index 55de23346..e3d70b333 100644 --- a/k8s/bases/apps/whoami/kustomization.yaml +++ b/k8s/bases/apps/whoami/kustomization.yaml @@ -6,3 +6,4 @@ resources: - helm-repository.yaml - httproute.yaml - http-scaled-object.yaml + - networkpolicy.yaml diff --git a/k8s/bases/apps/whoami/networkpolicy.yaml b/k8s/bases/apps/whoami/networkpolicy.yaml new file mode 100644 index 000000000..f16ef5f79 --- /dev/null +++ b/k8s/bases/apps/whoami/networkpolicy.yaml @@ -0,0 +1,28 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-whoami + namespace: whoami +spec: + endpointSelector: {} + ingress: + # KEDA interceptor proxy routes traffic from gateway + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: keda + toPorts: + - ports: + - port: "80" + protocol: TCP + egress: + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/cluster-policies/best-practices/add-default-deny.yaml b/k8s/bases/infrastructure/cluster-policies/best-practices/add-default-deny.yaml new file mode 100644 index 000000000..ae9842232 --- /dev/null +++ b/k8s/bases/infrastructure/cluster-policies/best-practices/add-default-deny.yaml @@ -0,0 +1,76 @@ +# Generates a default-deny CiliumNetworkPolicy and a DNS-allow policy +# in every namespace. This ensures zero-trust networking by default — +# workloads must explicitly allow the traffic they need. +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: add-default-deny + annotations: + policies.kyverno.io/title: Default Deny Network Policy + policies.kyverno.io/category: Networking, Best Practices + policies.kyverno.io/subject: CiliumNetworkPolicy + policies.kyverno.io/minversion: 1.6.0 + policies.kyverno.io/description: >- + Generates a CiliumNetworkPolicy that activates Cilium's whitelist + mode for all endpoints in a namespace (effectively deny-all), plus + a companion policy that allows DNS egress to kube-dns so pods can + still resolve names. +spec: + rules: + - name: generate-default-deny + match: + any: + - resources: + kinds: + - Namespace + exclude: + any: + - resources: + names: + - kube-system + - kube-public + - kube-node-lease + generate: + generateExisting: true + apiVersion: cilium.io/v2 + kind: CiliumNetworkPolicy + name: default-deny + synchronize: true + namespace: "{{request.object.metadata.name}}" + data: + spec: + endpointSelector: {} + - name: generate-allow-dns + match: + any: + - resources: + kinds: + - Namespace + exclude: + any: + - resources: + names: + - kube-system + - kube-public + - kube-node-lease + generate: + generateExisting: true + apiVersion: cilium.io/v2 + kind: CiliumNetworkPolicy + name: allow-dns + synchronize: true + namespace: "{{request.object.metadata.name}}" + data: + spec: + endpointSelector: {} + egress: + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/cluster-policies/best-practices/add-ns-quota.yaml b/k8s/bases/infrastructure/cluster-policies/best-practices/add-ns-quota.yaml index e4acd3c35..7c251f582 100644 --- a/k8s/bases/infrastructure/cluster-policies/best-practices/add-ns-quota.yaml +++ b/k8s/bases/infrastructure/cluster-policies/best-practices/add-ns-quota.yaml @@ -38,10 +38,10 @@ spec: data: spec: hard: - requests.cpu: "4" + requests.cpu: "8" requests.memory: 16Gi - limits.cpu: "4" - limits.memory: 16Gi + limits.cpu: "16" + limits.memory: 32Gi - name: generate-limitrange match: any: @@ -68,8 +68,8 @@ spec: limits: - default: cpu: 500m - memory: 1Gi + memory: 512Mi defaultRequest: - cpu: 200m - memory: 256Mi + cpu: 50m + memory: 128Mi type: Container diff --git a/k8s/bases/infrastructure/cluster-policies/flux/auto-vpa.yaml b/k8s/bases/infrastructure/cluster-policies/flux/auto-vpa.yaml index c0d572bf0..9ef3f15a4 100644 --- a/k8s/bases/infrastructure/cluster-policies/flux/auto-vpa.yaml +++ b/k8s/bases/infrastructure/cluster-policies/flux/auto-vpa.yaml @@ -7,13 +7,13 @@ metadata: policies.kyverno.io/title: Auto VPA policies.kyverno.io/category: Autoscaling policies.kyverno.io/severity: low - policies.kyverno.io/subject: Deployment,StatefulSet + policies.kyverno.io/subject: Deployment,StatefulSet,DaemonSet policies.kyverno.io/description: >- - Generates a VerticalPodAutoscaler for every Deployment and - StatefulSet outside kube-system. VPA is memory-only - (controlledResources: ["memory"]) so it does not conflict with - KEDA / HPA horizontal scaling on CPU. - Ref: https://brtkwr.com/posts/2026-02-07-running-hpa-and-vpa-together-on-kubernetes/ + Generates a VerticalPodAutoscaler for every Deployment, StatefulSet, + and DaemonSet outside kube-system. VPA controls both CPU and memory + to ensure workloads do not over-request resources. KEDA-managed + workloads use HTTP request rate (not CPU) for horizontal scaling, + so VPA CPU management does not conflict. spec: rules: - name: generate-vpa-for-deployment @@ -43,8 +43,9 @@ spec: resourcePolicy: containerPolicies: - containerName: "*" - controlledResources: ["memory"] + controlledResources: ["cpu", "memory"] minAllowed: + cpu: "10m" memory: "64Mi" - name: generate-vpa-for-statefulset match: @@ -73,6 +74,38 @@ spec: resourcePolicy: containerPolicies: - containerName: "*" - controlledResources: ["memory"] + controlledResources: ["cpu", "memory"] minAllowed: + cpu: "10m" + memory: "64Mi" + - name: generate-vpa-for-daemonset + match: + resources: + kinds: + - DaemonSet + exclude: + resources: + namespaces: + - kube-system + generate: + apiVersion: autoscaling.k8s.io/v1 + kind: VerticalPodAutoscaler + name: "{{request.object.metadata.name}}" + namespace: "{{request.object.metadata.namespace}}" + synchronize: true + data: + spec: + targetRef: + apiVersion: apps/v1 + kind: DaemonSet + name: "{{request.object.metadata.name}}" + updatePolicy: + updateMode: "Auto" + minReplicas: 1 + resourcePolicy: + containerPolicies: + - containerName: "*" + controlledResources: ["cpu", "memory"] + minAllowed: + cpu: "10m" memory: "64Mi" diff --git a/k8s/bases/infrastructure/cluster-policies/kustomization.yaml b/k8s/bases/infrastructure/cluster-policies/kustomization.yaml index 991c71b71..68b21edca 100644 --- a/k8s/bases/infrastructure/cluster-policies/kustomization.yaml +++ b/k8s/bases/infrastructure/cluster-policies/kustomization.yaml @@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: + - best-practices/add-default-deny.yaml - best-practices/add-ns-quota.yaml - flux/auto-vpa.yaml - flux/helm-release-enable-tests.yaml diff --git a/k8s/bases/infrastructure/controllers/auth-proxy/deployment.yaml b/k8s/bases/infrastructure/controllers/auth-proxy/deployment.yaml index 26e9b5a0c..ecf26e626 100644 --- a/k8s/bases/infrastructure/controllers/auth-proxy/deployment.yaml +++ b/k8s/bases/infrastructure/controllers/auth-proxy/deployment.yaml @@ -30,6 +30,10 @@ spec: labelSelector: matchLabels: app: auth-proxy + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault containers: - name: traefik image: docker.io/library/traefik:v3.6 @@ -37,6 +41,13 @@ spec: - --configFile=/etc/traefik/traefik.yaml ports: - containerPort: 8080 + securityContext: + runAsNonRoot: true + runAsUser: 65532 + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + readOnlyRootFilesystem: true volumeMounts: - name: config mountPath: /etc/traefik diff --git a/k8s/bases/infrastructure/controllers/cert-manager/kustomization.yaml b/k8s/bases/infrastructure/controllers/cert-manager/kustomization.yaml index 7edec9cc4..d47b36be5 100644 --- a/k8s/bases/infrastructure/controllers/cert-manager/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/cert-manager/kustomization.yaml @@ -4,3 +4,4 @@ resources: - namespace.yaml - helm-release.yaml - helm-repository.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/cert-manager/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/cert-manager/networkpolicy.yaml new file mode 100644 index 000000000..94db8f4b0 --- /dev/null +++ b/k8s/bases/infrastructure/controllers/cert-manager/networkpolicy.yaml @@ -0,0 +1,56 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-cert-manager + namespace: cert-manager +spec: + endpointSelector: {} + ingress: + # Webhook traffic from kube-apiserver (hostNetwork on control plane nodes) + - fromEntities: + - kube-apiserver + - remote-node + - host + toPorts: + - ports: + - port: "10250" + protocol: TCP + - port: "6443" + protocol: TCP + # Metrics scraping from monitoring + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + toPorts: + - ports: + - port: "9402" + protocol: TCP + egress: + # ACME challenges / CA communication + - toEntities: + - world + toPorts: + - ports: + - port: "443" + protocol: TCP + - port: "80" + protocol: TCP + # DNS-01 challenge verification queries external nameservers + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP + # Kube API for managing certificates + - toEntities: + - kube-apiserver + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/cloudnative-pg/kustomization.yaml b/k8s/bases/infrastructure/controllers/cloudnative-pg/kustomization.yaml index 34f97aec9..6ed64f866 100644 --- a/k8s/bases/infrastructure/controllers/cloudnative-pg/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/cloudnative-pg/kustomization.yaml @@ -6,3 +6,4 @@ resources: - helm-repository.yaml - pod-disruption-budget.yaml - r2-credentials-secret.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/cloudnative-pg/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/cloudnative-pg/networkpolicy.yaml new file mode 100644 index 000000000..131e13059 --- /dev/null +++ b/k8s/bases/infrastructure/controllers/cloudnative-pg/networkpolicy.yaml @@ -0,0 +1,43 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-cnpg + namespace: cnpg-system +spec: + endpointSelector: {} + ingress: + # Webhook from kube-apiserver (hostNetwork on control plane nodes) + - fromEntities: + - kube-apiserver + - remote-node + - host + toPorts: + - ports: + - port: "9443" + protocol: TCP + egress: + # Kube API for managing PG clusters + - toEntities: + - kube-apiserver + # Reach managed PostgreSQL instances in any namespace (status + postgres ports) + - toEndpoints: + - matchExpressions: + - key: k8s:io.kubernetes.pod.namespace + operator: Exists + toPorts: + - ports: + - port: "8000" + protocol: TCP + - port: "5432" + protocol: TCP + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/dex/kustomization.yaml b/k8s/bases/infrastructure/controllers/dex/kustomization.yaml index 2eb0946bd..5365a2272 100644 --- a/k8s/bases/infrastructure/controllers/dex/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/dex/kustomization.yaml @@ -5,3 +5,4 @@ resources: - helm-release.yaml - helm-repository.yaml - httproute.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/dex/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/dex/networkpolicy.yaml new file mode 100644 index 000000000..f42376533 --- /dev/null +++ b/k8s/bases/infrastructure/controllers/dex/networkpolicy.yaml @@ -0,0 +1,47 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-dex + namespace: dex +spec: + endpointSelector: {} + ingress: + # Gateway ingress for OIDC endpoints + - fromEntities: + - ingress + toPorts: + - ports: + - port: "5556" + protocol: TCP + # gRPC from oauth2-proxy + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: oauth2-proxy + toPorts: + - ports: + - port: "5556" + protocol: TCP + - port: "5558" + protocol: TCP + egress: + # Kube API for reading secrets + - toEntities: + - kube-apiserver + # GitHub/OIDC upstream connectors + - toEntities: + - world + toPorts: + - ports: + - port: "443" + protocol: TCP + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/flux-operator/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/flux-operator/networkpolicy.yaml index 1354589ab..f261d452e 100644 --- a/k8s/bases/infrastructure/controllers/flux-operator/networkpolicy.yaml +++ b/k8s/bases/infrastructure/controllers/flux-operator/networkpolicy.yaml @@ -1,17 +1,48 @@ apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: - name: allow-gateway-to-flux-web + name: allow-flux namespace: flux-system spec: - endpointSelector: - matchLabels: - app.kubernetes.io/instance: flux-operator - app.kubernetes.io/name: flux-operator + endpointSelector: {} ingress: + # Gateway ingress for flux-web UI - fromEntities: - ingress toPorts: - ports: - port: "9080" protocol: TCP + # Webhook notifications + - fromEntities: + - world + toPorts: + - ports: + - port: "80" + protocol: TCP + # Metrics scraping + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + egress: + # Kube API + - toEntities: + - kube-apiserver + # OCI registries (GHCR, etc) + - toEntities: + - world + toPorts: + - ports: + - port: "443" + protocol: TCP + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/keda/kustomization.yaml b/k8s/bases/infrastructure/controllers/keda/kustomization.yaml index 7edec9cc4..d47b36be5 100644 --- a/k8s/bases/infrastructure/controllers/keda/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/keda/kustomization.yaml @@ -4,3 +4,4 @@ resources: - namespace.yaml - helm-release.yaml - helm-repository.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/keda/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/keda/networkpolicy.yaml new file mode 100644 index 000000000..66a8d2681 --- /dev/null +++ b/k8s/bases/infrastructure/controllers/keda/networkpolicy.yaml @@ -0,0 +1,53 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-keda + namespace: keda +spec: + endpointSelector: {} + ingress: + # Gateway ingress to interceptor proxy + - fromEntities: + - ingress + toPorts: + - ports: + - port: "8080" + protocol: TCP + # Webhook from kube-apiserver (hostNetwork on control plane nodes) + - fromEntities: + - kube-apiserver + - remote-node + - host + toPorts: + - ports: + - port: "9443" + protocol: TCP + - port: "6443" + protocol: TCP + # Intra-namespace communication (scaler→interceptor:9090, etc.) + - fromEndpoints: + - {} + # Metrics scraping + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + egress: + # Kube API for watching scalers + - toEntities: + - kube-apiserver + # Reach backend services in any namespace + - toEndpoints: + - matchExpressions: + - key: k8s:io.kubernetes.pod.namespace + operator: Exists + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/kube-prometheus-stack/kustomization.yaml b/k8s/bases/infrastructure/controllers/kube-prometheus-stack/kustomization.yaml index 50ee4c1d6..6a52b6bb6 100644 --- a/k8s/bases/infrastructure/controllers/kube-prometheus-stack/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/kube-prometheus-stack/kustomization.yaml @@ -5,3 +5,4 @@ resources: - helm-repository.yaml - webhook-secret.yaml - helm-release.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/kube-prometheus-stack/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/kube-prometheus-stack/networkpolicy.yaml new file mode 100644 index 000000000..b3f245f2f --- /dev/null +++ b/k8s/bases/infrastructure/controllers/kube-prometheus-stack/networkpolicy.yaml @@ -0,0 +1,56 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-monitoring + namespace: monitoring +spec: + endpointSelector: {} + ingress: + # Intra-namespace (prometheus → alertmanager, etc) + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + # Webhook from kube-apiserver (hostNetwork on control plane nodes) + - fromEntities: + - kube-apiserver + - remote-node + - host + toPorts: + - ports: + - port: "10250" + protocol: TCP + egress: + # Kube API for service discovery + - toEntities: + - kube-apiserver + # Scrape targets in all namespaces + - toEndpoints: + - matchExpressions: + - key: k8s:io.kubernetes.pod.namespace + operator: Exists + # Scrape node-exporter on nodes + - toEntities: + - host + - remote-node + # Alertmanager webhooks (Slack, etc) + - toEntities: + - world + toPorts: + - ports: + - port: "443" + protocol: TCP + # Intra-namespace + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/kubescape/kustomization.yaml b/k8s/bases/infrastructure/controllers/kubescape/kustomization.yaml index 7edec9cc4..d47b36be5 100644 --- a/k8s/bases/infrastructure/controllers/kubescape/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/kubescape/kustomization.yaml @@ -4,3 +4,4 @@ resources: - namespace.yaml - helm-release.yaml - helm-repository.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/kubescape/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/kubescape/networkpolicy.yaml new file mode 100644 index 000000000..40fc190ae --- /dev/null +++ b/k8s/bases/infrastructure/controllers/kubescape/networkpolicy.yaml @@ -0,0 +1,47 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-kubescape + namespace: kubescape +spec: + endpointSelector: {} + ingress: + # Webhook from kube-apiserver (hostNetwork on control plane nodes) + - fromEntities: + - kube-apiserver + - remote-node + - host + toPorts: + - ports: + - port: "8443" + protocol: TCP + # Intra-namespace communication + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kubescape + egress: + # Kube API for scanning + - toEntities: + - kube-apiserver + # Kubescape cloud backend + - toEntities: + - world + toPorts: + - ports: + - port: "443" + protocol: TCP + # Intra-namespace communication + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kubescape + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/kyverno/helm-release.yaml b/k8s/bases/infrastructure/controllers/kyverno/helm-release.yaml index abc324bb5..0a8a750de 100644 --- a/k8s/bases/infrastructure/controllers/kyverno/helm-release.yaml +++ b/k8s/bases/infrastructure/controllers/kyverno/helm-release.yaml @@ -46,6 +46,20 @@ spec: - update - patch - delete + # The add-default-deny ClusterPolicy generates CiliumNetworkPolicy + # objects in every namespace. + - apiGroups: + - cilium.io + resources: + - ciliumnetworkpolicies + verbs: + - get + - list + - watch + - create + - update + - patch + - delete updateStrategy: type: RollingUpdate rollingUpdate: @@ -79,6 +93,18 @@ spec: - update - patch - delete + - apiGroups: + - cilium.io + resources: + - ciliumnetworkpolicies + verbs: + - get + - list + - watch + - create + - update + - patch + - delete reportsController: replicas: ${kyverno_reports_replicas:=1} cleanupController: diff --git a/k8s/bases/infrastructure/controllers/kyverno/kustomization.yaml b/k8s/bases/infrastructure/controllers/kyverno/kustomization.yaml index 7edec9cc4..d47b36be5 100644 --- a/k8s/bases/infrastructure/controllers/kyverno/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/kyverno/kustomization.yaml @@ -4,3 +4,4 @@ resources: - namespace.yaml - helm-release.yaml - helm-repository.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/kyverno/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/kyverno/networkpolicy.yaml new file mode 100644 index 000000000..ba6bf0334 --- /dev/null +++ b/k8s/bases/infrastructure/controllers/kyverno/networkpolicy.yaml @@ -0,0 +1,40 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-kyverno + namespace: kyverno +spec: + endpointSelector: {} + ingress: + # Webhook from kube-apiserver (hostNetwork on control plane nodes) + - fromEntities: + - kube-apiserver + - remote-node + - host + toPorts: + - ports: + - port: "9443" + protocol: TCP + # Metrics scraping + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + toPorts: + - ports: + - port: "8000" + protocol: TCP + egress: + # Kube API for policy enforcement + - toEntities: + - kube-apiserver + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/oauth2-proxy/kustomization.yaml b/k8s/bases/infrastructure/controllers/oauth2-proxy/kustomization.yaml index bb6c9d7d4..9407175ad 100644 --- a/k8s/bases/infrastructure/controllers/oauth2-proxy/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/oauth2-proxy/kustomization.yaml @@ -7,3 +7,4 @@ resources: - httproute.yaml - namespace.yaml - reference-grant.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/oauth2-proxy/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/oauth2-proxy/networkpolicy.yaml new file mode 100644 index 000000000..903f4fe4b --- /dev/null +++ b/k8s/bases/infrastructure/controllers/oauth2-proxy/networkpolicy.yaml @@ -0,0 +1,51 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-oauth2-proxy + namespace: oauth2-proxy +spec: + endpointSelector: {} + ingress: + # Gateway ingress + - fromEntities: + - ingress + toPorts: + - ports: + - port: "8080" + protocol: TCP + - port: "4180" + protocol: TCP + egress: + # Dex for OIDC + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: dex + toPorts: + - ports: + - port: "5556" + protocol: TCP + # Upstream backends (homepage, etc) + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: homepage + # GitHub OAuth + - toEntities: + - world + toPorts: + - ports: + - port: "443" + protocol: TCP + # Kube API + - toEntities: + - kube-apiserver + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/opencost/kustomization.yaml b/k8s/bases/infrastructure/controllers/opencost/kustomization.yaml index 7edec9cc4..d47b36be5 100644 --- a/k8s/bases/infrastructure/controllers/opencost/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/opencost/kustomization.yaml @@ -4,3 +4,4 @@ resources: - namespace.yaml - helm-release.yaml - helm-repository.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/opencost/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/opencost/networkpolicy.yaml new file mode 100644 index 000000000..e3c50f841 --- /dev/null +++ b/k8s/bases/infrastructure/controllers/opencost/networkpolicy.yaml @@ -0,0 +1,41 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-opencost + namespace: opencost +spec: + endpointSelector: {} + ingress: + # Metrics scraping from monitoring + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + toPorts: + - ports: + - port: "9003" + protocol: TCP + - port: "9090" + protocol: TCP + egress: + # Kube API + - toEntities: + - kube-apiserver + # Prometheus for cost data + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + toPorts: + - ports: + - port: "9090" + protocol: TCP + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/reloader/kustomization.yaml b/k8s/bases/infrastructure/controllers/reloader/kustomization.yaml index 7edec9cc4..d47b36be5 100644 --- a/k8s/bases/infrastructure/controllers/reloader/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/reloader/kustomization.yaml @@ -4,3 +4,4 @@ resources: - namespace.yaml - helm-release.yaml - helm-repository.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/reloader/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/reloader/networkpolicy.yaml new file mode 100644 index 000000000..73b33cc37 --- /dev/null +++ b/k8s/bases/infrastructure/controllers/reloader/networkpolicy.yaml @@ -0,0 +1,22 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-reloader + namespace: reloader +spec: + endpointSelector: {} + egress: + # Kube API for watching configmaps/secrets + - toEntities: + - kube-apiserver + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/velero/kustomization.yaml b/k8s/bases/infrastructure/controllers/velero/kustomization.yaml index f33b5e2db..42ae25ce8 100644 --- a/k8s/bases/infrastructure/controllers/velero/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/velero/kustomization.yaml @@ -5,3 +5,4 @@ resources: - helm-repository.yaml - credentials-secret.yaml - helm-release.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/velero/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/velero/networkpolicy.yaml new file mode 100644 index 000000000..fd0e93e20 --- /dev/null +++ b/k8s/bases/infrastructure/controllers/velero/networkpolicy.yaml @@ -0,0 +1,38 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-velero + namespace: velero +spec: + endpointSelector: {} + ingress: + # Metrics scraping + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + toPorts: + - ports: + - port: "8085" + protocol: TCP + egress: + # Kube API for backup operations + - toEntities: + - kube-apiserver + # S3-compatible backup target + - toEntities: + - world + toPorts: + - ports: + - port: "443" + protocol: TCP + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/helm-release.yaml b/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/helm-release.yaml index cdf13b49e..4a7e98c2c 100644 --- a/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/helm-release.yaml +++ b/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/helm-release.yaml @@ -5,7 +5,6 @@ metadata: namespace: vertical-pod-autoscaler labels: helm.toolkit.fluxcd.io/crds: enabled - helm.toolkit.fluxcd.io/helm-test: enabled helm.toolkit.fluxcd.io/remediation: enabled spec: interval: 2m @@ -16,6 +15,8 @@ spec: remediation: retries: -1 remediateLastFailure: true + test: + enable: false chart: spec: chart: vpa diff --git a/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/kustomization.yaml b/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/kustomization.yaml index 7edec9cc4..d47b36be5 100644 --- a/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/kustomization.yaml +++ b/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/kustomization.yaml @@ -4,3 +4,4 @@ resources: - namespace.yaml - helm-release.yaml - helm-repository.yaml + - networkpolicy.yaml diff --git a/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/networkpolicy.yaml b/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/networkpolicy.yaml new file mode 100644 index 000000000..607ff1f35 --- /dev/null +++ b/k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/networkpolicy.yaml @@ -0,0 +1,36 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-vpa + namespace: vertical-pod-autoscaler +spec: + endpointSelector: {} + ingress: + # Webhook from kube-apiserver (hostNetwork on control plane nodes) + - fromEntities: + - kube-apiserver + - remote-node + - host + toPorts: + - ports: + - port: "8000" + protocol: TCP + egress: + # Kube API for managing VPAs + - toEntities: + - kube-apiserver + # Metrics server + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/clusters/prod/variables/variables-cluster-config-map.yaml b/k8s/clusters/prod/variables/variables-cluster-config-map.yaml index 6a7f3637e..fe894a4a9 100644 --- a/k8s/clusters/prod/variables/variables-cluster-config-map.yaml +++ b/k8s/clusters/prod/variables/variables-cluster-config-map.yaml @@ -9,9 +9,9 @@ data: domain_regex: platform\.devantler\.tech domain: platform.devantler.tech github_app_client_id: Iv23limfvbk93bAXZI6b - issuer_group: cert-manager.k8s.cloudflare.com - issuer_kind: ClusterOriginIssuer - issuer_name: cloudflare-origin + issuer_group: cert-manager.io + issuer_kind: ClusterIssuer + issuer_name: letsencrypt-prod # Per-env prefixes inside the shared R2 bucket. r2_prefix_velero: velero/prod r2_prefix_cnpg: cnpg/prod @@ -65,11 +65,10 @@ data: # cert controller. Disabled until we actually need Cloudflare Origin certs. origin_ca_issuer_replicas: "0" # VPA recommender computes resource recommendations; admission controller - # applies them at pod start. Updater (pod eviction) is scaled to 0 - # replicas to avoid disruption, so recommendations are applied on the - # next pod restart instead. + # applies them at pod start. Updater evicts pods to apply new resource + # recommendations continuously. vpa_recommender_replicas: "1" - vpa_updater_replicas: "0" + vpa_updater_replicas: "1" # Kyverno background controller is required for generate rules (auto-vpa # ClusterPolicy). kyverno@3.8.0 rejects 0 replicas for any controller, # so reports and cleanup run at 1 replica each. diff --git a/k8s/providers/docker/infrastructure/controllers/minio/deployment.yaml b/k8s/providers/docker/infrastructure/controllers/minio/deployment.yaml index 03cb080c5..23c2e53ea 100644 --- a/k8s/providers/docker/infrastructure/controllers/minio/deployment.yaml +++ b/k8s/providers/docker/infrastructure/controllers/minio/deployment.yaml @@ -27,6 +27,13 @@ spec: labels: app.kubernetes.io/name: minio spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault containers: - name: minio image: quay.io/minio/minio:RELEASE.2025-04-08T15-41-24Z @@ -35,6 +42,11 @@ spec: - /data - --console-address - :9001 + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] env: - name: MINIO_ROOT_USER value: minio @@ -97,9 +109,20 @@ spec: template: spec: restartPolicy: OnFailure + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + seccompProfile: + type: RuntimeDefault containers: - name: mc image: quay.io/minio/mc:RELEASE.2025-04-08T15-39-49Z + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] command: - /bin/sh - -c diff --git a/k8s/providers/hetzner/infrastructure/cluster-issuers/kustomization.yaml b/k8s/providers/hetzner/infrastructure/cluster-issuers/kustomization.yaml index 9f4c46d2f..5681846a9 100644 --- a/k8s/providers/hetzner/infrastructure/cluster-issuers/kustomization.yaml +++ b/k8s/providers/hetzner/infrastructure/cluster-issuers/kustomization.yaml @@ -3,3 +3,4 @@ kind: Kustomization resources: - cloudflare-origin-issuer.yaml - cloudflare-origin-ca-trust-bundle.yaml + - letsencrypt-prod-issuer.yaml diff --git a/k8s/providers/hetzner/infrastructure/cluster-issuers/letsencrypt-prod-issuer.yaml b/k8s/providers/hetzner/infrastructure/cluster-issuers/letsencrypt-prod-issuer.yaml new file mode 100644 index 000000000..c7a85eeab --- /dev/null +++ b/k8s/providers/hetzner/infrastructure/cluster-issuers/letsencrypt-prod-issuer.yaml @@ -0,0 +1,16 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: nikolaj@devantler.tech + privateKeySecretRef: + name: letsencrypt-prod-account-key + solvers: + - dns01: + cloudflare: + apiTokenSecretRef: + name: cloudflare-api-token + key: api-token diff --git a/k8s/providers/hetzner/infrastructure/controllers/external-dns/helm-release.yaml b/k8s/providers/hetzner/infrastructure/controllers/external-dns/helm-release.yaml index a3260cedf..f4d45ad8e 100644 --- a/k8s/providers/hetzner/infrastructure/controllers/external-dns/helm-release.yaml +++ b/k8s/providers/hetzner/infrastructure/controllers/external-dns/helm-release.yaml @@ -27,7 +27,6 @@ spec: domainFilters: - "${cloudflare_zone}" extraArgs: - - --cloudflare-proxied - --exclude-target-net=10.0.0.0/8 env: - name: CF_API_TOKEN diff --git a/k8s/providers/hetzner/infrastructure/controllers/external-dns/kustomization.yaml b/k8s/providers/hetzner/infrastructure/controllers/external-dns/kustomization.yaml index 299d4b166..651404464 100644 --- a/k8s/providers/hetzner/infrastructure/controllers/external-dns/kustomization.yaml +++ b/k8s/providers/hetzner/infrastructure/controllers/external-dns/kustomization.yaml @@ -6,3 +6,4 @@ resources: - cloudflare-api-token-secret.yaml - helm-release.yaml - helm-repository.yaml + - networkpolicy.yaml diff --git a/k8s/providers/hetzner/infrastructure/controllers/external-dns/networkpolicy.yaml b/k8s/providers/hetzner/infrastructure/controllers/external-dns/networkpolicy.yaml new file mode 100644 index 000000000..2abdbac7c --- /dev/null +++ b/k8s/providers/hetzner/infrastructure/controllers/external-dns/networkpolicy.yaml @@ -0,0 +1,38 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-external-dns + namespace: external-dns +spec: + endpointSelector: {} + ingress: + # Metrics scraping + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + toPorts: + - ports: + - port: "7979" + protocol: TCP + egress: + # Kube API for watching services/ingresses + - toEntities: + - kube-apiserver + # Cloudflare/Hetzner DNS API + - toEntities: + - world + toPorts: + - ports: + - port: "443" + protocol: TCP + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/providers/hetzner/infrastructure/controllers/kubelet-serving-cert-approver/kustomization.yaml b/k8s/providers/hetzner/infrastructure/controllers/kubelet-serving-cert-approver/kustomization.yaml index 95cacc0ce..e3befb20f 100644 --- a/k8s/providers/hetzner/infrastructure/controllers/kubelet-serving-cert-approver/kustomization.yaml +++ b/k8s/providers/hetzner/infrastructure/controllers/kubelet-serving-cert-approver/kustomization.yaml @@ -2,3 +2,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - https://raw.githubusercontent.com/alex1989hu/kubelet-serving-cert-approver/main/deploy/standalone-install.yaml + - networkpolicy.yaml diff --git a/k8s/providers/hetzner/infrastructure/controllers/kubelet-serving-cert-approver/networkpolicy.yaml b/k8s/providers/hetzner/infrastructure/controllers/kubelet-serving-cert-approver/networkpolicy.yaml new file mode 100644 index 000000000..1b9e4144f --- /dev/null +++ b/k8s/providers/hetzner/infrastructure/controllers/kubelet-serving-cert-approver/networkpolicy.yaml @@ -0,0 +1,31 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-kubelet-cert-approver + namespace: kubelet-serving-cert-approver +spec: + endpointSelector: {} + ingress: + # Metrics scraping + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + toPorts: + - ports: + - port: "9090" + protocol: TCP + egress: + # Kube API for approving CSRs + - toEntities: + - kube-apiserver + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/k8s/providers/hetzner/infrastructure/controllers/longhorn/kustomization.yaml b/k8s/providers/hetzner/infrastructure/controllers/longhorn/kustomization.yaml index 8b2253c35..e6c1f2415 100644 --- a/k8s/providers/hetzner/infrastructure/controllers/longhorn/kustomization.yaml +++ b/k8s/providers/hetzner/infrastructure/controllers/longhorn/kustomization.yaml @@ -5,3 +5,4 @@ resources: - namespace.yaml - helm-repository.yaml - helm-release.yaml + - networkpolicy.yaml diff --git a/k8s/providers/hetzner/infrastructure/controllers/longhorn/networkpolicy.yaml b/k8s/providers/hetzner/infrastructure/controllers/longhorn/networkpolicy.yaml new file mode 100644 index 000000000..d6a0d996b --- /dev/null +++ b/k8s/providers/hetzner/infrastructure/controllers/longhorn/networkpolicy.yaml @@ -0,0 +1,55 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-longhorn + namespace: longhorn-system +spec: + endpointSelector: {} + ingress: + # Webhook from kube-apiserver (hostNetwork on control plane nodes) + - fromEntities: + - kube-apiserver + - remote-node + - host + toPorts: + - ports: + - port: "9502" + protocol: TCP + # Intra-namespace (manager, driver, engine) + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: longhorn-system + # Metrics scraping + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: monitoring + egress: + # Kube API + - toEntities: + - kube-apiserver + # Intra-namespace + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: longhorn-system + # iSCSI to nodes + - toEntities: + - host + - remote-node + # Backup targets (S3-compatible) + - toEntities: + - world + toPorts: + - ports: + - port: "443" + protocol: TCP + # DNS resolution + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP diff --git a/ksail.prod.yaml b/ksail.prod.yaml index 6e3370a3a..9276aefaf 100644 --- a/ksail.prod.yaml +++ b/ksail.prod.yaml @@ -63,7 +63,7 @@ spec: provider: hetzner: controlPlaneServerType: cx23 - workerServerType: cx23 + workerServerType: cx33 location: fsn1 networkCidr: 10.0.0.0/16 placementGroupStrategy: Spread