Cwww3's Blog

Record what you think

0%

SSH

SSH(Secure Shell 的缩写)是一种网络协议,用于加密两台计算机之间的通信,并且支持各种身份验证机制。主要用于保证远程登录和远程通信的安全。

SSH 的软件架构是服务器-客户端模式(Server - Client)。在这个架构中,SSH 软件分成两个部分:向服务器发出请求的部分,称为客户端(client),OpenSSH 的实现为 ssh;接收客户端发出的请求的部分,称为服务器(server),OpenSSH 的实现为 sshd

客户端安装ssh

Linux 系统一般都自带 ssh,如果没有就需要安装。

1
2
3
4
# Ubuntu
$ sudo apt install openssh-client
# 查看版本号
$ ssh -V

连接远程机器

1
2
# p 指定端口号 默认22  user为用户名 hostname为连接的主机
$ ssh -p port user@hostname
  • 首次登录会出现提醒。表示不认识这台机器,提醒用户确认是否需要连接。
1
2
3
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)?
  • 服务器指纹,指的是 SSH 服务器公钥的哈希值。每台 SSH 服务器都有唯一一对密钥,用于跟客户端通信,其中公钥的哈希值就可以用来识别服务器。

  • ssh 会将本机连接过的所有服务器公钥的指纹,都储存在本机的~/.ssh/known_hosts文件中。每次连接服务器时,通过该文件判断是否为陌生主机(陌生公钥)。

  • 然后,客户端就会跟服务器建立连接。接着,ssh 就会要求用户输入所要登录账户的密码。用户输入并验证密码正确以后,就能登录远程服务器的 Shell 了。

客户端配置文件

SSH 客户端的全局配置文件是/etc/ssh/ssh_config,用户个人的配置文件在~/.ssh/config,优先级高于全局配置文件。

  • 按照不同服务器,列出各自的连接参数,从而不必每一次登录都输入重复的参数
1
2
3
4
Host remoteserver
HostName remote.example.com
User neo
Port 2112
  • 登录remote.example.com时,只要执行ssh remoteserver命令,就会自动套用 config 文件里面指定的参数。
1
2
3
$ ssh remoteserver
# 等同于
$ ssh -p 2112 neo@remote.example.com

SSH 密钥登录

  • SSH 默认采用密码登录,这种方法有很多缺点,简单的密码不安全,复杂的密码不容易记忆,每次手动输入也很麻烦。密钥登录是比密码登录更好的解决方案。

  • 密钥(key)是一个非常大的数字,通过加密算法得到。对称加密只需要一个密钥,非对称加密需要两个密钥成对使用,分为公钥(public key)和私钥(private key)。

密钥登录的过程
  • 客户端通过ssh-keygen生成自己的公钥和私钥。

    • ssh-key 生成密钥
    1
    2
    3
    4
    5
    $ ssh-keygen -t rsa -b 4096 -C "your_email@domain.com"
    # 生成的密钥文件默认就会是~/.ssh/id_rsa(私钥)和~/.ssh/id_rsa.pub(公钥)。
    # -t 指定加密算法
    # -b 参数指定密钥的二进制位数。这个参数值越大,密钥就越不容易破解,但是加密解密的计算开销也会加大。
    # -C 参数可以为密钥文件指定新的注释,格式为username@host。
  • 手动将客户端的公钥放入远程服务器的指定位置。

    • OpenSSH 规定,用户公钥保存在服务器的~/.ssh/authorized_keys文件。你要以哪个用户的身份登录到服务器,密钥就必须保存在该用户主目录的~/.ssh/authorized_keys文件。
  • 客户端向服务器发起 SSH 登录的请求。

  • 服务器收到用户 SSH 登录的请求,发送一些随机数据给用户,要求用户证明自己的身份。

  • 客户端收到服务器发来的数据,使用私钥对数据进行签名,然后再发还给服务器。

  • 服务器收到客户端发来的加密签名后,使用对应的公钥解密,然后跟原始数据比较。如果一致,就允许用户登录。

服务器安装sshd

如果没有安装 sshd,可以用下面的命令安装。

1
2
# Ubuntu
$ sudo apt-get install openssh-server
1
2
3
4
5
6
7
8
# 启动  默认启动
$ sshd
# 如果提示“sshd re-exec requires execution with an absolute path”,就需要使用绝对路径来启动。这是为了防止有人出于各种目的,放置同名软件在$PATH变量指向的目录中,代替真正的 sshd。
# no hostkeys available —- exiting
# 需要生成ssh公钥私钥(用户也要对应,一般是root)
# Missing privilege separation directory: /run/sshd 需要创建该目录
$ /usr/sbin/sshd &
# & 表示后台运行
1
2
3
4
5
6
7
8
9
# 通过 Systemd 管理 sshd
# 启动
$ sudo systemctl start sshd.service
# 停止
$ sudo systemctl stop sshd.service
# 重启
$ sudo systemctl restart sshd.service
# 自启动
$ sudo systemctl enable sshd.service

sshd 配置文件

  • sshd 的配置文件在/etc/ssh目录,主配置文件是sshd_config
1
2
3
4
5
6
7
8
# sshd 配置项
AllowUsers指定允许登录的用户,用户名之间使用空格分隔
AllowTcpForwarding 指定是否允许端口转发
Ciphers指定 sshd 可以接受的加密算法 多个算法之间使用逗号分隔。
ListenAddress指定 sshd 监听的本机 IP 地址,即 sshd 启用的 IP 地址,默认是 0.0.0.0(ListenAddress 0.0.0.0)表示在本机所有网络接口启用。
PasswordAuthentication指定是否允许密码登录
Port指定 sshd 监听的端口,即客户端连接的端口,默认是22
PubKeyAuthentication指定是否允许公钥登录
1
2
3
4
# 修改配置文件以后,可以使用下面的命令验证,配置文件是否有语法错误
$ sshd -t
# 新的配置文件生效,必须重启 sshd
$ sudo systemctl restart sshd

SSH 端口转发

SSH 除了登录服务器,还有一大用途,就是作为加密通信的中介,充当两台服务器之间的通信加密跳板,使得原本不加密的通信变成加密通信。这个功能称为端口转发(port forwarding),又称 SSH 隧道(tunnel)。

端口转发有两个主要作用:

  1. 将不加密的数据放在 SSH 安全连接里面传输,使得原本不安全的网络服务增加了安全性,比如通过端口转发访问 Telnet、FTP 等明文服务,数据传输就都会加密。
  2. 作为数据通信的加密跳板,绕过网络防火墙。

端口转发有三种使用方法:动态转发,本地转发,远程转发。

动态转发

  • 本机与 SSH 服务器之间创建了一个加密连接,然后本机内部针对某个端口的通信,都通过这个加密连接转发。它的一个使用场景就是,访问所有外部网站,都通过 SSH 转发。
  • 动态转发需要把本地端口绑定到 SSH 服务器。至于 SSH 服务器要去访问哪一个网站,完全是动态的,取决于原始通信,所以叫做动态转发。
1
2
3
4
5
$ ssh -D local-port tunnel-host -N
# -D 表示动态转发
# local-port是本地端口
# tunnel-host是 SSH 服务器,
# -N表示这个 SSH 连接只进行端口转发,不登录远程 Shell,不能执行远程命令,只能充当隧道。
1
2
3
4
5
$ ssh -D 2121 tunnel-host -N
# 注意,这种转发采用了 SOCKS5 协议。访问外部网站时,需要把 HTTP 请求转成 SOCKS5 协议,才能把本地端口的请求转发出去。

$ curl -x socks5://localhost:2121 http://www.example.com
# -x 指定代理服务器 通过SOCKS5协议的本地2121端口,访问http://www.example.com。

本地转发

  • SSH 服务器作为中介的跳板机,建立本地计算机与特定目标网站之间的加密连接。本地转发是在本地计算机的 SSH 客户端建立的转发规则。
  • 它会指定一个本地端口(local-port),所有发向那个端口的请求,都会转发到 SSH 跳板机(tunnel-host),然后 SSH 跳板机作为中介,将收到的请求发到目标服务器(target-host)的目标端口(target-port)。
1
2
3
4
5
6
$ ssh -L local-port:target-host:target-port tunnel-host
# -L参数表示本地转发
# local-port是本地端口
# target-host是你想要访问的目标服务器
# target-port是目标服务器的端口
# tunnel-host是 SSH 跳板机。
1
2
3
4
5
$ ssh -L 2121:www.example.com:80 tunnel-host -N -f
# -f 表示 SSH 连接在后台运行

$ curl http://localhost:2121
# 注意,本地端口转发采用 HTTP 协议,不用转成 SOCKS5 协议。

远程转发

  • 这种场景比较特殊,主要针对内网的情况。本地计算机在外网,SSH 跳板机和目标服务器都在内网,而且本地计算机无法访问内网之中的 SSH 跳板机,但是 SSH 跳板机可以访问本机计算机。
  • 由于本机无法访问内网 SSH 跳板机,就无法从外网发起 SSH 隧道,建立端口转发。必须反过来,从 SSH 跳板机发起隧道,建立端口转发,这时就形成了远程端口转发。
1
2
3
4
5
6
7
8
$ ssh -R local-port:target-host:target-port local -N
# 首先需要注意,不是在本机执行的,而是在 SSH 跳板机执行的 从跳板机去连接本地计算机
# -R参数表示远程端口转发
# local-port是本地计算机的端口
# target-host和target-port是目标服务器及其端口
# local是本地计算机。

# 显然,远程端口转发要求本地计算机也安装了 SSH 服务器,这样才能接受 SSH 跳板机的远程登录。
1
2
3
4
$ ssh -R 2121:www.example.com:80 local -N
# 执行上面的命令以后,跳板机到本地计算机的隧道已经建立了。
# 可以从本机访问目标服务器了,即在本机执行下面的命令。
$ curl http://localhost:2121
Donate comment here.