Centos7学习笔记(十四)- SSH部分
1、数字签名
了解ssh之前,先了解什么是数字签名?
http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html
http://www.youdzone.com/signature.html
参看这2篇文章,通俗易懂的解释了什么是数字签名。简单的讲,就是有一个CA中心,它可以颁发“数字证书”,这个证书,可以证明其后的公钥真伪性,以便客户端正确识别。
2、SSH基本原理与运用(一)
以下部分,抄录于“阮一峰”博客,链接:https://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html
a)、基本用法
SSH主要用于远程登录。假定你要以用户名user,登录远程主机host,只要一条简单命令就可以了。
$ ssh user@host
如果本地用户名与远程用户名一致,登录时可以省略用户名。
$ ssh host
SSH的默认端口是22,也就是说,你的登录请求会送进远程主机的22端口。使用p参数,可以修改这个端口。
$ ssh -p 2222 user@host
上面这条命令表示,ssh直接连接远程主机的2222端口。
b)、中间人攻击
SSH之所以能够保证安全,原因在于它采用了公钥加密。
整个过程是这样的:(1)远程主机收到用户的登录请求,把自己的公钥发给用户。(2)用户使用这个公钥,将登录密码加密后,发送回来。(3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。
这个过程本身是安全的,但是实施的时候存在一个风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的。
以下图说明SSH认证中间过程:

对上图的具体解读:
初始状态:topgun终端要登录Server服务器,发起连接请求ssh work@server.com
服务端运行有
ssh服务,并持续监听22号端口,因此可以生成一对公钥和私钥;此时将公钥返回给客户端客户端使用公钥,对登录密码进行加密,(如服务器
work用户密码为xxx),生成公钥加密字符串客户端将公钥加密字符串发送给服务端
服务端使用私钥,解密公钥加密字符串,得到原始密码
校验密码是否合法(此为本机
work密码)返回登录结果给客户端:成功登录或密码错误
在非对称加密中,由于只有公钥会被传输,而私钥是服务端本地保存,因此即便公钥被监听,也无法拿到原始密码,从而登录服务器。
可以设想,如果攻击者插在用户与远程主机之间(比如在公共的wifi区域),用伪造的公钥,获取用户的登录密码。再用这个密码登录远程主机,那么SSH的安全机制就荡然无存了。这种风险就是著名的"中间人攻击"(Man-in-the-middle attack)。
中间人攻击原理图
SSH协议是如何应对的呢?
(说起来其实很简单,ssh让用户自行核实远程主机(server)提供的真实性安全性,具体来说,就是通过获取远程主机明文贴示出的公钥,并对其进行hash计算后,得到128位指纹值,然后本地人工验证,即下面“口令登录”的内容)
c)、口令登录
如果你是第一次登录对方主机,系统会出现下面的提示:
$ ssh user@host The authenticity of host 'host (12.18.429.21)' can't be established. RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d. Are you sure you want to continue connecting (yes/no)?
这段话的意思是,无法确认host主机的真实性,只知道它的公钥指纹,问你还想继续连接吗?
所谓"公钥指纹",是指公钥长度较长(这里采用RSA算法,长达1024位),很难比对,所以对其进行MD5计算,将它变成一个128位的指纹。上例中是98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d,再进行比较,就容易多了。
很自然的一个问题就是,用户怎么知道远程主机的公钥指纹应该是多少?回答是没有好办法,远程主机必须在自己的网站上贴出公钥指纹,以便用户自行核对。
假定经过风险衡量以后,用户决定接受这个远程主机的公钥。
Are you sure you want to continue connecting (yes/no)? yes
系统会出现一句提示,表示host主机已经得到认可。
Warning: Permanently added 'host,12.18.429.21' (RSA) to the list of known hosts.
然后,会要求输入密码。
Password: (enter password)
如果密码正确,就可以登录了。
当远程主机的公钥被接受以后,它就会被保存在文件$HOME/.ssh/known_hosts之中。下次再连接这台主机,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。
每个SSH用户都有自己的known_hosts文件,此外系统也有一个这样的文件,通常是/etc/ssh/ssh_known_hosts,保存一些对所有用户都可信赖的远程主机的公钥。
d)、公钥登录(免密登录)
使用密码登录,每次都必须输入密码,非常麻烦。好在SSH还提供了公钥登录,可以省去输入密码的步骤。
所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。
这种方法要求用户必须提供自己的公钥。如果没有现成的,可以直接用ssh-keygen生成一个:
$ ssh-keygen
ssh-keygen常用参数如下:
-t:指定生成密钥类型(rsa、dsa)。默认为rsa-f:指定存放私钥的文件,公钥文件名为私钥文件名加.pub后缀。默认为id_rsa-P:指定passphrase(私钥的密码),用于确保私钥的安全。默认为空-C:备注。默认为user@hostname
运行上面的命令以后,系统会出现一系列提示,可以一路回车。其中有一个问题是,要不要对私钥设置口令(passphrase),如果担心私钥的安全,这里可以设置一个。
运行结束以后,在$HOME/.ssh/目录下,会新生成两个文件:id_rsa.pub和id_rsa。前者是你的公钥,后者是你的私钥。
这时再输入下面的命令,将公钥传送到远程主机host上面:
$ ssh-copy-id user@host
ssh-copy-id命令就几个参数,
-f不检查远端主机是否存在公钥文件,可能导致远端主机有多份公钥文件的拷贝。
-n不是安装公钥文件到远程主机,仅仅是把文件内容打印看一下。
-i指定公钥文件名,若不指定,则用默认名。
-p指定ssh端口
-o指定ssh参数选项
需要说明的是,如果是为一个之前从未ssh登录过的远端主机做次免密登录,那么上面的口令登录过程,还是免不了要来一遍的,类似下面过程(当然,也可以用-o StrictHostKeyChecking=no的方式来免除那个输入yes的过程)。
(若想完全做到非交互式执行命令呢?那么需要安装sshpass软件,用“sshpass -p密码 ssh-copy-id -f -i ~/.ssh/abc.pub "-o StrictHostKeyChecking=no" -p 11223 root@192.168.1.30” )
[root@c7study ~]# ssh-copy-id -i abc.pub -p 11223 root@192.168.1.30 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "abc.pub" The authenticity of host '192.168.1.30:11223 ([192.168.1.30]:11223)' can't be established. ECDSA key fingerprint is SHA256:+J2vwsLe2Jp5lI/aQLm9kLDRSaHrKXsbSg1a/jvTKGc. ECDSA key fingerprint is MD5:ab:13:a3:6e:52:3a:7b:42:b0:04:3e:a6:ce:f8:b1:fd. Are you sure you want to continue connecting (yes/no)? yes /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys root@www.hcrbbs.top's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh -p '11223' 'root@192.168.1.30'" and check to make sure that only the key(s) you wanted were added.
(补充说明一下,执行以上命令,其实就是将本地生成的公钥id_rsa.pub内容,完全复制到远端主机user用户.ssh/目录下的authorized_keys文件中,如果.ssh目录和该文件都没有,则先创建再复制)
(么完成这一步,正常情况下,远端主机user用户.ssh/目录下应仅有authorized_keys文件。在etc/ssh/下,ssh_host_*_key文件和ssh_host_*_key.pub文件,分别对应各类算法下,自身的公钥文件和私钥文件)
好了,从此你再登录,就不需要输入密码了。
如果还是不行,就打开远程主机的/etc/ssh/sshd_config这个文件,检查下面几行前面"#"注释是否取掉。
RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys
然后,重启远程主机的ssh服务。
// ubuntu系统 service ssh restart // debian系统 /etc/init.d/ssh restart
PS:特别要注意,这里有一个巨坑——如果在ssh-keygen时,用-f参数指定了私钥文件名(比如不叫默认的id_rsa,改为manger_rsa之类。包括指定了私钥文件名的存放位置,比如这样:"-f /home/study/manger_rsa"),那么,一定要在“~/.ssh/”下生成“config”文件,并在文件中,指定私钥文件名的位置(就如下面要讲的“所Server免密登录配置”中的config文件配置一样)。原因呢,就因为在执行ssh登录的时候,如果没有config配置文件,那么默认引用的私钥就是“~/.ssh/id_rsa”,但这个文件因为-f参数,并不存在,所以秘钥认证过程,就会因为找不到私钥文件,认证不通过,从而又变成了password认证。
$vim ~/.ssh/config Host 172.16.1.7 User study IdentityFile ~/.ssh/manager_rsa
网上有一篇文章——https://www.cnblogs.com/li-mzx/p/13140061.html,详细讲述了,免密配置后还出现密码认证的调试解决过程,非常有参考意义。
其中,主要的是让服务器端,用/bin/sbin/sshd -d的方式,将ssh服务以守护进程的方式运行,方便直接调试显示ssh认证过程中的错误。客户端呢,ssh增加-v参数,显示认证过程的详细信息,便于查看分析问题。
e)多Server免密登录配置
首先说明一下免密登录配置后,本地用户.ssh/目录下所应有的文件
id_rsa // 私钥文件,执行ssh-keygen生成,未指定私钥文件名时,默认的文件名 id_rsa.pub // 公钥文件,执行ssh-keygen生成 known_hosts // 确认过公钥指纹的可信服务器列表的文件,这个文件仅在未实行免密登录前,就成功认证登录远端Server后产生 config // 默认无该文件,可手工创建产生,可用户指定不同域名使用哪个密钥的配置文件
(如果本地主机,也被当做ssh服务端并做免密ssh远程访问过,那么,也会有“authorized_keys” 文件)
我们的服务器会有很多的用户,如果所有的用户都用同一份密钥,可能就没办法划分权限或者区分用户,如多个用户提交git就需要按照用户名来生成密钥,用于区分用户。同时你可能也希望针对不同的服务器使用不同的密钥对,因此需要config配置文件来配置针对不同服务器的配置:
$vim ~/.ssh/config Host a.baidu.com 《============= 目标主机(服务端)名称,可以是域名,可以是IP地址,可以是netbios名称 User work 《============= 目标主机的ssh登录用户名 Port 52113 《============= 目标主机的ssh登录端口 IdentityFile ~/.ssh/id_rsa 《============= 客户端所需要引用的私钥文件,需要写绝对路径 Host b.baidu.com User user IdentityFile ~/.ssh/user
这样在连接不同的服务器时,就会使用不同的密钥文件来登录。
在客户端生成密钥对之后,将公钥追加到服务器的authorized_keys文件中即可。
(这里,需要注意,config文件权限必须是600)
(其实,不光是config文件的权限,整个需要注意服务端和客户端“”~”目录的权限必须是700,“.ssh”目录的权限必须是600)
贴一个实际在用的config文件写法:
HOST 172.16.1.* User study Port 52113 IdentityFile ~/.ssh/m01_61 HOST www.hcrbbs.top User root Port 56319 IdentityFile ~/.ssh/www_hcrbbs
免密登录的原理图,用下图表达:

具体解释:
在客户端使用
ssh-keygen生成一对密钥:公钥+私钥将客户端公钥追加到服务端的
authorized_key文件中,完成公钥认证操作认证完成后,客户端向服务端发起登录请求,并传递公钥到服务端
服务端检索
authorized_key文件,确认该公钥是否存在如果存在该公钥,则生成随机数
R,并用公钥来进行加密,生成公钥加密字符串pubKey(R)将公钥加密字符串传递给客户端
客户端使用私钥解密公钥加密字符串,得到
R服务端和客户端通信时会产生一个会话
ID(sessionKey),用MD5对R和SessionKey进行加密,生成摘要(即MD5加密字符串)客户端将生成的
MD5加密字符串传给服务端服务端同样生成
MD5(R,SessionKey)加密字符串如果客户端传来的加密字符串等于服务端自身生成的加密字符串,则认证成功
此时不用输入密码,即完成建连,可以开始远程执行
shell命令了
f)、authorized_keys文件
远程主机将用户的公钥,保存在登录后的用户主目录的$HOME/.ssh/authorized_keys文件中。公钥就是一段字符串,只要把它追加在authorized_keys文件的末尾就行了。
这里不使用上面的ssh-copy-id命令,改用下面的命令,解释公钥的保存过程:
$ ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub
这条命令由多个语句组成,依次分解开来看:(1)"$ ssh user@host",表示登录远程主机;(2)单引号中的mkdir .ssh && cat >> .ssh/authorized_keys,表示登录后在远程shell上执行的命令:(3)"$ mkdir -p .ssh"的作用是,如果用户主目录中的.ssh目录不存在,就创建一个;(4)'cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub的作用是,将本地的公钥文件~/.ssh/id_rsa.pub,重定向追加到远程文件authorized_keys的末尾。
写入authorized_keys文件后,公钥登录的设置就完成了。
3、SSH原理与运用(二)
以下部分,抄录于“阮一峰”博客,链接:http://www.ruanyifeng.com/blog/2011/12/ssh_port_forwarding.html
g)、远程操作
SSH不仅可以用于远程主机登录,还可以直接在远程主机上执行操作。
上一节的操作,就是一个例子:
$ ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub
单引号中间的部分,表示在远程主机上执行的操作;后面的输入重定向,表示数据通过SSH传向远程主机。
这就是说,SSH可以在用户和远程主机之间,建立命令和数据的传输通道,因此很多事情都可以通过SSH来完成。
下面看几个例子。
【例1】
将$HOME/src/目录下面的所有文件,复制到远程主机的$HOME/src/目录。
$ cd && tar czv src | ssh user@host 'tar xz'
【例2】
将远程主机$HOME/src/目录下面的所有文件,复制到用户的当前目录。
$ ssh user@host 'tar cz src' | tar xzv
【例3】
查看远程主机是否运行进程httpd。
$ ssh user@host 'ps ax | grep [h]ttpd'
h)、绑定本地端口
既然SSH可以传送数据,那么我们可以让那些不加密的网络连接,全部改走SSH连接,从而提高安全性。
假定我们要让8080端口的数据,都通过SSH传向远程主机,命令就这样写:
$ ssh -D 8080 user@host
SSH会建立一个socket,去监听本地的8080端口。一旦有数据传向那个端口,就自动把它转移到SSH连接上面,发往远程主机。可以想象,如果8080端口原来是一个不加密端口,现在将变成一个加密端口。
i)、本地端口转发
有时,绑定本地端口还不够,还必须指定数据传送的目标主机,从而形成点对点的"端口转发"。为了区别后文的"远程端口转发",我们把这种情况称为"本地端口转发"(Local forwarding)。
假定host1是本地主机,host2是远程主机。由于种种原因,这两台主机之间无法连通。但是,另外还有一台host3,可以同时连通前面两台主机。因此,很自然的想法就是,通过host3,将host1连上host2。
我们在host1执行下面的命令:
$ ssh -L 2121:host2:21 host3
命令中的L参数一共接受三个值,分别是"本地端口:目标主机:目标主机端口",它们之间用冒号分隔。这条命令的意思,就是指定SSH绑定本地端口2121,然后指定host3将所有的数据,转发到目标主机host2的21端口(假定host2运行FTP,默认端口为21)。
这样一来,我们只要连接host1的2121端口,就等于连上了host2的21端口。
$ ftp localhost:2121
"本地端口转发"使得host1和host3之间仿佛形成一个数据传输的秘密隧道,因此又被称为"SSH隧道"。
下面是一个比较有趣的例子。
$ ssh -L 5900:localhost:5900 host3
它表示将本机的5900端口绑定host3的5900端口(这里的localhost指的是host3,因为目标主机是相对host3而言的)。
另一个例子是通过host3的端口转发,ssh登录host2。
$ ssh -L 9001:host2:22 host3
这时,只要ssh登录本机的9001端口,就相当于登录host2了。
$ ssh -p 9001 localhost
上面的-p参数表示指定登录端口。
j)、远程端口转发
既然"本地端口转发"是指绑定本地端口的转发,那么"远程端口转发"(remote forwarding)当然是指绑定远程端口的转发。
还是接着看上面那个例子,host1与host2之间无法连通,必须借助host3转发。但是,特殊情况出现了,host3是一台内网机器,它可以连接外网的host1,但是反过来就不行,外网的host1连不上内网的host3。这时,"本地端口转发"就不能用了,怎么办?
解决办法是,既然host3可以连host1,那么就从host3上建立与host1的SSH连接,然后在host1上使用这条连接就可以了。
我们在host3执行下面的命令:
$ ssh -R 2121:host2:21 host1
R参数也是接受三个值,分别是"远程主机端口:目标主机:目标主机端口"。这条命令的意思,就是让host1监听它自己的2121端口,然后将所有数据经由host3,转发到host2的21端口。由于对于host3来说,host1是远程主机,所以这种情况就被称为"远程端口绑定"。
绑定之后,我们在host1就可以连接host2了:
$ ftp localhost:2121
这里必须指出,"远程端口转发"的前提条件是,host1和host3两台主机都有sshD和ssh客户端。
k)、SSH的其他参数
SSH还有一些别的参数,也值得介绍。
N参数,表示只连接远程主机,不打开远程shell;T参数,表示不为这个连接分配TTY。这个两个参数可以放在一起用,代表这个SSH连接只用来传数据,不执行远程操作。
$ ssh -NT -D 8080 host
f参数,表示SSH连接成功后,转入后台运行。这样一来,你就可以在不中断SSH连接的情况下,在本地shell中执行其他操作。
$ ssh -f -D 8080 host
要关闭这个后台连接,就只有用kill命令去杀掉进程。
4、scp命令
scp作为ssh的一个附属命令(属于openssh-clients软件),也有推、拉模式,跟rsync相同,具体用法:
推:
scp -P 端口 -rp 本地源文件或目录 用户名@目标主机:/目标目录
拉:
scp -P 端口 -rp 用户名@远端主机:/远端目录 本地目录或文件
具体详见:“Centos7学习笔记(八)-基础命令(三)”中关于scp命令的解释。
5、sftp命令
参数:-oPort————指定使用的端口
sftp登录后的交互命令:


