認証

このページでは、認証の概要について説明します。

Kubernetesにおけるユーザー

すべてのKubernetesクラスターには、2種類のユーザーがあります。Kubernetesによって管理されるサービスアカウントと、通常のユーザーです。

クラスターから独立したサービスは通常のユーザーを以下の方法で管理することを想定されています。

  • 秘密鍵を配布する管理者
  • KeystoneやGoogle Accountsのようなユーザーストア
  • ユーザー名とパスワードのリストを持つファイル

これを考慮すると、 Kubernetesは通常のユーザーアカウントを表すオブジェクトを持ちません。 APIコールを介して、通常のユーザーをクラスターに追加することはできません。

APIコールを介して通常のユーザーを追加できませんが、クラスターの認証局(CA)に署名された有効な証明書で表すユーザーは認証済みと判断されます。この構成では、Kubernetesは証明書の‘subject’内にある一般的な名前フィールド(例えば、“/CN=bob”)からユーザー名を特定します。そこから、ロールベースアクセス制御(RBAC)サブシステムは、ユーザーがあるリソースにおける特定の操作を実行するために認証済みかどうか特定します。詳細は、 証明書要求内の通常のユーザーの題目を参照してください。

対照的に、サービスアカウントはKubernetes APIによって管理されるユーザーです。サービスアカウントは特定の名前空間にバインドされており、APIサーバーによって自動的に作成されるか、APIコールによって手動で作成されます。サービスアカウントは、Secretsとして保存された資格情報の集合に紐付けられています。これをPodにマウントすることで、クラスター内のプロセスがKubernetes APIと通信できるようにします。

APIリクエストは、通常のユーザーかサービスアカウントに紐付けられているか、匿名リクエストとして扱われます。つまり、ワークステーションでkubectlを入力する人間のユーザーから、ノード上のkubeletsやコントロールプレーンのメンバーまで、クラスター内外の全てのプロセスは、APIサーバーへのリクエストを行う際に認証を行うか匿名ユーザーとして扱われる必要があります。

認証戦略

Kubernetesは、クライアント証明書、Bearerトークン、認証プロキシー、HTTP Basic認証を使い、認証プラグインを通してAPIリクエストを認証します。APIサーバーにHTTPリクエストが送信されると、プラグインは以下の属性をリクエストに関連付けようとします。

  • ユーザー名: エンドユーザーを識別する文字列です。一般的にな値は、kube-adminjane@example.comです。
  • UID: エンドユーザーを識別する文字列であり、ユーザー名よりも一貫性と一意性を持たせようとするものです。
  • グループ: 各要素がユーザーの役割を示すような意味を持つ文字列の集合です。system:mastersdevops-teamといった値が一般的です。
  • 追加フィールド: 認証者が有用と思われる追加情報を保持する文字列のリストに対する、文字列のマップです。

すべての値は認証システムに対して非透過であり、認可機能が解釈した場合にのみ意味を持ちます。

一度に複数の認証方法を有効にすることができます。通常は、以下のように少なくとも2つの方法を使用するべきです。

  • サービスアカウント用のサービスアカウントトークン
  • ユーザー認証のための、少なくとも1つの他の方法

複数の認証モジュールが有効化されている場合、リクエストの認証に成功した最初のモジュールが、評価が簡略化します。APIサーバーは、認証の実行順序を保証しません。

system:authenticatedグループには、すべての認証済みユーザーのグループのリストが含まれます。

他の認証プロトコル(LDAP、SAML、Kerberos、X509スキームなど)との統合は、認証プロキシー認証Webhookを使用して実施できます。

X509クライアント証明書

クライアント証明書認証は、APIサーバーに--client-ca-file=SOMEFILEオプションを渡すことで有効になります。参照されるファイルには、APIサーバーに提示されたクライアント証明書を検証するために使用する1つ以上の認証局が含まれている必要があります。クライアント証明書が提示され、検証された場合、サブジェクトのCommon Nameがリクエストのユーザー名として使用されます。Kubernetes1.4時点では、クライアント証明書は、証明書のOrganizationフィールドを使用して、ユーザーのグループメンバーシップを示すこともできます。あるユーザーに対して複数のグループメンバーシップを含めるには、証明書に複数のOrganizationフィールドを含めます。

例えば、証明書署名要求を生成するために、opensslコマンドラインツールを使用します。

openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj "/CN=jbeda/O=app1/O=app2"

これにより、"app1"と"app2"の2つのグループに属するユーザー名"jbeda"の証明書署名要求が作成されます。

クライアント証明書の生成方法については、証明書の管理を参照してください。

静的なトークンファイル

コマンドラインで--token-auth-file=SOMEFILEオプションを指定すると、APIサーバーはファイルからBearerトークンを読み込みます。現在のところ、トークンの有効期限は無く、APIサーバーを再起動しない限りトークンのリストを変更することはできません。

トークンファイルは、トークン、ユーザー名、ユーザーUIDの少なくとも3つの列を持つcsvファイルで、その後にオプションでグループ名が付きます。

リクエストにBearerトークンを含める

HTTPクライアントからBearerトークン認証を利用する場合、APIサーバーはBearer THETOKENという値を持つAuthorizationヘッダーを待ち受けます。Bearerトークンは、HTTPのエンコーディングとクォート機能を利用してHTTPヘッダーの値に入れることができる文字列でなければなりません。例えば、Bearerトークンが31ada4fd-adec-460c-809a-9e56ceb75269であれば、HTTPのヘッダを以下のようにします。

Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

ブートストラップトークン

FEATURE STATE: Kubernetes v1.18 [stable]

新しいクラスタの効率的なブートストラップを可能にするために、Kubernetesにはブートストラップトークンと呼ばれる動的に管理されたBearerトークンタイプが含まれています。これらのトークンは、kube-system名前空間にSecretsとして格納され、動的に管理したり作成したりすることができます。コントローラーマネージャーには、TokenCleanerコントローラーが含まれており、ブートストラップトークンの有効期限が切れると削除します。

トークンの形式は[a-z0-9]{6}.[a-z0-9]{16}です。最初のコンポーネントはトークンIDであり、第2のコンポーネントはToken Secretです。以下のように、トークンをHTTPヘッダーに指定します。

Authorization: Bearer 781292.db7bc3a58fc5f07e

APIサーバーの--enable-bootstrap-token-authフラグで、Bootstrap Token Authenticatorを有効にする必要があります。TokenCleanerコントローラーを有効にするには、コントローラーマネージャーの--controllersフラグを使います。--controllers=*,tokencleanerのようにして行います。クラスターをブートストラップするためにkubeadmを使用している場合は、kubeadmがこれを代行してくれます。

認証機能はsystem:bootstrap:<Token ID>という名前で認証します。これはsystem:bootstrappersグループに含まれます。名前とグループは意図的に制限されており、ユーザーがブートストラップ後にこれらのトークンを使わないようにしています。ユーザー名とグループは、クラスタのブートストラップをサポートする適切な認可ポリシーを作成するために使用され、kubeadmによって使用されます。

ブートストラップトークンの認証機能やコントローラーについての詳細な説明、kubeadmでこれらのトークンを管理する方法については、ブートストラップトークンを参照してください。

サービスアカウントトークン

サービスアカウントは、自動的に有効化される認証機能で、署名されたBearerトークンを使ってリクエストを検証します。このプラグインは、オプションとして2つのフラグを取ります。

  • --service-account-key-file: Bearerトークンに署名するためのPEMエンコードされた鍵を含むファイルです。指定しない場合は、APIサーバーのTLS秘密鍵が使われます。
  • --service-account-lookup: 有効にすると、APIから削除されたトークンは取り消されます。

サービスアカウントは通常、APIサーバーによって自動的に作成され、ServiceAccountAdmission Controllerを介してクラスター内のPodに関連付けられます。Bearerトークンは、Podのよく知られた場所にマウントされ、これによりクラスター内のプロセスがAPIサーバー通信できるようになります。アカウントはPodSpecserviceAccountNameフィールドを使って、明示的にPodに関連付けることができます。

apiVersion: apps/v1 # このapiVersionは、Kubernetes1.9時点で適切です
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  template:
    metadata:
    # ...
    spec:
      serviceAccountName: bob-the-bot
      containers:
      - name: nginx
        image: nginx:1.14.2

サービスアカウントのBearerトークンは、クラスター外で使用するために完全に有効であり、Kubernetes APIと通信したい長期的なジョブのアイデンティティを作成するために使用することができます。サービスアカウントを手動で作成するには、単にkubectl create serviceaccount (NAME)コマンドを使用します。これにより、現在の名前空間にサービスアカウントと関連するSecretが作成されます。

kubectl create serviceaccount jenkins
serviceaccount "jenkins" created

以下のように、関連するSecretを確認できます。

kubectl get serviceaccounts jenkins -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  # ...
secrets:
- name: jenkins-token-1yvwg

作成されたSecretは、APIサーバーのパブリック認証局と署名されたJSON Web Token(JWT)を保持します。

kubectl get secret jenkins-token-1yvwg -o yaml
apiVersion: v1
data:
  ca.crt: (base64でエンコードされたAPIサーバーの認証局)
  namespace: ZGVmYXVsdA==
  token: (base64でエンコードされたBearerトークン)
kind: Secret
metadata:
  # ...
type: kubernetes.io/service-account-token

署名されたJWTは、与えられたサービスアカウントとして認証するためのBearerトークンとして使用できます。トークンをリクエストに含める方法については、リクエストにBearerトークンを含めるを参照してください。通常、これらのSecretはAPIサーバーへのクラスタ内アクセス用にPodにマウントされますが、クラスター外からも使用することができます。

サービスアカウントは、ユーザー名system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT)で認証され、グループsystem:serviceaccountssystem:serviceaccounts:(NAMESPACE)に割り当てられます。

警告: サービスアカウントトークンはSecretに保持されているため、Secretにアクセスできるユーザーは誰でもサービスアカウントとして認証することができます。サービスアカウントに権限を付与したり、Secretの読み取り機能を付与したりする際には注意が必要です。

OpenID Connectトークン

OpenID Connectは、Azure Active Directory、Salesforce、Googleなど、いくつかのOAuth2プロバイダーでサポートされているOAuth2の一種です。 このプロトコルのOAuth2の主な拡張機能は、ID Tokenと呼ばれる、アクセストークンとアクセストークンと一緒に返される追加フィールドです。 このトークンは、ユーザーの電子メールなどのよく知られたフィールドを持つJSON Web Token(JWT)であり、サーバーによって署名されています。トークンをリクエストに含める方法については、リクエストにBearerトークンを含めるを参照してください。

Kubernetes OpenID Connect Flow

  1. IDプロバイダーにログインします
  2. IDプロバイダーは、access_tokenid_tokenrefresh_tokenを提供します
  3. kubectlを使う場合は、--tokenフラグでid_tokenを使うか、kubeconfigに直接追加してください
  4. kubectlは、id_tokenをAuthorizationと呼ばれるヘッダーでAPIサーバーに送ります
  5. APIサーバーは、設定で指定された証明書と照合することで、JWT署名が有効であることを確認します
  6. id_tokenの有効期限が切れていないことを確認します
  7. ユーザーが認可されていることを確認します
  8. 認可されると、APIサーバーはkubectlにレスポンスを返します
  9. kubectlはユーザーにフィードバックを提供します

自分が誰であるかを確認するために必要なデータはすべてid_tokenの中にあるので、KubernetesはIDプロバイダーと通信する必要がありません。すべてのリクエストがステートレスであるモデルでは、これは非常に認証のためのスケーラブルなソリューションを提供します。一方で、以下のようにいくつか課題があります。

  1. Kubernetesには、認証プロセスを起動するための"Webインターフェース"がありません。クレデンシャルを収集するためのブラウザやインターフェースがないため、まずIDプロバイダに認証を行う必要があります。
  2. id_tokenは、取り消すことができません。これは証明書のようなもので、有効期限が短い(数分のみ)必要があるので、数分ごとに新しいトークンを取得しなければならないのは非常に面倒です。
  3. Kubernetesダッシュボードへの認証において、kubectl proxyコマンドやid_tokenを注入するリバースプロキシーを使う以外に、簡単な方法はありません。

APIサーバーの設定

プラグインを有効にするには、APIサーバーで以下のフラグを設定します。

パラメーター説明必須か
--oidc-issuer-urlAPIサーバーが公開署名鍵を発見できるようにするプロバイダーのURLです。 https://スキームを使用するURLのみが受け入れられます。これは通常、"https://accounts.google.com"や"https://login.salesforce.com"のようにパスを持たないプロバイダのディスカバリーURLです。このURLは、.well-known/openid-configurationの下のレベルを指す必要があります。ディスカバリーURLがhttps://accounts.google.com/.well-known/openid-configurationである場合、値はhttps://accounts.google.comとします。はい
--oidc-client-idすべてのトークンが発行されなければならないクライアントIDです。kubernetesはい
--oidc-username-claimユーザー名として使用するJWTのクレームを指定します。デフォルトではsubが使用されますが、これはエンドユーザーの一意の識別子であることが期待されます。管理者はプロバイダーに応じてemailnameなどの他のクレームを選択することができます。ただし、他のプラグインとの名前の衝突を防ぐために、email以外のクレームには、プレフィックスとして発行者のURLが付けられます。subいいえ
--oidc-username-prefix既存の名前(system:ユーザーなど)との衝突を防ぐために、ユーザー名の前にプレフィックスを付加します。例えばoidc:という値は、oidc:jane.doeのようなユーザー名を生成します。このフラグが指定されておらず、--oidc-username-claimemail以外の値である場合、プレフィックスのデフォルトは(Issuer URL)#で、(Issuer URL)--oidc-issuer-urlの値です。すべてのプレフィックスを無効にするためには、-という値を使用できます。oidc:いいえ
--oidc-groups-claimユーザーのグループとして使用するJWTのクレームです。クレームがある場合は、文字列の配列である必要があります。groupsいいえ
--oidc-groups-prefix既存の名前(system:グループなど)との衝突を防ぐために、グループ名の前にプレフィックスを付加します。例えばoidc:という値は、oidc:engineeringoidc:infraのようなグループ名を生成します。oidc:いいえ
--oidc-required-claimIDトークンの中の必須クレームを記述するkey=valueのペアです。設定されている場合、クレームが一致する値でIDトークンに存在することが検証されます。このフラグを繰り返して複数のクレームを指定します。claim=valueいいえ
--oidc-ca-fileIDプロバイダーのWeb証明書に署名した認証局の証明書へのパスです。デフォルトはホストのルート認証局が指定されます。/etc/kubernetes/ssl/kc-ca.pemいいえ

重要なのは、APIサーバーはOAuth2クライアントではなく、ある単一の発行者を信頼するようにしか設定できないことです。これにより、サードパーティーに発行されたクレデンシャルを信頼せずに、Googleのようなパブリックプロバイダーを使用することができます。複数のOAuthクライアントを利用したい管理者は、azpクレームをサポートしているプロバイダや、あるクライアントが別のクライアントに代わってトークンを発行できるような仕組みを検討する必要があります。

KubernetesはOpenID Connect IDプロバイダーを提供していません。既存のパブリックなOpenID Connect IDプロバイダー(Googleやその他など)を使用できます。もしくは、CoreOS dexKeycloak、CloudFoundryUAA、Tremolo SecurityのOpenUnisonなど、独自のIDプロバイダーを実行することもできます。

IDプロバイダーがKubernetesと連携するためには、以下のことが必要です。

  1. すべてではないが、[OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html)をサポートしていること
  2. 廃れていない暗号を用いたTLSで実行されていること
  3. 認証局が署名した証明書を持っていること(認証局が商用ではない場合や、自己署名の場合も可)

上述の要件#3、認証局署名付き証明書を必要とすることについて、注意事項があります。GoogleやMicrosoftなどのクラウドプロバイダーではなく、独自のIDプロバイダーをデプロイする場合は、たとえ自己署名されていても、CAフラグがTRUEに設定されている証明書によって署名されたIDプロバイダーのWebサーバー証明書を持っていなければなりません。これは、Go言語のTLSクライアント実装が、証明書検証に関する標準に対して非常に厳格であるためです。認証局をお持ちでない場合は、CoreOSチームのこのスクリプトを使用して、シンプルな認証局と署名付きの証明書と鍵のペアを作成することができます。 または、この類似のスクリプトを使って、より寿命が長く、よりキーサイズの大きいSHA256証明書を生成できます。

特定のシステム用のセットアップ手順は、以下を参照してください。

kubectlの使用

選択肢1 - OIDC認証機能

最初の選択肢は、kubectlのoidc認証機能を利用することです。これはすべてのリクエストのBearerトークンとしてid_tokenを設定し、有効期限が切れるとトークンを更新します。プロバイダーにログインした後、kubectlを使ってid_tokenrefresh_tokenclient_idclient_secretを追加してプラグインを設定します。

リフレッシュトークンのレスポンスの一部としてid_tokenを返さないプロバイダーは、このプラグインではサポートされていないので、以下の"選択肢2"を使用してください。

kubectl config set-credentials USER_NAME \
   --auth-provider=oidc \
   --auth-provider-arg=idp-issuer-url=( issuer url ) \
   --auth-provider-arg=client-id=( your client id ) \
   --auth-provider-arg=client-secret=( your client secret ) \
   --auth-provider-arg=refresh-token=( your refresh token ) \
   --auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
   --auth-provider-arg=id-token=( your id_token )

例として、IDプロバイダーに認証した後に以下のコマンドを実行します。

kubectl config set-credentials mmosley  \
        --auth-provider=oidc  \
        --auth-provider-arg=idp-issuer-url=https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP  \
        --auth-provider-arg=client-id=kubernetes  \
        --auth-provider-arg=client-secret=1db158f6-177d-4d9c-8a8b-d36869918ec5  \
        --auth-provider-arg=refresh-token=q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXqHega4GAXlF+ma+vmYpFcHe5eZR+slBFpZKtQA= \
        --auth-provider-arg=idp-certificate-authority=/root/ca.pem \
        --auth-provider-arg=id-token=eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw

これは以下のような構成になります。

users:
- name: mmosley
  user:
    auth-provider:
      config:
        client-id: kubernetes
        client-secret: 1db158f6-177d-4d9c-8a8b-d36869918ec5
        id-token: eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw
        idp-certificate-authority: /root/ca.pem
        idp-issuer-url: https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP
        refresh-token: q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXq
      name: oidc

id_tokenの有効期限が切れると、kubectlrefresh_tokenclient_secretを用いてid_tokenの更新しようとします。refresh_tokenid_tokenの新しい値は、.kube/configに格納されます。

選択肢2 - --tokenオプションの使用

kubectlコマンドでは、--tokenオプションを使ってトークンを渡すことができる。以下のように、このオプションにid_tokenをコピーして貼り付けるだけです。

kubectl --token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL21sYi50cmVtb2xvLmxhbjo4MDQzL2F1dGgvaWRwL29pZGMiLCJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNDc0NTk2NjY5LCJqdGkiOiI2RDUzNXoxUEpFNjJOR3QxaWVyYm9RIiwiaWF0IjoxNDc0NTk2MzY5LCJuYmYiOjE0NzQ1OTYyNDksInN1YiI6Im13aW5kdSIsInVzZXJfcm9sZSI6WyJ1c2VycyIsIm5ldy1uYW1lc3BhY2Utdmlld2VyIl0sImVtYWlsIjoibXdpbmR1QG5vbW9yZWplZGkuY29tIn0.f2As579n9VNoaKzoF-dOQGmXkFKf1FMyNV0-va_B63jn-_n9LGSCca_6IVMP8pO-Zb4KvRqGyTP0r3HkHxYy5c81AnIh8ijarruczl-TK_yF5akjSTHFZD-0gRzlevBDiH8Q79NAr-ky0P4iIXS8lY9Vnjch5MF74Zx0c3alKJHJUnnpjIACByfF2SCaYzbWFMUNat-K1PaUk5-ujMBG7yYnr95xD-63n8CO8teGUAAEMx6zRjzfhnhbzX-ajwZLGwGUBT4WqjMs70-6a7_8gZmLZb2az1cZynkFRj2BaCkVT3A2RrjeEwZEtGXlMqKJ1_I2ulrOVsYx01_yD35-rw get nodes

Webhookトークン認証

Webhook認証は、Bearerトークンを検証するためのフックです。

  • --authentication-token-webhook-config-file: リモートのWebhookサービスへのアクセス方法を記述した設定ファイルです
  • --authentication-token-webhook-cache-ttl: 認証をキャッシュする時間を決定します。デフォルトは2分です

設定ファイルは、kubeconfigのファイル形式を使用します。 ファイル内で、clustersはリモートサービスを、usersはAPIサーバーのWebhookを指します。例えば、以下のようになります。

# Kubernetes APIのバージョン
apiVersion: v1
# APIオブジェクトの種類
kind: Config
# clustersは、リモートサービスを指します。
clusters:
  - name: name-of-remote-authn-service
    cluster:
      certificate-authority: /path/to/ca.pem         # リモートサービスを検証するためのCA
      server: https://authn.example.com/authenticate # クエリするリモートサービスのURL。'https'を使用する必要があります。

# usersは、APIサーバーのWebhook設定を指します。
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem # Webhookプラグインを使うための証明書
      client-key: /path/to/key.pem          # 証明書に合致する鍵

# kubeconfigファイルにはコンテキストが必要です。APIサーバー用のものを用意してください。
current-context: webhook
contexts:
- context:
    cluster: name-of-remote-authn-service
    user: name-of-api-sever
  name: webhook

クライアントが上記のようにBearerトークンを使用してAPIサーバーとの認証を試みた場合、認証Webhookはトークンを含むJSONでシリアライズされたauthentication.k8s.io/v1beta1 TokenReviewオブジェクトをリモートサービスにPOSTします。Kubernetesはそのようなヘッダーが不足しているリクエストを作成しようとはしません。

Webhook APIオブジェクトは、他のKubernetes APIオブジェクトと同じように、Versioning Compatibility Ruleに従うことに注意してください。実装者は、ベータオブジェクトで保証される互換性が緩いことに注意し、正しいデシリアライゼーションが使用されるようにリクエストの"apiVersion"フィールドを確認する必要があります。さらにAPIサーバーは、API拡張グループauthentication.k8s.io/v1beta1を有効にしなければなりません(--runtime config=authentication.k8s.io/v1beta1=true)。

POSTボディは、以下の形式になります。

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "spec": {
    "token": "(Bearerトークン)"
  }
}

リモートサービスはログインの成功を示すために、リクエストのstatusフィールドを埋めることが期待されます。レスポンスボディのspecフィールドは無視され、省略することができます。Bearerトークンの検証に成功すると、以下のようにBearerトークンが返されます。

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": true,
    "user": {
      "username": "janedoe@example.com",
      "uid": "42",
      "groups": [
        "developers",
        "qa"
      ],
      "extra": {
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    }
  }
}

リクエストに失敗した場合は、以下のように返されます。

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": false
  }
}

HTTPステータスコードは、追加のエラーコンテキストを提供するために使うことができます。

認証プロキシー

APIサーバーは、X-Remote-Userのようにリクエストヘッダの値からユーザーを識別するように設定することができます。 これは、リクエストヘッダの値を設定する認証プロキシーと組み合わせて使用するために設計です。

  • --requestheader-username-headers: 必須であり、大文字小文字を区別しません。ユーザーのIDをチェックするためのヘッダー名を順番に指定します。値を含む最初のヘッダーが、ユーザー名として使われます。
  • --requestheader-group-headers: バージョン1.6以降で任意であり、大文字小文字を区別しません。"X-Remote-Group"を推奨します。ユーザーのグループをチェックするためのヘッダー名を順番に指定します。指定されたヘッダーの全ての値が、グループ名として使われます。
  • --requestheader-extra-headers-prefix バージョン1.6以降で任意であり、大文字小文字を区別しません。"X-Remote-Extra-"を推奨します。ユーザーに関する追加情報を判断するために検索するヘッダーのプレフィックスです。通常、設定された認可プラグインによって使用されます。指定されたプレフィックスのいずれかで始まるヘッダーは、プレフィックスが削除されます。ヘッダー名の残りの部分は小文字化されパーセントデコーディングされて追加のキーとなり、ヘッダーの値が追加の値となります。

例えば、このような設定を行います。

--requestheader-username-headers=X-Remote-User
--requestheader-group-headers=X-Remote-Group
--requestheader-extra-headers-prefix=X-Remote-Extra-

以下のようなリクエストを考えます。

GET / HTTP/1.1
X-Remote-User: fido
X-Remote-Group: dogs
X-Remote-Group: dachshunds
X-Remote-Extra-Acme.com%2Fproject: some-project
X-Remote-Extra-Scopes: openid
X-Remote-Extra-Scopes: profile

このリクエストは、このユーザー情報を取得します。

name: fido
groups:
- dogs
- dachshunds
extra:
  acme.com/project:
  - some-project
  scopes:
  - openid
  - profile

ヘッダーのスプーフィングを防ぐため、認証プロキシーはリクエストヘッダーがチェックされる前に、指定された認証局に対する検証のために有効なクライアント証明書をAPIサーバーへ提示する必要があります。

  • --requestheader-client-ca-file: 必須です。PEMエンコードされた証明書バンドルです。有効なクライアント証明書を提示し、リクエストヘッダーでユーザー名がチェックされる前に、指定されたファイル内の認証局に対して検証する必要があります。
  • --requestheader-allowed-names: 任意です。Common Name(CN)の値のリストです。設定されている場合、リクエストヘッダーでユーザー名がチェックされる前に、指定されたリストのCNを持つ有効なクライアント証明書を提示する必要があります。空の場合は、任意のCNが許可されます。

匿名リクエスト

この機能を有効にすると、他の設定された認証方法で拒否されなかったリクエストは匿名リクエストとして扱われ、 system:anonymousというユーザー名とsystem:unauthenticatedというグループが与えられます。

例えば、トークン認証が設定されており、匿名アクセスが有効になっているサーバー上で、無効なBearerトークンを提供するリクエストは401 Unauthorizedエラーを受け取ります。Bearerトークンを提供しないリクエストは匿名リクエストとして扱われます。

バージョン1.5.1から1.5.xでは、匿名アクセスはデフォルトでは無効になっており、APIサーバーに --anonymous-auth=trueオプションを渡すことで有効にすることができます。

バージョン1.6以降では、AlwaysAllow以外の認証モードが使用されている場合、匿名アクセスがデフォルトで有効であり、--anonymous-auth=falseオプションをAPIサーバーに渡すことで無効にできます。 1.6以降、ABACおよびRBAC認可機能は、system:anonymousユーザーまたはsystem:unauthenticatedグループの明示的な認証を必要とするようになったため、*ユーザーまたは*グループへのアクセスを許可する従来のポリシールールには匿名ユーザーは含まれません。

ユーザーの偽装

ユーザーは偽装ヘッダーを使って別のユーザーとして振る舞うことができます。これにより、リクエストが認証したユーザー情報を手動で上書きすることが可能です。例えば、管理者はこの機能を使って一時的に別のユーザーに偽装、リクエストが拒否されたかどうかを確認することで認可ポリシーをデバッグすることができます。

偽装リクエストは最初にリクエスト中のユーザーとして認証を行い、次に偽装ユーザー情報に切り替えます。

  • ユーザーは、認証情報と偽装ヘッダーを使ってAPIコールを行います。
  • APIサーバーはユーザーを認証します。
  • APIサーバーは、認証されたユーザーが偽装した権限を持っていることを確認します。
  • リクエストされたユーザー情報は、偽装した値に置き換えられます。
  • リクエストが評価され、認可は偽装されたユーザー情報に基づいて実行されます。

偽装リクエストを実行する際には、以下のHTTPヘッダを使用することができます。

  • Impersonate-User: ユーザー名を指定します。このユーザーとして振る舞います。
  • Impersonate-Group: グループ名を指定します。このグループとして振る舞います。複数回指定して複数のグループを設定することができます。任意であり、"Impersonate-User"が必要です。
  • Impersonate-Extra-( extra name ): 追加フィールドをユーザーに関連付けるために使用される動的なヘッダーです。任意であり、"Impersonate-User"が必要です。一貫して保存されるためには、( extra name )は小文字である必要があり、HTTPヘッダーラベルで使用可能な文字以外の文字は、UTF-8であり、パーセントエンコーディングされている必要があります.

以下が、ヘッダーの例です。

Impersonate-User: jane.doe@example.com
Impersonate-Group: developers
Impersonate-Group: admins
Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com
Impersonate-Extra-acme.com%2Fproject: some-project
Impersonate-Extra-scopes: view
Impersonate-Extra-scopes: development

kubectlを使う場合は、--asフラグにImpersonate-Userヘッダーを、--as-groupフラグにImpersonate-Groupヘッダーを設定します。

kubectl drain mynode
Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode)

--asフラグと--as-groupフラグを設定します。

kubectl drain mynode --as=superman --as-group=system:masters
node/mynode cordoned
node/mynode drained

ユーザー、グループ、または追加フィールドを偽装するために、偽装ユーザーは偽装される属性の種類("user"、"group"など)に対して、"偽装した"操作を行う能力を持っている必要があります。RBAC認可プラグインが有効なクラスターの場合、以下のClusterRoleは、ユーザーとグループの偽装ヘッダーを設定するために必要なルールを網羅しています。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: impersonator
rules:
- apiGroups: [""]
  resources: ["users", "groups", "serviceaccounts"]
  verbs: ["impersonate"]

追加フィールドは、"userextras"リソースのサブリソースとして評価されます。ユーザーが追加フィールド"scopes"に偽装ヘッダーを使用できるようにするには、ユーザーに以下のようなロールを付与する必要があります。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: scopes-impersonator
rules:
# "Impersonate-Extra-scopes"ヘッダーを設定できます。
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes"]
  verbs: ["impersonate"]

偽装ヘッダーの値は、リソースが取り得るresourceNamesの集合を制限することで、管理することもできます。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: limited-impersonator
rules:
# "jane.doe@example.com"というユーザーを偽装できます。
- apiGroups: [""]
  resources: ["users"]
  verbs: ["impersonate"]
  resourceNames: ["jane.doe@example.com"]

# "developers"と"admins"というグループを偽装できます。
- apiGroups: [""]
  resources: ["groups"]
  verbs: ["impersonate"]
  resourceNames: ["developers","admins"]

# "view"と"development"を値に持つ"scopes"という追加フィールドを偽装できます。
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes"]
  verbs: ["impersonate"]
  resourceNames: ["view", "development"]

client-goクレデンシャルプラグイン

FEATURE STATE: Kubernetes v1.11 [beta]

k8s.io/client-goと、それを使用するkubectlkubeletのようなツールは、外部コマンドを実行してユーザーの認証情報を受け取ることができます。

この機能はk8s.io/client-goがネイティブにサポートしていない認証プロトコル(LDAP、Kerberos、OAuth2、SAMLなど)とクライアントサイドで統合するためのものです。プラグインはプロトコル固有のロジックを実装し、使用する不透明なクレデンシャルを返します。ほとんどすべてのクレデンシャルプラグインのユースケースでは、クライアントプラグインが生成するクレデンシャルフォーマットを解釈するために、Webhookトークン認証をサポートするサーバーサイドコンポーネントが必要です。

使用例

ある組織は、LDAPクレデンシャルをユーザー固有の署名済みトークンと交換する外部サービスを実行すると仮定します。このサービスは、トークンを検証するためにWebhookトークン認証リクエストに応答することもできます。ユーザーはワークステーションにクレデンシャルプラグインをインストールする必要があります。

以下のようにして、APIに対して認証を行います。

  • ユーザーはkubectlコマンドを発行します。
  • クレデンシャルプラグインは、LDAPクレデンシャルの入力をユーザーに要求し、クレデンシャルを外部サービスとトークンと交換します。
  • クレデンシャルプラグインはトークンをclient-goに返します。これはAPIサーバーに対するBearerトークンとして使用されます。
  • APIサーバーは、Webhookトークン認証を使用して、TokenReviewを外部サービスに送信します。
  • 外部サービスはトークンの署名を検証し、ユーザーのユーザー名とグループを返します。

設定

クレデンシャルプラグインの設定は、userフィールドの一部としてkubectlの設定ファイルで行います。

apiVersion: v1
kind: Config
users:
- name: my-user
  user:
    exec:
      # 実行するコマンドです。必須です。
      command: "example-client-go-exec-plugin"

      # ExecCredentialsリソースをデコードする際に使用するAPIのバージョン。必須です。
      #
      # プラグインが返すAPIのバージョンは、ここに記載されているバージョンと一致しなければなりません
      #
      # 複数のバージョンをサポートするツール(client.authentication.k8s.io/v1alpha1など)と統合するには、
      # 環境変数を設定するか、execプラグインが期待するバージョンを示す引数をツールに渡します。
      apiVersion: "client.authentication.k8s.io/v1beta1"

      # プラグインを実行する際に設定する環境変数です。任意です。
      env:
      - name: "FOO"
        value: "bar"

      # プラグインを実行する際に渡す引数です。任意です。
      args:
      - "arg1"
      - "arg2"
clusters:
- name: my-cluster
  cluster:
    server: "https://172.17.4.100:6443"
    certificate-authority: "/etc/kubernetes/ca.pem"
contexts:
- name: my-cluster
  context:
    cluster: my-cluster
    user: my-user
current-context: my-cluster

相対的なコマンドパスは、設定ファイルのディレクトリーからの相対的なものとして解釈されます。KUBECONFIGが/home/jane/kubeconfigに設定されていて、execコマンドが./bin/example-client-go-exec-pluginの場合、バイナリー/home/jane/bin/example-client-go-exec-pluginが実行されます。

- name: my-user
  user:
    exec:
      # kubeconfigのディレクトリーへの相対パス
      command: "./bin/example-client-go-exec-plugin"
      apiVersion: "client.authentication.k8s.io/v1beta1"

入出力フォーマット

実行されたコマンドはExecCredentialオブジェクトをstdoutに出力します。k8s.io/client-gostatusで返された認証情報を用いて、Kubernetes APIに対して認証を行ういます。

対話的なセッションから実行する場合、stdinはプラグインに直接公開されます。プラグインはTTYチェックを使って、対話的にユーザーにプロンプトを出すことが適切かどうかを判断する必要があります。

Bearerトークンのクレデンシャルを使用するために、プラグインはExecCredentialのステータスにトークンを返します。

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token"
  }
}

あるいは、PEMエンコードされたクライアント証明書と鍵を返して、TLSクライアント認証を使用することもできます。 プラグインが後続の呼び出しで異なる証明書と鍵を返すと、k8s.io/client-goはサーバーとの既存の接続を閉じて、新しいTLSハンドシェイクを強制します

指定された場合、clientKeyDataclientCertificateData両方が存在しなければなりません。

clientCertificateDataには、サーバーに送信するための中間証明書を含めることができます。

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  }
}

オプションで、レスポンスにはRFC3339のタイムスタンプとしてフォーマットされたクレデンシャルの有効期限を含めることができます。有効期限の有無には、以下のような影響あります。

  • 有効期限が含まれている場合、BearerトークンとTLSクレデンシャルは有効期限に達するまで、またはサーバーがHTTPステータスコード401で応答したとき、またはプロセスが終了するまでキャッシュされます。
  • 有効期限が省略された場合、BearerトークンとTLSクレデンシャルはサーバーがHTTPステータスコード401で応答したとき、またはプロセスが終了するまでキャッシュされます。
{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token",
    "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  }
}
最終更新 February 01, 2022 at 11:42 AM PST: change Japanese URL to English ID link (96d831443f)