Using Kubernetes's new Bound Service Account Tokens for secure workload identity
Linkerdにとって、セキュリティは最上の関心事です。これは、システムの全体的なセキュリティを強化する上で重要な役割を果たします。これは、Linkerd自体が安全である場合にのみ可能です。最近、Kubernetesの新しいbound service account tokensのサポートをLinkerdに追加しました。これはセキュリティにとって大きな前進です。しかし、なぜ?それを理解するには、まずLinkerdがサービスアカウントをどのように使用するかを理解する必要があります。
Linkerdは、ワークロード間の通信を保護するための相互TLS(mTLS)を提供します。あらゆるタイプの通信セキュリティの中心となるのは、アイデンティティの概念です。A Kubernetes engineer’s guide to mTLSで説明されているように、アイデンティティがないと信頼性がなく、信頼性がないと安全な通信ができません。LinkerdのmTLSの魔法のすべては、コントロールプレーン(特にidentityコンポーネント)が、プロキシが他のサービスで自身を認証するために使用する証明書を発行するために可能となります。 しかし、このTLS証明書に含まれているIDは何ですか?また、LinkerdのIDコンポーネントは、クラスター内の他のサービスと通信しようとする侵入者ではなく、クラスター内のプロキシに証明書を発行していることをどのように保証しますか?コントロールプレーンは、プロキシ自体のアイデンティティをどのように保証しますか?これらの質問には、このブログ投稿で回答します。飛び込みましょう!
Kubernetes Service Accounts
これはLinkerdだけの問題ではありません。 多くのコンポーネントまたはK8sコントローラーは、サービスを提供する前に、クライアントのアイデンティティを確認する必要があります(クラスター内で実行されているかどうかは関係ありません)。 そのため、Kubernetesは、デフォルトでポッドに添付されたサービスアカウントを提供し、Kubernetesクラスターの一部であることを他のコンポーネントに証明するために内部のアプリケーションで使用できます。 これらはボリュームとしてポッドにアタッチされ、コンテナーの/var/run/secrets/kubernetes.io/serviceaccountファイルパスにマウントされます。 デフォルトでは、Kubernetesはポッド名前空間のデフォルトのサービスアカウントをアタッチします。
code:yaml
spec:
containers:
...
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-tsbwl
readOnly: true
...
volumes:
- name: kube-api-access-tsbwl
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
サービスアカウントは、Kubernetes RBACでも一般的に使用され、ポッドにKubernetesAPIサービスへのアクセスを許可します。 これは、ClusterRoleBindingを使用して(必要な権限を持つ)ClusterRoleをサービスアカウントに(ServiceAccountオブジェクトを作成することによって)アタッチすることによって行われます。 次に、ワークロードのserviceAccountNameに同じサービスアカウントを指定できます。 これにより、名前空間ごとに存在するデフォルトのサービスアカウントが上書きされます。 デフォルトのサービスアカウントトークンには、クラスター内のリソースを表示、一覧表示、または変更するためのアクセス許可がありません。
Kubernetesがデフォルトのサービスアカウントトークンをアタッチすると、APIサーバーの信頼されたルート証明書を含むkube-root-ca.crt(上記のYAMLに表示)のConfigMapもアタッチされます。 これは、アプリケーションがAPIサーバーと通信するときにAPIサーバーとのTLS認証に使用されます。
Linkerdは、Kubernetes APIと相互作用しないため、トークン以外にこれらの追加ファイルを必要としませんでした(バインドされたサービスアカウントトークンがこれ(=必要以上のものが与えられている状態)を修正する方法については後で説明します)。
So how does Linkerd validate that its proxies are who they say they are?
プロキシが証明書を取得するには、アイデンティティコンポーネントを使用して自身を検証する必要があります。 これは、新しい証明書が必要になるたびに呼び出されるCertifyリクエストにサービスアカウントトークンを埋め込むことによって行われます(デフォルトでは24時間)。 アイデンティティコンポーネントは、TokenReview Kubernetes APIと通信してトークンを検証し、その後にのみ証明書を含むCertifyResponseを返します。 アイデンティティコンポーネントは、トークンが有効であることを確認するだけでなく、トークンが証明書を要求しているのと同じポッドに関連付けられているかどうかも確認します。 これは、TokenReview応答のStatus.User.Usernameを確認することで確認できます。 Kubernetes APIは、ユーザー名をそのトークンがアタッチされたポッド名に設定します。
Linkerdのアイデンティティコンポーネントのみが、トークンを検証するために必要なAPIアクセスを持っています。 トークンが検証されると、アイデンティティコンポーネントは、プロキシが他のサービスとの通信に使用するための証明書を発行します。
How does Linkerd provide workload identity?
Linkerdは、ここで(私の考えでは)美しい単純化のステップを実行します。サービスアカウントは、プロキシが本人であるかどうかを検証するためだけに使用されるのではなく、ワークロードのアイデンティティ自体の基礎として使用されます。 これにより、ポッドに付与された機能にすでに関連付けられているワークロードアイデンティティが得られ、追加の構成なしでmTLSを提供できることを意味します。 これが、すべてのメッシュポッドにデフォルトでオンのmTLSを提供するLinkerdの機能の背後にある秘密です。
Linkerdが2つのエンドポイント間で相互TLS接続を確立するたびに、交換されるアイデンティティはいずれかの側のサービスアカウントのIDになります。 このアイデンティティは、Linkerdのメトリクスにも組み込まれています。メッシュ化されたリクエストが受信または送信されるたびに、関連するメトリクスには、そのピアが関連付けられたサービスアカウントも含まれます。
emojivotoのメトリックの例を次に示します。
code:txt
request_total{..., client_id="web.emojivoto.serviceaccount.identity.linkerd.cluster.local", authority="emoji-svc.emojivoto.svc.cluster.local:8080", namespace="emojivoto", pod="emoji-696d9d8f95-5sj4j"} 14532
上記のメトリックのclient_idラベルは、リクエストを受信した場所からクライアントポッドに接続されたサービスアカウントです。
Authorization Policy
Linkerdの新しい承認ポリシー機能を使用すると、ユーザーは一連のリソースにのみアクセスできる一連のクライアントを指定できます。これは、同じアイデンティティを使用して、ServerAuthorizationリソース内の(Serverリソースによってグループ化された)ワークロードのグループとの通信を許可する必要があるクライアントのサービスアカウントをユーザーが指定できるようにすることで行われます。
code:yaml
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
namespace: emojivoto
name: internal-grpc
labels:
app.kubernetes.io/part-of: emojivoto
app.kubernetes.io/version: v11
spec:
server:
selector:
matchLabels:
emojivoto/api: internal-grpc
client:
meshTLS:
serviceAccounts:
- name: web
上記の例では、webサービスアカウントを使用してinternal-grpcサーバーと通信するワークロードを許可しています。
Bound Service Account Tokens
これはすべて素晴らしいことですが、まだ問題があります。このトークンは、アプリケーションがKubernetes APIと通信することを目的としたもので、Linkerd専用ではありません。 Linkerdには、デフォルトのボリュームマウントの一部である追加の証明書も必要ありません。これはセキュリティのベストプラクティスではありません。 Linkerdは、デフォルトのサービスアカウントトークンで実際に必要な数よりも多くのアクセス許可を実際に取得します。これは、発生するのを待っている潜在的な脆弱性です。これは、ユーザーが使用する可能性のあるこのサービストークンを管理するためのコントロールがLinkerdの外部にあり、Linkerdは検証を行うためにトークンが存在することを期待しているため、Linkerdに問題が発生することも意味します。ユーザーは、ポッドへのトークンの自動マウントを明示的に無効にして、Linkerdで問題を引き起こすこともできます。 Linkerd 2.11以降、トークンの自動マウントが無効になっている場合、我々はポッドインジェクションをスキップします。
→他の文脈からの要求で、セキュリティ向上の目的でうっかりautomountServiceAccountToken: falseにすると、サイドカープロキシの注入がされなくなるということ
これらの課題に対処するために、edge-21.11.1から、バウンドサービスアカウントトークンの自動マウントサポートが追加されました。デフォルトでマウントされるトークンを使用する代わりに、Linkerdは、Bound Service Account Tokens機能を使用して、独自のトークンのセットを要求します。バインドされたサービスアカウントトークン(Kubernetes v1.20のGA)機能を使用すると、コンポーネントは、(APIサーバーへのアクセスに使用されるデフォルトの代わりに)特定の目的にバインドされた、特定のサービスアカウントのためのトークンをオンデマンドでAPIサーバーからリクエストできます。 これを使用して、Linkerdインジェクターは、24時間の有効期限(アイデンティティの有効期限と同様)とともに、Linkerd専用にバインドされたトークンを要求します。このトークンは、Kubernetesによってポッドにマウントされたのと同じサービスアカウントに対して生成されるため、上記のアイデンティティとポリシーに関するLinkerdの既存の機能には影響しません。
code:yaml
spec:
containers:
...
volumeMounts:
- name: linkerd-identity-token
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
...
volumes:
- name: linkerd-identity-token
projected:
defaultMode: 420
sources:
- serviceAccountToken:
audience: identity.l5d.io
expirationSeconds: 86400
path: linkerd-identity-token
このトークンは、プロキシがアイデンティティで自分自身を検証するために、Linkerd用に特別に生成され、Kubernetes APIとの通信には使用できないため、関心の分離がうまくいきます。
Conclusion
この投稿では、Kubernetesの新しいバインドされたサービスアカウントトークンに移行する動機について説明しました。これにより、LinkerdによるKubernetes APIへのアクセスの範囲が、セキュリティ機能をサポートするために必要な最小限に抑えられます。また、証明書を発行する前にコントロールプレーンがプロキシを検証する方法の内部動作の一部を明らかにし、Linkerdが承認ポリシーなどの機能を構築するためのプリミティブとしてKubernetesのサービスアカウントを使用する方法を確認しました。
Linkerdの目標は、Kubernetesユーザーに負担をかけずにワールドクラスのセキュリティを提供することです。サービスアカウントに依存することで、Linkerdをインストールした瞬間に、すべてのメッシュポッドにゼロ構成のデフォルトの相互TLSを提供できます。また、バインドされたサービスアカウントを使用すると、実装は以前よりもさらに安全になります。
Linkerd is for everyone
Linkerdは、Cloud Native Computing Foundationのgraduatedプロジェクトです。 Linkerdはオープンガバナンスに取り組んでいます。機能のリクエスト、質問、コメントがある場合は、急成長しているコミュニティにぜひご参加ください。 LinkerdはGitHubでホストされており、Slack、Twitter、およびメーリングリストで活発なコミュニティがあります。是非、参加して楽しんでください!
/icons/hr.icon