DevSecOps

Securitatea containerelor si Kubernetes: Bune practici pentru productie

Nicu Constantin
--9 min lectura
#kubernetes#container-security#docker#pod-security#cloud-native

Securitatea containerelor si Kubernetes: Bune practici pentru productie

Securitatea containerelor este o provocare pe mai multe straturi care acopera intregul ciclu de viata al containerului - de la imagini de baza pana la runtime. Kubernetes adauga o dimensiune suplimentara cu propriul model de securitate pentru orchestrare.

Acest ghid acopera practici de securitate aplicabile in medii de productie cu containere.

Securitatea imaginilor de container

Imagini de baza securizate

# Foloseste imagini de baza distroless sau minimale
FROM gcr.io/distroless/nodejs20-debian12:nonroot
 
# Sau foloseste o versiune specifica Alpine cu actualizari de securitate
FROM node:20.10.0-alpine3.18
 
# Aplica actualizari de securitate
RUN apk update && apk upgrade --no-cache
 
# Creeaza un utilizator non-root
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup
 
# Seteaza directorul de lucru
WORKDIR /app
 
# Copiaza doar fisierele necesare
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --only=production
 
COPY --chown=appuser:appgroup . .
 
# Sterge pachetele inutile
RUN rm -rf /var/cache/apk/* /root/.npm
 
# Ruleaza ca non-root
USER appuser
 
# Foloseste un UID specific
USER 1001
 
# Nu rula ca root chiar daca imaginea permite
CMD ["node", "server.js"]

Build multi-stage pentru securitate

# Etapa de build
FROM node:20-alpine AS builder
 
WORKDIR /build
 
COPY package*.json ./
RUN npm ci
 
COPY . .
RUN npm run build && npm prune --production
 
# Etapa de scanare de securitate
FROM aquasec/trivy:latest AS scanner
COPY --from=builder /build /scan
RUN trivy filesystem --exit-code 1 --severity HIGH,CRITICAL /scan
 
# Etapa de productie
FROM gcr.io/distroless/nodejs20-debian12:nonroot
 
WORKDIR /app
 
# Copiaza doar artefactele compilate
COPY --from=builder /build/dist ./dist
COPY --from=builder /build/node_modules ./node_modules
COPY --from=builder /build/package.json ./
 
USER nonroot
 
CMD ["dist/server.js"]

Pipeline de scanare a imaginilor

# GitHub Actions workflow pentru scanarea imaginilor
name: Container Security Scan
 
on:
  push:
    branches: [main]
  pull_request:
 
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Build image
        run: docker build -t app:${{ github.sha }} .
 
      - name: Trivy vulnerability scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'app:${{ github.sha }}'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'
 
      - name: Grype vulnerability scan
        uses: anchore/scan-action@v3
        with:
          image: 'app:${{ github.sha }}'
          fail-build: true
          severity-cutoff: high
 
      - name: Dockle lint
        uses: erzz/dockle-action@v1
        with:
          image: 'app:${{ github.sha }}'
          exit-code: '1'
          failure-threshold: high
 
      - name: Upload results to GitHub Security
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

Securitatea pod-urilor Kubernetes

Standarde de securitate pentru pod-uri

# Standard de securitate restrictiv pentru pod-uri
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted
 
---
# Specificatie de pod securizat
apiVersion: v1
kind: Pod
metadata:
  name: secure-app
  namespace: production
spec:
  # Context de securitate la nivel de pod
  securityContext:
    runAsNonRoot: true
    runAsUser: 1001
    runAsGroup: 1001
    fsGroup: 1001
    seccompProfile:
      type: RuntimeDefault
 
  # Service account
  serviceAccountName: app-service-account
  automountServiceAccountToken: false
 
  containers:
    - name: app
      image: myregistry/app:v1.0.0@sha256:abc123...
      imagePullPolicy: Always
 
      # Context de securitate la nivel de container
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        runAsNonRoot: true
        runAsUser: 1001
        capabilities:
          drop:
            - ALL
 
      # Limite de resurse
      resources:
        limits:
          cpu: "1"
          memory: "512Mi"
          ephemeral-storage: "1Gi"
        requests:
          cpu: "100m"
          memory: "128Mi"
 
      # Probe de sanatate
      livenessProbe:
        httpGet:
          path: /health
          port: 8080
        initialDelaySeconds: 30
        periodSeconds: 10
 
      readinessProbe:
        httpGet:
          path: /ready
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 5
 
      # Montari de volume
      volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: secrets
          mountPath: /secrets
          readOnly: true
 
  # Volume
  volumes:
    - name: tmp
      emptyDir: {}
    - name: secrets
      secret:
        secretName: app-secrets
        defaultMode: 0400
 
  # Afinitate de nod
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: node-type
                operator: In
                values:
                  - production
 
  # Toleratii
  tolerations: []
 
  # Politica DNS
  dnsPolicy: ClusterFirst

Securitatea deployment-urilor

apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
  namespace: production
spec:
  replicas: 3
  revisionHistoryLimit: 5
 
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
 
  selector:
    matchLabels:
      app: secure-app
 
  template:
    metadata:
      labels:
        app: secure-app
      annotations:
        # Activeaza scanarea de securitate
        container.apparmor.security.beta.kubernetes.io/app: runtime/default
 
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1001
        fsGroup: 1001
        seccompProfile:
          type: RuntimeDefault
 
      containers:
        - name: app
          image: myregistry/app:v1.0.0
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop:
                - ALL
 
          ports:
            - containerPort: 8080
              protocol: TCP
 
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: password
 
          resources:
            limits:
              cpu: "1"
              memory: "512Mi"
            requests:
              cpu: "100m"
              memory: "128Mi"
 
---
# Pod Disruption Budget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: secure-app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: secure-app

Securitatea retelei

Politici de retea

# Blocheaza tot traficul implicit
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
 
---
# Permite ingress specific
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-app-ingress
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: secure-app
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
          podSelector:
            matchLabels:
              app.kubernetes.io/name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080
 
---
# Permite aplicatiei sa acceseze baza de date
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-app-to-db
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: secure-app
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: postgresql
      ports:
        - protocol: TCP
          port: 5432
    # Permite DNS
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
 
---
# Blocheaza egress catre serviciul de metadata (AWS/GCP)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-metadata-access
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 169.254.169.254/32  # AWS metadata
              - 169.254.0.0/16       # Link-local
              - 10.0.0.0/8           # Intern (ajusteaza dupa necesitati)

Managementul secretelor

External Secrets Operator

# Configurare External Secrets
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets-manager
  namespace: production
spec:
  provider:
    aws:
      service: SecretsManager
      region: eu-west-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
 
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secrets
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: SecretStore
 
  target:
    name: app-secrets
    creationPolicy: Owner
    template:
      type: Opaque
      data:
        db-password: "{{ .db_password }}"
        api-key: "{{ .api_key }}"
 
  data:
    - secretKey: db_password
      remoteRef:
        key: production/app/database
        property: password
 
    - secretKey: api_key
      remoteRef:
        key: production/app/api
        property: key

Sealed Secrets

# Sealed Secret (criptat, sigur pentru git)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: app-secrets
  namespace: production
spec:
  encryptedData:
    db-password: AgBY8s9j3K...encrypted...
    api-key: AgDJ2k4m8N...encrypted...
  template:
    metadata:
      name: app-secrets
      namespace: production
    type: Opaque

Securitatea RBAC

Service Account cu privilegii minime

# Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-service-account
  namespace: production
automountServiceAccountToken: false
 
---
# Rol cu permisiuni minime
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: production
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["app-config"]
    verbs: ["get"]
  - apiGroups: [""]
    resources: ["secrets"]
    resourceNames: ["app-secrets"]
    verbs: ["get"]
 
---
# Role Binding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-role-binding
  namespace: production
subjects:
  - kind: ServiceAccount
    name: app-service-account
    namespace: production
roleRef:
  kind: Role
  name: app-role
  apiGroup: rbac.authorization.k8s.io

Restrictii RBAC la nivel de cluster

# Blocheaza escaladarea privilegiilor la nivel de cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: deny-privilege-escalation
rules:
  - apiGroups: [""]
    resources: ["pods/exec", "pods/attach"]
    verbs: ["create"]
  - apiGroups: [""]
    resources: ["nodes/proxy", "pods/proxy", "services/proxy"]
    verbs: ["*"]
 
---
# Rol de audit pentru echipa de securitate
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: security-auditor
rules:
  - apiGroups: ["*"]
    resources: ["*"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: []  # Fara acces la secrete

Securitate runtime

Reguli Falco

# Reguli Falco pentru detectia la runtime
- rule: Terminal shell in container
  desc: Detecteaza executia unui shell intr-un container
  condition: >
    spawned_process and container
    and shell_procs
    and proc.tty != 0
    and container_entrypoint
  output: >
    Shell pornit intr-un container
    (user=%user.name container=%container.name
    shell=%proc.name parent=%proc.pname)
  priority: WARNING
  tags: [container, shell]
 
- rule: Sensitive file access
  desc: Detecteaza accesul la fisiere sensibile
  condition: >
    open_read and container
    and sensitive_files
  output: >
    Fisier sensibil citit
    (user=%user.name file=%fd.name container=%container.name)
  priority: WARNING
  tags: [container, filesystem]
 
- rule: Crypto mining detection
  desc: Detecteaza potential crypto mining
  condition: >
    spawned_process and container
    and (proc.name in (crypto_miners) or
         proc.args contains "stratum" or
         proc.args contains "xmr")
  output: >
    Crypto mining detectat
    (user=%user.name command=%proc.cmdline container=%container.name)
  priority: CRITICAL
  tags: [container, crypto]
 
- rule: Outbound connection to unusual port
  desc: Detecteaza conexiuni de iesire catre porturi neobisnuite
  condition: >
    outbound and container
    and not fd.port in (allowed_outbound_ports)
  output: >
    Conexiune de iesire neobisnuita
    (container=%container.name connection=%fd.name)
  priority: WARNING
  tags: [container, network]

Politici Admission Controller (Kyverno)

# Necesita limite de resurse
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-resource-limits
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: require-limits
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Limitele de resurse sunt obligatorii"
        pattern:
          spec:
            containers:
              - resources:
                  limits:
                    memory: "?*"
                    cpu: "?*"
 
---
# Necesita non-root
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-non-root
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-non-root
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Containerele nu trebuie sa ruleze ca root"
        pattern:
          spec:
            securityContext:
              runAsNonRoot: true
            containers:
              - securityContext:
                  runAsNonRoot: true
 
---
# Necesita registre aprobate
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-approved-registries
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-registry
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Imaginile trebuie sa provina din registre aprobate"
        pattern:
          spec:
            containers:
              - image: "gcr.io/myproject/* | myregistry.azurecr.io/*"

Monitorizarea securitatii

Metrici de securitate Prometheus

# ServiceMonitor pentru metrici de securitate
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: security-metrics
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: security-metrics
  endpoints:
    - port: metrics
      interval: 30s
      path: /metrics
 
---
# PrometheusRule pentru alerte de securitate
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: security-alerts
  namespace: monitoring
spec:
  groups:
    - name: container-security
      rules:
        - alert: PrivilegedContainerDetected
          expr: sum(kube_pod_container_info{container_privileged="true"}) > 0
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: Container privilegiat detectat
 
        - alert: ContainerRunningAsRoot
          expr: sum(kube_pod_container_info{container_run_as_user="0"}) > 0
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: Container care ruleaza ca root
 
        - alert: HighCVEVulnerabilities
          expr: trivy_vulnerability_count{severity="CRITICAL"} > 0
          for: 1m
          labels:
            severity: critical
          annotations:
            summary: CVE critic detectat in imagine

Concluzie

Securitatea containerelor si Kubernetes necesita o abordare de aparare in profunzime de-a lungul intregului ciclu de viata. De la imagini de baza securizate pana la protectie runtime, fiecare strat contribuie la postura generala de securitate.

Concluzii principale:

  1. Imagini securizate - Foloseste baze minimale, scaneaza regulat
  2. Privilegii minime - Non-root, capabilitati minimale, filesystem read-only
  3. Segmentarea retelei - Blocheaza implicit, permite explicit
  4. Managementul secretelor - Secrete externe, criptare at rest
  5. Protectie runtime - Monitorizeaza, detecteaza, raspunde

La DeviDevs, ajutam organizatiile sa isi securizeze deployment-urile Kubernetes cu implementari de securitate complete. Contacteaza-ne pentru a discuta nevoile tale de securitate a containerelor.


Sistemul tau AI e conform cu EU AI Act? Evaluare gratuita de risc - afla in 2 minute →

Ai nevoie de ajutor cu conformitatea EU AI Act sau securitatea AI?

Programeaza o consultatie gratuita de 30 de minute. Fara obligatii.

Programeaza un Apel

Weekly AI Security & Automation Digest

Get the latest on AI Security, workflow automation, secure integrations, and custom platform development delivered weekly.

No spam. Unsubscribe anytime.