Amazon EKSでExternal Secretsを利用する

# はじめに
Amazon EKSで[External Secrets](https://github.com/external-secrets/external-secrets/)を利用する方法をまとめます。
External Secretsは、AWS Secrets Managerに登録された秘匿情報をAmazon EKSのSecretに同期させるKubernetesオペレーターです。

※2021/11/03に[非推奨化](https://github.com/external-secrets/kubernetes-external-secrets/issues/864)となりました。

Mac環境を想定しています。

# 実行環境の準備
1.  AWS CLIの設定
[AWS CloudFormationを動かすためのAWS CLIの設定](https://qiita.com/miyabiz/items/fed11796f0ea2b7608f4)を参考にしてください。

1.  EKSクラスタの構築
[Macでeksctlを利用してAmazon EKSのクラスターを構築する](https://qiita.com/miyabiz/items/75f83237795b0b7d52bd)を参考にしてください。

1.  EKSのコンテキストの設定
[MacにてAmazon EKSの設定をする](https://qiita.com/miyabiz/items/b98d145cedb35dab9524)を参考にしてください。

1.  Helmの設定
[Amazon EKSでHelmを利用する](https://qiita.com/miyabiz/items/453361e6af64b324443c)を参考にしてください。

1.  ArgoCDの設定
[Amazon EKSでArgoCDを利用する](https://qiita.com/miyabiz/items/4022b0aba372a8f2c292)を参考にしてください。

# Secrets Managerの作成

1.  Secrets Managerを作成する
    ※今回は `akane/private-password` という名前にする
    ```sh
    aws secretsmanager create-secret \
      --name akane/private-password \
      --secret-string XXXXXXXX
    ```

# IAM OIDC IDプロバイダの作成
[クラスター用の IAM OIDC プロバイダーの作成](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/enable-iam-roles-for-service-accounts.html)を参考にしてください。

1.  クラスター名を設定する

    ```sh
    CLUSTER_NAME=${クラスター名}
    ```

1.  IDプロバイダを作成する

    ```sh
    eksctl utils associate-iam-oidc-provider \
      --cluster ${CLUSTER_NAME} \
      --approve
    ```

1.  IDプロバイダのARNを取得する

    ```sh
    OIDC_ARN=$(aws iam list-open-id-connect-providers \
      --query "OpenIDConnectProviderList[*].Arn" \
      --output text \
    | grep $( \
      aws eks describe-cluster \
        --name ${CLUSTER_NAME} \
        --query "cluster.identity.oidc.issuer" \
        --output text \
        | cut -d '/' -f 5 \
      )
    )
    ```

1.  IDプロバイダ名を取得する

    ```sh
    OIDC_NAME=$(aws iam get-open-id-connect-provider \
      --query "Url" \
      --output text \
      --open-id-connect-provider-arn \
      $( \
        aws iam list-open-id-connect-providers \
          --query "OpenIDConnectProviderList[*].Arn" \
          --output text \
        | grep $( \
          aws eks describe-cluster \
            --name ${CLUSTER_NAME} \
            --query "cluster.identity.oidc.issuer" \
            --output text \
            | cut -d '/' -f 5 \
          ) \
      )
    )
    ```

# IAMロールの作成

1.  IAMロールを作成する
    ※今回はロール名を`akane-dev-external-secrets-role`にしています。

    ```sh
    ROLE_POLICY=$(echo -n '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Federated": "'; echo -n "${OIDC_ARN}"; echo -n '"
                },
                "Action": "sts:AssumeRoleWithWebIdentity",
                "Condition": {
                    "StringEquals": {
                        "'; echo -n "${OIDC_NAME}"; echo -n ':sub": "system:serviceaccount:default:external-secrets"
                    }
                }
            }
        ]
    }')
    aws iam create-role \
      --role-name akane-dev-external-secrets-role \
      --assume-role-policy-document "${ROLE_POLICY}"
    ```

1.  ポリシーを作成する
    ※今回はポリシー名を`akane-dev-external-secrets-policy`にしています。
    ※今回はSecrets Managerのプレフィクスは`akane`にしています。

    ```sh
    export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
    POLICY=$(echo -n '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect" : "Allow",
          "Action" : [
            "secretsmanager:GetSecretValue",
            "secretsmanager:DescribeSecret"
          ],
          "Resource" : [
            "arn:aws:secretsmanager:ap-northeast-1:'; echo -n "${ACCOUNT_ID}"; echo -n ':secret:akane/*"
          ]
        }
      ]
    }')
    aws iam create-policy \
      --policy-name akane-dev-external-secrets-policy \
      --policy-document "${POLICY}"
    ```

1.  ポリシーをロールにアタッチする

    ```sh
    export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
    aws iam attach-role-policy \
      --role-name akane-dev-external-secrets-role \
      --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/akane-dev-external-secrets-policy
    ```

# 環境設定
※事前にKubernetesクラスターのコンテキストの設定をします。
※ArgoCDのインストールが必要です。

1.  `external-secrets.yaml`を作成する
    ※targetRevisionは`0.6.0`としていますが、最新バージョンは[External Secrets](https://github.com/external-secrets/external-secrets/)で確認してください。
    ※IAMロールのARNを設定します。

    ```yaml:external-secrets.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: external-secrets
      namespace: argocd
      finalizers:
        - resources-finalizer.argocd.argoproj.io
    spec:
      project: default
      source:
        repoURL: 'https://charts.external-secrets.io'
        targetRevision: 0.6.0
        helm:
          values: |
            serviceAccount:
              name: "external-secrets"
            env:
              AWS_REGION: "ap-northeast-1"
          parameters:
            - name: serviceAccount.annotations.eks\.amazonaws\.com/role-arn
              value: "${IAMロールのARN}"
        chart: external-secrets
      destination:
        server: 'https://kubernetes.default.svc'
        namespace: default
      syncPolicy:
        automated:
          prune: true

    ```

1.  クラスターに適用する

    ```sh
    kubectl apply -f external-secrets.yaml
    ```

# Secretを作成する
※今回は`akane`というNamespaceでSecretを作成します。

1.  `akane-namespace.yaml` を作成する
    ```yaml:akane-namespace.yaml
    kind: Namespace
    apiVersion: v1
    metadata:
      name: akane
      labels:
        name: akane
    ```
1.  `akane-secrets.yaml` を作成する
    ※remoteRef.keyにSecrets Managerのシークレット名を記載します。
    ※今回は `akane/private-password` という名前です。

    ```yaml:akane-secrets.yaml
    apiVersion: external-secrets.io/v1beta1
    kind: SecretStore
    metadata:
      name: akane
      namespace: akane
    spec:
      provider:
        aws:
          service: SecretsManager
          region: ap-northeast-1
    ---
    apiVersion: external-secrets.io/v1beta1
    kind: ExternalSecret
    metadata:
      name: akane-secret
      namespace: akane
    spec:
      refreshInterval: 1m
      secretStoreRef:
        name: akane
        kind: SecretStore
      target:
        name: akane-secret
        creationPolicy: Owner
      data:
      - secretKey: private-password
        remoteRef:
          key: akane/private-password
    ```

1.  クラスターに適用する

    ```sh
    kubectl apply -f akane-namespace.yaml
    kubectl apply -f akane-secrets.yaml
    ```

# Secretを確認する

1.  Secretの内容を取得する
    ```sh
    kubectl get secret -n akane akane-secret -o yaml
    ```

    ※下記のようなdataが表示されると思います。
    ```yaml
    data:
      private-password: WFhYWFhYWFg=
    ```
1.  base64でデコードする
    ※Secrets Managerに登録した`XXXXXXXX`が表示されます。
    ```sh
    echo "WFhYWFhYWFg=" | base64 -D
    ```

# クリーンアップ

1.  クラスターから削除する

    ```sh
    kubectl delete -f akane-secrets.yaml
    kubectl delete -f akane-namespace.yaml
    kubectl delete -f external-secrets.yaml
    ```

1.  ポリシーをロールからデタッチする

    ```sh
    export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
    aws iam detach-role-policy \
      --role-name akane-dev-external-secrets-role \
      --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/akane-dev-external-secrets-policy
    ```

1.  ポリシーを削除する

    ```sh
    export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
    aws iam delete-policy \
      --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/akane-dev-external-secrets-policy
    ```

1.  IAMロールを削除する

    ```sh
    aws iam delete-role \
      --role-name akane-dev-external-secrets-role
    ```

1.  Secrets Managerを削除する

    ```sh
    aws secretsmanager delete-secret \
      --secret-id akane/private-password \
      --force-delete-without-recovery
    ```

1.  IAM OIDC IDを削除する
    `eksctl delete cluster`を実行するとクラスターと一緒に削除されます。