@cdmonkey
2016-04-27T16:39:16.000000Z
字数 9847
阅读 1129
网络服务
SSH(Secure Shell)由IETF的网络工作小组所制定。SSH是建立在应用层和传输层基础上的安全协议。SSH是目前较可靠,专为远程登录会话以及其他网络服务提供安全性的协议。利用SSH协议可以有效的防止远程管理过程中的信息泄露问题。SSH最初是UNIX系统上的一个程序,后来又迅速扩展到其他操作系统平台。SSH在正确使用时可弥补网络中的安全漏洞。SSH客户端适用于多种平台,几乎所有类UNIX平台以及其他平台都可运行SSH。
传统的网络服务程序,如FTP、POP和Telnet等在本质上都是不安全的,因为它们在网络上使用明文传送口令和数据,别有用心的人非常容易就可以截获这些口令和数据。而且,这些服务程序的安全验证方式也是有其弱点的,那就是很容易受到“中间人(man-in-the-middle)”这种方式的攻击。所谓“中间人”的攻击方式,就是“中间人”冒充真正的服务器接收你传给服务器的数据,然后再冒充你把数据传给真正的服务器。服务器和你之间的数据传送被“中间人”一转手做了手脚之后,就会出现很严重的问题。而通过使用SSH,你可以把所有传输的数据进行加密,那么这种攻击方式就不可能实现了,而且也能够防止DNS欺骗和IP欺骗。使用SSH,还有一个额外的好处就是传输的数据是经过压缩的,所以可以加快传输的速度。SSH有很多功能,它既可以代替Telnet,又可以为其他网络服务提供一个安全的通道。
从客户端来看,SSH提供两种级别的安全验证。
第一种级别:基于口令的安全验证
只要你知道帐号和口令,就可以登录到远程主机。所有传输的数据都会被加密,但是不能保证你正在连接的服务器就是你想连接的服务器。可能会有别的服务器在冒充真正的服务器,也就是受到“中间人”这种方式的攻击。
第二种级别:基于密匙的安全验证
需要依靠密匙,也就是你必须为自己创建“一对”密匙,并把公用密匙放在需要远程访问的服务器上。如果你要通过SSH协议远程连接到服务器上,客户端软件就会向服务器发出请求,请求用你的密匙进行安全验证。服务器收到请求之后,先在该服务器上你的主目录下面寻找你的公用密匙,然后把它和你发送过来的公用密匙进行比较。如果两个密匙一致,服务器就会用公用密匙加密“质询(challenge)”并把它发送给客户端。客户端软件收到“质询”之后就可以用你的私人密匙解密再把它发送给服务器。
用这种方式,你必须知道自己密匙的口令。但是,与第一种级别相比,第二种级别不需要在网络上传送口令。使用第二种级别不仅加密所有传送的数据,而且“中间人”这种攻击方式也是不可能的(因为他没有你的私人密匙)。但是整个登录的过程可能需要10秒。
/etc/ssh/
文件 | 说明 |
---|---|
moduli |
这个文件用于在需要模数的时候从中搜寻符合条件的模数。 |
ssh_config |
该文件是SSH的配置文件,允许你通过设置不同的选项来改变客户端程序的运行方式。 |
sshd_config |
该文件是SSH的配置文件,允许通过设置选项改变守护进程的运行方式。 |
ssh_host_dsa_key |
私钥(DSA) |
ssh_host_dsa_key.pub |
公钥(DSA) |
ssh_host_key |
私钥 |
ssh_host_key.pub |
公钥 |
ssh_host_rsa_key |
私钥(RSA) |
ssh_host_rsa_key.pub |
公钥(RSA) |
/home/user/.ssh/
文件 | 说明 |
---|---|
authorized_keys |
认证文件。 |
id_dsa |
基于密钥的身份验证的私钥文件。 |
id_dsa.pub |
基于密钥的身份验证的公钥文件。 |
我们可以利用SSH来完成哪些工作?我想最为常用的就是在客户端(往往是Windows系统)使用类似SecureCRT这样的SSH应用软件对服务器进行远程管理。当然,Linux系统本身就有许多的SSH应用程序:
ssh
、scp
(集体使用方法请参见相关文档)。
有些时候,我们在一台服务器上使用ssh命令登录另一台服务器时,或者在复制、移动文件到另一台机器时会用到scp指令,因为它比较安全。但如果每次都要输入密码,就比较烦了,尤其是在shell脚本程序里。不过,如上章节所述,ssh有另一种用密钥对来验证的方式。我们就可以利用这个方式来达到通过ssh无需密码登录另一台机器的目的。
其实“无密码登录”的原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求输入密码。这种方法要求用户必须提供自己的公钥。
每台Linux主机的SSH服务默认是开启的,我们需要做的就是修改配置文件,然后重启服务以使配置生效。
[root@LB-N2 ~]# vim /etc/ssh/sshd_config
Port 22 #这行默认是注释掉的,即缺省端口为22。
PermitRootLogin no
#上面这行也是默认注释掉的(且默认值为yes),即默认允许远程客户端通过服务器端的root用户登录到服务器。
AuthorizedKeysFile .ssh/authorized_keys
[root@LB-N2 ~]# /etc/init.d/sshd restart
Stopping sshd: [ OK ]
Starting sshd: [ OK ]
--------------------------------
#从另外一台主机远程登录该管理员账号:
[root@LB-N1 ~]# ssh root@172.16.1.9
root@172.16.1.9's password:'
Permission denied, please try again. #无法使用管理员用户进行远程登录。
第一步:切换到需要使用ssh远程登录的用户下,然后生成密钥对。
[root@LB-N1 ~]# su - cdmonkey
#我们使用本机的cdmonkey用户进行远程登录。注意:此时cdmonkey的主目录下是没有.ssh这个隐藏目录的。
#生成密钥对:
[cdmonkey@LB-N1 ~]$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/cdmonkey/.ssh/id_dsa):
#此时提示保存密钥文件的路径,直接输入回车采用默认路径即可,然后出现:
Created directory '/home/cdmonkey/.ssh'.
Enter passphrase (empty for no passphrase):
#此时提示键入密钥口令,如果担心私钥的安全,这里可以设置一个口令。如果直接回车为不建立密码短语(密码短语至少五个字符),然后出现:
Enter same passphrase again:
#此时提示再次输入密码短语,直接回车为不建立密码短语,然后出现:
Your identification has been saved in /home/cdmonkey/.ssh/id_dsa.
Your public key has been saved in /home/cdmonkey/.ssh/id_dsa.pub.
The key fingerprint is: #密钥指纹是:
6d:f7:a6:da:48:1e:ac:ca:ac:46:f1:cc:b3:98:08:67 cdmonkey@LB-N1
#密钥对(公钥和私钥)创建在此用户主目录下的.ssh子目录中。
The key's randomart image is:'
+--[ DSA 1024]----+
| |
| |
| |
| . . |
| = S o . |
|. E . = o . . |
| + o o o + o |
| . +o. + + o |
| ...+.. +.o |
+-----------------+ #至此,密钥对已经生成。
--------------------------------------------
[cdmonkey@LB-N1 ~]$ ll .ssh
total 8
-rw------- 1 cdmonkey cdmonkey 668 Dec 16 19:17 id_dsa
-rw-r--r-- 1 cdmonkey cdmonkey 604 Dec 16 19:17 id_dsa.pub
#生成了一对密钥。当前用户cdmonkey的主目录下的.ssh目录中并没有known_hosts文件,那是因为我们并未使用该用户远程登录过任何的主机。
通过上面的操作,“cdmonkey@LB-N1”用户的密钥对已经生成,接下来要做的工作就是将该用户的公钥上传到免密码登录的目标远程服务器端。计划使用目标远程服务器端的哪个用户身份进行登录,就要将公钥信息存放到该用户的主目录中的authorized_keys
文件中。可以使用ssh-copy-id
指令来自动完成这项任务。
第二步:发送公钥到远程主机。
#将公钥安全的推送到远程主机上的相应用户的主目录下:
[cdmonkey@LB-N1 ~]$ ssh-copy-id -i .ssh/id_dsa.pub root@172.16.1.9
The authenticity of host '172.16.1.9 (172.16.1.9)' can't be established.'
RSA key fingerprint is 1a:ee:21:52:76:c3:23:9e:99:0e:27:17:7e:c1:1a:8d.
Are you sure you want to continue connecting (yes/no)? yes
#这段话的意思是,无法确认主机的真实性,只知道它的公钥指纹,问你还想继续连接吗?
#上面的那一串用冒号分隔的一长串字符串就是指纹信息。
#还有一个问题就是,用户怎么知道远程主机的公钥指纹应该是多少?答案是没有好办法,远程主机必须在自己的网站上贴出公钥指纹,以便用户自行核对。上面这个提示信息只有第一次登录时会出现。
Warning: Permanently added '172.16.1.9' (RSA) to the list of known hosts.
#警告信息:已将远程主机的公钥信息添加到已知主机列表中。
#当远程主机的公钥被接受以后,它就会被保存在文件known_hosts之中。下次再连接这台主机时系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。
root@172.16.1.9's password:'
Now try logging into the machine, with "ssh 'root@172.16.1.9'", and check in:
.ssh/authorized_keys
to make sure we haven't added extra keys that you weren't expecting.
所谓公钥指纹,是指公钥长度较长(这里采用RSA算法,长达1024位),很难比对,所以对其进行MD5计算,将它变成一个128位的指纹,再进行比较,就容易多了。
此时,服务器端的root用户的主目录下的.ssh
目录中将会生成authorized_keys
文件:
[root@LB-N2 ~]# ll .ssh
total 4
-rw------- 1 root root 604 Dec 16 19:53 authorized_keys
[root@LB-N2 ~]# cat .ssh/authorized_keys
ssh-dss AAAAB3NzaC1kc3MAAAC...UphByHq7VGW9YS/qTLzY228JyRL2pO96GlUWInDjcWdY74RVBZln44FCOh8NIDEcT5hU1BIrqmjLJxWb8DjXVJGmhgZ/Ouh3Opge16ThUe8E6H4RLn7yNID24ownKQ== cdmonkey@LB-N1
#这里省略了N多密钥信息字符串。
#该文件的内容就是刚刚远程客户端的cdmonkey用户传过来的公钥文件,该文件与其本地的公钥文件完全相同。
SSH默认存放公钥信息的文件就是对应用户主目录下的
.ssh/authorized_keys
文件,而且这个文件中可以存放来自多个客户端的公钥文件。
远程登录目标主机会将客户端『特定用户』的公钥,保存在对应的登录用户主目录内的~/.ssh/authorized_keys
认证文件中,而公钥就是一段字符串,只要把它追加在该文件的末尾就行了。
如果不使用上面的ssh-copy-id
命令,改用下面的命令也可以:
[cdmonkey@LB-N1 ~]$ scp -P 22 .ssh/id_rsa.pub root@172.16.1.9 :~/.ssh/authorized_keys
#如果远程主机的认证文件已经存在,也可以往里添加公钥:
[cdmonkey@LB-N1 ~]$ scp .ssh/id_dsa.pub root@172.16.1.9 :~
root@172.16.1.9's password:'
id_dsa.pub 100% 603 0.6KB/s 00:00
#远程登录到远程主机,将公钥追加到认证文件中:
[root@LB-N2 ~]# cat id_dsa.pub >> .ssh/authorized_keys
如图所示,免密码登陆的过程对于服务器及客户端双方来讲,实际上就是个交换公钥的过程。而交换公钥的目的就是要建立一种“互信”的关系。
[cdmonkey@LB-N1 ~]$ ssh root@172.16.1.9
Last login: Tue Dec 16 19:04:26 2014 from 172.16.1.8
[root@LB-N2 ~]# #免密码登陆成功。
---------------
[root@LB-N1 ~]# ssh root@172.16.1.9
root@172.16.1.9's password:'
#使用客户端的管理员用户通过服务器端的管理员用户进行远程连接仍然需要输入密码。
Last login: Tue Dec 16 20:40:28 2014 from 172.16.1.8
无密码登录的注意事项:
从这个文件的名称上即可看出,这个文件存储的是这个用户已知(已经信任)的主机。SSH会把每个你访问过计算机的公钥信息都记录在~/.ssh/known_hosts
文件中。当下次访问相同计算机时,SSH会核对公钥。如果公钥不同,SSH会发出警告,避免你受到DNS Hijack之类的攻击。
每个SSH用户都有自己的known_hosts文件。
[cdmonkey@LB-N1 ~]$ ll -d .ssh;ll .ssh
drwx------ 2 cdmonkey cdmonkey 4096 Dec 16 19:50 .ssh
#这个目录的权限必须是700,并且用户的主目录也不能给其他用户写权限,否则SSH服务器会拒绝登陆。
total 12
-rw------- 1 cdmonkey cdmonkey 668 Dec 16 19:17 id_dsa
-rw-r--r-- 1 cdmonkey cdmonkey 604 Dec 16 19:17 id_dsa.pub
-rw-r--r-- 1 cdmonkey cdmonkey 392 Dec 16 19:50 known_hosts
[cdmonkey@LB-N1 ~]$ cat .ssh/known_hosts
#下面显示的是保存的服务器端的公钥信息,其内容与服务器端的公钥文件内容相同(见示意图)。
172.16.1.9 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAziSzjvQ1hDJEWFFxOf2BpFMNmTDr9HTC...
通过上面的内容,我们可以看到,known_hosts
属于客户端所持有的文件,记录了所有远程登录过(已知的、信任的)的主机的公钥信息。相对应的,authorized_keys
是服务器端所持有的文件,记录了远程登录用户的公钥信息。
批量分发的基础是免密码登录,换句话说,操作批量分发的用户是一个受信的中心用户。
[oldboy@Server-A ~]$ ssh-keygen -t dsa #生成密钥对
[oldboy@Server-A ~]$ ssh-copy-id -i .ssh/id_dsa.pub oldboy@10.0.0.104
[oldboy@Server-A ~]$ ssh-copy-id -i .ssh/id_dsa.pub oldboy@10.0.0.123
-----------------------
#测试无密码登录。测试成功,分发服务器A可以免密码远程登录节点服务器B和C:
[oldboy@Server-A ~]$ ssh oldboy@10.0.0.104
Last login: Tue Sep 9 10:00:13 2014 from 10.0.0.108
[oldboy@Server-A ~]$ ssh oldboy@10.0.0.123
[oldboy@Server-C ~]$
当我们要面对多台服务器时,这样的公钥分发是低效的,解决办法是:
通过脚本将本机的公钥分发到多台节点服务器中。
或者是将本机的公钥放在一个公共目录中,然后有需要的节点服务器下载该公钥。
注意事项:
如果节点服务器的SSH服务端口号不是默认的22端口(例如出于安全因素,改成52113),则使用ssh-copy-id
指令时要使用-p
参数后面接指定的端口号,但是书写格式有些特殊:
[oldboy@Server-A ~]$ ssh-copy-id -i .ssh/id_dsa.pub "-p 52113 oldboy@10.0.0.104"
#注意书写格式。双引号内的内容被视为一个参数。
#还有一个办法,就是直接修改指令脚本程序的代码(第41行,只有管理员用户才能修改):
[root@Server-A ~]# vi /usr/bin/ssh-copy-id
...| ssh -p52113 $1 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ..."
另外一点就是ssh-copy-id
指令的工作原理:其实就是把客户端(此场景中为分发服务器)的对应用户主目录下的公钥文件(id_dsa.pub
)复制到登录端服务器指定用户的主目录下的.ssh
子目录,并改名为authorized_keys
。当然,这个文件可以存储许多公钥信息,每存储一个公钥,就会将新的公钥信息追加到authorized_keys
中,且文件权限为600。
之所以是
authorized_keys
这个文件名,完全是由于配置文件/etc/ssh/sshd_config
中默认就是这个文件名,当然,也可以改成其他的文件名,但最好是别改^_^。)
小试牛刀,我们先用最普通的方法来分发文件:
[oldboy@Server-A ~]$ touch today.txt
[oldboy@Server-A ~]$ scp -P52113 today.txt oldboy@10.0.0.104:~
today.txt 100% 0 0.0KB/s 00:00
#注意,此时的节点服务器B和C的SSH端口号已改为52113,如果仍旧是默认端口,则无需使用-P选项。
[oldboy@Server-A ~]$ scp -P52113 today.txt oldboy@10.0.0.123:~
ssh: connect to host 10.0.0.123 port 52113: No route to host
lost connection
#无法发送文件到服务器C上,看到route字样应该能想到是防火墙的问题,那就将其关闭。
[oldboy@Server-A ~]$ scp -P52113 today.txt oldboy@10.0.0.123:~
today.txt 100% 0 0.0KB/s 00:00
如果分发过程有明显的延迟,请参见老师的博客:http://oldboy.blog.51cto.com/2561410/1300964
上面的分发方法过于简陋,一旦节点主机数量增大,再使用这样逐一进行的分发方法的话,那结局就是活活累死。所以当我们面对为数众多的节点主机时,就只能依靠脚本了:
[oldboy@Server-A ~]$ vim fenfa.sh
#!/bin/sh
for n in 104 123
do
scp -P52113 $1 oldboy@10.0.0.$n:~
done
#脚本编写结束。很简单,很微不足道,但毕竟向前迈进了一步。脚本还需要很多的改进,这里不再详述。
----------------------
#成功分发文件:
[oldboy@Server-A ~]$ /bin/sh fenfa.sh /etc/hosts
hosts 100% 182 0.2KB/s 00:00
hosts 100% 182 0.2KB/s 00:00
这种生产场景与批量分发的模式相反,这时的中心服务器不再是分发服务器,而是备份服务器,接收来自其他节点服务器推送过来的备份数据。此场景中的节点服务器需要免密码通过SSH远程连接到备份服务器。常规方法是节点服务器生成密钥对,并将其公钥发送到备份服务器端。当然,还有更简洁的方法,那就是将备份服务器上的私钥发送至各个节点服务器,而将公钥回传给自己。这样就可以仅仅使用一对密钥对来完成免密码登录。
对于这种方法,我个人持保留意见,因为原则上每台主机的私钥是绝不可公开分发的,上述的方法只是为了简便易行,但安全性有待商榷。
#将自己的公钥回传给自己:
[oldboy@Server-A ~]$ ssh-copy-id -i .ssh/id_dsa.pub oldboy@10.0.0.108
#将私钥分发给节点服务器:
[oldboy@Server-A ~]$ scp -p .ssh/id_dsa 10.0.0.104:~/.ssh
[oldboy@Server-A ~]$ scp -p .ssh/id_dsa 10.0.0.123:~/.ssh
说明:该题方案在生产环境中用途为数据备份等,由于这个方案有更好的替代方案,例如:Rsync统一备份方案,因此不推荐使用这个方案,这里仅仅学习下思路和部署而已。当批量分发或批量汇集过程中面对数百台服务器,如果要实现快速部署免密码登录,常用的方法有:scp+expect
,或者通过HTTP方式公布公钥的下载地址,其他节点再进行下载。
SSH服务的日志文件
SSH服务对密钥的权限、~/.ssh
目录的权限甚至是用户主目录的权限都有着严格的规定,一旦权限没有设置正确,sshd服务将无法使用相关文件,SSH也就会出现问题。如果authorized_keys
文件、~/.ssh
目录或用户主目录让本用户之外的用户有写权限,那么sshd都会拒绝使用~/.ssh/authorized_keys
文件中的公钥来进行认证。
[root@hadoop21 ~]# vim /etc/ssh/sshd_config
ClientAliveInterval 0
ClientAliveCountMax 3
#以上两个配置项决定了超时时间:
#守护进程每隔一段时间(第一个项目设置的时间间隔秒数)就会自动发送一个信号给客户端,并等待客户端的回应。如果客户端没有回应,会记录下来直到记录数超过某个值(第二个项目设置的最大次数)时,才会断开连接。
“ClientAliveInterval指定了服务器端向客户端请求消息的时间间隔, 默认是0, 不发送.而ClientAliveInterval 60表示每分钟发送一次, 然后客户端响应, 这样就保持长连接了.这里比较怪的地方是:不是客户端主动发起保持连接的请求(如FTerm, CTerm等),而是需要服务器先主动。
另外,至于ClientAliveCountMax, 使用默认值3即可.ClientAliveCountMax表示服务器发出请求后客户端没有响应的次数达到一定值, 就自动断开。正常情况下, 客户端不会不响应。“