Agent转发的实现

ssh-agent除了用于免密登录之外,还可以实现跨主机的key认证.
假设有如下一个场景,我们需要从公网连接到公司一个服务器ssh_server,再通过这个服务器跳转到公司的其他内网机器ssh_server2.但是处于安全因素考虑,IT要求所有的ssh连接都需要通过公钥认证,不允许通过密码登录到ssh服务器.
在这种前提下,我们连接到ssh_server是没有问题的,可以直接用本机(ssh_client)的密钥对进行认证,但是从ssh_server跳到ssh_server2就比较麻烦,我们不能在ssh_server2上配置ssh_server的私钥,如果配置了,意味着任何人登录到ssh_server都能登录ssh_server2,万一ssh_server被攻陷了,ssh_server2也同样遭殃.我们希望的是ssh_server2只接受客户端本机(ssh_client)的公钥登录.有一种解决方案,是将ssh_client的私钥临时拷贝到ssh_server上,用完删除.但是这样会导致私钥在网络上传输,容易被截取,同样是不安全的.
ssh-agent的转发功能提供了另外一种解决方案,用户从ssh_client连接到ssh_server后,如果试图连接到ssh_server2,ssh_server可以将公钥认证请求转发到ssh_client上,ssh_client上的ssh-agent可以代理ssh_server公钥认证.
前提条件,ssh_server和 ssh_server2上都对ssh_client的私钥做了授权,通过ssh-client的ssh-agent可以免密登录.

  1. [root@ssh_client ~]# ssh 172.17.0.2
  2. Last login: Thu Apr 26 13:15:24 2018 from test
  3. [root@ssh_server ~]# logout
  4. Connection to 172.17.0.2 closed.
  5. [root@ssh_client ~]# ssh 172.17.0.4
  6. Last login: Wed Apr 25 13:08:01 2018 from 172.17.0.3
  7. [root@ssh_server2 ~]# logout
  8. Connection to 172.17.0.4 closed.
  9. [root@ssh_client ~]# ssh-add -l
  10. 2048 SHA256:XVtu4pjbOq39iJeG13+Ml6IFjoy/7pfv7YwSByyvV4U /root/.ssh/id_rsa (RSA)
  11. 2048 SHA256:qvSCPe3JJjX7o4JbEaR+QCcVthHKvKGqOraLW4+Z4UQ keys/k2 (RSA)
  12. [root@ssh_client ~]#

从ssh_server直接连ssh_server2还是需要输入密码.

  1. [root@ssh_server ssh]# ssh root@172.17.0.4
  2. The authenticity of host '172.17.0.4 (172.17.0.4)' can't be established.
  3. ECDSA key fingerprint is SHA256:Z24pWQCUSlqJF7Nyx0iA4AE/Z5z5WJWbSKA6AFspDbA.
  4. ECDSA key fingerprint is MD5:a7:a5:b2:42:ba:be:dd:f1:5f:fe:5c:89:9c:b9:eb:c3.
  5. Are you sure you want to continue connecting (yes/no)? yes
  6. Warning: Permanently added '172.17.0.4' (ECDSA) to the list of known hosts.
  7. root@172.17.0.4's password:
  8. [root@ssh_server ssh]#

我们要实现的功能是,通过ssh_client连接到ssh_server之后,在当前会话(ssh_server上)继续连接ssh_server2能实现免密.
测试如下:

  1. [root@ssh_client ~]# ssh 172.17.0.2
  2. Last login: Thu Apr 26 23:22:19 2018 from test
  3. [root@ssh_server ~]# ssh root@172.17.0.4
  4. root@172.17.0.4's password:
  5. [root@ssh_server ~]# logout
  6. Connection to 172.17.0.2 closed.

验证失败,从ssh_server试图连接ssh_server2(172.17.0.4)还是提示输入密码.
Agent转发需要在ssh的客户端和服务端都做些配置.
在ssh_server机器上,sshd_config需要支持配置AllowAgentForwarding选项:

  1. [root@ssh_server /]# cd /etc/ssh/
  2. [root@ssh_server ssh]# grep Forward sshd_config
  3. #AllowAgentForwarding yes
  4. #AllowTcpForwarding yes
  5. X11Forwarding yes
  6. # X11Forwarding no
  7. # AllowTcpForwarding no

AllowAgentForwarding默认是yes,因此,服务端无需变更.
在ssh_client机器上,也就是私钥实际存放的机器上,ssh_config需要配置ForwardAgent为yes

  1. [root@ssh_client ssh]# grep Forward ssh_config
  2. # ForwardAgent no
  3. # ForwardX11 no
  4. ForwardX11Trusted yes

默认是no,因此需要修改:

  1. Host *
  2. ForwardAgent yes

去除Host *ForwardAgent no的注释,并将ForwardAgent设置成yes.
再次尝试agent转发功能

  1. [root@ssh_client ssh]# ssh root@172.17.0.2
  2. Last login: Thu May 3 13:10:58 2018 from 172.17.0.3
  3. [root@ssh_server ~]# ssh root@172.17.0.4
  4. Last login: Thu May 3 13:11:05 2018 from 172.17.0.2
  5. [root@ssh_server2 ~]#

从ssh_client登录到ssh_server之后,再从ssh_server登录到ssh_server2也实现了免密登录.
从ssh_server2 logout到ssh_server,可以查看下当前环境变量中和SSH相关的内容:

  1. [root@ssh_server2 ~]# logout
  2. Connection to 172.17.0.4 closed.
  3. [root@ssh_server ~]# env|grep SSH
  4. SSH_CLIENT=172.17.0.3 39854 22
  5. SSH_TTY=/dev/pts/2
  6. SSH_AUTH_SOCK=/tmp/ssh-HZ0OghvUE4/agent.69
  7. SSH_CONNECTION=172.17.0.3 39854 172.17.0.2 22
  8. [root@ssh_server ~]#

从上面的结果,可以看到环境变量中有SSH_AUTH_SOCK,这个变量和ssh_client上启动ssh-agent后导入的环境变量名称是一致的,从ssh_server上发起ssh连接时,会经过这个SSH_AUTH_SOCK做认证,/tmp/ssh-HZ0OghvUE4/agent.69这个socket再通过已经建立的加密的SSH隧道(172.17.0.3 39854 172.17.0.2 22) 将认证请求转发到ssh_client上的ssh-agent进程.

主机之间的文件拷贝

SSH的Agent转发功能除了用于在主机间跳转之外,还能实现通过scp指令在远程主机之间进行文件拷贝.
scp进程是ssh客户端程序中用于文件传输的指令,通常的用途是将SSH客户端上的文件拷贝到SSH服务端(scp ./localfile remotename@remoteserver:/remotefile),或者从SSH服务端将文件拷贝到SSH客户端(scp remotename@remoteserver:/remotefile ./localfile).如果我们需要从客户端机器C上直接将远程服务器A上的文件F转移到远程服务器B上,可以用scp中的 -3参数是实现 Copies between two remote hosts are transferred through the local host. Without this option the data is copied directly between the two remote hosts. Note that this option disables the progress meter..但是这种方式有个缺点,它的实际实现方式是将文件F从服务器A拷贝到客户端C,再从客户端C拷贝到服务器B.如果我们是在低速的VPN中连接远程机器,这样传输文件的效率就太低了.

  1. ClinetC:~$ time scp -3 xxx@ServerA:~/google-chrome-stable_current_amd64.deb yyy@ServerB:/tmp
  2. real 3m53.849s
  3. user 0m2.144s
  4. sys 0m1.820s
  5. ClinetC:~$ssh yyy@ServerB
  6. ServerB:/tmp$ ls -tlr
  7. total 50936
  8. -rw-rw-r-- 1 ubuntu ubuntu 52149676 5 7 21:31 google-chrome-stable_current_amd64.deb
  9. drwx------ 2 ubuntu ubuntu 4096 5 7 21:35 ssh-GoQ1x6HnWZ
  10. ServerB:/tmp$ rm google-chrome-stable_current_amd64.deb

拷贝50M文件花费了大概4分钟.
如果通过ssh-agent,可以不用通过客户端直接远程scp:

  1. ClinetC:~$ eval `ssh-agent`
  2. Agent pid 16565
  3. ClinetC:~$ ssh-add
  4. Identity added: /home/xxx/.ssh/id_rsa (/home/xxx/.ssh/id_rsa)
  5. ClinetC:~$ time scp xxx@ServerA:~/google-chrome-stable_current_amd64.deb yyy@ServerB:/tmp
  6. real 0m5.254s
  7. user 0m0.024s
  8. sys 0m0.004s
  9. ClinetC:~$ssh yyy@ServerB
  10. ServerB:/tmp$ ls -tlr
  11. total 50932
  12. -rw-rw-r-- 1 ubuntu ubuntu 52149676 5 7 21:39 google-chrome-stable_current_amd64.deb
  13. drwx------ 2 ubuntu ubuntu 4096 5 7 21:39 ssh-zRWyUfhFIa

这次拷贝只需要5秒.注意,这里scp指令没有带-3参数,也就是说文件不需要通过第三方(SSH客户端)中转.
如果是ssh-agent转发的功能没有打开,执行远程主机间的拷贝会类似如下结果:

  1. [root@ssh_client ssh]# scp root@172.17.0.2:/root/test root@172.17.0.4:/root/test
  2. root@172.17.0.4's password:
  3. test 100% 152KB 27.1MB/s 00:00
  4. Connection to 172.17.0.2 closed.

登录172.17.0.2和172.17.0.4都是免密的,但是这里我们依然需要输入172.17.0.4的密码,因为这个登录指令实际上是在172.17.0.2主机上触发的.