KubernetesのSecret情報をExternalSecretとAWS Secrets Managerで安全に管理する方法

#はじめに
[セゾン情報システムズ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/sisco)
2日目を担当させていただきます。よろしくお願いいたします！

本記事では、以下を使用することで、機密情報をマニフェストファイルに記載することなく安全に管理する方法を紹介いたします。

- Amazon EKS
AWS で Kubernetes を簡単に実行できるマネージド型の Kubernetes サービス
https://aws.amazon.com/jp/eks/

- ExternalSecret
kubernetesのSecretを安全に管理するためのオープンソースソフトウェア
https://github.com/external-secrets/kubernetes-external-secrets

- AWS Secrets Manager
秘匿情報をAWS上に保管し、APIコールで取得できるサービス
https://aws.amazon.com/jp/secrets-manager/

#概要
kubernetes上に作成されたExternalSecretリソースが、外部のSecretManagerに登録されている情報を読み取り、Secretリソースを作成します。
![externalsecret](https://github.com/external-secrets/kubernetes-external-secrets/blob/master/architecture.png?raw=true)
Secretリソースとして登録するKey名と、外部のSecretManagerに登録されているデータの参照情報で秘匿情報を取得できるため、
機密情報をマニフェストファイルに記載する必要はありません!


#手順
AWS Secrets Managerに登録されている秘匿情報を取得する手順を記載いたします。

- [サービスアカウントへのIAM権限付与設定](#サービスアカウントへのiam権限付与設定)
- [AWS Secrets Managerで秘匿情報登録](#aws-secrets-managerで秘匿情報登録)
- [ExternalSecretリソース作成](#externalsecretリソース作成)
- [Secret Managerから秘匿情報取得](#secret-managerから秘匿情報取得)

##サービスアカウントへのIAM権限付与設定
AWSリソースへアクセスするための権限をpod単位で付与したかったので、「[IAM Roles for Service Accounts](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)」の仕組みを使用


###IAM IDプロバイダ作成  
Kubernetes サービスアカウントでIAMロールの権限を使用するために、まずは以下のコマンドでOIDC IDプロバイダを作成

```
eksctl utils associate-iam-oidc-provider --cluster {CLUSTER_NAME} --approve --region ap-northeast-1
```


###IAMロール作成  
既存のAWSのポリシーに「SecretsManagerReadWrite」があるので、そのポリシーを付与したIAMロールを作成し、
上記で作成したOIDC IDプロバイダのIDを信頼関係に設定


```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::{AWS_ACCOUNT_ID}:oidc-provider/oidc.eks.ap-northeast-1.amazonaws.com/id/{PROVIDER_ID}"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.ap-northeast-1.amazonaws.com/id/{PROVIDER_ID}:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "oidc.eks.ap-northeast-1.amazonaws.com/id/{PROVIDER_ID}:sub": "system:serviceaccount:default:*"
        }
      }
    }
  ]
}
```
>※「*」を使用する場合は、「StringEquals」ではなく「StringLike」を使用する必要あり!

##AWS Secrets Managerで秘匿情報登録
e.g.
シークレット名：test-credentials
値：{"username":"admin","password":"admin123"}

以下のようにAWS CLIで作成

```
aws secretsmanager create-secret --region ap-northeast-1 --name test-credentials --secret-string '{"username":"admin","password":"admin123"}'
```



##ExternalSecretリソース作成

helmでExternalSecretのリポジトリを追加
(リポジトリは最近移動したらしい -> [関連記事](https://qiita.com/hayao_k/items/23312d1bfa7c6a27fda5))

```
helm repo add external-secrets https://external-secrets.github.io/kubernetes-external-secrets/
```

helm installでExternalSecretリソースを作成

```
helm install {RELEASE_NAME} external-secrets/kubernetes-external-secrets --skip-crds \
--set securityContext.fsGroup=65534 \
--set nodeSelector."alpha\.eksctl\.io/nodegroup-name"={NODE_GROUP_NAME} \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"='arn:aws:iam::{AWS_ACCOUNT_ID}:role/{ROLE_NAME}' \
--set env.AWS_REGION='ap-northeast-1' \
--set env.AWS_DEFAULT_REGION='ap-northeast-1'
```


>securityContext.fsGroup=65534 : 
シークレット情報にアクセスするためのtokenファイルに、rootユーザー以外のユーザーがアクセスできるようにするための設定
(起動したpodのユーザーはrootではないため必要な設定)

>nodeSelector :
指定したNodeにpodを起動させたかったので設定

>serviceAccount.annotations :
 サービスアカウントでIAMロールの権限を使用するための設定、[上記で作成したIAMロール](#iamロール作成)を指定

>その他オプション等については以下参照
https://artifacthub.io/packages/helm/external-secrets/kubernetes-external-secrets

 

##Secret Managerから秘匿情報取得
以下のyamlをkubectlでapply

```{#lst:externalSecret.yaml}
apiVersion: kubernetes-client.io/v1
kind: ExternalSecret
metadata:
  name: test-external-secret
spec:
  backendType: secretsManager
  data:
    - key: test-credentials #SecretManagerのシークレット名
      name: username #変数名
      property: username #取得したい値のキー
```


```
$ kubeclt apply -f externalSecret.yaml
$ kubectl get externalsecret
NAME                    LAST SYNC   STATUS    AGE
test-external-secret    2s          SUCCESS   34h
```

以下のコマンドで取得した秘匿情報の確認も可能
↓結果抜粋↓(値はbase64でエンコードされてます)

```
$ kubectl get secret test-external-secret -o yaml
data:
  username: YWRtaW4K
  password: YWRtaW4xMjMK
  :
```

上記の秘匿情報を使用する場合は以下のようにyamlに環境変数を定義することで使用可能


```
env:
  - name: TEST_VALUE
    valueFrom:
      secretKeyRef:
        name: test-external-secret #ExternalSecretのmetadata.name
        key: username #ExternalSecretのspec.data.name
```