一、SSH协议原理
1. SSH协议
SSH 为 Secure Shell 的缩写,由 IETF 的网络小组(Network Working Group)所制定;SSH 为建立在应用层基础上的安全协议。SSH协议有各种实现,在Linux操作系统中基本上都内置了openssh软件包,包括ssh命令行客户端和sshd服务端,在Windows 下有开源的客户端putty和商业客户端SecureCRT等,也有Windows版本OpenSSH服务端。
SSH协议默认使用22端口,采用非对称加密算法进行加密。
2. SSH会话过程
session key: 这个是用来作为secret key加密用的一个key,同时也作为每个ssh连接的标识ID。
host key: 这个是用来作为server的身份验证用的。
known-hosts: 这个是存在客户端的一个可信server的public key列表。
user key: 这个是用来作为client的身份验证用的。
本文中Linux客户机是 192.168.10.51,服务端为192.168.10.58,均安装CentOS7;windows7 IP是192.168.14.117。
(1)服务器建立公钥: 每一次启动 sshd 服务时,会主动去找 /etc/ssh/ssh_host* 的文件,没有的话 sshd 会主动去计算出这些需要的公钥,同时也会计算出服务器自己需要的私钥。例如:
[root@localhost ssh]# ll /etc/ssh/
总用量 604
-rw-r--r--. 1 root root 581843 8月 9 2019 moduli
-rw-r--r--. 1 root root 2276 8月 9 2019 ssh_config
-rw-------. 1 root root 3907 8月 9 2019 sshd_config
-rw-r-----. 1 root ssh_keys 227 1月 14 2020 ssh_host_ecdsa_key
-rw-r--r--. 1 root root 162 1月 14 2020 ssh_host_ecdsa_key.pub
-rw-r-----. 1 root ssh_keys 387 1月 14 2020 ssh_host_ed25519_key
-rw-r--r--. 1 root root 82 1月 14 2020 ssh_host_ed25519_key.pub
-rw-r-----. 1 root ssh_keys 1675 1月 14 2020 ssh_host_rsa_key
-rw-r--r--. 1 root root 382 1月 14 2020 ssh_host_rsa_key.pub
[root@localhost ssh]# cat /etc/ssh/ssh_host_ecdsa_key.pub
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDmSjz7GVGPH1ZBeQtt0dz6bMNtduXbDP801YoPSMYGXMzOlkHPeKEcSbFNrPOYlqNiczDKxruDskr2f9TBsGwg=
(2)客户端第一次连接到SSH服务器,则会将服务器的公钥记录到客户端的主目录内的 ~/.ssh/known_hosts中。
[root@localhost ssh]# ssh 192.168.10.58
The authenticity of host '192.168.10.58 (192.168.10.58)' can't be established.
ECDSA key fingerprint is SHA256:3HgRbgWOV9Y8es4SAA1bZzM5r5XrY7svBleyPnm4Pvw.
ECDSA key fingerprint is MD5:11:d2:b6:64:36:f4:c4:89:5b:8e:df:09:de:d3:d4:d9.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.10.58' (ECDSA) to the list of known hosts.
root@192.168.10.58's password:
Last failed login: Fri Oct 9 11:29:52 CST 2020 from 111.229.116.118 on ssh:notty
There were 2 failed login attempts since the last successful login.
Last login: Fri Oct 9 11:29:16 2020 from 192.168.14.117
打开known_hosts文件,其中有一项是192.168.10.58服务器的公钥指纹,这个与SSH服务端(192.168.10.58)的 /etc/ssh/ssh_host_ecdsa_key.pub内容一致
[root@localhost .ssh]# cat /root/.ssh/known_hosts
192.168.10.58 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDmSjz7GVGPH1ZBeQtt0dz6bMNtduXbDP801YoPSMYGXMzOlkHPeKEcSbFNrPOYlqNiczDKxruDskr2f9TBsGwg=
3. SSH协议两种级别的验证方法
第一种级别(基于口令的安全验证):只要你知道自己帐号和口令,就可以登录到远程主机。所有传输的数据都会被加密,但是不能保证你正在连接的服务器就是你想连接的服务器。可能会有别的服务器在冒充真正的服务器,也就是受到“中间人攻击”这种方式的攻击。如下图,这种方式下,接收到客户端的要求后,服务器便将自己的公钥传送给客户端,

- 服务端收到登录请求后,首先互换公钥。
- 客户端用服务端的公钥加密账号密码(服务端的有效账户)并发送。 #注意这里传送了账号密码
- 服务端用自己的私钥解密后得到账号密码(服务端的有效账户),然后进行验证。
- 服务端用客户端的公钥加密验证结果并返回。
- 客户端用自己的私钥解密后得到验证结果。
SSH虽然传输过程中很安全,但是在首次建立链接时并没有办法知道发来的公钥是否真的来自自己请求的服务器,如果有人在客户端请求服务器后拦截了请求,并返回自己的公钥冒充服务器,这时候如果链接建立,那么所有的数据就都能被攻击者用自己的私钥解密了。这也就是所谓的中间人攻击。 前面讲SSH会话过程中,客户端第一次连接到SSH服务器会有一条信息提示,大意是无法验证对方是否可信,并给出对方公钥指纹(MD5值),问是否确定要建立链接。这是因为公钥长度较长难以比对,将其经过MD5计算转换为128位的指纹便于比较。Server需在自己的网站上贴出自己的公钥指纹用于用户核对。
第二种级别(基于密钥的安全验证):在客户端上创建一对密钥,并把公钥放在需要访问的服务器上。客户端软件向服务器发出SSH连接请求,并将公钥发送给服务器。服务器收到请求之后,先在该服务器上你的主目录下寻找你的公钥,然后把它和你发送过来的公钥进行比较。如果两个密钥一致,服务器就用公钥加密“质询”(challenge)并把它发送给客户端软件。客户端软件收到“质询”之后就可以用你的私钥在本地解密再把它发送给服务器完成登录。与第一种级别相比,第二种级别不仅加密所有传输的数据,也不需要在网络上传送口令,因此安全性更高,可以有效防止中间人攻击。

《SSH两种登录方式》一文中描述的最为详细。两种级别的登录都存在公钥交换,基于口令的安全验证是客户端第一次访问的时候,服务端将自己的公钥发送给客户端;基于密钥的安全验证是客户端第一次访问的时候,将客户端的公钥发给服务端。
二、SSH服务配置
1. sshd服务的启动
Linux系统,不管是桌面还是服务器,一般都会预装openssh软件包,包括ssh服务(ssh-server和ssh-sftp-server)和客户端工具。SSH服务默认端口为#22,相关命令如下:
--检查sshd服务是否启动,查看22号端口是否处于监听状态。
#netstat -ntlp
--sshd服务开机自动启动
#systemctl enable sshd
--关闭sshd服务开机自动启动
#systemctl disable sshd
--sshd服务状态
#systemctl status sshd
--启动sshd服务
systemctl start sshd
--停止sshd服务
systemctl stop sshd
2. ssh服务配置文件
SSH服务的配置文件是:/etc/ssh/sshd_config,默认的如下:
[root@localhost ~]# egrep -v "^#|^$" /etc/ssh/sshd_config
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
SyslogFacility AUTHPRIV
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication yes
ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
UsePAM yes
X11Forwarding yes
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS
Subsystem sftp /usr/libexec/openssh/sftp-server
ssh服务安全非常重要,/var/log/secure 可以看到尝试登陆的安全日志。因此日常工作中的常见配置工作有:
(1) 修改ssh服务默认端口。ssh 作为远程管理设备的工具,默认情况下使用TCP的22端口,若不进行修改,很容易被利用遭到攻击,所以建议修改端口,在配置文件中格式如下
port 60022
从客户端登录的时候,ssh -p 60022 root@192.168.14.211
(2)禁止root用户远程登录。一般在远程登录管理上我们会禁止直接使用 root 用户登录,同时会对会话进行一些限制,在配置文件中格式如下:
PermitRootLogin no #不允许ssh登录root账户,限制为仅控制台访问
PermitEmptyPasswords no # 禁止使用空密码登录
PasswordAuthentication yes # 可使用密码,更安全可以只允许密钥登陆
MaxAuthTries 5 #尝试次数5次
ClientAliveInterval 600 # 600为秒,即600秒后无动作就自动断开连接
ClientAliveCountMax 3 # 检测客户端响应次数 3次没响应就退出
Protocol 2 # 强制使用SSH Protocol2(版本2更安全)
(3)允许X11转发,“X11Forwarding yes”,可以在ssh客户端远程允许GUI程序,参见本博客《Linux桌面系统远程访问全攻略》的“SSH X11转发”一节。
(4)sftp服务的开启,“Subsystem sftp /usr/libexec/openssh/sftp-server”。
注:在中标麒麟V7和loongson操作系统中,都是预装的openssh7.4p1版本,而麒麟V10中预装的是openssh1.7.2p2。
前者的sftp-server命令在openssh-server包中,后者是一个单独的软件包openssh-sftp-server。
3. webmin中对ssh进行配置
在webmin中对ssh服务进行管理,等同于手工编辑/etc/ssh/sshd_config配置文件,在这里可以更改配置和启动/停止 SSH服务:

【模块配置】主要是openssh配置文件路径等设置:

【验证】模块主要是登录与验证选项,如下图:

【网络】模块主要是配置监听端口和协议版本等,如下图:

【杂项中】有X11 forwarding设置等,【Edit config file】可以直接编辑配置文件。
三、SSH免密配置
1. SSH免密登录
介绍的文章很多,例如https://www.jianshu.com/p/0f9b72d691c2/ https://www.jb51.net/article/130039.htm,https://www.jb51.net/article/94599.htm
第一步是首先生成 ssh key:$ ssh-keygen
[root@localhost ~]# ssh-keygen
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.
The key fingerprint is:
SHA256:GRPFazvNYdxkJ7IIHbTpdLJRtcwV1aoTn2BE9aCUe2g root@localhost.localdomain
The key's randomart image is:
+---[RSA 2048]----+
| .=+o+++.*|
| ..o=+++=o|
| o.*=oO++.|
| *+=E + |
| S.oB B . |
| o = o |
| . . |
| |
| |
+----[SHA256]-----+
(1)上例中,提示用户输入生成的私钥的名称,如果不填,默认私钥保存在 $HOME/.ssh/id_rsa 文件中,生成的公钥的文件名,通常是私钥的文件名后面加 .pub 的后缀。
(2)提示输入密码,这里的密码是用来保证私钥的安全的。如果填写了密码,那么在使用密钥进行登录的时候,会让你输入密码,这样子保证了如果私钥丢失了不至于被恶意使用。
完成后可以在目录下看到:
[root@localhost ~]# ll /root/.ssh/
总用量 12
-rw-------. 1 root root 1679 10月 9 15:49 id_rsa
-rw-r--r--. 1 root root 408 10月 9 15:49 id_rsa.pub
-rw-r--r--. 1 root root 175 10月 9 11:30 known_hosts
此外,ssh-keygen使用参数可以省去生成公钥/私钥的回车步骤,格式如下:
$ssh-keygen -t rsa -P '' -f /root/.ssh/id_rsa
#-t 指定rsa 加密算法
#-P 指定密码,次数为空
#-f 指定key文件路径
第二步是把第一步 public key 添加到信任方(SSH服务器)的authorized_keys中,#authorized_keys权限设置为600
可以通过scp命令,如:scp -p ~/.ssh/id_rsa.pub root@<remote_ip>:/root/.ssh/authorized_keys
或者通过ssh-copy-id的方式,如: ssh-copy-id -i ~/.ssh/id_rsa.put <romte_ip>,这是推荐的方式。
[root@localhost ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.10.58
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/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@192.168.10.58's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'root@192.168.10.58'"
and check to make sure that only the key(s) you wanted were added.
客户端执行成功ssh-copy-id后,在服务器(这里是192.168.10.58),root用户的authorized_keys中会添加加客户机(192.168.10.51)的这个公钥信息。
这里要注意两个用户的概念。本地执行这条命令的用户,即当前登录用户root,远程服务器192.168.10.58的用户root。生成的密钥也是和用户相关的:
- 如果客户端root账户想以服务器上其它账户(例如ser1账户)免密登录,需要相应执行一次ssh-copy-id操作(参数中,root@192.168.10.58改成ser1@192.168.10.58)。
- 同理,如果客户端使用其它账户(如liu)想实现以服务器上root账户免密登录,也需要执行一次ssh-copy-id操作。
第三步:现在可以免密码登录远程服务器,例如:
[user1@localhost] ssh root@192.168.10.58
在客户端的~/.ssh/known.hosts中,也会添加这个远程服务器(192.168.10.58)的公钥指纹。
更完整的免密执行命令:$ ssh -o "StrictHostKeyChecking no" username@password
并且,SSH免密设置完成后,所有使用SSH协议的应用,如scp都可以进行免密操作。
2. Windows下SSH客户端的免密登录
本例中:windows7 客户端IP是192.168.14.117,服务端还是刚才的192.168.10.58(centos7)。
参考:《Securecrt免密码登录》,《ssh免密码登录、SecureCRT免密码登录》,《putty免密登录》
步骤和上面基本差不多,在SecureCRT Tools菜单中有Create public Key......功能,PuTTY安装目录里的puttygen.exe工具。会生成有两个文件,一个公钥文件(Identity.pub)一个私钥文件(Identity),需要把Identity.pub的内容追加到服务器的文件~/.ssh/authorized_keys中(最好是从界面上复制再再vi authorized下粘贴,因为这个.pub文件多出一些注释内容)。以putty为例
(1)用puttygen生成密钥对:

(2)保存私钥文件(Save private key,文件名建议为identity.ppk);并且将这个PublicKey的内容复制,然后粘贴到服务端的 ~/.ssh/authroized_keys文件中。如果.ssh目录不存在,就创建一个。
mkdir ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authroized_keys
chmod 600 ~/.ssh/authroized_keys
注:.ssh 目录的权限必须是0700,.ssh/authorized_keys 文件权限必须是0600,否则公钥认证不会生效。
注:确保粘贴无误,很多时候拒绝密钥都是粘贴出现的问题,多对比一下!
[root@localhost ~]# cat ~/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDqGPgfbLdUTeRQXrd1A0Ua5HZMgX9uVXTw/gBKbIU/ T0GuSBxLXDYeNjPqFP4hEKEnWEuy/wglV0uHqz0KKYYr/a0oWvSdIyipfoDTFZnD56JZTM+abym4Bzor QFzBnS0s0hcGNj9o3v0JGPdlaR1G4lcG5c81OxXlqhQlMpGDXAXZ7wZzWEtZZNfGWfQqf4OtuN+x1H25 1CC7SOupKEVbEd52NbFWdb+Q0BEUF+YVICTChLOcop5c34kK459LzvSv6JxOiq8Uap6X7JlNg5Zyd7y1 XLnUeMDPlPuoaoZrvN1YG+zERlwgdFH0Pj5mombTB8S/MyPKBrAUzKgu8xgx root@localhost.loca ldomain
ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA06MnaiTedG8Abbakzo8pGYntjYoni+NiiOBDriuNyUAX Itd+Anp5Eg7iY5p8BNS/caEBSLwkXLQdAUElLilIl2ZzytkQZ+zreCCwvU79EhNs8PU4+ShpzW/Hpdbz kIcMjMPwYtBFREYcXEctZjRcRBBMGMX1/nGcEdDZPp4WOqBqdazQbDK+kJ2AY+uusKslywbBwP/2Rsiq 4jjAj2h5b68aZ3khNmHF/9YVwwml/DK0x9mV2QmP8wP3BvZsjDJLvXmQxlYmq3n5fNLe/USD4nVYF/tW OZbWkt7TAsxpFvL30OQXTN++vawD3BE/+bzTtlsHJthOxkq1GZZ1LMm/KQ== rsa-key-20201010
(3)再使用putty建立到服务端192.168.10.58的连接选项中,Connection->SSH->Private keyfile for authentication“认证私钥文件”选择刚才puttygen生成的.ppk文件。

(4)现在可以免密登录服务端(192.168.10.58),如图
Using username "root".
Authenticating with public key "rsa-key-20201010"
Last login: Sat Oct 10 17:03:24 2020 from 192.168.14.117
[root@localhost ~]#
如果出现报错“Server refused our key”,可以检查1..ssh目录和authorized_keys权限;2. /etc/ssh/sshd_config中的几个参数,如"StrictModes no"、"PubkeyAuthentication yes";3.关闭SELinux(临时关闭使用setenforce=0)
对于SecureCRT中。建立连接,勾选公钥选项,去掉密码登陆选项。点开公钥的属性选项,选择使用会话公钥设置,然后选择自己的私钥文件。
注:SecureCRT 查看密钥保存地址方法:选项Option---全局选项 Global---SSH主机密钥Host Key----主机密钥数据库位置
3. SSH与文件传输scp、sftp
Linux scp 命令用于 Linux 之间复制文件和目录。
scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令。
scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]
[-l limit] [-o ssh_option] [-P port] [-S program]
[[user@]host1:]file1 [...] [[user@]host2:]file2
简易写法: scp [可选参数] file_source file_target
例1:从远程服务器中下载数据文件到本地主机
scp root@host1:/tmp/test1 /tmp
如果你想从远程主机host1中的/tmp/目录下拷贝test1 文件到本地主机的/tmp目录
例2:从远程服务器中拷贝目录文件到本地主机下面
scp -r root@hots1:/tmp /tmp
当你从远程主机中拷贝一个目录的时候,你需要给scp命令传入一个“-r“ 选项,这样scp命令就会将远程主机下面的整个目录的文件都拷贝的本地主机下。
例3:将本地主机的文件拷贝到远程服务器上
scp /tmp/test1 root@host1:/tmp
如果要将本地主机的 /tmp目录下的test1文件拷贝到远程主机的/tmp目录下
例4:将本地主机下的目录上传到远程服务器
scp -r /tmp root@hots1:/tmp
这个和刚才第二步的命令类似,就是需要传”-r” 参数给linux scp 命令
和scp比较类似内置于SSH软件包的一个工具是sftp,sftp是一个和ftp类似的交互式文件传输协议,有文件管理功能等,支持断点续传。
openssh中,如果配置文件中下面一行存在且没有被注释掉,那么SFTP已经开启。所有可使用ssh的用户都可使用SFTP(缺陷是用户在SFTP软件里面可以cd /从而看到系统所有文件)。
#/etc/ssh/sshd_config
Subsystem sftp /usr/libexec/openssh/sftp-server
sftp常用命令
sftp xxx.xxx.xxx.xxx 登录root用户,
sftp zygf@xxx.xxx.xxx.xxx 进行登录zygf用户
ls 查看服务器当前目录下文件 lls是看linux当前目录下
help 查看sftp支持哪些命令
cd 指定目录
pwd和lpwd pwd是看远端服务器的目录, lpwd是看linux本地目录的当前目录
!command 在本linux上执行{command}命令, 比如!rm a.txt是删除linux当前目录下的a.txt文件
get xxx.txt 下载xxx文件
put xxx.txt 上传xxx文件
quit / bye / exit 退出sftp
四、sshfs:通过SSH协议挂载Linux文件系统
修改远程文件常常用ftp、scp、sftp把这些远程文件拉取到本地对其进行修改再上传回去。实际上只要能ssh到远程主机,通过sshfs就能把远程主机上文件系统挂载到本地,然后像操作本地文件和目录那样方便操作它们,期间所作的修改会自动保存到远程对应的目录及文件。一是 不用把远程文件手动复制一份到本地修改:;而是使用ssh协议连接,安全上有保障,使用起来也很方便。
参考《CentOS 7安装SSHFS 实现远程主机目录 挂载为本地目录》和《使用SSHFS挂载远程目录》
1. 本机安装sshfs
sshfs通过SSH协议访问远程文件系统的用户空间文件系统,Sshfs的实现依靠于linux内核模块fuse对用户空间文件系统的支持。可以把远程主机上的文件系统挂载到本地使用。
#lsmod | grep fuse #查看Linux系统有没有这个模块
--如果没有,如下是在CentOS/Fedora下进行安装
#yum install fuse #安装fuse
#yum install fuse-sshfs #安装sshfs
--查看sshfs版本
#sshfs --version
注:安装好后,系统会自动建立fuse用户组,要使用sshfs的普通用户只要加入这个用户组即可!
2. 挂载远程Linux上的文件系统
sshfs命令格式(详见sshfs man):
--挂载
sshfs [user@]host:[directory] mountpoint [options]
--卸载
fusermount -u mountpoint #也可 umount mountpoint,如果出现devce is busy,可以umount -fl
sshfs常用参数:
-o ro #只读方式挂载。
-o rw #读写方式挂载。
如果不指定参数,默认是读写方式挂载。
-o idmap=user #idmap=TYPE用户和组的映射模式,可以是none(默认)、user(仅映射uid)和file(映射UID和GID)
-o allow_other #指挂载后/mnt/remote能被你所在系统上的其它用户访问。
例如:
sshfs -o allow_other user@111.111.111.111:/home /mnt/remote
sshfs -o idmap=user pi@192.168.0.116:/home/pi /mnt/Pi
执行sshfs命令后输入密码然后cd 到挂载点如/mnt/remote目录下,就可以在里面看到远程主机上目录的内容。
3. sshfs开机自动挂载远程文件系统
开机自动挂载需要做两步操作:
(1)在 /etc/fstab 添加挂载项,例如:
#user@111.111.111.111:/home /mnt/remote fuse defaults,auto,allow_other 0 0
(2)设置ssh无密码登陆,参考前文。
设置ssh无密码登陆远程主机(public key认证),方法不再列出,并在fstab中加上IdentityFile参数,如下。
#user@111.111.111.111:/home /mnt/remote fuse IdentityFile=~/.ssh/id_rsa defaults,auto,allow_other 0 0
这样以后就能开机自动把远程主机上的文件系统挂载到本地使用了。也可以# mount -a 更新 fstab 文件使修改立即生效。
【注】Linux桌面文件管理器中的远程服务器连接-SSH连接,也可以完成类似功能,但是是通过SSH-sftp协议挂载远程的Linux目录。
4. 其它
Windows也有win-sshfs工具(包括其DokanMounter服务),可以用SSH协议将Linux服务器目录挂载为windows本地硬盘。日常使用起来比SAMBA要简单直接。参考:《Windows 下使用 SSHFS 通过 SSH 协议挂载远程服务器目录》、win10上使用win-sshfs。
Mac上支持sshfs的工具是OSXFuse( http://osxfuse.github.io/)。