目录

SSH入门

概述

SSH是Linux系统的登录工具,现在广泛用于服务器登录和各种加密通信

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

ssh客户端

ssh登录

信息
若不指定密钥进行登录,默认是会使用~/.ssh文件夹下的id_rsa, id_dsa, id_ecdsa, id_ecdsa_sk, id_ed25519, id_ed25519_sk, id_xmss等密钥。
1
2
3
4
5
6
# 使用user登录服务器server_ip
ssh user@server_ip
# 指定密钥登录服务器
ssh user@server_ip -i id_rsa
# 指定端口2222,连接ssh服务器
ssh user@server_ip -p 2222

此外还可以使用ssh-add命令来把密钥添加到高速缓存中,从而不需要指定对应的密钥,提升登录速度。

信息

使用ssh-add之前是需要开启ssh-agent的,很多操作系统默认是不开启的,可以通过下面的方式开启:

1
2
3
ssh-agent bash
# 若是zsh,则执行下面的内容
ssh-agent zsh
警告
ssh-add添加的密钥只在当前会话中有效,结束会话之后便失效。

主要包含以下几个命令:

  1. 添加密钥到缓存
    1
    2
    
    # 添加~/.ssh/id_dsa
    ssh-add ~/.ssh/id_dsa
    
  2. 列出所有添加的密钥
    1
    
    ssh-add -l
    
示例: 使用ssh密钥克隆代码

配置了上述ssh-add之后,应该能够顺利的通过下面的命令克隆代码

1
git clone [email protected]:poetlife/blog.git

相比与这种方式,在~/.ssh/config文件中配置可能会更为简单有效,配置示例如下:

1
2
3
4
Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_rsa

ssh端口转发

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

端口转发有两个主要作用

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

术语

  1. Client Host:也就是我们运行ssh命令定义隧道(tunnel)的一端
  2. Tunnel Host:其实就是建立隧道连接的一端
  3. Ingress:我们称初始化的那一端,或者进入的那一端,或者监听的那一端为ingress
  4. Egress:退出、终止的一端为egress

动态转发

动态转发指的是,本机与 SSH 服务器之间创建了一个加密连接,然后本机内部针对某个端口的通信,都通过这个加密连接转发。它的一个使用场景就是,访问所有外部网站,都通过 SSH 转发。动态转发需要把本地端口绑定到 SSH 服务器。至于 SSH 服务器要去访问哪一个网站,完全是动态的,取决于原始通信,所以叫做动态转发。

1
ssh -D local-port tunnel-host -N

上面命令中,-D表示动态转发,local-port是本地端口,tunnel-host是 SSH 服务器,-N表示这个 SSH 连接只进行端口转发,不登录远程 Shell,不能执行远程命令,只能充当隧道。

如果经常使用动态转发,可以将设置写入 SSH 客户端的用户个人配置文件(~/.ssh/config)。

1
2
3
4
5
6
# ~/.ssh/config
Host pengjd
    HostName 10.192.10.153
    Port 22
    User jakie
    DynamicForward :9999
动态转发示例

将本机的9999端口都转发到guopai16(10.176.22.16)

1
ssh -D 9999 guopai16 -N

注意,这种转发采用了 SOCKS5协议。访问外部网站时,需要把 HTTP 请求转成 SOCKS5协议,才能把本地端口的请求转发出去。 下面是 SSH 隧道建立后的一个使用实例:

1
2
3
4
# 可以发现学校内网是可以进行访问的
curl -x socks5://localhost:9999 https://www.fudan.edu.cn/
# 访问外网可以发现需要进行网络登录
curl -x socks5://localhost:9999 https://baidu.com/

本地转发

本地转发(local forwarding)指的是,SSH 服务器作为中介的跳板机,建立本地计算机与特定目标网站之间的加密连接。本地转发是在本地计算机的 SSH 客户端建立的转发规则。

它会指定一个本地端口(local-port),所有发向那个端口的请求,都会转发到 SSH 跳板机(tunnel-host),然后 SSH 跳板机作为中介,将收到的请求发到目标服务器(target-host)的目标端口(target-port)。

1
ssh -L local-port:target-host:target-port tunnel-host

上面命令中,-L参数表示本地转发,local-port是本地端口,target-host是你想要访问的目标服务器,target-port是目标服务器的端口,tunnel-host是 SSH 跳板机。

还可以添加-g 参数,表示将本地当作网关,其他可以访问本机的机器可以通过local-port访问到target-port

注意,本地端口转发采用 HTTP协议,不用转成 SOCKS5协议。

如果经常使用本地转发,可以将设置写入 SSH 客户端的用户个人配置文件(~/.ssh/config)。

1
2
3
# ~/.ssh/config
Host test.example.com
LocalForward client-IP:client-port server-IP:server-port
本地转发示例1

现在有一台 SSH 跳板机guopai16,我们想要通过这台机器,在本地9999端口与目标网站www.baidu.com80端口之间建立 SSH 隧道,就可以写成下面这样。

1
ssh -L 9999:www.baidu.com:80 guopai16 -N

接着我们在本地访问localhost:9999/,在guopai16上抓取80端口的流量如下: https://blog-1253312362.cos.ap-shanghai.myqcloud.com/image.png

本地转发示例2:当作网关
1
ssh -L 8000:10.176.22.11:8000 guopai13 -N -g

则别的机器可以通过当前本机的ip:8000访问10.176.22.11:8000。

远程转发

远程转发指的是在远程 SSH 服务器建立的转发规则。 它跟本地转发正好反过来。建立本地计算机到远程计算机的 SSH 隧道以后,本地转发是通过本地计算机访问远程计算机,而远程转发则是通过远程计算机访问本地计算机。它的命令格式如下。

1
ssh -R remote-port:target-host:target-port -N remotehost

上面命令中,-R参数表示远程端口转发,remote-port是远程计算机的端口,target-hosttarget-port是目标服务器及其端口,remotehost是远程计算机。

如果经常执行远程端口转发,可以将设置写入 SSH 客户端的用户个人配置文件(~/.ssh/config)。

1
2
3
4
# ~/.ssh/config
Host remote-forward
  HostName test.example.com
  RemoteForward remote-port target-host:target-port
远程转发示例

内网某台服务器localhost80端口开了一个服务,可以通过远程转发将这个 80 端口,映射到具有公网 IP 地址的my.public.server服务器的 8080端口,使得访问my.public.server:8080这个地址,就可以访问到那台内网服务器的 80端口。

1
ssh -R 8080:localhost:80 -N my.public.server

ssh-keygen生成密钥

密钥登录时,首先需要生成公钥和私钥。OpenSSH 提供了一个工具程序ssh-keygen命令,用来生成密钥。 ssh-keygen常用选项如下所示:

-b指定密钥的二进制位数

这个参数值越大,密钥就越不容易破解,但是加密解密的计算开销也会加大。一般来说,-b至少应该是1024,更安全一些可以设为2048或者更高。

-C 指定密钥的注释

格式为username@host。下面命令生成一个4096位 RSA 加密算法的密钥对,并且给出了用户名和主机名。

1
ssh-keygen -t rsa -b 4096 -C "[email protected]"

-f 指定生成的私钥文件

1
2
# 上面命令会在当前目录生成私钥文件mykey和公钥文件mykey.pub。
ssh-keygen -t dsa -f mykey

-F 检查某个主机名是否在known_hosts文件里面

1
ssh-keygen -F example.com

-N指定私钥的密码(passphrase)

1
ssh-keygen -t dsa -N secretword

-p重新指定私钥的密码(passphrase)

-p参数用于重新指定私钥的密码(passphrase)。它与-N的不同之处在于,新密码不在命令中指定,而是执行后再输入。ssh 先要求输入旧密码,然后要求输入两遍新密码。

-R将指定的主机公钥指纹移出known_hosts文件

1
ssh-keygen -R example.com

-t指定生成密钥的加密算法,一般为dsarsa

1
2
# 生成2048位的rsa的公钥/私钥并保存名称位jakie
ssh-keygen -t rsa -b 2048 -f jakie

SSH服务器配置

安装基本的依赖

1
2
3
4
# ubuntu
sudo apt install openssh-server
# arch
pacman -S openssh-server

修改sshd配置信息

1
2
3
4
# 切换到对应的目录
cd /etc/ssh
# 修改对应的配置信息
sudo vi sshd_config

我们可以修改一下信息:

1
2
pubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

给对应用户下增加公钥

生成密钥以后,公钥必须上传到服务器,才能使用公钥登录。

手动设置公钥

OpenSSH 规定,用户公钥保存在服务器的~/.ssh/authorized_keys文件。你要以哪个用户的身份登录到服务器,密钥就必须保存在该用户主目录的~/.ssh/authorized_keys文件。只要把公钥添加到这个文件之中,就相当于公钥上传到服务器了。每个公钥占据一行。如果该文件不存在,可以手动创建。

用户可以手动编辑该文件,把公钥粘贴进去,也可以在本机计算机上,执行下面的命令。

1
cat ~/.ssh/id_rsa.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

上面示例中,user@host要替换成你所要登录的用户名和主机名。 注意,authorized_keys文件的权限要设为644,即只有文件所有者才能写。如果权限设置不对,SSH 服务器可能会拒绝读取该文件。

自动上传公钥

OpenSSH 自带一个ssh-copy-id命令,可以自动将公钥拷贝到远程服务器的~/.ssh/authorized_keys文件。如果~/.ssh/authorized_keys文件不存在,ssh-copy-id命令会自动创建该文件。

1
ssh-copy-id -i key_file user@host

上面命令中,-i参数用来指定公钥文件,user是所要登录的账户名,host是服务器地址。如果省略用户名,默认为当前的本机用户名。执行完该命令,公钥就会拷贝到服务器。

默认情况下会与同名的私钥进行校验,如果想不校验的话,那么直接加上-f

重新启动ssh服务

1
2
3
4
# systemd作为init.d
sudo systemctl restart sshd
# 早期的重启方式
sudo /etc/init.d/ssh restart

常见问题

验证私钥后,仍要求输入密码

在私钥验证通过后,仍然要求输入对应密码的情况下,需要检查以下事项:

  1. 检查~/.ssh/authorized_keys的权限是否为600,且组权限是否正常
  2. 检查~/.ssh文件夹的权限是否为700,且组权限是否正常
  3. 检查~/.ssh/authorized_keys是否和私钥匹配,且组权限是否正常

连接服务器时间过长

有可能是因为DNS反查时间太长了,在/etc/ssh/sshd_config中将useDNS修改为no,然后执行如下语句重启ssh服务器。

1
sudo systemctl restart sshd

如何debug ssh

开启debug模式下的sshd:

1
/usr/sbin/sshd -d -p 2222 # 在2222上开启debug模式

然后在指定连接这个端口下的ssh,无论连接是否成功,一次连接后,在服务器上都会显示对应的日志。

参考文献

  1. ssh登录原理解析
  2. ssh基本用法
  3. 基于SSH的文件传输:scp命令
  4. 阮一峰 SSH入门教程
  5. An Illustrated Guide to SSH Tunnels, Scott Wiersdorf
  6. OpenSSH - ArchWiki (archlinux.org)
  7. ssh_config(5) — Arch manual pages (archlinux.org)
  8. sshd 设置黑白名单 - 简书 (jianshu.com)