🐳

おうちKubernetes におけるSecret管理

2024/09/06に公開

はじめに

みなさん、Secretの管理に苦労していませんか?
当サービスのインフラはパブリックリポジトリーで管理しており、直接Secret情報を書くことは避けたいと考えていました。kubernetesにはSecretリソースが用意されており、コマンドを実行することで手動でSecretを追加することができます。
ただ、万が一クラスターを再編する必要に迫られた際に全てのSecret を挿入するのは非常に手間です。また、Secret情報を記載したドキュメントが更新されなくなることは目に見えていますし、Secretを各サービスから再取得してくるのも非常にめんどくさいです。
これらの問題を解決する管理方法を見つけたのでご紹介したいと思います。

実現したいこと

KubernetsのリソースはArgoCDで管理しており、ArgocdへのログインはGithb SSOで行っているため、Github Appのclient secretを自動で挿入したい。
DBなどのパスワードも管理したいが、管理するSecretが膨大になるのは防ぎたい。
クラウドのSecret ManagerでSecretを一元管理しておき管理を楽にしたい。

External Secret

kubernetesのSecret管理といえば、External Secret Operator(以下ESO)を使う方法が主流かと思います。
ESOはGCPなどの外部に保存されたSecretを取得してKubernetesのSecretリソースとして利用できるようにするためのCRDです。定期的に取得するといった柔軟な設定が可能です。
External Secret Flow
しかし、ESOはArgoCDによってデプロイされるため、ArgoCDを動作させるのに必要なSecret(SSOに使用するGitHub App のSecretなど)は別途kubectlコマンドを使用し手動で挿入するしかありません。可能であればこれらのオペレーションを減らしたいですし、セキュリティー的にも避けたいです。
また、当サービスはGCPを使用しており、Secrer ManagerなどのGCP上のリソースにアクセスする手段として、オンプレミスのKubernetesからOIDCが利用できず、サービスアカウントキーで認証するしかありません。サービスアカウントキーは、ベストプラクティスでも言及されていますが、有効期間が非常に長く、漏洩した際のセキュリティリスクが非常に高いです。これらの理由から、他の方法を検討することにしました。

ではどのように実現するのか

結論から言うとTerraformを使用します。
TerraformというとクラウドでのIaC実現のために使われることが多いですが、実は、kubernetesのリソース管理を行うこともできます。例えばSecretを作成したりhelmを使用してデプロイをすると言った具合です。
そして、Terraformを使用することで、クラウドのSecret Managerから値を取得し、Secretとしてデプロイするまでを人の手を介さずに行うことが出来ます。

Providerの作成

Terraformでkubernetesを扱うためのProviderを作成します。

terraform {
  required_providers {
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "2.31.0"
    }
  }
  backend "gcs" {
    bucket = "commet-terraform"
    prefix = "dev/k8s"
  }
}
# kubeconfigをControl PlaneにSSHして取得し、ローカルに保存したものを読み込む。
data "local_file" "kubeconfig" {
  filename = "./kubeconfig/config"
}

# オンプレクラスタの kubeconfig.yaml は、cluster CA certificate、client certificate、client keyをそれぞれ
#  - clusters[?].cluster.certificate-authority-data に
#  - users[?].user.client-certificate-data に
#  - users[?].user.client-key-data に
# base64で保持している。

locals {
  onp_kubernetes_cluster_ca_certificate = base64decode(yamldecode(data.local_file.example.content).clusters[0].cluster.certificate-authority-data)
  onp_kubernetes_client_certificate = base64decode(yamldecode(data.local_file.example.content).users[0].user.client-certificate-data)
  onp_kubernetes_client_key = base64decode(yamldecode(data.local_file.example.content).users[0].user.client-key-data)
}

provider "kubernetes" {
  host                   = var.onp_k8s_server_url
  cluster_ca_certificate = local.onp_kubernetes_cluster_ca_certificate
  client_certificate     = local.onp_kubernetes_client_certificate
  client_key             = local.onp_kubernetes_client_key
}
provider "helm" {
  kubernetes {
    host                   = var.onp_k8s_server_url
    cluster_ca_certificate = local.onp_kubernetes_cluster_ca_certificate
    client_certificate     = local.onp_kubernetes_client_certificate
    client_key             = local.onp_kubernetes_client_key
  }
}

Secretの作成

以上で準備は完了です!
例えば、Secretを作りたい場合、以下のように書くことで簡単に作ることができます。

resource "kubernetes_secret" "example" {
  metadata {
    name = "basic-auth"
  }

  data = {
    username = "admin"
    password = "P4ssw0rd"
  }

  type = "Opaque"
}

また、Terraformにはrandom_passwordというリソースがあり、これらを活用することで、簡単にDBのパスワードの管理を行うこともできます。作られたパスワードを先ほどの方法でSecretとして作成することで、podsから参照させることができます。

resource "random_password" "password" {
  length           = 16
  special          = true
  override_special = "!#$%&*()-_=+[]{}<>:?"
}

TerraformとKubernetesのリソース管理の境界

上の例で示したようにTerraformを使用してHelmリソースの管理を行うことも可能ですし、ArgoCD上で管理することを可能です。どちらで管理するべきなのか方針を示しておきます。
ひとつめに考えるべきことは、それぞれの同期タイミングです。TerraformはApply時に差分を確認し実行します。つまり、applyを行わなければ同期が行われません。
一方で、Kubernetesではマニフェストと実際のリソースとの差分は基本的には発生せず、常に同期されています。
また、Terraformで管理しているリソースに変更が生じた場合、例えば、Secretはそこまででは無いですが、ArgoCDは特にapplyにものすごく時間がかかります。そのため、タイムアウトや不整合が発生するリスクがあります。
これらのことを考えると、Terraformに安易にリソースの管理を行わせるべきではなく、最小限にとどめるべきです。例えば、SecretやArgoCDなどの基盤系かつ変更がほとんどないものをTerraformによって管理するべきです。

まとめ

kubernetesにおけるSecret管理についてご紹介しました。運用してから日が経っておらず至らぬ点があるかと思います。もっといい方法があれば、是非とも教えて欲しいです!
現時点はこの形で落ち着いていますが、さらに良い方法を探していきたいと思います。

エンジニア垢作りました!
お友達になってください🙇‍♀️
https://x.com/Kanameimaichi

Discussion