質問: レンタルサーバにjenkinsをインストールし、仮想マシン等を利用し開発環境を構築しようとしています。 しかし、jenkinsから仮想マシンにsshでログインし、githubに対し`git pull`する段階でジョブが固まってしまいます。 --- 環境・状況 ----- **開発用ノートPC** Debian GNU/LINUX 8.0 **ゲスト仮想マシン(ノートPC内)** VM:VirtualBox 4.3.30 OS:CentOS6.6 mem:512MB HDD:8.0GB ホストオンリーアダプタ(ホストとの通信用)->eth1 192.168.56.10 NAT(インターネットに出る用) 作業ユーザ:vagrant **jenkinsサーバ(レンタルVPS・固定IP・root権限)** OS:CentOS6.6 mem:1GB HDD:50GB 作業ユーザ:jenkins **補足** ホストPC及びゲスト仮想マシンは固定IPを持っていない為、jenkinsサーバとの接続はssh forwordを使用しトンネルを掘っています。このトンネルの通信は正常に確保できています。 jenkinsサーバとgithubの連携設定は行っており、連携確認ができています。(githubにプッシュした際にjenkinsのジョブが発動できる状態になっています) jenkinsサーバからゲスト仮想マシンにjenkinsでのsshプラグインを介して正常にログインできています。 githubの鍵は、jenkinsサーバ、ゲスト仮想マシンともに別個に登録し秘密鍵を持たせています。 --- やりたいこと ------ ホストノートPCでコーディング・開発を行い、githubにpushしたタイミングでjenkinsを介して、ゲスト仮想マシンで自動的にgithubからpullしたい。 --- 行ったjenkinsジョブの設定 ---------------- * [ソースコード管理]等項目は適切に設定 * ビルド・トリガはgithubプラグインを入れ、「Build when a change is pushed to -GitHub」に設定 * ビルド環境はsshプラグインを入れ、「リモートホストでシェルを実行」を選択。ゲスト仮想マシンには秘密鍵を使用し`vagrant@localhost:10022`としてログインできるように設定(ptyを有効にしています) * 設定したシェルスクリプト `cd /var/www/html/hoge` `git pull` --- 当初この状態でジョブを発動したところ、githubに入る為の秘密鍵のパスフレーズを入れられていないため、弾かれジョブは失敗しました。 ### jenkinsのコンソール出力 ``` ~~(一部省略) ~~ cd /var/www/html/force git pull Permission denied (publickey). fatal: The remote end hung up unexpectedly [SSH] exit-status: 1 Finished: FAILURE ``` ### ssh-agentの使用 そこでゲスト仮想マシンでssh-agentを立ち上げ、予めssh-addにて鍵・パスフレーズを登録し、再度ジョブを発動させたところ、弾かれはしないのですが、ジョブが固まってしまいます。(ぐるぐるマークがずっと動いている状態) ![固まった時のコンソール出力](https://i.stack.imgur.com/zhMxW.png) --- ### 手動では成功する 開発用ノートPCからか、jenkinsサーバから(jenkinsサーバに開発用ノートPCからsshでjenkinsユーザとしてログインしてから)直接SSHでゲスト仮想マシンに入り、`git pull`すると成功します。 --- 自分の理解では 開発用ノートPC->github->jenkinsサーバ->ゲスト仮想マシン->github といった順序で動いていき、最終的にゲスト仮想マシンの秘密鍵を使ってgithubにログインしようとしているものと思っています。 おそらく今回の問題は鍵のパスフレーズをどのように入れるのかに関することとおもいますが、経験が浅く解決出来ていません。 今回のような構成がおかしいというご意見があるかと思いますが、個人的に後々にもssh forwordを利用した開発環境の構築が必要になってきますので、できれば今環境内での解決をご教授できればと思います。 よろしくお願いします。 --- ### 追記 web等で検索したところexpectというコマンドでパスフレーズを入力できることがわかりました。 早速expectでシェルスクリプトを作成したところ、パスフレーズの入力に成功し、pullが成功しました。 ``` #!/usr/bin/expect expect -c " set timeout 5 spawn git pull expect \"Enter passphrase for key '/home/vagrant/.ssh/id_rsa':\" send \"パスフレーズ\n\" interact " ``` 結果的にジョブが成功したことにはなりますが、expectではシェルスクリプトにパスフレーズを記載する必要があり、あまり好ましい状態では無いように思えます。 できればセキュリティ的にも納得の行く方法で行えたらと思っています。 引き続き、よろしくお願いします。
回答: 1. 秘密鍵のパスフレーズについて セキュリティポリシー次第ですが、個人的な見解としては次の場合を除けばパスフレーズは不要だと思っています。 * ノートPCなど盗難の可能性がある場合 * 鍵が複数人で共有されてる場合 * 鍵の管理がなされてない場合 また、ssh-agent もセキュリティレベルを下げる要因になりますので 一長一短です。 2. ssh-agent の使い方について ssh-agentを実行すると自身はバックグランドで動き ssh クライアントからの接続を待ちます。 sshクライアントから ssh-agent を利用するには環境変数が必用です。 引数無しで起動すると環境変数を設定するコマンドが表示されます。 ``` $ ssh-agent SSH_AUTH_SOCK=/tmp/ssh-xNQEOWNXVGC6/agent.8228; export SSH_AUTH_SOCK; SSH_AGENT_PID=8716; export SSH_AGENT_PID; echo Agent pid 8716; ``` 引数ありの場合、引数のプログラムを起動して 環境変数を自動的に設定します。 ``` $ ssh-agent bash $ ←何も表示されないけど新しいbashが起動している $ echo $SSH_AUTH_SOCK /tmp/ssh-xNQEOWNXVGC6/agent.8228 ←環境変数が自動的に設定されてる $ echo $SSH_AGENT_PID 8716 ←環境変数が自動的に設定されてる ``` ssh-agentが起動したシェルを終了するとssh-agentは停止し、環境変数は破棄されます。 ``` $ exit ← ssh-agentが起動した bashを終了 exit $ ← 元のシェルに戻った ``` 別の方法として eval 使う場合は、現在のシェルに ssh-agent が出力したコマンドを読み込ませます。「引数ありの場合」との違いは、現在のシェル上に環境変数が設定される点です。 ``` $ eval `ssh-agent` Agent pid 8716 ←ssh-agentのプロセスIDが表示される $ echo $SSH_AUTH_SOCK /tmp/ssh-xNQEOWNXVGC6/agent.8228 ←環境変数が自動的に設定されてる $ echo $SSH_AGENT_PID 8716 ←環境変数が自動的に設定されてる ``` ssh-agent を停止するには `-k` オプションを指定します。 ``` $ eval `ssh-agent -k` Agent pid 8716 killed ``` 3. Jenkinsのジョブについて `ssh-add -l` の結果が `Could not open a connection to your authentication agent` と出てるので ssh-agent に接続できてないのが確認出来ます。これはJenkinsのジョブで上述の環境変数が有効になっていないからではないかと思います。 Jenkins のジョブの中で `SSH_AUTH_SOCK` と `SSH_AGENT_PID` の環境変数を設定してみてください。ただ、ssh-agent のプロセスを再起動する度に これらの環境変数を変えなきゃいけないので ハードコーディングせず 何かの設定ファイルに書いて 読ませるのがいいかなと思います。 また、Jenkinsのジョブの実行ユーザには、ssh-gent が作成したソケットファイルへのアクセス権が必用です。適宜アクセス権を設定してみてください。 * ssh-agentの起動手順(手動で操作) ``` $ ssh-agent > /hoge/foo/bar/agent.conf ←設定ファイルに残す $ eval `cat /hoge/foo/bar/agent.conf` ←設定ファイルを読み込んで環境変数を設定 $ ssh-add .... ←鍵を追加 ``` * ソケットファイルに アクセス権を付与 次の例ではグループのアクセス許可を付与します。 ``` $ sudo chgrp jenkins $SSH_AUTH_SOCK ←ソケットファイルのグループをjenkinsに変更 $ chmod g+rw $SSH_AUTH_SOCK ←ソケットファイルにグループのRead/Writeを許可 ``` * Jenkinsのジョブ ``` $ eval `cat /hoge/foo/bar/agent.conf` ←設定ファイルを読み込んで環境変数を設定 $ git pull ```