logo

SSH

王哲峰 / 2021-01-25


目录

安装 SSH 服务(Debina)

安装 SSH

# 更新软件源
$ sudo apt-get update

# 安装 SSH
$ sudo apt-get install ssh

# 修改配置文件
$ vim /etc/ssh/sshd_config
PasswordAuthentication yes
PermitRootLogin yes

# 重启 SSH 服务
$ /etc/init.d/ssh restart

此时我们就可以使用 SSH 对服务器进行远程连接, 但也不排除无法连接的情况

SSH 无法连接的场景排查

检查网络

很多情况下, 无法使用 SSH 进行连接都是因为网络或者 iptables 限制导致的, 所以可以通过 ping 命令来检查网络的连通性

当然, 有时候也会遇到防火墙拦截了 ICMP 和 SSH, 所以检查网络最好是在需要 SSH 连接的服务下进行

$ ping xxx.xxx.xxx.xxx

检查端口

SSH 使用的是 22 端口, 所以可以使用 telnet 命令检测该端口

$ telnet xxx.xxx.xxx.xxx 22

检查服务

如果网络能通, 且端口未被屏蔽, 依旧无法使用 SSH 连接到服务器, 那么检查 SSH 服务, 以及配置文件是否正确配置

$ /etc/init.d/ssh status

什么是 SSH

ssh

SSH 是 Secure Shell 的缩写, 也叫安全外壳协议, 是一种网络协议, 用于计算机之间的安全远程加密登录.

最早的时候, 互联网通信都是明文通信, 一旦被截获, 内容就暴露无疑.

1995年, 芬兰学者 Tatu YIonen 设计了 SSH 协议, 将登录信息全部加密, 成为互联网安全的一个基本解决方案, 迅速在全世界获得推广, 目前已经成为 Linux 系统的标准配置.

SSH 工作原理

ssh

SSH 的安全性比较好, 其对数据进行加密的方式主要有两种:

SSH 存在的问题–中间人攻击

SSH 之所以能够保证安全, 原因在于它采用了公钥加密, 这个过程本身是安全的, 但是实际用的时候存在一个风险: 如果有人截获了登录请求, 然后冒充远程主机, 将伪造的公钥发送给用户, 那么用户很难辨别真伪. 因为不像 HTTPS 协议, SSH 协议的公钥是没有证书中心(CA)公证的, 也就是说, 都是自己签发的.

可以设想, 如果攻击者插在用户与远程主机之间(比如公共的 WIFI 区域), 用伪造的公钥获取用户的登录密码. 再用这个密码登录远程主机, 那么 SSH 的安全机制就荡然无存了. 这种风险就是著名的 “中间人攻击(Man-in-the-middle attack)”.

SSH 功能

操作场景

操作系统

鉴权方式

前提条件

SSH 口令登录

操作步骤

  1. 执行以下命令,连接 Linux 云服务器。
# 如果没有修改默认端口可以省略端口参数 `-p 22`
ssh -p 22 <username>@<hostname or IP address>
  1. 输入已经获取的密码,按 Enter,即可完成登录。

问题处理

如果是第一次登录远程机, 会出现以下提示:

$ ssh user@host
# The authenticity of host 'host (12.18.429.21)' can't be established.
# RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.
# Are you sure you want to continue connecting (yes/no)? 

当用户发送登录请求 ssh user@host 给远程主机后, 远程主机发送自己的公钥给用户. 因为公钥长度较长(采用 RSA 算法, 长达 1024 位), 很难比对, 所以对其进行 MD5 计算, 将它变成一个 128 位的指纹. 如: 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d, 这样比对就容易多了.

经过对比后, 如果用户接受这个远程主机的公钥, 系统会出现一句提示语, 表示 host 主机已经得到认可, 然后输入登录密码就可以登录了:

Warning: Permanently added 'host,12.18.429.21' (RSA) to the list of known hosts.

当远程主机的公钥被接受以后, 它就会被保存在文件 ~/.ssh/known_hosts 之中. 下次再连接这台主机, 系统就会认出它的公钥已经保存在本地了, 从而跳过警告部分, 直接提示输入密码. 每个 SSH 用户都有自己的 known_hosts 文件, 此外系统也有一个这样的文件, 一般是 /etc/ssh/ssh_known_hosts, 保存一些对所有用户都可信赖的远程主机的公钥.

SSH 公钥登录

使用密码登录, 每次都必须输入密码, 非常麻烦. 好在 SSH 还提供了公钥登录, 可以省去输入密码的步骤. 所谓"公钥登录", 原理很简单, 就是用户将自己的公钥储存在远程主机上. 登录的时候, 远程主机会向用户发送一段随机字符串, 用户用自己的私钥加密后, 再发回来. 远程主机用事先储存的公钥进行解密, 如果成功, 就证明用户是可信的, 直接允许登录 shell, 不再要求密码. 这种方法要求用户必须提供自己的公钥.

如果没有现成的, 可以直接用 ssh-keygen 生成一个。 下面创建本地 SSH 密钥,如果生成密钥匙不输入密码,则可以实现免密登录

$ ssh-keygen
ssh-keygen -t rsa -b 4096 -C "email@example.com"

运行上面的命令以后, 系统会出现一系列提示, 可以一路回车. 其中有一个问题是, 要不要对私钥设置口令(passphrase), 如果担心私钥的安全, 这里可以设置一个. 运行结束以后, 在 ~/.ssh/ 目录下, 会新生成两个文件: id_rsa.pubid_rsa. 前者是公钥, 后者是私钥.

这时再输入下面的命令, 将本地 SSH 公钥上传到远程服务器:

$ ssh-copy-id user@host
# or
$ scp ~/.ssh/id_rsa.pub <username>@<hostname or IP address>:/home/username/.ssh/

远程主机将用户的公钥保存在登录后的用户主目录的 ~/.ssh/authorized_keys 服务的默认验证文件中. 这样, 以后就登录远程主机不需要输入密码了.

cd ~/.ssh
cat /home/username/.ssh/id_rsa.pub >> authorized_keys

如果还是不行, 就用 vim 打开远程主机的 /etc/ssh/sshd_config 这个文件, 将以下几行的注释去掉.

RSAAuthentication yes 
PubkeyAuthentication yes 
AuthorizedKeysFile 
.ssh/authorized_keys

然后, 重启远程主机的ssh服务:

# Redhat6系统
$ service ssh restart

# Redhat7系统
$ systemctl restart sshd

# ubuntu系统
$ service ssh restart

# debian系统
$ /etc/init.d/ssh restart

配置本地 ssh config 文件

vim ~/.ssh/config
Host cssor_server                       # 别名,域名缩写
    HostName cssor.com                  # <hostname or IP address>
    User cssor                          # <username>
    PreferredAuthentications publickey  # 有些情况或许需要加入此句,优先验证类型ssh
    IdentityFile ~/.ssh/id_rsa          # 本地私钥文件的路径

最后, 登录远程主机:

$ ifconfig                 # 查看服务器 IP 地址
$ netstat -ntlp | grep ssh # 查看服务器是否启用了 SSH
$ ssh -p 22 user@host
$ ssh user@host            # 默认端口为 22, 当端口为 22时, 可以省略端口
$ ssh host                 # 本地使用的用户名与远程登录的用户名一致, 登录的用户名可以省略

退出远程服务器:

$ exit                     # 退出服务器, 或者 `Control D`

SSH 常用软件

下载

# 下载脚本:
curl -o finalshell_install.sh www.hostbuf.com/downloads/finalshell_install.sh
# 授权+执行:
chmod +x finalshell_install.sh
sudo ./finalshell_install.sh

SSH 端口转发

SSH 不仅能够自动加密和解密 SSH 客户端与服务端之间的网络数据, 同时, SSH 还能够提供一个非常有用的功能, 端口转发, 即将 TCP 端口的网络数据转发到指定的主机的某个端口上. 在转发的同时会对数据进行相应的加密和解密. 如果工作环境中防火墙限制了一些网络端口的使用, 但是允许 SSH 的连接, 那么也是能够使用 SSH 转发后的端口进行通信.

SSH 端口转发有三种: 动态端口转发、本地端口转发、远程端口转发

举例说明: 假设有三台主机: host1、host2、host3

端口转发的参数

本地转发

有本地网络服务器的某个端口, 转发到远程服务器的某个端口. 说白了就是, 将发送到本地端口的请求, 转发到目标端口, 格式如下:

$ ssh -L 本地网卡地址:本地端口:目标地址:目标端口 user@host
$ ssh -L 本地端口:目标地址:目标端口 user@host            # 本地网卡地址可以省略
$ ssh -L 本地网卡地址:本地端口:目标地址:目标端口           # 如果本地用户名与远程用户名相同, 用户名可以省略

举例说明:

# current on centOS A
$ ssh -L 127.0.0.1:3306:127.0.0.1:3306 root@192.168.13.142
$ bin/mysql -h127.0.0.1 -uroot -p
$ lsof -i:3306 # 查看 SSH 转发监听的进程
$ netstat -apn |grep 3306
$ ps -ef |grep ssh

远程转发

远程转发是指由远程服务器的某个端口转发到本地网络服务器的某个端口. 说白了, 就是将发送到远程端口的请求转发到目标端口, 格式如下:

$ ssh -R 远程网卡地址:远程端口:目标地址:目标端口

举例说明:

# current on centOS A
$ ssh -R 127.0.0.1:80:10.18.78.135:80 root@192.168.13.142

动态端口转发

对于 SSH 的本地转发和远程转发, 都需要将本地端口和远程端口一一绑定, 格式如下:

$ ssh -D [本地地址:]本地端口 user@host

SSH 远程操作

SSH 远程操作, 主要用于在远程服务器上面执行某个操作, 格式如下:

$ ssh user@host 'command'

示例:

user_a $ ssh user_b@host_b 'uname -a' # 在 user_a 上查看 user_b 的操作系统
user_a $ tar -cz test | ssh user_b@host_b 'tar -xz'
user_a $ ssh user_b@host_b 'netstat -tln |grep 1080' # 在 user_a 上查看 user_b 是否监听了 1080 端口

利用远程转发实现代理功能

目前 B 机器, 只能在自己 127.0.0.1 的 80 端口监听并转发, 如何让 B 机器作为代理, 转发其他机器的请求到 A 机器上面呢?比如, 现在有一台机器 C(192.168.13.143), C 不能访问 A, 但是能够访问 B. 如何让 C 利用 B 来访问 A 呢?

此时, 需要将 B 的监听, 由 127.0.0.1:8081, 改为 0:0.0.0:8081, 修改 sshd 的配置 /etc/ssh/sshd_config:

vim /etc/ssh/sshd_config
#如果有
GatewayPorts no
#改为
GatewayPorts yes
#没有, 添加即可

然后重启 sshd

$ sudo service sshd restart

然后重新, 设置动态转发, 如下:

$ ssh -f -g  -N -R 8081:127.0.0.1:80 dequan@192.168.13.149

可以看到, 此时 B 机器, 已经监听了 0:0.0.0:8081

在C机器上面, 我们通过curl模拟请求, 利用B机器做代理, 如下:

$ curl -x 192.168.13.149:8081 127.0.0.1

当然, 如果还有其他机器, 也可以使用类似的方式, 来请求A机器.

内网、外网穿透

cpolar 内网穿透服务

#软件安装
curl -L https://www.cpolar.com/static/downloads/install-release-cpolar.sh | sudo bash

#查看版本
cpolar version

#添加个人账户token认证
cpolar authtoken xxxxxxx

#开启穿透http端口服务
cpolar http 8080
#指定二级域名 -subdomain=YOUR_SUBDOMAIN 

#开启穿透ssh端口服务
cpolar tcp 22
#指定二级域名 -remote-addr=1.tcp.vip.cpolar.cn:xxxxx

#向系统添加服务
sudo systemctl enable cpolar

#启动服务
sudo systemctl start cpolar

#查看状态
sudo systemctl status cpolar