ssh-agent のしくみ

ssh-agent のように daemon として起動し秘密の情報を保持しつつ別プロセスと通信するようなプログラムを書きたくて、ssh-agent はどう実装しているのかざっくり調べた。
https://github.com/openssh/openssh-portable

通信方法

これは普通に ssh-agent を使っていてもすぐ気付くことだけど、ssh-agent は UNIX domain socket を使って通信している。 eval $(ssh-agent) のように実行すると SSH_AUTH_SOCK と SSH_AGENT_PID の2つの環境変数がセットされ、SSH_AUTH_SOCK は UNIX domain socket のパスを、SSH_AGENT_PID は daemon 化した ssh-agent の pid を指している。 SSH_AUTH_SOCK は /tmp/ssh-*/agent.${parent_pid} というパスになっている。parent_pid は daemon 化する前の pid。/tmp/ssh-* も /tmp/ssh-*/agent.${parent_pid} もオーナー以外アクセスできないようなパーミッションになっている。 このへんは /tmp 以下にできるだけ安全にプライベートなファイルを作るときの一般的な方法をそのままやっているかんじ。
https://github.com/openssh/openssh-portable/blob/V_8_5_P1/ssh-agent.c#L1517-L1543

SSH_AGENT_PID は ssh-agent -k のときに利用される。 sshSSH_AUTH_SOCK が定義されていればそれを使って ssh-agent に通信しにいって、通信内容のプロトコルは独自。この部分は各プログラムが独自に決めればよいことなのであまりよく読んでない。
https://github.com/openssh/openssh-portable/blob/V_8_5_P1/ssh-agent.c#L961-L1061

daemon

daemon 化はだいたい一般的な流れ。fork して setsid して chdir して dup2 で stdin、stdout、stderr を /dev/null に向ける。
https://github.com/openssh/openssh-portable/blob/V_8_5_P1/ssh-agent.c#L1560-L1596

秘密の情報を漏らしにくく

ssh-agent は秘密鍵の情報をメモリ上に持っておく必要があるため、できるだけメモリ情報が漏れないようにする工夫が見られた。

自分で同じように秘密の情報を保持するプログラムを書くときはこのへんを真似しておけばよさそう。 通信のプロトコルを gRPC にして実装したサンプルコード https://github.com/eagletmt/misc/tree/master/rust/agent-proto