Secure Shell (SSH) 是系统管理员最常用的工具之一。
它对于管理所有服务器和执行日常任务至关重要。
SSH 密钥文件权限
由于 SSH 客户端在连接到不同系统时从 known_hosts 文件中读取数据,因此当前用户需要对该文件具有适当的权限。
如果没有适当的权限,我们可能会看到如下错误:
如果使用密钥进行身份验证(而不是密码),我们将被完全锁定在系统之外,直到我们修复了密钥文件的权限。
运行这些命令应该可以解决问题:
$ chmod 700 ~/.ssh $ chmod 644 ~/.ssh/authorized_keys $ chmod 644 ~/.ssh/known_hosts $ chmod 644 ~/.ssh/config $ chmod 600 ~/.ssh/id_rsa $ chmod 644 ~/.ssh/id_rsa.pub
SSH 服务不工作
我们可以执行的最基本的故障排除是首先验证系统上是否安装了 SSH。
SSH 有客户端版本(用于远程连接到其他系统)和服务器版本(用于接受进入系统的传入连接)。
要查看 SSH 客户端是否正确安装,只需在终端中输入“ssh”即可。
$ ssh
如果系统上安装了 SSH 客户端,我们将看到一些类似上面屏幕截图的输出。
要检查系统是否安装了 SSH 服务器,请尝试启动到系统本身的远程连接:
$ ssh localhost
尝试通过 SSH 连接到 localhost 是查看系统当前是否接受连接的好方法。
在这里,我们得到了可怕的“连接被拒绝”错误消息。
这或者意味着系统上没有安装 SSH 服务器包,或者它可能只是意味着该服务当前没有运行。
让我们尝试检查 SSH 服务的状态:
$ systemctl status sshd
在检查 SSH 守护进程的状态时,系统通知我们找不到该服务。
这意味着我们需要安装它。
我们在这个例子中使用 Ubuntu,所以我们将使用 apt-get 来安装 openssh-server 包。
我们可能需要使用稍微不同的命令,具体取决于我们使用的发行版。
$ sudo apt-get install openssh-server
安装完成后,SSH 服务可能会自动启动。
要检查它是否正在运行,请再次检查状态:
Systemctl 向我们显示 SSH 服务现在正在运行(按“q”退出此屏幕并返回到终端)。
如果 SSH 守护程序尚未在系统上运行,我们可以使用以下命令启动它:
$ systemctl start sshd
IP地址冲突问题
如果系统在网络上有重复的 IP 地址,SSH(以及其他依赖于网络的服务)可能无法正常运行。
检查这总是一件好事,并且使用 host 命令非常简单。
$ host x.x.x.x
host 命令返回有关我们刚刚传递给它的 IP 地址的信息。
使用主机名来确定我们确实在尝试访问正确的系统。
在 Windows 上,我们可以在命令提示符下使用 ping -a 来获得类似的结果:
忘记在启动和解决方案时打开它
每次需要时启动 SSH 服务显然有点烦人,所以如果你想确保你的系统在启动时自动接收 SSH 连接,你可以使用以下命令作为 root 或者 sudo :
$ sudo systemctl enable ssh
运行此命令会告诉系统在每次计算机启动时启动 SSH 服务。
如果我们稍后需要恢复此设置,只需键入相同的命令,但使用“禁用”而不是“启用”。
连接请求积压(收到攻击,导致过多连接)
对服务器进行的所有连接尝试都保存在一个日志文件中。
在对连接或者用户帐户问题进行故障排除时,仔细阅读此日志文件会很有帮助,但我们也可以使用它来确定服务器是否已被连接请求淹没。
使用以下命令检查失败的登录尝试:
$ cat /var/log/auth.log | grep "Failed password"
如果服务器在端口 22 上开放到互联网,那么每 10 分钟左右至少进行几次连接尝试是很常见的,这只是因为机器人不断浏览互联网以攻击弱服务器。
下面是我的一台公共服务器的 auth.log 文件:
为了使服务器免受 SSH 蛮力攻击,我们建议我们安装 CSF 防火墙。
默认情况下,它被配置为停止来自同一 IP 地址的重复 SSH 尝试。
我们可以根据自己的喜好微调这些设置,但默认设置应该适合大多数系统:
$ vi /etc/csf/csf.conf
# [*] 启用 sshd 连接的登录失败检测 LF_SSHD = "5" LF_SSHD_PERM = "1"
LF_SSHD 设置为“5”是阻止 IP 地址之前的最大失败连接数。
LF_SSHD_PERM 设置为“1”使这种阻塞永久有效。
限制这些虚假连接尝试的另一种流行方法是将受信任的 IP 列入白名单并阻止所有其他 IP。
这仅在相同的 IP 始终通过 SSH 连接到服务器时才有效。
要使用 iptables 实现这一点,我们可以使用以下命令:
$ sudo iptables -A INPUT -p tcp -s 10.1.1.1,10.1.1.2 --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
此命令将只允许来自 10.1.1.1 和 10.1.1.2 的 SSH 连接。
我们可以在该命令中根据需要列出任意数量的 IP,或者列出整个子网,例如 10.1.1.0/24.
请注意,我们输入的链策略必须设置为 drop。
使用 ssh -vvv 调试 SSH 连接并检查日志
得到一个像“连接被拒绝”这样的简单错误并不能帮助找出问题所在。
如果我们想查看有关 SSH 连接尝试期间发生的情况的更多信息,可以使用 ssh -v。
$ ssh -v hostname
或者
$ ssh -vvv hostname
在命令中加入三个 v 会进一步增加详细信息。。
防火墙阻止 SSH 端口
要检查的另一件事是操作系统的防火墙。
Ubuntu 和许多其他发行版默认安装了 ufw(简单的防火墙),它通常是向系统快速发出与防火墙相关的命令的最常用方法之一。
要通过 ufw 允许 SSH 通过防火墙,请使用以下命令:
$ sudo ufw allow ssh
ufw 只是 iptables 防火墙的前端,所以如果你更喜欢使用 iptables 命令(或者你甚至没有安装 ufw),这里是 iptables 命令来允许传入的 SSH 连接:
$ sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
如果我们正在运行 firewalld 并且需要允许 SSH 连接,请使用以下命令:
$ firewall-cmd --zone=public --add-service=ssh
如果禁用根登录以及如何启用它
出于安全原因,Linux 几乎从不希望我们在默认情况下以 root 身份执行任何操作。
如果可能,我们应该使用权限较少的帐户,并且仅在必要时才提升为 root。
嗯,在极少数情况下建议这样做,有时我们可能只想直接登录到 root。
SSH 默认配置为拒绝 root 登录。
如果我们尝试以 root 身份登录系统,并且没有进行任何配置以允许这样做,那么我们将只能使用普通用户帐户登录。
我们可以通过编辑 sshd_config 文件将 SSH 服务器配置为允许 root 登录。
使用 nano、vi 或者我们喜欢的其他文本编辑器在此处打开文件:
$ sudo vi /etc/ssh/sshd_config
向下滚动此文件,直到我们看到 PermitRootLogin。
在我们的 Ubuntu 安装中,默认情况下 PermitRootLogin 设置为“prohibit-password”。
这意味着我们可以通过密钥身份验证以 root 身份登录,但不能通过提供 root 密码来登录。
在某些系统上,我们可能会看到它只是说“不”,这意味着不允许任何身份验证方法的 root 登录。
要允许 root 用户通过 SSH 登录,我们可以将此行更改为 yes。
确保删除开头的 # 以便它不再被注释掉。
它需要看起来像这样才能允许 root 登录:
每当我们对此文件进行更改时,我们都必须重新启动 SSH 服务才能使其生效:
$ sudo systemctl restart ssh
警告:在编辑 SSH 配置文件以允许使用密码进行 root 登录之前,请确保我们设置了非常安全的 root 密码。
有机器人不断扫描互联网以寻找服务器以进行 SSH 连接,并且它们会尝试登录 root 帐户而不是其他任何帐户。
这就是为什么 SSH 默认配置为不允许通过密码进行任何类型的 root 登录。
我们将在下一节展示如何查看这些连接尝试,并展示一个真实的示例,说明如何不断对我们的实时生产服务器执行攻击。
SSH 端口没有打开
如果我们在确认 SSH 服务已启动并运行后仍然遇到连接问题,则 SSH 连接可能在它有机会到达系统之前就被阻止了——就像在你的路由器上一样。
路由器通常会阻止端口 22 上的传入 SSH 连接。
我们可以使用任何端口转发测试器来查看该端口是否对 Internet 可见。
如果不是,则连接可能在路由器或者系统上的防火墙处被阻止,我们将在接下来进行讨论。
要将路由器配置为允许传入 SSH 连接,我们需要查阅制造商关于特定路由器型号上的端口转发的说明。
SELinux 阻止 SSH
一些发行版,如 Red Hat 和基于它的发行版,运行 SELinux(安全增强 Linux)。
SELinux 应该自动配置为在端口 22 上侦听 SSH,我们可以使用以下命令进行验证:
$ semanage port -l | grep ssh
在上面的屏幕截图中,semanage 命令向我们显示它正在侦听端口 22(SSH 的默认端口)上的传入 SSH 连接。
除非我们尝试更改系统运行 SSH 服务的端口,否则我们不需要对此进行任何另外配置。
如果我们需要将 SELinux 配置为在不同端口上侦听 SSH,可以使用以下命令:
$ semanage port -a -t ssh_port_t -p tcp 1234
将“1234”替换为我们配置 SSH 运行的新端口。
进行这些更改后,请务必重新启动 SSH 服务:
$ systemctl restart sshd
重新安装后更改公钥
SSH 服务为每个系统生成一个唯一的公钥。
这样做是为了让连接到系统的客户端有办法确保它们连接到预期的服务器。
例如,如果我们通常连接的服务器已切换 IP 地址怎么办?
我们不想将登录信息输入到假定以前 IP 地址的错误系统中。
如果发生这种情况, SSH 客户端应该警告我们公钥已更改。
$HOME/.ssh 路径包含系统之前连接到的主机的所有密钥。
我们可以像这样查看 known_hosts 文件:
$ cat ~/.ssh/known_hosts
我们的文件中只有一台主机,但即使这样对我们来说也只是胡言乱语。
与其自己编辑此文件,不如使用 ssh-keygen 命令从文件中删除特定的主机密钥。
$ ssh-keygen -r x.x.x.x
或者
$ ssh-keygen -r example.com
使用服务器的 IP 地址或者主机名。
在我们的例子中,我们文件中的键只是来自 localhost,所以我们像这样删除它: