SSH 知识汇总
Secure Shell Protocol(安全外壳协议,简称 SSH)是一个基于 TCP 的应用层网络传输协议,由 IETF(互联网工程任务组) 的网络工作小组制定。它由 SSH 传输层协议、SSH 用户认证协议以及 SSH 连接协议三个子协议组成,各个子协议分工合作,实现加密、认证、完整性检查等多种安全服务。
SSH 基于 C/S(客户/服务器) 结构,通过加密和认证机制,可在不安全的网络环境中提供安全的传输环境,也可以对传输的数据进行压缩,加快传输速度。既可以取代传统的 Telnet、FTP 等应用程序进行远程登录和文件传输,也可以在端口转发功能的基础上为 Pop 应用程序、X 应用程序、Linuxconf 应用程序等多种应用程序提供安全保障。
SSH 协议框架
SSH协议框架中核心部分有三个协议:传输层协议、用户认证协议、连接协议。
- 传输层协议(The Transport Layer Protocol):提供服务器认证,数据安全性,信息完整性等功能的支持,有时还提供压缩功能。
- 用户认证协议(The User Authentication Protocol):为服务器提供客户端的身份验证。
- 连接协议(The Connection Protocol):将加密的信息隧道复用成若干个逻辑通道,提供给更高层的应用协议使用; 各种高层应用协议可以相对地独立于 SSH 基本体系之外,然后依靠这个基本框架,通过连接协议使用 SSH 的安全机制。
SSH 连接建立过程
在建立 TCP 连接的基础上,SSH 协议建立连接还要经过以下五个阶段:
- 版本协商(明文传输):SSH协议主要有两个版本SSH1和SSH2,SSH1因存在漏洞已停用,目前主要用的是SSH2。
- 公钥交换
- 加密算法协商
- 身份认证
- 会话
SSH 只是一个协议,存在多种实现,本文主要针对 OpenSSH 实现在 Ubuntu 上的一些操作和相关知识,其它 Linux 操作系统基本类似,只有安装和开启、重启、关闭服务的命令与操作系统有关,需要根据自己的操作系统确定。
一、openssh 安装
SSH 分为客户端 openssh-client 和服务器 openssh-server,可以利用以下命令确认电脑上是否安装了客户端和服务器。
dpkg -l | grep ssh
如果命令返回结果中有 openssh-client 和 openssh-server 这两项,则表示已经安装。
如果没有安装,使用以下命令安装客户端和服务器:
sudo apt install openssh-client # 安装客户端
sudo apt install openssh-server # 安装服务器
如果只想远程登录服务器,可以只安装 openssh-client(ubuntu 默认已经安装)。
- 在安装 openssh 前,可能需要先更新源列表:
sudo apt update
二、密钥产生
使用以下命令产生密钥:
ssh-keygen [-t rsa/dsa/ed25519/ecdsa] [-c comment] [-f file] ......
常用参数有:
-t 用于指定密钥类型,如果不加 -t ,默认产生 rsa 类型的密钥;
-c 指定密钥的备注;
-f 指定密钥存放位置,默认 ~/.ssh
这个命令产生的密钥默认存放在 ~/.ssh 目录下
ssh key 类型
- 目前 ssh key 的类型有四种,分别是 dsa、rsa、ecdsa、ed25519。
- 根据数学特性,这四种类型又可分为两大类,dsa/rsa 是一类,ecdsa/ed25519 是一类,后者算法更先进。
- dsa 因为安全问题,已经不再使用了;ecdsa 因为政治原因和技术原因,不推荐使用。
- rsa 是目前兼容性最好的,应用最广泛的 key 类型,使用 ssh-keygen 工具生成 key 的时候,默认使用的也是它,不过在生成 key 时,key size 不能设的太小。
- ed25519 是目前最安全、加解密速度最快的 key 类型,由于其数学特性,它的 key 长度比 rsa 小很多,它目前惟一的问题就是兼容性。
三、登录远程服务器
使用以下命令登录远程服务器:
ssh [options] [user@]host [command]
如:
ssh xiaoming@192.168.8.8
这个命令会尝试以用户名 user 登录远程服务器,验证成功后,就可以安全的访问服务器,可以使用 -p 参数指定端口号,默认是 22 端口。如果本地用户名和远程用户名一致,登录时就可以省略用户名。
- 第一次登录服务器时,需要确认服务器的公钥指纹,手动确认后,服务器的公钥会被添加到本地的 ~/.ssh/known_hosts 文件里,再次登录时,如果服务器的公钥指纹跟上次连接时没有变化,ssh 就会跳过确认阶段,直接进行登录验证。(“公钥指纹”,是指公钥长度较长,很难比对,所以对其进行 MD5 计算,将它变成一个 128位 的指纹。)
两种验证方式
ssh 有两种验证方式登录远程服务器,分别是基于口令的安全验证和基于密钥的安全验证。
1. 基于口令的安全验证
如果用户没有提前将自己的公钥存放到服务器上,那么在登录服务器时,就需要输入口令验证身份。
- 这种验证方式需要在网络上传输口令(加密的),并且也不能保证正在连接的服务器就是目标服务器,可能会有别的服务器在冒充目标服务器,也就是可能受到“中间人”攻击,安全性不如密钥验证的方式。
- 这种验证方式每次都要输入口令,很不方便。
2. 基于密钥的安全验证
如果用户提前将自己的公钥存放到了服务器上,那么在用户登录时,服务器就会使用用户的公钥进行身份验证,不需要输入口令。
- 这种验证方式不需要在网络上传输口令,也不会受到“中间人”攻击,安全性比较高。
- 这种验证方式在客户和服务器之间传输信息次数较多,验证过程稍长。
使用以下方式实现免密登录远程服务器:
-
产生 rsa 密钥对:
ssh-keygen -t rsa
-
将产生的公钥上传到服务器:
ssh-copy-id -i ~/.ssh/id_rsa_pub user@XXX.XXX.XXX.XXX
如果这个命令不管用,则需要手动将公钥内容拷贝到服务器的 ~/.ssh/authorized_keys 文件中,并重启 ssh 服务。
-
这样下次通过
ssh [user@]host [command]
命令登录到服务器时就不需要验证密码了。
- ssh-copy-id 命令好像只可以用于 rsa 密钥类型(有待验证)
- 将密钥上传到服务器时需要验证用户身份(输入口令)
- 如果将公钥内容拷贝到服务器后连接依然出错,可以看一下服务器端 ~/.ssh/authorized_keys 文件和 ~/.ssh 目录的访问权限,确保只有所有者可读。
- 不同的用户对应不同的 ~/.ssh/authorized_keys 文件,将公钥拷贝到某个用户的~/.ssh/authorized_keys 文件中,就只能以这个用户名免密登录,以另一个用户名登录时,还是需要输入口令。
退出登录
exit
四、远程执行命令
SSH 除了可以远程登录服务器执行操作以外,还可以直接在远程执行命令或脚本。
命令格式如下:
ssh [options] [user@]host [command]
执行无需交互的命令
如:
ssh root@192.168.8.8 "pwd"
要执行的命令需用引号括起来。
如果要执行多条命令,用 ;(分号)分隔,如:
ssh root@192.168.8.8 "cd /root/.ssh;pwd"
执行需要交互的命令
当需要在远程执行需要交互的命令时,需要加 -t 选项(不加会出错),如:
ssh -t root@192.168.8.8 "top"
-t 选项会指定一个伪终端迫使 SSH 客户端以交互模式工作。
执行本地脚本
如:
ssh root@192.168.8.8 < test.sh
这里的脚本 test.sh 是保存在本地当前目录的。
如果要传递本地的参数,需要添加 bash -s
的选项(用单引号或双引号括起来),形如:
ssh root@192.168.8.8 "bash -s" < test.sh hello
执行远程脚本
如:
ssh root@192.168.8.8 "/root/test.sh"
这里的脚本是保存在远程主机的,脚本名要写绝对路径。
如果要加参数,则形如:
ssh root@192.168.8.8 "/root/test.sh hello"
五、scp 跨机远程拷贝
Scp(Secure Copy) 是一个在各个主机之间进行复制或者文件传输的一个命令行工具。它在后台使用 ssh 连接来进行文件的传输。scp 既指一种定义安全复制应该如何工作的协议,也指一种可以被安装的作为 OpenSSH 工具套的一部分的软件或是指令。
命令格式
scp [参数] [原路径] [目标路径]
命令参数
-1 强制 scp 命令使用协议 ssh1
-2 强制 scp 命令使用协议 ssh2
-4 强制 scp 命令只使用 IPv4 寻址
-6 强制 scp 命令只使用 IPv6 寻址
-B 使用批处理模式(传输过程中不询问传输口令或短语)
-C (大写)启用压缩功能,文件在传输过程中被压缩,节省时间和带宽;
-p (小写)保留原文件的修改时间,访问时间和访问权限;
-q 不显示传输进度条、诊断和警告信息;
-r 递归复制整个目录
-v 详细显示复制过程的信息,会显示出整个过程有关连接如何建立,正在使用什么配置和认证文件等等的详细信息,当程序失败或无法完成请求时非常有用;
-c cipher 以 cipher 将数据传输进行加密,此参数直接传递给 ssh,默认采用 AES 算法,其它的有:blowfish 等;
-F ssh_config 指定一个替代的 ssh 配置文件,此参数直接传递给 ssh;
-i identity_file 从指定文件中读取传输时使用的密钥文件,此参数直接传递给 ssh;
-l limit 限定 scp 使用的带宽,以 Kbit/s 为单位;
-o ssh_option 如果习惯于使用 ssh_config 中的参数传递方式;
-P port 注意 P 是大写, port 是指定数据传输用到的端口号;
-S program 指定加密传输时所使用的程序,此程序必须能够理解 ssh 的选项;
使用说明
1. 复制本地文件到远程主机
scp local_file remote_username@remote_ip:remote_folder
scp local_file remote_username@remote_ip:remote_file
scp local_file remote_ip:remote_folder
scp local_file remote_ip:remote_file
指定了用户名,命令执行后需要输入用户密码;如果不指定用户名,命令执行后需要输入用户名和密码;如果已经将本地公钥拷贝到远程服务器,双方基于密钥验证身份,就不需要输入密码。
- 注意远程主机 ip 地址和目录之间用冒号分隔
- 默认情况下,scp 总是覆盖目标地址的文件
从远程主机复制文件到本地,格式与上面一样,只需要将后面两个参数对换。
2. 复制目录时要加 -r 参数
scp -r local_folder remote_username@remote_ip:remote_folder # 从本地复制到远程
scp -r remote_username@remote_ip:remote_folder local_folder # 从远程复制到本地
3. 多文件传输
从本地复制多个文件到远程:
scp test1.txt test2.txt remote_username@remote_ip:remote_folder
从远程复制多个文件到本地:
scp remote_username@remote_ip:~/test/\{test1.txt,test2.txt\} local_folder
- 从远程复制多个文件到本地时要用 {}(大括号)将文件名括起来,大括号本身要进行转义,多个文件之间用 ,(逗号)分隔。
4. 在两个远程主机之间复制文件
scp remote1_username@remote1_ip:remote_file remote2_username@remote2_ip:remote_folder
六、SSH 端口转发
SSH 不仅可以远程登录,还有一个非常实用的功能:端口转发。
SSH 端口转发又叫 SSH 隧道(tunnel),是在 SSH 连接的基础上,通过将客户端或服务器端的某个端口的数据放到 SSH 连接中加密传输,从而在不安全的网络中安全的传输数据或者绕过防火墙的限制实现对目的主机目的端口的访问的一个功能。
端口转发有两个主要作用:
(1)将不加密的数据放在 SSH 安全连接里面传输,在不安全的网络或不受信任的网络提供安全连接,比如通过端口转发访问 Telnet、FTP 等明文服务,数据传输就都会加密,或者在不安全的公用网络中,利用信任的 SSH 服务器访问网络或邮件服务器。
(2)绕过防火墙的限制实现对目的主机的访问。
端口转发有三种使用方法:本地端口转发,远程端口转发,动态端口转发。
本地端口转发(本地隧道)
本地端口转发是指在 SSH 连接的基础上,将远程的 SSH 服务器作为中介,建立一个本地客户端主机到目的主机(第三台主机)的连接,将对于本地客户端主机指定端口的访问通过 SSH 连接转发给 SSH 服务器,再由 SSH 服务器发到目标主机的目标端口,同时将结果传回。由于被转发的端口位于本地客户端,所以就叫做本地端口转发。本地被转发的端口类似于隧道的的入口,目标主机的目标端口类似于隧道的出口。
创建本地端口转发的命令格式如下:
ssh -L 本地端口:目标主机:目标端口 uesr@host [-N]
- 这个命令在本地客户端主机执行,它在建立端口转发的同时也建立了基本的 SSH 连接;
- -L 选项表示建立本地端口转发,在之后本地端口、目标主机、目标端口之间用 :(冒号)隔开;
- user@host 是登录 SSH 服务器的用户名与地址;
- -N 选项(可选)表示这个 SSH 连接只进行端口转发,不登录远程 Shell,不能执行远程命令,只能充当隧道;
- 目标主机不一定必须是第三台主机,也可以是 SSH 服务器本身,此时目标主机就是 localhost;如果目标主机是第三台主机,那么端口转发只能保证本地主机至 SSH 服务器之间的连接是安全的,并不能保证 SSH 服务器至目标主机间的连接是安全的,这取决于 SSH 服务器与目标主机的连接方式。
应用场景1
(1)台式机 B 上运行着虚拟机 C,虚拟机使用虚拟机软件搭建的虚拟网络与宿主主机 B 相连接,但在主机 B 以外无法直接访问该虚拟网络。想要通过 SSH,用与台式机 B 处于同一 WiFi 下的笔记本 A 来远程控制虚拟机 C,(在A上)执行端口转发命令:
ssh -L 22022:虚拟机 C 地址:22 desktop_user@台式机 B 地址
这个命令中,22022 是一个主机 A 上未被占用的端口,desktop_user是登录到台式机 B 的用户名,注意此时台式机 B 需要开启 ssh 服务器端服务。
在虚拟机 C 上也开启 ssh 服务器端服务后,在主机 A 上执行:
ssh -P 22022 vitual_user@localhost
这个命令以 SSH 协议访问本机的 22022 端口,这个请求会通过主机 A 和台式机 B 之间的 SSH 连接到达台式机 B,台式机 B 将这个请求转变为对虚拟机 C 的 22 端口的访问请求,并为 主机 A 返回结果,这样就可以在主机 A 上以 SSH 协议登录虚拟机 C。
在这个命令中,-P 选项指定访问特定端口,而不是默认的 22 端口,vitual_user 是登录到虚拟机的用户名,因为主机 A 是访问自己的端口,所以地址是 localhost,也可以利用其它主机访问主机 A 的 22022 端口达到访问虚拟机 C 的目的,此时 localhost 就改为 主机 A 的 ip 地址或域名。
(2)加密访问邮件服务器
想要通过 SSH 连接安全的访问邮件服务器,在本地主机执行以下命令:
ssh -L 1100:mail.example.com:110 mail.example.com
这个命令中,1100 是本机未被占用的端口,mail.example.com 是邮件服务器,这个命令将对本机的 1100 端口的访问转发到邮件服务器 mail.example.com 的 110 端口(邮件获取协议 POP3 协议的默认端口)。端口转发建立以后,POP3 邮件客户端只需要访问本机的 1100 端口,请求就会自动转发到 mail.example.com 的 110 端口。
在这种情况下,邮件服务器 mail.example.com 本身作为中介,必须运行 SSH 服务器,否则,就必须通过另一台 SSH 服务器作为中介,命令如下:
ssh -L 1100:mail.example.com:110 other.example.com
这个命令中,本机的 1100 端口还是绑定 mail.example.com 的 110 端口,但是由于 mail.example.com 没有运行 SSH 服务器,所以必须通过 other.example.com 中介。本机的 POP3 请求通过 1100 端口,先发给 other.example.com 的 22 端口(sshd 默认端口),再由后者转给 mail.example.com,得到数据以后再原路返回。
注意,采用上面的中介方式,只有本机到 other.example.com 的这一段是加密的,other.example.com 到 mail.example.com 的这一段并不加密。
远程端口转发(远程隧道)
远程端口转发是指在建立 SSH 连接的基础上,将本地客户端主机作为中介,建立一个 SSH 服务器到目的主机(第三台主机)的连接,将对于 SSH 服务器指定端口的访问通过 SSH 连接转发给客户端,再由客户端主机发到目标主机的目标端口,同时将结果传回。由于被转发的端口位于远程 SSH 服务器,所以就叫做远程端口转发。远程 SSH 服务器上被转发的端口类似于隧道的的入口,目标主机的目标端口类似于隧道的出口。
创建远程端口转发的命令格式如下:
ssh -R 远程端口:目标主机:目标端口 uesr@host [-N]
- 这个命令同样在客户端主机执行;
- -R 选项表示建立远程端口转发,在之后远程端口、目标主机、目标端口之间用 :(冒号)隔开,远程端口是一个在远程 SSH 服务器上的端口;
- user@host 是登录 SSH 服务器的用户名与地址;
- -N 选项(可选)表示这个 SSH 连接只进行端口转发,不登录远程 Shell,不能执行远程命令,只能充当隧道;
- 目标主机不一定必须是第三台主机,也可以是本地客户端主机本身,此时目标主机就是 localhost;如果目标主机是第三台主机,那么端口转发只能保证 SSH 服务器至本地主机之间的连接是安全的,并不能保证本地客户端主机至目标主机间的连接是安全的,这取决于本地客户端主机与目标主机的连接方式。
- 注意:OpenSSH 服务器对于远程端口转发的设定,默认只接受远程服务器主机本机上的应用发起的请求,想要从其他连接到服务器的设备发起请求,需将 /etc/ssh/sshd_config 配置文件中 GatewayPorts 选项后的 no 修改为 yes。
应用场景2
(1)台式机 B 上运行着虚拟机 C,虚拟机使用虚拟机软件搭建的虚拟网络与宿主主机 B 相连接,但在主机 B 以外无法直接访问该虚拟网络。想要通过 SSH,用与台式机 B 处于同一 WiFi 下的笔记本 A 来远程控制虚拟机 C,在 B 上执行端口转发命令:
ssh -R 22122:虚拟机 C 地址:22 desktop_user@笔记本 A 地址
这个命令中,22122 是一个笔记本 A 上未被占用的端口,desktop_user是登录到笔记本 A 的用户名,注意此时笔记本 A 是远程 ssh 服务器,此时与本地端口转发的例子是不同的,在本地端口转发的例子中,台式机 B 是远程 ssh 服务器。
在虚拟机 C 上也开启 ssh 服务器端服务后,在笔记本 A 上执行:
ssh -P 22122 vitual_user@localhost
-P 选项指定访问特定端口,vitual_user 是登录到虚拟机的用户名,因为主机 A 是访问自己的端口,所以地址是 localhost。
这样,笔记本 A 对本机 22122 端口的 SSH 访问请求经过台式机 B 和笔记本 A之间的 SSH 连接转发到台式机 B,再由台式机 B 发送给虚拟机 C,这样就建立了笔记本 A 和虚拟机 C 之间的 SSH 连接。
(2)内网计算机 A 运行着 http 服务,但 A 没有公网 IP,其他设备不能使用该服务。恰好云服务器 B 有公网 IP(甚至域名),便于被访问。在不将 http 服务迁移至云服务器 B 的前提下,可以使用 SSH 端口转发使其他设备通过访问 B 的方式访问 A 上的 http 服务。在 A 上执行端口转发命令:
ssh -R 80:localhost:80 cloud_user@server.example.com
这时目标主机是 A 本身(localhost),80 号端口是 http 默认端口,cloud_user 是 B 上的用户名;server.example.com 是 B 的域名。
于是其它人就可以通过访问 http://server.example.com
来访问内网计算机 A 提供的 http 服务了。
动态端口转发
动态端口转发是指在本机与 SSH 服务器之间建立一个 SSH 连接,然后本机内部针对某个端口的通信,都通过这个加密连接转发,但这种转发不规定目标主机和目标端口,而是去读取应用发起的请求,从请求中获取目标信息。在这种情况下,SSH 服务器要去访问哪一个网站,完全是动态的,取决于原始通信,所以就叫做动态端口转发。
相对的,本地端口转发和远程端口转发在建立时要指定目标主机目标端口,可以统称为固定端口转发。
创建动态端口转发的命令格式如下:
ssh -D 本地端口 user@host [-N]
这个命令中,-D 表示动态端口转发,user@host 表示登录到 SSH 服务器的用户名和地址,-N 表示这个 SSH 连接只进行端口转发,不能执行远程命令。
需要注意动态端口转发只绑定本地端口,并没有指定目标主机目标端口,而是在通信到达 SSH 服务器时使用 SOCKS4 或 SOCKS5 协议动态确定。所以,如果要使用动态端口转发访问网络,需要在系统或应用(浏览器等)中设置一个使用SOCKS5 协议、服务器为 localhost、端口为绑定的端口的代理,利用代理使请求走绑定的端口。
举例来说,如果本地端口是2121,那么动态转发的命令就是下面这样。
ssh -D 2121 user@host -N
下面是动态端口转发建立后的一个使用实例:
curl -x socks5://localhost:2121 http://www.example.com
上面命令中,curl 的 -x 参数指定代理服务器,即通过 SOCKS5 协议的本地 2121 端口,访问 http://www.example.com
。
多级端口转发
端口转发可以有多级,以达到绕过多个防火墙的效果。比如建立两个端口转发,第一个端口转发的出口作为第二个端口转发的入口,通过第二个端口转发访问目标主机目标端口。
例如,本地主机 A 想访问 目的主机 D,但因为防火墙的限制,无法直接连接,现在知道主机 A 可以连接到服务器 B,而服务器 B 可以连接到服务器 C ,服务器 C 可以直接访问目标主机 D,在这种情况下,就可以建立两级连接。
先在主机 A 建立到服务器 B 的本地端口转发:
ssh -L 本地端口:localhost:2999 user_b@服务器 B ip 地址
这个命令在本地主机 A 和 服务器 B 之间建立一个端口转发,2999 是服务器 B 上一个空闲的端口,localhost 表示服务器 B 接收到主机 A 的请求后转发到自己的 2999 端口。这个命令没有加 -N 参数,因为还需要登录服务器 B 建立第二个端口转发。
在登录服务器 B 之后,建立服务器 B 经过服务器 C 到目标主机 D 的端口转发:
ssh -L 2999:目标主机 D:目标端口 user_c@服务器 C ip 地址 -N
这个命令将对 2999 端口的访问请求经由服务器 C 转发到目标主机 D。
这样,最终就通过两级端口转发实现访问主机 A 的端口相当于访问目标主机 D 的目标端口的效果。
七、服务器端 SSH 的配置
开启/重启 ssh 服务
要在服务器开启 ssh 服务需要确保已经安装了服务器端程序:
sudo apt install openssh_server
使用以下命令开启 ssh 服务:
sudo service sshd start
或
/etc/init.d/ssh start
使用以下命令重启 ssh 服务:
sudo service sshd restart
或
/etc/init.d/ssh restart
有时开启 ssh 服务时会失败,提示:sshd: no hostkeys available – exiting,解决方法为:
运行 ssh-keygen -A
命令,然后再次尝试开启 ssh 服务。
查看 ssh 服务
使用以下命令查看服务器的 ssh 服务是否打开:
ps -e | grep ssh
或
sudo service ssh status
或
/etc/init.d/ssh status
关闭 ssh 服务
使用以下命令关闭 ssh 服务:
sudo service sshd stop
或
/etc/init.d/ssh stop
ssh 服务配置文件
sshd 服务的配置文件为 /etc/ssh/sshd_config。
SSH 相关文件
~/.ssh/known_hosts
:存放本机连接过的服务器的公钥;
~/.ssh/authorized_keys
:存放允许基于密钥验证登录本机的客户的公钥;
/etc/ssh/ssh_config
:ssh 客户端的配置文件;
/etc/ssh/sshd_config
:ssh 服务器的配置文件;
参考文章: