SSH 是一种网络协议,用于计算机之间的加密登录。传统的网络服务程序,如:FTP、PoP 和 Telnet 在本质上都是不安全的,因为它们在网络上用明文传送口令和数据,非常容易就可以截获这些口令和数据。
除了可以代替 Telnet 做远程登录外(Telnet 因为采用明文传送报文,安全性不好,很多 Linux 服务器都不开放 Telnet 服务,要想使用 Telnet 需要安装),SSH 还可以为 FTP、PoP、甚至为 PPP 提供一个安全的”通道”(比如 SFTP)。
SSH 存在多种实现,既有商业实现,也有开源实现,其中 OpenSSH 是 Linux 下的开源实现,应用非常广泛。
安装配置
安装
SSH 已经成为 Linux 系统的标准配置,一般 Linux 系统都会自带 SSH(含 SSH Client 和 SSH Server)。
1 | # 验证 SSH Client |
注:Windows Git 自带 mingw,mingw 里面有 SSH Client 但是没有 Server,另外,Windows 10 中已默认安装 OpenSSH Client,通过设置 --> 应用 --> 管理可选应用 --> 添加功能
,还可以安装 OpenSSH Server。安装目录在 C:\Windows\System32\OpenSSH
,里面的程序有有:scp.exe、sftp.exe、ssh.exe…。
另外,Windows 下是通过 net start 和 net stop 来开启和停止某个服务。
1 | # 开启和停止 SSH Server |
配置
对于普通用户,默认配置即可,一般都无需再配置。如果需要配置,/etc/ssh
目录下,ssh_config 是客户端配置文件,sshd_config 是服务端配置文件。修改 sshd_config 文件,这个文件下可以改 SSH 登录端口和禁止 root 登录。改端口可以防止被端口扫描。
1 | # 编辑配置文件 |
修改完成后,重启 sshd 服务
1 | service sshd restart |
基本用法
1 | # 登录 SSH Server(更详细的可以用 ssh -h 查看) |
1 | # 生成密钥 |
生成密钥时,会提示设置私钥的密码 Enter passphrase (empty for no passphrase),直接回车则为不设密码,如果设置了私钥密码,每次使用 SSH 进行操作都需要输入私钥密码。虽然这样增加了安全性,但是使用 SSH 的目的很多时候就是想更加方便,跳过输入账号密码这一步。只要保存好自己的私钥不泄露,一般不设私钥密码是没有太大的风险的。如果已经生成带密码的私钥,又想取消密码可执行以下操作。
1 | # 修改私钥密码 |
注:作为 SSH 客户端,~/.ssh
目录下,会有 3 个文件 id_rsa、id_rsa.pub、known_hosts,作为 SSH 服务端,也会有三个文件 id_rsa、id_rsa.pub、authorized_keys。
1 | id_rsa # 保存的是私钥 |
口令登录
1 | # 基本格式 |
输入密码后回车,登录成功。在使用完毕后,通过 exit 命令退出 ssh 登录
中间人攻击
SSH 之所以能够保证安全,原因在于它采用了公钥加密。整个过程是这样的:
1 | 1)远程主机收到用户的登录请求,把自己的公钥发给用户; |
这个过程本身是安全的,但是实施的时候存在一个风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像 https 协议,SSH 协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的。
可以设想,如果攻击者插在用户与远程主机之间(比如在公共的 Wifi 区域),用伪造的公钥,获取用户的登录密码。再用这个密码登录远程主机,那么 SSH 的安全机制就荡然无存了。这种风险就是著名的”中间人攻击”(Man-in-the-middle attack)。
为了解决这个问题,SSH 在首次登录时会让用户确认是否相信远程主机的,确信过的远程主机会被保存在 known_hosts 这个文件中。
- known_hosts
第一次登录远程主机时,会出现下面这样的提示(意思是,无法确认 192.168.10.14 主机的真实性,只知道它的公钥指纹,问你还想继续连接吗?):
1 | The authenticity of host '192.168.10.14 (192.168.10.14)' can't be established. |
注:所谓”公钥指纹”,是指公钥的 SHA256 或 MD5 计算结果。因为公钥长度较长(这里采用 RSA 算法,长达 1024 位),很难比对,所以对其进行 SHA256 或 MD5 计算,将它变成一个 128 位的指纹,再进行比较,就容易多了。
很自然的一个问题就是,用户怎么知道远程主机的公钥指纹应该是多少?回答是没有好办法,远程主机必须在自己的网站上贴出公钥指纹,以便用户自行核对。经过风险衡量以后,用户决定接受这个远程主机的公钥:
1 | Are you sure you want to continue connecting (yes/no)? |
系统会出现一句提示,表示 host 主机已经得到认可:
1 | Warning: Permanently added '192.168.10.141' (ECDSA) to the list of known hosts. |
当远程主机的公钥被接受以后,它就会被保存在文件 $HOME/.ssh/known_hosts
之中。下次再连接这台主机,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。
每个 SSH 用户都有自己的 known_hosts 文件,此外系统也有一个这样的文件,通常是 /etc/ssh/ssh_known_hosts
,保存一些对所有用户都可信赖的远程主机的公钥。
公钥登录
使用密码登录,每次都必须输入密码,非常麻烦。好在 SSH 还提供了公钥登录,可以省去输入密码的步骤。
所谓”公钥登录”,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录 shell,不再要求密码。
这种方法要求用户必须提供自己的公钥。如果没有现成的,可以直接用 ssh-keygen 生成一个。
操作步骤
- 本机生成密钥文件
本地系统执行以下命令,一路回车,生成密钥文件(以 DSA 算法生成公钥和私钥),其中有一个问题是,要不要对私钥设置口令(passphrase),如果担心私钥的安全,这里可以设置一个。
1 | ssh-keygen -t rsa |
注:完成后,控制台里面有密钥生成的位置信息,生成密钥文件里面,$HOME/.ssh/ 目录下,会生成两个文件,id_rsa 为私钥文件,id_rsa.pub 为公钥文件。
- 将公钥文件传输到远程机器
本地机器执行以下命令,将公钥文件传输的远程机器。
1 | # 方法一:在本机上执行 ssh-copy-id 命令,ssh-copy-id 是一个快捷命令,用于将本地的公钥追加至远程主机的 ~/.ssh/authorized_keys 文件内 |
- 登录
再次使用已经做免密处理的用户登录远程机器,已经不需要密码了,免密登录处理完成。
1 | ssh root@192.168.10.141 |
authorized_keys
远程主机将用户的公钥,保存在登录后的用户主目录的 $HOME/.ssh/authorized_keys 文件中。公钥就是一段字符串,只要把它追加在 authorized_keys 文件的末尾就行了。
这里不使用上面的 ssh-copy-id 命令,改用下面的命令,解释公钥的保存过程:
1 | ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub |
这条命令由多个语句组成,依次分解开来看:
1 | ssh user@host # 表示登录远程主机 |
写入 authorized_keys 文件后,公钥登录的设置就完成了。
文件和目录权限
配置完成后,如果发现还是不能免密登录,查看日志发现,这是权限问题引起的:
1 | tail /var/log/secure -n 20 |
Authentication refused: bad ownership or modes for directory /root,从字面上可以看出是目录的属主和权限配置不当,查找资料得知:SSH 不希望 home 目录和 ~/.ssh 目录对组有写权限。
修改远程机器的 .ssh 目录需要 700 权限,authorized_keys 文件需要 600 权限即可:
1 | chmod 755 ~ |
注:关于 Linux 权限,请查看 Linux 相关章节。