配置 sshd 大家都很熟悉,主要就是围绕 /etc/ssh/sshd_config 进行配置。而配置 sshd 的端口则是配置 sshd_config 中的 Port。不过在 systemd 环境下,根据服务是由 .socket 文件配置启动还是 .service 文件配置启动的不同,配置端口分别需要配置 sshd.socket 文件或依然是 sshd_config

确认所用 sshd 服务

首先,我们需要确认系统所用的 sshd 服务,是由 sshd.socket 提供的,还是由 sshd.service 提供的。

  • 如果服务由 sshd.socket 提供,配置端口需要配置 sshd.socket 文件;
  • 如果服务由 sshd.service 提供,配置端口则需要配置传统的 sshd_config 文件。

一般系统中所安装的ssh服务都是由 openssh 包提供的,首先我们通过命令

# Ubuntu / Debian 下使用以下命令
dpkg -L openssh-server
# Red Hat / CentOS 下使用以下命令
rpm -ql openssh-server
# Arch Linux 下使用以下命令
pacman -Ql openssh

可以看到 openssh 包提供了如下 systemd 服务文件:

  • CentOS 7 下的输出:

    /usr/lib/systemd/system/sshd-keygen.service
    /usr/lib/systemd/system/sshd.service
    /usr/lib/systemd/system/sshd.socket
    /usr/lib/systemd/system/[email protected]
    
  • Arch Linux 下的输出:

    openssh /usr/lib/systemd/system/sshd.service
    openssh /usr/lib/systemd/system/sshd.socket
    openssh /usr/lib/systemd/system/[email protected]
    openssh /usr/lib/systemd/system/sshdgenkeys.service
    
  • 其他系统下的输出也是类似的。

而其中,在 systemd 环境下,

  • CentOS 7 的 sshd 服务默认是由 sshd.service 文件启动的;
  • Arch Linux 的 sshd 服务默认是由 sshd.socket 文件启动的;
  • 其他系统也可以按照下面介绍的方法来确认服务是如何启动的。

通过 systemctl 确认服务类型

我们可以通过以下命令来分别确认 sshd 服务是由 .service 文件启动,还是由 .socket 文件启动:

# 确认 sshd 服务是否由 .service 文件启动
systemctl status sshd.service

另外,上述命令中的 sshd.service 也可直接替换为 sshd,因为 systemctl 命令默认就假设所输入的参数是一个 .service 文件。

# 确认 sshd 服务是否由 .socket 文件启动
systemctl status sshd.socket

根据实际系统及配置的不同可以分别看到类似如下输出:

● sshd.service - OpenSSH Daemon
   Loaded: loaded (/usr/lib/systemd/system/sshd.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
● sshd.socket
   Loaded: loaded (/usr/lib/systemd/system/sshd.socket; enabled; vendor preset: disabled)
   Active: active (listening) since Sat 2015-12-26 15:23:31 CST; 1h 37min ago
   Listen: [::]:22 (Stream)
 Accepted: 3; Connected: 1

Active: 行中的 activeinactive 分别表示当前服务是否正在运行,也即 sshd 服务是否是由该配置文件启动。此外,如服务是由 .socket 文件配置启动的,还可以在 Listen: 行中看到具体的监听端口;如服务是由 .service 文件配置启动,则具体的监听端口需要在配置文件 sshd_config 中查看。

Loaded: 行中括号内的第一个 enabled 或者 disabled 分别表明了服务是否被默认启用,也就是重新启动系统后,服务是否会自动启动。该状态与服务当前是否正在运行无关。

通过 ss 或 netstat 确认服务类型

此外,我们还可以通过 ss 命令或 netstat 命令还查看当前 sshd 服务是由 .service 文件还是由 .socket 文件启动。

# 使用 ss 命令
sudo ss -tlp
# 或使用 netstat 命令
sudo netstat -tlp

得到如下输出:

LISTEN     0      128        *:ssh        *:*        users:(("sshd",pid=3824,fd=3))

或:

tcp        0      0 0.0.0.0:ssh         0.0.0.0:*           LISTEN      3824/sshd
  • 如在 ssnetstat 的结果中看到开启 ssh 端口的进程是 sshd 的话,则说明服务由 .service 文件启动;
  • 如在 ss 的结果中看到开启 ssh 端口的进程是 systemd ,或在 netstat 的结果中看到开启 ssh 端口的进程是 init 的话,则说明服务由 .socket 文件启动。

修改 sshd.socket 所用端口

如果已经启用 sshd.socket 服务,那么我们会发现 /etc/systemd/system/sockets.target.wants/sshd.socket 文件就会指向 /usr/lib/systemd/system/sshd.socket,而该文件会在更新 openssh 包时被更新,因此如果要修改 sshd.socket 所用的端口,我们不应直接修改该文件,而应先拷贝该文件:

cp /etc/systemd/system/sockets.target.wants/sshd.socket /etc/systemd/system/sshd.socket

再对 /etc/systemd/system/sshd.socket 进行修改,这样即使 openssh 包更新时,配置也能得到保留。

反之,如果直接修改 /etc/systemd/system/sockets.target.wants/sshd.socket,不仅改动会在 openssh 包升级时丢失,还会导致升级时 sshd.socket 由于在运行中被变更而不再工作,无法接受新连接,以至于有在重启系统前被锁在 SSH 外的风险:

sshd.socket: Socket unit configuration has changed while unit has been running, no open socket file descriptor left. The socket unit is not functional until restarted.

当修改配置文件时,需要修改的行如下:

ListenStream=22

将其中的 22 端口改成自己需要的端口,随后用以下命令加载新配置并重启服务:

systemctl daemon-reload
systemctl restart sshd.socket

重启完服务后,建议先在配置的新端口上验证可用后,再断开原先的ssh连接。

切换 sshd 的 socket 服务与 service 服务

如果目前使用的是 sshd.socket 服务,而想切换至 sshd.service 服务,可以执行如下命令:

systemctl disable sshd.socket
systemctl enable sshd.service
systemctl stop sshd.socket; systemctl start sshd.service

如果目前使用的是 sshd.service 服务,而想切换至 sshd.socket 服务,可以执行如下命令:

systemctl disable sshd.service
systemctl enable sshd.socket
systemctl stop sshd.service; systemctl start sshd.socket

socket 服务与 service 服务的异同

说了这么多,那两种启动 sshd 服务的方式到底有什么不同的,为什么需要有新的 socket 服务呢?

首先旧有的方式 sshd.service 模式会在后台保持一个 sshd 的守护进程,每当有 ssh 连接要建立时,就创建一个新进程,比较适合 SSH 下有大量流量的系统;

新的 sshd.socket 方式也是在每次要建立新的ssh连接时生成一个守护进程的实例,不过监听端口则是交给了 systemd 来完成,意味着没有 ssh 连接的时候,也不会有 sshd 守护进程运行,大部分情况下,使用 sshd.socket 服务更为合适。这也与 MacOS 下的行为相一致,默认只监听端口,有连接时才创建进程。

另外,通过使用 .socket 文件来管理需要监听端口的服务,可以直接通过 systemctl 来查看一些网络相关的信息,如监听的端口、目前已经接受的连接数、目前正连接的连接数等。


扩展阅读: