SSH 协议及 OpenSSH 实现
SSH
SSH(Secure Shell 的缩写)是一种网络协议,用于加密两台计算机之间的通信,并且支持各种身份验证机制
1996年提出了 SSH 2 协议(或者称为 SSH 2.0),为现在各种实现采用的协议
实现
协议只是理论,只有实现(程序)才能应用
程序软件架构采用服务器-客户端模式(Server - Client),客户端向服务器发出请求,服务器接收客户端的请求
-
putty
SSH client,支持 Windows、Linux。只有客户端实现,需要配合 OpenSSH Server 使用
https://www.ssh.com/academy/ssh/putty#how-to-get-an-ssh-server
-
OpenSSH
SSH 2 协议的开源实现,客户端实现为 ssh,服务器实现为 sshd
目前最流行的 SSH 实现。Linux 的所有发行版都自带 OpenSSH。
客户端
OpenSSH 的客户端实现是二进制程序 ssh
命令
ssh [options] user@hostname [command]
登出:logout、exit、
ctrl + D
options
-i
指定用于登录的私钥,默认为~/.ssh/id_rsa
(rsa算法)
-p
指定客户端连接远程服务器端口,默认为22,如果服务器监听其他端口,则客户端需要通过该参数指定
-t
在 ssh 直接运行远端命令时,提供一个互动式 Shell
command
SSH 登录成功后,用户就进入了远程主机的命令行环境,终端提示符是远程主机的提示符。默认情况下会分配一个 tty,提供交互式 Shell 环境
如果命令后面直接添加 command,则不分配tty,不提供交互式 Shell 环境,而是直接执行 command,并将 command 的执行结果输出到本地终端,然后退出登录。如果命令需要交互,则可以通过 -t
参数,分配一个tty——交互式 Shell 环境,ssh 保持登录状态,直到登出
-
命令要放在单引号中,多个命令用分号隔开
ssh root@xxx.xx 'ls; cat hello.txt'
-
执行远程服务器脚本
ssh root@xxx.xx '/root/update.sh'
- 需要脚本的绝对路径
-
执行本地脚本
ssh root@xxx.xx < update.sh
- 通过重定向 stdin,本地的脚本在远程服务器上被执行
相关文件
-
known_hosts
客户端保存已登录过的 SSH 服务器的公钥
-
id_rsa
用于 SSH 协议版本2 的 RSA 私钥
-
id_rsa.pub
用于SSH 协议版本2 的 RSA 公钥
-
/etc/ssh/ssh_config
全局配置文件
-
~/.ssh/config
,用户个人的配置文件,优先级高于全局配置文件
默认没有该文件,自己创建
配置
-
配置文件的每一行,就是一个配置命令。配置命令与对应的值之间,可以使用空格,也可以使用等号
Compression yes # 等同于 Compression = yes
-
-
Host
指定连接的域名或 IP 地址,也可以是别名,支持通配符。
Host
命令后面的所有配置,都是针对该主机的,直到下一个Host
命令为止。Host *.example.com PreferredAuthentications publickey IdentityFile ~/.ssh/996icu_id_rsa ForwardAgent yes Host github HostName github.com PreferredAuthentications publickey IdentityFile ~/.ssh/996icu_id_rsa
使用缩进来更好的表示
-
HostName
在
Host
命令使用别名的情况下,HostName
指定域名或 IP 地址。HostName myserver.example.com
-
IdentityFile
指定私钥文件
IdentityFile keyfile
-
ForwardAgent
是否允许代理
ForwardAgent yes
-
PreferredAuthentications
指定各种登录方法的优先级
PreferredAuthentications publickey,hostbased,password
-
长连接
用ssh连接服务端,一段时间不操作或屏幕没输出(比如复制文件)的时候,会自动断开
服务端设置
找到/etc/ssh/sshd_config
,大约126-127行,取消注释,并修改数值
# 30表示30s给客户端发送一次心跳
ClientAliveInterval 30
# 3此客户端没有返回心跳,则会断开连接
ClientAliveCountMax 3
修改服务端的配置往往会比较麻烦,也涉及到权限问题,以及安全问题。推荐修改客户端
客户端配置(推荐)
如果是想让主机所有用户都生效,修改/etc/ssh/ssh_config
如果只想让本人生效,则修改 ~/.ssh/config
Host *
ServerAliveInterval 30
ServerAliveCountMax 3
如果此处还有一个配置项叫 SendEnv LANG LC_*,老高建议最好注释掉,否则如果本地是中文环境,而服务器没有对应的中文语言选项时系统可能会出现很多莫名其妙的BUG,所以保持原始英文语言环境为上。
一次性配置
如果只是想临时使用一次,完全可以不用大动干戈地找配置文件改,ssh命令支持直接注入参数,如下:
ssh -o ServerAliveInterval=30 user@host
查看是否生效
想测试是否生效,我们直接ssh到服务器后等一会儿看效果就行,但是如果想看到服务器和客户端发送心跳包的过程,可以这样
ssh -o ServerAliveInterval=30 -vvv user@host
等30s后我们应该可以看到如下字样,说明我们在指定时间发送了心跳给服务器,服务器也有应答。同理如果我们在服务器也打开了心跳,则应该是先收到服务器的心跳然后再应答。
debug3: send packet: type 80
debug3: receive packet: type 82
更多研究可以参考Linux使用ssh超时断开连接的真正原因
服务器
密钥变更
- 重装SSH服务器
- 两台服务器拥有同样的IP,不同的密钥
客户端连接时就会发生公钥不吻合的情况,中断连接,此时客户端需要将原来的公钥从~/.ssh/known_hosts
文件中删除,重新验证远程服务器。因此
会出现一个 konw_hosts.old 的文件
ssh-keygen -R hostname
# ssh-keygen -f "/home/lfp/.ssh/known_hosts" -R "192.168.200.2"
- hostname 公钥发生变更的主机名
相关文件
-
authorized_keys
服务器保存已登录的客户端的公钥
如果不存在则手动创建,文件的权限要设为
644
,即只有文件所有者才能写chmod 644 ~/.ssh/authorized_keys
远程登录
密码登录
流程
-
服务器验证
连接远程服务器需要验证其是否可信,远程服务器会发送自己的指纹(公钥),如果在本地
~/.ssh/known_hosts
文件中没有该公钥,则提示用户该服务器指纹是陌生的,是否需要继续连接,输入yes
或no
,如果输入yes
则认为该服务器的公钥可信,加入本地~/.ssh/known_hosts
文件中,下次再次连接时则不提示。服务器指纹指 SSH-Server 生成的密钥对,在安装 sshd 时默认会在
/etc/ssh
目录下生成密钥对,例如 ssh_host_xxx_key 及 ssh_host_xxx_key.pubssh root@www.example.com The authenticity of host 'foo.com (192.168.121.111)' can't be established. ECDSA key fingerprint is SHA256:Vybt22mVXuNuB5unE++yowF7lgA/9/2bLSiO3qmYWBY. Are you sure you want to continue connecting (yes/no)? yes # 输入yes Warning: Permanently added 'foo.com (192.168.121.111)' (RSA) to the list of known hosts
-
输入登录密码
客户端就会跟服务器建立连接后,SSH-Client 用服务器的公钥,将所要登录的用户的密码加密后,发送给服务器
-
登录密码验证
远程服务器的SSH-Server利用自己的私钥解密,验证登录密码是否正确,如果正确则允许登录
密钥登录
SSH 默认采用密码登录,这种方法有很多缺点,简单的密码不安全,复杂的密码不容易记忆,每次手动输入也很麻烦。密钥登录是比密码登录更好的解决方案
SSH 密钥登录采用非对称加密,两个密钥成对使用,分为公钥(public key)和私钥(private key)
- 使用公钥加密,只有使用对应的私钥解密
- 使用私钥签名,只有使用对应的公钥解密
流程
- 手动将客户端的公钥放入远程服务器的指定位置
- 客户端向服务器发起 SSH 登录的请求
- 服务器收到用户 SSH 登录的请求,发送一些随机数据给用户,要求用户证明自己的身份。
- 客户端收到服务器发来的数据,使用自己的私钥对数据进行签名,然后再发还给服务器。
- 服务器收到客户端发来的加密签名后,使用对应的公钥解密,然后跟原始数据比较。如果一致,就允许用户登录
生成密钥
ssh-keygen -t rsa -C "your_email@example.com"
# 代码参数含义:
# -t 指定密钥类型,默认是 rsa ,可以省略。
# -C 设置注释文字,比如邮箱。
# -f 指定密钥文件存储文件名。
# 以上代码省略了 -f 参数,因此,运行上面那条命令后会让你输入一个文件名,用于保存刚才生成的SSH key
Generating public/private rsa key pair.
Enter file in which to save the key (/home/lfp/.ssh/id_rsa): /home/lfp/.ssh/996icu_id_rsa
# 可以不输入文件名,使用默认文件名(推荐),就会生成 id_rsa 和 id_rsa.pub 两个秘钥文件。
# 公司的可以修改文件名
# 接着又会提示你输入两次密码(该密码是你push文件的时候要输入的密码,而不是github账号的密码),
# 可以不输入密码,直接按回车(两下)。那么push的时候就不需要输入密码,直接提交到github上了,如:
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
#接下来,就会显示如下代码提示:
Your identification has been saved in /home/lfp/.ssh/996icu_id_rsa.
Your public key has been saved in /home/lfp/.ssh/996icu_id_rsa.pub.
The key fingerprint is:
SHA256:U2QI4FzNgyBr2z2gG2iIZAPHM2FgHjGVA6FFa5w your_email@example.com
The key's randomart image is:
+---[RSA 2048]----+
|o&%o+oo= .o |
|O=XB .. =o |
|+Eoo= .. |
|B+.+ o . |
|+.+ . o S |
|. o . . |
| . |
| |
| |
+----[SHA256]-----+
#当你看到上面这段代码时,那就说明,你的 SSH key 已经创建成功
上传密钥
生成密钥以后,公钥必须上传到服务器,才能使用公钥登录。
OpenSSH 规定,用户公钥保存在服务器的~/.ssh/authorized_keys
文件
执行如下命令或手动复制粘贴
cat ~/.ssh/id_rsa.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
端口转发
https://wangdoc.com/ssh/port-forwarding.html#远程转发
https://blog.mythsman.com/post/5d301903976abc05b3454691/
代理转发
https://docs.github.com/cn/developers/overview/using-ssh-agent-forwarding
参考
https://wangdoc.com/ssh/index.html
https://www.ssh.com/academy/ssh/openssh
应用
github
添加公钥
添加 SSH key 到 github上
-
复制 id_rsa.pub 公钥
-
点击头像--->Settings--->SSH and GPG keys--->New SSH key--->将公钥粘贴到里面,并取个名字即可
-
测试是否成功
ssh -T git@github.com
看到如下内容,则设置成功
Hi xxx! You've successfully authenticated, but GitHub does not provide shell access.
添加ssh后仍需要输入密码
git config --list
查看当前项目对应的远程地址 remote.origin.url,要改为 git开头的地址git remote set-url origin git@xxxx.git
FAQ
Connection closed by remote host
-
如果原来是可以用ssh连接的, 突然连接不上通常是连接数过多导致的
-
修改最大连接数
# 找到配置文件 /etc/ssh/sshd_config # 去掉前面的 "#" 并把连接数改大 MaxSession 10
重启 service sshd reload
-
每次退出ssh时,使用 exit 断开连接
Enter passphrase for key '/root/.ssh/id_rsa'
- 如果创建 ssh key 的时候设置了密码,则每次使用ssh免密登录的时候都需要输入密码
解决办法:
-
修改(删除)密码
ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]
将原始密码 123456 改为 ''(空)
ssh-keygen -p -P 123456 -N '' -f ~/.ssh/id_rsa
-
将密码添加到 ssh-agent
如果不想在每次使用 SSH 密钥时重新输入密码,您可以将密钥添加到 SSH 代理,让它管理您的 SSH 密钥并记住您的密码。
ssh : connect to host xxx port 22:Connection refused
原因是被访问主机没有启动 OpenSSH
查看本机是否启动 OpenSSH
ps -ef | grep ssh
# 或
systemctl status sshd
ubuntu 需要安装 openssh-server
sudo apt install openssh-server