ssh 配置

样例

Windows 下是 %userprofile%\.ssh\config。Linux 下自己对应一下。

  • 含有 ForwardAgent 选项表示启用代理转发,相当于在连接它时自动添加了参数 -A
  • 含有 ProxyJump 表示先通过跳板机再连接到此主机,相当于在连接它时自动添加了参数 -J

样例:

Host github
  HostName github.com
  IdentityFile ~/.ssh/id_rsa

Host jump
  HostName <能访问的 IP>
  User <用户>
  Port <能访问的端口>
  IdentityFile ~/.ssh/id_rsa

Host jump_docker
  ProxyJump jump
  HostName localhost
  User root
  Port 22275

其中 jump_docker 在局域网中,我们必须通过跳板 jump 来访问。这里 jump_docker 没有写 IdentityFile,如果默认的密钥文件(比如 ~/.ssh/id_rsa 等)匹配不了,那么就只能通过密码登陆。

转发代理(-A)不如代理跳转安全

Man page 的说法

man ssh-agent

它是一个在启动时记录私钥的程序,这样方便匹配公钥验证。

Connections to ssh-agent may be forwarded from further remote hosts using the -A option to ssh(1) (but see the caveats documented therein), avoiding the need for authentication data to be stored on other machines.

什么是 ssh-agent

ssh-agent 在内存中记录私钥,然后完成公钥指纹匹配请求。根据文章 https://www.cnblogs.com/f-ck-need-u/p/10484531.html,如果密钥不是规范密钥(比如 ~/.ssh/id_rsa),就要手动指定具体的 IdentityFile,否则可以考虑使用 ssh-agent。

Behind the scenes, ssh-agent binds to a Unix domain socket to communicate with other programs ($SSH_AUTH_SOCK environment variable).

ssh-agent 只需要对私钥解密一次。也就是说如果私钥由密码保护,只需要输入一次密码,直到 ssh-agent 停止,都不用再次输入密码。

什么是 ForwardAgent

代理转发则是指连接服务器后将服务器上 ssh-agent 收到的请求转发到本地来。这样我们先 ssh 到 A,打开 shell 之后,在 A 的 shell 中 ssh B,也是能登陆的(假设本地能过 A 和 B 的密钥检查,但是仅 A 不能通过 B 的密钥检查;另一方面,本地能够直接连接到 A,A 也能直接连接到 B,但是 本地 不能直接连接到 B)。

https://www.infoworld.com/article/3619278/proxyjump-is-safer-than-ssh-agent-forwarding.html

根据上面的网页,转发代理允许目标机器对 client 的 key challenge(从已知公钥构造)转发到本地机器上,这需要打开一个 socket,这样特权用户就能通过 socket 获取本地的 ssh-agent 的信息,攻击者也能通过构造密钥来以 agent 认可的身份完成登陆;而代理跳转是通过 ssh 加密传输 stdin 和 stdout 的数据,所以更加安全。

http://www.unixwiz.net/techtips/ssh-agent-forwarding.html

这篇文章是最清楚的,介绍了从 ssh 密码连接、公钥连接到 ssh-agent、agent 转发以及 key challenge 构造。它将 ssh agent 描述为在 ssh 建立连接过程中替代 ssh 完成 key challenge 的第三方程序。构造好 key response 之后还是从 ssh 发回去。

它们解决了什么问题?

以下讨论假设本机是 A,中间机器是 B,目标机器是 C。其中 A 可以访问 B,B 可以访问 C,但 A 不能直接访问 C。

ForwardAgent 和 ProxyJump 都解决了密钥验证问题,允许在密钥集中存储在本地的情况下经过中间机器去访问目标机器。ProxyJump 还解决了连接问题,在本机和目标机器之间形成逻辑上直接连接的信道;而 ForwardAgent 是在登陆上中间机器后使用的,本身就不存在连接问题。

使用 ForwardAgent 时,一般情况下意图工作机器是 B,仅部分操作需要登陆机器 C 或者让机器 C 验证身份。使用 ProxyJump 时,意图工作机器只能是 C,B 仅充当连接跳板。这个时候虽然 ForwardAgent 也能达到目标,但是 ProxyJump 更好:首先是它使用起来更加方便,允许直接自动连接目标服务器,而不需要先手动连接到中间服务器、再手动连接到目标服务器,也不需要开启 ssh-agent、加载密钥,不需要修改 /etc/ssh/sshd_config 中的 AllowAgentForwarding;其次它也更加安全,直接转发了 stdin 和 stdout,就像本地 ssh 直接和最终目标机通信一样,不需要 ssh-agent 的参与。

实验:通过 ForwardAgent 访问 GitHub

本小节参考 GitHub 的官方教程。因为 ProxyJump 比较简单所以略去实验。

假设本机已经生成了公钥。首先需要在 GitHub 个人设置中添加本机的公钥,然后添加以下配置到 ~/.ssh/config 中:

Host github
  HostName github.com
  IdentityFile ~/.ssh/id_rsa

然后运行 ssh -T git@github.com,确保能够成功登陆。

用以下配置创建一个新的镜像(用 root 登陆,密码是 123456):

# Image name: mysshd.
FROM debian:trixie
RUN apt update && apt install -y --no-install-recommends openssh-server neovim && rm -rf /var/lib/apt/lists/*
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
RUN sed -i 's/#AllowAgentForwarding yes/AllowAgentForwarding yes/g' /etc/ssh/sshd_config
RUN echo root:123456 | chpasswd
# Needs privilege separation directory.
RUN mkdir /run/sshd
# Needs absolute path.
ENTRYPOINT [ "/usr/sbin/sshd", "-D" ]

用以下命令去构建和运行镜像(入口是 /usr/sbin/sshd -D,不会有任何输出,并不是程序卡住了):

docker build . -t mysshd
docker run -it --rm -p 45522:22 mysshd

开启一个新的终端,启动 ssh-agent 并加载默认密钥:

eval `ssh-agent`
ssh-add -k

连接之前容器中启动的 sshd(注意要加 -A 打开转发代理,而且当前终端需要有 ssh-agent 产生的环境变量 SSH_AUTH_SOCK):

ssh root@localhost -p 45522 -A

然后测试是否能够正常连接 Github:

$ ssh -T git@github.com
# Attempt to SSH in to github
> Hi USERNAME! You've successfully authenticated, but GitHub does not provide
> shell access.

再试试不用选项 -A 连接之前的容器,就会提示访问被拒绝(因为容器并没有配置 GitHub 的连接信息)。