こんにちは、Necoプロジェクトの池添(@zoetro)です。
今回はTeleportというツールを利用して、Kubernetesクラスタへのユーザーアクセスを管理する方法を紹介します。
TL;DR
TeleportとKubernetesを連携させることで、以下のような仕組みを実現することができます。
- ユーザーが踏み台サーバーを経由してKubernetesクラスタにアクセスできる
- Kubernetesリソースへのアクセス権を統合的に管理することができる
- kubectl execの内容はセッションレコードとして保存されリプレイ再生することも可能
- kubectlの証明書の有効期限を短くすることでリスクを低減
Teleportとは
Teleport は、簡単に言ってしまうと従来のSSHの踏み台サーバー(Bastion) をクラウドネィティブ 時代に合わせて進化させたものです。TeleportはGravitational社が開発しています。 Teleportによって次のようなことができます。なお、無償版では一部の機能が利用できません。
- 踏み台サーバー経由でのSSHやSSH-over-HTTPSによるLinuxサーバ群へのアクセス
- 踏み台サーバー経由でのKubernetes APIによるKubernetesクラスタへのアクセス
- 様々なSSOとの連携(無償版はGitHub OAuthのみ)
- SSHのAudit Logの保存
- SSHやkubectlのセッションレコードの保存
- RBACによるSSHユーザーの統合的なアクセス権限管理(有償版のみ)
- RBACによるKubernetesユーザーの統合的なアクセス権限管理
なお、SSHの踏み台サーバーとしての解説は、以下の記事がわかりやすいのでおすすめです。
Kubernetesクラスタのアクセス権管理
Kubernetesクラスタを利用しているみなさんは、どのようにユーザーのアクセス権限を管理しているでしょうか?
小さなクラスタであれば管理者と開発者の権限を分ける必要はないかもしれません。
しかしクラスタの規模が大きくなり、複数のチームが1つのKubernetesクラスタを利用するような場合、 アクセス権限管理は重要になります。
例えば、開発者がクラスタレベルのリソースや他のチームのリソースにアクセスできないようにする必要があります。 また、NetworkPolicyやPodSecurityPolicyなどのポリシーに関しては、誤って書き換えられないように しておかないとセキュリティインシデントにも繋がってしまいます。
そこで、Kubernetesの認証認可の仕組みを利用することになります。
Kubernetesは様々な認証方式に対応しています(Authenticating | Kubernetes)。 そして認証されたユーザーやサービスアカウントに、必要なアクセス権限を割り当てることができます。(Using RBAC Authorization | Kubernetes)。
しかし、ユーザーのアクセス権に応じて適切な証明書やトークンを発行する仕組みを構築するのは面倒です。 また、証明書やトークンの有効期限を長くしておくとそれらが漏洩した時のリスクも大きくなるため、 有効期限を短くして定期的に再発行する仕組みについても考えなくてはなりません。
Teleportを利用すると、SSO のような仕組みを簡単に構築することができます。
試してみよう
Teleportを利用したKubernetesのユーザー管理の仕組みを試してみましょう。
ここでは、下記のバージョンのソフトウェアを利用します。
- kind: 0.4.0
- Kubernetes: 1.15.0
- Helm: 2.14.2
- Teleport: 4.0.2 (無償版を利用)
- cert-manager: 0.10.0
利用しているファイルは下記のリポジトリで公開しています。
Kubernetesクラスタの作成
kindを利用してKubernetesクラスタを作成します。 kindの使い方に関してはこちらの記事をごらんください。
まず下記のようなファイルを作成しcluster.yaml
という名前で保存します。
今回はTeleportのサービスにホストマシンからアクセスできるようにNodePortサービスを利用するので、
extraPortMappings
の設定をしておきます。
kind: Cluster apiVersion: kind.sigs.k8s.io/v1alpha3 nodes: - role: control-plane - role: worker extraPortMappings: - containerPort: 30023 hostPort: 3023 - containerPort: 30026 hostPort: 3026 - containerPort: 30080 hostPort: 3080
下記のコマンドを実行してクラスタを作成します。
$ kind create cluster --name teleport-demo --config cluster.yaml
また、kindで作成したクラスタにアクセスできるようにKUBECONFIG
環境変数を設定しておきます。
$ export KUBECONFIG="$(kind get kubeconfig-path --name="teleport-demo")"
Helmのセットアップ
cert-managerとTeleportをセットアップするためにHelmを利用します。 Helmの公式ドキュメントに従って、HelmのCLIをインストールしてください。
次に以下のようなファイルを作ってhelm-account.yaml
という名前で保存します。
apiVersion: v1 kind: ServiceAccount metadata: name: helm namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: helm roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: helm namespace: kube-system
下記のコマンドを実行して、HelmをKubernetesクラスタにデプロイします。
$ kubectl apply -f helm-account.yaml $ helm init --service-account helm $ helm repo update
cert-manager
次にcert-managerをKubernetesクラスタにデプロイします。
cert-managerのドキュメントに従って、下記のようにデプロイします。
$ kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.10/deploy/manifests/00-crds.yaml $ kubectl create namespace cert-manager $ kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true $ helm repo add jetstack https://charts.jetstack.io $ helm repo update $ helm install --name cert-manager --namespace cert-manager --version v0.10.0 jetstack/cert-manager
次にTeleport用の証明書を発行します。
まず下記のyamlをcertificate.yaml
という名前で保存します。
apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata: name: selfsigning-issuer spec: selfSigned: {} --- apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: teleport-tls namespace: teleport spec: secretName: tls-web commonName: teleport.example.com isCA: true issuerRef: name: selfsigning-issuer kind: ClusterIssuer
下記のコマンドを実行して証明書を発行します。
$ kubectl create namespace teleport $ kubectl apply -f certificate.yaml
なお、ここではself signed証明書を発行していますが、実環境で利用する場合はLet's Encryptなどを利用して証明書を発行するのがよいでしょう。
Teleport
さて、いよいよTeleportをデプロイします。 サンプルとして用意されているHelm Chartを利用することにします。
$ git clone https://github.com/gravitational/teleport.git $GOPATH/src/github.com/gravitational/teleport
ただしこのサンプルは有償版のTeleportを利用するようになっているので、無償版を利用するように以下のようにvalues.yaml
を書き換えます。
また、サービスをClusterIPからNodePortに変更します。
image: repository: quay.io/gravitational/teleport tag: 4.0.2 pullPolicy: IfNotPresent strategy: RollingUpdate service: type: NodePort ports: proxyweb: port: 3080 targetPort: 3080 nodePort: 30080 protocol: TCP authssh: port: 3025 targetPort: 3025 nodePort: 30025 protocol: TCP proxykube: port: 3026 targetPort: 3026 nodePort: 30026 protocol: TCP proxyssh: port: 3023 targetPort: 3023 nodePort: 30023 protocol: TCP ingress: enabled: false ports: proxyweb: containerPort: 3080 authssh: containerPort: 3025 proxykube: containerPort: 3026 proxyssh: containerPort: 3023 nodessh: containerPort: 3022 proxytunnel: containerPort: 3024 proxy: tls: enabled: true secretName: tls-web license: enabled: false config: teleport: log: output: stderr severity: DEBUG data_dir: /var/lib/teleport storage: type: dir auth_service: enabled: yes authentication: type: github public_addr: teleport.example.com:3025 cluster_name: teleport.example.com ssh_service: enabled: yes public_addr: teleport.example.com:3022 proxy_service: enabled: yes public_addr: teleport.example.com web_listen_addr: 0.0.0.0:3080 listen_addr: 0.0.0.0:3023 https_key_file: /var/lib/certs/tls.key https_cert_file: /var/lib/certs/tls.crt kubernetes: enabled: yes listen_addr: 0.0.0.0:3026 rbac: create: true serviceAccount: create: true persistence: enabled: false automountServiceAccountToken: true
下記のコマンドでTeleportをデプロイします。
$ helm install --name teleport --namespace teleport -f values.yaml $GOPATH/src/github.com/gravitational/teleport/examples/chart/teleport/
GitHub連携
Teleportは有償版は様々な認証方式が利用できますが、無償版ではパスワード認証とGitHub OAuthしか利用できません。 今回はGitHub OAuth認証でKubernetesと連携する方法を紹介します。
TeleportのKubernetes連携機能では、GitHubのTeamとKubernetesのGroupをマッピングすることができます。
まずGitHubにOrganizationとTeamを用意してください。OrganizationのOwner権限が必要となります。
次にCreating an OAuth App の手順に従って、GitHubにOAuth Appの登録をおこないます。
入力項目は下記のようにしてください。callback URLにはブラウザからアクセス可能なTeleportのアドレスを指定する必要があります。
- Application name:
Teleport
- Homepage URL:
https://teleport.example.com
- Authorization callback URL:
https://teleport.example.com:3080/v1/webapi/github/callback
登録後に表示されるClient ID
とClient Secret
を利用して、下記のようなgithub.yaml
ファイルを作成します。
organization
とteam
には、GitHubのOrganizationとTeamを指定します。
kubernetes_groupsにはKubernetesクラスタのグループ名を指定します。
system:masters
を指定するとKubernetesクラスタのすべてのリソースにアクセスできます。
kind: github version: v3 metadata: name: github spec: client_id: <client-id> client_secret: <client-secret> display: Github redirect_url: https://teleport.example.com:3080/v1/webapi/github/callback teams_to_logins: - organization: <your-organization> team: <your-team> logins: - root kubernetes_groups: ["system:masters"]
TeleportのPodに入り、github.yaml
の登録をおこないます。
$ kubectl -n teleport exec -it teleport-xxx bash $ tctl create github.yaml authentication connector "github" has been created
ログイン
ログインの前に、Teleportにアクセスするためのアドレスを、下記のように/etc/hosts
に登録しておきます。
本来はDNSで名前解決できるようにすべきですが、今回は/etc/hosts
の編集でごまかします。
127.0.0.1 teleport.example.com
次にログインをおこないます。
TeleportのDownloadページからバイナリをダウンロードし、
tsh
コマンドが利用できるようにインストールをおこなってください。
下記のコマンドを実行するとブラウザが開き、GitHubの認証画面が開きます。 ログインに利用するOrganizationを選択してください。
なお今回はself signedな証明書なので--insecure
オプションを付与してますが、Let's Encryptの証明書を使うならこのオプションは不要です。
$ tsh login --insecure --proxy=teleport.example.com:3080 --auth=github
statusを確認してみます。証明書の有効期限はデフォルトで12時間に設定されています。
$ tsh status > Profile URL: https://teleport.example.com:3080 Logged in as: xxxx Cluster: teleport.example.com Roles: admin* Logins: root Valid until: 2019-07-30 03:58:06 +0900 JST [valid for 11h49m0s] Extensions: permit-agent-forwarding, permit-port-forwarding, permit-pty * RBAC is only available in Teleport Enterprise https://gravitational.com/teleport/docs/enterprise
ログインに成功すると、kubeconfigにTeleport用のcontextが追加されています。
teleport.example.com
という名前のContextがあればOKです。
$ kubectl config view apiVersion: v1 clusters: - cluster: certificate-authority-data: DATA+OMITTED server: https://teleport.example.com:3026 name: teleport.example.com contexts: - context: cluster: teleport.example.com user: teleport.example.com name: teleport.example.com current-context: kubernetes-admin@teleport-demo kind: Config preferences: {} users: - name: teleport.example.com user: client-certificate-data: REDACTED client-key-data: REDACTED
現在のContextを確認し、必要があれば切り替えます。
$ kubectl config current-context teleport.example.com $ kubectl config use-context teleport.example.com
kubectlを叩いてみましょう。現在はsystem:masters
の権限なので、すべてのリソースにアクセスができるはずです。
なお今回はself signedな証明書なので--insecure-skip-tls-verify=true
オプションを付与しています。
$ kubectl --insecure-skip-tls-verify=true get pods -A
kubectl exec
を実行して、適当にコマンドを叩いてみてください。
ブラウザでhttps://teleport.example.com:3080/web
を開いてログインすると、
kubectl exec
で実行した内容を動画のようにリプレイ表示することができます。
なお本物の動画ではないので、一時停止して、実行したコマンドやその結果をテキストとしてコピーすることもできます。
権限設定
次にアクセス権の制限されたKubernetesグループを定義してみましょう。
下記のようなファイルを作成し、rbac.yaml
という名前で保存します。
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: guest-role rules: - apiGroups: - "" resources: - pods - deployments - services verbs: - get - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: guest-role-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: guest-role subjects: - kind: Group name: guest apiGroup: rbac.authorization.k8s.io
下記のコマンドで適用します。
$ kubectl apply -f rbac.yaml
先ほどのgithub.yaml
のkubernetes_groups
を書き換えます。
kind: github version: v3 metadata: name: github spec: client_id: <client-id> client_secret: <client-secret> display: Github redirect_url: https://teleport.example.com:3080/v1/webapi/github/callback teams_to_logins: - organization: <your-organization> team: <your-team> logins: - root kubernetes_groups: ["guest"]
TeleportのPodに入り、github.yaml
を再登録します。
$ kubectl -n teleport exec -it teleport-xxx bash $ tctl rm github/github github connector "github" has been deleted $ tctl create github.yaml authentication connector "github" has been created
再度tshでログインし直して、kubectlを実行してみましょう。
pod
やservice
をgetすることはできますが、それ以外のリソースはアクセスできないことが確認できます。
$ kubectl --insecure-skip-tls-verify=true get configmaps Error from server (Forbidden): configmaps is forbidden: User "xxxx" cannot list resource "configmaps" in API group "" in the namespace "default"
まとめ
今回は、Teleportを利用してKubernetesクラスタへのユーザーアクセスを管理する方法を紹介しました。 この方法を利用することで、GitHubのTeamごとにKubernetesクラスタへのアクセス権限を設定することが できるようになり、非常に簡単にユーザー管理をおこなうことができるようになりました。
本記事がKubernetesのユーザー管理の参考になれば幸いです。