明日 Kubernetes が少し楽しくなる認証の話

この記事はFixer Advent Calendar2021 ~技術編~4 日目の記事です。
また、なむゆの個人ブログにもマルチポストする予定です。

はじめに

この記事を読んでいる方は、普段 Kubernetes をどのように利用しているでしょうか。
多くの方が、アプリケーションのデプロイを日常的に行っているか、あるいはその処理を自動化する作業を日々行っていると思います。
アプリケーションをデプロイすると、当たり前のように Pod が展開されて動き始めますが、その後ろでは普段気にしなくても様々なパラメータが扱われており、それを使って処理が行われています。
そのことをそこまで深く知らなくても Kubernetes を扱うことができますが、知るともう少しだけ Kubernetes を深く知り、アプリケーションを展開するときに「あ~今頃 Kubernetes ではこんなことが起きてるんだな~」と思えるようになります。
今回はそんなお話、Pod が立ち上がるたびにやりとりされる認証関連のお話です。

Kubernetes には2種類のアカウントが居る

実は、Kubernetes には 2 種類のアカウントが居ます。
一つは、普段 Kubernetes 外部からユーザーが Kubernetes を操作するために使うユーザーアカウントです。
これは、ユーザーが Kubernetes を操作する際にその操作を行っていいかの認可処理や、そもそもその操作をしようとしているユーザーが何者かを認証するために使用されます。
ユーザーアカウントの認証はローカルに置かれている ~/.kube/config にある kubeconfig ファイルの中にあるクライアント証明書というものを用いて行います。

users:
- name: docker-desktop
  user:
    client-certificate-data: <クライアント証明データ>
    client-key-data: <クライアントキー>

上は docker-desktop にアクセスする際のクライアント証明書です。
Kubernetes にアクセスするときは上記のデータを Kubernetes 側に渡して、自分が何者であるか証明し、割り当てられている権限を用いて操作を行います。

2 種類のアカウントのうちもう一つは、サービスアカウントです。
こちらは、Kubernetes 内部のプロセスが使用するアカウントになります。
サービスアカウントは Namespace 毎に作られます。
そして、サービスアカウントが作られると、その資格情報としてトークンがSecret内に作られます。
つまり、作りたてほやほやの Namespace にも Secret とサービスアカウントが一つずつ作られています。

ローカルの Kubernetes で試してみましょう。
「そもそもローカルに Kubernetes 仕込んでないよ・・・」という方はこちらの記事を参考にしてローカル PC に Kubernetes を仕込んでみてください。
意外と簡単です。

kubectl create Namespace sample

上記のコマンドで Namespace を新規作成します。

kubectl get serviceaccount, secret

次に、上記のコマンドを実行して serviceaccount と secret のリソースの一覧を取得します。
結果は以下のようになるはずです。

NAME                     SECRETS   AGE
serviceaccount/default   1         11d
serviceaccount/sample1   1         2d

NAME                         TYPE                                  DATA   AGE
secret/default-token-8g2zb   kubernetes.io/service-account-token   3      11d
secret/sample1-token-5b9pr   kubernetes.io/service-account-token   3      2d

それぞれのリソースに、sample1 という名前のサービスアカウントと Secret が追加されています。
さらに、それとは別に「default」という名前のサービスアカウントと Secret も見えます。
こちらは、Namespace がまだ一つも手動で生成されていないときに自動で生成される Namespace で、Namespace を指定せずに作成されたリソースは全部 default の Namespace に作られます。

このように、namespace を新規作成するとそのたびに Secret とサービスアカウントが生成されます。

実は全部の Pod はサービスアカウントの資格情報を持っている

Kubernetes が動いているマシンの中で動いている Pod のプロセスも Kubernetes それ自体と通信することがあります。
ただ、Kubernetes と通信を行って何かしら操作しようとするマシン内のプロセス全部が Kubernetes のものとは限りません、ひょっとしたら何かしら悪意のあるプロセスがマシン内で動いていて Kubernetes を乗っ取ろうとしているかも?
ということで、プロセス側がそのような悪意のあるものでないと証明するのに使うのがサービスアカウントです。
Pod それぞれにサービスアカウントの資格情報を持たせることで、その Pod から kubernetes への通信に資格情報を含めて送ることでその通信がちゃんとした kubernetes のプロセスのものであると証明しています。
なので、全ての Pod は実はサービスアカウントの資格情報を持っているのです。
「え、これまで Pod 作るときにサービスアカウントの資格情報とか渡してないんだけど・・・」と思うかもしれませんが、大丈夫です、資格情報は指定しなくても自動的に default サービスアカウントのものが Pod に渡されています。

確認してみましょう。

kubectl run nginx --image=nginx
kubectl get pod nginx -o=yaml

すると、get pod コマンドを打った結果の大量の出力の中に次のような行があるかと思います。

  serviceAccount: default
  serviceAccountName: default

このように、サービスアカウントとしては Pod の生成時に明示的に指定しなくても、自動的に default のものが割り当るようになっています。
あるいは、サービスアカウントを作成してこれらのパラメータに設定することで明示的に「これを使って!」と宣言することもできます。
デフォルト以外のサービスアカウントを設定する目的としては、RBAC を用いて Pod 毎に厳密に権限をコントロールすることが挙げられますが、普段はあまり気にしないかもです。

おわりに

今回の記事では kubernetes の中で実は暗黙的に行われている認証の話について語ってみました。
ユーザーが認証、認可を受けて kubernetes とやりとりしているように、個々の Pod も認証、認可を行って kubernetes とやりとりしています。
これらの設定については普段触らなくても暗黙的にやってくれているものですが、知っておいて損はないかと思います。
明日からは「あ~今 kubernetes の中ではそれぞれの Pod にサービスアカウントが割り当っててそのおかげで Pod のプロセスが kubernetes とやりとりできているんだな~」と思いながらアプリケーションをデプロイしてください。

参考