🕸

Linkerdにおけるトラフィック制御

2022/12/24に公開

Linkerd は Kubernetes 用の軽量サービスメッシュです。
複雑な設定なしにセキュリティ、可観測性、信頼性をクラスタに追加できるのが特徴とされています。
また CNCF では Graduated Project としてホストされています。
(ちなみにサービスメッシュのデファクトスタンダードとされている Istio は CNCF では Incubating Project です。)

Linkerd の機能の 1 つにトラフィックの制御があります。
これはある Pod にリクエストを投げられるのは特定の Pod だけというような制限をかけるためのものです。
トラフィック制御の設定は

  1. デフォルトポリシーの設定
  2. カスタムリソース(以下、CR) によるきめ細かい設定

の 2 つの段階で行います。

デフォルトポリシー

デフォルトポリシーとして設定できるフィールドには以下のようなものがあります

  • all-unauthenticated: 全てのトラフィックを許可する。何も設定しない場合はこれになります。
  • all-authenticated: サービスメッシュ内のクライアントからのトラフィックを全て許可します。マルチクラスタの場合、別クラスタのサービスメッシュ内のクライアントも許可されます。
  • cluster-authenticated: 同一クラスタのサービスメッシュ内のクライアントからのトラフィックを許可します。
  • cluster-unauthenticated: 同一クラスタ内のクライアントからのトラフィックを許可します。
  • deny: 全てのトラフィックを拒否します。

デフォルトポリシーは Linkerd インストール時にproxy.defaultInboundPolicyとして設定することでクラスタ全体に適用することができます。
また Namespace と Pod(テンプレート)に対してconfig.linkerd.io/default-inbound-policyアノテーションとして設定することでクラスタ全体の設定を上書きすることができます。

設定は名前の通りインバウンド方向のものです。
例えばある Pod に対してdenyを設定した場合はその Pod へのリクエストが拒否されることになります。
後々説明するAuthorizationPolicyについても 1 つのターゲットへのインバウンドリクエストを認可する方式になっていて、Linkerd のトラフィック制御はインバウンド方向で考えるように統一されています。

具体的にどの設定をするべきかはドキュメントで見つけられませんでしたが、セキュリティは基本的にホワイトリスト方式にするべきですので、可能ならdenyを設定する方が良いと思います。
Linkerd のサービスメッシュ内通信は全て TLS を利用する(mTLS)ので、denyが設定できない場合でもxxx-authenticatedにするなどできるだけセキュアな値を設定する方が良いはずです。

きめ細かい設定

デフォルトポリシーでdenyなどの強い制限をかけた場合、どのようなトラフィックを許可していくかを設定していく必要があります。
Linkerd ではAuthorizationPolicyという CR で、許可するリクエストのターゲットとリクエスト元の条件を設定します。
ServerAuthorizationという CR もありますが、ServerAuthorizationAuthorizationPolicyの下位互換である古い CR で、現在はAuthorizationPolicyが推奨されているため、今回は解説しません。

AuthorizationPolicyを解説するにあたって、どのようなリソースをターゲットやリクエスト元の条件に設定できるかから説明していきたいのですが、そのためにまずは Linkerd の mTLS の仕組みについて説明しようと思います。

Linkerd の mTLS

ここでは Linkerd の mTLS 機能のうち、トラフィック制御に関係ある部分のみを説明します。

まず Linkerd にはコントロールプレーン部分とデータプレーン部分があります。
コントロールプレーンは Linkerd 全体の制御をするための複数のコンポーネントから成っていて、特に TLS 認証局(以下、CA)を含みます。
データプレーンは各 Pod のサイドカーとして動作し、特にプロキシのコンポーネントを含みます。
下図で identity が CA のコンポーネント、linkerd-proxy がプロキシのコンポーネントです。

control-plane

CA はプロキシに対して証明書を発行します。
この証明書は Kubernetes の ServiceAccount(以下、SA)を表す Identity に紐づいています。
Identity は"books.booksapp.serviceaccount.identity.linkerd.cluster.local"のように"{SA}.{NAMESPACE}.serviceaccount.identity.linkerd.cluster.local"の形になっています。
また Linkerd が SA を利用しているのは、SA が Kubernetes で認証のために使われているリソースだからという理解です。
プロキシはこの証明書を使用してサービスメッシュ内での全てのリクエストを TLS で行います。
そのため、トラフィック制御にあたっても SA が深く関わってきます。

各 CR について

ここではトラフィック制御に関係があり、かつ非推奨でない 5 つのカスタムリソース(CR)について説明します。
なおここで説明しない CR としてはServerAuthorizationServiceProfileがあります。

5 つの CR は大きく 3 つに分類されます。

  • トラフィック認可のルールを表現するAuthorizationPolicy
  • ルールのうちトラフィックのターゲットを表現するServerHTTPRoute
  • ルールのうちトラフィックが許可される条件を表現するMeshTLSAuthenticationNetworkAuthentication

Server リソース

Serverは Pod とそのポートと通信プロトコルを指定します。
Pod の指定はspec.podSelectorで行いますが、これには Kubernetes のlabelSelectorと同じ構文を使います。

apiVersion: policy.linkerd.io/v1beta1
kind: Server
metadata:
  namespace: emojivoto
  name: backend-services
spec:
  podSelector:
    matchExpressions:
      - { key: app, operator: In, values: [voting-svc, emoji-svc] }
      - { key: environment, operator: NotIn, values: [dev] }
  port: 8080
  proxyProtocol: "HTTP/2"

HTTPRoute リソース

Linkerd ではHTTPRouteを使うことでServerで指定した Pod のトラフィックのさらに一部を指定することができます。
spec.parentRefs[*]Serverを指定し、spec.rules[*]でどのようなトラフィックを指定するか設定します。
例えばspec.rules[*].matchesではパス、ヘッダ、クエリパラメータ、メソッドを指定できます。

apiVersion: policy.linkerd.io/v1beta1
kind: HTTPRoute
metadata:
  name: authors-get-route
  namespace: booksapp
spec:
  parentRefs:
    - name: authors-server
      kind: Server
      group: policy.linkerd.io
  rules:
    - matches:
        - path:
            value: "/authors.json"
          method: GET
        - path:
            value: "/authors/"
            type: "PathPrefix"
          method: GET

MeshTLSAuthentication リソース

MeshTLSAuthenticationではトラフィックを許可する Identity を指定します。
spec.identities[*]で Identity を指定できるほか、spec.identityRefs[*]では Kubernetes の SA を直接指定できます。
筆者の理解では、SA を利用できるのは Linkerd が SA を用いて認証を行っているからです。

apiVersion: policy.linkerd.io/v1alpha1
kind: MeshTLSAuthentication
metadata:
  name: authors-get-authn
  namespace: booksapp
spec:
  identities:
    - "books.booksapp.serviceaccount.identity.linkerd.cluster.local"
    - "webapp.booksapp.serviceaccount.identity.linkerd.cluster.local"
apiVersion: policy.linkerd.io/v1alpha1
kind: MeshTLSAuthentication
metadata:
  name: authors-get-authn
  namespace: booksapp
spec:
  identityRefs:
    - kind: ServiceAccount
      name: books
    - kind: ServiceAccount
      name: webapp

NetworkAuthentication リソース

NetworkAuthenticationではトラフィックを許可する IP サブネットを指定します。

apiVersion: policy.linkerd.io/v1alpha1
kind: NetworkAuthentication
metadata:
  name: cluster-network
  namespace: booksapp
spec:
  networks:
    - cidr: 10.0.0.0/8
    - cidr: 100.64.0.0/10
    - cidr: 172.16.0.0/12
    - cidr: 192.168.0.0/16

AuthorizationPolicy リソース

ここまでで説明した CR 達を使って、認可のルールを記述します。
AuthorizationPolicyで対象となるのはここまでで説明した 4 つの CR と Kubernetes の Namespace, SA です。
spec.targetRefでインバウンドポリシーを設定する対象を 1 つ指定し、spec.requiredAuthenticationRefs[*]で認証に使う方法を指定します。

apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
  name: authors-get-policy
  namespace: booksapp
spec:
  targetRef:
    group: policy.linkerd.io
    kind: HTTPRoute
    name: authors-get-route
  requiredAuthenticationRefs:
    - name: authors-get-authn
      kind: MeshTLSAuthentication
      group: policy.linkerd.io

spec.requiredAuthenticationRefs[*]で SA を指定した例

apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
  name: authors-policy
  namespace: booksapp
spec:
  targetRef:
    group: policy.linkerd.io
    kind: Server
    name: authors
  requiredAuthenticationRefs:
    - name: webapp
      kind: ServiceAccount

spec.targetRefに Namespace を指定した例

apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
  name: authors-policy
  namespace: booksapp
spec:
  targetRef:
    kind: Namespace
    name: booksapp
  requiredAuthenticationRefs:
    - name: webapp
      kind: ServiceAccount

ドキュメントによると、ポリシーの一般的なパターンはspec.targetRefではServerHTTPRouterを指定し、spec.requiredAuthenticationRefs[*]ではMeshTLSAuthenticationNetworkAuthenticationを指定するようです。
筆者の理解では同一クラスタ内の Pod からのトラフィックはMeshTLSAuthenticationで指定し、Node や別クラスタからのトラフィックはNetworkAuthenticationを使うのではないかと思っています。

Linkerd はデフォルトポリシーとしてdenyなどの強い制限がかけられている場合でも Probe 用のリクエストは自動で認可するようになっていますが、AuthorizationPolicyを利用する場合は注意が必要です。
この自動認可は Probe 用のポートにServerHTTPRouteの両方が設定されている場合には無効化されてしまい、Pod は立ち上がらなくなります。
回避策としては以下のような方法が考えられます。

  • Serverに対してHTTPRouteを設定しない
  • Probe 用ポートとアプリケーションのポートを分け、Serverでは後者のみ指定する
  • kubelet からのヘルスチェックリクエストをNetworkAuthenticationで別途認可する

終わりに

本記事は Linkerd についての日本語の記事があまり多くなかったことから執筆しました。
筆者がドキュメントを読んだ時はトラフィック制御について特に SA がなぜ関わってくるのか分かりづらく感じましたが、それが本記事で解消できていればと思います。
一方で筆者の TLS、認証、認可への理解は浅い自覚がありますので、単語の用法や説明の誤りがありましたら是非ご指摘してもらえればと思います。

参考

https://linkerd.io/2.12/features/server-policy
https://linkerd.io/2.12/reference/authorization-policy
https://linkerd.io/2.12/reference/architecture
https://linkerd.io/2.12/features/automatic-mtls

Discussion