基础服务类系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.htmlhtml
本文对SSH链接验证机制进行了很是详细的分析,还详细介绍了ssh客户端工具的各类功能,相信能让各位对ssh有个全方位较透彻的了解,而不是仅仅只会用它来链接远程主机。linux
另外,本人翻译了ssh客户端命令的man文档,如本文有不理解的地方,能够参考man文档手册:ssh中文手册。算法
SSH系列文章: shell
SSH基础:SSH和SSH服务windows
对称加密:加密和解密使用同样的算法,只要解密时提供与加密时一致的密码就能够完成解密。例如QQ登陆密码,银行卡密码,只要保证密码正确就能够。bash
非对称加密:经过公钥(public key)和私钥(private key)来加密、解密。公钥加密的内容可使用私钥解密,私钥加密的内容可使用公钥解密。通常使用公钥加密,私钥解密,但并不是绝对如此,例如CA签署证书时就是使用本身的私钥加密。在接下来介绍的SSH服务中,虽然一直建议分发公钥,但也能够分发私钥。服务器
因此,若是A生成了(私钥A,公钥A),B生成了(私钥B,公钥B),那么A和B之间的非对称加密会话情形包括:网络
(1).A将本身的公钥A分发给B,B拿着公钥A将数据进行加密,并将加密的数据发送给A,A将使用本身的私钥A解密数据。
(2).A将本身的公钥A分发给B,并使用本身的私钥A加密数据,而后B使用公钥A解密数据。
(3).B将本身的公钥B分发给A,A拿着公钥B将数据进行加密,并将加密的数据发送给B,B将使用本身的私钥B解密数据。
(4).B将本身的公钥B分发给A,并使用本身的私钥B加密数据,而后A使用公钥B解密数据。
虽然理论上支持4种情形,但在SSH的身份验证阶段,SSH只支持服务端保留公钥,客户端保留私钥的方式,因此方式只有两种:客户端生成密钥对,将公钥分发给服务端;服务端生成密钥对,将私钥分发给客户端。只不过出于安全性和便利性,通常都是客户端生成密钥对并分发公钥。后文将给出这两种分发方式的示例。
(1).SSH是传输层和应用层上的安全协议,它只能经过加密链接双方会话的方式来保证链接的安全性。当使用ssh链接成功后,将创建客户端和服务端之间的会话,该会话是被加密的,以后客户端和服务端的通讯都将经过会话传输。
(2).SSH服务的守护进程为sshd,默认监听在22端口上。
(3).全部ssh客户端工具,包括ssh命令,scp,sftp,ssh-copy-id等命令都是借助于ssh链接来完成任务的。也就是说它们都链接服务端的22端口,只不过链接上以后将待执行的相关命令转换传送到远程主机上,由远程主机执行。
(4).ssh客户端命令(ssh、scp、sftp等)读取两个配置文件:全局配置文件/etc/ssh/ssh_config和用户配置文件~/.ssh/config。实际上命令行上也能够传递配置选项。它们生效的优先级是:命令行配置选项 > ~/.ssh/config > /etc/ssh/ssh_config。
(5).ssh涉及到两个验证:主机验证和用户身份验证。经过主机验证,再经过该主机上的用户验证,就能惟一肯定该用户的身份。一个主机上能够有不少用户,因此每台主机的验证只需一次,但主机上每一个用户都须要单独进行用户验证。
(6).ssh支持多种身份验证,最经常使用的是密码验证机制和公钥认证机制,其中公钥认证机制在某些场景实现双机互信时几乎是必须的。虽然经常使用上述两种认证机制,但认证时的顺序默认是gssapi-with-mic,hostbased,publickey,keyboard-interactive,password。注意其中的主机认证机制hostbased不是主机验证,因为主机认证用的很是少(它所读取的认证文件为/etc/hosts.equiv或/etc/shosts.equiv),因此网络上比较少见到它的相关介绍。总的来讲,经过在ssh配置文件(注意不是sshd配置文件)中使用指令PreferredAuthentications改变认证顺序不失为一种验证的效率提高方式。
(7).ssh客户端其实有很多很强大的功能,如端口转发(隧道模式)、代理认证、链接共享(链接复用)等。
(8).ssh服务端配置文件为/etc/ssh/sshd_config,注意和客户端的全局配置文件/etc/ssh/ssh_config区分开来。
(9).很重要却几乎被人忽略的一点,ssh登陆时会请求分配一个伪终端。但有些身份认证程序如sudo能够禁止这种类型的终端分配,致使ssh链接失败。例如使用ssh执行sudo命令时sudo就会验证是否要分配终端给ssh。
假如从客户端A(172.16.10.5)链接到服务端B(172.16.10.6)上,将包括主机验证和用户身份验证两个过程,以RSA非对称加密算法为例。
[root@xuexi ~]# ssh 172.16.10.6
服务端B上首先启动了sshd服务程序,即开启了ssh服务,打开了22端口(默认)。
当客户端A要链接B时,首先将进行主机验证过程,即判断主机B是不是否曾经链接过。
判断的方法是读取~/.ssh/known_hosts文件和/etc/ssh/known_hosts文件,搜索是否有172.16.10.6的主机信息(主机信息称为host key,表示主机身份标识)。若是没有搜索到对应该地址的host key,则询问是否保存主机B发送过来的host key,若是搜索到了该地址的host key,则将此host key和主机B发送过来的host key作比对,若是彻底相同,则表示主机A曾经保存过主机B的host key,无需再保存,直接进入下一个过程——身份验证,若是不彻底相同,则提示是否保存主机B当前使用的host key。
询问是否保存host key的过程以下所示:
[root@xuexi ~]# ssh 172.16.10.6 The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established. RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf. Are you sure you want to continue connecting (yes/no)? yes
或者windows端使用图形界面ssh客户端工具时:
在说明身份验证过程前,先看下known_hosts文件的格式。以~/.ssh/known_hosts为例。
[root@xuexi ~]# cat ~/.ssh/known_hosts 172.16.10.6 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC675dv1w+GDYViXxqlTspUHsQjargFPSnR9nEqCyUgm5/32jXAA3XTJ4LUGcDHBuQ3p3spW/eO5hAP9eeTv5HQzTSlykwsu9He9w3ee+TV0JjBFulfBR0weLE4ut0PurPMbthE7jIn7FVDoLqc6o64WvN8LXssPDr8WcwvARmwE7pYudmhnBIMPV/q8iLMKfquREbhdtGLzJRL9DrnO9NNKB/EeEC56GY2t76p9ThOB6ES6e/87co2HjswLGTWmPpiqY8K/LA0LbVvqRrQ05+vNoNIdEfk4MXRn/IhwAh6j46oGelMxeTaXYC+r2kVELV0EvYV/wMa8QHbFPSM6nLz
该文件中,每行一个host key,行首是主机名,它是搜索host key时的索引,主机名后的内容便是host key部分。以此文件为例,它表示客户端A曾经试图链接过172.16.10.6这个主机B,并保存了主机B的host key,下次链接主机B时,将搜索主机B的host key,并与172.16.10.6传送过来的host key作比较,若是能匹配上,则表示该host key确实是172.16.10.6当前使用的host key,若是不能匹配上,则表示172.16.10.6修改过host key,或者此文件中的host key被修改过。
那么主机B当前使用的host key保存在哪呢?在/etc/ssh/ssh_host*文件中,这些文件是服务端(此处即主机B)的sshd服务程序启动时重建的。以rsa算法为例,则保存在/etc/ssh/ssh_host_rsa_key和/etc/ssh/ssh_host_rsa_key.pub中,其中公钥文件/etc/ssh/ssh_host_rsa_key.pub中保存的就是host key。
[root@xuexi ~]# cat /etc/ssh/ssh_host_rsa_key.pub # 在主机B上查看 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC675dv1w+GDYViXxqlTspUHsQjargFPSnR9nEqCyUgm5/32jXAA3XTJ4LUGcDHBuQ3p3spW/eO5hAP9eeTv5HQzTSlykwsu9He9w3ee+TV0JjBFulfBR0weLE4ut0PurPMbthE7jIn7FVDoLqc6o64WvN8LXssPDr8WcwvARmwE7pYudmhnBIMPV/q8iLMKfquREbhdtGLzJRL9DrnO9NNKB/EeEC56GY2t76p9ThOB6ES6e/87co2HjswLGTWmPpiqY8K/LA0LbVvqRrQ05+vNoNIdEfk4MXRn/IhwAh6j46oGelMxeTaXYC+r2kVELV0EvYV/wMa8QHbFPSM6nLz
发现/etc/ssh/ssh_host_rsa_key.pub文件内容和~/.ssh/known_hosts中该主机的host key部分彻底一致,只不过~/.ssh/known_hosts中除了host key部分还多了一个主机名,这正是搜索主机时的索引。
综上所述,在主机验证阶段,服务端持有的是私钥,客户端保存的是来自于服务端的公钥。注意,这和身份验证阶段密钥的持有方是相反的。
实际上,ssh并不是直接比对host key,由于host key太长了,比对效率较低。因此ssh将host key转换成host key指纹,而后比对两边的host key指纹便可。指纹格式以下:
[root@xuexi ~]# ssh 172.16.10.6 The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established. RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf. Are you sure you want to continue connecting (yes/no)? yes
host key的指纹可由ssh-kegen计算得出。例如,下面分别是主机A(172.16.10.5)保存的host key指纹,和主机B(172.16.10.6)当前使用的host key的指纹。可见它们是彻底同样的。
[root@xuexi ~]# ssh-keygen -l -f ~/.ssh/known_hosts 2048 f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf 172.16.10.6 (RSA) [root@xuexi ~]# ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key 2048 f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf (RSA)
其实ssh还支持host key模糊比较,即将host key转换为图形化的指纹。这样,图形结果相差大的很容易就比较出来。之因此说是模糊比较,是由于对于很是近似的图形化指纹,ssh可能会误判。图形化指纹的生成方式以下:只需在上述命令上加一个"-v"选项进入详细模式便可。
[root@xuexi ~]# ssh-keygen -lv -f ~/.ssh/known_hosts 2048 f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf 172.16.10.6 (RSA) +--[ RSA 2048]----+ | | | | | . | | o | | S. . + | | . +++ + . | | B.+.= . | | + B. +. | | o.+. oE | +-----------------+
更详细的主机认证过程是:先进行密钥交换(DH算法)生成session key(rfc文档中称之为shared secret),而后从文件中读取host key,并用host key对session key进行签名,而后对签名后的指纹进行判断。(In SSH, the key exchange is signed with the host key to provide host authentication.来源:https://tools.ietf.org/html/rfc4419)
过程以下图:
主机验证经过后,将进入身份验证阶段。SSH支持多种身份验证机制,它们的验证顺序以下:gssapi-with-mic,hostbased,publickey,keyboard-interactive,password,但常见的是密码认证机制(password)和公钥认证机制(public key)。当公钥认证机制未经过时,再进行密码认证机制的验证。这些认证顺序能够经过ssh配置文件(注意,不是sshd的配置文件)中的指令PreferredAuthentications改变。
若是使用公钥认证机制,客户端A须要将本身生成的公钥(~/.ssh/id_rsa.pub)发送到服务端B的~/.ssh/authorized_keys文件中。当进行公钥认证时,客户端将告诉服务端要使用哪一个密钥对,并告诉服务端它已经访问过密钥对的私钥部分~/.ssh/id_rsa(客户端从本身的私钥中推导,或者从私钥同目录下读取公钥,计算公钥指纹后发送给服务端。因此有些版本的ssh不要求存在公钥文件,有些版本的ssh则要求私钥和公钥同时存在且在同目录下),而后服务端将检测密钥对的公钥部分,判断该客户端是否容许经过认证。若是认证不经过,则进入下一个认证机制,以密码认证机制为例。
当使用密码认证时,将提示输入要链接的远程用户的密码,输入正确则验证经过。
当主机验证和身份验证都经过后,分两种状况:直接登陆或执行ssh命令行中给定某个命令。如:
[root@xuexi ~]# ssh 172.16.10.6 [root@xuexi ~]# ssh 172.16.10.6 'echo "haha"'
(1).前者ssh命令行不带任何命令参数,表示使用远程主机上的某个用户(此处为root用户)登陆到远程主机172.16.10.6上,因此远程主机会为ssh分配一个伪终端,并进入bash环境。
(2).后者ssh命令行带有命令参数,表示在远程主机上执行给定的命令【echo "haha"】。ssh命令行上的远程命令是经过fork ssh-agent获得的子进程来执行的,当命令执行完毕,子进程消逝,ssh也将退出,创建的会话和链接也都将关闭。(之因此要在这里明确说明远程命令的执行过程,是为了说明后文将介绍的ssh实现端口转发时的注意事项)
实际上,在ssh链接成功,登陆或执行命令行中命令以前,能够指定要在远程执行的命令,这些命令放在~/.ssh/rc或/etc/ssh/rc文件中,也就是说,ssh链接创建以后作的第一件事是在远程主机上执行这两个文件中的命令。
以主机A链接主机B为例,主机A为SSH客户端,主机B为SSH服务端。
在服务端即主机B上:
在客户端即主机A上:
分为服务端配置文件/etc/ssh/sshd_config和客户端配置文件/etc/ssh/ssh_config(全局)或~/.ssh/config(用户)。
虽然服务端和客户端配置文件默认已配置项虽然很是少很是简单,但它们可配置项很是多。sshd_config完整配置项参见金步国翻译的sshd_config中文手册,ssh_config也能够参考sshd_config的配置,它们大部分配置项所描述的内容是相同的。
简单介绍下该文件中比较常见的指令。
[root@xuexi ~]# cat /etc/ssh/sshd_config
#Port 22 # 服务端SSH端口,能够指定多条表示监听在多个端口上 #ListenAddress 0.0.0.0 # 监听的IP地址。0.0.0.0表示监听全部IP Protocol 2 # 使用SSH 2版本 ##################################### # 私钥保存位置 # ##################################### # HostKey for protocol version 1 #HostKey /etc/ssh/ssh_host_key # SSH 1保存位置/etc/ssh/ssh_host_key # HostKeys for protocol version 2 #HostKey /etc/ssh/ssh_host_rsa_key # SSH 2保存RSA位置/etc/ssh/ssh_host_rsa _key #HostKey /etc/ssh/ssh_host_dsa_key # SSH 2保存DSA位置/etc/ssh/ssh_host_dsa _key ################################### # 杂项配置 # ################################### #PidFile /var/run/sshd.pid # 服务程序sshd的PID的文件路径 #ServerKeyBits 1024 # 服务器生成的密钥长度 #SyslogFacility AUTH # 使用哪一个syslog设施记录ssh日志。日志路径默认为/var/log/secure #LogLevel INFO # 记录SSH的日志级别为INFO ################################### # 如下项影响认证速度 # ################################### #UseDNS yes # 指定是否将客户端主机名解析为IP,以检查此主机名是否与其IP地址真实对应。默认yes。 # 由此可知该项影响的是主机验证阶段。建议在未配置DNS解析时,将其设置为no,不然主机验证阶段会很慢 ################################### # 如下是和安全有关的配置 # ################################### #PermitRootLogin yes # 是否容许root用户登陆 #GSSAPIAuthentication no # 是否开启GSSAPI身份认证机制,默认为yes #PubkeyAuthentication yes # 是否开启基于公钥认证机制 #AuthorizedKeysFile .ssh/authorized_keys # 基于公钥认证机制时,来自客户端的公钥的存放位置 PasswordAuthentication yes # 是否使用密码验证,若是使用密钥对验证能够关了它 #PermitEmptyPasswords no # 是否容许空密码,若是上面的那项是yes,这里最好设置no #MaxSessions 10 # 最大客户端链接数量 #LoginGraceTime 2m # 身份验证阶段的超时时间,若在此超时期间内未完成身份验证将自动断开 #MaxAuthTries 6 # 指定每一个链接最大容许的认证次数。默认值是6。 # 若是失败认证次数超过该值一半,将被强制断开,且生成额外日志消息。 MaxStartups 10 # 最大容许保持多少个未认证的链接。默认值10。 ################################### # 如下能够自行添加到配置文件 # ################################### DenyGroups hellogroup testgroup # 表示hellogroup和testgroup组中的成员不容许使用sshd服务,即拒绝这些用户链接 DenyUsers hello test # 表示用户hello和test不能使用sshd服务,即拒绝这些用户链接 ################################### # 如下一项和远程端口转发有关 # ################################### #GatewayPorts no # 设置为yes表示sshd容许被远程主机所设置的本地转发端口绑定在非环回地址上 # 默认值为no,表示远程主机设置的本地转发端口只能绑定在环回地址上,见后文"远程端口转发"
通常来讲,如非有特殊需求,只需修改下监听端口和UseDNS为no以加快主机验证阶段的速度便可。
配置好后直接重启启动sshd服务便可。
须要说明的是,客户端配置文件有不少配置项和服务端配置项名称相同,但它们一个是在链接时采起的配置(客户端配置文件),一个是sshd启动时开关性的设置(服务端配置文件)。例如,两配置文件都有GSSAPIAuthentication项,在客户端将其设置为no,表示链接时将直接跳过该身份验证机制,而在服务端设置为no则表示sshd启动时不开启GSSAPI身份验证的机制。即便客户端使用了GSSAPI认证机制,只要服务端没有开启,就绝对不可能认证经过。
下面也简单介绍该文件。
# Host * # Host指令是ssh_config中最重要的指令,只有ssh链接的目标主机名能匹配此处给定模式时, # 下面一系列配置项直到出现下一个Host指令才对这次链接生效 # ForwardAgent no # ForwardX11 no # RhostsRSAAuthentication no # RSAAuthentication yes # PasswordAuthentication yes # 是否启用基于密码的身份认证机制 # HostbasedAuthentication no # 是否启用基于主机的身份认证机制 # GSSAPIAuthentication no # 是否启用基于GSSAPI的身份认证机制 # GSSAPIDelegateCredentials no # GSSAPIKeyExchange no # GSSAPITrustDNS no # BatchMode no # 若是设置为"yes",将禁止passphrase/password询问。比较适用于在那些不须要询问提供密 # 码的脚本或批处理任务任务中。默认为"no"。 # CheckHostIP yes # AddressFamily any # ConnectTimeout 0 # StrictHostKeyChecking ask # 设置为"yes",ssh将从不自动添加host key到~/.ssh/known_hosts文件, # 且拒绝链接那些未知的主机(即未保存host key的主机或host key已改变的主机)。 # 它将强制用户手动添加host key到~/.ssh/known_hosts中。 # 设置为ask将询问是否保存到~/.ssh/known_hosts文件。 # 设置为no将自动添加到~/.ssh/known_hosts文件。 # IdentityFile ~/.ssh/identity # ssh v1版使用的私钥文件 # IdentityFile ~/.ssh/id_rsa # ssh v2使用的rsa算法的私钥文件 # IdentityFile ~/.ssh/id_dsa # ssh v2使用的dsa算法的私钥文件 # Port 22 # 当命令行中不指定端口时,默认链接的远程主机上的端口 # Protocol 2,1 # Cipher 3des # 指定ssh v1版本中加密会话时使用的加密协议 # Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc # 指定ssh v1版本中加密会话时使用的加密协议 # MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160 # EscapeChar ~ # Tunnel no # TunnelDevice any:any # PermitLocalCommand no # 功能等价于~/.ssh/rc,表示是否容许ssh链接成功后在本地执行LocalCommand指令指定的命令。 # LocalCommand # 指定链接成功后要在本地执行的命令列表,当PermitLocalCommand设置为no时将自动忽略该配置 # %d表本地用户家目录,%h表示远程主机名,%l表示本地主机名,%n表示命令行上提供的主机名, # p%表示远程ssh端口,r%表示远程用户名,u%表示本地用户名。 # VisualHostKey no # 是否开启主机验证阶段时host key的图形化指纹 Host * GSSAPIAuthentication yes
如非有特殊需求,ssh客户端配置文件通常只需修改下GSSAPIAuthentication的值为no来改善下用户验证的速度便可,另外在有非交互需求时,将StrictHostKeyChecking设置为no以让主机自动添加host key。
此处先介绍ssh命令的部分功能,其余包括端口转发的在后文相关内容中解释,关于链接复用的选项本文不作解释。
语法:
ssh [options] [user@]hostname [command] 参数说明: -b bind_address :在本地主机上绑定用于ssh链接的地址,当系统有多个ip时才生效。 -E log_file :将debug日志写入到log_file中,而不是默认的标准错误输出stderr。 -F configfile :指定用户配置文件,默认为~/.ssh/config。 -f :请求ssh在工做在后台模式。该选项隐含了"-n"选项,因此标准输入将变为/dev/null。 -i identity_file:指定公钥认证时要读取的私钥文件。默认为~/.ssh/id_rsa。 -l login_name :指定登陆在远程机器上的用户名。也能够在全局配置文件中设置。 -N :显式指明ssh不执行远程命令。通常用于端口转发,见后文端口转发的示例分析。 -n :将/dev/null做为标准输入stdin,能够防止从标准输入中读取内容。ssh在后台运行时默认该项。 -p port :指定要链接远程主机上哪一个端口,也可在全局配置文件中指定默认的链接端口。 -q :静默模式。大多数警告信息将不输出。 -T :禁止为ssh分配伪终端。 -t :强制分配伪终端,重复使用该选项"-tt"将进一步强制。 -v :详细模式,将输出debug消息,可用于调试。"-vvv"可更详细。 -V :显示版本号并退出。 -o :指定额外选项,选项很是多。 user@hostname :指定ssh以远程主机hostname上的用户user链接到的远程主机上,若省略user部分,则表示使用本地当前用户。 :若是在hostname上不存在user用户,则链接将失败(将不断进行身份验证)。 command :要在远程主机上执行的命令。指定该参数时,ssh的行为将再也不是登陆,而是执行命令,命令执行完毕时ssh链接就关闭。
例如,以172.16.10.6主机上的longshuai用户登陆172.16.10.6。
[root@xuexi ~]# ssh longshuai@172.16.10.6 The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established. ECDSA key fingerprint is 18:d1:28:1b:99:3b:db:20:c7:68:0a:f8:9e:43:e8:b4. Are you sure you want to continue connecting (yes/no)? yes # 主机验证 Warning: Permanently added '172.16.10.6' (ECDSA) to the list of known hosts. longshuai@172.16.10.6's password: # 用户验证 Last login: Wed Jul 5 12:27:29 2017 from 172.16.10.6
此时已经登陆到了172.16.10.6主机上。
[longshuai@xuexi ~]$ hostname -I 172.16.10.6
要退出ssh登陆,使用logout命令或exit命令便可返回到原主机环境。
使用ssh还能够实现主机跳转,即跳板功能。例如主机B能和A、C通讯,但A、C之间不一样通讯,即A<-->B<-->C<-x->A的情形。若是要从A登录到C,则能够借助B这个跳板登陆到C。此处一个简单示例为:从172.16.10.5登陆到172.16.10.6,再以此为基础登陆到172.16.10.3上。
[root@xuexi ~]# ssh 172.16.10.6 The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established. RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '172.16.10.6' (RSA) to the list of known hosts. Last login: Wed Jul 5 12:36:51 2017 from 172.16.10.6
[root@xuexi ~]# ssh 172.16.10.3 The authenticity of host '172.16.10.3 (172.16.10.3)' can't be established. ECDSA key fingerprint is 18:d1:28:1b:99:3b:db:20:c7:68:0a:f8:9e:43:e8:b4. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '172.16.10.3' (ECDSA) to the list of known hosts. root@172.16.10.3's password: Last login: Thu Jun 29 12:38:56 2017 from 172.16.10.6
[root@xuexi ~]# hostname -I 172.16.10.3 172.16.10.4
一样,在退出时,也是一层一层退出的。
[root@xuexi ~]# exit logout Connection to 172.16.10.3 closed. [root@xuexi ~]# hostname -I 172.16.10.6 [root@xuexi ~]# exit logout Connection to 172.16.10.6 closed.
注意,因为在借助172.16.10.6当跳板链接到172.16.10.3,因此172.16.10.3的host key是添加到172.16.10.6上而非172.16.10.5上的。
若是在命令行给出了要执行的命令,默认ssh将工做在前台,若是同时给定了"-f"选项,则ssh工做在后台。但尽管是工做在后台,当远程执行的命令若是有消息返回时,将随时可能显示在本地。当远程命令执行完毕后,ssh链接也将当即关闭。
[root@xuexi ~]# ssh 172.16.10.6 'sleep 5' # 在前台睡眠5秒钟 [root@xuexi ~]# ssh 172.16.10.6 -f 'sleep 5;echo over' # 在后台睡眠5秒,睡眠完成后echo一段信息
因为第二条命令是放在后台执行的,因此该ssh一创建完成ssh会话就当即返回本地bash环境,但当5秒以后,将在本地忽然显示"over"。
ssh执行远程命令默认容许从标准输入中读取数据而后传输到远程。可使用"-n"选项,使得标准输入重定向为/dev/null。例如:
[root@xuexi ~]# echo haha | ssh 172.16.10.6 'cat' haha [root@xuexi ~]# ssh 172.16.10.6 'cat' </etc/fstab
再看以下两条命令:
[root@xuexi ~]# tar zc /tmp/* | ssh 172.16.10.6 'cd /tmp;tar xz' [root@xuexi ~]# ssh 172.16.10.6 'tar cz /tmp' | tar xz
第一条命令将/tmp下文件归档压缩,而后传送到远程主机上并被解包;第二条命令将远程主机上的/tmp目录归档压缩,并传输到本地解包。因此它们实现了拷贝的功能。
不妨再分析下面的命令,该命令改编自ssh-copy-id脚本中的主要命令。若是不知道ssh-copy-id命令是干什么的,后文有介绍。
[root@xuexi ~]# cat ~/.ssh/id_rsa.pub | ssh 172.16.10.6 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys"
该命令首先创建ssh链接,并在远程执行"umask 077"临时修改远程的umask值,使得远程建立的目录权限为700,而后判断远程主机上是否有~/.ssh目录,若是没有则建立,最后从标准输入中读取本地公钥文件~/.ssh/id_rsa.pub的内容并将其追加到~/.ssh/authorized_keys文件中。
若是将此命令改成以下命令,使用ssh的"-n"选项,并将追加剧定向改成覆盖重定向符号。
[root@xuexi ~]# cat ~/.ssh/id_rsa.pub | ssh -n 172.16.10.6 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat > ~/.ssh/authorized_keys"
该命令的结果是清空远程主机172.16.10.6上的~/.ssh/authorized_keys文件,由于ssh的"-n"选项强行改变了ssh读取的标准输入为/dev/null。
scp是基于ssh的远程拷贝命令,也支持本地拷贝,甚至支持远程到远程的拷贝。
scp因为基于ssh,因此其端口也是使用ssh的端口。其实,scp拷贝的实质是使用ssh链接到远程,并使用该链接来传输数据。下文有scp执行过程的分析。
另外,scp还很是不占资源,不会提升多少系统负荷,在这一点上,rsync远不及它。虽然 rsync比scp会快一点,但rsync是增量拷贝,要判断每一个文件是否修改过,在小文件众多的状况下,判断次数很是多,致使rsync效率较差,而scp基本不影响系统正常使用。
scp每次都是全量拷贝,在某些状况下,确定是不及rsync的。
scp [-12BCpqrv] [-l limit] [-o ssh_option] [-P port] [[user@]host1:]src_file ... [[user@]host2:]dest_file 选项说明: -1:使用ssh v1版本,这是默认使用协议版本 -2:使用ssh v2版本 -C:拷贝时先压缩,节省带宽 -l limit:限制拷贝速度,Kbit/s,1Byte=8bit,因此"-l 800"表示的速率是100K/S -o ssh_option:指定ssh链接时的特殊选项,通常用不上。 -P port:指定目标主机上ssh端口,大写的字母P,默认是22端口 -p:拷贝时保持源文件的mtime,atime,owner,group,privileges -r:递归拷贝,用于拷贝目录。注意,scp拷贝遇到连接文件时,会拷贝连接的源文件内容填充到目标文件中(scp的本质就是填充而非拷贝) -v:输出详细信息,能够用来调试或查看scp的详细过程,分析scp的机制
src_file是源位置,dest_file是目标位置,即将src_file复制到dest_file,其中src_file能够指定多个。因为源位置和目标位置均可以使用本地路径和远程路径,因此scp能实现本地拷贝到远程、本地拷贝到本地、远程拷贝到本地、远程拷贝到另外一个远程。其中远程路径的指定格式为"user@hostname:/path",能够省略user,也能够省略":/path",省略":/path"时表示拷贝到目的用户的家目录下。
注意:scp拷贝是强制覆盖型拷贝,当有重名文件时,不会进行任何询问。
例如:
(1).本地拷贝到本地:/etc/fstab-->/tmp/a.txt。
[root@xuexi ~]# scp /etc/fstab /tmp/a.txt
(2).本地到远程:/etc/fstab-->172.16.10.6:/tmp/a.txt。
[root@xuexi ~]# scp /etc/fstab 172.16.10.6:/tmp fstab 100% 805 0.8KB/s 00:00
(3).远程到本地:172.16.10.6:/etc/fstab-->/tmp/a.txt。
[root@xuexi ~]# scp 172.16.10.6:/etc/fstab /tmp/a.txt fstab 100% 501 0.5KB/s 00:00
(4).远程路径1到远程路径2:172.16.10.6:/etc/fstab-->/172.16.10.3:/tmp/a.txt。
[root@xuexi ~]# scp 172.16.10.6:/etc/fstab 172.16.10.3:/tmp/a.txt fstab 100% 501 0.5KB/s 00:00 Connection to 172.16.10.6 closed.
远程复制到远程时,可能会由于sudoers的限制不给ssh分配终端,致使没法输入yes/密码或者直接拒绝等而失败。这时能够加上一层ssh:
ssh -tt 172.16.10.2 "scp 172.16.10.6:/etc/fstab 172.16.10.3:/tmp/a.txt"
其中172.16.10.2是执行ssh命令所在的主机A,也就是在A主机上使用scp将主机B(10.6)上的文件复制到主机C(10.3)上,这里的"-tt"表示强制分配伪终端给ssh。
另外,还可使用scp的"-3"选项,改变scp远程到远程的默认传输模式(默认传输模式见下面的机制分析),若是上面的命令失败的话,也可使用该选项一试。
scp -3 172.16.10.6:/etc/fstab 172.16.10.3:/tmp/a.txt
scp的拷贝实质是创建ssh链接,而后经过此链接来传输数据。若是是远程1拷贝到远程2,则是将scp命令转换后发送到远程1上执行,在远程1上创建和远程2的ssh链接,并经过此链接来传输数据。
在远程复制到远程的过程当中,例如在本地(172.16.10.5)执行scp命令将A主机(172.16.10.6)上的/tmp/copy.txt复制到B主机(172.16.10.3)上的/tmp目录下,若是使用-v选项查看调试信息的话,会发现它的步骤相似是这样的。
# 如下是从结果中提取的过程 # 首先输出本地要执行的命令 Executing: /usr/bin/ssh -v -x -oClearAllForwardings yes -t -l root 172.16.10.6 scp -v /tmp/copy.txt root@172.16.10.3:/tmp # 从本地链接到A主机 debug1: Connecting to 172.16.10.6 [172.16.10.6] port 22. debug1: Connection established. # 要求验证本地和A主机之间的链接 debug1: Next authentication method: password root@172.16.10.6's password: # 将scp命令行修改后发送到A主机上 debug1: Sending command: scp -v /tmp/copy.txt root@172.16.10.3:/tmp # 在A主机上执行scp命令 Executing: program /usr/bin/ssh host 172.16.10.3, user root, command scp -v -t /tmp # 验证A主机和B主机之间的链接 debug1: Next authentication method: password root@172.16.10.3's password: # 从A主机上拷贝源文件到最终的B主机上 debug1: Sending command: scp -v -t /tmp Sending file modes: C0770 24 copy.txt Sink: C0770 24 copy.txt copy.txt 100% 24 0.0KB/s # 关闭本地主机和A主机的链接 Connection to 172.16.10.6 closed.
也就是说,远程主机A到远程主机B的复制,其实是将scp命令行从本地传输到主机A上,由A本身去执行scp命令。也就是说,本地主机不会和主机B有任何交互行为,本地主机就像是一个代理执行者同样,只是帮助传送scp命令行以及帮助显示信息。
其实从本地主机和主机A上的~/.ssh/know_hosts文件中能够看出,本地主机只是添加了主机A的host key,并无添加主机B的host key,而在主机A上则添加了主机B的host key。
在身份验证阶段,因为默认状况下基于公钥认证的机制顺序优先于基于密码认证的机制,因此基于公钥认证身份,就能够免输入密码,即实现双机互信(实际上只是单方向的信任)。
基于公钥认证机制的认证过程在前文已经详细说明过了,如还不清楚,请跳回上文。
如下是实现基于公钥认证的实现步骤:
(1).在客户端使用ssh-keygen生成密钥对,存放路径按照配置文件的指示,默认是在~/.ssh/目录下。
[root@xuexi ~]# ssh-keygen -t rsa # -t参数指定算法,能够是rsa或dsa Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): # 询问私钥保存路径 Enter passphrase (empty for no passphrase): # 询问是否加密私钥文件 Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub.
若是不想被询问,则可使用下面一条命令完成:"-f"指定私钥文件,"-P"指定passphrase,或者"-N"也同样。
[root@xuexi ~]# ssh-keygen -t rsa -f ~/.ssh/id_rsa -P '' # 指定加密私钥文件的密码为空密码,即不加密 [root@xuexi ~]# ssh-keygen -t rsa -f ~/.ssh/id_rsa -N '' # 同上
查看~/.ssh/目录下私钥的权限。私钥文件有严格的权限要求,当私钥文件的非全部者有可读权限时,将直接忽略该私钥文件致使公钥认证失败。
[root@xuexi ~]# ls -l ~/.ssh total 12 -rw------- 1 root root 1671 Jun 29 00:18 id_rsa # 私钥权限必须600,属主为本身 -rw-r--r-- 1 root root 406 Jun 29 00:18 id_rsa.pub -rw-r--r-- 1 root root 393 Jun 29 05:56 known_hosts
(2).将上面生成的公钥使用ssh-copy-id分发(即复制)到远程待信任主机上。
ssh-copy-id用法很简单,只需指定待信任主机及目标用户便可。若是生成的公钥文件,路径不是~/.ssh/id_rsa.pub,则使用"-i"选项指定要分发的公钥。
ssh-copy-id [-i [identity_file]] [user@]machine
例如,将公钥分发到172.16.10.6上的root用户家目录下:
[root@xuexi ~]# ssh-copy-id 172.16.10.6
ssh-copy-id惟一须要注意的是,若是ssh服务端的端口不是22,则须要给ssh-copy-id传递端口号,传递方式为 "-p port_num [user@]hostname" (注意加上双引号),例如"-p 22222 root@172.16.10.6"。之因此要如此传递,见下面摘自ssh-copy-id中公钥分发的命令部分。
{ eval "$GET_ID" ; } | ssh $1 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys && (test -x /sbin/restorecon && /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1 || true)" || exit 1
其中"{ eval "$GET_ID" ; }"可理解为待分发的本地公钥内容,"(test -x /sbin/restorecon && /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1 || true)"和selinux有关,不用管,因此上述命令简化为:
cat ~/.ssh/id_rsa.pub | ssh $1 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys || exit 1
可见,ssh-copy-id的全部参数都是存储在位置变量$1中传递给ssh,因此应该将ssh的端口选项"-p port_num"和user@hostname放在一块儿传递。
经过分析上面的命令,也即知道了ssh-copy-id的做用:在目标主机的指定用户的家目录下,检测是否有~/.ssh目录,若是没有,则以700权限建立该目录,而后将本地的公钥追加到目标主机指定用户家目录下的~/.ssh/authorized_keys文件中。
就这样简单的两步就实现了基于公钥的身份认证。固然,不使用ssh-copy-id,也同样能实现上述过程。更便捷地,能够写一个简单的脚本,简化上述两个步骤为1个步骤。
#!/bin/bash ########################################################### # description: public key authentication in one step # # author : 骏马金龙 # # blog : http://www.cnblogs.com/f-ck-need-u/ # ########################################################### privkey="$HOME/.ssh/id_rsa" publickey="$HOME/.ssh/id_rsa.pub" # Usage help if [ $# -ne 1 ];then echo "Usage:$0 [user@]hostname" exit 1 fi # test private/publick key exist or not, and the privilege 600 or not if [ -f "$privkey" -a -f "$publickey" ];then privkey_priv=`stat -c %a $privkey` if [ "$privkey_priv" -ne 600 ];then echo "The privilege of private key ~/.ssh/id_rsa is not 600, exit now." exit 1 fi else echo "private/public key is not exist, it will create it" ssh-keygen -t rsa -f $privkey -N '' echo "keys created over, it located on $HOME/.ssh/" fi ssh-copy-id "-o StrictHostKeyChecking=no $1" if [ $? -eq 0 ];then echo -e "\e[31m publickey copy over \e[0m" else echo "ssh can't to the remote host" exit 1 fi
该脚本将检查本地密钥对~/.ssh/{id_rsa,id_rsa.pub}文件是否存在,还检查私钥文件的权限是否为600。若是缺乏某个文件,将自动新建立密钥对文件,最后分发公钥到目标主机。
对于基于公钥认证的身份验证机制,除了上面客户端分发公钥到服务端的方法,还能够经过分发服务端私钥到客户端来实现。
先理清下公钥认证的原理:客户端要链接服务端,并告诉服务端要使用那对密钥对,而后客户端访问本身的私钥,服务端检测对应的公钥来决定该公钥所对应的客户端是否容许链接。因此对于基于公钥认证的身份验证,不管是客户端分发公钥,仍是服务端分发私钥,最终客户端保存的必定是私钥,服务端保存的必定是公钥。
那么服务端分发私钥实现公钥认证是如何实现的呢?步骤以下:假如客户端为172.16.10.5,服务端为172.16.10.6。
(1).在服务端使用ssh-keygen生成密钥对。
[root@xuexi ~]# ssh-keygen -f ~/.ssh/id_rsa -P ''
(2).将上面生成的公钥追加到本身的authorized_keys文件中。
[root@xuexi ~]# ssh-copy-id 172.16.10.6
(3).将私钥拷贝到客户端,且路径和文件名为~/.ssh/id_rsa。
[root@xuexi ~]# scp -p ~/.ssh/id_rsa* 172.16.10.5:/root/.ssh/
注意,第三步中也拷贝了公钥,缘由是客户端链接服务端时会比较本身的公钥和私钥是否配对,若是不配对将直接忽略公钥认证机制,因此会要求输入密码。能够将客户端的公钥删除掉,或者将服务端生成的公钥覆盖到客户端的公钥上,都能完成公钥认证。
虽然说,服务端分发私钥的方式不多用,但经过上面的步骤,想必对ssh基于公钥认证的身份验证过程有了更深刻的理解。
expect工具能够在程序发出交互式询问时按条件传递所需的字符串,例如询问yes/no自动传递y或yes,询问密码时自动传递指定的密码等,这样就能让脚本彻底实现非交互。
显然,ssh等客户端命令基于密码认证时老是会询问密码,即便是基于公钥认证,在创建公钥认证时也要询问一次密码。另外,在ssh主机验证时还会询问是否保存host key。这一切均可以经过expect自动回答。
关于expect工具,它使用的是tcl语言,虽然说应用在expect上的tcl语言并不是太复杂,但这并不是本文内容,若有兴趣,可网上搜索或直接阅读man文档。
首先安装expect工具。
如下是scp自动问答的脚本。
[root@xuexi ~]# cat autoscp.exp #!/usr/bin/expect
########################################################### # description: scp without interactive # # author : 骏马金龙 # # blog : http://www.cnblogs.com/f-ck-need-u/ # ########################################################### set timeout 10 set user_hostname [lindex $argv 0] set src_file [lindex $argv 1] set dest_file [lindex $argv 2] set password [lindex $argv 3] spawn scp $src_file $user_hostname:$dest_file expect { "(yes/no)?" { send "yes\n" expect "*assword:" { send "$password\n"} } "*assword:" { send "$password\n" } } expect "100%" expect eof
用法:autoscp.exp [user@]hostname src_file dest_file [password]
该自动回答脚本能够自动完成主机验证和密码认证,即便已是实现公钥认证的机器也没问题,由于公钥认证机制默认优先于密码认证,且此脚本的password项是可选的,固然,在没有实现公钥认证的状况下,password是必须项,不然expect实现非交互的目的就失去意义了。
如下是几个示例:
[root@xuexi ~]# ./autoscp.exp 172.16.10.6 /etc/fstab /tmp 123456 spawn scp /etc/fstab 172.16.10.6:/tmp The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established. RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf. Are you sure you want to continue connecting (yes/no)? yes # 主机验证时询问是否保存host key,自动回答yes Warning: Permanently added '172.16.10.6' (RSA) to the list of known hosts. root@172.16.10.6's password: # 密码认证过程,自动回答指定的密码"123456" fstab 100% 805 0.8KB/s 00:00
也能够指定完成的用户名和主机名。
[root@xuexi ~]# ./autoscp.exp root@172.16.10.6 /etc/fstab /tmp 123456 spawn scp /etc/fstab root@172.16.10.6:/tmp root@172.16.10.6's password: fstab 100% 805 0.8KB/s 00:00
如下是在创建公钥认证机制时,ssh-copy-id拷贝公钥到服务端的自动应答脚本。
[root@xuexi ~]# cat /tmp/autocopy.exp #!/usr/bin/expect ########################################################### # description: scp without interactive # # author : 骏马金龙 # # blog : http://www.cnblogs.com/f-ck-need-u/ # ########################################################### set timeout 10 set user_hostname [lindex $argv 0] set password [lindex $argv 1] spawn ssh-copy-id $user_hostname expect { "(yes/no)?" { send "yes\n" expect "*assword:" { send "$password\n"} } "*assword:" { send "$password\n" } } expect eof
用法:autocopy.exp [user@]hostname password
如下是一个示例,
[root@xuexi ~]# /tmp/autocopy.exp root@172.16.10.6 123456 spawn ssh-copy-id root@172.16.10.6 The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established. RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf. Are you sure you want to continue connecting (yes/no)? yes # 主机认证时,自动应答yes Warning: Permanently added '172.16.10.6' (RSA) to the list of known hosts. root@172.16.10.6's password: # 密码认证时自动输入密码"123456" Now try logging into the machine, with "ssh 'root@172.16.10.6'", and check in: .ssh/authorized_keys to make sure we haven't added extra keys that you weren't expecting.
若是要实现批量非交互,则能够写一个shell脚本调用该expect脚本。例如:
[root@xuexi ~]# cat /tmp/sci.sh #!/bin/bash ########################################################### # description: scp without interactive # # author : 骏马金龙 # # blog : http://www.cnblogs.com/f-ck-need-u/ # ########################################################### passwd=123456 # 指定要传递的密码为123456 user_host=`awk '{print $3}' ~/.ssh/id_rsa.pub` # 此变量用于判断远程主机中是否已添加本机信息成功 for i in $@ do /tmp/autocopy.exp $i $passwd >&/dev/null ssh $i "grep "$user_host" ~/.ssh/authorized_keys" >&/dev/null # 判断是否添加本机信息成功 if [ $? -eq 0 ];then echo "$i is ok" else echo "$i is not ok" fi done
用法:/tmp/sci.sh [user@]hostname
其中hostname部分可使用花括号展开方式枚举。但有个bug,最好ssh-copy-id的目标不要是脚本所在的本机,可能会强制输入本机密码,但批量脚本autocopy.exp则没有此bug。
例如:
[root@xuexi tmp]# /tmp/sci.sh 172.16.10.3 172.16.10.6 172.16.10.3 is ok 172.16.10.6 is ok
[root@xuexi tmp]# /tmp/sci.sh 172.16.10.{3,6} 172.16.10.3 is ok 172.16.10.6 is ok
[root@xuexi tmp]# /tmp/sci.sh root@172.16.10.3 172.16.10.6 root@172.16.10.3 is ok 172.16.10.6 is ok
ssh链接包括两个阶段:主机验证阶段和身份验证阶段。这两个阶段均可能致使链接速度慢。
具体是哪一个阶段的速度慢,彻底能够经过肉眼看出来:
(1).卡着好久才提示保存host key确定是主机验证过程慢。
(2).主机验证完成后卡着好久才提示输入密码,确定是身份验证过程慢。
其中主机验证过程慢的缘由,多是网络链接慢、DNS解析慢等缘由。网络链接慢,ssh对此毫无办法,而DNS解析慢,ssh是能够解决的,解决方法是将ssh服务端的配置文件中UseDNS设置为no(默认为yes)。
而身份验证慢的缘由,则考虑ssh的身份验证顺序:gssapi,host-based,publickey,keyboard-interactive,password。其中gssapi认证顺序是比较慢的,因此解决方法一是在ssh客户端配置文件中将GSSAPI认证机制给关掉,解决方法二是在ssh客户端配置文件中使用PreferredAuthentications指令修改身份验证顺序。
方法一修改:GSSAPIAuthentication yes
方法二修改:PreferredAuthentications publickey,password,gssapi,host-based,keyboard-interactive
若是感觉不到哪一个阶段致使速度变慢,可使用ssh或scp等客户端工具的"-vvv"选项进行调试,看看是卡在哪一个地方,不过,想看懂"-vvv"的过程,仍是比较考验耐心的。
本文篇幅太长,这部份内容单独成篇:SSH隧道:端口转发功能详解。