Container Engine for Kubernetes (OKE)でExternal Secrets Operatorの設定方法を確認する

# はじめに
OCIのContainer Engine for Kubernetes (OKE)において、Secretを安全に管理するためのツールとしてExternal Secret Operatorの設定方法を確認します。

https://external-secrets.io/v0.5.6/

External Secret Operatorは、外部プロバイダーに保存された情報からKubernetesのSecretを作成するためのCRDです。
OCIの場合は、Vaultサービスを使用します。

https://docs.oracle.com/ja-jp/iaas/Content/KeyManagement/home.htm

使用したクラスタです。
```
$ kubectl get node
NAME         STATUS   ROLES   AGE   VERSION
10.0.10.75   Ready    node    39h   v1.23.4
```

# 設定方法
以下を参考にしました。

https://external-secrets.io/v0.5.6/provider-oracle-vault/

## キーとシークレットの作成
Vaultにキーとシークレットを作成します。Vaultはあらかじめ作成済みです。
なお、OCI Vaultのシークレットはカタカナ、KubernetesリソースのSecretはアルファベットで記載します。

OCIコンソール画面で以下のようにキーを作成します。
![スクリーンショット 2022-05-31 16.20.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/93feac84-bee7-c704-fb51-95db1fa9ff54.png)

作成したキーを指定して、シークレットを以下のように作成します。
![スクリーンショット 2022-05-31 16.23.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/f03b54bf-c0f5-f5e7-032a-b58d1da7b1dc.png)

## External Secret Operatorのデプロイ

以下を参考にHelmを使用してデプロイします。

https://external-secrets.io/v0.5.6/guides-getting-started/

```
$ helm repo add external-secrets https://charts.external-secrets.io
"external-secrets" has been added to your repositories
$ helm repo list
NAME                    URL                               
external-secrets        https://charts.external-secrets.io
$ helm install external-secrets \
>    external-secrets/external-secrets \
>     -n external-secrets \
>     --create-namespace
NAME: external-secrets
LAST DEPLOYED: Tue May 31 07:27:26 2022
NAMESPACE: external-secrets
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
external-secrets has been deployed successfully!

In order to begin using ExternalSecrets, you will need to set up a SecretStore
or ClusterSecretStore resource (for example, by creating a 'vault' SecretStore).

More information on the different types of SecretStores and how to configure them
can be found in our Github: https://github.com/external-secrets/external-secrets
```

デプロイされたリソースを確認します。

```
$ kubectl get all -n external-secrets
NAME                                                    READY   STATUS    RESTARTS   AGE
pod/external-secrets-7bc8757d6-7wb2r                    1/1     Running   0          2m27s
pod/external-secrets-cert-controller-55754f89fb-mqrxb   1/1     Running   0          2m27s
pod/external-secrets-webhook-67965bb887-rpvtw           1/1     Running   0          2m27s

NAME                               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/external-secrets-webhook   ClusterIP   10.96.204.196   <none>        443/TCP   2m27s

NAME                                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/external-secrets                   1/1     1            1           2m28s
deployment.apps/external-secrets-cert-controller   1/1     1            1           2m28s
deployment.apps/external-secrets-webhook           1/1     1            1           2m28s

NAME                                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/external-secrets-7bc8757d6                    1         1         1       2m28s
replicaset.apps/external-secrets-cert-controller-55754f89fb   1         1         1       2m28s
replicaset.apps/external-secrets-webhook-67965bb887           1         1         1       2m28s
```

## ポリシーの作成
External Secret Operatorはインスタンスプリンシパルを使用して認証しますので、動的グループに以下のポリシーを設定します。

KubernetesのServiceAccountでも認証できるようですが、未確認です。

![スクリーンショット 2022-05-31 16.45.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/9b3cd576-9f2a-ad20-ee5e-43ffba2592fa.png)

## Secretの作成
VaultにAPIアクセスするために、APIキーの秘密キーとフィンガープリントを取得し、Secretを作成します。

以下のコンソール画面で秘密キーをダウンロードします。
![スクリーンショット 2022-05-31 17.06.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/934bc580-50fa-8e49-c719-8fddfcddbf29.png)

APIキーのフィンガープリントをメモします。
![スクリーンショット 2022-05-31 17.07.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/829238a6-75fa-1678-e11e-ca1796d72f9f.png)

以下のマニフェストファイルを作成して、Secretを作成します。

```oracle-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: oracle-secret
  labels:
    type: oracle
type: Opaque
stringData:
  privateKey: |-
        -----BEGIN PRIVATE KEY-----
        MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDR/ahwU01vyJZH
        #### insert you PEM certificate ####
        -----END PRIVATE KEY-----
  fingerprint: 03:06:00:00:00:00:00:00:00:00:00:00:00:00:00:00
```

```
$ kubectl apply -f oracle-secret.yaml 
secret/oracle-secret created
$ kubectl describe secret oracle-secret
Name:         oracle-secret
Namespace:    default
Labels:       type=oracle
Annotations:  <none>

Type:  Opaque

Data
====
fingerprint:  47 bytes
privateKey:   1703 bytes
```

## Secret Storeの作成
<!-- 
インスタンスプリンシパルを認証するためのSecret　Storeを作成します。
VaultのOCIDとリージョンを指定します。
インスタンスプリンシパルを使用せずに、KubernetesのService　Account経由でも認証できるようです。その場合、このSecret　Storeは不要だと思います。（未確認）

```principalSecretStore.yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: example-instance-principal
spec:
  provider:
    oracle:
      vault: ocid1.vault.oc1.uk-london-1.xxxxxxxxxxxxxxxxxxxxx # The vault OCID
      region: uk-london-1 # The vault region
```

```
$ kubectl apply -f principalSecretStore.yaml 
secretstore.external-secrets.io/example-instance-principal created
$ kubectl get ss
NAME                         AGE   STATUS
example-instance-principal   20s   Valid
```
-->

外部プロバイダーに認証するためのSecret Storeを作成します。

```authSecretStore.yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: secretstore-auth
spec:
  provider:
    oracle:
      vault: ocid1.vault.oc1.uk-london-1.xxxxxxxxxxxxxxxxxxxxx # vault OCID
      region: uk-london-1 # vault region 
      auth:
        user: ocid1.user.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # user OCID
        tenancy: ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxx # tenancy OCID
        secretRef:
          privatekey:
            name: oracle-secret
            key: privateKey
          fingerprint:
            name: oracle-secret
            key: fingerprint
```

```
$ kubectl apply -f authSecretStore.yaml
secretstore.external-secrets.io/secretstore-auth created
$ kubectl get ss
NAME                         AGE     STATUS
secretstore-auth             22s     Valid
```

# External Secretの作成
Secretを作成するためのExternal Secretを作成します。

```externalSecret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: ocisecret
  namespace: default
spec:
  refreshInterval: 0.03m # 外部プロバイダーに対してSecretを更新する頻度
  secretStoreRef:
    kind: SecretStore
    name: secretstore-auth # Secret Store名
  target:
    name: k8s-secret # External Secretによって作成されるSecret名
    creationPolicy: Owner
  data:
    - secretKey: oci-secret # secretのKey-Valueの「Key」
      remoteRef:
        key: oke-secret01 # OCI Vaultのシークレット名
```


```
$ kubectl apply -f externalSecret.yaml
externalsecret.external-secrets.io/ocisecret created
$ kubectl get es
NAME        STORE                        REFRESH INTERVAL   STATUS
ocisecret   example-instance-principal   0.03m              SecretSynced
```

External SecretによってSecretが作成されています。

```
$ kubectl get secret
NAME                  TYPE                                  DATA   AGE
default-token-zftfg   kubernetes.io/service-account-token   3      23h
k8s-secret            Opaque                                1      77s
oracle-secret         Opaque                                2      5m28s
```

```
$ kubectl describe secret k8s-secret
Name:         k8s-secret
Namespace:    default
Labels:       <none>
Annotations:  reconcile.external-secrets.io/data-hash: 8b6c7979477db7cffa9a66740dc10ca4

Type:  Opaque

Data
====
oci-secret:  8 bytes

```

Secretの値をデコードして確認します。

```
$ kubectl get secret k8s-secret -o jsonpath='{.data.oci-secret}'| base64 -d
aabbccdd
```

OCI Vaultのコンソール画面でシークレットの値を更新します。

![スクリーンショット 2022-06-01 15.41.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/86a6a7b7-d88c-8827-90a4-15608935fe6c.png)

再度、Secretの値をデコードして確認します。

```
$ kubectl get secret k8s-secret -o jsonpath='{.data.oci-secret}'| base64 -d
xxyyzz
```

Vaultのシークレットに合わせてSecretの値が更新されていることが確認できます。