1.1 主机验证过程
当客户端A要连接服务端B时,首先将进行主机验证过程,即判断是否曾经连接过主机B。
判断的方法是读取~/.ssh/known_hosts文件和/etc/ssh/known_hosts文件,搜索是否有主机B的主机信息(主机信息称为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
在说明身份验证过程前,先看下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保存在服务端B的/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 | +-----------------+
1.2 身份验证过程
主机验证通过后,将进入身份验证阶段。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(不能直接提供给服务端匹配检测,因为私钥不能泄露),然后服务端将检测密钥对的公钥部分,判断该客户端是否允许通过认证。如果认证不通过,则进入下一个认证机制,以密码认证机制为例。
当使用密码认证时,将提示输入要连接的远程用户的密码,输入正确则验证通过。
1.2.1 密码认证
① 服务端生成自己的密钥,保存在/etc/ssh/目录下,该目录下存放着ssh_host_xxxkey和ssh_host_xxxkey.pub类型的文件
② 客户端发送ssh连接请求给服务器
③ 服务器把自己的公钥发送给客户端
④ 客户端保存发送过来的公钥至~/.ssh/known_hosts文件中,(我们第一次连接的时候需要敲yes就是保存公钥文件)如果之前保存过,则进行对比,一致则进行下去,不一致则报错。例如下图:
解决的办法是删除~/.ssh/known_hosts文件再重新连接。
⑤ 客户端生成自己的密钥
⑥ 客户端发送自己的公钥至服务端
⑦ 客户端用服务器的公钥对密码进行加密,然后发给服务器
⑧ 服务器用自己的私钥对公钥进行解密,获取密码,如果密码正确则允许登录
1.2.2 密钥登录
① 客户端生成一对密钥,生成的密钥保存在~/.ssh/(权限700)目录下,分别为id_xxx(私钥600)、id_xxx.pub(公钥644)
客户端生成密钥:ssh-keygen [-t 密钥类型]
② 把公钥id_xxx.pub发送给服务端,在服务端中把发送过来的公钥保存在~/.ssh/authorized_keys(644)文件中
拷贝公钥:ssh-copy-id -i id_xxx.pub
③ 客户端向服务端发送ssh连接请求,信息包括客户端的ip和用户名
④ 服务端收到该连接请求后,在~/.ssh/authorized_keys中查找是否有客户端的ip和用户名,如果存在的话服务端随机生成一段字符串char
⑤ 服务端用公钥authorized_keys加密char发送给客户端
⑥ 客户端收到后用私钥id_xxx解密,得到char
在用私钥解密的之前可以加入密码:ssh-keygen -p
⑦ 客户端用服务端发送的公钥(服务端自己生成的公钥)加密char发送给服务端
⑧ 服务端收到后用私钥(服务端自己生成的私钥)解密,得到char与之前随机生成的char比对,如果一致则登录
实现密钥登录的方法:
① 我们要实现CentOS6密钥方式登录CentOS7,刚开始CentOS6的~/.ssh目录下并没有自己的密钥;
② CentOS6生成自己的密钥(公钥和私钥),白色框内的分别表示生成的密钥的文件名、是否输入密码、请再次输入密码,直接回车则保持默认,第二项的密码用在客户端用私钥解密的过程;
③ 将CentOS6生成的公钥拷贝发送给CentOS7;
④ 该公钥在CentOS7下的~/.ssh目录下,并按指定的命名规则命名该公钥文件;
⑤ 之后ssh登录就可以不用输入密码;
⑥ 设置客户端的私钥密码,这里的私钥密码不是服务端主机的登录密码,而是客户端在用私钥解密的时候要用到的密码。
1.3 验证通过
当主机验证和身份验证都通过后,分两种情况:直接登录或执行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连接建立之后做的第一件事是在远程主机上执行这两个文件中的命令。
三、配置文件
1.配置文件分布
分为服务端配置文件/etc/ssh/sshd_config和客户端配置文件/etc/ssh/ssh_config(全局)或~/.ssh/config(用户)。
2.1 sshd_config
简单介绍下该文件中主要参数含义。
Port 定义sshd服务的端口,默认为22 Protocol 定义ssh协议的版本号,一般为2 ListenAddress 定义sshd监听的IP地址,默认所有IP,若要指定则为:ListenAddress xxx.xxx.xxx.xxx PidFile 定义开启sshd服务的进程ID的文件,/var/run/sshd.pid存放着PID LoginGraceTime 定义输入密码阶段的宽容时间,超过该时间还没有连接成功则自动退出。默认2分钟 Compression 定义是否使用压缩方式传输,有yes no delayed(登录后采用压缩方式) HostKey 定义sshd服务生成的密钥文件格式 SyslogFacility 定义ssh登录日志存放的文件,AUTHPRIV表示/var/log/secure PermitRootLogin 表示是否允许root登录 StrictModes 表示是否对sshd相关的文件进行权限的检查 PubkeyAuthentication 表示是否允许用户密钥登录的方式 AuthorizedKeysFile 表示密钥登录的时候,客户端公钥需要保存为服务端的什么目录、格式,默认为.ssh/authorized_keys PasswordAuthentication 表示是否需要进行密码验证 PermitEmptyPasswords 表示是否允许空密码登录,默认为no UsePAM 表示是否使用PAM管理认证,默认为yes X11Forwarding 表示是否允许图形化数据经过ssh通道转发 TCPKeepAlive 表示是否让ssh时刻监控TCP的连接状态 MaxStartups 定义允许同时出现连接输入密码界面的最大数目 UseDNS 表示是否对客户端进行DNS反向解析,用以判断客户端的合法性。默认为yes,如果内外互联可以设置为no DenyUsers/DenyGroups 定义禁止登陆的用户和群组
一般来说,如非有特殊需求,只需修改下监听端口和UseDNS为no以加快主机验证阶段的速度即可。
配置好后直接重启启动sshd服务即可。
[root@xuexi ~]# service sshd restart
2.2 ssh_config
需要说明的是,客户端配置文件有很多配置项和服务端配置项名称相同,但它们一个是在连接时采取的配置(客户端配置文件),一个是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。
-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
-1:使用ssh v1版本,这是默认使用协议版本 -2:使用ssh v2版本 -C:拷贝时先压缩,节省带宽 -l limit:限制拷贝速度,Kbit/s,1Byte=8bit,所以"-l 800"表示的速率是100K/S -o ssh_option:指定ssh连接时的特殊选项,一般用不上 -p:拷贝时保持源文件的mtime,atime,owner,group,privileges -P port:指定目标主机上ssh端口,大写的字母P,默认是22端口 -r:递归拷贝,用于拷贝目录,注意,scp拷贝遇到链接文件时,会拷贝链接的源文件内容填充到目标文件中(scp的本质就是填充而非拷贝) -v:输出详细信息,可以用来调试或查看scp的详细过程,分析scp的机制
get 下载 put 上传 ls pwd cd