SSH协议基本原理及用法

前言

  SSH是linux系统远程登录及命令操作的常用协议与指令,基本用法非常简单。最近在做持续集成工作时候,在SSH上面遇到一些坑,因此在这里总结一下。

远程登录

  SSH远程登录有两种方式,分别是口令登录及公钥登录。

口令登录

主要流程

  口令登录即在登录时候输入远程主机的用户名及密码。其流程如下:

  1. 远程主机收到用户的登录请求,把自己公钥发给用户。
  2. 用户用远程主机的公钥将登录密码加密,发给远程主机。
  3. 远程主机用自己的私钥解密登录密码,判断是否正确,决定是否让用户登录。
known_hosts

  上面用户收到远程主机的公钥后,如果是第一次连接该远程主机,则一般会先在shell里打印出公钥指纹,询问用户是否继续连接,如果选择继续则会把公钥加入到known_hosts文件里。如果要做自动化构建和部署的话,则不能用这种询问模式。解决办法有两种:

  1. 在用户的~/.ssh/config文件或/etc/ssh/ssh_config里加入这一行:StrictHostKeyChecking no
  2. 提前获知远程主机的公钥,事先手动写入known_hosts文件中。

  在写入known_hosts文件以后,之后再次登录就不会再询问了,而是在获取远程主机公钥后与known_hosts文件进行比对。如果远程主机更改了公钥,导致不一致了,则会产生异常。这时需要删除known_hosts里对应那一行的公钥信息,重新获取并写入。如果不想用known_hosts保存远程主机的公钥,则可以修改/etc/ssh/ssh_config,加入这一行:UserKnownHostsFile=/dev/null

  ~/.ssh/config是针对特定用户的配置文件,/etc/ssh/ssh_config和/etc/ssh/sshd_config都是公共的配置文件,其中前者针对ssh客户端,后者针对服务器端。

公钥登录

  公钥登录不需要输入密码,是自动化技术的必备武器。需要事先将客户机的公钥拷贝到远程主机的~/.ssh/authorized_keys文件里。一般是用ssh-copy-id指令实现,可以用-i参数指定公钥文件。ssh-copy-id实际上相当于使用了这条指令:ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub

主要流程

  流程如下:

  1. 远程主机收到登录请求,向用户发送一段随机字符串。
  2. 用户用自己的私钥加密发到远程主机里。
  3. 远程主机再用事先存好的公钥解密。如果成功,则直接允许登录。

  即便是公钥登录,客户机仍然会收到远程主机的公钥数据,并加到known_hosts中。

注意事项

  有几种情况可能导致使用了ssh-copy-id但仍提示需要输入登录密码:

  • 远程主机/etc/ssh/sshd_config这个文件下面几行注释没有去掉:
1
2
3
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

  其中主要是第三行,前两行即便注释了默认都是yes。

  • 远程主机~/.ssh/authorized_keys权限不足。需要让所有者用户可读写,可以用chmod解决:
1
chmod 600 ~/.ssh/authorized_keys
  • 远程主机~目录和.ssh目录不能让除了该用户其余的人具有写权限,这个需要格外注意。
  • 出于某种原因,客户机的ssh-agent高速缓存内没有该私钥的信息。可以用ssh-add -l查看。如果的确没有,则要ssh-add进去。其中~/.ssh/id_rsa会自动加进去,一般不需要手动添加,但其他私钥需要手动添加。这个是放在内存里的临时缓存,重启后需要重新ssh-add私钥进去。

中间人攻击

  前面提到过,客户机会收到远程主机的公钥后,会在shell打印出其指纹。因为在用户发出登录请求后,如果有第三方截获了该请求,并冒充远程主机将伪造的公钥发给用户,那么用户就会把远程主机的密码用伪造公钥加密发给第三方,从而被它获得远程主机的登录密码,使得这个过程存在着极高的风险。

  客户机只要用返回的指纹和远程主机公示的指纹签名进行比对,就可以确定是否伪造了。

创建公钥私钥对

  ssh-keygen命令可以创建。-N输入私钥密码,-f指定文件名,-C输入备注,-t指定加密算法,默认是rsa。

  需要用ssh-add加入ssh-agent高速缓存内才有效。ssh-agent同时也可以作为自动化免去私钥密码的一项技术。事先将私钥加入ssh-agent,这时候要输入私钥密码。以后用ssh指令的时候就只会在缓存中直接调用私钥,不需要再输入密码了。

管理多个SSH key

  最简单的就是上面那样用ssh-add把私钥都放到高速缓存中,客户端在用ssh指令的时候会按照一定的策略轮流尝试。但因为是放在内存中的,每次重启都会清空,有点不方便。

  最正常的做法是放到~/.ssh/config文件中,配置示例如下:

1
2
3
4
5
6
7
8
9
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_a
Host git.oschina.net
HostName git.oschina.net
User git
IdentityFile ~/.ssh/id_rsa_b

  config文件有很多种写法,还支持通配符。不仅可以用来管理ssh key,还可以用来省略输入用户名,简化hostname使得便于记忆等等。管理ssh key最常见的就是管理github和gitlab多个账户。