ssh 知识整理

这并不是一篇指导性的文章,而是一篇初学者的笔记,可能会随时改动,其中也可能遍布错误。此外,我感觉这更像是一篇冗长啰嗦的唠叨。😪

以下代码都是在 Debian 系统上进行的操作。

基础

一般情况下 linux 默认安装了 ssh。linux 一般使用 ssh 连接。
没有安装的话可以按照一下命令安装

1
2
3
4
5
6
#安装
sudo apt-get install openssh-server # 对于Debian/Ubuntu
sudo yum install openssh-server # 对于RHEL/CentOS
#启动
sudo service ssh start # 对于系统V(System V)初始化系统
sudo systemctl start ssh # 对于使用systemd的系统

/etc/ssh/sshd_config 可以调整 ssh 配置,调整前先备份。

1
2
3
4
5
cp /etc/ssh/sshd_config /etc/ssh/backup.sshd_config # 备份好习惯
sudo vim /etc/ssh/sshd_config # 使用vim
sudo nano /etc/ssh/sshd_config # 使用nano
sshd -t #用于测试ssh服务器配置文件是否正确
sudo service ssh restart # 修改完之后重启

ssh 最常见的用途之一是远程登录到远程服务器或计算机。

1
ssh username@remote_host # 替换为你的username和remote_host

第一次连接服务器大概会出现以下情况。

1
2
3
4
5
6
7
8
9
The authenticity of host '1.1.1.1 (1.1.1.1)' can't be established.
ED25519 key fingerprint is SHA256:rqEuH0ymaN0LbB1vPKzRpfgew+5hBusv1/ReDGJ6bDs.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?
# 翻译
无法确定主机 "1.1.1.1 (1.1.1.1) "的真实性。
ED25519 密钥指纹为SHA256:rqEuH0ymaN0LbB1vPKzRpfgew+5hBusv1/ReDGJ6bDs。
此密钥没有其他名称
您确定要继续连接(是/否/[指纹])?

这个消息通常是在你首次通过 ssh 连接到一个远程服务器时出现的。它是 ssh 的一种安全机制,目的是确保你连接的服务器确实是你想要连接的服务器,而不是中间人攻击(Man-in-the-Middle Attack)的一部分。

一般情况下只需按下”Enter” 即可。
接下来就是输入密码,linux 默认输入密码的时候不会有任何提示或者显示。输入完密码再次回车即可登入服务器。
以后将不会再次提醒无法验证该服务器的真实性,因为 ssh 客户端将保存该主机的公钥,并在将来的连接中使用它来验证主机的真实性。
不过,当服务器修改了公钥或者对系统进行了重置,那么 ssh 客户端会在下一次连接中警告称:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256: [新的密钥指纹]
Please contact your system administrator.
Add correct host key in /path/to/your/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /path/to/your/.ssh/known_hosts:1
remove with:
ssh-keygen -f "/path/to/your/.ssh/known_hosts" -R [服务器IP地址]
ECDSA host key for [服务器IP地址] has changed and you have requested strict checking.
Host key verification failed.
# 翻译
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@ 警告:远程主机标识已更改! @

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
有可能有人在做坏事!
可能有人正在窃听你(中间人攻击)!
也有可能是主机密钥刚刚被更改。
远程主机发送的 ECDSA 密钥的指纹是
sha256: [新的密钥指纹] 请联系系统管理员。
请联系系统管理员。
在 /path/to/your/.ssh/known_hosts 中添加正确的主机密钥,以消除此信息。
在 /path/to/your/.ssh/known_hosts:1 中的违规 ECDSA 密钥
删除:
ssh-keygen -f "/path/to/your/.ssh/known_hosts" -R [服务器 IP 地址] ECDSA 主机密钥。
服务器 IP 地址] 的 ECDSA 主机密钥已更改,您要求进行严格检查。
主机密钥验证失败。

ssh 无密码登录

我们会发现,每一次连接的时候,服务器都要求我们输入密码。
如果我们每一次都使用诸如 ssh username@remote_host+password 的方式,可能会感到十分的厌烦,这时候我们就可以
使用 ssh 密钥对来连接到远程服务器。如果私钥没有设置密码,就不需要输入密码。从而达到无密码登录。
ssh-keygen 可以生成密钥对,接下来连续三次回车即可生成密钥对,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ssh-keygen -t rsa -b 2048
# 生成一个2048位的RSA密钥对,可以选择更长的
❯ ssh-keygen -t rsa -b 2048
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/admin/.ssh/id_rsa):# 存储位置(回车)
Enter passphrase (empty for no passphrase): # 可以选择输入密码(回车)
Enter same passphrase again: # 再次输入密码(回车)
Your identification has been saved in /c/Users/admin/.ssh/id_rsa
Your public key has been saved in /c/Users/admin/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:a9G5vG/6Qz6Aptj/cFQsPZtt+DjiAMIBVI9fW/OIENM admin@PC
The key's randomart image is:
+---[RSA 2048]----+
| .o.. o. |
| . o oE o |
| o o . + = |
| . o o = B * |
| o o S.= = o |
| . .o=...+ |
| o o= =+o . |
| . o. = o=. |
| ...==oo |
+----[SHA256]-----+

此时我们就生成好了一个 2048 位的 RSA 密钥对,此时它存储在 c/Users/admin/.ssh/id_rsa 这个位置,可以从自己的终端看到文件所在位置。
接下来我们可以使用 ssh-copy-id 将它复制到远程服务器上。

1
ssh-copy-id username@remote-server

执行这个命令后,系统会要求您输入远程服务器的密码,然后会将您本地计算机上的 ~/.ssh/id_rsa.pub(或其他公钥文件)的内容追加到远程服务器上的 ~/.ssh/authorized_keys 文件中。此时,就完成了公钥的复制。
此时就已经完成了无密码登录的步骤,以后使用 ssh 连接都只需要输入以下代码即可

1
ssh username@remote-server

如果不想用 ssh-copy-id,可以使用 scp 指令,将公钥发送到服务器。

1
scp /Users/admin/.ssh/id_rsa.pub username@remote_host:~/.ssh

多讲’亿’句。其中,scp(Secure Copy Protocol)是一个用于在本地计算机和远程服务器之间安全地复制文件的命令行工具。基础指令如下。

  1. 可以将本地的文件复制到服务器。

    1
    scp local_file.txt user@remote_server_ip:/path/to/destination/
    • local_file.txt: 本地文件的路径。
    • user: 远程服务器上的用户名。
    • remote_server_ip: 远程服务器的 IP 地址。
    • /path/to/destination/: 目标目录在远程服务器上的路径。
  2. 反过来可以将服务器的文件复制回来。

    1
    scp user@remote_server_ip:/path/to/remote_file.txt /local/destination/
    • user: 远程服务器上的用户名。
    • remote_server_ip: 远程服务器的 IP 地址。
    • /path/to/remote_file.txt: 远程服务器上要复制的文件路径。
    • /local/destination/: 本地目录,用于保存复制的文件。
  3. 复制整个目录(递归)到远程服务器

    1
    scp -r local_directory user@remote_server_ip:/path/to/destination/
    • -r: 表示递归复制,用于复制整个目录。
    • local_directory: 本地目录的路径
    • user: 远程服务器上的用户名。
    • remote_server_ip: 远程服务器的 IP 地址。
      -/path/to/destination/: 目标目录在远程服务器上的路径,指定复制到远程服务器的位置。
  4. 复制整个目录(递归)到本机

    1
    scp -r user@remote_server_ip:/path/to/remote_directory /local/destination/
    • -r: 表示递归复制,用于复制整个目录。
    • user: 远程服务器上的用户名。
    • remote_server_ip: 远程服务器的 IP 地址。
    • /path/to/remote_directory: 远程服务器上要复制的目录路径。
    • /local/destination/: 本地目录,用于保存复制的目录。

更简便一点

经过了刚才繁琐冗赘的说明。此时,我们完成了无密码登录,以后使用 ssh 连接都只需要输入我们想登录的服务器和用户即可登录。
那么,我们还可以再简便一点吗?

当然,这样做还可以更懒一点,比如建立一个 sh 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/bash

# 显示菜单
echo "1, 1.1.1.1"
echo "2, 2.2.2.2"
echo "3, 3.3.3.3"
echo "4, 退出"

# 获取用户输入
read -p "请选择需要连接的服务器(输入相应的数字): " choice

# 使用 case 语句进行选择
case $choice in
1)
echo "1.1.1.1"
ssh username@1.1.1.1
;;
2)
echo "连接2.2.2.2"
ssh username@2.2.2.2
;;
3)
echo "连接3.3.3.3"
ssh username@3.3.3.3
;;
4)
echo "退出脚本"
exit 0
;;
*)
echo "无效的选择,请输入1-4之间的数字"
;;
esac

这下每次只需运行脚本,输入数字即可连接。

或者说,你的目前在用移动端类 unix\linux 系统(比如 termux,一个适用于 Android 操作系统的开源终端模拟器应用),你打开它的唯一目的就是远程连接服务器,那么我们还可以玩的更极端点,直接使用 alias 来起一个别名。
alias 是一个用于创建命令别名的 shell 内建命令。别名允许用户为常用的命令或一系列命令定义短而易记的替代名称。
alias 基本语法为 alias name='command'
别名只在当前会话中有效,如果您想要使别名在每个新的 shell 会话中都可用,可以将别名定义添加到 shell 配置文件(如~/.bashrc 或~/.bash_profile)中。

1
2
echo 'alias link=ssh username@remote_host' >> ~/.bashrc # 使用bash
echo 'alias link=ssh username@remote_host' >> ~/.zshrc # 使用zsh

此时在终端输入 link 即可直接连接到 username@remote_hostalias 只做替换功能,我们完全可以继续使用其他参数诸如 -p-t

注:termux 好像没有 bashrc,需要自己新建,以上代码可以直接新建。

其他的指令

既然都讲到这里了,不如再多讲讲有关 ssh 的知识。
现在说说 -t 选项。-t 是 ssh 命令的一个选项,用于在远程服务器上分配一个伪终端,在某些情况很有用。
例子:

1
2
3
4
5
6
ssh -t username@remote_host 'sudo apt update && sudo apt upgrade'
Hit:1 http://mirrors.cloud.aliyuncs.com/debian bullseye InRelease
......
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Connection to remote_host closed.
~/

可以看到,此时参数 -t 表示要分配一个伪终端(pseudo-tty)。
在执行完 sudo apt update && sudo apt upgrade 后,伪终端则是会立即关闭。这种行为是由于指令执行完毕后没有额外的交互需要,所以终端会自动关闭。可以增加一个 &&bash 来启动一个新的 bash 会话,能够在保持 ssh 连接的情况下继续与服务器互动。
但是我暂时还不知道这个能用来干什么。🤔

看了一下文档,发现 ssh 还有用作代理的方法,使用 -D 端口号参数,即可指定动态端口转发,创建一个 SOCKS 代理服务器,该代理服务器将在本地计算机的指定端口上监听。

1
ssh -D 1080 username@remote_host

以上代码建立了一个 ssh,它将在本地计算机的 1080 端口上监听,并且转发流量。

1
2
ssh -fNCqTn -D 1080 username@remote_host 
# 加这么多参数好像有点重复,实际使用上可以看需删减参数

如果我们使用 ssh 作为代理的话,可以用以下的参数

  • -f:将 ssh 连接放到后台运行,使其在后台运行,以便您可以继续使用终端。
  • -N:告诉 ssh 不要执行任何远程命令,它只会建立连接而不执行任何额外的命令。
  • -C: 启用数据压缩。这将通过 zlib 库对传输的数据进行压缩,以减小传输的数据量。
  • -q: 静默模式。该选项用于减少输出,使 ssh 在执行时更为静默,不输出不必要的信息。
  • -T: 禁用伪终端分配。通常,ssh 会为远程连接分配伪终端,但使用 -T 选项会禁用这个分配,适用于不需要交互的连接。
  • -n: 防止远程命令执行时,ssh 从标准输入读取数据。

-L 也是大差不差。在使用 -L 选项时,可本地主机上的一个端口映射到远程主机的指定端口。

1
ssh -L 9999:example.org:80 -N -T username@remote_host

此时,访问 local:9999 即为访问 example.org:80
这个 ssh 命令的目的是通过 ssh 隧道将本地端口(localhost:9999)转发到远程主机(example.org)的端口 80。

-J,这个 ssh 参数是用于通过跳板主机(jumphost)连接到远程服务器的。跳板主机充当一个中间站,帮助我们间接地连接到目标服务器。

1
ssh -J username@jump_host username@remote_host

-J: 指定跳板主机,表示要通过跳板主机连接到目标服务器。

可以用来连接当作外网连接内网的跳板机。
而在一些安全要求较高的环境中,跳板主机可以作为一个堡垒机(Bastion Host),所有外部连接都必须先通过堡垒机,再通过内部网络连接到其他服务器。
还有的时候需要通过多个跳板主机才能达到目标服务器,这就形成了一个跳板链。通过 ssh 的 - J 选项,可以指定多个跳板主机,以建立跳板链。比如 ssh -J user1@jump_host1,user2@jump_host2 user@remote_host

累了,不想写了。over.😇

参考:

ssh | tldr InBrowser.App
ssh 命令的三种代理功能(-L/-R/-D)