我试图理解一个 sshd 配置,我认为该配置不应该起作用,但确实有效。前提来自于我正在开发的一个生产系统;不过,我为了自己的测试而简化了它。由于我无法解释为什么这个简单的示例有效,因此我也无法解释为什么更复杂的迭代有效。
我有两台服务器,用户分别为 Auser、Buser 和 Cuser。
我的客户端计算机的 IP 地址为 192.168.10.1
我的服务器有一个 sshd 配置,如下所示:
AllowGroups Cuser
Match Address 192.168.10.1
AllowGroups Cuser Buser
Match Address 192.168.10.1
AllowGroups Cuser Auser
根据 sshd_config(5) 的手册页
Match 引入条件块。如果匹配行上的所有条件都满足,则以下行中的关键字将覆盖配置文件全局部分中设置的关键字,直到另一个匹配行或文件末尾。如果某个关键字出现在多个满足条件的 Match 块中,则仅应用该关键字的第一个实例。
我的解释是,从客户端(192.168.10.1),只应允许 Cuser 和 Buser ssh 到服务器。
但是,当我对此进行测试时,所有三个用户:Auser、Buser 和 Cuser 都具有访问权限。如果我查看服务器的 sshd 日志,我会看到每个匹配块的应用位置:
Jul 25 10:48:59 server1 sshd[3525]: debug3: Trying to reverse map address 192.168.10.1.
Jul 25 10:48:59 server1 sshd[3525]: debug2: parse_server_config: config reprocess config len 854
Jul 25 10:48:59 server1 sshd[3525]: debug3: checking match for 'Address 192.168.10.1' user buser host client addr 192.168.10.1 laddr 192.168.10.4 lport 22
Jul 25 10:48:59 server1 sshd[3525]: debug1: connection from 192.168.10.1 matched 'Address 192.168.10.1' at line 138
Jul 25 10:48:59 server1 sshd[3525]: debug3: match found
Jul 25 10:48:59 server1 sshd[3525]: debug3: reprocess config:139 setting AllowGroups cuser buser
Jul 25 10:48:59 server1 sshd[3525]: debug3: checking match for 'Address 192.168.10.1' user buser host fedora addr 192.168.10.1 laddr 192.168.10.4 lport 22
Jul 25 10:48:59 server1 sshd[3525]: debug1: connection from 192.168.10.1 matched 'Address 192.168.10.1' at line 140
Jul 25 10:48:59 server1 sshd[3525]: debug3: match found
Jul 25 10:48:59 server1 sshd[3525]: debug3: reprocess config:141 setting AllowGroups cuser auser
因此,有趣的是,从我对手册页的解释来看,我预计只会应用第一个“reprocess config:139”行,因为它是AllowGroups关键字的第一个实例。但是,查看日志,由于我看到“重新处理配置:141 设置AllowGroups cuser auser”,我可能只希望应用第二个实例。
然而,这些解释似乎都不正确,因为所有三个用户都能够连接。
因此,通过一些额外的测试,我将 sshd_config 更改为如下所示:
AllowGroups Cuser
Match Address 192.168.10.1
AllowGroups Cuser Buser
Match Address 192.168.10.1
AllowGroups Auser
和
AllowGroups Cuser
Match Address 192.168.10.1
AllowGroups Auser
Match Address 192.168.10.1
AllowGroups Cuser Buser
所有三个用户仍然可以登录。
还有一个最终测试
AllowGroups Cuser
Match Address 192.168.10.1
AllowGroups Buser
Match Address 192.168.10.1
AllowGroups Auser
最后,只有 Auser 和 Buser 有权访问。
几乎就像第一个匹配块将覆盖任何默认设置,但后续匹配块会附加到任何先前的匹配块。
答案1
我无法像我希望的那样仔细地追踪代码,但我想我知道发生了什么。一个非常重要的线索是解析命令行参数的例程的源代码文件、sshd_config 文件及其包含的文件:
/*
* The strategy for the Match blocks is that the config file is parsed twice.
*
* The first time is at startup. activep is initialized to 1 and the
* directives in the global context are processed and acted on. Hitting a
* Match directive unsets activep and the directives inside the block are
* checked for syntax only.
*
* The second time is after a connection has been established but before
* authentication. activep is initialized to 2 and global config directives
* are ignored since they have already been processed. If the criteria in a
* Match block is met, activep is set and the subsequent directives
* processed and actioned until EOF or another Match block unsets it. Any
* options set are copied into the main server config.
*/
本质上有一个例程通过三个解析阶段读取配置关键字和参数(如Compression no
和AllowGroups foo bar baz
):命令行参数、跳过块的第一遍Match
以及读取块的第二遍Match
。有几个标志变量可以跟踪解析所处的阶段 - 一个用于命令行解析,另一个用于第一个和第二个文件传递。
AllowGroups、DenyGroups、AllowUsers 和 DenyUsers 是采用多个值而不是单个值的参数。因此,该例程解析出该行的参数列表,并将每个参数附加到一个字符串数组中。虽然第一个和第二个文件传递有一个标志,但没有“这是一个新的匹配块”的标志。
这意味着当解析第一个 Match 块(第二遍开始)并且它包含 AllowGroups 参数时,现有值列表(在 Match 块之前)将被擦除并替换为 Match 块中的新参数。然而,当第二个 Match 块也匹配并被解析时,没有任何信号通知例程再次擦除列表。如果AllowGroups 是单值参数,则每个新实例都将被忽略,而优先于第一个实例。但是,由于它是多值参数,因此新参数将被附加到列表中。这是当你发现它时让你感到困惑的行为。 (我也是)
我的猜测是解析例程的作者没有设想像您的测试服务器那样的配置 - 具有多个设置相同多值参数的有效匹配块。更典型的情况是只有一个 Match 块会成功并被解析,因为它们匹配不同的东西。或者如果多个块成功,则多个块的原因是因为每个块设置了不同的参数。
在几天前的评论中,我提到这四个参数的工作方式并不直观。尽管这不是这个问题的直接答案,但我会在这里添加我的发现(为了后代)。
连接建立并完成第二遍配置解析后,代码会检查允许/拒绝组以查看哪些组适用。它们按以下顺序检查:DenyUsers、AllowUsers、DenyGroups 和AllowGroups。允许/拒绝的测试和决定的工作方式类似于以下伪代码:
if DenyUsers has > 0 items and user is one of them
deny
if AllowUsers has > 0 items and user is not one of them
deny
if DenyGroups has > 0 items and user is member of one of them
deny
if AllowGroups has > 0 items and user is not a member of any of them
deny
allow
一旦做出“拒绝”决定,例程就会停止进一步检查。因此“AllowUsers”列表不能以直观的方式工作。如果您将任何名称放入“AllowUsers”列表中,则所有其他用户(不在列表中)都会被拒绝。其他用户是否在“AllowGroups”列表中并不重要,因为代码在查阅“AllowGroups”之前会停止检查。 “AllowGroups”列表中的任何组都会导致不属于这些组的用户被拒绝,即使他们列在“AllowUsers”中也是如此。