Cloudflare Zero Trust Kubernetes networking

Cloudflare Zero Trust Tunnel: udostępnianie serwisów Kubernetes bez publicznych IP i reguł firewall

Wdrożenie Cloudflare Tunnel (cloudflared) jako Deployment w Kubernetes do udostępniania wewnętrznych serwisów ClusterIP bez otwierania portów. Działa identycznie na EKS, GKE, AKS i on-prem.

·
Udostępnij wewnętrzne serwisy Kubernetes w internecie bez otwierania reguł firewall dla ruchu przychodzącego, bez publicznych adresów IP i bez zarządzania certyfikatami TLS. Cloudflare Tunnel tworzy połączenia wyłącznie wychodzące z klastra do sieci brzegowej Cloudflare. Działa identycznie na EKS, GKE, AKS i klastrach bare-metal.

Objawy

Musisz udostępnić wewnętrzne serwisy, ale napotykasz ograniczenia uniemożliwiające tradycyjny ingress:

# Wewnętrzne serwisy dostępne tylko przez ClusterIP
kubectl get svc
# NAME           TYPE        CLUSTER-IP     PORT(S)
# my-api         ClusterIP   10.96.0.15     8080/TCP
# admin-panel    ClusterIP   10.96.0.22     3000/TCP

# Brak publicznych IP lub zakaz ich używania przez politykę
# Firewall blokuje CAŁY ruch przychodzący (sieć zero-trust)
# Cloud NAT lub korporacyjny proxy blokuje tradycyjne przekierowanie portów
# Klaster on-prem za CGNAT bez publicznego adresu

Tradycyjne rozwiązania (serwis typu LoadBalancer, kontroler Ingress z zewnętrznym IP) wymagają reguł firewall dla ruchu przychodzącego, publicznych IP i zarządzania certyfikatami TLS. W środowiskach zero-trust lub klastrach on-prem za NAT nie są to dostępne opcje.

Przyczyna

Potrzeba Cloudflare Tunnel wynika z uzasadnionych ograniczeń architektonicznych:

  • Polityki sieci zero-trust zabraniają połączeń przychodzących do nodów klastra. Cały ruch musi być inicjowany na zewnątrz.
  • Klastry on-prem za CGNAT nie mają publicznych adresów IP i nie mogą odbierać połączeń przychodzących z internetu.
  • Klastry w chmurze w prywatnych podsieciach celowo nie mają load balancerów z dostępem z internetu. Udostępnienie serwisów wymaga dodatkowej infrastruktury (NAT, bastion host, VPN).
  • Wdrożenia wieloklastrowe lub hybrydowe gdzie serwisy obejmują EKS, GKE, AKS i bare metal. Zunifikowana warstwa dostępu eliminuje konfigurację ingress per dostawca.

Cloudflare Tunnel rozwiązuje te wszystkie problemy ustanawiając trwałe połączenia wyłącznie wychodzące z klastra do sieci brzegowej Cloudflare. Przepływ ruchu: Klient -> Cloudflare Edge -> Tunel -> pod cloudflared -> serwis ClusterIP.

Rozwiązanie

Krok 1: Utwórz tunel przez panel Cloudflare lub CLI

# Zainstaluj CLI cloudflared lokalnie
brew install cloudflare/cloudflare/cloudflared
# Lub: curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared

# Zaloguj się do Cloudflare
cloudflared tunnel login
# Otwiera przeglądarkę do uwierzytelnienia OAuth - wybierz swoją strefę

# Utwórz nowy tunel
cloudflared tunnel create k8s-production
# Dane uwierzytelniające tunelu zapisane w ~/.cloudflared/<TUNNEL_ID>.json

# Zanotuj ID tunelu
export TUNNEL_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"

Krok 2: Utwórz Secret z danymi uwierzytelniającymi w Kubernetes

# Utwórz namespace dla infrastruktury tunelu
kubectl create namespace cloudflared

# Utwórz secret z pliku danych uwierzytelniających tunelu
kubectl create secret generic tunnel-credentials \
  --namespace cloudflared \
  --from-file=credentials.json=$HOME/.cloudflared/${TUNNEL_ID}.json

Krok 3: Utwórz ConfigMap cloudflared z regułami ingress

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: cloudflared-config
  namespace: cloudflared
data:
  config.yaml: |
    tunnel: ${TUNNEL_ID}
    credentials-file: /etc/cloudflared/creds/credentials.json
    metrics: 0.0.0.0:2000
    no-autoupdate: true

    ingress:
      - hostname: api.example.com
        service: http://my-api.default.svc.cluster.local:8080
      - hostname: admin.example.com
        service: http://admin-panel.default.svc.cluster.local:3000
        originRequest:
          noTLSVerify: true
      - service: http_status:404
EOF

Reguły ingress mapują zewnętrzne hosty na wewnętrzne serwisy ClusterIP za pomocą nazw DNS Kubernetes (<serwis>.<namespace>.svc.cluster.local).

Krok 4: Wdróż cloudflared jako Deployment

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cloudflared
  namespace: cloudflared
  labels:
    app: cloudflared
spec:
  replicas: 2
  selector:
    matchLabels:
      app: cloudflared
  template:
    metadata:
      labels:
        app: cloudflared
    spec:
      containers:
        - name: cloudflared
          image: cloudflare/cloudflared:2024.6.1
          args:
            - tunnel
            - --config
            - /etc/cloudflared/config/config.yaml
            - run
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 200m
              memory: 128Mi
          livenessProbe:
            httpGet:
              path: /ready
              port: 2000
            initialDelaySeconds: 10
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 2000
            initialDelaySeconds: 5
            periodSeconds: 5
          volumeMounts:
            - name: config
              mountPath: /etc/cloudflared/config
              readOnly: true
            - name: creds
              mountPath: /etc/cloudflared/creds
              readOnly: true
      volumes:
        - name: config
          configMap:
            name: cloudflared-config
        - name: creds
          secret:
            secretName: tunnel-credentials
EOF

Krok 5: Utwórz rekordy DNS wskazujące na tunel

# Skieruj hosty na tunel
cloudflared tunnel route dns ${TUNNEL_ID} api.example.com
cloudflared tunnel route dns ${TUNNEL_ID} admin.example.com

# To tworzy rekordy CNAME: api.example.com -> <TUNNEL_ID>.cfargotunnel.com

Krok 6: Dodaj polityki Cloudflare Access dla uwierzytelniania

Zabezpiecz serwisy politykami dostępu opartymi na tożsamości:

# Przez panel Cloudflare Zero Trust:
# Access > Applications > Add an Application > Self-hosted

# Lub przez API/Terraform:
cat <<EOF
# Przykład Terraform - Cloudflare Access Application
resource "cloudflare_access_application" "admin_panel" {
  zone_id          = var.cloudflare_zone_id
  name             = "Admin Panel"
  domain           = "admin.example.com"
  session_duration = "24h"
}

resource "cloudflare_access_policy" "admin_policy" {
  zone_id        = var.cloudflare_zone_id
  application_id = cloudflare_access_application.admin_panel.id
  name           = "Allow team members"
  precedence     = 1
  decision       = "allow"

  include {
    email_domain = ["example.com"]
  }
}
EOF

To dodaje uwierzytelnianie SSO przed dowolnym udostępnionym serwisem bez modyfikowania samej aplikacji.

Weryfikacja

Po wdrożeniu zweryfikuj, że tunel jest połączony i serwisy są dostępne:

# 1. Zweryfikuj, że pody cloudflared działają
kubectl get pods -n cloudflared
# Oczekiwane:
# NAME                          READY   STATUS    AGE
# cloudflared-7b9f4d-abc12     1/1     Running   2m
# cloudflared-7b9f4d-def34     1/1     Running   2m

# 2. Sprawdź status tunelu
cloudflared tunnel info ${TUNNEL_ID}
# Oczekiwane: Status: healthy, Connections: 4 (2 per replika)

# 3. Przetestuj zewnętrzny dostęp do serwisów
curl -s -o /dev/null -w "%{http_code}" https://api.example.com/health
# Oczekiwane: 200

# 4. Zweryfikuj, że polityka Access blokuje nieuwierzytelnione żądania
curl -s -o /dev/null -w "%{http_code}" https://admin.example.com/
# Oczekiwane: 302 (przekierowanie do logowania Cloudflare Access)

# 5. Sprawdź endpoint metryk
kubectl port-forward -n cloudflared deploy/cloudflared 2000:2000
curl http://localhost:2000/metrics | grep cloudflared_tunnel_active_streams
# Oczekiwane: cloudflared_tunnel_active_streams > 0

# 6. Zweryfikuj brak reguł firewall dla ruchu przychodzącego
# (zależne od klastra - przykład dla GKE)
gcloud compute firewall-rules list \
  --filter="direction=INGRESS AND network=my-vpc" \
  --format="table(name,direction,allowed)"
# Oczekiwane: brak reguł zezwalających na ruch przychodzący 80/443 z 0.0.0.0/0

Udostępnianie serwisów przez publiczne IP i reguły firewall dla ruchu przychodzącego tworzy powierzchnię ataku wymagającą stałego aktualizowania, ochrony przed DDoS i rotacji certyfikatów. Cloudflare Tunnel eliminuje to wszystko - brak publicznych IP do skanowania, brak portów do odpytywania, brak certyfikatów do zarządzania. Całe połączenie jest wyłącznie wychodzące, uwierzytelniane na brzegu sieci i szyfrowane end-to-end. To nie tylko wygoda - to fundamentalnie mniejsza powierzchnia ataku, która działa identycznie niezależnie czy klaster jest w AWS, GCP, Azure czy w szafie w Twoim centrum danych.

 

Jerzy Kopaczewski

Potrzebujesz dostępu zero-trust do serwisów Kubernetes?

Umów bezpłatną 30-minutową rozmowę. Wdrażamy Cloudflare Tunnel z politykami Access w środowiskach wieloklastrowych - zarówno w chmurze jak i on-prem.