ssh の ssh-agent は便利であるが、 ForwardAgent 機能を使う場合は注意が必要である。
あるマシン (ここでは senri とする) で、ssh-agent を実行すると、 ssh-agent は ssh 認証のための UNIX ドメイン ソケットを作成し、 そのパス名を環境変数「SSH_AUTH_SOCK」に設定する (正確に言えば環境変数を設定するのはシェルの組み込みコマンドである eval)。
senri:/home/sengoku % eval `ssh-agent` Agent pid 14610 senri:/home/sengoku % ssh-add Enter passphrase for /home/sengoku/.ssh/id_rsa: Identity added: /home/sengoku/.ssh/id_rsa (/home/sengoku/.ssh/id_rsa)
続いて、ssh-add を実行し、 このソケットを通して ssh 秘密鍵を ssh-agent に登録する。 すると ssh (あるいは scp など) を passphrase を入力することなく使用できる。
senri:/home/sengoku % ssh mail1.v6.klab.org Last login: Tue Jul 3 18:22:13 2007 from ishibashi.klab.org mail1:/home/sengoku %
もしマシン senri が、きちんと管理されていて、 かつ root 権限を持っている人が自分一人しかいないのであれば、 SSH_AUTH_SOCK ソケットを他者に使われる心配はないので、 ssh-agent を走らせっぱなしにしておくことができて、 ssh を常に passphrase 無しで使うことができて便利である。
さらに ssh の ForwardAgent 機能を使うと、 senri 以外のマシンでも senri 上の ssh-agent を利用できる。
senri:/home/sengoku % ssh -A mail1.v6.klab.org Last login: Wed Jul 4 07:13:46 2007 from senri.v6.gcd.org mail1:/home/sengoku % echo $SSH_AUTH_SOCK /tmp/ssh-dHyPx12011/agent.12011 mail1:/home/sengoku % ls -la /tmp/ssh-dHyPx12011 total 0 drwx------ 2 sengoku sengoku 80 Jul 4 07:15 . drwxrwxrwt 8 root root 368 Jul 4 07:15 .. srwxr-xr-x 1 sengoku sengoku 0 Jul 4 07:15 agent.12011 mail1:/home/sengoku % ssh mail2 hostname mail2.klab.org mail1:/home/sengoku %
つまりマシン mail1 上の sshd (ssh サーバ) が、 UNIX ドメイン ソケット /tmp/ssh-dHyPx12011/agent.12011 を作成し、 そのパス名を環境変数「SSH_AUTH_SOCK」に設定した上でログイン シェルを実行する。 ここで ssh を実行すると、このソケットを経由して senri の SSH_AUTH_SOCK ソケットへつながる (「ポート」ではないがポートフォワードと同様) ので、 passphrase 入力を求められない。
さらに、mail1 から別のマシンへ ssh でログインするときも、 同様に ForwardAgent を使うことができるので、 ForwardAgent の連鎖が続く限り ログインした全てのマシンで ssh を passphrase 無しで使うことができる。
以上のように、ForwardAgent は大変便利な機能であるが、 ログインした先のマシンの root 権限を持っている人が自分以外にもいる場合は、 注意が必要である。 例えば上記の例で言うと、 mail1 の root 権限を持っている人は、 UNIX ドメイン ソケット /tmp/ssh-dHyPx12011/agent.12011 へアクセスできる。 つまり、その人は senri 上で動いている私の ssh-agent すなわち私の ssh 秘密鍵を (passphrase 無しで) 利用することができてしまう。
もちろん、そんな悪いことをするヤツを root にしてはいけないのであるが、 ForwardAgent の連鎖を続けすぎると、 ログインしたマシンの中には 100% 信用できるとは限らない人が root 権限を持っている場合もあるだろう。 仮に全てのマシンの root 全員が 100% 信用できたとしても、 常に最悪のケースを考えるのが「セキュリティ」の基本である。 passphrase 無しで ssh を使うという利便性を優先したいのであれば、 せめて悪用されたときに即座に気づく仕掛けを組み込んでおきたいものである。
といっても、不正な利用と正当な利用とを区別することは一般に困難である。 悪用しようとする人がそこそこ注意深ければ、 正当な利用と判別しにくくなるような工夫は可能であろう。 そこで、正当な利用であろうが悪用であろうが、 ssh-agent へのアクセスがあったときは常に知らせる方法について考えてみる。
ssh-agent が利用ログを出力するように変更
ssh-agent.c に次のような変更を加えて、 ssh-agent がアクセスされたときは syslog へ出力するようにしてみる。
--- ssh-agent.c.org 2007-02-28 19:19:58.000000000 +0900 +++ ssh-agent.c 2007-06-28 22:41:19.000000000 +0900 @@ -734,7 +734,7 @@ return; } - debug("type %d", type); + logit("type %d", type); switch (type) { case SSH_AGENTC_LOCK: case SSH_AGENTC_UNLOCK:
この変更で ssh-agent へのアクセスがあったときは syslog へ出力が行なわれる。 あとは syslog 側で、 このログ出力をユーザ (つまり私) へ知らせる方法を考えればよい。
私の場合、 認証関係のログは携帯電話へメールで届くように syslog-ng を設定してある。 ssh-agent を利用していないのにケータイ メールが届くようなことがあれば、 ssh-agent が悪用された可能性を考えて調査することになるだろう。
ssh が ForwardAgent 利用ログを出力するように変更
ssh-agent にログを出力させる方法だと、 ローカルホストから ssh-agent を利用する場合にもログ出力を行なってしまう。 もしローカルホストの root 権限を持っている人が自分一人しかいないのであれば、 ログ出力は ForwardAgent 経由の場合に限定してよい。 リスクが全く無いケースのログ出力はできるだけ抑制したほうが、 危険度が高いケースを際立たせることができるので、より望ましいだろう。
clientloop.c と ssh.c に次のような変更を加えて、 ssh がリモートから ForwardAgent 要求を受取ったときに ログ出力するようにしてみる。
--- clientloop.c.org 2007-02-25 18:36:49.000000000 +0900 +++ clientloop.c 2007-07-03 07:02:39.000000000 +0900 @@ -1763,6 +1763,8 @@ error("Warning: this is probably a break-in attempt by a malicious server."); return NULL; } + logit("sshd %.100s@%.64s requested agent forwarding.", + options.user, host); sock = ssh_get_authentication_socket(); if (sock < 0) return NULL; --- ssh.c.org 2007-01-05 14:30:17.000000000 +0900 +++ ssh.c 2007-07-03 07:15:38.000000000 +0900 @@ -630,7 +630,8 @@ channel_set_af(options.address_family); /* reinit */ - log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 1); + log_init(av[0], options.log_level, SYSLOG_FACILITY_AUTH, + (options.log_level > SYSLOG_LEVEL_VERBOSE)); seed_rng();
このような変更を加えると、 ssh でログインした先 (上記の例で言うと mail1) で ssh を使ったとき、 あるいはさらにそこから別のマシンへ ssh でログインした先で ssh を使うなどして、 大元のマシン (上記の例で言うと senri) 上の ssh-agent へアクセスしたときに、 syslog への出力が行なわれる。
ログ出力の際、 「sshd sengoku@mail1.v6.klab.org requested agent forwarding.」などと、 ForwardAgent 要求を行なったのがどのサーバか示せる点も、 ssh-agent に変更を加える方法と比べてこの方法が優れている点と言える (ForwardAgent 連鎖していても、残念ながら一番手前のサーバしか示せないが)。