AWS EKSのIAM Roles for Service AccountsをローカルのバニラなKubernetesで実現したくて、LocalStackとAmazon EKS Pod Identity Webhookでやってみたら意外と苦労した話。

以前の記事の「EKSの外でIAM Roles for Service Accountsっぽいことを実現するMutating Admission Webhookを作った」と実現したいことは同じだけど、そのとき作ったirsa-emuの代わりに、今回はAWS製のAmazon EKS Pod Identity Webhookを使う。

IAM Roles for Service Accounts (IRSA)とは

IAM Roles for Service Accounts (IRSA)は、IAMロールをEKSのServiceAccountに紐づける機能。

IRSAを使うには、IAMにOIDCプロバイダの設定をちょろっとして、特定の信頼ポリシーを付けたIAMロールを作っておいて、ServiceAccountのアノテーションにeks.amazonaws.com/role-arn: <IAMロールのARN>を付けるだけ。 そうしておくと、そのServiceAccountを付けたPodがそのIAMロールの権限でAWSサービスにアクセスできる。

IRSAの仕組み

詳しくはひとつ前の記事で書いたのでここでは簡単に説明すると、Amazon EKS Pod Identity WebhookがPodにServiceAccountトークンを挿入して、それを使ってPod内のAWSクライアントがAssumeRoleWithWebIdentityを実行して、eks.amazonaws.com/role-arnで指定したIAMロールの一時的な認証情報を取得して、AWSサービスにアクセスするというもの。

LocalStackとは

LocalStackはAWSサービスのモックを提供するアプリ。 有償なPro版と無償版があって、無償版でもIAM、S3、KMS、DynamoDB、Kinesisとかのモックが使えてすごい。

けどEKSは有償版でしかサポートしてないし、IAMのOIDCプロバイダとかフェデレーションはどちらの版でもサポートしてないので、ちゃんとしたIRSAをぱっと実現できるようにはなっていない。

ただ、無償版のLocalStackのIAMの実装がいい感じに雑で、AssumeRoleWithWebIdentityをどんなトークンで実行しても、任意のIAMロールの認証情報を取得できるので、雑なIRSAは実現できるはずと考えたのがこの記事の取り組みのきっかけ。

つまり、ひとつ前の記事でIRSAの処理の流れを書いた下図でいうと、#6~9をすっとばすので、Amazon EKS Pod Identity WebhookさえあればIAMのOIDCプロバイダとかフェデレーションなんかとは関係なく動くはず、ということ。

irsa.png

LocalStackに対するIRSAの実装

irsa-emuのときと同様に、手元のバニラなKubernetes 1.21.2のクラスタにLocalStackをデプロイして、そのKMSでHashiCorp Vaultawskms Sealを動かすことをめざす。

Amazon EKS Pod Identity Webhookのデプロイ

まずAmazon EKS Pod Identity Webhookをデプロイするんだけど、そのWebhookサーバのTLS証明書を作るためにcert-managerが要るので、それを先に入れる。

cert-managerはGitHubに上がっているマニフェストをapplyするだけでデプロイできる。 v1.9.1を入れる。

[root@vm-1 ~]# kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml
[root@vm-1 ~]# kubectl get po -n cert-manager
NAME                                      READY   STATUS    RESTARTS   AGE
cert-manager-55649d64b4-xcmlx             1/1     Running   0          23s
cert-manager-cainjector-666db4777-vsz2q   1/1     Running   0          23s
cert-manager-webhook-6466bc8f4-x24mp      1/1     Running   0          23s


Amazon EKS Pod Identity Webhookはv0.4.0を使う。 makeでデプロイできる。

[root@vm-1 ~]# git clone https://github.com/aws/amazon-eks-pod-identity-webhook.git
[root@vm-1 ~]# cd amazon-eks-pod-identity-webhook/
[root@vm-1 amazon-eks-pod-identity-webhook]# git checkout v0.4.0
[root@vm-1 amazon-eks-pod-identity-webhook]# make cluster-up IMAGE=amazon/amazon-eks-pod-identity-webhook:v0.4.0

makeを実行すると、Waiting for CSR to be createdというメッセージが出続けて処理が終わらないけど、これはSIGINTで止めていい。 CSRというのはWebhookサーバのTLS証明書を生成するためのもので、v0.3.0以前はその処理をKubernetesのclient-goのcertificate.Managerで実装していて、つまりKubernetesのCertificates APIを使っていた。 v0.4.0ではCertificates APIからcert-managerに移行済みだし、「Waiting for CSR to be created」のあとの処理もCSRの承認しかないので、止めてしまって問題ない。 以下のようにちゃんとデプロイできている。

[root@vm-1 amazon-eks-pod-identity-webhook]# kubectl get po
NAME                                    READY   STATUS    RESTARTS   AGE
pod-identity-webhook-665f8649bc-t86cq   1/1     Running   1          57s

LocalStackのデプロイ

LocalStackはirsa-emuのときと同様にHelmでデプロイする。

[root@vm-1 ~]# helm repo add localstack-repo https://helm.localstack.cloud
[root@vm-1 ~]# helm install localstack localstack-repo/localstack
[root@vm-1 ~]# kubectl get po
NAME                                    READY   STATUS    RESTARTS   AGE
pod-identity-webhook-665f8649bc-t86cq   1/1     Running   0          11m
localstack-6498fc9748-dbph6             1/1     Running   0          31s
[root@vm-1 ~]# kubectl get svc localstack
NAME         TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                        AGE
localstack   NodePort   10.0.170.148   <none>        4566:31566/TCP,4571:31571/TCP   57s

ちゃんと動いたっぽいので、Vaultのawskms Sealで使うKMSのキーを作っておく。

[root@vm-1 ~]# export LS_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services localstack)
[root@vm-1 ~]# export LS_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
[root@vm-1 ~]# echo ${LS_IP}:${LS_PORT}
192.168.11.201:31566
[root@vm-1 ~]# export AWS_ACCESS_KEY_ID=hoge
[root@vm-1 ~]# export AWS_SECRET_ACCESS_KEY=huga
[root@vm-1 ~]# aws --endpoint-url=http://$LS_IP:$LS_PORT kms create-key --region us-east-1 --key-spec SYMMETRIC_DEFAULT --origin AWS_KMS
{
    "KeyMetadata": {
        "AWSAccountId": "000000000000",
        "KeyId": "160aead1-b4ab-4d21-834e-518b45669775",
        "Arn": "arn:aws:kms:us-east-1:000000000000:key/160aead1-b4ab-4d21-834e-518b45669775",
        "CreationDate": "2022-09-24T09:19:48.986462+09:00",
        "Enabled": true,
        "Description": "",
        "KeyState": "Enabled",
        "Origin": "AWS_KMS",
        "KeyManager": "CUSTOMER",
        "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
        "KeySpec": "SYMMETRIC_DEFAULT",
        "EncryptionAlgorithms": [
            "SYMMETRIC_DEFAULT"
        ],
        "SigningAlgorithms": [
            "RSASSA_PKCS1_V1_5_SHA_256",
            "RSASSA_PKCS1_V1_5_SHA_384",
            "RSASSA_PKCS1_V1_5_SHA_512",
            "RSASSA_PSS_SHA_256",
            "RSASSA_PSS_SHA_384",
            "RSASSA_PSS_SHA_512"
        ]
    }
}

Vaultのデプロイ

Vaultもirsa-emuのときと同様にデプロイする。 肝はawskmsのendpointをLocalStackのNodePortに向けること。

[root@vm-1 ~]# cat <<'EOF' > /tmp/vault.overrides.yaml
injector:
  enabled: false

server:
  authDelegator:
    enabled: false
  service:
    type: NodePort
    nodePort: 30000
  dataStorage:
    enabled: false
  auditStorage:
    enabled: false
  standalone:
    config: |
      ui = true
      listener "tcp" {
        tls_disable = 1
        address = "[::]:8200"
        cluster_address = "[::]:8201"
      }
      storage "file" {
        path = "/home/vault"
      }
      seal "awskms" {
        region     = "us-east-1"
        kms_key_id = "160aead1-b4ab-4d21-834e-518b45669775"
        endpoint   = "http://192.168.11.201:31566"
      }
  serviceAccount:
    create: true
    annotations:
      eks.amazonaws.com/role-arn: arn:aws:iam::000000000000:role/Hoge
EOF
[root@vm-1 ~]# helm install vault -n kube-system -f /tmp/vault.overrides.yaml hashicorp/vault

これだけでawskmsが動くと思いきや、Vaultに以下のエラーが出た。

2022-09-19T09:25:20.789Z [WARN]  seal.awskms: error assuming role: roleARN=arn:aws:iam::000000000000:role/Hoge tokenPath=/var/run/secrets/eks.amazonaws.com/serviceaccount/token sessionName=""
  err=
  | WebIdentityErr: failed to retrieve credentials
  | caused by: InvalidIdentityToken: The ID Token provided is not a valid JWT. (You may see this error if you sent an Access Token)
  |     status code: 400, request id: f6ae9d75-da22-4e7f-bacc-09c6d20ddbff

AssumeRoleWithWebIdentityしたときにトークンが不正というエラーが返ってきたというメッセージだけど、上に書いたようにLocalStackはトークンのバリデーションなんてしないのでおかしい。 調べたら、awskmsのendpointで指定できるのはKMSのAPIのエンドポイントだけなのに対して、AssumeRoleWithWebIdentityはSTSのAPIなので、AWSに対して実行してしまっていた。あたりまえといえばあたりまえかも。

awskmsの設定には、sts_endpointみたいなのがありそうで無い。 AWS SDKの環境変数で設定できそうだと思って見てみたら、正に現在AWS_STS_ENDPOINT_URLという環境変数で設定できるようにするPRがレビュー中だった。 のでそれがマージされて、リリースされて、Vaultに取り込まれるまでの別の解を探さないといけない。

VaultのSTSエンドポイントの変更方法

Vaultの視点ではKMSのAPIを実行しようとするだけ。その先でAWS SDKがAWS_WEB_IDENTITY_TOKEN_FILEを見てSTSのエンドポイントであるhttps://sts.amazonaws.comにAssumeRoleWithWebIdentityしに行くわけだけど、そのAWS SDKがSTSエンドポイントを指定するインターフェースを備えていない(っぽい)ので、sts.amazonaws.comにつないじゃうのはどうにもならない。

なら、sts.amazonaws.comがLocalStackのIPアドレス(i.e k8sノードのIPアドレスか、LocalStackのPodのIPアドレスか、LocalStackのServiceのClusterIP)に解決されるようにすればいい。 VaultのPod内のAWS SDKがDNSクエリを投げる先はKubernetesクラスタのDNSで、つまりPodで動いているCoreDNSだし、VaultのPodのDNS設定も手の内なので、ドメイン名解決はなんとでもできる。

考えられる策は以下。

  1. VaultのPodのhostAliasesをいじって、VaultのPodのhostsファイルに<k8sノードのIPアドレス> sts.amazonaws.comを追加する。
  2. VaultのPodのdnsConfigをいじって、ネームサーバを変えて、そのネームサーバの設定でsts.amazonaws.comをいいように解決する。
  3. sts.amazonaws.comlocalstack.default.svc.cluster.local(LocalStackのServiceのFQDN)にマッピングするExternalNameを作る。
  4. LocalStackをデプロイするNamespaceをcomに変えたうえで、LocalStackのPodのhostnameとsubdomainをいじることで、LocalStackのFQDNをsts.amazonaws.com.svc.cluster.localにする。
  5. CoreDNSのhostsプラグインで、sts.amazonaws.comをk8sノードのIPアドレスに解決するAレコードを追加する。
  6. CoreDNSのfileプラグインで、sts.amazonaws.comをk8sノードのIPアドレスに解決するAレコードを追加する。
  7. CoreDNSのfileプラグインで、sts.amazonaws.comlocalstack.default.svc.cluster.localの別名として登録するCNAMEレコードを追加する。

これらの内、sts.amazonaws.comをk8sノードのIPアドレスに解決する#1、#5、#6は、EKS環境とかだとk8sノードのIPアドレスがノード再起動とかで変わるのでいまいち。

#2はネームサーバを用意して設定するのが面倒。

#3は実は、ServiceとかNamespaceの名前にドットを入れられないので無理。

#4はトリッキーすぎ。変なNamespaceを作ったり、Podのhostnameを変なのにするのがなんとなく微妙。 (諸事情でCoreDNSの設定がいじれない場合は、この#4が有力になりそうではあるが。)

ということで#7にする。

CoreDNSの設定

まず、fileプラグインに渡すdbファイルを、以下のようなConfigMapで登録しておく。

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns-db-file
  namespace: kube-system
data:
  db.amazonaws.com: |
    $ORIGIN amazonaws.com.
    @	3600 IN	SOA kube-dns.kube-system.svc.cluster.local. hoge.foo.bar. 20220920 7200 3600 1209600 3600

    sts	IN CNAME localstack.default.svc.cluster.local.

このConfigMapのdb.amazonaws.comを、CoreDNSのDeploymentをいじってそのPodの/etc/coredns/db.amazonaws.comにマウントするようにしておいて、Corefileに以下のようなブロックを追加する。

sts.amazonaws.com {
    log
    errors
    file /etc/coredns/db.amazonaws.com {
      upstream
    }
}

これでsts.amazonaws.comゾーンに目的のCNAMEレコードが追加されて、sts.amazonaws.comから読み替えられたlocalstack.default.svc.cluster.localもLocalStackのServiceのClusterIPにちゃんと解決される。

因みにCoreDNSの設定でひとつ注意すべきなのが、LocalStackが起動時に本物のS3(s3-eu-west-2.amazonaws.com)からランタイムをダウンロードするので、amazonaws.comを全部LocalStackに向けるようにしてはだめということ。

LocalStackのポート設定

AWS SDKはhttps://sts.amazonaws.comにアクセスするので、つまり443ポートにつなぎに行くんだけど、LocalStackはデフォルトでHTTPもHTTPSも4566ポートで待つので、変えておかないといけない。

LocalStackのServiceのportを443にすればそれでいいはずだけど、LocalStackのChartにそのための変数がないので、起動ポートを443に変える。

[root@vm-1 ~]# cat <<'EOF' > /tmp/localstack.overrides.yaml
service:
  edgeService:
    targetPort: 443

extraEnvVars:
  - name: EDGE_PORT
    value: "443"
EOF
[root@vm-1 ~]# helm upgrade -f /tmp/localstack.overrides.yaml localstack localstack-repo/localstack

これでawskmsが動くと思いきや、Vaultに以下のエラーが出た。

2022-09-21T02:28:37.403Z [WARN]  seal.awskms: error assuming role: roleARN=arn:aws:iam::000000000000:role/Hoge tokenPath=/var/run/secrets/eks.amazonaws.com/serviceaccount/token sessionName=""
  err=
  | WebIdentityErr: failed to retrieve credentials
  | caused by: RequestError: send request failed
  | caused by: Post "https://sts.amazonaws.com/": x509: certificate is valid for *.amplifyapp.localhost.localstack.cloud, *.cloudfront.localhost.localstack.cloud, *.execute-api.localhost.localstack.cloud, *.localhost.localstack.cloud, *.opensearch.localhost.localstack.cloud, *.s3.localhost.localstack.cloud, *.scm.localhost.localstack.cloud, localhost.localstack.cloud, not sts.amazonaws.com

LocalStackのTLSサーバ証明書のSANがまずい。 そりゃそうか。

LocalStackのTLSサーバ証明書設定

LocalStackには、現v1.1.0の時点でTLSサーバ証明書を指定する設定がない。 デフォルトではどこかからダウンロードしてくる。 環境変数SKIP_SSL_CERT_DOWNLOAD1を設定しておくと、ダウンロードする代わりに起動時に自動生成するけど、生成パラメータは全部ハードコードでSANとか指定できない。

LocalStackはWebアプリケーションフレームワークにHypercornを使っているのでその設定も見たけど、環境変数とかで外から証明書を指定することはできなそう。

/opt/code/localstack/.filesystem/var/lib/localstack/cache/server.test.pemにPEM形式の秘密鍵と証明書を改行でつなげてあらかじめおいておけば使ってくれるようなので、そこをハックすることにする。

まず、適当なキーペアと、SANにsts.amazonaws.comを入れたTLSサーバ証明書をPEMで作っておいて、以下のようなConfigMapで登録する。

apiVersion: v1
kind: ConfigMap
metadata:
  name: localstack-cert
data:
  server.test.pem: |
    -----BEGIN PRIVATE KEY-----
    MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCnwG/0HJ8twnuy
    lv27E9enAB0ZdnDd9zZLDDXrL68wfJ8bcPWaNIz12NGbDi/IbBwdezyCoppY6y/b
    jyRR8B21XzPQmCupGl930U9XO2O0pnEkXhxUQtoxsRbfEF10f4qFZOm15e2N8Q5w
    EY69Hb6dIpBwUbTfG2gGuy8wdnshVTA+qENmuqL5oziEAVSYT4UNIC/frQjjPxWF
    5KVOjiri7U88GWHyZEMzRicy1T3igli9yScOVkVLnEgNEXH/9OGUu7g8e4jsUhTq
    POwq7+q0jwK7Wxgf9gY1QUg/bxcQa9SM9wjmWNtJIK+00HK7CtaIipeEpAHkGgrA
    tqkhUnN3AgMBAAECggEAIkVpWdjjGEksVk8MpUSngSG/0CznYCGaQPjMpzDFvMUp
    CEUqmAd2aN+T6vaaAns7vzYnJZUsrU/XJib3R5ojp6M8hbgg61pM+MCVUf/7HbQH
    V2O2q+xAgXhpcMb6IMvRs/Dq4lOIFcCSr4iWvcIu6BFgquG2bwI9XWqueFssSqG0
    7MZrNs8vuLgAxHKOehT1QdSgu2Pm4/MXQ+ELWT9zB0O1FdZfWhg9TpG0hnSgBk5a
    +mDQJvmAGiuQ0DCryTB0cZN/CuPlUA+cfcXDzgEDX/t4fnUL2+UxA5fLtlgd9pE1
    t3JSacTDybERgmilVIGzDdyOIBZBdeo9NaHri1gs2QKBgQDBvUvghs5Mh6Lxhu1j
    JT8EdTnyq53B5LhpZpTRNduJWK8eyakwpE+InIrRQ+w3USpsgHspEMkxTp1gft1y
    HnnYI5QV2PUBeC2kp1auOKZGL3PNVru1iXr0A6/3UJgIsi5myFILrU159UKgWq4L
    qq5kXu+GtY4ebZRVBlScrmgI3QKBgQDdqSmabv3mIp6QXPp+42gCRTnXQRZnXhbL
    zhVayuHt1cDoPSQ+X9As5JHcujZRv+TQXgrZ4GFeLks7iTQYeNTQKEn7QH+fc0iL
    xJYsviGd7G9FVzWCut2PkZfA+Xlhab867yXcnwcupoHvZKXDX4mFRZ/OxlI4qMG5
    yLOXwN++YwKBgAo2r4xbrv6L3AQ9p8AlU4gpoe9wKrXxF+O+m7PX0gYQndLSsM+2
    qda64fqcSRS1e7YSo+uLTgACc/uFVKlSf/vsDD+Gm4wDSOy4QbKXm/RxRfqaSvUM
    FWZN13YDUrCKrDWQ8/pXnZEDZArOHQTSb7Us6BXC9oYWIs7Esas86pElAoGBANvZ
    7uO39qiN6cuyDNpR31VxJ/9yFTCNR/r4+oFE/XWSL3WpPwuGqMn+IxZUs4+9zstE
    m2qifLCXJhN08HDdV+bmLwq/XFXgWDIY2AKkyl8CpRKXfwGdKig99dB3IIcYBY4E
    qP771HnWuMWihC8V0SaYefrzBZYg+5Szlj2MFd+VAoGBAMEI3JufIFoxZxEqB4ng
    IZ1wGrk9DmDj8hL3IERWHuNJ/OucGE7aLmktSNXwLcQIO83+U7nE2zu7z95gCbWg
    8sL8Vy1zUVzIC04f77r3UpxEAkqx5OZfT9Hl7Ii3t5lWekjxWs3hUZQw5090Xo/6
    LS7d8V/WmpvXaWOKVQzmnASs
    -----END PRIVATE KEY-----
    -----BEGIN CERTIFICATE-----
    MIID2DCCAsCgAwIBAgICA+kwDQYJKoZIhvcNAQELBQAwgYExCzAJBgNVBAYTAkFV
    MRMwEQYDVQQIDApTb21lLVN0YXRlMRYwFAYDVQQHDA1Tb21lLUxvY2FsaXR5MRcw
    FQYDVQQKDA5Mb2NhbFN0YWNrIE9yZzEQMA4GA1UECwwHVGVzdGluZzEaMBgGA1UE
    AwwRc3RzLmFtYXpvbmF3cy5jb20wIBcNMjIwOTIxMDc0NjIxWhgPMjEyMjA4Mjgw
    NzQ2MjFaMIGBMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEWMBQG
    A1UEBwwNU29tZS1Mb2NhbGl0eTEXMBUGA1UECgwOTG9jYWxTdGFjayBPcmcxEDAO
    BgNVBAsMB1Rlc3RpbmcxGjAYBgNVBAMMEXN0cy5hbWF6b25hd3MuY29tMIIBIjAN
    BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp8Bv9ByfLcJ7spb9uxPXpwAdGXZw
    3fc2Sww16y+vMHyfG3D1mjSM9djRmw4vyGwcHXs8gqKaWOsv248kUfAdtV8z0Jgr
    qRpfd9FPVztjtKZxJF4cVELaMbEW3xBddH+KhWTpteXtjfEOcBGOvR2+nSKQcFG0
    3xtoBrsvMHZ7IVUwPqhDZrqi+aM4hAFUmE+FDSAv360I4z8VheSlTo4q4u1PPBlh
    8mRDM0YnMtU94oJYvcknDlZFS5xIDRFx//ThlLu4PHuI7FIU6jzsKu/qtI8Cu1sY
    H/YGNUFIP28XEGvUjPcI5ljbSSCvtNByuwrWiIqXhKQB5BoKwLapIVJzdwIDAQAB
    o1YwVDAcBgNVHREEFTATghFzdHMuYW1hem9uYXdzLmNvbTAMBgNVHRMBAf8EAjAA
    MA4GA1UdDwEB/wQEAwIF4DAWBgNVHSUBAf8EDDAKBggrBgEFBQcDATANBgkqhkiG
    9w0BAQsFAAOCAQEAPL+Op5byzTH82Walz2z+9+Qx0GiHEINkXUbboqprnO1lawxb
    BdvrtMIjr11R5x5WmnSJ/+8B4fUcn/VmGjLZOtecyk7lRk/buX+usScA3lIySsKN
    Of3yQjSuiDdLeB3cSHm4ah5SF6DKv77CjsQp0es6sikJweUlLNaYN0cYoBl/KeAw
    DULWZfWusp1GsMke3nNZC7/9KwFyNeEjIuCNpGFED/Cb28Ym0yKr2hEY+6Rroer+
    f91XlXIUhXnbcmCtc0cEtPJUo77ZdTEMPUdDcy1v5aKZ6VpKaOITJjLI98qoMMGu
    BaCodVAdOzrPn4yy3IXhWFW5S+KHltoMc2qHdg==
    -----END CERTIFICATE-----

因みにこれは100年有効なオレオレ証明書。

このConfigMapのserver.test.pemを、LocalStackのDeploymentをいじってそのPodの/opt/code/localstack/.filesystem/var/lib/localstack/cache/server.test.pemにマウントするようにしておいて、SKIP_SSL_CERT_DOWNLOADを設定する。

[root@vm-1 ~]# cat <<'EOF' > /tmp/localstack.overrides.yaml
service:
  edgeService:
    targetPort: 443

extraEnvVars:
  - name: EDGE_PORT
    value: "443"
  - name: SKIP_SSL_CERT_DOWNLOAD
    value: "1"
EOF
[root@vm-1 ~]# helm upgrade -f /tmp/localstack.overrides.yaml localstack localstack-repo/localstack

これでawskmsが動くと思いきや、Vaultに以下のエラーが出た。

2022-09-21T23:36:56.414Z [WARN]  seal.awskms: error assuming role: roleARN=arn:aws:iam::000000000000:role/Hoge tokenPath=/var/run/secrets/eks.amazonaws.com/serviceaccount/token sessionName=""
  err=
  | WebIdentityErr: failed to retrieve credentials
  | caused by: RequestError: send request failed
  | caused by: Post "https://sts.amazonaws.com/": x509: certificate signed by unknown authority

AWS SDKはオレオレ証明書を許してくれない。

VaultのCA証明書バンドル設定

AWS SDKはAWS_CA_BUNDLEという環境変数でCA証明書バンドルを指定できるので、VaultのPodにこれを設定してオレオレ証明書を信頼させる。

まず、さっき作ったserver.test.pemから証明書だけ切り出して以下のようなConfigMapを作る。

apiVersion: v1
kind: ConfigMap
metadata:
  name: localstack-ca-cert
  namespace: kube-system
data:
  server.test.pem.crt: |
    -----BEGIN CERTIFICATE-----
    MIID2DCCAsCgAwIBAgICA+kwDQYJKoZIhvcNAQELBQAwgYExCzAJBgNVBAYTAkFV
    MRMwEQYDVQQIDApTb21lLVN0YXRlMRYwFAYDVQQHDA1Tb21lLUxvY2FsaXR5MRcw
    FQYDVQQKDA5Mb2NhbFN0YWNrIE9yZzEQMA4GA1UECwwHVGVzdGluZzEaMBgGA1UE
    AwwRc3RzLmFtYXpvbmF3cy5jb20wIBcNMjIwOTIxMDc0NjIxWhgPMjEyMjA4Mjgw
    NzQ2MjFaMIGBMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEWMBQG
    A1UEBwwNU29tZS1Mb2NhbGl0eTEXMBUGA1UECgwOTG9jYWxTdGFjayBPcmcxEDAO
    BgNVBAsMB1Rlc3RpbmcxGjAYBgNVBAMMEXN0cy5hbWF6b25hd3MuY29tMIIBIjAN
    BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp8Bv9ByfLcJ7spb9uxPXpwAdGXZw
    3fc2Sww16y+vMHyfG3D1mjSM9djRmw4vyGwcHXs8gqKaWOsv248kUfAdtV8z0Jgr
    qRpfd9FPVztjtKZxJF4cVELaMbEW3xBddH+KhWTpteXtjfEOcBGOvR2+nSKQcFG0
    3xtoBrsvMHZ7IVUwPqhDZrqi+aM4hAFUmE+FDSAv360I4z8VheSlTo4q4u1PPBlh
    8mRDM0YnMtU94oJYvcknDlZFS5xIDRFx//ThlLu4PHuI7FIU6jzsKu/qtI8Cu1sY
    H/YGNUFIP28XEGvUjPcI5ljbSSCvtNByuwrWiIqXhKQB5BoKwLapIVJzdwIDAQAB
    o1YwVDAcBgNVHREEFTATghFzdHMuYW1hem9uYXdzLmNvbTAMBgNVHRMBAf8EAjAA
    MA4GA1UdDwEB/wQEAwIF4DAWBgNVHSUBAf8EDDAKBggrBgEFBQcDATANBgkqhkiG
    9w0BAQsFAAOCAQEAPL+Op5byzTH82Walz2z+9+Qx0GiHEINkXUbboqprnO1lawxb
    BdvrtMIjr11R5x5WmnSJ/+8B4fUcn/VmGjLZOtecyk7lRk/buX+usScA3lIySsKN
    Of3yQjSuiDdLeB3cSHm4ah5SF6DKv77CjsQp0es6sikJweUlLNaYN0cYoBl/KeAw
    DULWZfWusp1GsMke3nNZC7/9KwFyNeEjIuCNpGFED/Cb28Ym0yKr2hEY+6Rroer+
    f91XlXIUhXnbcmCtc0cEtPJUo77ZdTEMPUdDcy1v5aKZ6VpKaOITJjLI98qoMMGu
    BaCodVAdOzrPn4yy3IXhWFW5S+KHltoMc2qHdg==
    -----END CERTIFICATE-----

で、それをVaultのPodにマウントして、そのパスをAWS_CA_BUNDLEで指すように、以下のようにvault.overrides.yamlに追記して適用する。

[root@vm-1 ~]# cat <<'EOF' >> /tmp/vault.overrides.yaml
  extraEnvironmentVars:
    AWS_CA_BUNDLE: /localstack/server.test.pem.crt
  volumes:
    - name: localstack-ca-cert
      configMap:
        name: localstack-ca-cert
        items:
          - key: server.test.pem.crt
            path: server.test.pem.crt
  volumeMounts:
    - name: localstack-ca-cert
      mountPath: /localstack
      readOnly: true
EOF
[root@vm-1 ~]# helm upgrade vault -n kube-system -f /tmp/vault.overrides.yaml hashicorp/vault


これでやっとVaultのawskmsが動いた。

最後のCA証明書バンドル設定は、PodのMutating Admission Webhookでやってやると、Vault側からLocalStackを気にしなくてよくなってよさそう。