Terraformでk8sにSecretsを作って使う

Terraformを使ってGKEでk8sクラスタとサービスアカウントを作り、サービスアカウントのクレデンシャルを Secrets として作成した。やったことはそれなんだけど、 Secrets の作成・参照と位置付けをメモしとく。

コンテナに秘密情報を与えるには2つの方法がある。

  • 環境変数
  • ファイル(ボリュームのマウント)

実行パラメタはマニフェストに平文で残すことになり使えない。 k8sにはSecretsというリソースがある。 環境変数にもファイルにも使うことができる。

Secretsの作り方は後に書くとして使い方を並べておく。

Secretsの使い方

ボリュームとしてマウントする

公式をそのままコピーしてきた。 mysecret という Secretsfoo という名前を与えて /etc/bar にマウントしてる。 秘密情報なのでコンテナに書き込まれないように readOnly が指定してる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/bar"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

環境変数として使う

公式をそのままコピーしてきた。 env で環境変数を渡している。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never

Secrets作り方

k8sは手続き的な方法と宣言的な方法を提供している。

今回は k8s 公式ツールでなく Terraform を使った。 これはGCP, AWSを Terraform で管理しているからで、その接続情報をPodsに渡すのは、 渡す情報・渡す先の両方を知っている Terraform が自然だと考えたからだ。

  • コマンドラインで作る
  • マニフェストで作る
  • Terraformのマニフェストで作る

また後から思い出したので、最後に kustomize を使う方法も残しておく。

コマンドラインで作る

今回の目的に使える Secretsgeneric

1
  kubectl create secret generic NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run] [options]

定義には3種類のデータソースが使える。ただし –from-env-file は他2つと併用できない。

1
2
3
4
5
6
7
8
cat <<EOF > ./creds.env
db_user=fuji
db_pass=orin
EOF
kubectl create secret generic orange-secrets /
  --from-env-file=creds.env /
  --dry-run
secret/orange-secrets created (dry run)

他の例はこんな感じ。

1
2
3
4
5
6
7
8
echo {"id": "hoge", "secret": "xxxx"} > ./credentials.json
cp ~/.ssh/id_rsa.github ./
kubectl create secret generic orange-secrets /
  --from-file=credentials.json /
  --from-file=id_rsa=id_rsa.github /
  --from-literal=password=bad_password /
  --dry-run
secret/orange-secrets created (dry run)

ref. https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets

マニフェストで作る

data の項目はbase64エンコードして記述する。

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
stringData:
  username: administrator

ref. https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets

Terraformのマニフェストで作る

Terraformで管理しているマネージドサービスに関連した情報を埋め込むのに使える。 公式でそのまま欲しかった例を見つけてしまった。 provider の設定だけ足しておく。 kubernetes プロバイダを GCP の情報からセットする。 この部分はterraform-google-gkeモジュールを参考にした。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
provider "kubernetes" {
  version = "~> 1.7.0"

  load_config_file       = false
  host                   = google_container_cluster.cluster.endpoint
  token                  = data.google_client_config.client.access_token
  cluster_ca_certificate = base64decode(google_container_cluster.cluster.master_auth[0].cluster_ca_certificate)
}

data "google_service_account" "cloud_proxy" {
  account_id = "cloud-proxy"
}

resource "google_service_account_key" "cloud_proxy_key" {
  service_account_id = data.google_service_account.cloud_proxy.name
}

resource "kubernetes_secret" "google-application-credentials" {
  metadata {
    name = "google-application-credentials"
  }
  data = {
    credentials.json = base64decode(google_service_account_key.cloud_proxy_key.private_key)
  }
}

ref.

Kustomize で作る

Kustomizeは今回の用途を目指していてかつ kubectl に統合されているため選択肢として自然。 CDで使うときは Terraform のoutputで必要な情報を取り出せるようにして Pipeline スクリプトでファイルを作成。 その後、それらのファイルから Kustomize で Secrets を生成って流れが良いと思う。

大掛かりになったので具体例のリポジトリを作った。 関連スクリプトが大きくなった。これが良いのかは怪しい。

ただCDとか自動化された作業自体は命令的だし、受け渡しがスクリプトになってても問題はないと思う。

Example

tfから中間生成物として取り出す部分と kustomize が利用する場所に書き込む部分に分けた。 書き込む部分は環境ごとに色々やりたいと思うのでヘルパースクリプトを作るにとどめた。 取り出す部分はここにコピーしておくとこんな感じ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
## for both manual and CD pipeline

set -eu

TOP_DIR=$(cd $(dirname $0); pwd)/..
TF_WORK_DIR=${TOP_DIR}/tf

cd $TF_WORK_DIR
terraform output -no-color \
  | awk -f $TOP_DIR/scripts/save-output.awk \
    -v target=$TOP_DIR/tmp
1
2
3
4
5
6
7
8
9
BEGIN{
  env_file = sprintf("%s/_env.txt", target)
}
{
  close(file)
  file = sprintf("%s/%s", target, $1)
  print $3 >  file
  print $1"="$3 >> env_file
}

kustomization.yaml は下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  - ../base/
secretGenerator:
- name: app-tls
  files:
    - secrets/tls.cert
    - secrets/tls.key
  type: Opaque
- name: env-file-secret
  env: env.txt
  type: Opaque
comments powered by Disqus