はじめまして。VMware の伊藤です。Tanzu 製品のプラットフォームアーキテクトとして働いており、開発と運用双方の経験があります。この記事では TKG1.3 で追加された LDAP(もしくは OIDC)による認証機能を利用して Kubernetes クラスタへのアクセスコントロールと操作権限の制御方法について学びます。
TKG1.3 にはオプションの認証機能として「Identity Management(IM)」が提供されています。これは Okta などの OIDC を使ったり、LDAP (ActiveDirectory 含む)を使って「誰がどの Kubernetes クラスタをどのような権限で操作するか」ということを定義するために使います。この認証機能は k8s の「Dex」や「Pinniped」を使って構成されるオープンソースベースの仕組みであり、それが OIDC や LDAP などの様々な認証基盤を使うという構成になっています。本記事では自前の LDAP を認証基盤に利用していますが、自分で基盤を管理しない Okta などのほうが構築/管理面では優れています。金銭的なコストが許容できるのであれば、本番環境は OIDC をご検討ください。
IM 機能を使わない場合は「kubeconfigを持つユーザはadmin権限で操作できる。必要なユーザ以外には kubeconfig は渡さない」というシンプルながらも大雑把なアクセスコントロール方法となります。
以下に LDAP を使った IM 機能の概念図を記載します。
まず、TKG から独立した一般的な LDAP Server があり、そこにユーザー情報がおさめられています。この LDAP Server を TKG のマネージメントクラスタ構築時に指定することで IM 機能を有効化できます。IM 機能では Kubernetes のクラスターロールバインディングで「ユーザー名に権限(admin/write/readなど)を与える」操作をすることで、認証されたユーザーはクラスタにたいして定義された通りの権限でアクセスすることができます。ユーザー認証(ブラウザでクレデンシャルを入力)は kubectl でクラスタにアクセスした場合などに発生します。
上記の例では user-1 はクラスタのアドミン権限を定義されていますので、LDAP Server が管理するパスワードでログインできれば user-1 は kubectl get nodes コマンドの結果が得られます。一方、user-4 は LDAP Server にユーザー登録はあるものの、クラスタで役割を定義されていないためアクセス権限がありません。そのため kubectl get nodes を発行した際の LDAP 認証に成功したとしても、権限がない旨のエラーが表示されてコマンドの実行はされません。
なお、さきに説明したように IM を使った認証は必ず必要なものではありません。構築方法に応じて、以下のクラスタのアクセス制御方法を選択することができます。
TKG は Kubernetes ですので、いずれの手法でもクラスタにアクセスするためのコンテキストが必要になります。コンテキストは kubeconfig のファイルとして扱うか、直接現在のコンテキストファイルに追加するかのいずれかですが、管理用サーバー(後ほど紹介する bootstrap を使うことが多い)を除いて、ファイルを利用するのが一般的です。
IM 機能を有効にしない場合は上記の図にあるように「kubeconfig を持っていれば、誰でもアドミン権限としてアクセス可能」という使いかたしかできません。一方、IM を有効にしていても「誰でもアドミン権限でアクセス可能」という運用をとることもできます。
アクセス権限の制御はセキュリティ的には望ましいことですが、悪く言えば面倒くさいことでもあります。組織のポリシー次第でしょうが、IM を有効化したうえで「大事なところは IM 機能で権限制御を実施」し、「楽に使いたいテスト環境などはアドミン権限のみで運用」といった使いわけも考えられます。
テスト環境の構成
これから実際の TKG with LDAP-IM の構築と利用について説明をしますが、参考までに以下にテスト環境に使った構成を記載します。
重要なコンポーネントは以下の3つです。
- Bootstrap: マネージメントクラスタの構築に使うマシン。ワークロードクラスタのライフサイクル管理もここで実施するのが簡単。IM系の設定は全てここで実施
- ユーザのマシン: Bootstrapが管理するワークロードクラスタを使うユーザのマシン。今回は Ubuntu を使っているが、Windows/Macなどを使うことも多い。kubectl と tanzu cli のインストールが必要
- LDAP Server: SSLありの LDAPS で kubernetes からアクセスされるため、証明書周りの設定が必要。ようするに「このサーバーは信頼できません」とならない状態にする必要がある
LDAP Server については TKG にとっては本質的でないものの、正しく構築しないとIM機能を使えません。本記事の最後にこの構成で利用した証明書的に問題のない LDAP Server の構築方法を補足として紹介します。今回は Docker 上の OpenLDAP と phpLDAPadmin にたいして、Let’s Encrypt で生成した証明書を利用することで、正しい証明書を持つ LDAP Server を構築しています。
LDAP Server に問題がないかの確認
TKGのマネージメントクラスタの構築を開始するまえに、TKG が使う LDAP Server が正しく動作しているか確認することをおすすめします。ここがそもそも問題があると、TKG の設定に関係なく構築もしくは利用時の認証作業に失敗します。
LDAP サーバーが完全に停止している場合はすぐに気がつくと思いますので、ここでは証明書的に問題がないかのチェックをします。
1つめの openssl コマンドでは、証明書の発行元を確認しています。今回は Lets’ Encrypt を使った証明書を発行して利用していますので、それが確認できます。
2つめの ldapsearch コマンドではSSL付きの LDAPS でサーバーに問い合わせを実施しています。このLDAP Serverの管理ユーザーのDNである「cn=admin,dc=iyuichi-vmware,dc=com」を指定し、「ou=users,dc=iyuichi-vmware,dc=com」のユーザーを名前で検索しています。証明書やLDAPの設定に問題があると検索結果が表示されずにエラー表示がされます。よくあるエラー例は「ldap_sasl_bind(SIMPLE): Can’t contact LDAP server (-1)」などです。
この LDAP Server は問題なく LDAPS による接続ができることが確認できましたので、IMを使えるマネージメントクラスタを構築を開始します。
次項目のマネージメントクラスタのIM設定と照らし合わせることになるこのテスト構成の LDAP サーバーのエントリを以下に記載します。
詳細は LDAP 自体の解説記事や書籍を参考頂きたいですが、階層構造は左パネルのように構成されています。ルート(dc=iyuichi-vmware,dc=com)の直下にリードオンリーユーザー(cn=viewer)オブジェクト及び、グループ一覧(ou=groups)とユーザー一覧(ou=users)オブジェクトがあります。そして、グループ一覧配下にグループ1(cn=group-1)オブジェクトと、ユーザー一覧配下にユーザー1(uid=user-1)オブジェクトがあります。
IM機能を持つマネージメントクラスタの構築
マネージメントクラスタの構築は別記事で取り扱っていますので、詳細はそちらをご参照ください。
vSphere環境への TKG 1.3.0 with NSX-ALB の簡易展開手順
上記記事でスキップ(IM を有効化しなかった)した項目で IM を有効化できます。具体的には以下のスクリーンショットの設定項目となります。
Enable Identity Management にチェックをいれる(デフォルトでチェックされている)と OIDC もしくは LDAP を使った IM の設定項目が現れます。LDAP を選択した場合の各項目を上から説明します。
「LDAPS Identity Management Source」は LDAP サーバーへの接続情報です。それぞれ以下の設定をします。
- LDAPS Endpoint: 接続先のホストとポート。証明書がIPベースなら IP 指定、名前ベースならドメイン名を指定します。このテスト環境では Let’s Encrypt で名前登録している ldap.lab64.iyuichi-vmware.com をホストとして指定しています。ポートには LDAPS のデフォルトポートの636を指定しています。
- BIND DN: 接続に使うユーザー。ldapsearch できるユーザーを指定します。今回は「cn=viewer,dc=iyuichi-vmware,dc=com」を指定しています。
- BIND Password: BIND DN のユーザーパスワードです。
次の「User Search Attributes」は LDAP サーバーが持つ階層上のユーザーの位置と、ユーザーを特定するオブジェクトを指定します。
- BASE DN: 「user-1」がいる階層の「ou=users,dc=iyuichi-vmware,dc=com」を指定
- USERNAME: ユーザーのDNは「uid=user-1,ou=groups,dc=iyuichi-vmware,dc=com」などなので、特定するオブジェクトは「uid」となる
次の「Group Search Attributes」は LDAP 階層上のグループの位置と、グループを特定するオブジェクトを指定します。グループは IM の認証で直接利用しませんが、上記ユーザーの設定をした場合はグループの設定も必要となります。
- BASE DN: 「group-1」がいる階層の「ou=groups,dc=iyuichi-vmware,dc=com」を指定
- NAME ATTRIBUTE: グループのDNは「cn=group-1,ou=groups,dc=iyuichi-vmware,dc=com」などなので、特定するオブジェクトは「cn」となる
最後の Root CA は証明書の内容を貼り付けます。今回のテスト構成では Let’s Encrypt の certbot が生成した fullchain.pem の内容を指定しました。以上で IM の設定が完了です。このまま設定を進めてマネージメントクラスタの構築を完了してください。
IM の設定に問題がないかは展開されたマネージメントクラスタに IM 関連のリソースが展開されているかでチェック可能です。
このネームスペース tanzu-system-auth がなかったり、ポッドの展開に失敗している場合はパラメータに間違いがないか確認ください。
IM機能を使う kubeconfig の作成
マネージメントクラスタの準備が整いましたので、実際に IM 機能をテストします。このセクションではユーザーにアクセスさせるクラスタとその kubeconfig を作成します。また、LDAP ユーザーと権限のマッピングもします。それが終わったら、次のセクションで利用者側の操作方法を説明します。
以下におおまかな流れを記載します。
以下に上記図にある Bootstrap 上での操作例を記載します。
まず「tanzu cluster create」コマンドでクラスタを作成し、そのクラスタの admin コンテキストを取得してコンテキストスイッチしています。
次に「kubectl create clusterrolebinding <リソース名> –clusterrole <ロール名> –user <LDAPユーザー名>」コマンドでユーザーにたいして権限の設定をしています。今回であれば user-1 にたいして、cluster-admin の権限を与えています。
定義されているロールは以下となります。
- cluster-admin: なんでもできる
- admin: 運用者の仕事。コンテナのデプロイなどはできないが、ほぼ全てのリソースを見れて権限系(role,binding)あたりは変更できる
- edit: 開発者の仕事。権限系は操作できないが、コンテナ系は全て操作できる
- view: 閲覧のみ
定義されていないユーザーはこのクラスタの view すらできないので、権限の定義は必要ありません。
最後に「tanzu cluster kubeconfig get <クラスタ名> –export-file <kubeconfigファイル名>」で、IM を使う kubeconfig をファイルとして書き出しています。IM が有効化されていないとこのコマンドに失敗します。このコマンドにオプションで「–admin」を加えると IM をバイパスした誰でも cluster-admin 相当の操作ができる kubeconfig ファイルを得られます。
作成した IM を使う kubeconfig をこのクラスタを使うユーザーに配布します。IM を使う kubeconfig はクラスタの利用者以外が使ってもクラスタにアクセスできないので秘匿性はありません。共有ファイルサーバーなどに置いてしまっても問題ありません。一方、「–admin」オプションで生成した kubeconfig はそれがあれば誰でもアクセスできてしまうため、アクセスが必要なユーザー以外の手に渡らないように管理される必要があります。
IM を使った kubeconfig によるクラスタ操作
作成された kubeconfig を使って先ほど作成したクラスタを操作してみます。この操作端末には kubectl コマンドに加えて「tanzu cli」がインストールされている必要があるのでご注意ください。Docker は必要ではありません。
以下に「kubectl get nodes」コマンドを発行した際の状態遷移図を記載します。
IM を使う kubeconfig を指定してコマンドを発行すると、IM による認証画面がブラウザで開きます。その画面にユーザー名とパスワードを指定して Login ボタンをクリックします。Login に成功すると右下のように「ログインしました。このタブを閉じてください」とメッセージされ、タブを閉じるとコマンドが実行されます。
Login に失敗すると図の右上のブラウザ画面でログイン失敗メッセージが現れ、ログインに成功しても権限がないと左下のコンソール画面で「権限がありません」といったメッセージが出てコマンドが実行されません。
認証成功後は設定変更などがされない限りはしばらく再認証は発生しませんので、コマンドごとに毎回認証が発生することはありません。
参考情報: LDAP サーバーの構築
この機能のテストで一番苦労したポイントが実は LDAPS を正しく処理できる LDAP サーバーの構築でした。そのため、参考情報としてテスト構成で利用した LDAP サーバーの構築手順を記載します。
まず、TKG が LDAP Server に接続するプロトコルは LDAP ではなく LDAPS です。つまり SSL を使うので証明書が必要となります。信頼できない証明書を使うと「x509: cannot validate certificate for <host> because …」などといったエラーが認証時に発生しますので注意してください。
この問題を防ぐために正しい証明書を生成するのが正当な方法で、それが難しい場合は自己証明書を作成してクライアント側にそれを登録する処理をします。今回は Let’s Encrypt の DNS チャレンジ方式で正しい証明書を作成しています。一般的なトピックなので詳細は Google 検索などで調べてください。
Lets’s Encrypt の certbot コマンドによりドメイン「ldap.lab64.iyuichi-vmware.com」の証明書が4つ作成されます。
- cert.pem: サーバー証明書
- chain.pem: 中間CA証明書
- fullchain.pem: ルートCA証明書
- privkey.pem: プライベートキー
「ls -l」で確認できるように、これらのファイルは別ファイルへのソフトリンクとなっていますので、「/root/certs」ディレクトリにオリジナルのファイルをコピーしています。このコピーされた証明書を持つディレクトリを LDAP サーバーで利用します。
LDAP サーバーの構築手法はいくつかありますが、今回は OpenLDAP と、LDAPサーバーをGUIで操作する phpLDAPadmin を Docker 上に展開して利用します。OpenLDAP ではなく Windows Server Active Directory などを利用していただくのも一般的です。
OpenLDAP と phpLDAPadmin の Docker用イメージには以下を利用しています。コンテナの利用法はそれぞれのドキュメントを参照ください。
これらのコンテナを以下の Docker-Compose の定義ファイル(docker-compose.yml)で利用します。
設定について補足すると、2つのコンテナを接続するためにネットワーク(ldapnet)を作成し、それぞれのコンテナの永続化領域にボリュームを接続して LDAP サーバーが持つ情報をコンテナ再起動で失わないようにしています。LDAP や HTTP 系のポートはホストにそのままマッピングしています。
証明書はドキュメントに記載されるディレクトリにバインドでマウントし、証明書のファイル名を環境変数で指定しています。注意が必要なのが openldap の環境変数「LDAP_TLS_VERIFY_CLIENT」を try にしていることです。環境変数を設定しない場合のデフォルト値の demand だと、LDAPSでアクセスする際にクライアント側(TKG)にも証明が必要となります。つまり、TKGの LDAPS の認証に失敗します。
Docker-Compose のファイル定義ができたので、docker-compose コマンドでコンテナを展開して正常に動作していることを確認します。
コンテナレベルでの正常性を確認できたら、phpLDAPadmin に正しいドメイン名(今回は ldap.lab64.iyuichi-vmware.com)に HTTPS アクセスして、証明書エラーが表示されないことを確認します。
問題なければアドミンアカウント(cn=admin,dc=iyuichi-vmware,dc=com)で phpLDAPadmin にログインし、ユーザーとグループを定義します。今回は users と groups に「Generic: Organizational Unit」を指定し、groups の各エントリに「Generic: Posix Group」、users の各エントリに「Generic: Simple Security Object」を利用しています。
エントリが入力できたら、最後に LDAPS の証明書に問題ないか、及びエラー無く LDAPS で ldapsearch できるかを確認します。
両者とも問題なければ、LDAP サーバーの構築とエントリ作成は終了です。