MF KESSAIでは、一部社内で、cert-managerを利用しており、利用中のGKE(Google Kubernetes Engine)でWorkload Identity化をすすめるにあたってぶつかった壁があり暫定的ではあるものの解決したため、その方法を残しておきたいと思います。
社内で記事を温めてる間に、Documentを更新するPRが生まれてマージされました。 そのため公開するか悩みましたが日本語記事として読んでもらえればと思います。
この記事の背景
cert-managerのDNS01 Challengeを使ってGKEで証明書を生成するには、現時点では以下の通り設定する必要があります 。
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: example-issuer
spec:
acme:
...
solvers:
- dns01:
clouddns:
# The ID of the GCP project
project: $PROJECT_ID
# This is the secret used to access the service account
serviceAccountSecretRef:
name: clouddns-dns01-solver-svc-acct
key: key.json
spec.acme.solvers[0].dns01.clouddns.project
にはDNSが存在するGCP ProjectIDspec.acme.solvers[0].dns01.clouddns.serviceAccountSecretRef
にはDNSを操作する権限を持ったGSA(GCP Service Account)の鍵情報を登録したk8s secretとkey名
しかし現在のGKEでは、Workload Identityという仕組みを使うことで、k8s secretを介する事なくGSAの認証情報をPodが使えるようにする仕組みが推奨となっています。
対応方法
実は同様のIssueが存在しており、解決方法が提示されていたので、そちらの説明をしていく事になります。
Step1: KSA(Kubernetes Service Account)を作成or特定する
shinofara@cloudshell:~ (hoge)$ kubectl get deployment cert-manager -n cert-manager -o yaml | grep serviceAccount
serviceAccount: cert-manager
serviceAccountName: cert-manager
すでにcertmanagerには cert-manager
というKSAが作成され設定されています。
Step2: GSAの作成or更新する
弊社ではすでにk8s secret経由で設定する従来の方法で利用していたため、すでにGSAは作成済みでしたので弊社では更新のみ行いました。 ここでは手順として残しておきます。 https://github.com/jetstack/cert-manager/issues/3009#issuecomment-656663316 に書かれている内容と同じになります。
$ export PROJECT_ID=myproject-id
$ export $GOOGLE_SERVICE_ACCOUNT_NAME=myserviceaccount-name
Create service account
$ gcloud iam service-accounts create $GOOGLE_SERVICE_ACCOUNT_NAME --display-name "$GOOGLE_SERVICE_ACCOUNT_NAME"
DNS role
$ gcloud projects add-iam-policy-binding $PROJECT_ID \
--member serviceAccount:$GOOGLE_SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com \
--role roles/dns.admin
Workload identity
$ gcloud iam service-accounts add-iam-policy-binding \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:$PROJECT_ID.svc.id.goog[cert-manager/cert-manager]" \
$GOOGLE_SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com
これでGCP IAM側での作業は完了です。
Step3: 最後のKSAにannotationsを追加する
$ kubectl patch serviceaccount cert-manager -p '{"metadata": {"annotations":{"iam.gke.io/gcp-service-account":"$GOOGLE_SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com"}}}' -n cert-manager
これで、KSAの cert-manager
を利用するcert-manager podが、Step2で作成したGSAの認証を利用してDNSを操作できる形になりました。
Step4: ClusterIssuer or Issuerの設定変更
Step3まででSecretの利用が不要になりましたので、最終的に記述内容は以下の通りです。
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: example-issuer
spec:
acme:
...
solvers:
- dns01:
clouddns:
# The ID of the GCP project
project: $PROJECT_ID
- # This is the secret used to access the service account
- serviceAccountSecretRef:
- name: clouddns-dns01-solver-svc-acct
- key: key.json
連携が失敗してると
cert-manager/controller/challenges "msg"="re-queuing item due to error processing" "error"="GoogleCloud API call failed: Get https://www.googleapis.com/dns/v1/projects/dnsproject/managedZones?alt=json\u0026dnsName=example.com.\u0026prettyPrint=false: compute: Received 403 `
Unable to generate token;
IAM returned 403 Forbidden: The caller does not have permission
This error could be caused by a missing IAM policy binding on the target IAM service account.
You can create the necessary policy binding with:
gcloud iam service-accounts add-iam-policy-binding \\
--role=roles/iam.workloadIdentityUser \\
--member=\"serviceAccount:hoge.svc.id.goog[cert-manager/cert-manager]\" \\
[email protected]\n\nFor more information, refer to the Workload Identity documentation:
https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity`" "key"="hoge-cert-927063497-0"
この様にGSAに対して、KSAをroles/iam.workloadIdentityUser
Roleでmemberに追加してくださいと怒られます。そして対応したら問題なく証明書の作成が始まります。
cert-managerの実装を見てみよう
なぜ設定に必要そうな鍵情報を削除して動くのかコードを見てみましょう。
pkg/issuer/acme/dns/clouddns/clouddns.go#L80
func NewDNSProviderCredentials(project string, dns01Nameservers []string, hostedZoneName string) (*DNSProvider, error) {
..
client, err := google.DefaultClient(ctx, dns.NdevClouddnsReadwriteScope) |
..
}
CloudDNSのProviderを作成する際に、内部で、google.DefaultClient
を呼ばれているためWorkload Identityを利用できています。
最後に
この手順では、helm
を利用してcert-managerインストール後にpatchを当てる必要がありました。cert-manager
側が対応するまで待つか、helmを使わずにkustomizeを利用するなどしてpatchまでKSAに当てておくと良いかもしれませんね。