Pod内のGoアプリから、ServiceAccountに割り当てた権限でk8sリソースにアクセスする

2022/08/20に公開

最初に

k8sのリソースに対してプログラムなどから操作したくなることがあります。
操作自体はGo言語であればclient-goで、ローカルから操作するときは、~/.kube/configを読み込ませて操作できます。

ただ実際に動かすなら、同じk8sクラスター内にPodでこのプログラムを動かしたくなります。
この時に、このPodにkube/configを渡すのは、セキュリティ的に避けたいですね。
AWSのIAM Roleのような安全に必要最低限の権限だけを与えて、割り当てられたEC2インスタンスだけが利用できるようにしたいです。k8sでは、これをServiceAccount, Role, Role-bindingsというリソースで実現できます。

この記事では、

  • client-goでPodに割り当てられたService Accountの権限を取得する
  • Service Accountに必要最低限の権限を付与する例

について記載します

client-goでPodに割り当てられたService Accountの権限を取得する

kubeconfigの取得方法を次のように2つから取るようにします

func getK8sConfig() (*rest.Config, error) {
	// ①InClusterConfigの取得処理
	config, err := rest.InClusterConfig()
	// k8s Cluster内でないなら、エラーが返ってくる
	// その場合は次のローカルのkubeconfig取得処理へ
	if err == nil {
		return config, nil
	}

	// ②ローカルのkubeconfigからconfigを作成する
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()

	return clientcmd.BuildConfigFromFlags("", *kubeconfig)
}

上記のようなconfig取得のutil関数を用意する
rest.InClusterConfig()は下記のようなコードになっており、よしなにService Accountを読み込んでくれる

https://github.com/kubernetes/client-go/blob/4154d814be868b4bcf610332288a913fca8f4dc1/rest/config.go#L508-L542

これで取得できない場合は、ホームディレクトリのkubeconfigを読みに行っている

そして、このutil関数をもとにclientsetを作成すると、Service Account(or kubeconfig)に割り当てた権限で操作できるclientが作成できる

config, err := getK8sConfig()
if err != nil {
	panic(err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
	panic(err)
}

Service Accountに必要最低限の権限を付与する例

前述の通りで
k8sでは、Role に対して権限を割り当て、RoleRoleBindingsServiceAccountに割り当て、PodにServiceAccountを指定することで、Podの特定の位置に認証情報がマウントされるので、それを読み込むことで、Roleで指定した権限が利用可能となる -> Role Based Access Control(RBAC)というやつ

実際に適当な例を作ってみます

sample-saというservice accountを作ります

apiVersion: v1
kind: ServiceAccount
metadata:
  name: sample-sa
  namespace: default

sample-roleというroleを作ります。deploymentリソースの情報の取得(get)とreplica数更新(/scale, update)権限を付与しています。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: sample-role
rules:
  - apiGroups: ["extensions", "apps"]
    resources: ["deployments", "deployments/scale"]
    verbs: ["get", "update"]

RoleBindingsでsample-roleをsample-saに紐づけます

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: sample-rb
  namespace: default
subjects:
  - kind: ServiceAccount
    name: sample-sa
    namespace: default
roleRef:
  kind: Role
  name: sample-role
  apiGroup: rbac.authorization.k8s.io

最後にDeploymentリソースを作成します。sepc.template.spec.serviceAccountNameでsample-saを指定します

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample
  namespace: default
spec:
  replicas: 1
  template:
    spec:
      serviceAccountName: sample-sa
      containers: {}

これで作られるk8s podで上記のようなservice accountの権限読み込むコードを動かすと、deploymentリソースに対して操作が可能となります。

Discussion