Skip to content

Kubernetes Security: Admission Policy, NetworkPolicy e Controlli di Cluster

La sicurezza Kubernetes non si limita al singolo Pod. A livello cluster e' necessario controllare traffico, ingress pubblici e policy di ammissione, in modo da bloccare manifest non conformi prima che vengano salvati nell'API Server.

Panoramica

NetworkPolicy e ValidatingAdmissionPolicy coprono due aspetti diversi ma complementari: la prima limita i flussi di rete tra workload, namespace e ingress controller; la seconda impedisce l'applicazione di risorse non aderenti agli standard di sicurezza definiti dal cluster.

NetworkPolicy

Una NetworkPolicy restrittiva consente solo il traffico necessario verso il workload. Per un endpoint pubblico esposto da Traefik, l'ingress dovrebbe arrivare solo dal controller ingress/gateway e solo sulla porta applicativa.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: app-public-endpoint
  namespace: applicazioni
spec:
  podSelector:
    matchLabels:
      app: mkdocs # (1)!
      component: mcp # (2)!
  policyTypes:
    - Ingress # (3)!
    - Egress # (4)!
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system # (5)!
          podSelector:
            matchLabels:
              app.kubernetes.io/name: rke2-traefik # (6)!
      ports:
        - protocol: TCP
          port: 8000 # (7)!
  egress: [] # (8)!
  1. Seleziona i Pod appartenenti all'applicazione.
  2. Restringe la policy al componente MCP.
  3. Applica regole al traffico in ingresso.
  4. Applica regole al traffico in uscita.
  5. Limita la sorgente al namespace dove opera Traefik.
  6. Consente traffico solo dai Pod del controller Traefik.
  7. Espone esclusivamente la porta applicativa necessaria.
  8. Blocca traffico in uscita dal workload quando non richiesto.

ValidatingAdmissionPolicy: risorse obbligatorie

Questa policy richiede requests e limits CPU/memoria sia sui Pod creati direttamente sia sui Deployment. L'obiettivo e' evitare workload senza limiti, che potrebbero consumare risorse in modo incontrollato.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: require-container-resources
spec:
  failurePolicy: Fail # (1)!
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"] # (2)!
      - apiGroups: ["apps"]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["deployments"] # (3)!
  validations:
    - expression: >
        has(object.spec.containers) ?
        object.spec.containers.all(c,
          has(c.resources) &&
          has(c.resources.requests) &&
          has(c.resources.requests.cpu) &&
          has(c.resources.requests.memory) &&
          has(c.resources.limits) &&
          has(c.resources.limits.cpu) &&
          has(c.resources.limits.memory)
        ) :
        object.spec.template.spec.containers.all(c,
          has(c.resources) &&
          has(c.resources.requests) &&
          has(c.resources.requests.cpu) &&
          has(c.resources.requests.memory) &&
          has(c.resources.limits) &&
          has(c.resources.limits.cpu) &&
          has(c.resources.limits.memory)
        ) # (4)!
      message: "Containers must define CPU and memory requests and limits." # (5)!
  1. Blocca la richiesta se la policy non puo essere valutata o non viene rispettata.
  2. Applica la validazione ai Pod creati direttamente.
  3. Applica la validazione ai Deployment.
  4. Verifica che ogni container abbia requests e limits per CPU e memoria, gestendo sia struttura Pod sia struttura Deployment.
  5. Messaggio restituito quando il manifest non rispetta la policy.

ValidatingAdmissionPolicy: esecuzione non-root

Questa policy blocca Pod e Deployment che non dichiarano l'esecuzione non-root. Il controllo puo essere applicato a livello Pod oppure container, cosi resta compatibile con manifest che impostano runAsNonRoot nel punto piu adatto.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: require-non-root
spec:
  failurePolicy: Fail # (1)!
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"] # (2)!
      - apiGroups: ["apps"]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["deployments"] # (3)!
  validations:
    - expression: >
        has(object.spec.containers) ?
        (
          has(object.spec.securityContext) &&
          object.spec.securityContext.runAsNonRoot == true
        ) ||
        object.spec.containers.all(c,
          has(c.securityContext) &&
          has(c.securityContext.runAsNonRoot) &&
          c.securityContext.runAsNonRoot == true
        ) :
        (
          has(object.spec.template.spec.securityContext) &&
          object.spec.template.spec.securityContext.runAsNonRoot == true
        ) ||
        object.spec.template.spec.containers.all(c,
          has(c.securityContext) &&
          has(c.securityContext.runAsNonRoot) &&
          c.securityContext.runAsNonRoot == true
        ) # (4)!
      message: "Pods and Deployments must run as non-root." # (5)!
  1. Impedisce l'ammissione della risorsa se il controllo fallisce.
  2. Applica la policy ai Pod.
  3. Applica la policy ai Deployment.
  4. Accetta runAsNonRoot: true dichiarato a livello Pod template o su ogni container.
  5. Messaggio restituito quando il manifest permette esecuzione root o non dichiara il vincolo.

Traefik e superficie pubblica

Quando un workload e' esposto tramite Traefik, l'hardening deve includere anche il layer HTTP:

  • rimozione o mascheramento dell'header Server;
  • Cache-Control: no-store per endpoint API o MCP;
  • X-Content-Type-Options: nosniff;
  • CORS chiuso, senza wildcard Access-Control-Allow-Origin: *;
  • rate limit sull'endpoint pubblico per ridurre impatto da bot e richieste anomale.

Verifiche manuali

Controllare NetworkPolicy e ValidatingAdmissionPolicy applicate:

kubectl get networkpolicy -n applicazioni
kubectl describe networkpolicy app-public-endpoint -n applicazioni
kubectl get validatingadmissionpolicy
kubectl describe validatingadmissionpolicy require-container-resources
kubectl describe validatingadmissionpolicy require-non-root

Verificare il comportamento dell'endpoint pubblico:

curl -sI https://fabio.mogavero.eu/mcp | grep -Ei 'server|cache-control|access-control|x-content-type'

Testare il rate limit:

for i in {1..20}; do
  curl -o /dev/null -s -w "%{http_code}\n" https://fabio.mogavero.eu/mcp \
    -H "Content-Type: application/json" \
    -H "Accept: application/json, text/event-stream" \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
done

Risultato atteso

Una configurazione Kubernetes hardened deve garantire:

  • traffico ammesso solo dai componenti necessari;
  • egress bloccato quando non richiesto;
  • risorse CPU/memoria obbligatorie sui workload;
  • Pod e Deployment non-root imposti da admission policy;
  • endpoint pubblici protetti da header e rate limit;
  • policy verificabili con kubectl;
  • configurazione ripetibile e versionata.