rcmdnk's blog
Last update

暗号化 プライバシーを救った反乱者たち

TravisCI を色々なレポジトリに導入して遊んでいて、 GitHubにテストでpushする必要が出てきたので色々やってみたことについて。

Sponsored Links

GitHubへpushするのに必要なもの

普段手元のPC等からは公開鍵認証を使っています。

Generating SSH keys

ですが、勿論Travisにレポジトリを送った際に作られるテスト環境には 鍵が置いてないので何らかの方法で認証を行う必要があります。

また、別の方法として、Access Tokenを作って それを用いてアクセスする方法もあります。

Creating an access token for command-line use

Easier builds and deployments using Git over HTTPS and OAuth

これらいずれの場合もTravis側に鍵を送ったりTokenを知らせる必要があるわけですが、 そのままファイルをレポジトリに追加したりTokenを.travis.ymlに書いたりすると、 GitHubの公開レポジトリの場合には大変なことになります。

そこでTravisでは文字列やファイルを暗号化して送れる様なシステムが用意されています。

Travisのコマンドラインツールを用意する

暗号化を行うにあたってTravisのコマンドラインツールが必要になるので インストールする必要がります。

Ruby1.9.3以上が必要で、インストールにはgemコマンドを使って

$ gem install travis

とすればOK。

インストールが済んだら自分のTravisアカウントと手元のマシンのtravisをつなげるために

$ travis login --auto

というコマンドを行います。 --autoを使うと .netrcで設定してあるアクセストークンを探したり、 Macの場合にはKeyChainに認証の情報があればそれを用いて自動でセットアップしてくれます。

--autoをつけなかったり、それらで認証出来ない場合には、 GitHubのユーザー名やらパスワードやらを聞かれて、 それを使ってアクセストークンを作ってTravisと繋げられる様にします。

最初に実行した時に

Shell completion not installed. Would you like to like to install it now? |y| 

みたいな事も聞かれて、yと返事すると~/.bashrcなんかに

# added by travis gem
[ -f $HOME/.travis/travis.sh ] && source $HOME/.travis/travis.sh

といった行が加えられると同時に ~/.travis/travis.shも作成されます。 この設定はtravisコマンドにおける補完の設定です。

travis-ci/travis.rb

ファイルを暗号化

公開鍵認証を使ってGitHubにTravisのジョブからアクセスする方法についてです。 こちらは今年の8月にtravis 1.7.0から追加された方法です。

鍵の用意

まず、専用の鍵を作ります。

$ ssh-keygen -t rsa -C "for test deploy"

パスフレーズ無しで、出力先は適当に$HOME/.travis_rsaとでもして作成します。 (実際に打ち込むときは$HOMEとか~等は解釈してくれないので/Users/…とベタ書きする必要があります。 もしくは$HOMEにいってファイル名だけでも$HOMEに出来ます。)

パスフレーズを入れてしまうとTravisのジョブ内で止まってしまうので無しで。 どうしても入れたい場合には下に書く文字列を暗号化する方法で Travisに渡してあげて、さらにssh使用時にきちんと渡してあげる必要があります。

これで$HOME/.travis_rsa$HOME/.travis_rsa.pubができているはずです。

秘密鍵の暗号化

作成できたら秘密鍵の方を暗号化:

$ travis encrypt-file -r rcmdnk/travis-test .travis_rsa
encrypting .travis_rsa for rcmdnk/travis-test
storing result as .travis_rsa.enc
storing secure env variables for decryption

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

openssl aes-256-cbc -K $encrypted_ee14946e6582_key -iv $encrypted_ee14946e6582_iv -in .travis_rsa.enc -out .travis_rsa -d

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

Make sure to add .travis_rsa.enc to the git repository.
Make sure not to add .travis_rsa to the git repository.
Commit all changes to your .travis.yml.
$

コマンドはtravis encrypt-fileを使います。

追記: 2016/04/13

この際、

repository not known to https://api.travis-ci.org/: rcmdnk/travis-test

の様なエラーが出たら、

$ travis sync

をしてみてください。

loginを行った後に作ったレポジトリとかだと出る?っぽい感じですが、 login時にその辺の情報をを集めてるのか、 syncをするとその辺の情報の最収集をしてくれるようです。

追記ここまで

-rでTravis CIに送るレポジトリを指定します。 (Travis CIのジョブの中でアクセスしたいレポジトリではない) このレポジトリの中で使った場合のみTravis CI側で解読されるようになります。

最後に暗号化したいファイルを与えます。

上手く行けば.travis_rsa.encというファイルが出来てるはずです。

ここで出てきている

openssl aes-256-cbc -K $encrypted_ee14946e6582_key -iv $encrypted_ee14946e6582_iv -in .travis_rsa.enc -out .travis_rsa -d

の行は書いてあるとおり後で.travis.ymlに追加するのでメモっておくなりしておきます。

追記: 2016/04/13

複数の暗号化ファイルを使いたい場合には直接複数暗号化することは出来ないのでちょっと注意が必要。

追記ここまで

公開鍵をGitHubへ登録

これで準備が出来たので、実際にレポジトリに情報を追加したりしていきます。

まず、作成した公開鍵.travis_rsa.pubをGitHubに登録します。

セッティング項目にある SSH Keys へ登録しても使えますが、これだと全レポジトリにアクセスできる物になってしまいます。

Travisの方で不具合があると漏れてしまうので、テスト用の鍵は 使うレポジトリ専用にするため レポジトリにある Settings Deploy Keys から登録しておきます。

Managing deploy keys GitHub API

この際、pushしたいので、Add deploy keyを押して 鍵を登録する際、一番下にある Allow write accessにチェックを入れておきます。

このレポジトリは上でTravisに送るレポジトリとして指定したものと違うものでも構いません。

e.g.) travis-testというレポジトリをTravisと連携してpush時にテストジョブを走らせるようにしておいて、 そのテストジョブの中でoctogray_testというレポジトリにpushする場合は、 travis encrypt-fileではtravis-testレポジトリを指定して行う。

Travisに送るレポジトリでの設定

公開鍵の登録が出来たら実際に使うレポジトリをcloneするなり用意します。

まずは上で作成した暗号化されたファイルを追加 1:

$ git clone travis-test
$ cd travis-test
$ cp ~/.travis_rsa.enc
$ git add .travis_rsa.enc

次に.travis.ymlの中に、上の暗号化時に出ている表示に在る通り

before_install:
  - openssl aes-256-cbc -K $encrypted_ee14946e6582_key -iv $encrypted_ee14946e6582_iv -in .travis_rsa.enc -out .travis_rsa -d
  - cp .travis_rsa ~/.ssh/id_rsa
  - chmod 600 ~/.ssh/id_rsa

といった感じに、before_installのブロックに暗号解読のコマンドを入れます。 さらに、解読して出来た.travis_rsaをデフォルトキーとして~/.ssh/id_rsaへコピーします。

複数の鍵を使いたい場合にはその度、~/.ssh/id_rsaへ順にコピーするか、 ~/.ssh/configを書き換えるコマンドを用意する必要があります。

追記: 2014/11/3

git clone [email protected]:/rcmdnk/travis-test

みたいな、ssh([email protected]:...)を使ったgitのcloneを行いたい場合には

- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config

before_installブロックに加えておく必要があります。 StrictHostKeyChecking noを指定しておかないと、 github.comはデータベース(~/.ssh/known_hosts)に無いホストなので加えて良いか 聞かれるプロンプトが出てくるわけですが、ここで止まってしまいます。

また、OS Xではこの部分が外からsshでつないだりして作業を行ったりする場合には 機能せずにエラーを出します。 Travisでの作業はこの様な状態にあたるらしく、 StrictHostKeyChecking noしないでknown_hostsに無いホストへ sshで接続しようとすると

remote: ssh_askpass: exec(/usr/libexec/ssh-askpass): No such file or directory

というエラーが出てしまいます 2

ということで、git clone[email protected]:…で使いたい場合には StrictHostKeyChecking noの設定が必須です。 (他にもssh的な作業をしたいなら同じくそれらのホストに対して必須です。)

もし、鍵をid_rsa以外の名前で置いておきたい場合には

- echo -e "Host github.com\n\tStrictHostKeyChecking no\nIdentityFile ~/.ssh/travis_rsa\n" >> ~/.ssh/config

みたくgithub.comに対する鍵を指定しておけばOK。

追記ここまで

また、~/.gitconfigなんかも当然無いので、gitのコミッター名等をenv: global:

env:
  global:
    - GIT_COMMITTER_NAME=rcmdnk
    - [email protected]
    - GIT_AUTHOR_NAME=rcmdnk
    - [email protected]

の様に加えておきます。

これで、

script:
  - git checkout -b test
  - git branch
  - touch testfile
  - echo "test1" >> testfile
  - rm -f .travis.yml
  - git add -A
  - git commit -a -m "test1 commit"
  - git push -f [email protected]:rcmdnk/travis-test test

と、sshを使ったレポジトリ指定([email protected]…)でのpushを書いておけばTravis CIの中からtravis-testに向けてpushが出来ます。

encrypt-fileを使うときの注意点

絶対に秘密鍵そのものをレポジトリに追加しない

上では$HOMEに鍵を作って後でコピーして、みたいなことをしてますが、 これは間違って秘密鍵をレポジトリに追加しないようにするためです。

encrypt-fileコマンドはレポジトリ内で行うと -rが省略できて自動的に今いるレポジトリで暗号化を行うようになりますが、 間違ってgit add -Aとかすると秘密鍵も登録されてしまうので 鍵ファイルの扱いは他の場所で行った方が良いかな、と。

encrypt-fileは実行するたびに新しい暗号化を行う

encrypt-fileコマンドを行うと、 同じ場所、同じファイルに対しては 出てくるコマンド(openssl...)は全て同じですが、 作成される*.encファイルは新しくなっています。

なので、コマンドを打ったら必ず*.encファイルも再びcommit/pushする 必要があります。

1つのディレクトリで1つのファイルしか使えない?

きちんとしたDocumentが見つからないのでやってみて、ですが、 encrypt-fileに関しては、 -K $encrypted_ee14946e6582_keyのパスワードの部分は 同じディレクトリで行う限り全て同じ文字列が出てきます。

これはファイルを変えても同様。

これが、他のディレクトリ(行いたいレポジトリの内外問わず)で行うと 違う文字列になります。

これでちょっと試してみたところ、2つのファイルを暗号化したい時に、 同じディレクトリでそれぞれ暗号化してTravis CIに送ると上手く行きませんでした。 最初に暗号化した方のコマンドが解読できない、と言ったエラーを出します。

一方、それぞれ違うディレクトリに持って行って暗号化すると 両方共きちんと解読してファイルを元に戻してくれました。

その他

下にある共通の注意も。

文字列を暗号化

アクセストークンを使ってGitHubにTravisのジョブからアクセスする方法についてです。

アクセストークンの用意

専用のアクセストークンをGitHubのプロファイルページのApplications から作成します。

Creating an access token for command-line use

select scopesの項目では余計なものは外してpublic_repoだけにチェックしておきます。

アクセストークンの場合はレポジトリ毎等に指定できないので 全てのレポジトリが対象になるのでさらに注意が必要。

アクセストークンの暗号化

travisではtravis encryptというコマンドで文字列を暗号化出来ます。

$ travis encrypt -r rcmdnk/travis-test GH_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Please add the following to your .travis.yml file:

  secure: "GYsF6tslBPX6OG066tgHROhTdK0TvVwrPryozk+lNkaVh6Si0RWp0WX8MTFGz1IJzigIIgE6xfqvqYE30b1Xmgn5aKDNoIOr02yhHoI63T/sxtImVo2ieP2bdi62LTvnjxtD8zs3U2d334lo7kYA3SzYfiP349vyfvue8Eibg+E="

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

ただし、このsecure...の部分がGH_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX に変換され、それ自体がコマンドとして実行される形になるため、 ここにあるように、変数に暗号化したい文字列を代入するコマンド 全体を与えて暗号化する必要があるので注意です。

これを行うことでコレ以降で$GH_TOKENという値でトークンを参照できる様になるわけです。

Travisに送るレポジトリでの設定

上に書いたように、出力されたsecure...の部分を .travis.ymlenv: globalに設定します。 gitのコミッター情報等と共にこんな感じ。

env:
  global:
    - GIT_COMMITTER_NAME=rcmdnk
    - [email protected]
    - GIT_AUTHOR_NAME=rcmdnk
    - [email protected]
    - secure: "OHol8QUh2GtqoMk4VLExaVg2iRdkvP7typryQaY4Mh/Kk2k4kcz326uD6BoUkidgjcAb0cdK4Yvf04Tas2Q9sdCSSh6Qp8JUaZyB4XwBZiJ9yadAMUl7Sui3t8wEJ4ZVWL6SX6a+XVNCasvZsJGuKTkXdt6NpDWAV1f/6UEM5bU="

後は、scriptブロック内で

script:
  - git checkout -b test
  - git branch
  - touch testfile
  - echo "test2" >> testfile
  - rm -f .travis.yml
  - git add -A
  - git commit -a -m "test2 commit"
  - git push -f https://${GH_TOKEN}@github.com/rcmdnk/travis-test test >& /dev/null

と言ったトークンを使ったレポジトリ指定をしてpushすればOK。

下のログはファイルの暗号化と両方一緒に試した時のTravisのログです。

encrypt/encrypt-file test

注意点

–quietが必須、もしレポジトリが存在しない可能性があるならエラーも出力をさせない

トークンを使った方法の問題点として、 暗号化された物自体は変数代入の式にされた後は、ただの変数として扱われるため、 そいつをechoしたりすると普通に中身が見れます。

これだけならencrypt-fileをした場合でも戻したファイルをcatすれば。。。 と同じ事ですが、問題は上の様にpushする時、通常

$ git push -f https://${GH_TOKEN}@github.com/rcmdnk/travis-test test
Counting objects: 4, done.
Delta compression using up to 32 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 305 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://[email protected]/rcmdnk/travis-test
ee2c7a9..e05547a test -> test

の様にコマンド出力にレポジトリ名が出てしまうのでtravisのログで公開されてしまいます。

これを抑えるためには--quietを使います。

$ git push --quiet -f https://${GH_TOKEN}@github.com/rcmdnk/travis-test test

ですが、もし、この送り先のレポジトリが存在しないと、

$ git push --quiet -f https://${GH_TOKEN}@github.com/rcmdnk/travis-test test
fatal: 'https://[email protected]/rcmdnk/travis-test' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

の様にエラーの中にレポジトリ名が出てしまいます。 トークンは全レポジトリ共通なのでここで見えてしまうと他のレポジトリへも 被害が及ぶ可能性があります。

なので、トークンを使ってpushする際には、確実に存在してるレポジトリ (例えばTravisに送るレポジトリ自身の場合)は少なくとも--quietを、 基本的には>& /dev/null等を最後に加えて出力を全部捨てる様にしておく必要があります。

git pushコマンドをレポジトリ内のスクリプト等から使っていないか注意

.travis.ymlに直接書くコマンド以外でも レポジトリ内のスクリプトなどで、レポジトリを指定して、 その後、そこへpushするようなものがあると、 そこでもレポジトリがログに表示されてしまいます。

その辺もそのようなコマンド全体の出力を捨てる様にするか、 スクリプトの中でトークンを使ってる場合には出力させないようにするなり きちんと対処しておくことが必要です。

その他

下にある共通の注意も。

encrypt/encrypt-file共通の注意

travis encrypt/encrypt-fileでの–addの罠

上でtravis encrypt-fileを実行した際にも

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

と表示されてますが、使いたいレポジトリのトップディレクトリに居る時に このコマンドを使う場合には--addというオプションを与えることで 自動的にopenssl...の部分を.travis.ymlに加えてくれます。 (enctyptでも同様)

ですが、これは単に書き足すだけでなくてファイル全体を書き直し、 インデント等を勝手に変える上、コメント行も全て削除してしまいます。

また、ちょっと複雑な複数行に渡るコマンドなども消されたり 使えない形に変えたりもすることがあります。 #がコマンド中に使われていると、コレ以降を強制的にコメントとみなすのか この場合には余計に改行してコメント行にしたりもします。

なので、便利なオプションなのですが、一番最初に.travis.ymlを作成する時など以外は 使わずに直接自分で加えた方が無難です。

.travis.yml内にはX: *という文字列は使えない?

最初、上の--addオプションを使っている時に気づいたことですが、 あるレポジトリで

$ travis encrypt GH_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  --add
(.travis.yml): did not find expected alphabetic or numeric character while scanning an alias at line 40 column 5
for a full error report, run travis report --org

の様なエラーが出ました。 --addを外すと普通に暗号化出来るのでなんでか、と調べて行った所、 .travis.yml内のコマンドに、X: *(:の前に最低何か1文字の後、空白でアスタリスク)という文字列があると このエラーが出る様でした。

恐らく--addでは一度.travis.ymlを読み込んで整形して出力するので、 その際に*なんかが上手く扱えて無いのだと思いますが、 *だけならOKですが、上の様な組み合わせだとダメです。

ただし、全体がクォートされているコマンド行であれば大丈夫でした。

なので、特に*を使う様なコマンド行では問題が無い限り全体をクォートして置いた方が 良さ気。

ただし、

- sed -i"" "s/<\!-- *more *-->/This is test post."\\$'\n'"<\!-- more -->/" $post

こんな感じの途中で改行コードを含んだりするようなコマンドだと クォートしたりすると上手く行かないので注意。

aliasは使えない?

暗号化に関してではなくてTravis全体での話ですが、 変数は持続して使えるので、aliasなんかも使えるもんだと思ってましたが、 aliasは行を跨いでは使えない様です。

pull requestからの操作について

The Travis CI Blog: Secure Environment Variables Now Available in Pull Requests

これを見るとPull Requestの場合でも暗号化されたものを使うことが出来る、 とあります。

Pull Requestなので、勝手に.travis.yml自体をいじれるので、 echo $GH_TOKENみたいなことも簡単に出来るわけですが、 この時点では色々そういった点を克服出来た、とのこと。

この中で

as long as the origin of the pull request and the target repository are the same.

とあるので、Pull Requestの場合にはTravisに送るレポジトリ自身限定、 と言った感じですが、それでも、例えばmasterレポジトリへpush -f などとしてしまえば過去の履歴ごと消せたりしてしまう訳で結構危険。

後、--quietとか書いておいてもそれを外してPull Requestされたら?なども。

その辺どうなってるんだろう、と思って色々テストしてみたところ、 どうも現在はPull Requestでは一切暗号を解読出来ない様になってるみたいです。

Blogのポストではそれ以降この事に触れられていないのですが、 一応、暗号化の説明のところでは使えないよ、との記述に少なくとも現在はなっています。

Travis CI: Encryption keys

Travis CI: Encrypting Files

GH_TOKENに関するsecureの記述があっても Pull Requestでは変数は空のママです。

Travis無限ループ

GitHubにpush出来る、と言うことは、Travisに送るレポジトリの 自分自身のブランチにもpush出来るということ。

ですが、TravisはGitHubにpushされた瞬間にジョブを作るので、 そのジョブの中で自分自身にpushしたら。。。 と言う話。

流石にこんな誰でも簡単にやりそうな事は対処されてるだろう、 と思ってやってみました。

適当にトークンを作って

.travis.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
language: bash
env:
  global:
    - GIT_COMMITTER_NAME=rcmdnk
    - [email protected]
    - GIT_AUTHOR_NAME=rcmdnk
    - GIT_AUTHOR_EMAIL=rcmd[email protected]
    - secure: "..."
script:
  - echo "test1" >> testfile
  - git add -A
  - git commit -m "test commit"
  - git push --quiet -f https://${GH_TOKEN}@github.com/rcmdnk/travis-test master

こんな感じにしてpush。

として、Travisのページを見てみると恐ろしい勢いで新しいBuildが出来ていました。。。

外からはどうすることもできなくなるので、Travisのページへ行って、 現在実行中のBuildへ素早く移動して右上の停止ボタンを押して対処。

普通に無限ループに陥ってちょっとビビリましたcold_sweat

上の方でやってる例ではtravis-testレポジトリでは設定で .travis.ymlが無いブランチではTravisジョブを実行しない、 という設定にしてあって、 .travis.ymlを消してからpushを行っています。

追記: 2014/11/22

上ではブランチを変更する事で対処してますが、 コミットメッセージに[skip ci]もしくは[ci skip]が含まれている 場合はTravis CIはテストを実行しません。

Travis CI: How to skip a build)]

なので、上で

- git commit -m "test commit [skip ci]"

としてあげればこのコミットが含まれるpushはTravis CIが無視することになるので 上の様な問題は起こりません。

.travis.ymlの中でGitHubにpushするような場合は 取り敢えずコミットメッセージに[skip ci]を含む様にしておいた方が無難です。

追記ここまで

ファイルをencryptを使って暗号化

encrypt-fileコマンドが出来る前の話ですが、 以前は

Travis CI: Encrypting Files

の下の方にあるように、ファイルを暗号化するときのパスワードを Travisへ暗号化して渡して、暗号を解く、と言った方法や

squeezing private SSH key into .travis.yml file

こちらの様に、秘密鍵の内容そのものを暗号化してしまう、と言った方法も。 この場合には内容が長すぎて1つのsecureだと溢れてしまうので 2つに分けて送るなど。 こちらの方は暗号化したファイル、と言う存在が必要なくなる利点はあります。

ですが、いずれにしろencrypt-fileコマンドが出来た以上、 余程特殊な事情がない限りもう必要無いものです。

まとめ

取り敢えず色々やってみましたが、 encrypt-fileコマンドが出来たので鍵の登録も簡単になって、 公開鍵認証を使うやり方がレポジトリを限定できたり、 ふと間違えて暗号化されたものを表示してしまう可能性も小さいので 良さ気です。

Sponsored Links
Sponsored Links

« Macでウィルス対策ソフトSophosを入れてみた, Proxyを使ったりする際の注意点 OS X Yosemite 10.10にアップデートして気になったこととか »