Linkerdにおけるトラフィック制御
Linkerd は Kubernetes 用の軽量サービスメッシュです。
複雑な設定なしにセキュリティ、可観測性、信頼性をクラスタに追加できるのが特徴とされています。
また CNCF では Graduated Project としてホストされています。
(ちなみにサービスメッシュのデファクトスタンダードとされている Istio は CNCF では Incubating Project です。)
Linkerd の機能の 1 つにトラフィックの制御があります。
これはある Pod にリクエストを投げられるのは特定の Pod だけというような制限をかけるためのものです。
トラフィック制御の設定は
- デフォルトポリシーの設定
- カスタムリソース(以下、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 もありますが、ServerAuthorization
はAuthorizationPolicy
の下位互換である古い CR で、現在はAuthorizationPolicy
が推奨されているため、今回は解説しません。
AuthorizationPolicy
を解説するにあたって、どのようなリソースをターゲットやリクエスト元の条件に設定できるかから説明していきたいのですが、そのためにまずは Linkerd の mTLS の仕組みについて説明しようと思います。
Linkerd の mTLS
ここでは Linkerd の mTLS 機能のうち、トラフィック制御に関係ある部分のみを説明します。
まず Linkerd にはコントロールプレーン部分とデータプレーン部分があります。
コントロールプレーンは Linkerd 全体の制御をするための複数のコンポーネントから成っていて、特に TLS 認証局(以下、CA)を含みます。
データプレーンは各 Pod のサイドカーとして動作し、特にプロキシのコンポーネントを含みます。
下図で identity が CA のコンポーネント、linkerd-proxy がプロキシのコンポーネントです。
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 としてはServerAuthorization
とServiceProfile
があります。
5 つの CR は大きく 3 つに分類されます。
- トラフィック認可のルールを表現する
AuthorizationPolicy
- ルールのうちトラフィックのターゲットを表現する
Server
とHTTPRoute
- ルールのうちトラフィックが許可される条件を表現する
MeshTLSAuthentication
とNetworkAuthentication
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
ではServer
かHTTPRouter
を指定し、spec.requiredAuthenticationRefs[*]
ではMeshTLSAuthentication
かNetworkAuthentication
を指定するようです。
筆者の理解では同一クラスタ内の Pod からのトラフィックはMeshTLSAuthentication
で指定し、Node や別クラスタからのトラフィックはNetworkAuthentication
を使うのではないかと思っています。
Linkerd はデフォルトポリシーとしてdeny
などの強い制限がかけられている場合でも Probe 用のリクエストは自動で認可するようになっていますが、AuthorizationPolicy
を利用する場合は注意が必要です。
この自動認可は Probe 用のポートにServer
とHTTPRoute
の両方が設定されている場合には無効化されてしまい、Pod は立ち上がらなくなります。
回避策としては以下のような方法が考えられます。
-
Server
に対してHTTPRoute
を設定しない - Probe 用ポートとアプリケーションのポートを分け、
Server
では後者のみ指定する - kubelet からのヘルスチェックリクエストを
NetworkAuthentication
で別途認可する
終わりに
本記事は Linkerd についての日本語の記事があまり多くなかったことから執筆しました。
筆者がドキュメントを読んだ時はトラフィック制御について特に SA がなぜ関わってくるのか分かりづらく感じましたが、それが本記事で解消できていればと思います。
一方で筆者の TLS、認証、認可への理解は浅い自覚がありますので、単語の用法や説明の誤りがありましたら是非ご指摘してもらえればと思います。
参考
Discussion