はじめに
みなさん、KubernetesのSecretはどのように管理されていますか?
よく聞くOSSツールといえば、Kubesec、Sealed Secrets、External Secretsなどがあります。
それらとAWSのSecrets Manager等のクラウドマネージドサービスを連携して使う方法もありますね。
今回は、データストレージにHashiCorp Consulを使用するHashiCorp VaultクラスタをEKS上に作成して、hashicorp/vault-k8sを用いたサイドカー構成でSecret管理をさせてみたいと思います。
また、VaultはOSS版であってもWeb UIが提供されているのでそちらも合わせて構築したいと思います。
やりたいこと
- UIで秘匿情報を管理できる
- アプリケーションのデプロイ時にその秘匿情報を自動で埋め込んで欲しい
- 秘匿情報はひとつのVaultサーバに登録するだけで、複数クラスタ間で共有できる
- 今回はこれは確認しません
注意事項
- EKSの構築については触れません
- 構築の際はIAMの知識が必要です
- ConsulとVaultの原理や仕様は本筋から逸れるため本記事では説明しません
- Kubernetesクラスタ上にどのように構築するかということに重きを置いています
- 本文が非常に長くなってしまいました
使用した環境
- Amazon EKS
- Clusterバージョン 1.17
- HashiCorp Vault v1.6.1
- HashiCorp Consul v1.9.1
基本方針
Vaultの公式ドキュメントVault on Kubernetes Deployment GuideにあるHelmチャートを用いる方法でデプロイを行います。
参考アーキテクチャ
Vaultの公式ドキュメントVault on Kubernetes Reference Architectureで紹介されているアーキテクチャの参考を簡単にまとめます。
- VaultのストレージとしてConsulを使用する
- つまり、Vault Server PodとConsul Server Podが必要
- また、Consul Client Podをデプロイし、Vault Server Podはそれを介してConsul Serverを検索する
- 運用簡素化のため、Consul Client PodはDaemonSetでデプロイすることが望ましい
- 可能であれば、Vault/Consul専用のクラスタを作成する
- セキュリティ担保や高可用性のため
- 難しい場合であっても(マルチテナントクラスタ上にデプロイする場合であっても)、専用のノードを作成するのが望ましい
- PersistentVolumesおよびPersistentVolumeClaimsを使用したストレージ永続化
- ノード要件
- Consul
- 2-4 CPU / 8-16 GB RAM / 50GB Disk / e.g. m5.large, m5.xlarge
- 8-16 CPU / 32-64 GB RAM / 100GB Disk / e.g. m5.2xlarge, m5.4xlarge
- Vault
- 2 CPU / 4-8 GB RAM / 25GB Disk / e.g. m5.large
- 4-8 CPU / 16-32GB RAM / 50GB Disk / e.g. m5.xlarge, m5.2xlarge
- Consul
- Consul Server PodとVault Server Podはアベイラビリティゾーン間で均質に分散すること
- 5つのConsul Server Pod (StatefulSet)
(画像引用:https://learn.hashicorp.com/tutorials/vault/kubernetes-reference-architecture?in=vault/kubernetes#infrastructure-design)
今回のアーキテクチャ
今回は複数のVault Server Pod (StatefulSet)によるHA構成、Consulを用いたデータ永続化、Web UI有効化を行います。
データ永続化については、他にも様々なストレージの選択肢があるので必要に応じて使い分けてください。
また、VaultとConsulのそれぞれのWeb管理画面へのServiceとしてInternal NLBを自動生成するように設定します。
最終的に同一クラスタ内のアプリケーションにVaultを介して秘匿情報を埋め込むことにチャレンジします。
以下はイメージ図です(色々省いてます)。
Vaultのデータ永続化用のConsulをデプロイする
Consulの公式ドキュメントを参考にしながらデプロイを行います。
Helmリポジトリのセットアップ
Helmリポジトリの追加
公式Helmリポジトリが提供されているのでそれを追加します。
追加したらチャートを検索しましょう。
今回は記事を書いている時点で最新のチャートバージョン0.28.0を使用することにします。
$ helm repo add hashicorp https://helm.releases.hashicorp.com
$ helm search repo hashicorp/consul
NAME CHART VERSION APP VERSION DESCRIPTION
hashicorp/consul 0.28.0 1.9.1 Official HashiCorp Consul Chart
Helmチャートの内容確認
リポジトリを追加したらチャートの中身を確認してみましょう。
helm fetchでチャートをダウンロードしてhelm templateで内容を標準出力に出します。
今回は標準出力をlessにパイプで渡しています。
$ helm fetch hashicorp/consul --version 0.28.0
$ helm template consul-0.28.0.tgz | less
中身を確認すると、大まかにServiceAccount系リソース、ConfigMap系リソース、Service系リソース、Pod系リソース、PodDisruptionBudgetリソースを確認できます。
- Pod系リソース
- Consul Server PodとしてのStatefulSet
- Consul Client PodとしてのDaemonSet
- テスト用のConsul Pod
- Service系リソース
- Consul Server PodへのClusterIP
- Web UI用
- Consul Server PodへのHeadless Service
- Consul Server PodおよびConsul Client Podへの53番ポートClusterIP
- Consul Server PodへのClusterIP
- ServiceAccount系リソース
- Consul Server用のServiceAccount (serverと呼ぶことにする)
- ServiceAccount server用のRoleとそれを付与するRoleBinding
- Consul Client用のServiceAccount (clientと呼ぶことにする)
- ServiceAccount client用のRoleとそれを付与するRoleBinding
- ConfigMap系リソース
- Consul Server Pod用のConfigMap
- Consul Client Pod用のConfigMap
- PodDisruptionBudget
-
app: consul
ラベルが付与されたPodに対してのPodDisruptionBudget
-
values.ymlの更新
ファイル生成
変更可能なパラメータ確認のためオリジナルのvalues.ymlを生成しておきます。
$ helm show values hashicorp/consul --version 0.28.0 > values-origin.yml
更新内容
今回は必要なディレクティブのみ抜き出してyamlを作成します。
先に生成したvalues-origin.ymlを更新してもOKです。
-
global.datacenter
: Consul datacenterとしての名前 (好きに指定) -
server.storageClass
: EBSのStorageClassを指定 (後述) -
ui.service
: Web UI用の内部NLBを生成するように指定 -
syncCatalog.enabled
: Kubernetesのリソースを同期できるようにtrueを指定
global:
datacenter: booklive-dc1
server:
storageClass: gp2
ui:
service:
type: 'LoadBalancer'
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
service.beta.kubernetes.io/aws-load-balancer-internal: "true"
syncCatalog:
enabled: true
デプロイ
EBSのPersistentVolumeClaimはDynamic Provisioningで生成する
公式Helmチャートを使用してConsulをKubernetesにデプロイするには、Dynamic Provisioningが可能なStorageClassがクラスタにデプロイされている必要があります。
2021年1月現在、EFSはDynamic Provisioningに対応していませんので、今回はEBSを利用したいと思います。
EBSのDynamic Provisioningはデフォルトでデプロイされているgp2
StorageClassを使用すれば可能です。
Consul Server Podの実際のmanifestを覗いてどのようにPersistentVolumeをコンテナにマウントするのかを念のため確認しておきます。
先ほどfetch済みのmanifestで確認します。
次のコマンドを実行後、kind: StatefulSet
のmetadata.name: RELEASE-NAME-consul-server
を探してください。
$ helm template consul-0.28.0.tgz | less
すると、そのmanifest内に次のような内容が見つかると同時に、外部PersistentVolumeClaimのマウントは見つからないことが分かります。
volumeClaimTemplates:
- metadata:
name: data-default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
つまり、Consul Server PodはDynamic Provisioningを利用して動的にPersistentVolumeClaimを作成してくれるので、別途作成する必要がないということです。
Consulのデプロイ
先に紹介したvalues.ymlを使用してデプロイします。
$ helm install -f values.yml consul hashicorp/consul --version 0.28.0
NAME: consul
LAST DEPLOYED: Thu Jan 14 11:38:22 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Consul!
Now that you have deployed Consul, you should look over the docs on using
Consul with Kubernetes available here:
https://www.consul.io/docs/platform/k8s/index.html
Your release is named consul.
To learn more about the release if you are using Helm 2, run:
$ helm status consul
$ helm get consul
To learn more about the release if you are using Helm 3, run:
$ helm status consul
$ helm get all consul
default
namespaceにconsul用のpersistentvolumeclaims
とpersistentvolumes
が生成され(少し時間がかかります)、consul-consul-...
というPodが正常にRunningしていれば問題なくデプロイできています。
Vaultをデプロイする
1. 名前空間の作成
default名前空間を使用するのは推奨されませんので、新規に作成します。
今回はvaultという名前空間を作成して、Vaultリソースは全てこの名前空間にapplyします。
apiVersion: v1
kind: Namespace
metadata:
name: vault
$ kubectl apply -f namespace.yml
2. Helmリポジトリのセットアップ
Helmリポジトリの追加
公式Helmリポジトリが提供されているのでそれを追加します。
追加したらチャートを検索しましょう。
今回は記事を書いている時点で最新のチャートバージョン0.9.0を使用することにします。
$ helm repo add hashicorp https://helm.releases.hashicorp.com
$ helm search repo hashicorp/vault
NAME CHART VERSION APP VERSION DESCRIPTION
hashicorp/vault 0.9.0 1.6.1 Official HashiCorp Vault Chart
Helmチャートの内容確認
リポジトリを追加したらチャートの中身を確認してみましょう。
helm fetch
でチャートをダウンロードしてhelm template
で内容を標準出力に出します。
今回は標準出力をlessにパイプで渡しています。
$ helm fetch hashicorp/vault --version 0.9.0
$ helm template vault-0.9.0.tgz | less
中身を確認すると、大まかにServiceAccount系リソース、Service系リソース、Pod系リソース、MutatingWebhookConfigurationリソースを確認できます。
- Pod系リソース
- Vault Server PodとしてのStatefulSet
- vault-agent-injector Pod (hashicorp/vault-k8s)としてのDeployment
- Service系リソース
- Vault Server PodへのClusterIP
- Vault Server PodへのHeadless Service
- vault-agent-injectorへのClusterIP (vault-agent-injector-svc)
- MutatingWebhookConfigurationリソース
- vault-agent-injector-svcへのMutating Admission Webhook
- Pod等のリソース作成時・更新時にvault-agent-injectorが呼ばれるようになる
- vault-agent-injectorが特定の
spec.template.metadata.annotations
に反応できるようになる
- vault-agent-injector-svcへのMutating Admission Webhook
- ServiceAccount系リソース
- Vault Server Pod用のServiceAccount (vaultと呼ぶことにする)
- ServiceAccount vaultに
system:auth-delegator
のClusterRoleを割り当てるClusterRoleBinding - MutatingWebhookConfigurationのget/list/watch/patchができるClusterRole (injector-roleと呼ぶことにする)
- vault-agent-injector用のServiceAccount (injectorと呼ぶことにする)
- ServiceAccount injectorにClusterRole injector-roleを割り当てるClusterRoleBinding
values.ymlの生成
パラメータはなるべくコード化しておきたいので、この時点でvalues.ymlを生成しておきます。
ただし、600行近くあります。
$ helm show values hashicorp/vault --version 0.9.0 > values.yml
values.ymlの更新
公式HelmチャートではありますがデフォルトではHA構成になっていなかったりするので、設定を書き換えていきます。
各ディレクティブの説明は公式ドキュメントを参照してください。
HAモードの有効化
HAモードはデフォルトで無効になっています。
HAモードを有効化するとVault Server Pod (StatefulSet)はreplicasに指定した数だけデプロイされます。
ha:
で検索したらすぐに見つかります。
server:
ha:
enabled: false
replicas: 3
server:
ha:
enabled: true
replicas: 3
Vault Server Podのリソース制限
injector (vault-k8s)とVault本体のリソース制限をそれぞれできます。
本番で稼働させるには、参考アーキテクチャで紹介されているスペックが必要になるかもしれません。
今回は特に指定しません。
injector:
resources: {}
# resources:
# requests:
# memory: 256Mi
# cpu: 250m
# limits:
# memory: 256Mi
# cpu: 250m
server:
resources: {}
# resources:
# requests:
# memory: 256Mi
# cpu: 250m
# limits:
# memory: 256Mi
# cpu: 250m
StatefulSetとしてのデータ永続化
今回はVaultのデータのみ永続化するので、server.dataStorage.enabled
がtrueになっていることを確認します。
server:
dataStorage:
enabled: true
auditStorage:
enabled: false
また、HA構成にする場合は、ストレージスタンザの設定を必要に応じて書き変えます。
デフォルトではConsulが有効になっています。
HOST_IP
の箇所だけ、ConsulServerへ名前解決できるService名に書き換えておきます。
server:
ha:
config: |
ui = true
listener "tcp" {
tls_disable = 1
address = "[::]:8200"
cluster_address = "[::]:8201"
}
storage "consul" {
path = "vault"
address = "consul-consul-server.default.svc.cluster.local:8500"
}
service_registration "kubernetes" {}
Auto Unseal
今回はこの設定は行いませんが、AWSの場合、credentialsとKMSを使用してVault ServerのAuto Unsealを実現できます。
紹介までに方法を記載しておきます。
まず、credentialsをvalues.ymlに渡すためのsecretを作成します。
このとき、次のIAMポリシー権限を持つIAMユーザーのキーを使用するようにしてください。
kms:Encrypt
kms:Decrypt
kms:DescribeKey
$ kubectl -n vault create secret generic vault-aws-key \
> --from-literal=AWS_ACCESS_KEY_ID=AAAAAAAAAAAAAAAA \
> --from-literal=AWS_SECRET_ACCESS_KEY=BBBBBBBBBBBBBBBBB
作成が終えたら、secretをvalues.ymlから読み込むように設定します。
seal "awskms"
内のkms_key_id
にはAWS KMSのKey IDを調べて記載します。
server:
extraSecretEnvironmentVars:
- envName: AWS_ACCESS_KEY_ID
secretName: vault-aws-key
secretKey: AWS_ACCESS_KEY_ID
- envName: AWS_SECRET_ACCESS_KEY
secretName: vault-aws-key
secretKey: AWS_SECRET_ACCESS_KEY
ha:
config: |
seal "awskms" {
region = "ap-northeast-1"
kms_key_id = "aaa-bbb-ccc-ddd-eee-111"
}
Web UIの有効化
デフォルトではWeb UIは無効化されているので有効化し、Vault Server Podへの疎通のためにLoadBalancer Serviceを作成するように書き換えます。
EKSを使用している場合、type: LoadBalancer
を指定すればCLBまたはNLBを自動生成できるので、それでリソースの作成を行います。
今回は内部NLBを作成します。
ui:
enabled: true
publishNotReadyAddresses: true
activeVaultPodOnly: true
serviceType: "LoadBalancer"
serviceNodePort: null
externalPort: 80
# loadBalancerSourceRanges:
# - 10.0.0.0/16
# - 1.78.23.3/32
# loadBalancerIP:
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
service.beta.kubernetes.io/aws-load-balancer-internal: "true"
3. デプロイ
設定したvalues.ymlを使用してhelm installを実行します。
$ helm install vault hashicorp/vault --namespace vault -f values.yml --version 0.9.0
NAME: vault
LAST DEPLOYED: Thu Jan 14 12:27:55 2021
NAMESPACE: vault
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing HashiCorp Vault!
Now that you have deployed Vault, you should look over the docs on using
Vault with Kubernetes available here:
https://www.vaultproject.io/docs/
Your release is named vault. To learn more about the release, try:
$ helm status vault
$ helm get manifest vault
StatefulSetのreplicasを3に設定したので、vault-{number}が3つとvault-agent-injectorという合計4つのPodが作成されていれば問題ありません。
$ kubectl -n vault get pod
NAME READY STATUS RESTARTS AGE
vault-0 0/1 Running 0 12s
vault-1 0/1 Running 0 12s
vault-2 0/1 Running 0 12s
vault-agent-injector-b55d65869-mlxtt 1/1 Running 0 12s
Web UIに疎通可能なELB (NLB)が生成されていることも確認できます。
$ kubectl -n vault get service vault-ui
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
vault-ui LoadBalancer 172.20.223.44 aaaaabbbbb-cccccddddd.elb.ap-northeast-1.amazonaws.com 8200:32040/TCP 5m10s
4. Vaultの初期設定
HA構成の場合、コマンドラインでVaultをUnsealにする必要があります。
kubectl exec
でVault Server Podのどれかひとつにinitコマンドを実行します。
このとき、Unseal KeyとInitial Tokenを取得します(以降も大切な情報ですので安全な場所で保管します)。
$ kubectl -n vault exec -it vault-0 -- vault operator init
Unseal Key 1: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Unseal Key 2: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Unseal Key 3: cccccccccccccccccccccccccccccccccccccccccccc
Unseal Key 4: dddddddddddddddddddddddddddddddddddddddddddd
Unseal Key 5: eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
Initial Root Token: s.fffffffffffffffffffffff
Success! Vault is initialized
Recovery key initialized with 5 key shares and a key threshold of 3. Please
securely distribute the key shares printed above.
AWS KMSによるAuto Unsealを有効にしていれば、Unseal Keyを入力せずにこのタイミングで使える状態になります。
ただし、今回はマニュアルでUnsealを行います。
次のコマンドをすると対話形式でUnseal Keyを聞かれるので、operator init
で取得した5つのUnseal Keyのうち任意のひとつを入力します。
$ kubectl -n vault exec -it vault-0 -- vault operator unseal
Unseal Key (will be hidden):
これを全部で3回繰り返すとVaultはUnseal状態になり使用可能になります。
3回目にSealed Keyがfalseになったことを確認できればOKです。
※ 3回入力するUnseal Keyは全て別のものを使用してください
$ kubectl -n vault exec -it vault-0 -- vault operator unseal
Unseal Key (will be hidden):
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 5
Threshold 3
Version 1.6.1
Storage Type consul
Cluster Name vault-cluster-****
Cluster ID
HA Enabled true
HA Cluster n/a
HA Mode standby
Active Node Address <none>
$ kubectl -n vault get pod
NAME READY STATUS RESTARTS AGE
vault-0 1/1 Running 0 2m38s
vault-1 1/1 Running 0 2m38s
vault-2 1/1 Running 0 2m38s
vault-agent-injector-6fcf464c66-9k8ch 1/1 Running 0 2m38s
ここまで終えるとNLBのDNS名
にブラウザからアクセスしてみましょう。
するとログイン画面が表示されるので、kubectl exec
で取得したInitial Root Token
を入力し、Sign Inを実行します。
実行後、次のような画面が表示されれば初期設定完了です。
5. ロール/認証情報の設定
ログイン認証
Vaultの長所は細かく権限管理できるところです。
これは管理画面のログイン認証においても例外ではなく、ログインユーザーが持つポリシーによって可能な作業が制御されます。
先ほど初期設定のためにRoot Tokenを使用しましたが、このTokenにはrootポリシーが付与されていて何でもできるため、安全に管理する必要があります。
そのため、必要な権限に応じてポリシーを作成していく必要があります。
デフォルトではrootポリシーとdefaultポリシーがありますが、rootポリシーは使い回すことができず、一方でdefaultポリシーはできることが少な過ぎます。
そこで、今回は強めの権限を持ったadminというポリシーを作成しておきます。
まず、Top > Policies
に遷移してCreate ACL policy
をクリックします。
Nameにadmin
を入力し、Policyに次の設定をコピーし、Create policy
を実行します。
# Configure auth methods
path "sys/auth" {
capabilities = [ "read", "list" ]
}
# Configure auth methods
path "sys/auth/*" {
capabilities = [ "create", "update", "read", "delete", "list", "sudo" ]
}
# Manage auth methods
path "auth/*" {
capabilities = [ "create", "update", "read", "delete", "list", "sudo" ]
}
# Display the Policies tab in UI
path "sys/policies" {
capabilities = [ "read", "list" ]
}
# Create and manage ACL policies from UI
path "sys/policies/acl/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
# Create and manage policies
path "sys/policies/acl" {
capabilities = [ "read", "list" ]
}
# Create and manage policies
path "sys/policies/acl/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
# List available secrets engines to retrieve accessor ID
path "sys/mounts" {
capabilities = ["read"]
}
# Create and manage entities and groups
path "identity/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
# List, create, update, and delete key/value secrets
path "kv/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# Manage secrets engine
path "sys/mounts/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
次にユーザー・パスワード認証を有効化します。
Top > Access > Auth Methods > Enable new method
を押します。
今回はUsername & Password
を設定してみます。
Nextを押すと詳細設定ができますが今回はデフォルトのままEnable Methodを押します。
Methodの作成が完了したら、次の画面に移動してCreate userを押します。
ユーザー作成画面では、今回はUsername=admin,Password=adminpassword
を入力し、隠れている詳細設定のアコーディオンを開くためにTokensをクリックします。
Generated Token's Policiesに先ほど作成したポリシーのadmin
を入力しAdd
をクリックします。
ここまで完了したらSaveをクリックしてユーザー作成完了です。
以後、adminユーザーで作業を行うため、一度ログアウトしてログインし直します。
読み取り専用アプリケーションポリシー
アプリケーションからはSecretの読み取りのみ許可したいことがほとんどだと思われます。
そのため、Read Onlyなポリシーも作成しておきます。
今回は、readonly-secret
という名前で登録しました。
path "kv/*" {
capabilities = ["read"]
}
Kubernetes認証メソッドの有効化とロールの作成
KubernetesのServiceAccountとVaultのポリシーを関連づけるための設定です。
まず、Access > Auth Methods
からEnable new methodを押します。
Infra > Kubernetes
を選択しNext
を押します。
デフォルトのままEnable Methodを押し、Kubernetes Authentication Methodを有効化します。
有効化が終わったら、Configure Kubernetesという画面に遷移するので必要情報を入力します。
それぞれのキーに対して、次のコマンドを実行して得られた値を入力します。
-
Kubernetes host
- ここで得られるIPを
$IP
とするとhttps://$IP:443
の形式で入力してください
- ここで得られるIPを
$ kubectl -n vault exec -it vault-0 -- sh -c 'echo $KUBERNETES_PORT_443_TCP_ADDR'
-
Kubernetes CA Certificate
- 入力時は
Enter as text
を有効にしてから入力してください
- 入力時は
$ kubectl -n vault exec -it vault-0 -- cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
-
Token Reviewer JWT
- このJWTはVault起動時に作成されるvaultというServiceAccountに紐づくSecretとしてデプロイされています
$ kubectl -n vault exec -it vault-0 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
入力が済んだらSaveを押して保存します。
ここまで終えたら、次の画面まで移動してCreate role
を押します。
今回は、sample-app-roleという名前で全てのServiceAccountと全てのnamespaceを許可します。
また、Generated Token's Policiesにはreadonly-secret
ポリシーを追加しておきます。
6. Secret情報の登録
今回はKey-Value形式のSecretを有効にします。
まずは、Top > Secrets > Enable new engine > Generic/KV > Next > (特に何もせずに) Enable Engine
を実行します。
すると、Key-Value形式のSecretを登録できるようになるので、Create secretからSecretを登録します。
今回は、secret/test-secret
というSecretにKey=username,Value=testuser
とKey=password,Value=password
を登録してみます。
readonly-secret
のポリシーにkv/*
のreadを許可しているのは、今作成したkv
配下の読み込み権限を与えるためです。
コンシューマアプリケーションからSecret情報を取得してみる
サンプルアプリケーション構築
サンプルアプリケーションのマニフェストは次の通りです。
Nginxコンテナの /vault/secrets/test-secret
というファイルに秘匿情報が埋め込まれます。
---
apiVersion: v1
kind: Namespace
metadata:
name: sample-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-app
namespace: sample-app
spec:
replicas: 1
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
annotations:
# Enables the Vault Agent Injector service
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-status: update
# Vault Kubernetes authentication role
vault.hashicorp.com/role: sample-app-role
# agent-inject-secret-FILEPATH prefixes the path of the file, written to the /vault/secrets directory.
# The value is the path to the secret defined in Vault.
vault.hashicorp.com/agent-inject-secret-test-secret: kv/secret/test-secret
vault.hashicorp.com/agent-inject-template-test-secret: |
{{- with secret "kv/secret/test-secret" -}}
USERNAME={{ .Data.data.username }}
PASSWORD={{ .Data.data.password }}
{{- end -}}
spec:
containers:
- name: nginx-container
image: nginx:1.19
デプロイと確認
ファイルを作成したらデプロイします。
デプロイ後にREADY2/2
とSTATUS
がRunning
になっていればOKです。
$ kubectl apply -f sample-app.yml
$ kubectl -n sample-app get pod
NAME READY STATUS RESTARTS AGE
sample-app-6f4f9b46fd-l8wgd 2/2 Running 1 60s
なぜ2/2かというとvault-agentコンテナが埋め込まれているためです。
sample-app.ymlにvault.hashicorp.com/agent-inject: "true"
アノテーションがあるので、Vaultクラスタと一緒にデプロイしておいたvault-agent-injectorによってMutating Admission Webhookが発火し、Vaultと通信できるAgentがsample-app Podのサイドカーとして埋め込まれる仕組みです。
最終的にNginxコンテナにパスワードが埋め込まれていることを次のように確認できます。
$ kubectl -n sample-app exec -it sample-app-6f4f9b46fd-l8wgd -c nginx-container -- cat /vault/secrets/test-secret
USERNAME=testuser
PASSWORD=password
ローカル開発でも同じ場所に同じ形式でファイルを置いておけば、アプリケーションに秘匿情報を埋め込まず、かつビルド時に秘匿情報を渡すこともなく、.envのような使い方で実装を進められます。
まとめ
やりたかったことのうち次の2つを確認できました。
- UIで秘匿情報を管理できる
- アプリケーションのデプロイ時にその秘匿情報を自動で埋め込んで欲しい
3つ目の複数クラスタでVaultの情報を共有するためにはVault専用クラスタを作成するのが一番いいと考えられます。
その場合、Vault Helmのvalues.ymlにて「Vault Server Podは起動せず、外部Vault Serverを使用する」というような設定が可能なので、それを利用します。
そうすると、Ingector Agentのみを起動することができます。
そして、良くも悪くも柔軟な権限管理や様々な秘匿情報の管理に対応しているので、様々なケースに対応できると思います。
高機能であるが故に覚えないといけないことも多いというのも事実です。
まだまだ分かっていない点も多いので本番運用に向けて色々調べていく所存です。
おまけ
ついでに、データストレージとして使用しているConsulサーバにアクセスするとKey/Valueの項目にvaultのデータが格納されていることも確認できます。
更新情報
- 2021/01/15
- 説明不足の箇所や文脈がおかしいところの文章を修正しています
- 構築内容に変更はありません