Back to Hub

cargo.ayedo.cloud/ayedo/k8s/nginx

Registry block

polycrate block pull cargo.ayedo.cloud/ayedo/k8s/nginx:0.7.5

nginx-ingress

Dieser Block installiert den ingress-nginx Controller per Helm Chart in Kubernetes.

Actions

install

Installiert nginx-ingress im Cluster.

polycrate run ingress install

uninstall

Deinstalliert nginx-ingress aus dem Cluster.

polycrate run ingress uninstall

status

Zeigt den aktuellen Status der nginx-Pods und des LoadBalancer-Services an.

polycrate run ingress status

Konfiguration

Standardinstallation mit Metriken und Dashboards

blocks:
  - name: ingress
    from: cargo.ayedo.cloud/ayedo/k8s/nginx
    kubeconfig:
      from: k8s
    config:
      metrics:
        enabled: true
        dashboards:
          enabled: true
        vmpodscrape:
          enabled: true

Fixe LoadBalancer-IP (Cilium / MetalLB)

Mit controller.service.loadbalancer_ip wird dem Ingress Controller eine bestimmte IP zugewiesen. Der Block setzt automatisch die passenden Annotations für beide gängigen LB-Implementierungen:

  • Cilium IPAM: lbipam.cilium.io/ips
  • MetalLB: metallb.universe.tf/loadBalancerIPs
config:
  controller:
    service:
      loadbalancer_ip: "10.0.0.10"

Die IP muss im konfigurierten LB-Pool (CiliumLoadBalancerIPPool oder MetalLB IPAddressPool) vorhanden sein. Leer lassen, um eine IP automatisch zuweisen zu lassen.

Fixe LoadBalancer-IP (OpenStack Octavia / CCM)

Interner LB im K8s-Netz (keine Floating IP). Neutron-Port wird vom openstack-Block angelegt (ensure-lb-ports / install).

polycrate run openstack ensure-lb-ports -w .   # oder openstack install
polycrate run ingress install -w .
# openstack-Block (workspace.poly)
load_balancer_ports:
  - name: vivavis-poc-ingress-lb
    subnet_id: "d6b0123b-a47a-4bfe-afe7-1df3f5009f2a"
    address: "172.16.2.200"

# ingress-Block
config:
  controller:
    service:
      loadbalancer_ip: "172.16.2.200"
      openstack:
        internal: true
        subnet_id: "d6b0123b-a47a-4bfe-afe7-1df3f5009f2a"
        lb_port_name: vivavis-poc-ingress-lb

Schreibt artifacts/openstack-lb-ports.yml — nginx liest port_id daraus via lb_port_name.

Voraussetzung: openstack install (CCM) und service_lb: false im k8s-Block.


externalTrafficPolicy

Steuert, wie der LoadBalancer-Service Traffic an nginx-Pods weiterleitet:

Wert Verhalten Source-IP
Local Nur Nodes mit nginx-Pod nehmen Traffic an Erhalten
Cluster Alle Nodes nehmen Traffic an, SNAT auf Pod-Node Verloren

Empfehlung je Setup:

Setup Empfehlung Begründung
BGP-Anycast + Cilium DSR Local Cilium kündigt LB-IP nur von Nodes mit aktivem Pod an → Traffic landet immer am richtigen Node. DSR-Response-Path bleibt symmetrisch.
Hetzner / Cloud LB Cluster Cloud-LB verteilt auf alle Nodes unabhängig von Pod-Platzierung. Mit Local würden Nodes ohne nginx-Pod Traffic droppen.
config:
  controller:
    service:
      external_traffic_policy: Local   # Default: Cluster

Wichtig bei Local: Jeder Node, der Traffic empfangen kann, muss einen nginx-Pod haben. Bei replicas: 1 läuft der Pod nur auf einem Node — Cilium sorgt via BGP dafür, dass nur dieser Node die IP ankündigt. Für HA replicas: 3 mit topologySpreadConstraints verwenden.


Hetzner: Proxy Protocol aktivieren

Wird benötigt, wenn der Hetzner Load Balancer Proxy Protocol an nginx weitergibt.

config:
  controller:
    config:
      proxy_protocol: true
      use_forwarded_headers: true

Extra Ports (TCP/UDP)

config:
  extra_ports:
    tcp:
      8080: "default/example-tcp-svc:9000"
    udp:
      53: "kube-system/kube-dns:53"

ModSecurity WAF

Der Block unterstützt ModSecurity als Web Application Firewall, wahlweise global für alle Ingresses oder individuell pro Ingress.

Konzept

Ebene Steuerung Beschreibung
Global block.config.modsecurity.* Aktiviert ModSecurity für alle Ingresses über den nginx ConfigMap
Pro Ingress Kubernetes Annotations Überschreibt die globale Einstellung für einzelne Ingress-Objekte

ModSecurity läuft standardmäßig im Detection-Only-Modus – es werden nur Logs geschrieben, keine Requests geblockt. Blocking wird erst durch mode: On aktiviert.

Globale Aktivierung

blocks:
  - name: ingress
    from: cargo.ayedo.cloud/ayedo/k8s/nginx
    config:
      controller:
        modsecurity:
          enabled: true          # ModSecurity global aktivieren
          owasp_crs: true        # OWASP Core Rule Set aktivieren
          mode: DetectionOnly    # DetectionOnly (nur Logs) oder On (blockieren)
          snippet: ""            # Optionale zusätzliche SecRule-Direktiven

Mit dieser Konfiguration wird im nginx ConfigMap gesetzt:

enable-modsecurity: "true"
enable-owasp-modsecurity-crs: "true"
modsecurity-snippet: |
  SecRuleEngine DetectionOnly

Blocking-Modus aktivieren

config:
  controller:
    modsecurity:
      enabled: true
      owasp_crs: true
      mode: On

Eigene Regeln hinzufügen

Das snippet-Feld wird direkt an den globalen modsecurity-snippet angehängt:

config:
  controller:
    modsecurity:
      enabled: true
      owasp_crs: true
      mode: On
      snippet: |
        SecRule REQUEST_HEADERS:User-Agent "nikto" \
          "id:1001,phase:1,deny,status:403,msg:'Nikto scanner blocked'"

Pro-Ingress-Konfiguration

Unabhängig von der globalen Einstellung kann ModSecurity für jeden Ingress individuell gesteuert werden. Da annotations_risk_level: Critical gesetzt ist, sind alle Annotations erlaubt.

ModSecurity für einen einzelnen Ingress aktivieren

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/enable-modsecurity: "true"
    nginx.ingress.kubernetes.io/enable-owasp-core-rules: "true"

Blocking-Modus per Ingress aktivieren

metadata:
  annotations:
    nginx.ingress.kubernetes.io/enable-modsecurity: "true"
    nginx.ingress.kubernetes.io/modsecurity-snippet: |
      SecRuleEngine On

OWASP CRS + Blocking + eigene Regeln kombinieren

metadata:
  annotations:
    nginx.ingress.kubernetes.io/enable-modsecurity: "true"
    nginx.ingress.kubernetes.io/modsecurity-snippet: |
      Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf
      SecRuleEngine On
      SecRule REQUEST_HEADERS:User-Agent "nikto" \
        "id:1001,phase:1,deny,status:403,msg:'Nikto scanner blocked'"

Hinweis: Wenn modsecurity-snippet gesetzt ist, überschreibt es enable-owasp-core-rules. Um den OWASP CRS trotzdem zu nutzen, muss das Include explizit in den Snippet aufgenommen werden.

ModSecurity für einen Ingress deaktivieren (bei globaler Aktivierung)

metadata:
  annotations:
    nginx.ingress.kubernetes.io/enable-modsecurity: "false"

Referenz: Alle block.config.modsecurity-Felder

Feld Typ Default Beschreibung
enabled bool false ModSecurity global für alle Ingresses aktivieren
owasp_crs bool false OWASP Core Rule Set global aktivieren
mode string DetectionOnly DetectionOnly (nur Logs) oder On (blockieren)
snippet string "" Zusätzliche SecRule-Direktiven, die global angehängt werden

Referenz: Per-Ingress Annotations

Annotation Wert Beschreibung
nginx.ingress.kubernetes.io/enable-modsecurity "true" / "false" ModSecurity für diesen Ingress aktivieren oder deaktivieren
nginx.ingress.kubernetes.io/enable-owasp-core-rules "true" OWASP CRS für diesen Ingress aktivieren
nginx.ingress.kubernetes.io/modsecurity-snippet Multi-line string Custom SecRules; überschreibt enable-owasp-core-rules

Vollständige Dokumentation: ingress-nginx ModSecurity · ingress-nginx Annotations


ModSecurity Logs einsehen

ModSecurity schreibt Audit-Logs per Default als JSON nach stdout (SecAuditLog /dev/stdout, SecAuditLogFormat JSON). Damit landen alle WAF-Events automatisch in VictoriaLogs – kein separater Audit-Log-Pfad, kein ephemerer Container-Storage.

Gesteuert über block.config.modsecurity.audit_log:

modsecurity:
  enabled: true
  audit_log:
    enabled: true          # stdout-Weiterleitung an/aus
    engine: RelevantOnly   # On (alle Requests), RelevantOnly (nur Treffer), Off
    format: JSON           # JSON (strukturiert) oder Native

Live-Logs verfolgen

# Alle ModSecurity-Events (JSON-Zeilen)
kubectl logs -n ingress -l app.kubernetes.io/name=ingress-nginx -f | grep '"transaction"'

# Blocking-Events (mode: On)
kubectl logs -n ingress -l app.kubernetes.io/name=ingress-nginx | grep '"Access denied"'

Nginx-Blocking-Events erscheinen zusätzlich im nginx error-log:

[error] ModSecurity: Access denied with code 403 (phase 2). Inbound Anomaly Score Exceeded (Total Score: 20) ...

VictoriaLogs – Audit-Log abfragen

Alle ModSecurity WAF-Events (JSON Audit-Log):

{namespace="ingress"} | json | transaction_request_uri != ""

Nur Blocking-Events (mode: On, Score überschritten):

{namespace="ingress"} |= "Access denied"

Treffer nach Regel-ID filtern (z.B. XSS Rule 941100):

{namespace="ingress"} | json | _msg |= "941100"

Alle Treffer der letzten Stunde zählen:

count_over_time({namespace="ingress"} | json | transaction_request_uri != "" [1h])

Top angegriffene URIs:

{namespace="ingress"} | json | transaction_request_uri != ""
  | stats by (transaction_request_uri) count() as hits
  | sort by (hits desc)
  | limit 10