2012年4月21日土曜日

Ssh Scp Sftp の正しい自動実行方法


scp と sftp について調べていると、正しい自動実行についての文書を見つけた。cron から scp や sftp を自動実行しようと考えている私には役に立つ文書だ。

- 「専用のパスフレーズなしの鍵を作って権限限定」がベスト

正しいssh/scpの自動運転は ぴろ日記

おねがいだからパスワード入力をexpectで自動化なんつーバッド・ノウハウをWebで広めないでくれ。(略)
あとcronとかからssh/scpするのにssh-agentでパスフレーズ入力を自動運転ってのもバッド・ノウハウな。

正しいssh/scpの自動運転は:

自動運転専用の鍵をパスフレーズなしで作成する。
でもって、その鍵を使ってやれることを、いかにして必要最小限の作業だけに制限するかを考える

が正解(現時点では)。


必要最小限の作業に制限する方法としては、

毎回完全に同じコマンド(引数含めて)しか実行しないなら、authorized_keysでcommand='...'を使って実行できるコマンドをそれだけに制限。
ファイル転送用途だけなら、scponlyとかの転送用途に限定されたシェルをリモートのアカウントのシェルに設定する。
上記2つのケースには該当しないけれど、特定のコマンドに実行を制限したい時はrestricted shellとか。
可能ならchroot patchをあてたsshdを使ってchroot環境作るのも良い。
あるいはauthorized_keysのcommand='...'機能を使って強制的に特定のコマンドを実行された場合に、本来リモートから(ssh host 'cmd...'等で)与えられたコマンドラインが環境変数SSH_ORIGINAL_COMMANDに保存されるのを利用して、SSH_ORIGINAL_COMMANDを自前で安全に処理するコマンドを作るとか。

とかって方法が考えられる。さらに、どうしても必要最小限のコマンドに制限できない場合は、接続元のホストを制限した上で接続元ホストの方を守るのと、実行されたコマンドのログをacct等で確実に取る(必要ならアラートも飛ぶようにする)。


カードを分割する方法

なるほど、鍵でできることを限定することが先決か。あとは変なイベントが上がったときに検出する仕組みを作っておくってことね。
あと、意外に多い誤解が、「パスフレーズなしの秘密鍵は(パスワード認証における)パスワードなしと同じくらい危険」とか、(略)
秘密鍵のパスフレーズは秘密鍵ファイルを暗号化してるだけのもんです。逆に言うと秘密鍵ファイルを悪者に盗まれてしまったら、たとえパスフレーズが設定されていても、↑のCrack ( http://www.crypticide.com/users/alecm/ ) みたいなオフライン攻撃が可能、つまり悪者は手に入れた秘密鍵をどっか別の安心して作業ができるマシンでじっくり辞書攻撃なり総当り攻撃なり使ってパスフレーズを探すことができる。

なので、「秘密鍵を他人に渡さない」ということの方が、パスフレーズを設定することよりも遥かに重要。パスフレーズはいざという時の時間稼ぎでしかない。

それが分かれば、ssh-agentやkeychain使うより、いさぎよくパスフレーズなしにしちゃった方がいいってのも分かると思う(自動運転の場合ね)。だって、適切なパーミッションが設定されている秘密鍵ファイルを盗める人間(rootとか)なら、常駐しているssh-agent/keychainも乗っ取れるでしょ。パスフレーズ設定してる意味がない。「なんかの拍子にssh-agentのプロセスが死んじゃってて(めったに� ��いけど)、自動運転に失敗した」とか、「サーバをリブートするたびに、パスフレーズ入力してssh-agent起動しないといけない(運用担当者全員にパスフレーズ教えないといけない)」とかって、余計な手間が増えるだけ。


確かに。再起動の度に ssh-agent 起動はやだなあ。ここでまた運用ミスが発生しかねないし。だったらパスフレーズ無しの方が理にかなってる。結局セキュリティは利便性とトレードオフになることが多い。今回の件では妥当な判断だと思う。

- 自動実行用の鍵作成

というわけで実際に権限を限定してみる。今回は authorized_keys2 レベルの話。ファイアウォールとか OS の話は別次元。そういうのは ssh に限らないレベルの話なのですべてやってあることが前提。

自動実行専用の鍵を作成。-N オプションでパスフレーズを指定できる。今回は空で作成した。


"あなたを殺すだろうグラフィックス"
$ ssh-keygen.exe -t dsa -N "" -f ~/.ssh/auto_execute

生成した公開鍵 ~/.ssh/auto_execute.pub に from="*.example.com",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty オプションを付加し、リモートサーバの ~/.ssh/authorized_keys2 に追加。

下記は公開鍵の例。引用の際に改行を入れてしまっているが、実際は一行。

# 2004-11-22 追記。
# 以下の公開鍵のオプション指定では権限を限定できていない。
# 権限を限定できている公開鍵の例は、
# 後述の「できることを限定した自動実行用の鍵で ssh 接続してみる」の項を参照。

from="*.example.com",no-port-forwarding,no-X11-forwarding,
no-agent-forwarding,no-pty ssh-dss AAAAB3NzaC1kc
3MAAACBAKZhqMdcujcJgGOCIsi+PrmkEEaAy/gpRPpB3Q5OA
wNG+PwTdU5O8/NPde64wNux4XNUB7XkV2eXWfaTZRYyYe0oC
XeJMh2LKZ/a/F3Wu283uuExSZhbkt3Dcv3+u6oyjBzIfNU+P
(以下略)


2004-11-22 追記。上記の鍵に付けたオプションだけでは無意味という指摘を頂いた。

ssh scp sftp の正しい自動実行方法

えっと、authorized_keys自体を書き換えられる権限を、パスフレーズなしの鍵に与えてしまうと、結局ノーガードと同じことになってしまうです。悪い人の鍵でなんでもできるようにしたauthorized_keysをリモートにscpして一丁あがり。

あと、これも時々目にする勘違いだけど、「no-pty」は仮想端末とれなくなるだけで、シェルが取れなくなるわけじゃないので、仮想端末取らないで「ssh remotehost '/bin/sh' < 悪のスクリプト」とかやるだけの話。


指摘の通り、上記の鍵では ~/.ssh/authorized_keys2 に scp されることを防げないので意味がない。

また、command="" が無い場合、以下のようにシェルに直接コマンドを送り込むことができる。試したら authorized_keys2 の中身がローカルの端末に表示された。


ポケモン銀:どのようにumbreonを得るのですか
$ echo "cat .ssh/authorized_keys2" |ssh -i /home/hiroaki/.ssh/auto remotehost.example.com -l hiroaki

結局、commnad="" を使えない場合は、「正しいssh/scpの自動運転は」 http://www.banana-fish.com/~piro/20040609.html#p06 で提示されていた方法で権限を限定する必要がある。

- 指定したオプションの説明

sshd.0

authorized_keys ファイルの形式 から抜粋。
authorized_keys ファイルの形式

from="pattern-list"
        このオプションをつけると、公開鍵認証に加えて、クライアントのホス
        トをチェックできるようになります。カンマで区切ったリモートホスト
        名 (canonical name) のパターン列が指定できます (`*' および `?' が
        ワイルドカードとして使えます)。このリストには「〜でない」という否
        定 (negation) を入れることもできます。その場合はパターンの先頭に
        `!' をつけてください。否定つきのパターンにホストの canonical name
        がマッチした場合、この鍵は許可されません。このオプションはセキュ
        リティを上げるためにつけられました: 公開鍵認証それ自体は、(鍵を除
        いて) ネットワークやネームサーバ、その他ありとあらゆるものを信用
        しません。しかし、もし何物かが何らかの方法で鍵を盗むことができれ
        ば、その鍵を使って世界のどこからでもログインできてしまうことにな
        ります。このオプションは、そのような盗まれた鍵を使うことをより困
        難にします (もしこれを使おうとするなら、鍵のほかにネームサーバや
        ルータまでも手を入れなくてはならないからです)。

(略)


command="command"
        このオプションを使うと、認証にこの鍵が使われたときは必ずここで指
        定されたコマンドが実行されるようになります。ユーザが (訳注: クラ
        イアント側で) 指定したコマンドは無視されます。クライアント側が仮
        想端末を要求していれば、ここで指定されたコマンドは仮想端末上で実
        行されます。そうでなければ端末なしで実行されます。 8-bit クリーン
        な通信が欲しい場合は、仮想端末を要求してはいけません。あるいは
        no-pty オプションを使ってください。コマンド文字列中に引用符 (")
        を入れたいときは、バックスラッシュを前につけてください。このオプ
        ションは、ある公開鍵には特定の操作だけしかさせないようにするのに
        有効です。例として、リモートバックアップだけをさせて、それ以外な
        何もさせないような鍵がつくれます。クライアントの TCP/IP や X11 転
        送は、明示的に禁止されていない限り可能なので注意してください。こ
        のオプションはシェル、コマンドまたはサブシステムの実行に適用され
        ます。

no-port-forwarding
        認証にこの鍵が使われたときは TCP/IP 転送が禁止されます。クライア
        ントがポート転送を要求しても、すべてエラーになります。これはたと
        えば command オプションの指定されている接続などで使われます。

no-X11-forwarding
        認証にこの鍵が使われたときは X11 転送が禁止されます。クライアント
        が X11 転送を要求しても、すべてエラーになります。

no-agent-forwarding
        認証にこの鍵が使われたときは、認証エージェントの転送が禁止されま
        す。

no-pty  端末の割り当てを禁止します (仮想端末の割り当てが失敗するようにな
        ります)。



- できることを限定した自動実行用の鍵で ssh 接続してみる

前述の no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty を指定した scp/sftp 用の鍵を使い、putty で sshd に接続。TCP 接続はできるが、
Server refused to allocate pty
と表示されてシェルに入れなかった。no-pty の効果だな。ちなみに putty は CTRL + D で終了できた。

2004-11-22 追記。
前述の通り、上記は仮想端末が割り当てられていないだけで、シェルは使用可能となっている。できることを限定したければ、command="" を指定するか、scponly などのシェルを割り当てる必要がある。

今回は command="" を使う方を試してみた。以下は公開鍵の例。引用時に改行を入れているが、実際は一行。

no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,
command="echo $SSH_ORIGINAL_COMMAND; umask 077; f=backup`date +%F-%H%M%S`.tar.bz2.encoded; echo $f; cat >/home/hiroaki/backup/$f;,from="*.example.com"
ssh-dss AAAAB3NzaC1kc
3MAAACBAKZhqMdcujcJgGOCIsi+PrmkEEaAy/gpRPpB3Q5OA
wNG+PwTdU5O8/NPde64wNux4XNUB7XkV2eXWfaTZRYyYe0oC
XeJMh2LKZ/a/F3Wu283uuExSZhbkt3Dcv3+u6oyjBzIfNU+P
(以下略)

ローカルから接続してみる。
$ echo "cat .ssh/authorized_keys2" |ssh -i ~/.ssh/auto $remote_host -l $remote_user
Pseudo-terminal will not be allocated because stdin is not a terminal.

backup2004-11-22-235811.tar.bz2.encoded


リモートに生成された backup2004-11-22-235811.tar.bz2.encoded の中身は以下のようになっていた。
cat .ssh/authorized_keys2

scp も試してみる。
$ scp -oIdentityFile=~/.ssh/auto file $remote_user@$remote_host:~/backup/test
scp -t /home/hiroaki/backup/test

command="" のおかげで、~/backup/test は生成されずに済んだ。

0 件のコメント:

コメントを投稿