SSH详解
ssh的软件架构是服务器-客户端模式(Server - Client)
在这个架构中,SSH软件分成两个部分:
- 向服务器发出请求的部分,称为客户端 client,OpenSSH的实现位ssh
- 接收客户端发出的请求的部分,称为服务器 server, OpenSSH的实现位sshd
OpenSSH还提供一些辅助工具软件(比如ssh-keygen、ssh-agent)和专门的客户端工具(比如scp和ftp)
SSH 客户端
执行远程命令
另一种执行远程命令的方法,是将命令直接写在ssh
命令的后面。
$ ssh username@hostname command
采用这种语法执行命令的时候,不会提供互动式的shell环境。
如果需要互动式环境需要添加一个参数-t
ssh命令行配置项
-c
: 指定加密算法
-C
: 表示压缩数据传输
-D
: 指定本机的Socks监听端口,该端口收到的请求,都将转发到远程的SSH主机,又称动态端口转发
ssh -D 1080 server
将本机1080端口收到的请求,都转发到服务器server
-f
: 表示SSH连接在后台运行
-F
: 参数指定配置文件
-i
: 指定私钥
-l
: 指定远程登录的账户名
-L
: 设置本地端口转发
$ ssh -L 9999:targetServer:80 user@remoteserver
上面命令中,所有发向本地9999
端口的请求,都会经过remoteserver
发往 targetServer 的 80 端口,这就相当于直接连上了 targetServer 的 80 端口
-m
: 指定校验数据完整性的算法(message authentication code,简称MAC)
-N
: 用于端口转发,表示建立的SSH只用于端口转发,不能执行远程命令
-o
: 指定一个配置命令
-p
: 指定ssh客户端连接的服务器端口
-q
: 安静模式(quiet),不向用户输出任何警告信息
-R
: 指定远程端口转发
$ ssh -R 9999:targetServer:902 local
上面命令需在跳板服务器执行,指定本地计算机local
监听自己的 9999 端口,所有发向这个端口的请求,都会转向 targetServer 的 902 端口。
-t
: 参数在ssh直接运行远端命令时,提供一个互动式Shell
-v
: 参数显示详细信息,v可以重复多次,表示信息的详细程度
-V
: 输出ssh客户端的版本
客户端配置文件
个人配置文件的优先级要高于全局配置文件
全局配置文件:/etc/ssh/ssh_config
个人配置文件:~/.ssh/config
功能
按照不同的服务器,列出各自的连接参数,从而不必每一次登录都输入重复的参数
Host *
Port 2222
Host remoteserver
HostName remote.example.com
User neo
Port 2112
Hsot *
代表对所有的主机都生效,缩进不是必须的
remoteserver只是一个别名,可以直接使用ssh remoteserver
命令,就会套用config里面的参数进行连接
SSH密钥登录
密钥(key)是一个非常大的数字,通过加密算法得到。对称加密只需要一个密钥,非对称加密需要两个密钥成对使用,分为公钥和私钥。
如果数据使用公钥加密,那么只有使用对应的私钥才能解密,其他密钥都不行;反过来,如果使用私钥加密(一般称之为签名)也只能使用对应的公钥解密。
验证步骤
预备步骤,客户端通过ssh-keygen
生成自己的公钥和私钥。
ssh-keygen -t dsa
-t
参数用来指定密钥的加密算法,一般会选择dsa
算法或者rsa
算法,此参数必须要指定
第一步,手动将客户端的公钥放入远程服务器的指定位置。
第二步,客户端向服务器发起 SSH 登录的请求。
第三步,服务器收到用户 SSH 登录的请求,发送一些随机数据给用户,要求用户证明自己的身份。
第四步,客户端收到服务器发来的数据,使用私钥对数据进行签名,然后再发还给服务器。
第五步,服务器收到客户端发来的加密签名后,使用对应的公钥解密,然后跟原始数据比较。如果一致,就允许用户登录。
生成密钥以后,建议修改它们的权限,防止其他人读取。
$ chmod 600 ~/.ssh/id_rsa
$ chmod 600 ~/.ssh/id_rsa.pub
手动上传公钥
OpenSSH规定,用户公钥保存在服务器的~./ssh/authorized_keys
文件。要以哪个用户的身份登录到服务器,密钥就必须保存在该用户主目录的~/.ssh/authorized_keys
文件。只要把公钥添加到这个文件之中,就相当于公钥上传到服务器了。每个公钥占据一行。如果该文件不存在,可以手动创建。
用户可以手动编辑该文件,把公钥粘贴进去,也可以在本机计算机上,执行下面的命令。
$ cat ~/.ssh/id_rsa.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
注意,authorized_keys
文件的权限要设为644
,即只有文件所有者才能写。如果权限设置不对,SSH 服务器可能会拒绝读取该文件。
ssh-copy-id 自动上传公钥
OpenSSH 自带一个ssh-copy-id
命令,可以自动将公钥拷贝到远程服务器的~/.ssh/authorized_keys
文件。如果~/.ssh/authorized_keys
文件不存在,ssh-copy-id
命令会自动创建该文件。
用户在本地计算机执行下面的命令,就可以把本地的公钥拷贝到服务器。
$ ssh-copy-id -i key_file user@hosts
上面命令中,-i
参数用来指定公钥文件,user
是所要登录的账户名,host
是服务器地址。如果省略用户名,默认为当前的本机用户名。执行完该命令,公钥就会拷贝到服务器。
注意,公钥文件可以不指定路径和.pub
后缀名,ssh-copy-id
会自动在~/.ssh
目录里面寻找。
$ ssh-copy-id -i id_rsa user@host
端口转发
SSH除了可以登录服务器,还可以作为加密通信的中介,充当两台服务器之间的通信加密跳板,使得原本不加密的通信变成加密通信。这个功能称为端口转发(port forwarding),又称SSH隧道(tunnel)。
主要有两个作用:
- 将不加密数据放在SSH安全连接里面传输,增加不安全网络服务的安全性。
- 作为数据通信的加密跳板,绕过网络防火墙
三种使用方法:
- 动态转发
- 本地转发
- 远程转发
动态转发
动态转发指的是,本机与SSH服务器之间创建一个加密连接,然后本机内部针对某个端口的通信,都通过这个加密连接转发。对于本地转发和远程转发,都存在两个一一对应的端口,分别位于SSH的客户端和服务端,而动态转发只是绑定了一个本地端口,而目标地址:目标端口都是不固定的。目标地址:目标端口是由发起的请求决定的。
使用场景
-
访问所有外部网站,都通过SSH转发。(可做为简易VPN)
-
SSH服务器端运行了多个服务,使用了不同的端口,本地主机需要访问这些服务
为什么需要动态端口转发?
- 由于防火墙的限制,本地主机并不能直接访问远程主机上的服务
- 存在多个服务的话,为每个端口分别创建本地端口转发非常麻烦
ssh -D localhost:port tunnel-host
# example
ssh -D localhost:2121 root@112.3.42.245
在本地发起的请求,需要由Socket代理(Socket Proxy))转发到SSH绑定的2121端口。
以浏览器的proxy代理为例,浏览器发起的请求都会转发到2121端口,然后通过SSH转发到真正的请求地址。
若Node.js服务运行在远程云主机上,则在浏览器中访问localhost:3000即可以访问。如果主机B1能够访问外网的话,则可以访外网……
除了使用浏览器外,也可以通过命令行访问
$ curl -x socks5://localhost:2121 http://www.example.com
本地转发
一般来讲,云主机的防火墙默认只打开了22端口,如果需要访问3000端口的话,需要修改防火墙。为了保证安全,防火墙需要配置允许访问的IP地址。但是,本地公网IP通常是网络提供商动态分配的,是不断变化的。这样的话,防火墙配置需要经常修改,就会很麻烦。
本地转发就是将发送到本地端口的请求,转发到目标端口。这样就可以通过访问本地端口,来访问目标端口的服务。
ssh -L 本机地址:本地端口:目标地址:目标端口
通过本地端口转发,可以将发送到本地主机端口的请求,转发到远程主机端口上。
# 在本地主机登陆远程主机,并进行本地端口转发
ssh -L localhost:2000:local:3000 root@103.59.22.17
这样,在本地主机A1上可以通过访问http://localhost:2000来访问远程云主机3000端口上的服务。
实际上,-L选项中的本地网卡地址是可以省略的,这时表示2000端口绑定了本地主机的所有网卡:
# 在本地主机A1登陆远程云主机B1,并进行本地端口转发。2000端口绑定本地所有网卡
ssh -L 2000:localhost:3000 root@103.59.22.17
若本地主机A2能够访问A1,则A2也可以通过A1访问远程远程云主机B1上的Node.js服务。
另外,-L选项中的目标地址也可以是其他主机的地址。假设远程云主机B2的局域网IP地址为192.168.59.100,则可以这样进行端口转发:
# 在本地主机A1登陆远程云主机B1,并进行本地端口转发。请求被转发到远程云主机B2上
ssh -L 2000:192.168.59.100:3000 root@103.59.22.17
远程转发
应用场景:本地主机运行了一个服务,端口为3000,而本地主机没有一个固定的公网ip地址,远程主机需要访问这个服务
如果本地主机有一个独立公网ip,那么我们可以通过本地转发来访问本地主机上的服务。
但是大部分的时候本地主机都是在局域网内,与很多设备共享一个ip。
因此遇到这样的情况,我们需要远程转发来解决。
定义
远程转发就是将发送到远程端口的请求,转发到目标端口。这样就可以通过访问远程端口,来访问目标端口的服务。
-R 远程网卡地址:远程端口:目标地址:目标端口
通过远程转发,可以将发送到远程云主机2000端口的请求,转发到本地主机的3000端口。
# 在本地主机A1登陆远程主机B1,并进行远程端口转发
ssh -R localhost:2000:local:3000 root@103.59.22.17
这样,在远程主机A1可以通过访问http://localhost:2000来访问本地主机的服务。
同理,远程网卡地址可以省略,目标地址也可以是其他主机地址。假设本地主机A2的局域网IP地址为192.168.0.100
# 在本地主机A1登陆远程云主机B1,并进行远程端口转发
ssh -R 2000:192.168.0.100:3000 root@103.59.22.17
参考文章
1.https://wangdoc.com/ssh/port-forwarding.html
2.https://blog.fundebug.com/2017/04/24/ssh-port-forwarding/