綾小路龍之介の素人思考

[Travis CI] ビルドに必要な機密データをリポジトリに含める

例えば Travis CI で GitHub の gh-pages ブランチに push したいデータを作って、それを push する事を考える。push 時には ssh の秘密鍵が必要だけど、秘密鍵は機密データなので、平文でリポジトリに含めることはできない。だから、travis encrypt-file コマンドで秘密鍵を暗号化したものをリポジトリに含める。復号は Travis CI 側で行うが、その際に必要な情報 (key: 暗号化鍵、IV: 初期化ベクトル) は環境変数の形で Travis CI のデータを作るリポジトリに対する設定に登録する。複合に必要な情報が Travis CI のログなどに含まれないように注意する必要がある。

適当な機密データが含まれるファイルを作る。

$ echo super_secret > super_secret.txt

ファイルの準備ができたら Travis CI の CLI クライアントを使って Travis CI からログアウト。

$ travis \
	logout \
	--org \
	--debug \
;
** Loading "/****************/.travis/config.yml"
** Timeout::Error: execution expired
Successfully logged out!
** Storing "/****************/.travis/config.yml"

その後、今回機密データを含めたいリポジトリに管理者で Travis CI にログイン。

$ travis \
	login \
	--org \
	--debug \
;
** Loading "/****************/.travis/config.yml"
** Timeout::Error: execution expired
** Loading gh
** GET "config"
**   took 0.24 seconds
We need your GitHub login to identify you.
This information will not be sent to Travis CI, only to api.github.com.
The password will not be displayed.

Try running with --github-token or --auto if you don't want to enter your password anyway.

Username: ********
Password for ********: ********************************
** GitHub API: POST /authorizations
**   took 0.89 seconds
** GitHub API: GET /user?per_page=100
**   took 0.8 seconds
** POST "auth/github" {:github_token=>"****************************************"}
**   took 0.94 seconds
** GET "users/"
**   took 0.23 seconds
Successfully logged in as ********!
** GitHub API: DELETE /authorizations/********
**   took 0.89 seconds
** Storing "/****************/.travis/config.yml"

travis encrypt-file を使って機密データが含まれるファイルを暗号化。--debug をつけることで詳細な情報が得られるが、ここでわかることは、--repo で指定したリポジトリに対する環境変数を設定しているということ。この情報は Travis CI の Settings から見ることができる。とは言うものの、環境変数に設定される値は見れない。つまり、super_secret.txt.enc を復号したければこれらの情報を保存しておく必要がある。

$ travis \
	encrypt-file \
	super_secret.txt \
	super_secret.txt.enc.travis \
	--repo ********/**************** \
	--debug \
;
** Loading "/****************/.travis/config.yml"
** Timeout::Error: execution expired
** Loading gh
** GET "config"
**   took 0.22 seconds
** GitHub API: HEAD /repos/********/****************
**   took 0.77 seconds
** GET "repos/********/****************"
**   took 0.22 seconds
encrypting super_secret.txt for ********/****************
storing result as super_secret.txt.enc.travis
storing secure env variables for decryption
** GET "settings/env_vars/?repository_id=*******"
**   took 0.26 seconds
** POST "settings/env_vars/?repository_id=*******" "{\"env_var\":{\"public\":false,\"name\":\"encrypted_************_key\",\"value\":\"****************************************************************\"}}"
**   took 0.23 seconds
** GET "settings/env_vars/?repository_id=*******"
**   took 0.22 seconds
** POST "settings/env_vars/?repository_id=*******" "{\"env_var\":{\"public\":false,\"name\":\"encrypted_************_iv\",\"value\":\"********************************\"}}"
**   took 0.22 seconds

Please add the following to your build script (before_install stage in your .travis.yml, for instance):

    openssl aes-256-cbc -K $encrypted_************_key -iv $encrypted_************_iv -in super_secret.txt.enc.travis -out super_secret.txt -d

Pro Tip: You can add it automatically by running with --add.

Make sure to add super_secret.txt.enc.travis to the git repository.
Make sure not to add super_secret.txt to the git repository.
Commit all changes to your .travis.yml.
** Storing "/****************/.travis/config.yml"

先のコマンドの出力で説明されているとおり、暗号化された機密データを復号する方法は以下。実際、super_secret.txt と super_secret.txt.enc.travis.dec.openssl の内容は同じ。

$ openssl \
	aes-256-cbc \
	-d \
	-K **************************************************************** \
	-iv ******************************** \
	-in super_secret.txt.enc.travis \
	-out super_secret.txt.enc.travis.dec.openssl \
;
$ cmp \
	super_secret.txt \
	super_secret.txt.enc.travis.dec.openssl \
;

Travis CI のクライアントから復号することも可能。こちらでも super_secret.txt と super_secret.txt.enc.decrypt の内容は同じ。

$ travis \
	encrypt-file \
	super_secret.txt.enc.travis \
	super_secret.txt.enc.travis.dec.travis \
	--decrypt \
	--repo ********/**************** \
	--key **************************************************************** \
	--iv ******************************** \
	--debug \
;
** Loading "/****************/.travis/config.yml"
** Timeout::Error: execution expired
** GET "repos/********/****************"
**   took 0.21 seconds
decrypting super_secret.txt.enc for ********/****************
storing result as super_secret.txt.enc.decrypt
** Storing "/****************/.travis/config.yml"
$ cmp \
	super_secret.txt \
	super_secret.txt.enc.travis.dec.travis \
;

ここで重要なのは、リポジトリに暗号化された機密情報を登録して、これを復号するために必要な情報を Travis CI のこのリポジトリに対する環境変数の形で登録して、ビルド中にこの環境変数を使って復号するという点。この点を守っていれば、Travis CI のクライアントを使う必要もない。

例えば以下のとおり同じ key と IV を使えば openssl を使っても Travis CI クライアントと同じ暗号化ファイルができる。

$ openssl \
	aes-256-cbc \
	-e \
	-K **************************************************************** \
	-iv ******************************** \
	-in super_secret.txt \
	-out super_secret.txt.enc.openssl \
;
$ cmp \
	super_secret.txt.enc.travis \
	super_secret.txt.enc.openssl \
;

すなわち、Travis CI クライアントと同じように super_secret.txt から super_secret.txt.enc.openssl への暗号化して、super_secret.txt.enc.openssl から super_secret.txt.enc.openssl.dec.openssl への復号して、最後に super_secret.txt と super_secret.txt.enc.openssl.dec.openssl が同じことを確かめるには以下。ここで出来た super_secret.txt.enc.openssl をリポジトリに登録して、Travis CI の web インターフェイスから key と iv に適当な名前をつけて環境変数登録して、あとはビルドスクリプトまたは .travis.yml の中でその変数を使って復号化を行えば良い。

$ openssl \
	enc \
	-aes-256-cbc \
	-e \
	-salt \
	-K $(openssl rand -hex 32) \
	-iv $(openssl rand -hex 16) \
	-in super_secret.txt \
	-out super_secret.txt.enc.openssl \
	-p \
;
salt=78899C93FF7F0000
key=25AD28C1C0AD328EBA9309B4E4CD0AE0A9CB6E903485409F5102673F1DD7A870
iv =CABE7CF0F1D4F2C6ACE54B6A36AF6D1F
$ openssl \
	enc \
	-aes-256-cbc \
	-d \
	-salt \
	-K 8A7CDBE0E5E3F4D3FA3BAA6DF401BABCD6B2380871F6CFD27602038D3F424FE4 \
	-iv B4EC0D39F71071AFA2CAC8965D7C427B \
	-in super_secret.txt.enc.openssl \
	-out super_secret.txt.enc.openssl.dec.openssl \
	-p \
;
salt=78EA5506FD7F0000
key=8A7CDBE0E5E3F4D3FA3BAA6DF401BABCD6B2380871F6CFD27602038D3F424FE4
iv =B4EC0D39F71071AFA2CAC8965D7C427B
$ cmp super_secret.txt super_secret.txt.enc.openssl.dec.openssl

まずは機密データファイルに相当する GitHub のリポジトリに登録する deploy key を作成。

$ ssh-keygen \
        -t rsa \
        -b 4096 \
        -f ./deploy_key \
        -C 'travis@travis-ci.org' \
;
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ./deploy_key.
Your public key has been saved in ./deploy_key.pub.
The key fingerprint is:
**:**:**:**:**:**:**:**:**:**:**:**:**:**:**:** travis@travis-ci.org
The key's randomart image is:
+---[RSA 4096]----+
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
+-----------------+

次に deploy key の秘密鍵を暗号化する。

$ openssl \
        enc \
        -aes-256-cbc \
        -e \
        -salt \
        -K $(openssl rand -hex 32) \
        -iv $(openssl rand -hex 16) \
        -in ./deploy_key \
        -out ./deploy_key.enc.openssl \
        -p \
;
salt=****************
key=****************************************************************
iv =********************************

ここで表示された key と iv を Travis CI のリポジトリ Settings ページの Environment Variables に追記する。この時、Display value in build log を OFF にしておく。以下ではそれぞれ _DEPLOY_KEY_DECRYPT_KEY と _DEPLOY_KEY_DECRYPT_IV のような環境変数として設定したとする。

.travis.yml には以下の内容を追記して、build_script.sh が呼び出される前に復号した deploy_key を ~/.ssh 以下に配置しておく。umask 0077 は openssl の作る新しいファイルのパーミッションを一時的に0644から0600にするために必要。unset は _DEPLOY_KEY_DECRYPT_KEY と _DEPLOY_KEY_DECRYPT_IV を環境変数から削除するために必要。

(snip)
before_install:
  - (umask 0077; openssl enc -aes-256-cbc -d -salt -K "${_DEPLOY_KEY_DECRYPT_KEY}" -iv "${_DEPLOY_KEY_DECRYPT_IV}" -in .ssh/deploy_key.enc.openssl -out ~/.ssh/deploy_key;)
  - unset -v _DEPLOY_KEY_DECRYPT_KEY _DEPLOY_KEY_DECRYPT_IV
script:
  - build_script.sh 
(snip)

build_script.sh 内で gh-pages へ push するが、その際のコマンドは以下の様に使う。重要なのは環境変数 GIT_SSH_COMMAND を使って push 時に使うコマンドを指定しているという点。このようにすることで、一時的に push する際に使う鍵を ~/.ssh/deploy_key にすることが可能。

(snip)
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no -i ~/.ssh/deploy_key" \
git \
	push \
	--force \
	github \
	gh-pages \
;
(snip)

最後に push 先の GitHub リポジトリの設定画面から、deploy_key に対応する公開鍵 deploy_key.pub を登録する。

機密データを複合するために必要な情報が環境変数に収められているということから、printenv などの方法で簡単に参照することが可能。この点は注意が必要。秘密鍵の復号に必要なデータの内、key や iv が流出した場合は、公開鍵を GitHub から削除する必要がある。

リファレンス

  1. OpenSSL
  2. Travis CI - API Reference
  3. Encrypting Files - Travis CI
  4. add encrypt-file command, see #41 · travis-ci/travis.rb@4fc18f6 · GitHub

ソーシャルブックマーク

  1. はてなブックマーク
  2. Google Bookmarks
  3. del.icio.us

ChangeLog

  1. Posted: 2007-11-26T17:01:55+09:00
  2. Modified: 2007-11-26T17:01:55+09:00
  3. Generated: 2018-06-06T23:09:13+09:00