SSH使用指南

Last Updated: 2023-09-04 13:41:24 Monday

-- TOC --

使用Linux环境的同学,一定离不开SSH(Secure Shell)这个非常重要的工具!

openssh

配合ssh,tmux也是个不可或缺的工具!

SSH的历史和作用

历史上,网络主机之间的通信是明文通信,不加密。这使得通信很不安全,一个典型的例子就是远程服务器的登录。登录远程服务器,需要用户输入的账户密码,并传给服务器,如果这个过程是明文通信,就意味着传递过程中,线路经过的所有中间设备都能看到账号密码,这是很可怕的。

SSH 就是为了解决这个问题而诞生的,它能够加密计算机之间的通信,保证不被窃听或篡改。它还能对操作者进行认证(authentication)和授权(authorization)。明文网络协议可以套用在它的里面,从而实现加密通信。

1995年,芬兰赫尔辛基工业大学的研究员 Tatu Ylönen 设计了SSH 协议的第一个版本,同时给出了第一个实现(称为 SSH1)。当时,他所在的大学网络一直发生密码嗅探攻击(sniffing),他不得不为服务器设计一个更加安全的登录方式。写完SSH1以后,他就把这个工具公开了,并允许其他人免费使用。

嗅探(Sniffing)是指不间断地监测网络通信设备,截获用户传给服务器的账户密码,记录下来后另外使用。

SSH1可以替换 rlogin、TELNET、FTP 和 rsh 这些不安全的协议,所以大受欢迎,用户快速增长,1995年底已经发展到五十个国家的20,000多个用户。SSH1协议也变成 IETF 的标准文档。

1995年12月,由于客服需求越来越大,Tatu Ylönen 就成立了一家公司叫 SCS,专门销售和开发 SSH软件。这个软件的后续版本,逐渐从免费软件变成了专有的商业软件。由于SSH1 协议存在一些安全漏洞,所以1996年SCS公司又提出了 SSH2 协议(或者称为 SSH 2.0)。这个协议与SSH1.0版不兼容。1997年SSH2进行了标准化,1998年推出了软件实现 的SSH2。但是,官方的 SSH2 软件是一个专有软件,不能免费使用,而且 SSH1 的有些功能也没有提供。

1999年,OpenBSD的开发人员决定写一个SSH2协议的开源实现,这就是著名的OpenSSH 项目。该项目最初是基于SSH 1.2.12版本,那是当时SSH1 最后一个开源版本。但是,OpenSSH 很快就完全摆脱了原始的官方代码,在许多开发者的参与下,按照自己的路线发展。OpenSSH随OpenBSD 2.6版本一起提供,以后又移植到其他操作系统,成为最流行的SSH 实现。目前,Linux的所有发行版几乎都自带OpenSSH。现在,SSH2 有多种实现,既有免费的,也有收费的。OpenSSH还提供一些辅助工具软件(比如ssh-keygen,ssh-agent)和专用的客户端工具scp和sftp

现在还有谁会去使用收费的SSH2呢?Tatu Ylönen如果不成立公司,一直保持SSH的开源免费状态,现在会是什么样呢?不过,1995年发生的这些,的确太早了......我一直深深感到自己的落后......

除了安全登录服务器,SSH还可以用来创建Tunnel(隧道)和Port Forwarding(端口转发),为其它自身不带安全加密的业务提供一条安全的通道。

SSH是如何工作的

client-server架构。sshd负责为登录用户启动shell。

How SSH Works

When you connect through SSH, you will be dropped into a shell session, which is a text-based interface where you can interact with your server. For the duration of your SSH session, any commands that you type into your local terminal are sent through an encrypted SSH tunnel and executed on your server.

The SSH connection is implemented using a client-server model. This means that for an SSH connection to be established, the remote machine must be running a piece of software called an SSH daemon. This software listens for connections on a specific network port, authenticates connection requests, and spawns the appropriate environment if the user provides the correct credentials.

The user’s computer must have an SSH client. This is a piece of software that knows how to communicate using the SSH protocol and can be given information about the remote host to connect to, the username to use, and the credentials that should be passed to authenticate. The client can also specify certain details about the connection type they would like to establish.

有些Linux Workstation发行版默认没有安装openssh-server,有些安装了但默认不运行sshd,只有将sshd运行起来(不一定需要设置开机自启动),才能够通过网络登录:

$ sudo systemctl enable sshd  # not necessary
$ sudo systemctl start sshd

SSH服务器的安装

$ sudo apt install openssh-server

安装成功后,sshd进程(SSH服务器后台进程的名称)就运行起来了,非root账户可以直接登录(有的云服务器在初始化后,只有root账户,此时只能用root登录)。同时,SFTP也自动运行起来,SFTP与SSH使用相同端口。(注意:SFTP与FTPS是不一样的)

树莓派官方提供的系统自带SSH Server,不过要通过配置启动才可以使用。Lite系统启动SSH Server的方法如下:

$ sudo raspi-config

然后选择 3 Interface Options --> P2 SSH,按提示打开启动即可。

SSH服务器的配置

$ man 5 sshd_config

SSH服务器的配置文件如下:

$ sudo vim /etc/ssh/sshd_config

修改配置后,需要重启sshd进程:

$ sudo systemctl restart sshd

Ubuntu安装的sshd的配置文件中,没有指定具体的版本,anyway,增加一个版本信息不复杂,还可以提高安全性:

Protocol 2

SSH服务器默认的监听IP为运行主机上所有IP地址,默认端口号为22。

Port 22
Port 12345
ListenAddress 0.0.0.0  # listen on all ip, but no port specified
ListenAddress 0.0.0.0:12345  # listen on all ip and with port 12345
ListenAddress 192.168.1.8:12345  # listen only on 192.168.1.8

SSH服务器可以同时在多个端口上监听,这时就配置多条Port。公网上的服务器为了安全,很多时候不会现在在22号默认端口上监听,而会选择一个自定义的端口号。

在切换SSH监听端口的时候,可以先让其同时在两个端口上监听,然后再去掉其中一个,这样可以保证SSH的连接在修改配置时,不会被中断。如果直接将端口号从一个端口号修改到另一个,然后重启,可能当前连接就会中断,然后你再去连新端口号,可能会由于一些其它配置方面的问题,导致无法连接,这会很麻烦。

有的时候修改SSH监听端口后,无法成功连接,可能的原因是你的服务器外有一层防火墙,比如云服务器供应商可能会在外围提供一些安全机制,让你可以配置防火墙规则。

禁止root用户登录,是为安全考虑,阿里云的云盾就有一个检查项,叫做SSH登录基线检查,就是检查这个。

PermitRootLogin no  # yes

禁止root登录,但允许root执行某些特定的命令

PermitRootLogin forced-commands-only

此时,root只能使用key的方式连接host,执行在/root/.ssh/authorized_keys中明确的命令,在key前面增加command=,格式如下:

command="/path/to/command arg1 arg2" ssh-rsa ...

在修改PermitRootLogin参数前,建议先完成ssh-copy-id,修改authorized_keys等动作。

AllowUsers xinlin abc xyz

登录需要认证,禁止密码认证,实际上就是禁止用户用密码登录。比如:如果设置了使用SSH秘钥登录,这个时候取消密码认证,就没有人可以再使用密码登录系统,只能用你的私钥登录。

PasswordAuthentication no

默认是不允许的,但是如果你的环境非常安全,或者因为其他原因无法输入密码,或者可以把输入密码省掉:

PermitEmptyPasswords yes  # no

如果不配置服务器心跳,常常遇到的情况就是,ssh session无操作一段时间,连接就终止了。要不断地重连,挺烦人的。

ClientAliveInterval 90
ClientAliveCountMax 8

90表示:服务器每隔90秒向客户端发出一次心跳;8表示:如果服务器连续5次都收不到客户端的心跳回复,就断开连接。TCP链路,interval可以适当长一点。

SSH一段时间不操作后断开连接,常常是因为长时间没有数据传输,这条连接被中间的路由器掐断了。有了心跳就可以解决这个问题。SSH Port Forwarding通道的Keep alive,也依赖这个机制。这个机制可以在ssh client上配置,效果一样。

关于保活,还有个TCPKeepAlive配置项,打开也无妨,只是它们工作在不同层面,TCPKeepAlive保活的效果可能并不好,据说防火墙可能丢弃保活报文,似乎很少见到真正依赖TCP层保活机制的实现?!

有了X11Forwarding的加持,我们就可以通过SSH客户端直接运行对端主机上的GUI程序。这个功能在调试GUI程序的时候,异常的方便。

X11Forwarding yes
AllowTcpForwarding yes

后面又对此功能的详细介绍。

AllowAgentForwarding yes

后文有详细介绍。

sftp是ssh服务器自带的一个功能,默认的配置和建议配置如下:

Subsystem sftp /usr/lib/openssh/sftp-server  # default
Subsystem sftp internal-sftp                 # recommended
Subsystem sftp none                          # sftp disabled

以上两行配置,任何时候只能保留一行。(用#注释掉其中一行即可)

internal-sftp和sftp-server有什么区别?

In SSHD configuration, "internal-sftp" and "sftp-server" are two different subsystems that can be used to provide SFTP (SSH File Transfer Protocol) access.

The "internal-sftp" subsystem is a built-in SFTP server that is included with OpenSSH. It runs within the sshd daemon process and provides a more secure chrooted environment for SFTP users. When the "internal-sftp" subsystem is used, it restricts the user's shell access to only SFTP, preventing them from running any other commands or accessing the system in any way.

On the other hand, "sftp-server" is an external SFTP server that is executed as a standalone binary outside of sshd. It allows for more flexibility and customization, such as configuring different logging options or setting up virtual hosts. However, it also requires additional setup and configuration compared to the built-in internal-sftp subsystem.

So, the main difference between "internal-sftp" and "sftp-server" is that the former is a built-in and more secure solution, while the latter is an external and customizable option.

现在很多Linux发行版本中的sshd配置,默认还是sftp-server,可能是为了保持向下兼容。

远程登录前可以显示给用户的信息,通过在Banner字段后指定一个文件来实现,文件内容不支持转义:

# Banner none  # this is default config
Banner /etc/issue.net

传统都是使用/etc/issue.net文件,具体参考:issue,motd文件

用Banner防止非法登陆

用sshd的Banner这个方法,只能防止一些比较弱的非法登录程序,实施这个方法也很简单:在Banner的内容中,增加Access denied关键字。

登录后是否显示Motd,由下面这条配置控制:

PrintMotd no

看到一个别人的故障说明:motd信息在登录的时候,显示了两次!因为sshd配置了PrintMotd yes,pam也配置了显示motd,去掉其中一个就OK。

SSH客户端的配置

OS系统一般都自带SSH客户端,Windows10系统除了可以在cmd窗口里直接使用ssh之外,还有很多独立的可以用来做SSH客户端的软件,比如我比较喜欢的MobaXterm。Putty也是一个非常著名的SSH客户端软件。

$ man 5 ssh_config

Windows7系统没有自带的ssh客户端,需要自己安装。Windows10系统有自带的,但你可以对版本不满意。搜索openssh for windows,或者访问https://www.mls-software.com/opensshd.html,装好后再cmd窗口就可以使用ssh客户端了。

# on my raspberry pi
$ ssh -V
OpenSSH_7.9p1 Raspbian-10+deb10u2+rpt1, OpenSSL 1.1.1d  10 Sep 2019

在使用ssh连接某台主机的时候,如果加上-v参数,会打印出非常多的debug信息,用以辅助调试链接方面的问题。可以使用多个v,显示出来的信息更多,最多3个。

$ ssh -l pi 192.168.2.107 -p 22
$ ssh pi@192.168.2.107:22

-l(小写L)跟户名,-p跟端口号;上面两种写法等效!如果不指定端口号,端口号默认使用22。如果不指定用户名,默认使用登录当前系统的用户名(Windows系统下基本上就是Administrator)。

如果你无法配置服务器,而正好这台服务器没有配置心跳,你可以配置客户端发起心跳。客户端配置文件如下:

$ sudo vim /etc/ssh/ssh_config  # different with sshd_config
# configurate items
TCPKeepAlive yes
ServerAliveInterval 30
ServerAliveCountMax 5

保持TCP连接,客户端每30秒发一次心跳给服务器,如果连续5次都收不到服务器的心跳回复,就断开连接。30和5可按需配置。

当sshd配置了Client保活心跳,同时又在Client配置了Server保活心跳,测试发现,这种情况下,只有配置间隔较小的心跳起作用

ssh客户端的配置文件是按Host进行配置(后文有详细说明)。

$ ssh -o ServerAliveInterval=15 \
-o ServerAliveCountMax=5 username@serverip -p port_number

如果sshd端没有配置ClientAlive,ssh客户端就可以在命令行使用ServerAlive配置项,他们的功能和效果是一样的。不推荐使用TcpKeepAlive,它在TCP层发挥作用,发送空的TCP ACK报文,这样的报文有可能会被防火墙丢弃。(TCP的KeepAlive机制似乎很少人使用)

ssh通信是加密的,而且加密算法按什么优先级选择,可以自己设置,使用-c参数。

-c cipher_spec

Selects the cipher specification for encrypting the session. cipher_spec is a comma-separated list of ciphers listed in order of preference.  See the Ciphers keyword in ssh_config(5) for more information.

我理解,如果-c参数后面只有一个加密算法,相当于指定了用此算法。用-o参数指定Ciphers配置的效果是一样的。

$ ssh -Q cipher

在命令行指定配置文件,此时全局配置文件/etc/ssh/ssh_config被忽略。也可以使用-F none,表示不读取任何配置文件。

开启ssh客户端的ForwardAgent功能。

开启ssh客户端的ForwardX11功能。

ssh建立连接的过程中,server会将自己的public key发给client,这样才能安全的通信。

默认配置时,ssh每当第1次连接某台server时,ssh client界面都会弹出一个提示,显示server key fingerprint,让用户选择yes/no。如果选择yes,ssh client就会将这个fingerprint保存在know_hosts文件中,当下次连接的时候,自动对比,对比成功则不再提示,对比不成功,则拒绝连接,给出Warning!

ssh client端可以配置:

StrictHostKeyChecking no   # no checking,并会自动将fingerprint添加到know_hosts文件
StrictHostKeyChecking ask  # default
StrictHostKeyChecking yes  # safest,如果key不匹配,拒绝连接,没有详细信息提示

示例:

$ ssh root@167.172.189.123 -p 12345 -i do -o StrictHostKeyChecking=yes
No ED25519 host key is known for [167.172.189.123]:12345 and you have requested strict checking.
Host key verification failed.

当ssh服务器因为各种原因,它的key发生了变更,就会导致ssh client连接时,fingerprint不匹配。

更新ssh服务器的Keys

一般云服务器部署成功后,系统中默认就有了一组key:

$ ll /etc/ssh/ssh_host_*
-rw------- 1 root root 1.4K Apr 17  2022 /etc/ssh/ssh_host_dsa_key
-rw-r--r-- 1 root root  607 Apr 17  2022 /etc/ssh/ssh_host_dsa_key.pub
-rw------- 1 root root  513 Apr 17  2022 /etc/ssh/ssh_host_ecdsa_key
-rw-r--r-- 1 root root  179 Apr 17  2022 /etc/ssh/ssh_host_ecdsa_key.pub
-rw------- 1 root root  411 Apr 17  2022 /etc/ssh/ssh_host_ed25519_key
-rw-r--r-- 1 root root   99 Apr 17  2022 /etc/ssh/ssh_host_ed25519_key.pub
-rw------- 1 root root 2.6K Apr 17  2022 /etc/ssh/ssh_host_rsa_key
-rw-r--r-- 1 root root  571 Apr 17  2022 /etc/ssh/ssh_host_rsa_key.pub

这一组key是别人给你用的!为了安全,建议重新生成一组key:

$ sudo ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key
$ sudo ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key

Chroot User

SSH Port Forwarding

SSH Port Forwarding,SSH端口转发,也称为Tcp Forwarding,其又分为本地端口转发(Local Port Forwarding),远端端口转发(Remote Port Forwarding)和动态转发(Dynamic Port Forwarding)。

SSH有个Tunnel技术,这是个3层概念,可以用来实现SSH-Based虚拟私有网络,而Port Forwarding是4层概念。

SSH提供了一个非常有用的端口转发功能。它能够将其他TCP端口的网络数据通过SSH链路转发,并且自动提供了加解密和压缩服务。

打开SSH端口转发功能:

AllowTcpForwarding yes

本地端口转发

将一个可SSH链接主机可访问的端口拉到本地。

顾名思义,本地端口转发,通过访问本地端口,实现访问另一个通过SSH连接的远程端口。这个功能常用来正面穿透防火墙。

比如A和X在防火墙外,B和C在防火墙内,A和X不能直接访问B和C上的资源,幸运的是,A可以通过SSH连接B,此时,我们就可以通过SSH的本地端口转发功能,实现A任意访问B和C上的资源,也可以让X通过A实现同样的访问。如下图:

ssh_local_port_forwarding

A$ ssh -g -N -f -L 12345:remotehost:23456 username@B -p port

-L 参数是必须的,-g-f-N可选。

远端端口转发

将一个本地可访问的端口用SSH链接推到远端。

如果说本地端口转发是正面穿透防火墙,那么远端端口转发就是反向穿透,从内向外打通一条TCP链路。这几乎是最简单的内网穿透的方案

A和X在防火墙外,B和C在防火墙内部,A和X不能访问防火墙内部,不幸的是,A和X也不能通过SSH连接B和C。幸运的是,B可以在防火墙内部通过SSH连接A,此时可以在B上发起远端端口转发,实现A可以任意访问B和C的资源。

ssh_remote_port_forwarding

B$ ssh -N -f -R 12345:host:23456 username@A -p port

这样,A就可以访问B或C了!但是X还是不能访问,默认A只在loopback上监听12345端口。要实现X也能够像A一样访问B或C,可以

A$ ssh -g -N -L 12346:localhost:12345 localhost

X访问A的12346,相同于访问A的12345,也就相当于访问B或C。

这个方法最好,默认A上的端口只在localhost上监听,如果A在公网,这是必须的。X建立加密链接到A,去访问A的localhost,整个链路都有加密。

GatewayPorts yes

这样默认remote port forwarding的端口,会在remote host的所有接口上监听。如果host在公网,相当于端口就直接暴露在公网了,慎用!

动态端口转发

造一个可自由访问的通过SSH链接到远端的本地端口。

不同于前两种端口转发,动态端口转发不限制访问的地址,因此被称为动态。

$ ssh -D localhost:12345 username@remotehost -p port

在本地建立一个动态端口转发,监听地址localhost:12345,访问本地的12345端口时,所有访问流量全部通过加密TCP链接,转发到remotehost,然后再转发出去。(也可以监听在本地的某个ip地址上,这样网络中的其它host也可以使用)

内网穿透

多说一点内网穿透(Intranet Penetration)。

我给公司写过一个简单的定制化的内网穿透方案,在测试的时候,遇到了问题。当一个内网业务端口被映射到公网后,有可能对这个端口的访问,有多个TCP连接,比如典型的HTTP 80端口。因此,推到重来了一次:

  1. 映射出去的端口,要支持多连接;
  2. 公网先连接,然后再在内网发起连接;

难点是要在一路TCP连接中,区分多路TCP的数据。这需要设计一个私有协议,在IO multiplexing的时候,要非常小心。

很快代码就运行成功了,但做完之后,我发现这个功能,跟SSH的Remote Port Forwarding是何其相似呀!因此我说,一般情况下,SSH的Remote Port Forwarding,就是最简单最好用的内网穿透方案!只要是本地在内网可以访问的业务端口,都可以一键穿透。

动态端口的内网穿透

按前述方法,内网穿透只能是单端口的,即只能将内网中的某一个端口映射出去,能干什么取决于这个端口的能力。是否能够实现,内网穿透之后的动态端口转发的能力呢?可以的。

方法01

在内网,将一台Linux主机的22号端口先Remote出去:

$ ssh -fNCR 10055:localhost:22 username@yourdomain -p port

在yourdomain上,设置动态端口转发,目标端口是localhost:10055:

$ ssh -fNCD 0.0.0.0:10056 name2@localhost -p 10055

name2是内网Linux主机的username。在外网,将10056的访问映射到10055,10055对应内网的22,因此10056就相当于一个穿透到内网的动态转发端口。测试OK。

方法1的优点:动态端口的建立在外网主机上,在外网可以方便灵活控制。

方法02

可以将方法1反过来,在内网设置一个动态点,然后将内网的动态点remote出去。

方法2的优点:

  1. 所有的ssh命令输入,都在内网的某一个主机上完成;
  2. Windows主机也可以,现在默认都支持ssh client;

缺点就是方法1的优点。有的时候,这种灵活控制非常重要,涉及安全问题。

SSH Port Forwarding使用技巧

以上对SSH Port Forwarding的总结,肯定有一些不全面的地方,以后在使用过程中,再慢慢积累。

技巧01:多条SSH端口转发路径可以相互之间连接,组合形成一条更长的转发链路。

技巧02:在网速慢而且不主要传视频的情况下,可以使用-C参数,开启压缩传输,来提高速度!比如git clone的时候,能够从十几K一下子提高到几百K!

技巧03:SSH不管是Server还是Client,都可以配置链路保活心跳(间前面配置部分的说明),这个心跳配置对于端口转发也适用(抓包验证)。因此,不用太担心长时间没有数据的TCP链接的保活的问题。不过,还是有可能因为各种原因导致中间的TCP连接被中断。

技巧04:dynamic port可被local port forwarding拉到本地,也可被remote port forwarding推到远端...

SSH秘钥登录

How Do SSH Keys Enhance Security?

With SSH, any kind of authentication — including password authentication — is completely encrypted. However, when password-based logins are allowed, malicious users can repeatedly, automatically attempt to access a server, especially if it has a public-facing IP address. Although there are ways of locking out access after multiple failed attempts from the same IP, and malicious users will be limited in practice by how rapidly they can attempt to log in to your server, any circumstance in which a user can plausibly attempt to gain access to your stack by repeated brute force attacks will pose a security risk. (如果启用密码认证,总可以去尝试暴力破解,如果使用秘钥,暴力破解的机会都没有)

ssh-keygen命令

在命令行直接运行ssh-keygen,回车几次,就可以在~/.ssh目录中生成两个以id_开头文件,一个私钥,一个公钥!

-t,选择加密算法,默认RSA

-b,秘钥长度,默认2048bit

-f,指定生成的文件及路径,生成两个文件,公钥用.pub后缀表示

-C,Comments,默认为hostname@username

-p,modify or remove passphrase

-l,显示秘钥的fingerprint,此时配合-f指定input file(配对的私钥和公钥,它们的fingerprint是一样的)

ssh-keygen生产的public key文件格式,与openssl略有不同,前面有ssh-rsa前缀,后面有用-C生产的comments:

$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCtTajTMbZPSinFDPIvAMRkBdy03j52zXyLNUk9tsJaurS23eAW2Dr6qEiFRLEVfZ8Kh/KJwxdB8OnJlhWYSJWUQF0+8mxEZ6/tw7XHH3o5+WLcD5/W25tY8hEuAVqgUn4X0P3W4yuFhkEUawnKHQwFFvkOz0E7kJVsMCEeui0NKEKqEKftOUjdB1C/SVren9jmAr9ksqS3snzB5NKDGkk2OytrMQw3yqj054xyFQ81G+a1U7NHJiQf5RozvYxKWFwqIMoNZBOhf8EGZqKekoFvKtBJYFkMC8J8+G9SSx1su2+vsdZkbLAu5VnMgsDZxTaIpGO0PKkY+kUY8sZIRRu6MyGtk0EwTzZDH+E7pDgT/B/cOCKstVzUfg1gfWFKjssqNFm0U2qYq3Qnul5f5Qydgn1hBGP2qnFSqoPG5fH23JJmBXNlqchQzlbsgzLCCb/7JzXQM09mciF2jEgavZUh32ajw4n2qRZPs7s5ShwIUStX2gTpFXM66F+XM33R0I0= xinlin@K

passphrase

在使用ssh-keygen创建秘钥对的时候,有一个输入passphrase的提示。什么是passphrase?

By default, you will have to enter any passphrase you set here every time you use the private key, as an additional security measure. Feel free to press ENTER to leave this blank if you do not want a passphrase. Keep in mind though that this will allow anyone who gains control of your private key to login to your servers.

如果设置passphrase,每次使用private key的时候,都需要输入这个passphrase,它就像是使用private key的密码。不设置当然也是可以的,只要保护好private key文件!

-p,修改或移除passphrase:

$ ssh-keygen -p

使用-i在命令行指定私钥

$ ssh root@ip -i <path/to/private_key>
$ ssh -NCD 192.168.16.104:54321 root@ip -i ~/.ssh/private_key

ssh,scp和sftp,这几个命令都有-i参数!

当本地有多个私钥,这些私钥对应不同的Server时,除了-i参数在命令行指定具体的私钥,还可以通过配置文件来简化输入,见后。

ssh-copy-id命令

要把你的密钥复制到服务器上,可以使用ssh-copy-id命令:

$ ssh-copy-id -i path/to/pub_key yourname@example.com

注意:此时-i参数后面跟的是public key!

也可以手动copy public key到Server的~/.ssh/authorized_keys,效果也一样。

ssh如何认证key用户

To authenticate using SSH keys, a user must have an SSH key pair on their local computer. On the remote server, the public key must be copied to a file within the user’s home directory at ~/.ssh/authorized_keys. This file contains a list of public keys, one-per-line, that are authorized to log into this account. (ssh-copy-id命令就是修改这个文件)

When a client connects to the host, wishing to use SSH key authentication, it will inform the server of this intent and will tell the server which public key to use. The server then checks its authorized_keys file for the public key, generates a random string, and encrypts it using the public key. This encrypted message can only be decrypted with the associated private key. The server will send this encrypted message to the client to test whether they actually have the associated private key.

Upon receipt of this message, the client will decrypt it using the private key and combine the random string that is revealed with a previously negotiated session ID. It then generates an MD5 hash of this value and transmits it back to the server. The server already had the original message and the session ID, so it can compare an MD5 hash generated by those values and determine that the client must have the private key.

ssh-agent进程

当你使用秘钥登录,同时又设置了passphrase,每次使用秘钥时,还是要输入passphrase,挺烦人的。ssh-agent这个工具,就是用来解决这个问题的。它将秘钥保存在内存中,ssh client跟ssh-agent请求是否有某个host的私钥,如果有,就直接拿过来使用,什么都不用输入。

命令行启动ssh-agent时有个小技巧,ssh-agent会输出3行指令,在当前shell下执行,ssh client才能够与ssh-agent通信,因此,启动ssh-agent是这样的:

$ eval $(ssh-agent)

以下是ssh-agent启动时的输出:

$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-XXXXXXjDMe30/agent.1363873; export SSH_AUTH_SOCK;
SSH_AGENT_PID=1363874; export SSH_AGENT_PID;
echo Agent pid 1363874;

ssh-agent是一个daemon进程,在后台执行。

使用eval命令,就是直接执行了ssh-agent输出的指令,export两个环境变量,Unix Domain Socket文件和PID。此时,ssh client就可以与ssh-agent通信了。

多shell共享ssh-agent

只要在shell中export具有相同值的环境变量即可,方法多种多样。下面是我个人的一个方法:

$ echo $(ssh-agent) > ssh_agent_script.sh

此后,在任何shell中执行:

$ . ssh_agent_script.sh  # run script in current process

就能够使用一个相同的后台ssh-agent进程。

ssh-add命令

管理ssh-agent中保存的私钥,使用ssh-add命令。添加(仅此时输入一次passphrase),查看-l,删除...

ssh-add命令有个神奇的功能,它可以设置一个密码,然后将ssh-agent锁起来。此时ssh-agent不再对外提供任何服务。

-x,设置个密码,将ssh-agent锁起来。

-X,用相同的密码解锁,然后这个密码就没用了,下次锁ssh-agent,还需要重新设置密码。

Agent Forwarding

场景:

你在本地运行了ssh-agent,添加了私钥。此时,你ssh到某台server,在这台server上,你也希望能够使用相同的私钥去登录私钥对应的其它server。此时,你本地的ssh-agent可以将保存的私钥发送给server,你在server上可以直接使用此私钥,就像在本地一样。这就是Agent Forwarding功能!

使用Agent Forwarding功能,你需要确保server上的sshd配置文件开启了此功能:

AllowAgentForwarding yes

在ssh连接server的时候,带上-A参数。

Agent Forwarding原理图,注意server上没有运行ssh-agent进程:

agent_forwarding.png

一切OK时,在server上使用ssh-add -l,也能够看到本地的私钥。

ssh的manual中有如下一段:

-A

Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the agent's UNIX-domain socket) can access the local agent through the forwarded connection. An attacker cannot obtain key material from the agent, however they can perform operations on the keys that enable them to authenticate using the identities loaded into the agent. A safer alternative may be to use a jump host (see -J).

SSH远程命令执行

在登录ssh的命令后面,跟上一组命令字符串,就实现了远程命令的执行。

$ ssh -l <name> <domain|ip> [-p <port>] <command>
$ ssh name@domain|ip [-p <port>] <command>
$ ssh -l <name> <domain|ip> [-p <port>] 'pwd; cd ~/repos;ls'

当需要远程执行需要交互命令的时候,比如top命令,需要使用-t参数,来显式地告诉ssh提供一个TTY:

$ ssh name@domain|ip [-p <port>] -t <command>

An often overlooked feature of ssh is the ability to run commands directly. ssh foobar@server ls will execute ls in the home folder of foobar. It works with pipes, so ssh foobar@server ls | grep PATTERN will grep locally the remote output of ls and ls | ssh foobar@server grep PATTERN will grep remotely the local output of ls.

本地有一个shell脚本,在远端的主机上执行:

$ ssh name@domain|ip [-p <port>] 'bash -s' < test.sh

用autopass自动输入ssh密码

这是我个人的开源项目,纯Python开发,专注于实现自动输入ssh密码,以及sudo时的密码自动输入。具体请参考这里:autopass:自动输入ssh,scp,sudo的密码

SSH Tunnel

配置~/.ssh/config

SSH的参数配置有3个层次:

  1. 命令行参数,如-p 10086, -i /path/to/identity_file等选项
  2. 默认的针对某用户的配置文件(per-user config file),所在路径为~/.ssh/config,默认不存在,需要手动创建
  3. 针对系统所有用户的配置文件,所在路径为/etc/ssh/ssh_config

参数的使能顺序是1 > 2 > 3

示例:

Host aliyun
    HostName 192.168.12.34
    User tom
    Port 12345
    IdentityFile ~/.ssh/private.key

是Identityfile,不是Identifyfile....:)

此时,以下两行命令等效:

$ ssh aliyun
$ ssh tom@192.168.12.34 -p 12345 -i ~/.ssh/private.key

配置文件的主要规则如下:

可以使用wildcard,实现同时配置多个Host的效果(默认的system wide配置文件,虽然整个都被注释掉了,但是还是能看到Host *字样):

You can also use wildcards to match more than one host. Keep in mind that later matches can override earlier ones.

Host *
    ForwardX11 no
    ServerAliveInterval 120

Host testhost
    HostName your_domain
    ForwardX11 yes
    Port 4444
    User demo

关于配置文件的更多信息,参考:

$ man ssh_config

-F <config_file>,指定一个per-user的配置文件。如果命令行指定了per-user配置文件,system-wide的配置文件/etc/ssh/ssh_config将被忽略。如果在命令行使用-F none,ssh不再读取配置文件。

SSH Multiplexing

在ssh客户端配置multiplexing的效果,多个连接相同目标地址的ssh连接(各种功能的连接都算),共享共一个TCP连接。可能有两个好处:

配置示例:

Host *
    ControlMaster auto
    ControlPath ~/.ssh/%r@%h:%p.sock
    ControlPersist 1  # second, default is no, can be yes

每当第1个连接目的Host的TCP出现时,ssh创建一个UNIX Domain socket文件,文件名规则:

此socket文件用于多个ssh客户端与server通信时,共享一路TCP连接。ControlPersist 1表示当最后一个ssh客户端断开连接,1秒后关闭TCP连接(此时socket文件也会被删除)。

-S none,绕过共享的TCP连接,单独创建一路TCP连接。

当与port forwarding一起使用时,可能会出现一些奇怪的现象,简单绕过问题的方法,ControlPersist配置yes,或者不使用ssh multiplexing(-S none)。

当建立起了master连接之后,如果controlpersist=yes,后续的登陆,无需输入密码,无需提供证书。

后台的master TCP连接,同样有可能因为异常而被卡死或中断。(国内使用体验太差了,TCP连接总是莫名被中断,还是别用这个功能了吧)

本文链接:https://cs.pynote.net/net/202109201/

-- EOF --

-- MORE --