SSH
🔑 简介
安全外壳协议 (Secure Shell Protocol, SSH) 是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境。SSH 默认使用 TCP 端口 22。
SSH 最常见的用途是远程加密登录系统,例如:
- 本地加密登录远程服务器
- Github 身份验证,进行代码的克隆和推送
🔑 SSH 密钥
SSH 密钥是一对密钥(私钥 和 公钥)
私钥 | 公钥 |
---|---|
文件无后缀 | 文件后缀为.pub |
保密,存放在个人电脑上 | 可以公开,上传至服务器 |
用于加密数据 | 用于解密数据 |
在本机(主机1)生成 SSH 密钥对,将公钥上传至远程服务器(主机2/3),可以在多个场景中使用 SSH 密钥进行身份验证:
- 登陆远程服务器(可能是本地局域网,也可能是广域网,例如图中主机2)
- Github 身份验证(例如图中主机3),可以完成代码的克隆和推送
对于不同的服务器可以生成不同的密钥,这样可以区分不同的服务器,也可以区分不同的账户
SSH 密钥和配置文件通常存放在 ~/.ssh/
目录下(~
代表用户的家目录)
- Linux 中
~
为/home/<username>
- MacOS 中
~
为/Users/<username>
- Windows 中
~
为C:\Users\<username>
~/.ssh/
├── config # SSH 配置文件
├── id_rsa # 私钥
├── id_rsa.pub # 公钥
├── id_rsa_<name> # 私钥
├── id_rsa_<name>.pub # 公钥
├── ... # 其他密钥
└── known_hosts # 存放已知的主机公钥
SSH 身份认证过程如下:
- 「客户端」发送自己的「公钥」给「服务器」
- 「服务器」用自己的「私钥」加密一个随机字符串,发送给「客户端」
- 「客户端」用自己的「私钥」解密这个字符串,发送给「服务器」
- 「服务器」用「客户端」的「公钥」解密这个字符串。如果解密成功,认证通过;之后的通信都是用对称加密算法进行加密
🔑 生成 SSH 密钥
SSH 密钥和配置文件通常存放在 ~/.ssh/
目录下,如果没有该文件夹,可以手动创建:
mkdir -p ~/.ssh
在主机上使用 ssh-keygen
生成 SSH 密钥的命令如下:
ssh-keygen [-f <filename>] [-C <comment>] [-N ""] [...]
其他可选参数
-f
指定生成的密钥文件。默认生成的密钥文件为~/.ssh/id_rsa
。
如果有多个服务器可以设置不同的名称加以区分,例如设置一个名为 ~/.ssh/sshkey-github
的密钥专为 Github 使用(✅ 推荐这种命名密钥的做法)
-b
指定密钥长度,默认为 2048,通过-b 4096
可以设置为 4096-t
指定密钥类型,默认为 rsa,通过-t dsa
可以设置为 dsa-C
添加注释信息,可以为邮箱地址,例如-C "abc@gmail.com"
,也可以为空-N
指定密钥的密码,如果不需要密码可以为空,一般来说是不需要密码的--help
查看帮助信息
一些例子:
# 生成默认密钥 ~/.ssh/id_ed25519
ssh-keygen
# 生成默认密钥 ~/.ssh/sshkey [推荐]
ssh-keygen -f ~/.ssh/sshkey # 为了方便,一般不需要给自己电脑上的密钥再设定密码
ssh-keygen -f ~/.ssh/sshkey -N "" # 因此可以默认密码为空
# 生成名为 ~/.ssh/sshkey-github 的密钥,用于 Github
ssh-keygen -f ~/.ssh/sshkey-github
# 生成名为 ~/.ssh/sshkey-github 的密钥,用于 Github,并添加注释
ssh-keygen -f ~/.ssh/sshkey-github -C "Github"
# 生成名为 ~/.ssh/sshkey 的密钥,长度为 4096
ssh-keygen -f ~/.ssh/sshkey -b 4096
运行后会提示输入密钥文件的保存路径,直接(一路)回车即可,会在默认路径 ~/.ssh/
下生成密钥文件:
sshkey
为私钥sshkey.pub
为公钥
🔑 密钥管理
可以针对不同的服务器配置多个密钥,需要编 ~/.ssh/config
文件,如果没有则手动创建。文件内容如下:
Host my_server
HostName <host_name>
Port 22
User <user_name>
ForwardAgent yes
PreferredAuthentications publickey
IdentityFile <identity_file>
Host
指定一个或多个服务器的名称(别名),可以使用通配符(例如 *.example.com)来匹配多个,例如my_server
,也可以就是 IP 地址,登录的时候使用该名称。HostName
指定服务器的 IP 地址或域名Port
指定服务器的 SSH 端口,如果是默认端口22
可以省略该项User
指定登录服务器的用户名,例如ubuntu
IdentityFile
指定私钥的路径,例如~/.ssh/sshkey-github
IdentitiesOnly
指定只使用指定的私钥进行认证,默认为no
,可以在 ssh agent 提供了多个密钥的情况下,只使用指定的密钥进行认证ForwardAgent
指定是否转发本地的 SSH AgentPreferredAuthentications
指定登录服务器优先的认证方式,例如publickey
,password
,keyboard-interactive
StrictHostKeyChecking
指定是否检查主机的公钥,有三个选项(ask
,yes
,no
)如下:ask
默认值,首次连接位置服务器时,询问是否接受公钥,接受则添加到known_hosts
文件中,下次连接时不再询问yes
则每次验证远程服务器的公钥,如果与之前不同则会拒绝连接;需要手动更新known_hosts
文件no
不检查公钥,直接添加到known_hosts
文件,不推荐使用
如果有多个服务器,可以添加多个 Host,并且指定不同的私钥加以认证区分
随后,可以通过 Host 的名称登录服务器或者其他操作,例如:
# 登录 my_server
ssh my_server
# 上传文件到 my_server
scp my_server:~/file.txt ~/file.txt
注意,这时候如果采用
ssh <user>@<ip>
的方式登录,还是需要输入密码的,因为没有指定私钥
密钥管理-Github
采用这种方式也可以配置多个 github/gitee/HuggingFace 账户:
Host github.com
HostName github.com
User <Your_Github_Name>
AddKeysToAgent yes
IdentityFile ~/.ssh/sshkey-github
Host Github
HostName github.com
User <Your_Github_Name>
AddKeysToAgent yes
IdentityFile ~/.ssh/sshkey-github
连接测试的时候需要改成
ssh -T git@github.com
ssh -T git@Github
使用 git 获取时,需要根据 Host 的名称进行 clone 操作,例如:
git clone git@github.com:<github_username>/<repo>.git
git clone git@Github:<github_username>/<repo>.git
VSCode 中的远程资源管理器(Remote Explorer)中,显示远程服务器列表就是通过读取 ~/.ssh/config
文件中的 Host 来显示的,可以直接点击连接
🔑 SSH 密钥的作用
登录远程服务器
使用 SSH 登陆远程服务器前,需要确保远程服务器安装并且开启了 SSH 服务,一般来说 Linux 服务器会默认安装 openssh-server
,而一些桌面版的 Linux 系统可能没有安装 SSH 服务,需要手动安装。对于 Ubuntu ,可以如下安装
sudo apt install openssh-server -y
在登陆的时候,会自动将「本地私钥」和「服务器公钥」进行匹配,如果匹配成功,则可以免密登录。这在使用 VSCode 远程开发的时候非常有用,可以免去每次输入密码的麻烦。 (关于 VScode 远程开发可以参考 "Remote Development using SSH")
使用 ssh-copy-id
命令可以将「本地公钥」传输协议到远程服务器并存储在「公钥认证」文件中,例如:
ssh-copy-id -i <identity_file> <user>@<hostname>
-i
指定「本地公钥」的路径,例如-i ~/.ssh/sshkey.pub
<user>@<hostname>
远程服务器的用户名和主机名,例如ubuntu@192.168.1.1
-p
如果远程服务器的 SSH 端口不是默认的 22 端口,可以通过-p <port>
指定端口
(对于 win 系统,没有 ssh-copy-id
命令,)也可以手动将「本地公钥」复制到远程服务器的 ~/.ssh/authorized_keys
文件中 (如果不存在,需要手动创建) ,但是推荐使用 ssh-copy-id
命令(win 可以使用 git bash),因为该命令会自动创建文件,并自动设置权限
这里提供一个生成密钥并自动上传至服务器的脚本 ssh-keygen-auto.sh
,高亮部分需要自行修改:
查看完整代码
# ========================================
key_name="server" # 生成的密钥名称
username="ubuntu" # 服务器用户名,如果仅生成密钥,此项可忽略
server_ip="192.168.1.6" # 服务器 IP
port="22" # 服务器端口,默认 22
# ========================================
# 生成密钥文件路径,密钥名称为 sshkey-<key_name>
key_file="$HOME/.ssh/sshkey-${key_name}"
[ ! -d "$HOME/.ssh" ] && mkdir -p $HOME/.ssh
# 生成本地密钥
if [ -f ${key_file} ]; then
echo "密钥已存在: ${key_file}"
else
comment=${comment:-"${key_file}"} # 如果 comment 为空,则注释为 key_file
ssh-keygen -t rsa -f ${key_file} -C "${comment}" -N ""
fi
# 上传公钥 至 服务器
ssh-copy-id -i ${key_file}.pub -p ${port} ${username}@${server_ip}
echo ""
echo " SEE :${key_file}"
echo ""
Github 身份验证
Hugging Face 等也是如此操作添加
Github 支持使用 SSH 在 GitHub.com 上的存储库中访问和写入数据,参考 about-ssh
在 Github 中 Settings -> SSH and GPG keys -> New SSH key
中添加密钥,将本地生成的公钥(id_rsa_github.pub
)复制到 Github 中,该密钥的命名可以自定义,建议和设备绑定,并且加上密钥名称,例如 Ubuntu22.04-id_rsa
(配置名称后需要设置 config
文件,参考 ssh config
)
添加之后,需要进行连接测试以建立一次连接,例如:
ssh -T git@github.com
ssh -T git@Github # 如果在 ~/.ssh/config 中配置了 Host Github
ssh -T git@hf.co # for verifying Hugging Face
按照 Host 名称进行连接,参考 Github 配置
测试成功后出现如下提示则说明连接成功
Hi User! You've successfully authenticated, but GitHub does not provide shell access.
随后在 clone 项目采用 ssh 的方式,会自动与 Github 建立连接,Github 会将公钥和「当前设备中」的私钥进行匹配,如果匹配成功,则可以免密操作。基于 ssh 的仓库在 push 会自动使用 ssh 进行验证,不需要输入用户名和密码
注意:如果是多设备,需要将每个设备的公钥都添加到 Github 中,这样才能实现多设备免密操作。每个设备生成的私钥都不一样,当然了,把密钥复制到其他设备也是可以的,但是这样就失去了 SSH 密钥的意义了。
HTTPS 和 SSH clone 方式的区别:
- HTTPS 可以任意用户克隆,SSH 只能克隆自己(有权限的)的项目,并且需要配置 SSH 密钥
- 在推送代码的时候,HTTPS 需要输入用户名和密码,SSH 不需要输入用户名和密码
端口转发
修改 ssh 默认登陆端口
一些情况下,为了安全性,需要修改 ssh 默认登陆端口,可以通过修改 /etc/ssh/sshd_config
文件中的 Port
选项来修改 ssh 默认登陆端口,例如:
Include /etc/ssh/sshd_config.d/*.conf
#Port 22
Port <xxxx> # <xxxx> 是新的端口号
AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
添加新端口后,需要修改防火墙规则
SSH_PORT=<xxxx> # <xxxx> 是新的端口号
sudo firewall-cmd --permanent --zone=public \
--add-port=${SSH_PORT}/tcp # 添加新端口
sudo firewall-cmd --reload # 更新防火墙规则
sudo firewall-cmd --list-ports # 查看已开放的端口
# sudo firewall-cmd --permanent --zone=public \
# --remove-port=${SSH_PORT}/tcp # 移除端口
firewalld
可能需要安装
重启 ssh 服务使配置生效:
sudo systemctl restart sshd