KubernetesでGitOps運用となると必ず話題になるのがSecretの管理です。

  • Sealed Secretsやkubesecなどの手元で暗号化する系
  • Kubernetes Secrets Store CSI Driverやkubernetes-external-secretsなどの外部シークレットストアから引っ張ってくる系
  • 機密情報だけ別Repoにする

など様々な方法がありますが、学習コストや実運用をイメージするとどのソリューションもしっくり来ませんでした。

そんな中でIBM社が開発しているArgoCD Vault Pluginを触ってみたところ、ArgoCDのデプロイ時にplaceholderをreplaceするという合理的かつシンプルな仕組みで非常に好感触でした。

2022/02追記: Argo Projectに移管されたようです。 https://argocd-vault-plugin.readthedocs.io/en/stable/

(上記でいう「外部シークレットストアから引っ張ってくる系」の一種に該当します) ArgoCD Vault Plugin (以下AVP) は日本語の情報が皆無に等しかったため、布教の目的も込めて導入・運用方法を記載します。

テスト

AVPはbrewからも導入でき、手元で簡単にテストができます。 シークレットストアはAWS Secrets Mangerを使う前提で解説します。

ローカル環境にインストール (Mac)

1
$ brew install argocd-vault-plugin

AWS Secrets Mangerに機密文字列を登録する

1
2
key: my_secret
value: foobar

Kubernetes Manifestを作成する

Secretの実装は非常に簡単で、

  • アノテーションに参照するSecret Managerのパスを記述する
  • Secret Managerのキー名を<> で囲う

だけでOKです。

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
  name: credentials
  annotations:
    avp.kubernetes.io/path: "avp/test"
data:
  MY_SECRET: <my_secret | base64encode>

Decryptのテスト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ export AWS_ACCESS_KEY_ID=xxxx
$ export AWS_SECRET_ACCESS_KEY=xxxx
$ export AWS_REGION=ap-northeast-1
$ export AVP_TYPE=awssecretsmanager
$ argocd-vault-plugin generate path/to/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: credentials
  annotations:
    avp.kubernetes.io/path: avp/test
data:
  MY_SECRET: Zm9vYmFy # == $(echo -n foobar | base64)

placeholderがSecrets Mangerに登録していた値に変換されました! これで安心してGitHub repoにpushできます。

ArgoCDへのPluginの導入

導入方法は公式ドキュメントの方が参考になると思うので、ここではhelmでArgoCDを導入する場合のサンプルコードを掲載します。

1. IAM Userの設定

AVPがSecret ManagerにアクセスするためのIAM User credentialが必要です。 このcredentialはAVPで管理できないため、どのようにAVPに渡すかは少し考える必要があります。

方法1. このcredentialだけは手動でapplyする

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: argocd-vault-plugin-iam
  namespace: argo
data:
  AWS_ACCESS_KEY_ID: xxxxx
  AWS_SECRET_ACCESS_KEY: xxxxx
kind: Secret
1
$ kubectl apply -f secret-iam.yaml

方法2. terraformで作成する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
resource "aws_iam_user" "avp" {
  name = "argocd-vault-plugin"
}

resource "aws_secretsmanager_secret" "avp" {
  name = "avp/test"
}

resource "aws_iam_user_policy" "avp" {
  name = "ArgocdVaultPlugin"
  user = aws_iam_user.avp.name

  policy = jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Effect" : "Allow",
        "Action" : [
          "secretsmanager:GetResourcePolicy",
          "secretsmanager:GetSecretValue",
          "secretsmanager:DescribeSecret",
          "secretsmanager:ListSecretVersionIds"
        ],
        "Resource" : [
          "${aws_secretsmanager_secret.avp.arn}",
        ]
      }
    ]
  })
}

resource "aws_iam_access_key" "avp" {
  user = aws_iam_user.avp.name
}

resource "kubernetes_secret" "argocd-vault" {
  metadata {
    name      = "argocd-vault-plugin-iam"
    namespace = "argo"
  }

  data = {
    "AWS_ACCESS_KEY_ID"     = aws_iam_access_key.argocd-vault.id
    "AWS_SECRET_ACCESS_KEY" = aws_iam_access_key.argocd-vault.secret
  }

  type = "Opaque"
}

2. ArgoCD with AVPのインストール

公式のインストール手順をhelmにしました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
...
repoServer:
  env:
    - name: AVP_TYPE
      value: awssecretsmanager
    - name: AWS_REGION
      value: ap-northeast-1
  envFrom:
    - secretRef:
        name: argocd-vault-plugin-iam
  volumes:
    - name: custom-tools
      emptyDir: {}
  initContainers:
    - name: download-tools
      image: alpine:3.8
      command: [sh, -c]
      args:
        - >-
          wget -O argocd-vault-plugin
          https://github.com/IBM/argocd-vault-plugin/releases/download/v1.1.1/argocd-vault-plugin_1.1.1_linux_amd64 &&
          chmod +x argocd-vault-plugin &&
          mv argocd-vault-plugin /custom-tools/          
      volumeMounts:
        - mountPath: /custom-tools
          name: custom-tools
  volumeMounts:
  - name: custom-tools
    mountPath: /usr/local/bin/argocd-vault-plugin
    subPath: argocd-vault-plugin
server:
  config:
    configManagementPlugins: |-
      - name: argocd-vault-plugin
        generate:
          command: ["argocd-vault-plugin"]
          args: ["generate", "./"]
      - name: argocd-vault-plugin-helm
        init:
          command: [sh, -c]
          args: ["helm dependency build"]
        generate:
          command: ["sh", "-c"]
          args: ["helm template $ARGOCD_APP_NAME ${helm_args} . | argocd-vault-plugin generate -"]
      - name: argocd-vault-plugin-kustomize
        generate:
          command: ["sh", "-c"]
          args: ["kustomize build . | argocd-vault-plugin generate -"]      
...
1
2
$ helm repo add argo https://argoproj.github.io/argo-helm
$ helm install argo/argo-cd -f values.yaml

3. ArgoCDにApplicationを登録

アプリケーションのデプロイ時に使用するplugin設定を定義します。 例えばアプリケーションの実装がKustomizeであれば、前項で定義した argocd-vault-plugin-kustomize を使う事で kustomize build . | argocd-vault-plugin generate - と同様の挙動になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: testapp
  namespace: argo
spec:
  project: default
  source:
    repoURL: 'git@github.com:my-org/my-repo.git'
    path: path/to/yaml
    targetRevision: HEAD
    plugin:
      name: argocd-vault-plugin-kustomize
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

あとは[テストで作成したManifest](#Kubernetes Manifestを作成する)と同様の実装でrepoにpushすればOKです。