关于ssh的技巧

Posted by Hsz on October 22, 2020

SSH和远程访问

所谓远程访问是指在一台机器(本地机器Local)上操作互联上另一台机器(远端Remote)上的资源的行为.

工作时候我们往往需要远程访问我们的服务器/工作站.利用其上的资源,要么是做比较重的计算任务,要么是为服务debug等等.

通常远程访问的操作依赖于开源工具ssh.本文就是结合场景介绍如何使用这些工具更加高效的进行远程访问.

前置知识

  • Local端ssh的设置方法(客户端)

    通常ssh本地端设置是设置ssh客户端,一般在linux、macos中通过修改~/.ssh/config文件实现; windows下需要安装ssh客户端,可以用admin用户打开powershell,输入Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'检查是否安装. 如果并未安装通过执行Add-WindowsCapability -Online -Name OpenSSH.Client~~~~x.x.x进行安装. 然后通过修改C:\Users\<username>\.ssh\config文件实现. 这样配置是用户进程级的修改,每次重新打开terminal就会重载配置.

    ssh客户端的设置大致是这样

      <setting>
      <setting>
      <setting>
      Host <host alias>
          <setting>
          <setting>
          <setting>
      Host <host alias>
          <setting>
          <setting>
          <setting>
    

    在最外层的设置是全局设置,在Host内的则是连接别名的自有设置,自有设置会覆盖全局设置.客户端的配置关键字可以在这个文档中找到.

  • Remote端ssh的设置方法(服务端)

    通常ssh远端的设置是设置ssh服务器,一般修改/etc/ssh/sshd_config文件.这样配置是全局级别的修改.通常要重启ssh的服务端sudo reload ssh.

  • ssh客户端的基本用法

    ssh <username>@<host>:<port>默认的端口是22

    如果在配置文件中设置了连接的别名,那么可以直接使用ssh <连接别名>来访问连接

数据压缩

场景: 我们希望传输过程中数据量尽量小

可以在配置文件中配置


Compression yes

Compression的可选值为

  • yes, 传输的所有数据将会通过gzip压缩
  • delayed(默认),认证成功之后对通信数据压缩
  • no传输的所有数据将不会压缩

要指定压缩等级可以使用CompressionLevel来指定.

如果是单独某个连接希望压缩,则可以命令行中带上-C.

ssh防止断连

场景: 我们使用ssh连接远端,但通常上个厕所回来就发现连接断开了.

这类配置一般在客户端配置文件中作为全局设置.

TCPKeepAlive  yes  # 据说这个配置项默认是开启的
ServerAliveInterval 30   #客户端主动向服务端请求响应的间隔
ServerAliveCountMax  5  # 连续5此客户端收不到服务器的响应,就是退出链接

设置本地的ssh客户端,让它每隔30s发送一个信号给服务端以保持连接.

复用连接

场景: 我们需要在多个terminal窗口中打开到同一个服务器的连接,但不想每次都输入用户名,密码,或是等待连接建立.

这类配置一般在客户端配置文件中作为全局设置.

ControlMaster auto
ControlPath /tmp/ssh_mux_%h_%p_%r

连接共享不止可以帮助你共享多个SSH连接,如果你需要通过SFTP与服务器传输文件,你会发现它们使用的依然是同一条连接; 如果你使用的Bash,你会发现,你甚至ssh甚至支持Tab对服务器端文件进行自动补全,共享连接选项对于那些需要借助ssh的工具,比如rsync,git等等也有效.

长连接

场景: 我们需要在多个terminal窗口中打开到同一个服务器的连接,但不想每次都输入用户名,密码,或是等待连接建立.

这类配置一般在客户端配置文件中作为全局设置.

ControlPersist 4h

这样每次通过ssh与服务器建立连接之后,这条连接将被保持4个小时,即使在你退出服务器之后,这条连接依然可以重用,因此,在你下一次(4小时之内)登录服务器时,你会发现连接以闪电般的速度建立完成.

如果你发现自己每条需要连接同一个服务器无数次,那么长连接选项就是为你准备.

连接别名

场景: 某些主机是我非常常用的,我不想每次都要打一段很长的命令.

这个需求挺正常的,如果登录可以配置化就会很方便,在windows下,xshell默认就是配置登录信息然后登录,每次双击下就能连接也确实方便.

ssh可以在设置中将一些连接信息配置化以实现这个目的,其基本语法是使用关键字Host指定一个连接,再在其下编辑参数. 可以编辑的参数就是ssh -h中指示的参数(需要将命名法转变为帕斯卡命名法),其形式如下:

Host <host alias>
    <setting>
    <setting>
    <setting>

举个例子:

Host a
    HostName <host>
    Port <port>
    User <username>

需要注意连接别名不能配置密码,毕竟使用明文密码是非常不安全的.如果希望不用密码则可以使用上面的无密码登录方式配置.

无密码连接

场景: 多数时候我们使用密码来登录远端,但每次都要输入密码很麻烦.

这个场景很常见,而无密码登录也是如hadoop,spark这些分布式计算框架的基本需求.使用方法是使用非对称加密(通常使用rsa加密),并将公钥信息交给要访问的计算机即可,

具体步骤为:

  1. 使用ssh-keygen -t rsa -P "" 用于生成本机的公钥私钥对,-P后面的参数是这个公私钥对的密码,我们填空字符串含义是没有密码,ssh-keygen的具体参数含义可以看这个页面
  2. cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys命令将公钥信息保存到ssh客户端可以识别的位置.
  3. ssh localhost 自己连下自己,第一次会要你设置下,以后就可以无密码自己登陆自己了
  4. ssh-copy-id -i ~/.ssh/id_rsa.pub <username>@<remote_host>,这布会将刚才生成公私钥对中的公钥发送到要登录的远程机器上,记录在~/.ssh/authorized_keys

指定秘钥登录远端

场景: 工作中远端往往使用非对称加密的方式连接,只有使用指定私钥钥才可以访问.通常这个私钥会以文件的形式交付到我们手中.

在登录时可以使用-i <私钥/的/path>来指定使用哪个私钥

无密码登录在配置文件中的连接别名中这样设置:

Host a
    HostName <host>
    User <username>
    IdentityFile <私钥/的/path>

IdentityFile用于指定用于访问的私钥位置

构建网络隧道

ssh端口转发也称为SSH Tunnel(SSH隧道).

通常ssh隧道的作用有两个:

  1. 跨越内网限制
  2. 数据加密传输.

SSH隧道用来给古老的没有加密的应用提供一个加密通道,也可以用来部署VPN。

本地端口转发

本地端口转发一般用于外网连接内网,它需要处于外网的本地机可以通过ssh可以访问处于内网的机器

场景: 我们买了阿里/腾讯的服务器(B),我们希望在不开外网的情况下可以本地(A)直接访问它上面部署的服务(端口为B_port)

本地作为接收端端口转发

本地端口转发解决的最简单问题就是上面的这类问题.我们可以通过本地端口转发将B上的B_port映射到本地A上的A_port

ssh -L <A_port>:localhost:<B_port> <B_username>@<B_Host>

我们可以将这种本地端口转发的的请求也写成连接别名的形式

Host test
    HostName <B_hostname>
    Port 22
    User <B_username>
    IdentityFile xxxx
    LocalForward  <A_port> localhost:<B_port>

场景: 我们买了阿里/腾讯的数据库服务(远端机器C,端口为C_port),而且没有开通外网访问权限.但我们希望可以本地(A)直接访问它(比如用gui客户端查询什么的),这时候我们有一台与远端机C同一内网且可以连接到机器C的远端机B,而且AB可以通过ssh联通

本地接收由远端中转服务

这种场景也挺常见的,我们可以使用本地端口转发将机器B作为中转来实现.

ssh -L <A_port>:<C_hostname>:<C_port> <B_username>@<B_hostname>

我们可以将这种本地端口转发的的请求写成连接别名的形式

Host test
    HostName <B_hostname>
    Port 22
    User <B_username>
    IdentityFile xxxx
    LocalForward <A_port> <C_hostname>:<C_port>

场景: 我们买了阿里/腾讯的数据库服务(远端机器C,端口为C_port),而且没有开通外网访问权限.但我们希望可以让与本地(A)在同一局域网的X直接访问它(比如用gui客户端查询什么的),这时候我们有一台与远端机C同一内网且可以连接到机器C的远端机B,而且AB可以通过ssh联通

本地作为gateway由远端中转服务

这种场景也挺常见的,和上面一样我们可以使用本地端口转发将机器B作为中转,只是这次A的角色成了gateway. 这一变化只要使用参数-g就可以实现.

ssh -g -L <A_port>:<C_hostname>:<C_port> <B_username>@<B_hostname>

当X要访问C上的C_port对应的服务时只需要访问A上的A_port即可.

我们可以将这种本地端口转发的的请求写成连接别名的形式

Host test
    HostName <B_hostname>
    Port 22
    User <B_username>
    IdentityFile xxxx
    LocalForward <A_port> <C_hostname>:<C_port>
    GatewayPorts yes

动态端口转发

场景: 我们买了阿里/腾讯的服务器(B),我们希望在不开外网的情况下可以本地(A)直接访问它上面部署的服务各种服务和它可以访问到的各种服务.

在我们不清楚远端会有哪些端口需要映射的情况下我们可以使用动态端口映射的方式创建一个Sock5代理,借助它我们就可以动态的访问远端可以访问的所有服务了.如果要让其他机器可以借助A访问B可以访问的所有服务,一样的使用-g

本地作为gateway由远端中转服务

ssh [-g] -D <A_port> <B_username>@<B_hostname>

X要访问C上的C_port对应的服务时只需要访问A上的A_port即可.

我们可以将这种本地端口转发的的请求写成连接别名的形式

Host test
    HostName <B_hostname>
    Port 22
    User <B_username>
    IdentityFile xxxx
    DynamicForward [<A_bind_ip>]:<A_port>
    GatewayPorts yes

有了Sock5的代理我们也需要有可以使用这个协议代理的客户端,比如如果只限于代理http服务那么可以在chrome中安装SwitchyOmega插件进行管理.更多的用法就不多解释了.

远端端口转发

与本地端口转发相反,远端端口转发通常用于内网向外网主动暴露端口,这种方式一般不常用.

场景: 我们使用的本地端(B)处在一个内网环境内,而有一台处于外网的远端机(A),我们希望远端机A可以访问处在本地端B上的一个服务(端口B_port).B可以使用ssh连接A

本地作为发送端映射端口给远端

这种场景不太常见,但也可以配置实现.但这需要从B连接A

ssh -R <A_port>:localhost:<B_port> <A_username>@<A_Host>

我们可以将这种远程端口转发的的请求也写成连接别名的形式

Host test
    HostName <A_hostname>
    Port 22
    User <A_username>
    IdentityFile xxxx
    RemoteForward <A_port> localhost:<B_port>

场景: 我们使用的本地端(B)处在一个内网环境内,它可以连接处于内网的远端机C(C上提供一个服务端口为C_port).而有一台处于外网的远端机A希望使用C上的服务.B可以使用ssh连接A

本地作为发送端中转内部服务映射端口给远端

ssh -R <A_port>:<C_hostname>:<C_port> <A_username>@<A_Host>

我们可以将这种远程端口转发的的请求也写成连接别名的形式

Host test
    HostName <A_hostname>
    Port 22
    User <A_username>
    IdentityFile xxxx
    RemoteForward <A_port> <C_hostname>:<C_port>

场景: 我们使用的本地端(B)处在一个内网环境内,它可以连接处于内网的远端机C(C上提供一个服务端口为C_port).B可以使用ssh连接外网机器A,而和A同一网络环境的X希望可以访问到C上的服务

本地作为发送端中转内部服务映射端口给远端,远端作为gateway暴露服务

这种需求一样的只需要在建立连接时设置-g参数即可

ssh -g -R <A_port>:<C_hostname>:<C_port> <A_username>@<A_Host>

我们可以将这种远程端口转发的的请求也写成连接别名的形式

Host test
    HostName <A_hostname>
    Port 22
    User <A_username>
    IdentityFile xxxx
    RemoteForward <A_port> <C_hostname>:<C_port>
    GatewayPorts yes

文件交互

场景: 很多时候我们需要向远端发送文件或者从远端将一些log文件取下来本地分析.

通常文件交互可以使用两种工具,一种是scp,一种是lrzsz

通常windows下用lrzsz而mac/linux下使用scp.

使用lrzsz

lrzsz需要先登录上远端,然后在远端执行.mac下使用要安装iterm,使用iterm代替自带的terminal才可以使用.

  • 安装:

sudo apt-get install lrzsz

  • 本机向远端传输(rz)

rz 之后跳出选框选择文件即可

  • 远端向本机传输(sz)

sz <文件路径>之后跳出选框选择要下载到的目录

使用scp

scp都是在本地操作,因此需要指明远端的信息,其实scp本质上也是基于ssh的,因此同样使用-i指定公钥.

需要注意的是scp有如下使用限制:

  1. 如果远程服务器防火墙有为scp命令设置了指定的端口,我们需要使用-P参数来设置命令的端口号
  2. 使用scp命令要确保使用的用户具有可读取远程服务器相应文件的权限,否则scp命令是无法起作用的。

用法:

  • 指明本地文件复制到远端

      scp local_file remote_username@remote_ip:remote_folder
    
      scp local_file remote_username@remote_ip:remote_file
    
      scp local_file remote_ip:remote_folder
    
      scp local_file remote_ip:remote_file
    
      scp local_file host_alias:remote_folder
    
      scp local_file host_alias:remote_file
    
    
    • 第1,2个指定了用户名,命令执行后需要再输入密码,第1个仅指定了远程的目录,文件名字不变,第2个指定了文件名
    • 第3,4个没有指定用户名,命令执行后需要输入用户名和密码,第3个仅指定了远程的目录,文件名字不变,第4个指定了文件名
    • 第5,6个使用了ssh中设置的连接别名.
  • 指明本地文件夹复制到远端

      scp -r local_folder remote_username@remote_ip:remote_folder
    
      scp -r local_folder remote_ip:remote_folder
    
      scp -r local_folder host_alias:remote_folder
    
    • 第1个指定了用户名,命令执行后需要再输入密码
    • 第2个没有指定用户名,命令执行后需要输入用户名和密码
    • 第3个使用了ssh中设置的连接别名.
  • 从远程复制到本地

从远程复制到本地,只要将从本地复制到远程的命令的后2个参数调换顺序即可,比如scp -r remote_username@remote_ip:remote_folder local_folder

参数说明:

  • -1: 强制scp命令使用协议ssh1
  • -2: 强制scp命令使用协议ssh2
  • -4: 强制scp命令只使用IPv4寻址
  • -6: 强制scp命令只使用IPv6寻址
  • -B: 使用批处理模式(传输过程中不询问传输口令或短语)
  • -C: 允许压缩。(将-C标志传递给ssh,从而打开压缩功能)
  • -p:保留原文件的修改时间,访问时间和访问权限。
  • -q: 不显示传输进度条。
  • -r: 递归复制整个目录。
  • -v:详细方式显示输出。scp和ssh(1)会显示出整个过程的调试信息。这些信息用于调试连接,验证和配置问题。
  • -c cipher: 以cipher将数据传输进行加密,这个选项将直接传递给ssh。
  • -F ssh_config: 指定一个替代的ssh配置文件,此参数直接传递给ssh。
  • -i identity_file: 从指定文件中读取传输时使用的密钥文件,此参数直接传递给ssh。
  • -l limit: 限定用户所能使用的带宽,以Kbit/s为单位。
  • -o ssh_option: 如果习惯于使用ssh_config(5)中的参数传递方式,
  • -P port:注意是大写的P, port是指定数据传输用到的端口号
  • -S program: 指定加密传输时所使用的程序。此程序必须能够理解ssh(1)的选项。

使用SFTP同步文件

SFTP是Secure File Transfer Protocol的缩写,安全文件传送协议.可以为传输文件提供一种安全的网络的加密方法.sftp 与 ftp 有着几乎一样的语法和功能.

SFTP 为 SSH的其中一部分,是一种传输文件至服务器的安全方式.在SSH软件包中,已经包含了一个叫作SFTP(Secure File Transfer Protocol)的安全文件信息传输子系统,SFTP本身没有单独的守护进程,它必须使用sshd守护进程(端口号默认是22)来完成相应的连接和答复操作.

SFTP传输使用了加密/解密技术,所以传输效率比普通的FTP要低得多,如果您对网络安全性要求更高时,可以使用SFTP代替FTP.

综上,sftp是ssh自带的功能.

我们如何测试是否可以用sftp连通呢?

找一台机器作为客户端机器,mac使用自带的terminal,windows使用cmder.在其中使用命令

sftp pi@192.168.x.x

之后按要求输入密码,连上了就说明服务端是正常的.

连好后会进入一个交互模式,可以使用常见的linux命令操作远程文件.