Capistranoによるデプロイで発生したgithub接続エラーを解決する

リモートサーバにrailsアプリケーションをCapistranoでデプロイしようとしていました。

Capistranoによるデプロイの流れはザックリとこんな感じです。
f:id:WorldWorldWorld:20161113182308p:plain

  1. 自分の端末からcap production deploy を実行する。
  2. デプロイ先のサーバにて、github上のプロジェクトのcloneリクエストが行われる。
  3. githubからプロジェクトがデプロイ先サーバにcloneされる。


そうしたところ、2番目の手順でエラーが発生しました。

$ bundle exec cap production deploy
……
00:06 git:check
      01 git ls-remote --heads git@github.com:quotto/HatenaBlog.git
      01 Warning: Permanently added the RSA host key for IP address '192.30.253.112' to the list of known hosts.
      01 Permission denied (publickey). # こいつだ!
      01 fatal: The remote end hung up unexpectedly
(Backtrace restricted to imported tasks)
cap aborted!

cloneする前に「git ls-remote」でリポジトリの中身を確認しているようですが、その時に「Permission denied(publickey)」とエラーが出ています。

ということで、解決までの道のりをまとめました。

デプロイ先サーバからgithubへの接続確認

まずは、デプロイ先のサーバから本当に接続ができないのか確認してみます。

サーバのコンソールから直接コマンドを実行すると……

$ git ls-remote --heads git@github.com:quotto/HatenaBlog.git
Enter passphrase for key '/home/quotto/.ssh/id_rsa': 
87675c005b2992fe734dd54c2b560ea5c54512e5    refs/heads/master

つながるやんけ!
と突っ込みたいところですがチョット待った。

秘密鍵パスフレーズ入力を求められてます。


これ、Capistranoでのデプロイ時には求められませんでした。

つまり現状はssh経由でのgithubへの接続にパスフレーズが必要な状態であり、自動デプロイ時には秘密鍵の入力を省略するようにしなければならないようです。

ssh-agent

どうすればパスフレーズの入力を省略できるのか。

その答えは「ssh-agentに秘密鍵を登録する」というものになります。

ssh-agentは秘密鍵パスフレーズの組み合わせを預かってくれる仕組みです。
f:id:WorldWorldWorld:20161113203145p:plain
※図ではssh-agentが外部サービスのように見えますが、サーバ上のサービスです。

ssh-agentに秘密鍵を預けておくことで、リモートサーバにsshで接続する場合にssh-agentが認証を代行してくれます。
f:id:WorldWorldWorld:20161113203240p:plain

じゃあサーバ上で設定すればいいんだね!……とはいきません。

セキュリティ上の懸念

デプロイ先サーバ上のssh-agentにgithub秘密鍵を登録すれば解決するはずではありますが、その場合「サーバ上に秘密鍵を置いておく」ことになります。

外部から参照されるサーバ上にこのような大事なものを置いておくのは、セキュリティ上よろしくありません。

そこで登場するのが「エージェント転送機能」です。

エージェント転送機能で安全に接続する

エージェント転送とは、要するに「ローカル端末の秘密鍵をサーバ上で利用する」機能です。

ローカル端末上のssh-agentが管理する秘密鍵をサーバへ転送し、サーバはその秘密鍵を使ってさらに先(github)へsshで接続します。このため、サーバ上には秘密鍵を置いておく必要がありません。
f:id:WorldWorldWorld:20161113205826p:plain


では実際に設定してみます。

ローカル端末側の設定

まずは現在ssh-agentで管理している秘密鍵を確認します。

$ ssh-add -l
The agent has no identities.

何もありません。
秘密鍵の場所を指定して、ssh-agentに登録します。

$ ssh-add ~/.ssh/github_rsa
Enter passphrase for /Users/localuser/.ssh/github_rsa:  #パスフレーズを入力
Identity added: /Users/localuser/.ssh/github_rsa (/Users/localuser/.ssh/github_rsa)

# 登録されたことを確認
$ ssh-add -l
2048 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /Users/localuser/.ssh/github_rsa (RSA)

サーバ側の設定

サーバ側では、エージェント転送機能により接続を許可するホストを「~/.ssh/config」に設定する必要があります。

#~/.ssh/config
Host github
Hostname github.com
Port 22
User git
ForwardAgent yes

ポイントは一番最後の「ForwardAgent yes」ですね。

確認

まずはコンソール上から試してみます。

sshコマンドに「-A」オプションを付けることでエージェント転送機能を有効にします。

$ ssh -A hatenablog.wackwack.net
Last login: Sun Nov 13 20:49:55 2016 from pnnnnnn-ipngnnnnnxxxxxx.xxxxx.xx.xx
$ git ls-remote --heads git@github.com:quotto/HatenaBlog.git
87675c005b2992fe734dd54c2b560ea5c54512e5    refs/heads/master
$ 

パスフレーズの入力なしにgithubへ接続することができました。


さらにCapistranoでデプロイしてみます。

$ bundle exec cap production deploy
……
00:02 git:check
      01 git ls-remote --heads git@github.com:quotto/HatenaBlog.git
      01 87675c005b2992fe734dd54c2b560ea5c54512e5   refs/heads/master
  ✔ 01 localuser@hatenablog.wackwack.net 1.924s

これでバッチリです!

(参考?)Capistranoの設定ファイル

Capistranoの「config/deploy/デプロイ先.rb」には「ssh_options」という設定項目があります。
この設定項目を見る限りだと、OSのssh-agentには登録しなくともCapistranoの設定だけでエージェント転送できそうなんですが……できないんですよねぇ。

#config/deploy/production.rb
……
set :ssh_options, {
   keys: %w(~/.ssh/wackwack_id_rsa),
   forward_agent: true,
   auth_methods: %w(publickey)
 }

参考にしたリンク先を見る感じだと設定ファイルだけでいけそうなんですが……

参考にさせていただいたサイト

Githubのsshで何故かPermission denied (publickey)となる場合の設定メモ

Githubに接続できない時の対処法

capistrano3でssh agent forwarding

ssh-agent でパスフレーズの入力を省く - WebOS Goodies

ssh-agent - 公開鍵認証の鍵管理

OpenSSH[実践]入門 (Software Design plus)

OpenSSH[実践]入門 (Software Design plus)

Ruby on Rails環境構築ガイド

Ruby on Rails環境構築ガイド