# SSH
:Secure Shell ,一个加密的网络传输协议,常用于远程登录、传输文件。
- 于 1995 年发布。
- 最初是 Unix 系统上的一个程序,后来扩展到其他类 Unix 平台。
- 主要有两个版本,SSH2 修复了 SSH1 的一些漏洞。
- 基于 TCP 协议通信,工作在应用层。
- 采用 C/S 架构工作。
- SSH 服务器要运行一个守护进程 sshd ,监听发到某个端口(默认是 TCP 22 端口)的 SSH 请求。
- SSH 客户端可采用 SecureCRT、Putty 等软件,向服务器发起 SSH 请求。
- FTP、Telnet、POP 等网络传输协议采用明文传输数据,容易被监听、窃取。而 SSH 将数据经过非对称加密之后再传输,很安全,但传输耗时大了点。
# 服务器
# 部署
运行 sshd 服务器:
yum install ssh systemctl start sshd systemctl enable sshd
或者运行 docker 镜像:
docker run -d -p 10022:22 --name sshd \ -e SSH_ENABLE_ROOT=true \ -e SSH_ENABLE_ROOT_PASSWORD_AUTH=true \ panubo/sshd:1.5.0
- 需要进入容器,手动修改 root 密码。
# 配置
sshd 的主配置文件是 /etc/ssh/sshd_config
,内容示例:
Port 22 # 监听的端口号
ListenAddress 0.0.0.0 # 允许连接的 IP
Protocol 2 # SSH 协议的版本
HostKey /etc/ssh/ssh_host_rsa_key # 使用 SSH2 协议时,RSA 私钥的位置
MaxAuthTries 6 # 被 SSH 客户端登录时,每个 TCP 连接的最多认证次数。如果客户端输错密码的次数达到该值的一半,则断开连接
MaxSessions 10 # 同时最多连接的终端数
# ClientAliveCountMax 3 # 如果一个 SSH 连接长时间没有传输数据,则每隔 ClientAliveInterval 秒发送一次空消息给客户端,最多发送 ClientAliveCountMax ,然后关闭空闲的连接
# ClientAliveInterval 0 # 默认为 0 ,表示不会自动关闭空闲的连接
# 当前 SSH 服务器被 SSH 客户端连接时,允许以哪些用户的身份登录
AllowUsers root [email protected] # 只允许这些用户进行 SSH 登录。可以使用通配符 * 和 ? ,可以指定用户的 IP ,可以指定多个用户(用空格分隔)
AllowGroups * # 只允许这些用户组进行 SSH 登录
PermitRootLogin yes # 允许以 root 用户的身份登录
# 关于密码认证
PasswordAuthentication yes # 被 SSH 客户端登录时,允许以密码方式进行身份认证(否则只能采用密钥认证)
PermitEmptyPasswords no # 不允许使用空密码登录
# 关于密钥认证
StrictModes yes # 被 SSH 客户端登录时,如果采用密钥认证,则检查该用户的家目录、authorized_keys 的文件权限,只允许被该用户访问,否则存在被其他用户篡改的风险,拒绝 SSH 登录
UseDNS no # 是否检查 SSH 客户端的主机名、 IP 是否与 DNS 名称一致。建议禁用该功能,从而减少 SSH 连接的耗时
GSSAPIAuthentication yes # 是否允许以 Kerberos 协议进行身份认证。该功能通常没用,不过也不影响 SSH 连接的耗时
修改了 sshd_config 文件之后,要重启 sshd 服务才会生效:
systemctl restart sshd
使用 SSH 客户端登录 SSH 服务器时,主要有两种方式进行身份认证:
- 密码认证
- :登录时指定用户名,然后按提示输入密码。
- 优点:
- 可设置方便记忆的密码。
- 缺点:
- 每次登录时需要手动输入密码。
- 采用弱密码时容易被暴力破解。
- 密钥认证
- :先将 SSH 客户端的公钥放在 SSH 服务器上,当 SSH 客户端要登录时会发出该公钥的指纹,而 SSH 服务器会根据指纹检查 authorized_keys 中是否有匹配的公钥,有的话就允许该客户端登录。然后 SSH 服务器会用该公钥加密一个消息回复给 SSH 客户端,该消息只能被 SSH 客户端的私钥解密,这样就完成了双向认证。
- 优点:
- 很安全。
- 每次登录时不需要手动输入密码,又称为免密登录。
- 缺点:
- 密钥文件只能拷贝,不方便人记忆。
- 密码认证
如果 StrictModes 检查不通过,则 sshd 会拒绝 SSH 登录,并在
/var/log/secure
文件中报错Authentication refused: bad ownership or modes for file ~/.ssh/authorized_keys
。此时需要调整文件权限:chown `id -u`:`id -g` ~ ~/.ssh chmod 700 ~ ~/.ssh chmod 600 ~/.ssh/authorized_keys
即使其它主机不知道本机的 root 密码,但可能尝试用一些常见密码 SSH 登录本机,进行暴力破解。可采取以下防范措施:
- 给本机配置复杂的 SSH 密码,或者只允许使用 SSH 密钥进行登录。
- 禁止以 root 用户的身份登录,然后创建其它用户并分配 root 权限,这样暴力破解时还需要猜测有效的用户名。
- 配置
/etc/hosts.allow
、/etc/hosts.deny
,只允许被指定的 IP 登录。 - 如果本机被一个 IP 连续多次 SSH 登录失败,则自动封禁该 IP 。
- Linux 发行版默认没有自动封禁 IP 的功能,用户可以安装 fail2ban 等工具来实现,或者添加 crontab 定时任务来自动扫描日志。
- fail2ban (opens new window) 的工作原理:扫描
/var/log/btmp
等日志文件,找出需要封禁的 IP ,然后添加 iptables 规则来禁止该 IP 连接本机。
# 白名单、黑名单
当 Linux 收到一个 ssh 或 telnet 的连接请求时,会先查看
/etc/hosts.allow
文件:- 如果包含该 IP ,则建立 TCP 连接,开始身份认证。
- 如果不包含该 IP ,则查看 /etc/hosts.deny 文件:
- 如果包含该 IP ,则拒绝连接。
- 如果不包含该 IP ,则允许连接。
- 修改这两个配置文件之后,要重启 sshd、telnet 服务才会生效。
/etc/hosts.allow
的内容示例:sshd:10.0.0.1 sshd:10.0.0. # 允许一个网段 in.telnetd:10.0.0.1
/etc/hosts.deny
的内容示例:sshd:ALL # 禁止所有 IP in.telnetd:ALL
# 客户端
# 命令
$ ssh <user>@<host> # 使用 ssh 协议,以 user 用户(默认为 root )的身份登录 host 主机
-p 22 # 指定端口号
[command] # 不打开远程主机的 shell ,而是以非交互模式执行一条命令
# 建立 SSH 连接的同时,可以运行一个代理服务器
# bind_address 默认为 localhost ,绑定到其它地址时需要在 sshd_config 中设置 GatewayPorts=yes
-L [bind_address:]<bind_port>:<host>:<port> # 让本地的 ssh 进程监听 bind_port 端口,将该端口收到的 TCP 数据包传输到远程主机,由后者转发到任意主机的 <host>:<port>
-R [bind_address:]<bind_port>:<host>:<port> # 让远程主机的 sshd 进程监听 bind_port 端口,将其 TCP 数据包传输到本机,由后者转发到任意主机的 <host>:<port>
- 例:
ssh [email protected] ls -lh ssh [email protected] "hostname | xargs echo" # 用引号将待执行的命令标明为一个字符串,以免被特殊字符截断 ssh [email protected] 'echo $HOSTNAME' # 用单引号时,不会在本机读取变量的值,而是直接先将命令发送到远端去执行
- ssh 登录成功之后,会在目标主机上打开一个 shell 终端,并将其 stdin、stdout 绑定到当前终端。
- ssh 登录时报错 timeout 的可能原因:
- 与目标主机的网络不通。
- 目标主机没有运行 sshd 服务器,或者防火墙没有开通 22 端口。
- 目标主机的负载太大,接近卡死,不能响应 ssh 连接请求。
- 可通过以下格式发送多行命令:
ssh -tt [email protected] << EOL uname -n echo hello exit EOL
-tt
选项用于强制分配伪终端,从而将远程终端的内容显示到当前终端。- 这里将 EOF 声明为定界符,将两个定界符之间的所有内容当作 sh 脚本发送到远程终端执行。甚至两个定界符之间的缩进空格,也会被一起发送。
- 最后一条命令应该是 exit ,用于主动退出远程终端。
# 配置
~/.ssh/config
文件记录了 ssh 命令的配置参数。示例:Host * StrictHostKeyChecking ask # 是否检查远程主机的公钥。默认为 ask ,即在登录时询问是否检查。可改为 yes 或 no ,即总是检查、不检查 # Port 22 # SSH 登录端口 # User root # SSH 登录用户 # IdentityFile ~/.ssh/id_rsa.pub # 用于免密登录的私钥文件 # UserKnownHostsFile ~/.ssh/known_hosts # known_hosts 文件的路径 Host 10.0.* StrictHostKeyChecking no Host centos* !centos-1 # 匹配所有名称以 centos 开头的主机,排除 centos-1 主机 StrictHostKeyChecking yes
- 该文件默认不存在,可主动创建。
- 可定义多组 Host 配置,它们会分别生效。如果某个主机匹配一个或多个 Host 模式,则采用对应的配置参数。
~/.ssh/known_hosts
文件记录了所有与本机进行过 SSH 通信的远程主机的公钥,用于验证远程主机的身份。- ssh 命令默认会在第一次尝试登录远程主机时(不需要成功登录),按 SSH 协议进行一次通信,将其公钥记录到 known_hosts 文件。
- 以后再尝试登录远程主机时,如果其密钥与记录的值不同,则终止登录并发出警告,怀疑该远程主机是假冒的。
- ssh 命令第一次尝试登录远程主机时,known_hosts 文件未记录该主机的密钥,不能验证身份。默认配置了
StrictHostKeyChecking yes
,会询问是否依然要登录。如下:[root@CentOS ~]# ssh 10.0.0.1 The authenticity of host '10.0.0.1 (10.0.0.1)' can't be established. ECDSA key fingerprint is SHA256:4v11FRemHSDNLDPETHgi7TBKLzg6STOCNXsN1aX18Ko. ECDSA key fingerprint is MD5:69:8a:63:97:37:2f:13:5c:4f:d4:27:46:9d:e8:7d:43. Are you sure you want to continue connecting (yes/no)?
- ssh 命令默认会在第一次尝试登录远程主机时(不需要成功登录),按 SSH 协议进行一次通信,将其公钥记录到 known_hosts 文件。
~/.ssh/authorized_keys
文件记录了一些远程主机的公钥,允许使用这些公钥对应的私钥进行认证,登录本机。
# 相关命令
# sshpass
- 通过 sshpass 命令可以传递密码给 ssh、scp 命令,如下:但这样会将明文密码泄露到终端,不安全。
yum install sshpass sshpass -p 123456 ssh [email protected]
# ssh-keygen
$ ssh-keygen # 生成一对 SSH 密钥(默认采用 RSA 加密算法)
-r rsa # 指定加密算法(默认是 rsa)
-C "[email protected]" # 添加备注信息
- 默认会将私钥文件 id_rsa 、公钥文件 id_rsa.pub 保存到 ~/.ssh/ 目录下。
- id_rsa.pub 的内容示例如下,只占一行,包括加密算法名称、公钥值、备注信息三个字段。
ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEArFUEBouVr03Wr8qpS2BDSDxi/HCIykWnepfVdafRHoAVcp/YxjiuszjKMRiNXY78yg4P5j9NB1+r0M9OrKkg0yspluWQDLX06EWr3l48+tVHLaCCF+JNJQIuFILvbu+/paKnM3pnCw3WROmJL/o/E75bLNowT5NSIEU2nDbJCvNIslD/VnhdXAoLyqio28McOp2Wie0fJ2x8s1vbLsjdURsr3AUO+KlAoVOgg5Ok7/RZ0ywcWE78IWkIluBQV7I0K5wia2TM+X0I3KvaX1xj5zp18+1X9UeQOEDU10mCZN+mig3Z1qJov1MPS19bMN4BhS5HXDTihW1yW+oRYptG0Q== root@CentOS-1
# ssh-copy-id
$ ssh-copy-id [email protected] # 将本机的 SSH 公钥拷贝给目标主机上的指定用户
-i ~/.ssh/id_rsa.pub # 指定要拷贝的 SSH 公钥
- 执行该命令时,需要先通过目标主机的 SSH 认证,才有拷贝 SSH 公钥的权限。
- 该 SSH 公钥会被拷贝到目标主机的指定用户的 ~/.ssh/authorized_keys 文件中,允许以后本机以该用户的 SSH 私钥登录到目标主机。