LoginSignup
14
5

More than 5 years have passed since last update.

kustomize で環境変数から厳密に Secret を生成するのが意外と面倒な話

Last updated at Posted at 2018-09-26

注意

kustomize 2.0.0からsecretGeneratorのcommandsはセキュリティ上の理由から廃止されました。そのため以下で紹介している方法は現在は使用できません。

やりたいこと

  • kustomizesecretGenerator を使って、環境変数から厳密に Secret を生成したい
    • スペースや改行も含めてバイトレベルで同一の値を持たせて生成したい
    • 環境変数が存在しないときは kustomize build をエラーにさせたい

例えば CircleCI では環境変数として秘密情報を持たせることができます。
この秘密情報を露出しないように Secret を生成する方法として、secretGenerator を使おうというものです。

なお、kustomize はこの記事を書いている時点で最新の 1.0.8 を使用します。

結論

これでうまくできます。
多分。
問題になるケースがあれば教えてください。

secretGenerator:
- name: foo-secret
  commands:
    SECRET_JSON: bash -euc 'echo -n "$JSON"'

検証手順

いくつかの kustomization.yaml を用意して、それぞれ別々の secretGenerator を定義します。
これを検証用のスクリプトから実行し、結果を入力の環境変数と比較して値の同一性を比較します。

検証用のコードは GitHub に置いています。
https://github.com/yuya-takeyama/kustomize-secret-generator-env-problem
検証スクリプトの本体はこちら
https://github.com/yuya-takeyama/kustomize-secret-generator-env-problem/blob/master/bin/test

引数として kustomization.yaml が存在する overlay ディレクトリのパスとして渡して実行することで、検証を行えます。

git clone してそのまま実行することもできますし、Dockerfile も同梱しているので Linux (Alpine Linux) 環境での検証も行えます。

$ docker run -it yuyat/kustomize-secret-generator-env-problem

この記事中では macOS Sierra での実行結果を中心に記載しつつ、必要に応じて Linux での実行結果についても言及します。

1. echo

まずは素朴に echo でやってみましょう。

実行すると以下が出力されます。

  • kustomization.yaml の中身
  • 入力として渡した環境変数 (Expected)
    • 様々な問題をあぶり出すために、文字列中に改行コードを含み、改行と空白でインデントされた JSON を渡しています
    • 改行コード等も含めて違いがわかりやすいよう、Ruby で inspect した結果を出力しています
  • 生成された Secret から取り出した値 (Actual)
    • Secret には Base64 エンコードした値が保存されるので、それを取り出してデコードした値です
    • こちらも inspect した結果です
  • Expected と Actual を比較した結果 (OK or NG)
$ ./bin/test 01-echo
kustomization.yaml:
secretGenerator:
- name: foo-secret
  commands:
    SECRET_JSON: echo $JSON

Expected:
"{\n  \"FOO\": \"FOO    BAR\\nBAZ\"\n}"

Actual:
"{ \"FOO\": \"FOO BAR\nBAZ\" }\n"

NG

初歩的な問題として、$JSON をダブルクォートで囲んでいないので、半角スペースが一部消えてしまっています。
またよく見ると、JSON 中のエスケープされた改行コードであった \n が本物の改行コードに変わってしまっていますし、末尾にも改行が追加されてしまっていることがわかります。

2. ダブルクォートを使って echo

変数をきちんとダブルクォートで囲んだ上で echo してみます。

$ ./bin/test 02-echo-with-quotes
kustomization.yaml:
secretGenerator:
- name: foo-secret
  commands:
    SECRET_JSON: echo "$JSON"

Expected:
"{\n  \"FOO\": \"FOO    BAR\\nBAZ\"\n}"

Actual:
"{\n  \"FOO\": \"FOO    BAR\nBAZ\"\n}\n"

NG

空白が一部消えてしまう問題は解消されました。
しかし改行コード関連の問題は相変わらずです。

3. printenv

echo ではなく printenv を使ってみます。

$ ./bin/test 03-printenv
kustomization.yaml:
secretGenerator:
- name: foo-secret
  commands:
    SECRET_JSON: printenv JSON

Expected:
"{\n  \"FOO\": \"FOO    BAR\\nBAZ\"\n}"

Actual:
"{\n  \"FOO\": \"FOO    BAR\\nBAZ\"\n}\n"

NG

かなり正解に近づきましたが、末尾に改行が追加されてしまっています。

4. gprintenv -0

macOS の printenv は BSD のものですが、GNU Coreutils に含まれる GNU printenv (gprintenv) も試してみましょう。
こちらには BSD 版にはない -0 というオプションが含まれています。
(Linux では printenv として呼び出す必要がありますが、まぁそこは alias か何かで解決することにしましょう)

$ ./bin/test 04-gprintenv-0
kustomization.yaml:
secretGenerator:
- name: foo-secret
  commands:
    SECRET_JSON: gprintenv -0 JSON

Expected:
"{\n  \"FOO\": \"FOO    BAR\\nBAZ\"\n}"

Actual:
"{\n  \"FOO\": \"FOO    BAR\\nBAZ\"\n}\x00"

NG

今度は末尾にヌルバイトが含まれてしまいました。
man gprintenv には以下のように記載されています。

       -0, --null
              end each output line with NUL, not newline

JSON としては同一ですが、バイトレベルでの同一性を保証したいので、やはりこれも NG です。
これではパスワード等の文字列を渡したい時には問題になるかもしれません。

bash から echo -n を呼ぶ

printenv はうまく行かなそうなので、echo をもっと工夫して呼んでみることにします。

$ ./bin/test 05-bash-echo
kustomization.yaml:
secretGenerator:
- name: foo-secret
  commands:
    SECRET_JSON: bash -euc 'echo -n "$JSON"'

Expected:
"{\n  \"FOO\": \"FOO    BAR\\nBAZ\"\n}"

Actual:
"{\n  \"FOO\": \"FOO    BAR\\nBAZ\"\n}"

OK

うまくいきました!
これは Alpine Linux 上で実行してみても同じ結果が得られます。

ここからは各オプションの詳細をみていきます。

echo -n

これは末尾に改行が追加されるのを防ぐためのものです。
man echo によると以下のようにあります。

     -n    Do not print the trailing newline character.

注意しないといけないのは、echo には /bin/echo の他に、シェルによってはビルトインコマンドもあり、挙動が異なることです。
手元の macOS 上の Bash 3.2.57 の echo は Bash ビルトインのコマンドですが、sh の echo は /bin/echo で、こちらには -n オプションが存在しません。

ちなみにエスケープされた改行コードが改行に変換されてしまうのは sh のせいで、Bash ビルトインの echo ではこの問題もありません。
(Alpine Linux ではデフォルトのシェルが ash で、ash の echo も同様に問題ないようです)

bash -u

これは環境変数が存在しない時にエラーとするためのものです。
試しに検証スクリプトを通さずに kustomize build すると以下のようにエラーになることがわかります。

$ kustomize build 05-bash-echo
Error: NewResMapFromSecretArgs: makeSecret: commands map[SECRET_JSON:bash -euc 'echo -n "$JSON"']: exit status 1

bash -e

これは Bash スクリプト中でコマンドがエラーになったらその時点でエラーとしてスクリプトの実行を中止するためのオプションです。
今回の場合はあってもなくても特に変わりませんが、まぁあった方が厳密な感じはするので...

14
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
5