23.ssh协议及实现(openssh)

SSH协议

  • ssh: secure shell, protocol, 22/tcp, 安全的远程登录
  • 具体的软件实现:
    OpenSSH: ssh协议的开源实现,CentOS默认安装
    dropbear:另一个开源实现
  • SSH协议版本
    v1: 基于CRC-32做MAC,不安全;man-in-middle
    v2:双方主机协议选择安全的MAC方式
    基于DH算法做密钥交换,基于RSA或DSA实现身份认证
  • 两种方式的用户登录认证:
    基于password
    基于key

Openssh软件组成

  • OpenSSH介绍
  • 相关包3个:
    openssh :通用文件包
    openssh-clients :客户端,可执行文件一般在bin下
    openssh-server :服务器端,可执行文件一般在sbin下,后面带字母d
  • 工具:
    基于C/S结构
    Linux Client: ssh, scp, sftp,slogin
    Windows Client:xshell, putty, securecrt, sshsecureshellclient
    Server: sshd

ssh客户端

  • 客户端组件:
    ssh, 配置文件:/etc/ssh/ssh_config
    Host PATTERN
    StrictHostKeyChecking no 首次登录不显示检查提示,相当于直接输入yes,并自动下载对方公钥到konw_hosts文件中。
  • 格式:
    ssh [user@]host [COMMAND] :链接特定用户,不写的话默认为root,命令不写的话默认会交互式执行,写上的话就会执行然后退回到本机而不是留在连接状态。
    ssh [-l user] host [COMMAND]
  • 常见选项
    -p port:远程服务器监听的端口
    -b:指定连接的源IP
    -v:调试模式
    -C:压缩方式 :网速较慢时可压缩一下数据方式进行连接
    -X:支持x11转发
    -t:强制伪tty分配
    ssh -t remoteserver1 ssh -t remoteserver2 ssh remoteserver3

  • 允许实现对远程系统经验证地加密安全访问
  • 当用户远程连接ssh服务器时,会复制ssh服务器端的/etc/ssh/ssh_host*key.pub(CentOS7默认是ssh_host_ecdsa_key.pub)文件中的公钥到客户机的~./ssh/know_hosts中。下次连接时,会自动匹配相应私钥,不能匹配,将拒绝连接

注意点1:

  1. 如果利用ssh协议进行远程登录时是第一次登录到对方的主机上,会提示一个“指纹”验证信息在终端上,让你输入yes/no继续下一步。

    • 这个指纹就是对方主机的/etc/ssh/ssh_host*key.pub(CentOS6和7默认都是ssh_host_rsa_key.pub)中的公钥信息进行了sha256算法和MD5算法单向散列之后的值,可以被看做是对方主机的唯一标识。
    • 这个主要的目的就是为了确认要连接的IP的主机是否是真正你想要连接的IP的主机(中途可能被截获此连接请求),从而保证连接无误,增强安全性不会连接到***的主机上去。
    • 第一次ssh连接成功之后,本机就会把对方主机的公钥信息(注意是公钥信息不是指纹信息)存储到本地的~./ssh/know_hosts文件中(如果没有这个文件会自动生成),等到下次再次连接相同IP的对方主机时,便会计算并将指纹信息自动进行比对(此时也就不会再出现询问yes/no的选项了)。此时如果指纹比对相同一致会进行下一步的验证,如果不同则ssh协议认为对方主机被更换,便会自动断开连接请求拒绝连接,以实现安全连接的目的。
    • 如果对方重装了系统需要再次连接则可以把know_hosts中相对应的IP和公钥给删除掉即可,下次就会重新再次询问yes/no并下载对方的公钥信息了
  2. 从1中可知/etc/ssh文件夹下的公私钥对信息很重要(尤其是私钥,因为公钥是从私钥计算出来的),如果私钥被偷走则安全性就无法保证,因为这样就可以冒充对方的主机了。
  3. /etc/ssh文件夹下有客户端和服务器端的配置文件(ssh和sshd)- 其中客户端配置文件中这一项比较有用就是StrictHostKeyChecking ask 改为 no ,让它首次登录不显示检查指纹的提示,相当于直接输入yes,并自动下载对方公钥到konw_hosts文件中。
    • 客户端配置文件还有一项就是port 22,生产中为了安全会把服务器端口给修改为其它端口,因此每次链接的时候输入命令要加上 ssh IP -p PORT的-p和端口选项。如果把客户端配置文件这里直接改为服务器端口就可以省略手写-p加端口选项了。
  4. ssh客户端连接选项中的-X选项解释:首先因为图形界面也是一个软件(服务器端和客户端),客户端发送请求,则服务器端会显示出图形画面并将客户端的各种操作(鼠标,点击等)进行事实的显示出来,这就是X11协议等简单工作原理介绍。
    • 显示这个图形界面的一方就是服务器,而进行操作的一方就是客户端。图形界面并非一定要在同一台机器上既当做客户端又当做服务器端。
    • 在这里使用-X选项就能把SSH连接之后进行的操作(相当于图形界面的客户端操作,因为ssh连接之后的操作其实是在对方的机器上进行的)的命令转发到本机上,本机当做图形界面的服务器端进行显示。
    • 这里需要注意本机是SSH的客户端,图形X11服务的服务器端;对方是SSH的服务器端,并且是图形服务的客户端。
    • 如果是利用的Xshell软件先连接到linux机器上,然后再连接另一台linux主机,利用-X选项,此时相当于进行了二次转发,将本机windows的Xshell软件当做图形的服务器端进行了图形的显示。(Xshell默认有X11的服务器软件,很方便)
  5. 在某些情况下是需要图形界面环境的,因此4中的这个命令还是有很大的用处的。Xshell还可直接把整个linux的图形界面给抓取到windows界面中。

    • 打开xstart,然后写入session ,连接的IP地址,链接方式(密码或者是key),然后选择gnome方式连接即可,运行run
    • 退出的时候在窗口的上栏中选择logout命令即可。
  6. SSH客户端的-t选项是为了解决多台机器的跳板连接的问题的(最终想要连接的主机不可直连,但是可以通过前面多个主机的SSH跳转连接最终连接到想要连接的主机上)
    • ssh -t remoteserver1 ssh -t remoteserver2 ssh remoteserver3 :注意最后的ssh后面不需要-t

公钥交换

23.ssh协议及实现(openssh)_第1张图片

客户端发起链接请求
服务端返回自己的公钥,以及一个会话ID(这一步客户端得到服务端公钥)
客户端生成密钥对
客户端用自己的公钥异或会话ID,计算出一个值Res,并用服务端的公钥加密
客户端发送加密后的值到服务端,服务端用私钥解密,得到Res
服务端用解密后的值Res异或会话ID,计算出客户端的公钥(这一步服务端得到客户端公钥)
最终:双方各自持有三个秘钥,分别为自己的一对公、私钥,以及对方的公钥,
之后的所有通讯都会被加密,参看下面的图片

SSH实现加密通讯

ssh服务登录验证

ssh服务登录验证方式

  1. 用户/口令
  2. 基于密钥

1.基于用户和口令登录验证

  1. 客户端发起ssh请求,服务器会把自己的公钥发送给用户(参考上面的交换公钥的过程,最总双方都有对方的公钥)
  2. 用户会根据服务器发来的公钥对密码进行加密
  3. 加密后的信息回传给服务器,服务器用自己的私钥解密,如果密码正确,则用户登录成功

2.ssh服务基于密钥登录验证

  1. 首先在客户端生成一对密钥(ssh-keygen)
  2. 并将客户端的公钥ssh-copy-id 拷贝到服务端
  3. 当客户端再次发送一个连接请求,包括ip、用户名
  4. 服务端得到客户端的请求后,会到authorized_keys中查找,如果有响应的IP和用户,就会随机生成一个字符串,例如:magedu
  5. 服务端将使用客户端拷贝过来的公钥进行加密,然后发送给客户端
  6. 得到服务端发来的消息后,客户端会使用私钥进行解密,然后将解密后的字符串发送给服务端
  7. 服务端接受到客户端发来的字符串后,跟之前的字符串进行对比,如果一致,就允许免密码登录

基于key认证实现过程

  1. 在客户端生成密钥对
    ssh-keygen -t rsa [-P ''] [-f “~/.ssh/id_rsa"]
  2. 把公钥文件传输至远程服务器对应用户的家目录
    ssh-copy-id [-i [identity_file]] [user@]host
  3. 测试
  4. 在SecureCRT或Xshell实现基于key验证
    在SecureCRT工具—>创建公钥—>生成Identity.pub文件
    转化为openssh兼容格式(适合SecureCRT,Xshell不需要转化格式),并复制到需登录主机上相应文件authorized_keys中
    注意权限必须为600,在需登录的ssh主机上执行:ssh-keygen -i -f Identity.pub >> .ssh/authorized_keys
  5. 重设私钥口令:
    ssh-keygen –p
  6. 验证代理(authentication agent)保密解密后的密钥
    这样口令就只需要输入一次
    在GNOME中,代理被自动提供给root用户
    否则运行ssh-agent bash
  7. 钥匙通过命令添加给代理
    ssh-add

注意点2:

  1. 注意了基于KEY验证的方式的公钥私钥对和SSH传输数据过程中的公钥私钥对是不同的。前者就是为了登录验证所用,存放在~/.ssh文件夹中,后者是为了双方数据传输的安全性,在第一次连接时会进行交换公钥,它存放在/etc/ssh文件夹下(有不同算法的许多公钥私钥对)。
  2. ssh-keygen生成时直接回车即可,默认rsa,然后可以输入密码对这个生成的公私钥对的私钥进行加密,生成的公私钥对默认存放在当前用户的~/.ssh/id_rsa文件中(也可以修改到其他地方,这样的话拷贝的时候要-i指定文件).
    • 之后拷贝的时候也是把它拷贝到对方的对应用户的家目录下的.ssh文件夹中并更名为authorized_keys,就类似于knows_hosts文件,里面每一项就是一个公钥。
    • 拷贝的时候最好用ssh-copy-id 用户@hostIP 命令,它会自动创建.ssh目录以及authorized文件并拷贝过去,并将文件里的格式写好。(同时如果用-i命令指定本机拷贝的文件为私钥文件,它会自动拷贝的也是公钥文件,不用担心拷贝错误)。
    • 注意拷贝到哪个用户的家目录下,就只能连接这个用户,其他的用户仍旧不能连接。不过此时直接可以在远程要连接的主机上cp到其他用户的家目录下即可。不写用户时默认拷贝到root家目录下
    • 注意如果用命令拷贝的没有问题,但是如果自己手创建的.ssh文件夹和autuorized_keys文件要注意文件夹和文件的权限问题,这里面.ssh文件夹的权限是700,autho文件的权限是600.所属主所属组都是对应用户和它的组
      2.2 注意ssh-keygen命令默认生成的key如果再次执行这个命令会把原先的key的文件给覆盖掉(名字一样的话),而ssh-copy-id命令拷贝key到远程主机上不会覆盖掉远程主机上的authorized文件,而是追加本机的公钥到文件中(因此多个主机均可以连接同一台机器)。一定要注意的就是客户端私钥文件一定要放在对应用户的.ssh文件夹中。
  3. 注意基于Key验证的SSH登陆,就算更改了拷贝了KEY的主机的用户的密码也不会影响SSH的远程登录。因此一定要保证连接控制端的私钥不能丢,不然极度不安全,如果它被偷走并放入其他机器中的.ssh中,它也能连接导入key的主机了!因此最好加上密码:(私钥文件的默认权限也是600)
    • 可以在生成私钥的时候非交互式-N(脚本常用)指定密码:
      ssh-keygen -N 'PASSWORD'
    • 也可以生成私钥的时候交互式输入密码
    • 还有就是如果生成的时候没有加密码,则生成之后可用ssh-keygen -p 选项交互式修改密码(也可以再加上-P -N -f 3个参数并写好对应的旧密码新密码和指定文件来非交互式 修改密码)
  4. 加完口令之后,每次连接ssh服务的时候就会让自己输入口令,不过这次输入的口令是本机私钥文件的口令而非对方用户账户的口令。但这样就实现不了便捷连接的目的了,因此可以通过代理的方式:

    • ssh-agent bash 开启代理,这个命令其实开启了一个子bash并把它当做代理
    • ssh-add 输入口令添加到新开的bash中,这样再连接就不需要口令了
      通过代理的方式就实现了安全以及便捷的目的了(虽然每次开启shell仍然需要一次口令,不过安全性大大提高),不过一旦退出这个子bash shell,则代理就会失效
  5. Xshell中也可以利用KEY验证来登录linux中:

    • 在tools中打开新建用户密钥向导,然后生成密钥
    • 生成密钥之后查看它并将里面的公钥文件Save as a file到windows中
    • 然后将这个公钥文件导入到需要连接的linux主机中,并追加重定向到authorized_keys文件中(注意在root家目录下)
    • 新建连接并选择root用户连接,用KEY验证的方式。此时便可以连接了。
  6. 如果在一个集群环境中想要所有的机器都能互相连接(或者说某些机器能够连接其他所有机器),只需要把公私钥和authorized_keys这三个文件同时拷贝到所有主机上的.ssh文件中(拷贝到需要连接所有主机的机器上,只能被连接的主机只拷贝authorized文件)即可。

    • 简单点的做法就是生成公私钥对之后,先用ssh-copy-id命令拷贝到自己机器上(生成authorize),然后把ssh整个目录拷贝到需要连接并控制其他机器的主机上即可。
  7. scp命令不仅可以双方互拷贝,还能够控制别的两台机器之间向互拷贝(先做好KEY验证或者说知道用户密码)**
  8. scp命令默认和cp命令一样,拷贝的时候直接覆盖(cp -i可以询问,scp没有)。同时它还有一个缺点就是不能够实现只拷贝修改过的文件(没有类似cp -u的命令)。因此可以用rsync命令代替它弥补这个缺点
    • rsync和scp的用法基本上一致(也类似cp),不过它的好处就是默认只拷贝变化过的文件(虽然也是直接覆盖),与scp相比能够减少带宽,只拷贝需要拷贝的文件。
    • 因此可用它用来实现服务器之间的数据相互同步(实现备份),(当然相互更改也可以,此时没有主从备份之分),并且能够使用更少的带宽。
    • 需要注意的就是它拷贝目录的时候后面加上/和不加/分别代表拷目录下文件和目录本身,这点和scp和cp都不相同。(但要注意其它的问题比如目录存在不存在的不同情况,详细查看cp命令的表格)

其他一些命令:

scp命令

  • scp [options] SRC... DEST/
  • 两种方式:
    scp [options] [user@]host:/sourcefile /destpath
    scp [options] /sourcefile [user@]host:/destpath
  • 常用选项:
    -C 压缩数据流
    -r 递归复制
    -p 保持原文件的属性信息
    -q 静默模式
    -P PORT 指明remote host的监听的端口(默认也是受ssh_config控制)

rsync命令

基于ssh和rsh服务实现高效率的远程系统之间复制文件
使用安全的shell连接做为传输方式
rsync -av /etc server1:/tmp 复制目录和目录下文件
rsync -av /etc/ server1:/tmp 只复制目录下文件
比scp更快,只复制不同的文件

  • 常用选项:
    -n 模拟复制过程
    -v 显示详细过程
    -r 递归复制目录树
    -p 保留权限
    -t 保留时间戳
    -g 保留组信息
    -o 保留所有者信息
    -l 将软链接文件本身进行复制(默认)
    -L 将软链接文件指向的文件复制
    -a 存档,相当于–rlptgoD,但不保留ACL(-A)和SELinux属性(-X)

sftp命令(几乎不用)

交互式文件传输工具
用法和传统的ftp工具相似
利用ssh服务实现安全的文件上传和下载
使用ls cd mkdir rmdir pwd get put等指令,可用?或help获取帮助信息
sftp [user@]host
sftp> help

轻量级自动化运维工具

  • pssh:基于python编写,可在多台服务器上执行命令的工具,也可实现文件复制,提供了基于ssh和scp的多个并行工具
    项目:http://code.google.com/p/parallel-ssh/
  • pdsh:Parallel remote shell program,是一个多线程远程shell客户端,可以并行执行多个远程主机上的命令。 pdsh可以使用几种不同的远程shell服务,包括标准的“rsh” ,Kerberos IV和ssh
    项目: https://pdsh.googlecode.com/
  • mussh:Multihost SSH wrapper,是一个shell脚本,允许您使用一个命令在多个主机上通过ssh执行命令或脚本。 mussh可使用ssh-agent和RSA / DSA密钥,以减少输入密码
    项目:http://www.sourceforge.net/projects/mussh

说明:以上工具都包含在EPEL源中
yum install pssh

PSSH工具

选项如下:
--version:查看版本
-h:主机文件列表,内容格式为” [user@]host[:port]”
-H:主机字符串,内容格式为” [user@]host[:port]”
-A:手动输入密码模式
-i:每个服务器内部处理信息输出
-l:登录使用的用户名
-p:并发的线程数【可选】
-o:输出的文件目录【可选】,此项会让对方主机执行命令的结果输出到本机的-o后面指定的目录中,创建两个对方HOST名的文件来保存。
-e:错误输出文件【可选】
-t:TIMEOUT 超时时间设置,0无限制【可选】
-O:SSH的选项
-P:打印出服务器返回信息
-v:详细模式

pssh示例

  • 通过pssh批量关闭seLinux
    pssh -H [email protected] -i ‘sed -i "s/^SELINUX=.*/SELINUX=disabled/" /etc/selinux/config’
  • 批量发送指令
    pssh -H [email protected] -i setenforce 0
    pssh -H [email protected] -i hostname
  • 当不支持ssh的key认证时,通过 -A选项,使用密码认证批量执行指令
    pssh -H [email protected] -A -i hostname
  • 将标准错误和标准正确重定向都保存至/app目录下
    pssh -H 192.168.1.10 -o /app -e /app -i “hostname”

pscp.pssh命令

  • pscp.pssh功能是将本地文件批量复制到远程主机
    pscp [-vAr] [-h hosts_file] [-H [user@]host[:port]] [-l user] [-p par] [-o outdir] [-e errdir] [-t timeout] [-O options] [-x args] [-X arg] local remote
  • pscp-pssh选项
    -v 显示复制过程
    -r 递归复制目录
  • 将本地curl.sh 复制到/app/目录
    pscp.pssh -H 192.168.1.10 /root/test/curl.sh /app/
    pscp.pssh -h host.txt /root/test/curl.sh /app/
  • 将本地多个文件批量复制到/app/目录
    pscp.pssh -H 192.168.1.10 /root/f1.sh /root/f2.sh /app/
  • 将本地目录批量复制到/app/目录
    pscp.pssh -H 192.168.1.10 -r /root/test/ /app/

pslurp命令

  • pslurp功能是将远程主机的文件批量复制到本地
    pslurp [-vAr] [-h hosts_file] [-H [user@]host[:port]] [-l user] [-p par][-ooutdir] [-e errdir] [-t timeout] [-O options] [-x args] [-X arg] [-L localdir] remote local(本地名)
  • pslurp选项
    -L 指定从远程主机下载到本机的存储的目录,local是下载到本地后的名称
    -r 递归复制目录
  • 批量下载目标服务器的passwd文件至/app下,并更名为user
    pslurp -H 192.168.1.10 -L /app /etc/passwd user

注意点3:

  1. pssh可以实现小型化的自动化运维操作,利用-H选项的时候有两种方式
    • pssh -H HOST1 -H HOST2 CMD
    • pssh -H “HOST1 HOST2 ...” CMD
  2. 它利用的验证方式和ssh是一样的,可以基于key验证和密码验证。
    • 基于密码验证的时候要加上-A选项,此时它会变成交互式方式让用户输入密码。但是注意仅能输入一次密码,因此如果被控制的主机的密码不相同则无法实现全部控制并操作的效果。
  3. 利用-i选项可以看到被操作的主机上的命令的结果的输出(标准输出或者错误),不过注意一定要把pssh的各种选项放在前面,最后一项再写上要在被控制的主机上的CMD命令
    • Usage: pssh [OPTIONS] command [...]
  4. 同时经过测试得知最后一项只能写一个命令,如果写的更多的只能被当做这个命令的参数来使用。就算加上单引号或者双引号把命令分隔开也无效(就算加上还是会被当做命令以及命令的参数)
  5. 更加方便操作管理的方式是利用-h HOSTfile的方式来执行,其中HOSTfile中的每一行就是一个要被控制的主机IP地址
  6. 特别注意点:注意不论是ssh还是pssh不指定用户名的时候,都是会以当前在控制端(也就是本机)登陆的用户名为默认登陆用户名(也就是把它当做HOST前@前面写的用户名)来进行远程主机的相同的用户名进行登陆连接。
  7. 还有就是执行命令的时候如果需要对方主机的变量,以及使用通配符等,则需要用单引号(双引号无效)引起来才可,不然会当做为本机的变量给解释并使用了,比如说
    • pssh -H 192.168.36.103 -H 192.168.36.144 -i echo '$HOSTNAME'
      :$HOSTNAME必须单引号引起来,当然也可以把整个CMD命令 'echo $HOSTNAME'引起来
    • pssh -H 192.168.36.103 -H 192.168.36.144 -i 'rm -rf /data/* '
      : *这里如果不用单引号引起来rm命令,则会把/data/ 中的通配符解释为本机的/data/下的所有非隐藏文件而不是对方主机上的,需要注意**
  8. pscp.pssh 只能把本地的文件批量复制到远程主机上,注意可以一次性复制多个(或者目录,别忘了加-r),但是要注意要把目标端写在最后面,要复制的文件或者目录(可以多个)写在中间,最前面写选项:Usage: pscp.pssh [OPTIONS] local remote
  9. 而把远程主机上的文件复制到本机上的命令为pslurp命令,注意它的选项也是写在最前面(-h file -L localdir 中-h -L都是选项,只不过有参数而已),然后写上远程的要复制的文件或者目录,最后可以写上想要被改名的名字:
    • Example: pslurp -h hosts.txt -L /tmp/outdir -l irb2 /home/irb2/foo.txt foo.txt
    • 此命令就是把hosts.txt中的主机下,以irb2的用户连接它们并将它的家目录下的foo.txt文件复制到本机的outdir文件夹中并改名为foo.txt。 此命令会在本机-L后面的文件夹中创建hosts中对应主机名字的文件夹并将被复制的文件放入进去。

简单测试用(可用它配合命令修改selinux,也可直接用pscp.pssh拷贝本机selinux配置文件覆盖远程端的配置文件):

20:17[root@centos7 ~]# pssh -H 192.168.36.103 -H 192.168.36.144 -A -i hostname 
Warning: do not enter your password if anyone else has superuser
privileges or access to your account.
Password: 
[1] 20:17:21 [SUCCESS] 192.168.36.103
Centos6.10Test
[2] 20:17:21 [SUCCESS] 192.168.36.144
centos7.6test
20:17[root@centos7 ~]# pssh -H 192.168.36.103 -H 192.168.36.144 -i hostname 
[1] 20:17:26 [SUCCESS] 192.168.36.103
Centos6.10Test
[2] 20:17:26 [SUCCESS] 192.168.36.144
centos7.6test
20:17[root@centos7 ~]# pssh -H 192.168.36.103 -H 192.168.36.144  hostname 
[1] 20:17:31 [SUCCESS] 192.168.36.103
[2] 20:17:31 [SUCCESS] 192.168.36.144
20:17[root@centos7 ~]# 

20:38[root@centos7 ~]# pssh -H 192.168.36.103 -H 192.168.36.144 -i echo $HOSTNAME
[1] 20:43:24 [SUCCESS] 192.168.36.103
centos7.6test
[2] 20:43:24 [SUCCESS] 192.168.36.144
centos7.6test
20:43[root@centos7 ~]# pssh -H 192.168.36.103 -H 192.168.36.144 -i echo '$HOSTNAME'
[1] 20:43:30 [SUCCESS] 192.168.36.103
nihao
[2] 20:43:30 [SUCCESS] 192.168.36.144
nihao

批量复制本机文件到对方并且改名
21:11[root@centos7 ~]# pscp.pssh -H 192.168.36.103 -H 192.168.36.144 /etc/fstab /data/fstabpscptest
[1] 21:16:16 [SUCCESS] 192.168.36.103
[2] 21:16:16 [SUCCESS] 192.168.36.144

再复制回来,经过测试得知必须严格按照下面的格式才可。
Example: pslurp -h hosts.txt -L /tmp/outdir -l irb2 /home/irb2/foo.txt foo.txt

21:19[root@centos7 ~]# pslurp -H 192.168.36.103 -H 192.168.36.144  -L /data/ /data/fstabpscptest fstabtestpul
[1] 21:19:48 [SUCCESS] 192.168.36.103
[2] 21:19:48 [SUCCESS] 192.168.36.144
21:19[root@centos7 ~]# cd /data
21:20[root@centos7 /data]# ls 192.*
192.168.36.103:
fstabtestpul

192.168.36.144:
fstabtestpul

SSH端口转发

  • SSH端口转发
    SSH 会自动加密和解密所有 SSH 客户端与服务端之间的网络数据。但是,SSH 还能够将其他 TCP 端口的网络数据通过 SSH 链接来转发,并且自动提供了相应的加密及解密服务。这一过程也被叫做“隧道”(tunneling),这是因为SSH 为其他 TCP 链接提供了一个安全的通道来进行传输而得名。例如,Telnet,SMTP,LDAP 这些 TCP 应用均能够从中得益,避免了用户名,密码以及隐私信息的明文传输。而与此同时,如果工作环境中的防火墙限制了一些网络端口的使用,但是允许 SSH 的连接,也能够通过将 TCP 端口转发来使用 SSH 进行通讯

SSH 端口转发能够提供两大功能:

加密 SSH Client 端至 SSH Server 端之间的通讯数据
突破防火墙的限制完成一些之前无法建立的 TCP 连接

SSH端口转发

本地转发:

-L localport:remotehost:remotehostport sshserver

  • 选项:
    -f 后台启用
    -N 不打开远程shell,处于等待状态
    -g 启用网关功能
  • 示例
    ssh –L 9527:telnetsrv:23 -Nfg sshsrv :开启本地端口转发
    telnet 127.0.0.1 9527 :连接本地的端口9527(这个命令就相当于连接到了
    当访问本机的9527的端口时,被加密后通过SSH隧道转发到sshsrv的ssh服务,再解密被转发到telnetsrv:23
    data <--> ocalhost:9527(本地SSH转发端口,自定义的转发端口) <--> localhost:XXXXX(本地作为SSH客户端,发送的随机端口) <--> sshsrv:22(对方作为SSH服务器,接收端口) <--> sshsrv:YYYYY(对方作为telnet客户端,发送的随机端口) <--> telnetsrv:23(最终连接的主机telnet服务器,接收端口)

远程转发:

-R sshserverport:remotehost:remotehostport sshserver

  • 示例:
    ssh –R 9527:telnetsrv:23 –Nf sshsrv
    让sshsrv侦听9527端口的访问,如有访问,就加密后通过ssh服务转发请求到本机ssh客户端,再由本机解密后转发到telnetsrv:23
    Data <--> sshsrv:9527 <--> sshsrv:22 <--> localhost:XXXXX <--> localhost:YYYYY <--> telnetsrv:23

动态端口转发:

当用firefox访问internet时,本机的1080端口做为代理服务器,firefox的访问请求被转发到sshserver上,由sshserver替之访问internet
ssh -D 1080 root@sshserver -fNg
在本机firefox设置代理socket proxy:127.0.0.1:1080
curl --socks5 127.0.0.1:1080 http://www.google.com

X 协议转发

  • 所有图形化应用程序都是X客户程序
    能够通过tcp/ip连接远程X服务器
    数据没有加密机,但是它通过ssh连接隧道安全进行
  • ssh -X user@remotehost gedit
    remotehost主机上的gedit工具,将会显示在本机的X服务器上
    传输的数据将通过ssh连接加密

协议转发注意点:

  1. Xshell命令中可以直接打开gnome命令,只不过centos6和7不同而已。并不一定需要xstart,看下面的2中。
  2. 可以用ps aux | grep gnome 查看已经打开的进程来判断 gnome的打开命令,同时可以gnome-session --help查看帮助:
    • 6:/usr/bin/gnome-session --display $DISPLAY
    • 7:/usr/bin/gnome-session --session gnome-classic
  3. 如果在Xshell中无法拉取gedit等图形窗口,可以查看是否在对方主机的sshd_config中开启了两个转发的功能(详细看下面)
  4. 注意-X 选项可以换成 -Y 试试效果
  5. 如果两台主机都是linux主机,则需要另外的方法抓取,这里说的是利用windows的Xmanager来抓取linux的桌面以及图形工具的界面(当然也可以先利用Xshell连接到一台linux主机上,然后再次SSH连接到另外一台主机,然后利用genome,gedit等命令打开这个二次连接的主机上的图形界面)
检查是否安装xterm和xrog
[root@localhost bin]# rpm -qa|grep xorg-x11-xauth -y
xorg-x11-xauth-1.0.9-1.el6.x86_64
[root@localhost bin]# rpm -qa|grep xterm
xterm-253-1.el6.x86_64
检查是否安装了图形化界面
[root@localhost bin]# more /etc/inittab
id:5:initdefault:
到底应该可以确定sshd禁止了x11Forwarding功能呢个
修改文件
[root@localhost ~]# vi /etc/ssh/sshd_config
AllowTcpForwarding yes
X11Forwarding yes
重启sshd服务

注意点4:

  1. 本地开启SSH端口转发之后的数据传输过程详细看上面所写,需要注意的是本机一定要能够利用openssh连接到中间的一个用于转发(跳板)的机器的SSH服务才可,不能够被防火墙等安全策略阻隔(基于密码或者key都行)
    -L localport:remotehost:remotehostport sshserver [-p port]
    • 注意-L 只是一个选项而已,后面跟的都是参数,目的就是为了开启通道,其他的用法和ssh的用法相同。
    • localport就是本机要利用的转发到跳板机上的端口,一定要找一个没有使用的端口
    • 其中remotehost就是最终要连接的主机IP(或者配置了DNS之后的可解析域名)
    • remotehostport就是对应的最终连接主机要连接的端口,此端口一定要与接来下需要经过这个端口转发的命令服务想对应。比如如果这里写的是23,那么下面要进行的本机客户端命令肯定就是telnet,不能写其他的(除非自定义了端口)
    • 最后的sshserver就是中间的可以被SSH协议直接相连的转发机(跳板机),可写IP或者域名(要配置DNS)
    • 附加知识点:ssh服务可以连接本机localhost(用命令ssh localhost,本机即当客户端又当服务端,同时因为hosts文件中写的有解析),因为ssh服务默认监听任何IP。(可用ss -ntlp查看)
    • 最后加上-fN选项让它建立隧道并且后台执行不登录,需要关闭的时候杀掉此进程即可。
  2. SSH转发的过程中会对数据进行加密,但是telnet不会,不过因为在企业内网中,所以也不用特别担心安全问题。(注意安装telnet和telnet-server包,以及6中如果做服务器开启xinetd监控)
  3. 不过实际应用中基本上都是有防火墙配置,因此要在防火墙内部建立向外连接的SSH转发,此时用的是-R选项
    -R sshserverport:remotehost:remotehostport sshserver
    在这里和-L 的区别就是把sshserver和ssh客户端相互交换了,在命令的区别就是只有sshserver不同而已:
    • -L 选项是在远程机器上操作,那几项都很好理解上面也已经解释,其中此时的sshserver是跳板机的地址
    • -R 选项则是在跳板机上操作,中间的选项和-L的一模一样,惟一的区别就是sshserver换成了-L选项操作时的机器的IP
    • 最终实现的效果和-L一模一样,只不过跳过了防火墙的限制。可以先按照-L的思想去想然后再按照-R来写。
  4. 前面两种都是单一的只能让跳板机连接到最终端主机的的特定服务和特定端口,但是动态端口转发可让跳板机用于访问它能够连接到的任何主机的任何端口(访问外网)。
    ssh -D localport root@sshserver -fN
    • 注意此选项如果不加-g选项时,是在本机执行的(和-L相同,在本地本机执行),此时需要本机以及跳板机两台机器,这个跳板机可以连接它能连接到的任何机器并将数据转发回来。
    • 不过此时这个跳板机只能接收执行上面的这个命令的主机进行转发和访问(因为22端口只开启了监听执行上面命令的IP,而不是*监听所有的端口)。可以用ss -ntl查看便知。
    • 当然利用浏览器访问时要设置浏览器代理(sockets host)的IP及端口为127.0.0.1 localport(本机IP和本机端口)
    • 此命令不能用其他机器利用跳板机进行访问了(想要访问每台机器都要执行上面的命令),而且只限定了能够执行上面命令的linux机器才能(windows无法执行此命令)
      ssh -D localport root@sshserver(localhost) -fNg
    • 注意此命令加了-g之后,执行命令的机器是跳板机上,(相当于把上个命令中的本地机和跳板机合二为一)
    • 执行之前要现在sshd_config服务端配置文件中修改GATEWAYPorts 为yes ,然后再执行上面的命令,其中中间的主机就是本机的IP,可以直接写localhost.
    • 此时再用ss -ntl命令就可看到22端口前面的IP不是绑定为单一IP,而是任意IP的符号*了。这样就可以用其他任何机器利用此转发服务器机进行访问了(包括windos系统,不过也是要在浏览器中配置代理)
  5. 注意还有一种可能就是两台机器按照4中第一种的配置来配置,然后其他的机器通过这个配置中的控制端来两次转发,实现控制企业业内的机器的效果。
    • 此时4中第一种的配置的命令中,也要开启路由配置以及-g选项,把这个防火墙外的控制端主机作为防火墙外的跳板机,防火墙内的服务器作为防火墙内的跳板机(两个跳板机),然后再用其他的机器连接防火墙外的这个控制端跳板到防火墙内的服务器跳板,然后再连接防火墙内的其他机器。
  6. 5中不常用,最常用的就是直接用另外的机器先ssh连接到这个防火墙外的机器上,然后再次ssh连接到防火墙内的跳板机(这个跳板机只允许防火墙外的那个跳板机连接,可以配置),然后再控制防火墙内的其他主机。

本地转发测试示例(注意telnet服务受到PAM控制默认不能root远程登陆):

提前在最终的目标端配置好telnet-server服务,跳板转发端配置好telnet客户端以及ssh的服务端,本机配置好telnet的客户端(要连接端口用)
ss -na |grep 9696 :查看9696端口是否被占用
ssh -L 9696:192.168.36.103:23 192.168.36.144 -fN
telnet 127.0.0.1 192.168.36.103 -l zhang

最后利用ps aux | grep ssh ,然后kill PID 杀掉这个后台进程即可,不过在此之前查看:
注意下面的192.168.36.1是windows的IP,本机地址为102,跳板机为144,最终要连接到为103
本机查看ss -nt
ESTAB      0      0                                       127.0.0.1:60948                                               127.0.0.1:9696               
ESTAB      0      0                                  192.168.36.102:50902                                          192.168.36.144:22                 
ESTAB      0      0                                       127.0.0.1:9696                                                127.0.0.1:60948              
ESTAB      0      52                                 192.168.36.102:22                                               192.168.36.1:2109               
ESTAB      0      0                                  192.168.36.102:22                                               192.168.36.1:8360    
跳板机查看ss -nt
ESTAB      0      0                                  192.168.36.144:43140                                          192.168.36.103:23                 
ESTAB      0      52                                 192.168.36.144:22                                               192.168.36.1:8362               
ESTAB      0      0                                  192.168.36.144:22                                             192.168.36.102:50902    
最终端机查看ss -nt
ESTAB      0      0                                               192.168.36.103:23                                             192.168.36.144:43140 
ESTAB      0      52                                              192.168.36.103:22                                               192.168.36.1:2107  

远程转发测试示例:

上面的操作隧道kill后,直接在跳板机上输入
ssh -R 9696:192.168.36.103:23 192.168.36.102 -fN
既可以达到和上面一模一样的效果,注意端口9696不是在跳板机上打开的
然后控制端输入
telnet 127.0.0.1 192.168.36.103 -l zhang 
测试结果和上面也一样,就是服务器和客户端交换而已

本机开启网关并动态端口转发:

注意要在将要作为sshd服务器(跳板机上)执行此命令
先在sshd_config文件中修改GATWAYports 为yes,然后
ssh -D 9696 localhost(或者本机ip) -gfN
最后配置windows浏览器代理IP和端口指向这个机器和9696即可

ssh服务器

服务器端:sshd, 配置文件: /etc/ssh/sshd_config ,修改完别忘了重启服务。更详细查看man sshd_config
常用参数:

Port  : 此项修改作为服务器端的端口号,默认22.注意修改之后Xshell别忘了改
ListenAddress ip :默认所有的IP 可以自己修改指定某些特定的IP链接,其他的不能连接
LoginGraceTime 2m :未认证连接状态(也就是密码没输入(或没输入正确)但是ssh并未断开时)最大能持续时间,默认120秒。
PermitRootLogin yes : 是否允许SSH用root账号登陆,ubantu默认就是不能登陆。可以改为不允许,用普通用户登陆之后再用su 或者 sudo 切换。更加安全(避免***暴力猜解root口令)
StrictModes yes :检查.ssh/文件的所有者,权限等,如果不匹配则无法登陆
MaxAuthTries 6  :它的值的一半代表登陆时输入的口令最大次数,超过这个输入次数(输错)则连接断开。
MaxSessions 10 : 同一个连接内,SSHD服务支持的连接最多会话数量。改为1的话同一时间一台连接的主机上只能打开一个session。 注意它指的是一个连接(相同IP相同端口号)内的会话(session)数量,而不是连接数量;它不影响本机再开一个端口(相同IP但是不同的端口号)再次建立一个SSH连接.
PubkeyAuthentication yes  :是否允许基于KEY验证
PermitEmptyPasswords no   :是否允许空口令登陆(一个账号没有密码,是否允许用这个账号登录)
PasswordAuthentication yes :是否允许基于账号密码登陆
GatewayPorts no  :是否允许当做网关端口,在SSHD端口转发的时候开启它,则监听端口会变成所有IP,否则只能是本机的127.0.0.1端口。
ClientAliveInterval 10 :单位:秒 ,它表示客户端连接之后没有进行任何操作的检测间隔,要配合下面的次数一起使用。
假如在这个间隔内没有操作,则次数加1,然后进行下一轮的检测。如果在下一轮这个间隔内仍然没有操作,次数再次加一,直到达到下面规定的次数时,一直没有操作则此时连接断开。
但是如果这整个期间有任何一次操作,则检测次数清零,重新开始检测。
ClientAliveCountMax 0 :默认3 ,根据次数和间隔可以计算出客户端无操作令SSH服务自动断开的最大时间为 :间隔时间*检测次数

UseDNS yes : 是否允许DNS解析域名,不允许则只能输入IP了,把它禁用可以优化SSH加快连接速度
GSSAPIAuthentication yes : 提高速度可改为no 
MaxStartups :未认证连接(也就是连接上但是密码未输入,ssh也未断开的状态)最大值,默认值10
详细看man帮助,它的写法为 10:30:100 ,表示10个最大的未认证连接状态,然后就会以30%的比例丢弃连接,并随着未认证连接数量的增多线性增长这个比例,直到达到100个未认证连接状态的值,此时不能再有新的链接接入(丢弃率达到100%)
Banner /path/file  :登陆的提示信息,指定文件,类似于issue和motd

限制可登录用户的办法:
AllowUsers user1 user2 user3
DenyUsers
AllowGroups
DenyGroups

AllowUsers
             This keyword can be followed by a list of user name patterns, separated by spaces.  If specified, login is allowed only for user
             names that match one of the patterns.  Only user names are valid; a numerical user ID is not recognized.  By default, login is
             allowed for all users.  If the pattern takes the form USER@HOST then USER and HOST are separately checked, restricting logins to par‐
             ticular users from particular hosts.  HOST criteria may additionally contain addresses to match in CIDR address/masklen format.  The
             allow/deny directives are processed in the following order: DenyUsers, AllowUsers, DenyGroups, and finally AllowGroups.  All of the
             specified user and group tests must succeed, before user is allowed to log in.

ssh端口转发还有部分未写完,暂时空在这里,之后继续补充

sshd服务的推荐配置和操作:注意一定要先改了之后再连接到互联网中!

  1. 建议使用非默认端口
  2. 禁止使用protocol version 1
  3. 限制可登录用户
  4. 设定空闲会话超时时长
  5. 利用防火墙设置ssh访问策略
  6. 仅监听特定的IP地址
  7. 基于口令认证时,使用强密码策略
    tr -dc A-Za-z0-9_ < /dev/urandom | head -c 12| xargs
    这里xargs用于换行用的
  8. 使用基于密钥的认证
  9. 禁止使用空密码
  10. 禁止root用户直接登录
  11. 限制ssh的访问频度和并发在线数
  12. 经常分析日志 tail -f /var/log/secure
    可利用这个日志文件来判断是否某些有***嫌疑的IP进行过滤

附加:ssh链接数设置问题

今天碰到一个问题,脚本执行scp文件拷贝,因为拷贝的服务器很多,所以拷贝脚本的实现是在把拷贝动作转后台执行,结果发现一堆文件拷贝失败。比较有迷惑性的是,拷贝失败的通常是同一个文件夹拷贝到所有服务器时失败,所以开头查问题的方向是专门查该文件,后来发现了输出终端的输出错误:ssh_exchange_identification: Connection closed by remote host。
这里的问题是ssh的连接数超出了服务器设置的上限,解决如下:
修改服务器上的这个文件:/etc/ssh/sshd_config,找到两行:
MaxSessions:最大允许链接数,默认10.
MaxStartups:最大允许保持多少个未认证链接(未输入登录密码),默认值是10.
把他们的数字改大,例如改成
MaxSessions 1000
MaxStartups 1000
最后重启sshd service sshd restart,然后重新连接即可。

转载于:https://blog.51cto.com/14228129/2388604

你可能感兴趣的:(23.ssh协议及实现(openssh))