SSH
Warning本文发布于 2023/04/05,内容可能已过时。
SSH
SSH(Secure Shell)是一种协议,使用公钥密码学允许在网络上安全远程登录计算机。SSH 客户端程序(例如 OpenSSH 中的 ssh)通常在远程登录会话期间运行,并配置为查找用户主目录中的文件中的用户私钥(例如 ~/.ssh/id_rsa
)
为了增加安全性(例如,防止攻击者读取本地文件系统上的任何文件),通常将私钥以加密形式存储,在这种情况下,加密密钥是从用户记忆的口令计算出来。因为输入口令可能很繁琐,所以许多用户希望每个本地登录会话只需输入一次口令。存储未加密密钥最安全的位置是程序内存,在类 Unix 操作系统中,内存通常与进程相关联
通常 SSH 客户端进程不能用于存储未加密键,因为 SSH 客户端进程只持续远程登录会话期间。因此,用户运行一个名为 ssh-agent
的程序,在本地登录会话持续时间之外运行,并将未加密键存储在内存中,并使用 Unix 域套接字与 SSH 客户端进行通信
SSH 协议主要由两个部分组成:SSH 客户端和 SSH 服务器。SSH 客户端是一个工具,用于连接到 SSH 服务器并执行命令。SSH 服务器则是一个服务,用于接受来自 SSH 客户端的连接,并提供安全的远程访问
SSH 协议的主要特点和优点包括:
-
加密传输:SSH 使用加密技术来保护数据传输的安全性,防止数据被窃听、篡改或伪造
-
认证机制:SSH 提供了多种身份验证方式,包括密码、公钥、证书等,可以有效地防止非法访问
-
文件传输:SSH 可以用于安全地传输文件,支持 SCP 和 SFTP 协议
SSH 以非对称加密实现身份验证。身份验证有多种途径,例如其中一种方法是使用自动生成的公钥-私钥对来简单地加密网络连接,随后使用密码认证进行登录;另一种方法是人工生成一对公钥和私钥,通过生成的密钥进行认证,这样就可以在不输入密码的情况下登录
任何人都可以自行生成密钥。公钥需要放在待访问的电脑之中,而对应的私钥需要由用户自行保管。认证过程基于生成出来的私钥,但整个认证过程中私钥本身不会传输到网络中
开启 SSH
win 上默认开启了客户端,服务端要到 设置 -> 应用 -> 可选功能 -> 添加功能 -> 搜索 OpenSSH 服务器
Ubuntu 上则是 apt install openssh-server
开始
先以 Ubuntu 为例。配置文件在 /etc/ssh/sshd_config
# 指定 sshd 监听的端口号为 2222,而不是默认的 22 端口Port 2222
# 允许 root 用户通过 ssh 登录到服务器PermitRootLogin yes
# 允许使用密码进行身份验证,而不是仅使用 SSH 密钥PasswordAuthentication yes
# 允许所有用户通过 ssh 登录到服务器# 这个配置可能存在安全风险,建议更改为具体的用户名列表,以限制访问权限AllowUsers *
然后重启服务
sudo service ssh restart
Win 10 同理,但配置文件在 C:/ProgramData/ssh/sshd_config
,同时需要使用管理员权限的编辑器来编辑,也需要在 服务 中重启 OpenSSH Server
生成 key
其实有自带的 key,在 /etc/ssh/ssh_host_rsa_key.pub
和 C:/ProgramData/ssh/ssh_host_rsa_key.pub
下,以用户名@主机名的方式,并需要管理员权限才能查看。所以一般都应该是为当前用户生成一个 key
# 简单默认的方式:ssh-keygen -t rsa
其中的参数如下
- -t:指定要创建的密钥类型,例如 rsa、dsa、ecdsa 等
- -b:指定密钥长度,例如 2048 位或 4096 位
- -C:添加注释,用于描述密钥,例如用户名或电子邮件地址
- -f:指定要创建的密钥文件的名称和路径
- -N:指定密钥密码,用于保护密钥文件
- -P:更改或删除密钥密码
- -i:将现有的公钥转换为 OpenSSH 格式并打印出来
- -y:将现有的私钥转换为 OpenSSH 格式并打印出来
- -q:启用静默模式,不输出任何错误或警告信息
其中要求输入的 passphrase 是指这个私钥的密码,直接回车以跳过
默认情况下是以 当前用户名@主机名 作为密钥的标识,并生成在 ~/ssh/
下,id_rsa
标识密钥,id_rsa_pub
表示公钥,通常要分享公布的也是公钥,密钥用于系统解密通信时被公钥加密的数据
连接服务器
一般情况下,这时候就能用 ssh username@server -p 2222
来连接对方了,一般 host 是指服务器的 ip 或域名
其中的一些参数
-
-p:指定远程主机的端口号,默认为 22
-
-i:指定私钥文件路径
-
-l:指定登录用户名
-
-X:开启 X11 转发,可以在远程主机上运行图形界面应用程序
-
-v:显示详细的调试信息
-
-N:仅进行连接,不启动远程 shell
-
-C:开启压缩传输,可以加快传输速度
-
-A:开启身份验证代理,可以在远程主机上再次进行身份验证
首次连接时候需要先登记客户端的公钥信息,yes 就好
The authenticity of host '[192.168.50.88]:2222' can't be established.ECDSA key fingerprint is SHA256:giqj7wnJZt5eGEAz7JROlkw2b3qiseb5yvfPd0WEc0M.Are you sure you want to continue connecting (yes/no/[fingerprint])?
这时就需要输入服务器端这个用户的密码了。在 win 中,这个默认是微软账号的密码,如果是离线登录,则是 PIN 或者创建账号时设的密码
当然我们还是希望免密登录,这就需要将客户端的公钥完整地添加到服务器的 ~/.ssh/authenticity_keys
中。可以直接复制过去,也能通过以下来以 ssh 的形式添加过去。然后再输入一次服务器用户的密码就行了,这样之后的每次连接都不用密码了
ssh-copy-id user@server
其中的一些参数
- -i:指定要复制的公钥文件,默认为
~/.ssh/id_rsa.pub
- -p:指定远程主机的 SSH 端口号,默认为 22
- -o:指定 ssh 选项,例如
-o "ProxyCommand ssh user@proxyhost -W %h:%p"
- -f:将公钥添加到远程主机的 authorized_keys 文件中时,不需要进行交互式确认,直接进行覆盖
- -n:将公钥添加到远程主机的 authorized_keys 文件中时,不要将用户名追加到文件的末尾
有一点是,如果服务器的默认终端是 fish shell 就会报错,语法不兼容… 这时候就需要用 scp 来代替了
scp ~/.ssh/id_rsa.pub user@server:~/.ssh/authorized_keys
authorized_keys && known_hosts
authorized_keys 是 SSH 协议中用于实现公钥身份验证的文件,存储在 SSH 服务器的用户目录下。在使用 SSH 连接到远程服务器时,如果 SSH 客户端提供的公钥与 authorized_keys 文件中的公钥匹配,则可以成功连接到服务器
也就是说,只要将客户端的公钥添加到服务器的 authorized_keys,就能免密登录服务器了。但前提是得有权限访问到服务器端并更改文件
为了保证安全性,可以将 authorized_keys 文件的权限设置为 600 或 644。这样,只有 SSH 服务器的用户可以读取 authorized_keys 文件中的公钥,提高了安全性
而 known_hosts 是 SSH 协议中用于存储已知主机公钥的文件。在使用 SSH 连接到远程主机时,SSH 客户端会将远程主机的公钥保存到 known_hosts 文件中。下次连接到同一主机时,SSH 客户端会比对本地保存的公钥和远程主机返回的公钥是否一致,如果不一致则会发出警告,提示可能存在安全问题
known_hosts 文件的作用是确保 SSH 客户端连接到正确的主机,并防止中间人攻击。中间人攻击是指攻击者在 SSH 连接过程中截获数据并伪造数据,使得 SSH 客户端连接到了攻击者控制的主机上,从而获取敏感信息
SSH 客户端连接到新的主机时,会提示用户是否接受该主机的公钥。如果用户确认该主机的公钥是正确的,则可以将公钥添加到 known_hosts 文件中,避免下次连接时再次提示
ssh-agent
假设现在有两套密钥用于两台不同的主机,而 ssh 连接的时候如果不指定私钥的位置,由于有多套密钥,那它只能靠输入主机的密码来辨别身份(即便已经加入了 authorized_keys)。而连接的时候又指定私钥又太不优雅(当然可以在 config 中指定),所以就有了一个用于管理私钥的 ssh-agent
bash 下启用 ssh-agent
ssh-agent
然后添加 ssh key
ssh-add /path/to/key
# 输出Identity added: .ssh/id_rsa (name@host)
这样,用 ssh 连接的时候就可以不用指定私钥不输密码了
可以通过 ssh-add -l
来列出已经添加的私钥的指纹信息
还是 fish shell,并不能启用 agent,需要将以下函数添加到 ~/.config/fish/functions/fish_ssh_agent.fish
function fish_ssh_agent --description 'launch the ssh-agent and add the id_rsa identity' if begin set -q SSH_AGENT_PID and kill -0 $SSH_AGENT_PID and grep -q '^ssh-agent' /proc/$SSH_AGENT_PID/cmdline end echo "ssh-agent running on pid $SSH_AGENT_PID" else eval (command ssh-agent -c | sed 's/^setenv/set -Ux/') end set -l identity $HOME/.ssh/id_rsa set -l fingerprint (ssh-keygen -lf $identity | awk '{print $2}') ssh-add -l | grep -q $fingerprint or ssh-add $identityend
然后就能用 fish_ssh_agent
来启用了(需要先 fish 重进 fish shell)
~/.ssh/config
这个文件用于连接时的配置
- Host:远程主机名或 IP 地址
- HostName:远程主机名或 IP 地址,与 Host 配置项作用相同,但可以在 ProxyCommand 中使用
- Port:SSH 连接的端口号
- User:SSH 连接的用户名
- IdentityFile:SSH 连接时使用的私钥文件路径
- ProxyCommand:通过代理服务器连接远程主机的命令
- ForwardAgent:是否开启 SSH Agent 转发功能
- ServerAliveInterval:SSH 连接保持活跃的时间间隔
- LogLevel:日志输出级别
- Compression:是否开启数据压缩
- ControlMaster:是否开启 SSH 连接复用
- ControlPath:SSH 连接复用时的控制 socket 文件路径
- ControlPersist:SSH 连接复用时的持续时间
- LocalForward:本地端口转发
- RemoteForward:远程端口转发
- ExitOnForwardFailure:是否在端口转发失败时退出 SSH 连接
- SendEnv:发送本地环境变量到远程主机
一般都有这么设置
Host github.com HostName ssh.github.com IdentityFile ~/.ssh/id_rsa Port 443
Host organic.github.com HostName ssh.github.com IdentityFile ~/.ssh/organic_rsa Port 443
其中,如果多用户连接同一个主机时,可以通过用户名来区分,但连接 GitHub 时,默认用的都是 [email protected]
,也就是用户名只有一个
GitHub 是通过用户的公钥来区分用户的,一个公钥对应一个用户。这时候如果要在同一台电脑上 ssh 多个 github 账号时,就需要用 Host 来标识了。而其中的 HostName 是真正连接主机的 ip,并需要指定对应的私钥位置
Host 需要以域名的格式,不带协议、端口、参数、路径
同时,一般来说连接 GitHub 需要用 ssh.github.com:443 来连接
这里的缩进只是给人看的,用于更好地分辨不同的用户主机
scp & sftp 基于 SSH 传送文件
SCP(Secure Copy)是一种基于 SSH 协议的安全文件传输工具,用于在本地主机和远程主机之间传输文件
常用的参数包括:
- -r:递归复制整个目录
- -P:指定远程主机的端口号
- -i:指定使用的身份验证文件
- -q:不显示传输进度信息
- -C:压缩传输的数据
- -v:显示详细的传输进度信息
- -p:保留文件的访问和修改时间
SFTP(Secure File Transfer Protocol)是一种基于 SSH 协议的安全文件传输协议,用于在本地主机和远程主机之间传输文件
常用的参数包括:
- -r:递归复制整个目录
- -P:指定远程主机的端口号
- -i:指定使用的身份验证文件
- -q:不显示传输进度信息
- -c:指定加密算法
- -v:显示详细的传输进度信息
- -B:指定数据包的大小
示例:
上传文件:
scp local_file user@server:/remote/directory
下载远程文件到本地主机:
scp user@server:/remote/directory/remote_file local_file
上传本地目录到远程主机:
scp -r local_directory user@server:/remote/directory
下载远程目录到本地主机:
scp -r user@server:/remote/directory local_directory
上传本地文件到远程主机并指定端口号:
scp -P port local_file user@server:/remote/directory
下载远程文件到本地主机并指定身份验证文件:
scp -i /path/to/identity_file user@server:/remote/directory/remote_file local_file