转换inetd服务

转换inetd服务

在本系列的上一集中,我介绍了如何将SysV初始化脚本转换为systemd单位文件。在这个故事中,我希望解释如何将inetd服务转换为系统单元。

让我们从一些背景开始。inetd作为经典的Unix服务之一,具有悠久的传统。作为超级服务器,它代表另一个服务在Internet套接字上侦听,然后在传入的连接上激活该服务,从而实现按需套接字激活系统。这使资源有限的Unix机器可以提供各种各样的服务,而无需始终运行进程并为所有进程投入资源。多年以来,inetd的许多独立实现已在Linux发行版中发布。最突出的是基于BSD inetd和xinetd的数据库。尽管inetd曾经默认安装在大多数发行版上,但如今它仅用于很少的选定服务,并且所有公共服务都在启动时无条件运行,

systemd的核心功能之一(也是苹果公司为此而推出的)是套接字激活,这是inetd率先提出的方案,但是在那时却具有不同的重点。系统风格的套接字激活着重于本地套接字(AF_UNIX),而不是太多的Internet套接字(AF_INET),即使两者均受支持。甚至更重要的是,systemd中的套接字激活并不主要与inetd中关键的按需方面有关,而更多地在于提高并行化(套接字激活允许同时启动套接字的客户端和服务器),简单性(因为不需要配置服务之间的显式依赖关系)和健壮性(因为服务可以重新启动或可以崩溃而不会丢失套接字的连接性)。但是,当连接进入时,systemd也可以按需激活服务,

任何类型的套接字激活都需要服务本身的支持。systemd提供了一个非常简单的接口,服务可以实现该接口以围绕sd_listen_fds()构建套接字激活功能这样,它已经是一个非常简单的方案但是,传统的inetd接口更加简单。它只允许将单个套接字传递给已激活的服务:套接字fd只是复制到所产生的进程的STDIN和STDOUT,就已经足够了。为了提供兼容性,systemd可选地提供与进程相同的接口,从而利用已经支持inetd样式套接字激活但尚未支持systemd的本机激活的许多服务。

在继续具体示例之前,让我们看一下使用套接字激活的三种不同方案:

  1. 套接字激活可实现并行化,简单性和鲁棒性:套接字在早期启动期间绑定,并且在启动时立即启动用于服务所有客户端请求的单例服务实例。这对于很有可能频繁和连续使用的所有服务很有用,因此建议尽早并与系统的其余部分并行地启动它们。示例:D-Bus,Syslog。
  2. 按需激活单例服务的套接字在早期启动期间绑定套接字,并对传入流量执行单例服务实例。这对于很少使用的服务很有用,建议在启动时节省资源和时间并延迟激活,直到真正需要它们时才使用。示例:CUPS。
  3. 按需激活每个连接的服务实例的套接字套接字在早期启动期间绑定,并且对于每个传入的连接,将实例化一个新的服务实例,并将连接套接字(而不是监听套接字)传递给它。这对于很少使用的服务以及性能不是很关键的服务(即为每个传入连接生成新服务过程的成本受到限制的服务)很有用。示例:SSH。

这三种方案提供了不同的性能特征。服务完成启动后,前两种方案提供的性能与独立服务相同(即,在没有超级服务器的情况下启动,没有套接字激活的情况),因为侦听套接字已传递给实际服务,此后的代码路径与独立服务的路径相同,并且所有连接的处理过程均与独立服务中的处理路径完全相同。另一方面,第三种方案的性能通常不那么好:由于对于每个连接都需要启动新服务,因此资源成本要高得多。但是,它也具有许多优点:例如,客户端连接可以更好地隔离,并且更容易开发以这种方式激活的服务。

对于systemd来说,第一个方案是重点,但是其他两个方案也受支持。(实际上,我写的博客故事涵盖了有关系统类型的套接字激活的必要代码更改,涉及第二种类型的服务,即CUPS)。inetd主要关注第三种方案,但是第二种方案也受支持。(第一个方案不是。大概是由于对inetd的第三个方案的关注使它以“慢”的声誉而获得了声誉-有点不公平。)

关于背景的内容如此多,现在让我们开始讨论,展示一个inetd服务可以集成到systemd的套接字激活中。我们将专注于SSH,这是一种非常普遍的服务,已被广泛安装和使用,但是对于大多数计算机而言,启动的平均速度可能不会超过平均速度1 / h(通常甚至更少)。遵循上述第三种方案,SSH长期以来一直支持inetd样式的激活。由于它不时启动,并且只能同时使用有限数量的连接,因此它是该方案的很好的选择,因为额外的资源成本可以忽略不计:如果使套接字可激活,SSH基本上只要免费就可以了没有人使用它。一旦有人通过SSH登录,它将开始启动,并且在他或她断开所有资源的连接时,将再次释放它们。让'

这是用于通过经典inetd连接SSH的配置行:

ssh流tcp nowait根目录/ usr / sbin / sshd sshd -i

与xinetd配置片段相同:

服务SSH {
        socket_type =流
        协议= TCP
        等待=否
        用户=根
        服务器= / usr / sbin / sshd
        server_args = -i
}

其中大多数应该很容易理解,因为这两个片段表达的信息非常相同。不明显的部分:未在inetd配置中配置端口号(22),而是通过/ etc / services中的服务数据库间接配置:服务名称用作该数据库中的查找关键字并转换为端口号。通过/ etc / services进行的这种间接访问已成为Unix传统的一部分,尽管这种方式已经越来越不流行了,因此较新的xinetd可以选择允许使用显式端口号进行配置。这里最有趣的设置是不是很直观的命名为nowait(resp。wait = no)选项。它配置服务是否属于第二种(等待)。上面提到的第三种(nowait)方案。最后,-i开关用于在SSH中启用inetd模式。

这些配置片段的系统翻译为以下两个单元。首先:sshd.socket是一个封装有关套接字监听信息的单元:

[单元]
Description =用于每个连接服务器的SSH套接字

[插座]
ListenStream = 22
接受=是

[安装]
WantedBy = sockets.target

其中大多数应该是不言自明的。一些注意事项: Accept = yes对应于nowait它希望能更好地命名,指的是事实,NOWAIT的超级服务器调用()接受监听套接字,其中上 等待,这是执行服务流程的工作。WantedBy = sockets.target用于确保启用此功能后,在正确的时间启动该单元。

这是匹配的服务文件sshd @ .service

[单元]
Description = SSH每个连接服务器

[服务]
ExecStart =-/ usr / sbin / sshd -i
StandardInput =插座

这也应该主要是不言自明的。有趣的是 StandardInput = socket,该选项启用此服务的inetd兼容性。StandardInput =可以用于配置应为此服务连接什么服务的STDIN(有关详细信息,请参见手册页)。通过将其设置为套接字,我们确保如简单的inetd接口中所期望的那样在此处传递连接套接字。请注意,我们不需要在此处显式配置 StandardOutput =,因为默认情况下,来自StandardInput =的设置 如果没有其他配置,则继承。重要的是二进制名称前面的“-”。这样可以确保systemd忘记了每次连接sshd进程的退出状态。通常,systemd将存储异常死亡的所有服务实例的退出状态。SSH有时会异常退出,退出代码为1或类似值,并且我们要确保这不会导致systemd保留以此方式失效的许多先前连接的信息(直到systemctl reset-failed忘记了此信息))。

sshd @ .service是实例化的服务,如本系列的前一部分中所述对于每个传入的连接,systemd将实例化sshd @ .service的新实例,该实例标识符以连接凭据命名。

您可能想知道为什么在inetd服务的systemd配置中需要两个单位文件而不是一个。这样做的原因是为了简化操作,我们要确保活动单元和单元文件之间的关系是显而易见的,而与此同时,我们可以在依赖图中独立地订购套接字单元和服务单元并控制这些单元尽可能独立。(认为​​:这使您可以独立于实例关闭套接字,并分别关闭每个实例。)

现在,让我们看看它在现实生活中是如何工作的。如果将这些文件放到/ etc / systemd / system中,就可以启用套接字并启动它了:

#systemctl启用sshd.socket
ln -s'/etc/systemd/system/sshd.socket''/etc/systemd/system/sockets.target.wants/sshd.socket'
#systemctl启动sshd.socket
#systemctl status sshd.socket
sshd.socket-每个连接服务器的SSH套接字
	  已加载:已加载(/etc/systemd/system/sshd.socket;已启用)
	  活动:自2011年9月26日星期一20:24:31 +0200起活动(监听);14s前
	接受:0;已连接:0
	  CGroup:名称= systemd:/system/sshd.socket

这表明套接字正在侦听,到目前为止尚未建立任何连接(已接受:将显示自套接字启动以来总共建立了多少个连接, 已连接:当前有多少个连接。)

现在,让我们从两个不同的主机连接到此主机,然后查看哪些服务现在处于活动状态:

$ systemctl-完整| grep ssh
sshd@172.31.0.52:22-172.31.0.4:47779.service已加载活动运行的SSH Per-Connection服务器
sshd@172.31.0.52:22-172.31.0.54:52985.service已加载活动运行的SSH Per-Connection服务器
sshd.socket为每个连接服务器加载了活动的侦听SSH套接字

不出所料,对于这两个连接,现在有两个正在运行的服务实例,它们以TCP连接的源地址和目标地址以及端口号命名。(对于AF_UNIX套接字,实例标识符将包含连接客户端的PID和UID。)这使我们可以进行内部自省或杀死特定的sshd实例,以防您要终止特定客户端的会话:

#systemctl kill sshd@172.31.0.52:22-172.31.0.4:47779.service

这可能已经是将inetd服务与systemd挂钩以及之后如何使用它们所需要了解的大部分内容。

在SSH的情况下,对于大多数发行版来说,这可能是一个很好的建议,以便将资源默认情况下保存为这种inetd样式的套接字激活,但同时也向sshd提供一个独立的单元文件,可以选择启用该文件。我很快将针对Fedora中的SSH软件包提交有关此问题的愿望清单错误。

关于xinetd和systemd如何在功能方面进行比较以及xinetd是否被systemd完全淘汰的一些最后说明。简短的答案是systemd没有提供完整的xinetd功能集,并且还没有完全淘汰xinetd。更长的答案稍微复杂一点:如果您查看 xinetd提供众多选项,您会注意到systemd无法比较。例如,systemd不带有内置的echo, timedaytime丢弃服务器,并且永远不会包括这些服务器。不支持TCPMUX,也不支持RPC服务。但是,您还会发现其中大多数与当今的Internet无关,或者已成为其他过时的方式。绝大多数的inetd服务都没有直接利用这些附加功能。实际上,Fedora上提供的所有xinetd服务都不使用这些选项。也就是说,有一些systemd不支持的有用功能,例如IP ACL管理。但是,大多数管理员可能会同意,防火墙是解决此类问题的更好解决方案,最重要的是,systemd通过tcpwrap支持ACL管理,以帮助那些喜欢这种复古技术的人。另一方面,systemd还提供了许多xinetd功能从上面显示的实例的单独控制开始,或者实例的执行上下文的更具表达性的可配置性,它没有提供我相信systemd提供的功能非常全面,几乎没有遗留问题,但是应该为您提供所需的一切。而且,如果systemd无法覆盖某些内容,那么xinetd将始终在那里以填补空白,因为您可以轻松地将其与systemd结合使用对于大多数用途,systemd应该涵盖了必要的内容,并允许您减少构建系统所必需的组件。在某种程度上,systemd可以恢复经典Unix inetd的功能,并将其再次转变为Linux系统的核心。

到此为止。感谢您阅读这篇长篇文章。现在,开始并转换您的服务!更好的是,在上游的单个程序包或您的发行版中执行此操作!

posted @ 2020-11-12 14:00  soso101  阅读(285)  评论(0)    收藏  举报