インフィニットループ 技術ブログ

2024年01月19日 (金)

著者 : t-yamasaki

ExternalSecretsOperator(ESO)について

こんにちは、ILに新卒入社してから早二年が経とうとしているkerokeroです!

Kubernetesクラスタ上で機密情報を扱う際に使われるリソース種別としてSecretというものが存在するのですが、今回は外部KeyManegementStore等からクラスタ内へ機密情報を移動(取得)する場合に必要となる拡張であるExternalSecretsOperator(ESO)について以下で説明していきます。

ExternalSecretsOperatorとは?

詳しくは公式docを読むと精確かつ早いのですが、ざっとどういうものなのかを理解していただくためにあれこれと省略しつつお伝えしようと思います。

ざっくりと説明すると、ExternalSecretOperator(以下ESO)とは「AWS SecretsManagerHashiCorp Vault, Google SecretsManagerなどk8sクラスター外部のシークレット管理システム(外部provider)と、クラスタ内部のシークレットを統合して管理するためのKubernetesOperator」になります。

より簡潔に表現するならばクラウドプラットフォームを跨いで、パスワードやアクセスキーなどを扱いたいときに必要になるものとも言えます。

つまり、AWSやGoogleCloud上に展開されているk8sクラスタで使う機密情報の保管場所として、AWS SecretsManagerParameterStoreを使うことができるようになるということです。

External Secrets Operator is a Kubernetes operator that integrates external secret management systems like AWS Secrets Manager, HashiCorp Vault, Google Secrets Manager, Azure Key Vault, IBM Cloud Secrets Manager, CyberArk Conjur and many more. The operator reads information from external APIs and automatically injects the values into a Kubernetes Secret.

ExternalSecretsOperatorhttps://external-secrets.io/latest/

画像に alt 属性が指定されていません。ファイル名: diagrams-resource-mapping.png

具体的にESOはどんなものなのかというと、ExternalSecret, SecretStore, ClusterSecretStoreといったカスタムAPIリソースの集合体として存在しています。

他のArgoCD等のカスタムリソース定義と同様、PodやDeploymentのようなkindとして上記のものたちがCRD定義されて使うことができるようになります。

デプロイ時に1度だけ更新するといった使い方もできますし、数分に一回外部KMSを参照し中身を更新させる定義を書くこともできて便利です。

ESOを使うためには、Kubernetesクラスタを上記のカスタムリソースで拡張したのちにシークレットの保存場所(AWS SM ParameterStoreなど)同期先のSecretの情報(クラスタ内部)などの定義をManifestファイルとしてyaml形式で書く必要があります。

マニフェストの具体的な中身については記事の最後で紹介するとして、ここではそれぞれのESOリソースの役割について紹介しようと思います。

リソースのあれこれ

ESOは基本的にSecretStore(またはClusterSecretStore)にて外部providerに対する認証を行い、ExternalSecretに書かれている「取得したい機密情報パラメータに関する定義」を参照します。

外部ProviderからSecretを取得・保存する一連の流れとしては、簡単には以下の様になっています(便宜上default namespaceでAWS SM ParameterStoreからデータを持ってくる場合)

  • 1.SecretStoreリソースが用意され、そこにはmanifestに記載されている外部Provider情報とAWSアクセスキーが存在する状態です。
  • 2.ExternalSecretは、取得元の情報として上記SecretStoreを参照した状態で用意されており、ParameterStoreにアクセスするためのロール情報なども書いてあります。また、取得した情報をSecretリソースとしてクラスタ内に吐き出す役割も一部担います。(4のexternalAPIへと渡している)
  • 3.ESOPodが上記二つのカスタムリソースを用いて外部Secret情報を取得する
  • 4.externalAPIがクラスタ内部にSecretとしてその中身を吐き出す

(図中だと3,4は省略されています)

上記の流れでは、ある特定の名前空間(ここではdefaultと)の中で完結する場合のケースとしてSecretStoreを用意しましたが、特定の名前空間に縛られないクラスターリソースとしてClusterSecretStoreというものも作ることができます。

接続先は毎回同じGoogleCloudに保管されている機密情報だけど、Secretを生成したいk8sクラスタ上の名前空間が別々といった場合に「SecretStoreは流用できる」という発想のもと使われるものになります。

そうすることで、SecretStoreが扱う認証情報は一つのClusterSecretStoreリソースのみに与えることになり色々と都合が良いです。

GoogleCloudのGKEクラスタとGoogle Secret Managerを連携させたい場合の図を拝借してきたので、ClusterExternalSecretを用いる場合についても説明します。

下図では、ExternalSecretsリソースが生成されると、その中で指定されているClusterSecretStoreリソースを参照します。

ClusterSecretStoreリソースに設定されているアクセス先(GoogleCloud)の認証情報や取得するsecret manager secretの情報をもとに、externalAPIをインスタンス化します(①)。

externalAPIがインスタンス化されると、ESOはexternalAPIからsecret情報を抽出しExternalSecretリソースで定義されている「作成するsecretのnameやdata.keyなど」の情報をもとに、実際にCluster内でsecretを自動生成します(②)。

そして、ESOPod自身は常にexternalAPIと自動生成されたsecret valuesが同期しているかどうかチェックしています。

使われ方の一例

OperatorFrameworkのコンポーネントの一部として存在しているOperatorLifecycleManager(OLM)というものを使って、k8s環境に上述のESOをインストールすることもできます。

(OLMでもCatalogSourceSubscriptionといった別途CRD達を定義して使うことになるので、このあたりの知識についても紹介したいのですがそれはまた別の機会にしようと思います)

OLM経由で入れたESOはexternal-secrets namespaceに保管され、その上で名前空間に依存しないクラスターリソースとしてClusterSecretStoreClusterExternalSecretを生成しています。

その他のdev環境用namespaceなどどこからでも参照できるSecretStoreを一つだけ用意し、各種ExternalSecretClusterExternalSecretから生み出されるというイメージです。

上記のGoogleCloudの例と似たようなパターンですね。

また、KubernetesのKustomizeを使うことで各種リソースをデプロイする方法も合わせて用いることができます。

なので、環境ごとに共通な定義と非共通な定義をそれぞれまとめたマニフェストファイルを用意し、各環境namespace側にExternalSecretが入るような仕組みを作ることができるわけですね。

細かい話は図がないと分かりづらいので、下記の様なものを用意してみました。

各種リソースのyaml内容

ESO関連リソースのコードを二つほど具体例として載せてみました。AWSのSystemManagerのParameterStoreに保管しているKVから値を取得する仕組みとなっています。

ClusterSecretStore

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: cluster-secret-store
spec:
  provider:
    aws:
      service: ParameterStore
      role: arn:aws:iam::123456789012:role/knowledge-test
      region: ap-northeast-1
      auth:
        secretRef:
          accessKeyIDSecretRef:
            name: testaws-secret
            key: test-access-key
          secretAccessKeySecretRef:
            name: testaws-secret
            key: test-secret-access-key

ClusterExternalSecret

apiVersion: external-secrets.io/v1beta1
kind: ClusterExternalSecret
metadata:
  name: tls-cluster-external-secret
spec:
  externalSecretName: tls-external-secret
  namespaceSelector:
    matchLabels:
      eso: cluster
  externalSecretSpec:
    refreshInterval: 1m
    secretStoreRef:
      name: cluster-secret-store
      kind: ClusterSecretStore
    target:
      name: dev-tls-secret
      creationPolicy: Owner
      template:
        type: kubernetes.io/tls
    dataFrom:
    - find:
        path: "/develop/"
        name:
          regexp: "tls"
      rewrite:
      - regexp:
          source: "/develop/tls/CERT"
          target: "tls.crt"
      - regexp:
          source: "/develop/tls/PKEY"
          target: "tls.key"

余談

上記のコード例はClusterExternalSecretに限らず、ExternalSecretでも勿論問題ないです。ESOの細かい挙動については、実際にコードを見てみるとより一層理解が深まると思います。どうやらこのあたりにgolangによるExternalSecretの実装が書かれているようです。

おわりに

ILではインフラ技術に興味がある方の採用応募お待ちしております。

今回の記事でILに少しでも興味を持たれた方は是非、弊社採用情報ページからご応募よろしくお願いします!

参考資料

ブログ記事検索

このブログについて

このブログは、札幌市・仙台市の「株式会社インフィニットループ」が運営する技術ブログです。 お仕事で使えるITネタを社員たちが発信します!