1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vaultのデータ(kv)をExternalSecretを使ってEKSでSecretとして読み込む

Last updated at Posted at 2022-09-27

External Secretとは

External SecretとはExternal Secret Operatorによって提供される機能で、Kubernetes外にあるSecret(例:AWS Secret Manager, Vault等)を読み取ってKubernetesのSecretとしてクラスタにSecretを挿入してくれる。
提供されるカスタムリソースとしては、ExternalSecretとSecretStoreがあり、ExternalSecretは外部Secretとクラスタ内Secretの関係性を定義し、SecretStoreは外部Secretからどのように取得するかを指定する。
1654482450879.png
※画像はこちらから引用

通常EKSでExternalSecretを使う場合はSecretManagerから値を引っ張ることが多いが、今回はVaultから引っ張った際の検証メモ。

External Secret Operatorのインストール

オフィシャルサイトのGetting Startedに従い、External Secret Operatorをインストールする。

helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets \
   external-secrets/external-secrets \
    -n external-secrets \
    --create-namespace \
    --set installCRDs=true

Vaultのインストール

Vaultもインストールする。
詳細はオフィシャル手順にお任せして、ここではやった手順をメモ程度に残しておく。

helm repo add hashicorp https://helm.releases.hashicorp.com
helm show values hashicorp/vault > vault-values.yaml
helm upgrade -i -f vault-values.yaml vault -n vault --create-namespace hashicorp/vault

vault-values.yamlはIngressを使うよう書き換えている。
Vault起動後にunseal作業を行う。unseal作業を実施しないと、Podが以下のエラーを吐き続けて起動が完了しない。

2022-08-04T03:28:40.124Z [INFO]  core: seal configuration missing, not initialized
2022-08-04T03:28:45.090Z [INFO]  core: security barrier not initialized

unsealのためのkeyやTokenをvault operator initで取得する。

kubectl exec -ti vault-0 -n vault -- vault operator init

この時のInitial Root Tokenは色々使うので残しておくこと。
unseal keyを使ってunsealを行う。

kubectl exec -ti vault-0 -n vault -- vault operator unseal

3回unseal作業を行うと、以下のような出力が得られてVaultが使えるようになる。

Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    5
Threshold       3
Version         1.10.3
Storage Type    file
Cluster Name    vault-cluster-44f5b753
Cluster ID      d4b134d3-6888-37a1-dba4-3b7063a61a80
HA Enabled      false

Vaultの設定

ここではVault内にサンプルデータを投入し、またアクセスのためのポリシーを作成する。
ここの記述は基本的には以下の情報を参考にした。

ここからはHelmのVaultのIngressに対してローカルPCからvaultコマンドでアクセスする。

export VAULT_ADDR=https://vault.mydomain.info
vault login

デフォルトではKeyValueエンジンが有効化されていなかったので、有効化する。

vault secrets enable kv

KeyとValueのサンプルデータを投入する。

vault kv put kv/mysecret password=himitsu

確認する。

$ vault kv get kv/mysecret
====== Data ======
Key         Value
---         -----
password    himitsu

次にポリシーを作成する。

cat <<EOF > /tmp/vault_sample_policy.hcl
path "*" {
    capabilities = ["read", "list"]
}
EOF

ポリシーを割り当てる。ポリシーの名前はTutorialに従ってmyapp-kv-roとした。

vault policy write myapp-kv-ro /tmp/vault_sample_policy.hcl

Kubernetes内のリソースを触るServiceAccountを作成する。今回はTutorialに従ってdefaultのNamespaceに作成した。権限周りでトラシュすることがあったのでcluster-adminにしているが、もっと絞れるとは思う。

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: vault-auth
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: role-tokenreview-binding
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: vault-auth
  namespace: default
EOF

Kubernetesの認証を有効化し、認証情報を追加する。

vault auth enable kubernetes
export SA_SECRET_NAME=$(kubectl get secrets --output=json \
    | jq -r '.items[].metadata | select(.name|startswith("vault-auth-")).name')
export SA_JWT_TOKEN=$(kubectl get secret $SA_SECRET_NAME \
    --output 'go-template={{ .data.token }}' | base64 --decode)
export SA_CA_CRT=$(kubectl config view --raw --minify --flatten \
    --output 'jsonpath={.clusters[].cluster.certificate-authority-data}' | base64 --decode)
export K8S_HOST=$(kubectl config view --raw --minify --flatten \
    --output 'jsonpath={.clusters[].cluster.server}')
vault write auth/kubernetes/config \
     token_reviewer_jwt="$SA_JWT_TOKEN" \
     kubernetes_host="$K8S_HOST" \
     kubernetes_ca_cert="$SA_CA_CRT" \
     issuer="https://kubernetes.default.svc.cluster.local"

問題なければ、vault write auth/kubernetes/configの結果として以下のメッセージが得られるはずだ。

Success! Data written to: auth/kubernetes/config

次にロールを作成する。ロール名はhogeとした。

vault write auth/kubernetes/role/hoge \
     bound_service_account_names=vault-auth \
     bound_service_account_namespaces=default \
     policies=myapp-kv-ro \
     ttl=24h

なお、ここで出てくるServiceAccountの名前やNamespace、ポリシー名を変えている場合は適宜変更して実行すること。

vault write auth/kubernetes/login role=hoge jwt=$SA_JWT_TOKEN iss=https://kubernetes.default.svc.cluster.local

VaultのSecretをEKSに展開する

Vaultとの接続にはSecretStoreリソースを利用する。

cat << EOF | kubectl apply -f -
 apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: vault-backend
 spec:
   provider:
     vault:
       server: "https://vault.mydomain.info"
       path: "kv"
       version: "v1"
       namespace: "default"
       auth:
         kubernetes:
           mountPath: "kubernetes"
           role: "hoge"
           serviceAccountRef:
             name: "vault-auth"
EOF

なお、versionを"v2"にすると、ExternalSecretを作ると以下のようなエラーが出てSecretが作成されないことがある。自分も遭遇して少しハマった。

"cannot read secret data from Vault: Error making API request.\n\nNamespace: default\nURL: GET https://vault.mydomain.info/v1/kv/data/mysecret\nCode: 404.

そのため、こちらの記事を参考に、v1に変更して回避している。

※追記:
KVをGUIから作成するとv2となって、CLIから作成するとv2となる模様。GUIで作成した人はv2もしくは指定なしにする必要あり。

状態を確認する。

$ kubectl get secretstore
NAME            AGE   STATUS
vault-backend   8s    Valid

なお、設定ミス等があると、以下のようになる。

$ kubectl get secretstore.external-secrets.io/vault-backend
NAME            AGE     STATUS
vault-backend   9m50s   InvalidProviderConfig

この場合、例えばServiceAccountを設定し忘れた場合はexternal-secretsのPod内にCode: 500. Errors:\n\n* service account name not authorized
のようなエラーが出ているため、エラーを見て何が起きているか確認すること。

次にExternalSecretで実際にKubernetesのSecretリソースとして取り込む。

cat <<EOF | kubectl apply -f -
 apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: vault-example
 spec:
   secretStoreRef:
     name: vault-backend
     kind: SecretStore
   target:
     name: vault-example-secret
   data:
   - secretKey: fuga
     remoteRef:
       key: mysecret
       property: password
EOF

spec.target.nameは作成されるSecret名、spec.data.secretKeyは作成されるSecret内のKeyでspec.data.remoteRefでVault側のパス(key部分、SecretStoreで指定したpathを取り除いたもの)とKey-Valueのkey(property)を設定する。

上記リソース作成後、statusがSecretSyncedになっていればOK.

$ kubectl get externalsecret
NAME            STORE           REFRESH INTERVAL   STATUS
vault-example   vault-backend   1h                 SecretSynced

Secretが作成されて、Vaultから値が引っ張ってこれていることが分かる。

$ kubectl get secret vault-example-secret -o jsonpath={.data.fuga} | base64 -d
himitsu
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?