Subscribe to receive notifications of new posts:

Cloudflare Zero TrustとKubectl

06/24/2022

4 min read
Kubectl with Cloudflare Zero Trust

Cloudflareは、エンジニア作業におけるKubernetesのヘビーユーザーです。当社のAPIのバックエンド、分析集約やボット検出などのバッチ処理、CI/CDパイプラインなどのエンジニアリングツールに使用されています。ただし、ロードバランサー、APIサーバー、etcd、イングレス、ポッドの間では、Kubernetesにより公開される領域がかなり拡大する可能性があります。

この投稿では、CloudflareのエンジニアリングチームがCloudflare Zero TrustをドッグフーディングしてKubernetesを保護し、プロキシなしでkubectlを使用できるようにした方法について少しご紹介します。

Kubernetesのセキュリティに対する一般的なアプローチ

Cloudflareでは、セキュリティ対策の一環として、ネットワーク経由で当社のクラスターにアクセスできるものを大幅に制限しています。ネットワークサービスが公開されている場合は、Cloudflare Access認証またはMutual TLS(またはその両方)をイングレスリソースへのアクセスに要求するなど、追加の保護措置を導入しています。

これらのネットワーク制限には、クラスターのAPIサーバーへのアクセスも含まれます。アクセスできない場合、Cloudflareのエンジニアはkubectlのようなツールを使ってチームのリソースを省みることができません。ベストプラクティスは継続的デプロイとGitOpsですが、開発者がKubernetes APIを使えるようにすることで、トラブルシューティングと開発者の速度を向上させることができます。アクセスできないことは、契約違反になります。

Cloudflareではセキュリティ要件を満たすためにCloudflare Zero Trustを使用していますが、その使用方法と、ここに至るまでの過程を紹介したいと思います。

Zero Trust以前

Zero Trust以前の世界では、エンジニアはVPNアプライアンスに接続することでKubernetes APIにアクセスできました。これは、業界では一般的な手法であり、APIへのアクセスを許可するものですが、エンジニアをクライアントとして内部ネットワークに落とし込んでしまい、必要以上にネットワークにアクセスすることになっていました。

この状況に満足していたわけではありませんが、数年間はこの状態が続いていました。2020年初頭にVPNを廃止したため、Kubernetesチームは別のソリューションを見つける必要がありました。

Cloudflare Tunnelを利用したKubernetes

当時、私たちはCloudflare Tunnelの開発チームと密接に協力し、Accessを使用したkubectl接続とCloudflareトンネルの処理のサポートを追加しました。

これは、当社のエンジニアにとっては機能しましたが、新入社員のオンボーディングの際には大きなハードルとなっていました。Kubernetesのクラスターごとに、エンジニアのデバイスからの独自のトンネル接続が必要で、クラスター間のシャッフルが煩わしかったのです。kubectlはSOCKSプロキシ経由の接続をサポートしていましたが、このサポートはKubernetesエコシステムのすべてのツールに普遍的なものではありませんでした。

このソリューションを社内で使い続けながら、より良いソリューションの実現に向けて取り組んできたのです。

Zero Trustを使用したKubernetes

Cloudflare Oneのリリース以来、私たちはさまざまな構成でZero Trustエージェントをドッグフーディングしてきました。当初は1.1.1.1でセキュアなDNSを実装するために使われていましたが、じきにZero Trustの追加機能のドッグフーディングに使われるようになりました。

今では、Cloudflare Zero Trustのプライベートネットワークルーティングを活用することで、Cloudflareトンネルをセットアップしたり、kubectlなどのKubernetesエコシステムのツールがトンネルを使用するように設定したりしなくても、エンジニアがKubernetes APIにアクセスできるようになりました。これはCloudflareに限ったことではなく、お客様のチームでも今すぐできることです。

Zero Trustの設定

私たちは構成管理ツールを使って、コードとしてのインフラストラクチャを実現するためのZero Trustの構成を行っており、以下にそれをご紹介します。ただし、Cloudflare Zero Trustダッシュボードを使用しても同じ構成を行うことができます。

まず最初に、新しいトンネルを作成する必要があります。このトンネルは、CloudflareのエッジネットワークをKubernetes APIに接続するために使用されます。後述する構成で、Kubernetes内でトンネルエンドポイントを実行します。

resource "cloudflare_argo_tunnel" "k8s_zero_trust_tunnel" {
  account_id = var.account_id
  name       = "k8s_zero_trust_tunnel"
  secret     = var.tunnel_secret
}

「tunnel_secret」シークレットは32バイトの乱数にする必要があります。後でKubernetesの設定に再利用するので、一時的に保存しておきます。

トンネルを作成したら、Cloudflareネットワークがトンネルを通してどのようなトラフィックを送信するかを知るために、ルートを作成する必要があります。

resource "cloudflare_tunnel_route" "k8s_zero_trust_tunnel_ipv4" {
  account_id = var.account_id
  tunnel_id  = cloudflare_argo_tunnel.k8s_zero_trust_tunnel.id
  network    = "198.51.100.101/32"
  comment    = "Kubernetes API Server (IPv4)"
}
 
resource "cloudflare_tunnel_route" "k8s_zero_trust_tunnel_ipv6" {
  account_id = var.account_id
  tunnel_id  = cloudflare_argo_tunnel.k8s_zero_trust_tunnel.id
  network    = "2001:DB8::101/128"
  comment    = "Kubernetes API Server (IPv6)"
}

IPv4とIPv6の両方のアドレス経由でKubernetes APIにアクセスすることをサポートしているため、両方に対応したルートを設定します。ホスト名でAPIサーバーに接続している場合、これらのIPアドレスはDNSルックアップで返されるものと一致する必要があります。

次に、Cloudflare Gatewayの設定を行い、APIサーバーやクライアントと互換性を持たせるようにします。

resource "cloudflare_teams_list" "k8s_apiserver_ips" {
  account_id = var.account_id
  name       = "Kubernetes API IPs"
  type       = "IP"
  items      = ["198.51.100.101/32", "2001:DB8::101/128"]
}
 
resource "cloudflare_teams_rule" "k8s_apiserver_zero_trust_http" {
  account_id  = var.account_id
  name        = "Don't inspect Kubernetes API"
  description = "Allow connections from kubectl to API"
  precedence  = 10000
  action      = "off"
  enabled     = true
  filters     = ["http"]
  traffic     = format("any(http.conn.dst_ip[*] in $%s)", replace(cloudflare_teams_list.k8s_apiserver_ips.id, "-", ""))
}

クライアントとAPIサーバー間では相互TLSを使用しており、kubectlとAPIサーバー間のトラフィックはすべてHTTPではないため、これらの接続についてはHTTPインスペクションを無効にしています。

これらのルールを、デバイスアテステーションセッションの存続時間ユーザーとグループのアクセスポリシーなどの追加のZero Trustルールと組み合わせることで、さらにセキュリティをカスタマイズできます。

トンネルのデプロイ

トンネルの作成と設定が完了したら、そのエンドポイントをネットワークにデプロイできます。トンネルをポッドとしてデプロイすることにより、アップグレードのロールアウトやノード障害の処理にKubernetesのデプロイメント戦略を利用できるようになります。

apiVersion: v1
kind: ConfigMap
metadata:
  name: tunnel-zt
  namespace: example
  labels:
    tunnel: api-zt
data:
  config.yaml: |
    tunnel: 8e343b13-a087-48ea-825f-9783931ff2a5
    credentials-file: /opt/zt/creds/creds.json
    metrics: 0.0.0.0:8081
    warp-routing:
        enabled: true

これにより、指定したトンネルIDのWARPルーティングを有効にする基本設定を持つKubernetes ConfigMapが作成されます。このトンネルIDは、設定管理システム、Cloudflare Zero Trustダッシュボード、または同じアカウントにログインした別のデバイスから以下のコマンドを実行することで取得できます。

cloudflared tunnel list

次に、トンネルのクレデンシャル用のシークレットを作成する必要があります。本来はシークレット管理システムを使うべきですが、簡単にするためにここでは直接作成することにします。

jq -cn --arg accountTag $CF_ACCOUNT_TAG \
       --arg tunnelID $CF_TUNNEL_ID \
       --arg tunnelName $CF_TUNNEL_NAME \
       --arg tunnelSecret $CF_TUNNEL_SECRET \
   '{AccountTag: $accountTag, TunnelID: $tunnelID, TunnelName: $tunnelName, TunnelSecret: $tunnelSecret}' | \
kubectl create secret generic -n example tunnel-creds --from-file=creds.json=/dev/stdin

これにより、新しいシークレット「tunnel-creds」が「example」ネームスペースに、トンネルが想定するクレデンシャルファイルとともに作成されます。

これで、トンネル自体をデプロイできるようになりました。複数のレプリカをデプロイすることで、ノードが枯渇している間でも、常にいくつかのレプリカを利用できるようにしています。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    tunnel: api-zt
  name: tunnel-api-zt
  namespace: example
spec:
  replicas: 3
  selector:
    matchLabels:
      tunnel: api-zt
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
  template:
    metadata:
      labels:
        tunnel: api-zt
    spec:
      containers:
        - args:
            - tunnel
            - --config
            - /opt/zt/config/config.yaml
            - run
          env:
            - name: GOMAXPROCS
              value: "2"
            - name: TZ
              value: UTC
          image: cloudflare/cloudflared:2022.5.3
          livenessProbe:
            failureThreshold: 1
            httpGet:
              path: /ready
              port: 8081
            initialDelaySeconds: 10
            periodSeconds: 10
          name: tunnel
          ports:
            - containerPort: 8081
              name: http-metrics
          resources:
            limits:
              cpu: "1"
              memory: 100Mi
          volumeMounts:
            - mountPath: /opt/zt/config
              name: config
              readOnly: true
            - mountPath: /opt/zt/creds
              name: creds
              readOnly: true
      volumes:
        - secret:
            name: tunnel-creds
          name: creds
        - configMap:
            name: tunnel-api-zt
          name: config

Cloudflare Zero TrustとKubectlを使用する

Screenshot of the Cloudflare WARP app showing it connected to Teams

Cloudflare Zero Trustエージェントをデプロイすると、チームのメンバーは特別なSOCKSトンネルを設定することなく、Kubernetes APIにアクセスできるようになります。

kubectl version --short
Client Version: v1.24.1
Server Version: v1.24.1

今後の展開は?

試された方は、ぜひフィードバックをお寄せください。Cloudflareは、HTTP以外のワークフローに対するZero Trustの改良を続けてまいります。

We protect entire corporate networks, help customers build Internet-scale applications efficiently, accelerate any website or Internet application, ward off DDoS attacks, keep hackers at bay, and can help you on your journey to Zero Trust.

Visit 1.1.1.1 from any device to get started with our free app that makes your Internet faster and safer.

To learn more about our mission to help build a better Internet, start here. If you're looking for a new career direction, check out our open positions.
Cloudflare One Week (JP)Security (JP)Zero Trust (JP)Kubernetes (JP)日本語

Follow on X

Terin Stock|@terinjokes
Cloudflare|@cloudflare

Related posts