Konnectivity とは

今回は、Kubernetes のコントロールプレーンとノードの間の通信を支える Konnectivity サービスについてみていきます。

きっかけは、最近 GKE の release notes で見かけた以下のアナウンスでした。

For VPC peering-based private clusters running version 1.27 or later, traffic from kube-apiserver to nodes routes through the Konnectivity service. If your cluster was created before 2020-09-17, this traffic from does not route through Konnectivity unless you have rotated the control plane IP address after 2020-09-17.

以前から GKE の kube-system にも Konnectivity の名前をもつ Pod がおり、なんとなくコントロールプレーンとノード間の通信に関与しているんだろうくらいの認識だったのですが、これをきっかけに実際の通信がどのように変化するのか把握しておきたいと思い調べてみました。

Konnectivity サービスの概要

Konnectivity はコントロールプレーンからクラスタへの通信に TCP プロキシを提供するサービスです。
Kubernetes API Server (kube-apiserver) などのコントロールプレーンも包含して「クラスタ」という言葉が使われることもありますが、ここではコントロールプレーンを含まず、ノード, Pod, サービス などのワークロードが実行される環境を指しています。

Konnectivity サービスは、サーバとエージェントの 2 つの役割から構成されるもので、コントロールプレーン側に Konnectivity サーバを、クラスタ側に Konnectivity エージェントを配置します。
Konnectivity サービスが有効なクラスタでは、Kubernetes API Server などのコントロールプレーンのコンポーネントから、クラスタ宛ての通信は Konnectivity サーバと Konnectivity エージェントの間に作成されたネットワーク接続を経由することになります。
Konnectivity エージェントは必ずすべてのノードに配置されている必要はありません。
実際、GKE の実装では GKE ノードに配置される Konnectivity エージェントは Deployment (とレプリカ数を動的に調整するためのオートスケーラ)で管理されており、Konnectivity エージェントが配置されないノードができることもあります。

※ Konnectivity サーバには「クラスタ向けの TCP プロキシ」と「Kubernetes API Server からコントロールプレーン内の他のコンポーネント向けの TCP プロキシ」のふたつの機能があるようですが、ここでは説明の簡単のため前者のみを描いています

Kubernetes の公式ドキュメントでは、こちらのページ で Konnectivity サービスについて説明されています。

何が嬉しいのか

Konnectivity サービスを利用することで、Kubernetes API Server がクラスタ内部(ノード, Pod, サービス, etc.)に直接接続しなくなるため、クラスタ内部で行われるネットワーキングと分離してトラフィックを管理することができるようになります。
これらを分離できることで、コントロールプレーンとクラスタネットワークの IP アドレスが重複することを許容できるようになったり、セキュリティの強化につながります。

Konnectivity サービスの登場以前、コントロールプレーンからクラスタへの通信(例えば Kubernetes API Server からノードへの通信)には、プレーン HTTP ないし SSH が利用されてきました。
しかし、プレーン HTTP ではセキュリティ上の懸念があり、SSH によるトンネリングはクラウドベンダによっては提供されていない場合もありました(現在では SSH によるトンネリングは非推奨となっています)。
一方、Konnectivity サービスは(リファレンスの実装であれば)エージェントを Kubernetes の Pod として実行するため、ノードの実装を問わず共通的な仕組みでコントロールプレーンからノード宛ての通信を分離することができます。

また、GKE のようにクラウドベンダが Kubernetes のコントロールプレーンをサービスとして提供する場合、コントロールプレーンとノードは異なるネットワークで実行されることになります。
そうした環境では、過去、/api/v1/namespaces/$NS/pods/$POD/status への書き込み権限を持つ攻撃者が Pod の IP アドレスを変更することで 、Kubernetes API Server が実行されているネットワーク内のサーバに対して HTTP リクエストを送信できることが知られていました ※ 。
Kubernetes の管理下であれば、RBAC などで適切に権限管理することでこれを制限することができますが、ノードに直接アクセスできる場合においては権限制御で防ぐことができません(攻撃者が kubelet になりすます可能性があるため)。
Konnectivity サービスが TCP プロキシとして機能し、コントロールプレーンからノードへの直接アクセスが行われないようにすることで、こうしたセキュリティ上の懸念を回避するという目的もあるようです。

https://groups.google.com/g/kubernetes-security-announce/c/tyd-MVR-tY4/m/tyREP9-qAwAJ

独立したサービスであるメリット

Kubernetes API Server に機能を追加するのではなく、コントロールプレーン内の他のコンポーネントと独立したサービスとして設計されていることには理由があります。
コントロールプレーンからクラスタへの接続管理に関する責務を Konnectivity サービスに集約することで、コントロールプレーンからクラスタへの通信を監査するための独自のプロキシサーバを構成できるようになったり、コントロールプレーンとクラスタ間の接続を Kubernetes API Server が管理する必要がなくなったりするといったメリットがあります。

詳しくは、Konnectivity サービスの提案時に作成された KEP (Kubernetes Enhancement Proposal) を参照ください。

なお、Kubernetes API Server と Konnectivity サーバの間の通信には、HTTP Connect または gRPC が利用できるようです。
EgressSelectorConfiguration というリソースでプロトコルが指定できるようになっています。
詳しいセットアップ方法は下記の Kubernetes 公式ドキュメントに掲載されています:

kubernetes.io

まとめ

Kubernetes のコントロールプレーンとノードの間の通信を管理する Konnectivity サービスについて調べました。

Konnectivity サービスはコントロールプレーンからクラスタへの通信に TCP プロキシを提供します。
これにより、GKE のようにコントロールプレーンとノードの実行されるネットワークが異なる場合でも、コントロールプレーンからノードへの直接アクセスが行われなくなることで(Firewall などを併用して)セキュリティを高めることができるようになります。

GKE では、1.27 以降のプライベートクラスタ(限定公開クラスタ)で Konnectivity サービスが使われるようになるとのアナウンスがありました。
1.27 を利用開始する際には、実機での検証もしてみたいと思います。

なお、上記の説明は私の調査と理解に基づくもので、正確な情報については各種公式ドキュメントを参照ください。