2014年4月17日木曜日

[linux]sshで鍵認証を行う際、commandに任意の引数を与えたい!

自分は、以前は{Windows命}だったのですが、最近linuxを使うようになって、便利なツールやコマンドが大量にあるので、{あれ、もしかしてなんか自動でやらせたりする時にはすっごく便利かも}と思うようになってきました。
もちろん、コマンドがいっぱいあり過ぎてとても全貌はつかめないのですが、自分が思いつくような処理はだいたいやる方法があります。
今回は、あるマシンから別のマシンのコマンドをある程度セキュリティを保ちながら、ビシバシ自動で実行するというところまで、結構大変だったのでメモをだらだらと残します。

あるクライアントマシン(以下C)からサーバー(以下S)上のコマンドをC上のプログラムやらcronやらから自動で実行したいとします。
話が長いので先にまとめると、
・ CからSのいろんなコマンドを自動で実行したい。
→ Sにログインするとき、自動でsshのパスワード入力ができなくて困る。
→ パスフレーズ無しの秘密鍵を使用したssh鍵認証を使用する。
→ パスフレーズ無しなので、秘密鍵ファイルさえあればS上でやりたい放題なので困る。
→ Sのauthorized_keysにcommandを書いてそのコマンドしか実行できないようにする。
→ CからSに投げたいコマンドは1つじゃないし、パラメータとかも変えたいので困る。
→ sshの機能を使いCからSに環境変数を渡して解決。

1: ssh鍵認証でログインできるようにする。
まず、C上で、公開鍵と秘密鍵を作成します。(参考にしたページ)
client$ ssh-keygen -t rsa
# 途中で秘密鍵のパスフレーズを聞かれるので、ただエンターすればパスフレーズ無しになる。 
# ホームの.sshにできた、id_rsa.pubが公開鍵で、id_rsaが秘密鍵。
今度は、Sに先ほどCで作った公開鍵(id_rsa.pub)をどうにかして持っていって、ログインしたい「Sのユーザー」のホーム下の.ssh/authorized_keys
の一番最後に、id_rsa.pubの内容をコピーします。
.sshディレクトリやauthorized_keysがない時は、
server$ cd
server$ mkdir .ssh
server$ chmod 700 .ssh
server$ touch .ssh/authorized_keys
server$ chmod 600 .ssh/authorized_keys
# 他のユーザーが中身を見られないようにちゃんとchmodしておく。
これでクライアント側から
client$ ssh -i ~/.ssh/id_rsa user@serverAddress 
なんてやるとパスワード入力無しでログインできます。
ただし、秘密鍵がパスフレーズ無しなので、id_rsaを持っているひとはS上でやりたい放題になってしまい危険です。

2: 鍵認証でログインしたユーザーが好きなコマンドを打てないようにする。
これは、Sの~/.ssh/authorized_keys にオプションを書くことにより行います。
authorized_keysというファイルは、かなり高機能なオプション設定ができまして、このファイルをいじるだけで、「使用される公開鍵ごとに」接続ホストを限定できたり、対話コンソール禁止にしたり、ポートフォワーディングを禁止にしたりいろいろできます。
接続ホストを限定すれば、だいぶ安心なのですが、それだと誰かが勝手にCのマシンを使った時にパスワード無しでSに侵入できるので、今回は、commandオプションを使います。
commandオプションを設定すると、sshで接続してきたら特定のコマンド(サーバー側で設定)だけを実行して接続を切るようになります。sshコマンドで実行コマンドが指定されていても無視します。(参照:sshdのman)
オプションを書く場所は、authorized_keysの先ほどの公開鍵をコピーした行の先頭で、例えば
command="ls -al" ssh-rsa AAABBBCCC...
# ssh-rsa以降が公開鍵部分
としておくと、クライアント側から
client$ ssh -i ~/.ssh/id_rsa user@serverAddress 
なんてやると、ログイン先のホームディレクトリの内容がずらずらと出て、すぐにSからログアウトされます。
これで、誰かが秘密鍵を使ってログインしても好きなことが出来なくなります。
しかし、この状態だと自分もcommandで指定したコマンドしか実行できないし、コマンドに可変で引数とか渡せないので不便です。

3: sshの機能でサーバーに環境変数を渡す。
sshの機能でCからSに環境変数を渡すことができます。(ここのページをヒントにしました。)
まず、S側の/etc/ssh/sshd_config を下のようにいじります。
# 環境変数の変更を許可する設定
PermitUserEnvironment yes
# クライアントから値を受け取る環境変数の指定
# SSH_ARGは自分でテキトーに決めた環境変数名
AcceptEnv SSH_ARG
PermitUserEnvironmentはyes/noなので、値を上書きします。
AccveptEnvは、上書きでなく追加してください。追加した環境変数がクライアントから変更可能になります。
なお、sshd_configを変更したらsshdの再起動が必要です。
これで今度は、authorized_keysのcommandを、例えば
command="echo SSH_ARG=$SSH_ARG" ssh-rsa AAABBBCCC...
# ssh-rsa以降が公開鍵部分
としておくと、クライアント側で
client$ env SSH_ARG=AHO ssh -i ~/.ssh/id_rsa -oSendEnv="SSH_ARG" \
     user@serverAddress  // ここまでコマンド
SSH_ARG=AHO  // ← これが表示結果
client$ 
となり、めでたく環境変数が渡せました。もちろん複数の環境変数も渡せます。
ここで、例のechoの代わりに自分で書いたscript等を使えば、CからSを自由にコントロールでき、また、意図しないコマンドの実行を抑えることが出来ます。