日頃の行い

個人的な日頃の行いをつらつら書いてます\\\\ ٩( 'ω' )و ////

秘密鍵にパスワード設定してる状態で自動的にgit cloneしたいそんな時の対処方法

こんばんは。
今日はVagrantシェルスクリプトでアプリケーションの実行環境の作成してる時に困った話をします
環境作成にchefやpuppet, ansibleを使わずシェルスクリプトを使っているのですが、アプリケーションをgit cloneする時に

Are you sure you want to continue connecting (yes/no)?

とか

Enter passphrase for key '/path/to/key/private_key':

聞くなよやってるのはvagrantだy
などのプロンプトが出てきて、 vagrant up中にインストール出来ず嫌だなーと思ったので対処法を探しました。そしてたどり着いたのが、 expectコマンド

ということで、お試しにgit cloneを自動化してみました。
作ったレポジトリ

概要

expectコマンドを利用して、vagrant上に自分自身のレポジトリ tarata/expect-command-getting-started · GitHub をcloneしてきます。

準備

expectコマンドとgitをインストールします。

yum install -y expect git

やったこと

#! /bin/sh

password=`cat /vagrant/script/keys/password_for_private_key`
# パスワードはtextファイルに事前に書いておきます。
repo=git@github.com:tarata/expect-command-getting-started.git

expect_message_ssh="Are you sure you want to continue connecting (yes/no)?"
expect_message_key_password="Enter passphrase for key '/home/vagrant/.ssh/private_key_for_github': "
# 秘密鍵の設定は別で行っています。
# https://github.com/tarata/expect-command-getting-started/blob/master/script/install/vagrant/install-keys.sh


expect -c "
set timeout -1
spawn git clone $repo
expect \"${expect_message_ssh}\" {
    send \"yes\n\"
    expect \"${expect_message_key_password}\"
    send \"${password}\n\"
    expect \"${expect_message_key_password}\" {
        set timeout 1
    } \"*$\";
} \"${expect_message_key_password}\" {
    send \"${password}\n\"
    expect \"${expect_message_key_password}\" {
        set timeout 1
    } \"*$\";
}
"

if [ ! -d $HOME/`basename ${repo%.git}` ]; then
    echo "\n"
    echo "failed to git clone"
fi

主な部分はexpect -c 以降の部分です。

expect \"hogehoge\"

とやれば「hogehogeという文字が標準出力に出現したら」という意味になります。
{}を利用することで条件分岐が出来るようです。
条件分岐というとif文が出てきたのですが、イメージ的にはswitch文が近いかと思います。
なので、expectの部分をなんとなく、switchに直すと

git clone $repo
switch (標準出力) {
  case ssh時に出る出力(${expect_message_ssh})
    yes\nと打つ;
    switch(標準出力) {
      case パスワード入力(${expect_message_key_password}):
        [パスワード]\nと打つ;
        switch(標準出力) {
          case パスワード入力(${expect_message_key_password}):
            タイムアウトを1秒にする(パスワードが間違ってるため止める処理);
            break;
          default:
            break;
        }
        break;
      default:
        break;
  case パスワード入力(${expect_message_key_password}):
    [パスワード]\nと打つ;
    switch(標準出力) {
      case パスワード入力(${expect_message_key_password}):
        タイムアウトを1秒にする(パスワードが間違ってるため止める処理);
        break;
      default:
        break;
    }
}

みたいな対応かと・・・(わかりづらい・・・?w
という感じに事前に鍵の準備とパスワードをどこかに記載する必要は当然必要になりますが、プロンプトが出るようなものであったとしても自動化できます。

色々やって困ったこと

ctrl+cをしたいんだけど、どうやってsendすればよいの?

set timeout 1

です。
以下のページでpingを例にしている時に気が付きました。


expectの条件分岐を使ってるんだけど、なぜか2つ目の条件分岐に引っかからない

expect hoge {
  ...何らかの処理
} expect fuga { # expectがいらない

}

となっていた。
case文的な感じなので、expectはいらない。つまり

expect hoge {
  ...何らかの処理
} fuga {

}

みたいな感じです。
プロンプトも操れるようになったので、もうなんでも出来る気がします←
上にも書いちゃいましたが、こちらが今回作ったレポジトリです。
tarata/expect-command-getting-started · GitHub

vagrant upで一応動きは確認できます。
/path/to/project/script/keys/private_key_for_github
githubに登録した秘密鍵を置き、

/path/to/project/script/keys/password_for_private_key
にその秘密鍵のパスワードを記載し、

/path/to/project/script/install/vagrant/install-application.sh
repo変数のレポジトリを変更すれば試せるかと思います。

鍵のパスワードが無くても動く・・・はずw

追記

ssh-agentとssh_configで色々出来るよねと突っ込まれたので、メモとして書いておきます

expect_message_ssh="Are you sure you want to continue connecting (yes/no)?"
expect_message_key_password="Enter passphrase for key '/home/vagrant/.ssh/private_key_for_github':  

この部分を他の方法でどうにかできます。
前者はssh_config
後者はssh-agent

Are you sure you want to continue connecting (yes/no)? の対処

今、ssh_configは /home/vagrant/.ssh/config に書いていて、以下のようになってますが、

Host github.com
    User git
    Port 22
    Hostname github.com
    IdentityFile ~/.ssh/private_key_for_github
    TCPKeepAlive yes
    IdentitiesOnly yes

StrictHostKeyChecking noを追記すれば Are you sure you want to continue connecting (yes/no)? は尋ねられなくなります。

Enter passphrase for key '/home/vagrant/... の対処

こっちはssh-agent、または、forward-agent機能(?)によって、上手くやれそうです(できてない)
思いついたやり方は2つ

  • ゲストOS(VM)でssh-agentを実行し、ssh-addでgithub用の秘密鍵を登録する。
  • ホストOS(VMではない本体)でssh-addを行いforward-agentによって、秘密鍵をゲストOSと共有する

このいずれかの設定ができれば、git push時などにも秘密鍵のパスワードを入力しなくていいので便利です。


2つのやり方のうち前者であれば、ゲストOSで完結するのでMacWindowsに依らない点がメリットです
(なんだけど出来ない(´・ω・`)w
後者はホストOSで設定している鍵を利用できるので、ゲスト側で設定する必要が無いという点がメリットかなぁと思います。


ちなみに、Vagrantの場合Vagrantfileに以下を設定すればforward-agentの設定が出来るようです。

config.ssh.forward_agent = true

参考
ssh-agentを使ってVagrant上のゲストOSからMac側の秘密鍵を使えるようにする | Firegoby