CUBE SUGAR CONTAINER

技術系のこと書きます。

SSH/SCP のログイン自動化に sshpass が便利すぎた

手元で検証環境の構築なんかをするときは、何らかの形で自動化したくなる。 そんなとき、よく障壁となるのが SSH/SCP でパスワードの入力を求められるところだった。 例えば、複数のホストをまたいで操作したいときや、ソフトウェアが要件として公開鍵の設置を求めてくるときに必要となる。 そういった場面で SSH/SCP でログインするためのパスワード入力を自動化するところが、なかなか面倒くさい。 今回は、そんな折に sshpass の存在を知って使ってみたところ便利だった、という話。

操作の題材としてはローカルホストに SSH でログインすることを考えてみよう。 尚、あくまでこれはセキュアな環境で検証用の構築などを自動化するために使うことを想定している。 使い方を誤ればセキュリティ上のリスクとなるので注意してほしい。

使った環境は次の通り。

$ cat /etc/redhat-release 
CentOS Linux release 7.3.1611 (Core)
$ uname -r
3.10.0-514.21.1.el7.x86_64

SSH/SCP のログインを自動化する上での問題点

まず、当たり前だけど SSH デーモンでパスワードログインを有効にしている場合には、こんな感じでプロンプトが出る。

$ ssh localhost
vagrant@localhost's password:

あるいは、フィンガープリントが known_hosts ファイルに載っていない状態では、次のように確認される。

$ ssh localhost
The authenticity of host 'localhost (::1)' can't be established.
ECDSA key fingerprint is 2a:dd:72:1a:f5:01:be:03:cc:d2:3c:33:11:3b:77:f0.
Are you sure you want to continue connecting (yes/no)?

上記のような状況で、ログインを自動化するにはどうしたら良いか?というのが今回のポイント。 ひとまず、フィンガープリントの検証については -o オプションを使ってスキップできる。

$ ssh -o "StrictHostKeyChecking no" localhost
Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
vagrant@localhost's password: 

しかし肝心のログインパスワードの入力はというと…標準入力なんかで入れば楽ちんなんだけど、そうは上手くいかない。

$ echo "vagrant" | ssh -o "StrictHostKeyChecking no" localhost
Pseudo-terminal will not be allocated because stdin is not a terminal.
vagrant@localhost's password: 

ターミナル上での対話の自動化といえば古典的には expect がよく使われるけど、これもまた面倒くさい。 やりたいことに対してオーバースペック感がある。

sshpass を使ったログインパスワード入力の自動化

そんなとき使うと便利なのが sshpass だった。 CentOS7 なら EPEL にパッケージがあるのでインストールする。

$ sudo yum -y install epel-release
$ sudo yum -y install sshpass

あとは sshpass コマンドに -p オプションでパスワードを指定しつつ ssh コマンドを実行するだけ。

$ sshpass -p "vagrant" ssh -o "StrictHostKeyChecking no" localhost
Last login: Sat Jun  3 03:05:17 2017 from 10.0.2.2
[vagrant@localhost ~]$ 

あっさりログインできた。

もちろん SCP だってこの通り。

$ echo 'Hello, World!' > greeting.txt
$ sshpass -p "vagrant" scp -o "StrictHostKeyChecking no" greeting.txt localhost:/tmp
$ cat /tmp/greeting.txt 
Hello, World!

ばっちりだね。

ssh-copy-id のパスワード入力も自動化できる

ちなみに sshpass は公開鍵の設置の自動化にも役に立つ。 公開鍵を設置には ssh-copy-id を使うのが定石なんだけど、これのパスワード入力も代わりにやってくれる。 これも試してみることにしよう。

まずは公開鍵ペアを用意する。 ここでのポイントは -P オプションでパスフレーズを指定したり -f オプションで鍵の場所を指定すること。 こうすればインタラクティブモードにならないからワンライナーで作れる。

$ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa

あとは、この作った公開鍵をどうやって設置するか、というのが問題になる。 ssh-copy-id を使えばパーミッションがどうとかいうのを考えずに公開鍵が設置できるのでめっちゃ楽できる。 とはいえ設置するのには SSH でのログインが必要なのでパスワードを聞かれる。

$ ssh-copy-id -i ~/.ssh/id_rsa.pub -o "StrictHostKeyChecking no" localhost
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
vagrant@localhost's password:

なんと、ここの部分も sshpass で自動化できる!やったー! さっきと同じように sshpass コマンド経由で ssh-copy-id コマンドを呼び出すだけ。

$ sshpass -p "vagrant" ssh-copy-id -i ~/.ssh/id_rsa.pub -o "StrictHostKeyChecking no" localhost
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh -o 'StrictHostKeyChecking no' 'localhost'"
and check to make sure that only the key(s) you wanted were added.

これでもういくらでも SSH/SCP でのログインが自動化できるね。

$ ssh localhost
Last login: Sat Jun  3 03:15:14 2017 from ::1
[vagrant@localhost ~]$

実際に Vagrant で自動化してみる

次は実際に Vagrant を使ったユースケースを試してみることにする。

まずは Shell Provisioner で provision.sh を実行するようにした Vagrantfile を用意する。 ポイントは privileged: false を指定しているところ。 こうすることで、プロビジョニングの実行ユーザが root ではなく一般ユーザの vagrant になる。 これをやらないとファイルの権限とかパスの指定を後から付け替えることになって色々とめんどくさいことになる。

$ cat << 'EOF' > Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "bento/centos-7.3"
  config.vm.provision "shell", privileged: false do |shell|
    shell.path = "provision.sh"
  end
end
EOF

ちなみに、パスワードログインを SSHD の設定ファイルで無効にしてある Vagrant Box もたまにあるので注意しよう。

続いて上記の Vagrantfile で実行する provision.sh を用意する。 この中では localhostssh-copy-id で公開鍵を設置するところまで自動化している。

$ cat << 'EOF' > provision.sh
#!/bin/sh

set -x
set -e

: "Install sshpass(1)" && {
  sudo yum -y install epel-release
  sudo yum -y install sshpass
}

: "Generate SSH keypair" && {
  ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
}

: "Install public key" && {
  sshpass -p "vagrant" ssh-copy-id -i ~/.ssh/id_rsa.pub -o "StrictHostKeyChecking no" localhost
}

EOF

上記の設定ファイルを元にして仮想マシンを立ち上げよう。

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'bento/centos-7.3'...
==> default: Matching MAC address for NAT networking...
...(snip)...
==> default: 
==> default: Number of key(s) added: 1
==> default: 
==> default: Now try logging into the machine, with:   "ssh -o 'StrictHostKeyChecking no' 'vagrant@localhost'"
==> default: and check to make sure that only the key(s) you wanted were added.

仮想マシンが完成したらログインする。

$ vagrant ssh

すると、この状態で既に localhost にパスフレーズなしでログインできるようになっている!

$ ssh localhost
Last login: Sat Jun  3 04:10:29 2017 from 10.0.2.2
[vagrant@localhost ~]$

めでたしめでたし。

まとめ

  • sshpass を使うと SSH/SCP のログインパスワードの入力を自動化できる
  • ssh-copy-id と組み合わせて使うことで公開鍵の設置も自動化できる
  • 注意点としては、あくまでセキュアな環境で検証用にのみ使うこと