OpenSSH服务器sshd是高度可配置的,允许你限制谁可以连接到服务器,这些用户可以采取什么行动,以及它允许什么行动。每个现代类Unix操作系统都安装了sshd作为基本操作系统的一部分。
从客户端测试服务器是否运行可访问的SSH守护进程的最简单方法是尝试登录服务器。
如果客户端能连上服务器,那说明服务器的sshd正在运行。但如果客户端无法连上服务器,并不意味着服务器的sshd有问题,也许是中间有一台数据包过滤器,也许是客户端的问题。
SSH通常在TCP的22端口上运行,用netcat查看是否可以访问守护进程:
xxxxxxxxxx
$ nc -v devio.us 22
Connection to devio.us 22 port [tcp/ssh] succeeded!
SSH-2.0-OpenSSH_7.0
^C
当你通过原始TCP连接时,sshd会返回一个横幅(banner),显示SSH协议的版本、SSH服务器软件和软件版本。此主机使用OpenSSH7.0版提供的SSH协议2。
如果你没有得到类似的结果,也许sshd没有运行,或者遇到了数据包过滤器。
从服务器检查sshd进程是否在运行:
xxxxxxxxxx
$ ps ax | grep sshd
626 - Is 0:00.03 /usr/sbin/sshd
31960 - Is 0:00.38 sshd: mwlucas [priv] (sshd)
44387 - S 0:05.75 sshd: mwlucas@pts/0 (sshd)
此主机显示三个sshd进程。第一个PID 626显示了普通的旧/usr/sbin/sshd。它是监听TCP端口22的主进程。
第二个进程PID 31960是处理我与主机的SSH连接的特权进程。第三个PID 44387是处理登录会话的无特权子进程。OpenSSH通过权限分离提高了安全性,本章结尾的“保护SSH服务器”中对此进行讨论。如果有人故意禁用了权限分离,并且运行sshd不安全,你将看不到无权限的会话。
如果sshd没有运行,请通过操作系统配置工具启用它。
大多数操作系统将sshd作为独立服务器运行,不需要任何命令行参数。配置sshd的常用方法是通过/etc/ssh/sshd_config中的关键字。不过,在开始处理该文件中的更改之前,你应该知道如何测试和调试它们。
OpenSSH是调试sshd配置变得尽可能简单。无论是否调试,你都必须是root才能运行sshd。最简单的调试方法是备用配置文件、备用端口和调试模式。
假设你想编辑sshd_config,但需要确保更改按预期工作。-f命令行参数告诉sshd使用备用配置文件:
xxxxxxxxxx
# /usr/sbin/sshd -f sshd_config.test
注意,我使用sshd的完整路径执行了此调试配置。OpenSSH在sshd在接受连接时会自动重新执行,它需要完整的路径才能执行。
如果你没有给出完整的路径,你会得到一个错误,比如“sshd re-exec requires execution with an absolute path."
只有一个sshd实例可以连接到特定的TCP端口。你的测试sshd进程可能无法启动,因为它无法绑定到端口22.
你可以编辑sshd_config.test来为你的测试进程分配另一个端口,但在将其转移到生产环境时,你必须重新编辑该文件。相反,重写配置的TCP端口,并使用-p参数分配一个新端口:
xxxxxxxxxx
# /usr/sbin/sshd -f sshd_config.test -p 2022
测试进程现在正在2022端口上监听。注意,-p不能覆盖将sshd绑定到端口和地址的LinstenAddress关键字,请参阅本章后面的”网络选项“。
通过在命令行上设置备用配置文件和端口,你可以测试新配置、批准它并将其投入生产,确信在进行最终的未经测试的更改时没有破坏文件。如论如何,请保存你的原始sshd_config,以防你的更改导致测试未暴露的问题。
完成测试后,记得终止测试sshd进程。
也许你想做一个小的改变,认为你不需要进行全面的测试。您可以要求sshd(8)使用-t标志验证配置文件和所有密钥文件:
xxxxxxxxxx
# sshd -t
/etc/ssh/sshd_config: line 112: Bad configuration option: ExposeAuthInfo
/etc/ssh/sshd_config: terminating, 1 bad configuration options
此主机上安装sshd版本太旧,无法支持ExposeAuthInfo关键字,或者操作系统打包程序故意删除了该选项。
-d标志告诉sshd在前台调试模式下运行,而不与控制终端分离。
在调试模式下,sshd只能处理一个登录请求——不,一次不能处理一个请求。它处理一次登录或登录尝试,然后退出。
不要在生产中这样做;在备用端口上运行它。
调试会实时显示sshd进程所做的一切,就像这样:
xxxxxxxxxx
# /usr/sbin/sshd -p 2022 -d
debug1: sshd version OpenSSH_7.5, OpenSSL 1.0.2l-freebsd 25 May 2017
debug1: private host key #0: ssh-rsa
SHA256:N+faE/OyKhlho8MR8Vw3uhdo75aiuhYotnP/gOOe82E
debug1: private host key #1: ecdsa-sha2-nistp256 SHA256:Q1buYGtWowrN1/8g/EaTEMQr+69h+/Pai3xI4LXN0c8
debug1: private host key #2: ssh-ed25519 SHA256:0TCTf0jZUxzu8dahNrLmuKu19T0BkruI4e3mPOjVInE
debug1: rexec_argv[0]='/usr/sbin/sshd'
debug1: rexec_argv[1]='-p'
debug1: rexec_argv[2]='2022'
debug1: rexec_argv[3]='-d'
debug1: Bind to port 2022 on ::.
debug1: Server TCP RWIN socket size: 65536
Server listening on :: port 2022.
debug1: Bind to port 2022 on 0.0.0.0.
debug1: Server TCP RWIN socket size: 65536
Server listening on 0.0.0.0 port 2022.
调试会话从您的sshd(8)版本的标识信息开始——在本例中,是使用OpenSSL 1.0.2l构建的OpenSSH 7.5,作为FreeBSD的一部分。然后,我们看到使用RSA2、ECDSA和ED25519加载了三个私钥。守护进程解析其参数并绑定到端口。
如果守护进程无法启动,它会在这里非常清楚地说明原因。你可能需要阅读手册页或进行一些互联网搜索来找出错误的含义,但你会知道确切的问题。
使用SSH客户端连接到此服务器,当服务器和客户端就加密协议达成一致、用户尝试进行身份验证以及协商X转发等各种SSH功能时,您将获得数百行调试输出。我不会引导您完成这样的会话,因为输出因客户端、身份验证方法以及请求和提供的SSH功能而异。
如果SSH有问题,请在调试模式下运行服务器,连接客户端,然后读取输出。大多数情况下,sshd会准确地告诉你问题所在。
调试完成后,退出客户端。sshd(8)进程将自行清理并退出。您还可以通过点击CTRL-C来随意终止sshd并抛出客户端。
如果单个-d不能提供足够的细节,请添加多个倍数以增加详细程度。运行/usr/sbin/sshd-dd应该会打消你的好奇心。如果没有,添加更多的-d,直到你不再好奇。
本书的配置基于OpenSSh 7.6编写。FreeBSD14.1是OpenSSH 9.7版
HostKey关键字给出了保护私钥的文件的完整路径。每种支持的加密算法都使用单独的文件:
xxxxxxxxxx
HostKey /usr/local/etc/ssh/ssh_host_rsa_key
HostKey /usr/local/etc/ssh/ssh_host_ecdsa_key
HostKey /usr/local/etc/ssh/ssh_host_ed25519_key
默认文件以其包含的密钥类型命名。文件ssh_host_rsa_key包含一个rsa密钥,ssh_host_ed25519_key是一个ed25519密钥,以此类推。这不是强制性的——OpenSSH会找出文件中的密钥类型,并在适当的情况下加载它——但这绝对是最佳实践。将RSA密钥放在以ED25519命名的文件中会让所有人感到困惑。
不同的操作系统对丢失的关键文件的处理方式不同。BSD风格和基于RedHat的系统会自动创建丢失的密钥文件。许多Linux系统要求系统管理员手动创建丢失的密钥文件,但将密钥创建到其常用的系统管理工具中。例如,当你运行dpkg-reconfigure openssh-server时,基于Debian的系统会创建丢失的密钥文件。
第七章”SSH密钥“介绍了使用OpenSSH的本机工具创建主机密钥。
你可以控制sshd如何使用网络,从IP版本到一直到TCP端口:
xxxxxxxxxx
Port 22
AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::
Port关键字控制sshd使用的TCP端口。互联网标准要求SSH在端口22上运行。一些组织使用不同的SSH端口来提高安全性。
在不寻常的端口上运行SSH实际上并不能帮助保护SSH,但它会减少SSH破解蠕虫的登录尝试次数,如本章后面的”保护SSH服务器“所述。它还可以让你避开特别无效的防火墙。用-p覆盖命令行上的Port关键字。
AddressFamily是指sshd使用的TCP/IP版本。若仅使用IPv4,请将其设置inet;若仅使用IPv6,请将其设置为inet6。默认为any,表示无论哪种协议都可以。一些操作系统修补sshd以支持非TCP/IP协议,如流控制传输协议(Stream Control Transmission Protocol——SCTP)。
许多主机都具有多个IP地址,默认情况下,sshd会监听所有这些设备上的传入请求。如果要限制sshd附加到的IP地址,请使用ListenAddress关键字。0.0.0.0表示所有IPv4的地址,而::则表示所有IPv6地址。每个ListenAddress都有一个IP地址作为参数,但你可以根据需要使用任意多个ListenAddress关键字。明确列出你希望SSH服务器接收连接的每个IP地址。
如果一个主机有许多IP地址,而你只想阻止其中几个的SSH访问,建议使用数据包过滤阻止流量,这比使用许多ListenAddress语句更容易。
你还可以通过在ListenAddress语句中指定端口,使用ListenAddress在特定IP地址上添加其他端口,比如:
xxxxxxxxxx
ListenAddress 0.0.0.0
ListenAddress 192.0.2.8:2222
我们的第一个ListenAddress,0.0.0.0,告诉sshd监听这台机器上的所有地址,默认端口22。因此我们将在所有地址上获得端口22。第二个ListenAddress使sshd也监听地址192.168.2.8上的端口2222上的连接。每个地址都可以有自己的ListenAddress语句。
xxxxxxxxxx
ListenAddress 192.0.2.8:2222
ListenAddress 192.0.2.9:25
ListenAddress 192.0.2.10:80
三个不同的地址,每个地址都有不同的端口。请注意,让sshd监听SMTP和HTTP端口是不明智地,但OpenSSH的设计并不是为了防止你做通常不明智的事情。如果你被困住一个幼稚的防火墙后面,除了端口80和443外,什么都阻止,那么在这些端口上运行sshd可以让你避开防火墙。
许多系统管理员希望用户在登录前向其显示一条消息。这被称为横幅(banner)。
SSH协议不要求客户端显示横幅。服务器可以提供横幅,但你不能保证用户会看到它。ssh和PuTTY都会显示横幅。
将关键字Banner设置为文件的完整路径:
xxxxxxxxxx
Banner /etc/ssh/banner
请注意,如果横幅确实有效,它可能会干扰通过SSH运行的自动化流程。
在某些地方,横幅可以作为对入侵者的法律通知。但作者从未听说过谁通过使用这样的横幅警告被成功起诉,但这是法律。
如果用户使用公钥进行身份验证,并且客户端确实显示了横幅,则登录将继续。在登录完成之前,没有人会看到你的法律部门关于登录主机的措辞精细的警告。
你可以可靠地显示当天的系统消息/etc/motd。不过,此消息在客户端经过身份验证后才会出现,因此它可能不符合你的需求。默认情况下,关键字PrintMotd设置为yes,但你可以将其关闭:
xxxxxxxxxx
PrintMotd yes
在使用可插拔身份验证模块(Pluggable Authentication Modules——PAM)的系统上,PAM模块可能负责打印/etc/motd。如果你在启用或禁用/etc/motd的显示时遇到问题,请检查你的PAM配置。
用户登录后,sshd会打印用户上次登录的时间以及登录的位置。要关闭此功能,请将PrintLastLog设置为no:
xxxxxxxxxx
PrintLastLog yes
虽然这似乎没有必要,但我强烈建议让PrintLastLog保持打开状态。如果用户看到他们之前的登录来自国外或在荒谬的时间,可以提醒管理员注意入侵。
在默认的OpenSSH安装中,用户可以在2分钟内尝试单个SSH会话中登录6次。你应该在几乎所有地方使用公钥身份验证,但即使是有密码的用户也应该能够在20秒内错误地键入密码。
你可以更改尝试的时间和次数。
LoginGradeTime关键字控制sshd给用户多长时间进行身份验证。如果会话连接到sshd达到设定时长却没有成功进行身份验证,则连接终止。此关键字的值可以使用s表示秒、m表示分钟、h表示小时,例如:
xxxxxxxxxx
LoginGraceTime 2m
你还可以控制用户在使用MaxAuthTries的单个连接中尝试身份验证的次数。默认为6次:
xxxxxxxxxx
MaxAuthTries 6
如果用户在单个会话中允许的尝试达到MaxAuthTries设置的值的一半失败时,sshd会记录一次失败。身份验证尝试包括公钥身份验证和密码。MaxAuthTries失败后,用户必须启动新的SSH会话并重试。
IP地址的所有者控制该地址的反向DNS。控制其IP地址反向DNS的入侵者可以将明显的主机名更改为公司内部的某个名称。为了防止这种攻击,sshd可以验证针对转发DNS条目的连接尝试:
xxxxxxxxxx
UseDNS no
当此值设置为yes时,每次客户端连接时,sshd都会查找源IP的主机名,然后查找主机名的IP地址。如果DNS名称不匹配,sshd将拒绝连接。
假设入侵者控制了其IP地址192.0.2.99的反向DNS。他在你的组织中为他提供了一个主机名,如dhcp12.mwl.io,并连接到你的SSH服务器。你的SSH服务器向其DNS服务器请求dhcp12.mwl.io的IP地址。如果该DNS条目不存在,或指向了其他IP,sshd将拒绝连接。
如果DNS失败,sshd会在允许连接之前等待完整的DNS超时。
UseDNS要求你的DNS整洁、连贯且正确。看起来合理,但实际使用会遇到很多问题,比如入侵者可以污染服务器的DNS缓存,DNS检查将无济于事;如果你是家庭用户,你的ISP可能会控制你连接上的反向DNS。
此外,DNS检查可能会增加系统负载。如果你同时为数百或数千名SSH用户提供服务,那么负载可能会更大。当DNS失败时,失败的DNS检查将减慢所有SSH登录的速度。
最后,许多IPv6站点还没有配置反向DNS,在可预见的未来也不会配置。
综上,不建议启用UseDNS。
告诉sshd在哪里用PidFile关键字存放其进程ID文件。不要乱动。许多管理工具愚蠢地使用PID文件。
xxxxxxxxxx
PidFile /var/run/sshd.pid
此文件是在sshd降低其权限之前编写的,因此它可以由root拥有。如果要禁止写入PID文件,可将PidFile设置为none。
sshd进程通过syslogd进行日志记录,默认为AUTH功能和INFO级别。使用SyslogFacility和LogLevel关键字控制这些设置:
xxxxxxxxxx
SyslogFacility Auth
LogLevel INFO
SyslogFacility关键字接收任何syslog设置。有关设置列表,可查看syslogd的文档。
syslogd不仅使用LogLevel来确定向哪里发送日志消息,sshd还使用它来确定向syslogd发送什么:
你可能会在配置中找到关键字Cipher和Mac。它们不会出现在OpenSSH提供的sshd_config中,但一些操作系统会添加它们。这些设置允许你更改服务器支持的加密方法。
不要乱搞这些设置,只会伤到你自己。
某些组织,最常见的是政府,只要求使用经过批准的加密算法。最著名的是美国的FIPS标准。这些组织有非常具体的文件,要求如何配置SSH以符合要求。
OpenSSH通过启动一个单独的进程来处理每个传入的连接,避免了线程编程的麻烦。对运行此类程序的主机的常见拒绝服务攻击是启动一大堆客户端连接,直到服务器耗尽所有资源并崩溃。OpenSSH通过MaxStartups选项避这个问题。
MaxStartups允许你设置多个与SSH守护进程同时进行的未经身份验证的连接。一旦有这么多连接试图进行身份验证,sshd将不会接受另一个连接,直到现有连接失败或现有未经身份验证的连接的LoginGraceTime到期。像10这样的简单值可以保护服务器,但不允许你登录以尝试防御正在进行的攻击。
更好的选择是使用随机早期丢弃(Random Early Drop——RED),这是一种长期被网络工程师用来避免拥塞的协议。DOS攻击并不完全是网络拥塞,但它与网络拥塞有一系列共同特征。RED通过设置节流限制来工作。一旦传入连接超过下限,sshd就会给每个后续传入连接一个被完全拒绝的机会。拒绝连接的可能性会增加,直到未经身份验证的连接数量达到上限,此时所有连接都会被拒绝。使用RED意味着攻击者需要向SSH服务器投入大量资源,以确保系统管理员无法进入。这并没有是攻击变得不那么烦人,但它确实未系统管理员(和合法用户)在攻击期间提供了登录的机会。
通过指定下限、拒绝连接的初始机会和上限阈值,为sshd配置RED。默认值为10、30、100。
xxxxxxxxxx
MaxStartups 10:30:100
这意味着sshd最多可以同时接受10个未经身份验证的连接。第11个同时未经身份验证的连接有30%的可能性被拒绝。连接被拒绝的几率线性增加,直到达到100的上限阈值,此时所有连接都被拒绝。
使用RED意味着,如果你在DOS攻击期间继续尝试连接,你有机会能登录进去。
本章末尾的“保护SSH服务器”中详细讨论了如何防御sshd。
许多网络应用程序依赖于底层操作系统的用户帐户。人们通过网页或专有客户端使用应用程序,但从未真正通过SSH连接到主机。
OpenSSH通过DenyUsers、AllowUsers、DenyGroups和AllowGroups选项支持用户限制。
这些选项采用逗号分隔的用户或组列表作为参数,并安装特定顺序进行处理。第一次匹配成功后忽略后续匹配。
此外,AllowUsers或AllowGroups条目的存在意味着没有其他人可以登录。系统拒绝所有未明确允许的人通过SSH登录。
这些限制在第一场比赛中起作用。语句按顺序处理,当用户匹配规则时,规则立即应用,处理停止。
以下一些示例中,我们的主机有四个用户:backup、mwlucas、pkdick和jgballard,他们分组如下:
xxxxxxxxxx
wheel: mwlucas
staff: mwlucas, pkdick, jgballard
support: pkdick, mwlucas
billing: jgballard
计费应用程序需要系统帐户,但用户不需要通过SSH访问。如果我只想阻止计费部门的用户通过SSH登录,我可以使用DenyUsers:
xxxxxxxxxx
DenyUsers jgballard
所有未列出的用户仍然具有SSH访问权限。不过,当我从该部门添加另一个用户时,我必须明确地将他们添加到DenyUsers中。阻止群组访问会更好:
xxxxxxxxxx
DenyGroups billing
通过这一条声明,我可以将用户添加到billing组,他们就无法登录SSH了。
AllowGroups语句的存在意味着只有该组的成员才能登录。在BSD系统上,wheel是系统管理员的组。要只允许系统管理员通过SSH登录,可使用AllowGroups:
xxxxxxxxxx
AllowGroups wheel
wheel组中的任何人都可以登录。虽然我没有明确禁止其他人登录,但用户backup、pkdick和jgballard不在wheel组,所以他们不能登录。
我是wheel组的唯一成员,我可以明确地列出自己:
xxxxxxxxxx
AllowUsers mwlucas
当我为新系统管理员创建了帐户,并将他们添加到我所有计算机上的AllowUsers语句中,这可能会发生疏漏,尽可能使用组。
支持团队可以访问其他主机。我有一个特殊的系统,禁止某人登录。在这里,我阻止了该用户,但允许该组登录:
xxxxxxxxxx
DenyUsers pkdick
AllowGroups support
这表明“第一场比赛获胜”。用户pkdick立即被拒绝,该决定是最终决定。
其他用户可以继续执行AllowGroups语句。
一些应用程序,如配置正确的rsync,需要具有SSH访问权限的帐户。这需要一个具有公钥身份验证的用户帐户(第七章:SSH密钥)。这些帐户可能很危险。虽然你可以限制用户在使用密钥进行身份验证时可以运行的帐户,但你不希望来自随机主机的rsync连接,也不希望具有shell访问权限的用户能够通过编辑他拥有的文件来绕过限制。你可以使用这些Allow和Deny选项,通过在用户后面添加@和IP地址来限制用户的来源:
xxxxxxxxxx
AllowUsers backup@192.0.2.0/24
AllowGroups support
support组中的用户可以从任何地方登录,用户backup可以从IP在192.0.2.0和192.0.2.255之间的任何主机登录。所有其他用户都被拒绝。 通过合理的组成员资格和周到的允许和拒绝选项,您几乎可以以任何需要的方式限制登录访问。如有疑问,请给予帐户最低级别的权限,让用户和程序完成所需的任务。
有时,你似乎必须允许用户、系统管理员或应用程序以root身份通过SSH登录系统。
在几乎所有的环境中,这都是一个非常糟糕的主意。
当用户必须以普通用户身份登录,然后更改为root时,系统会记录用户的帐户,提供责任和归属。
以root身份登录会破坏审计跟踪。许多服务器程序最初由root启动,使用户帐户友好的环境更改可能会传播到这些程序的环境中,从而中断服务。
如果用户需要root级别的访问权限,则总是有su,或者sudo,或者pfexec,或者任何数量的权限管理工具。
基于SSH的编排系统,如Ansible,支持所有这些程序。特别是sudo可以配置为通过SSH代理进行身份验证,这样用户的凭据就永远不会暴露给服务器。
某些环境,特别是大型的基于云的服务器场,被设计为不仅可以以root身份登录,而且更可取。这些环境需要公钥身份验证,并记录用于验证每个会话的密钥。本书将在第十四章“证书颁发机构”中介绍设置。
OpenSSH使用PermitRootLogin关键字控制以root身份的直接登录。默认情况下,如果使用公钥身份验证,sshd允许直接root登录。
xxxxxxxxxx
PermitRootLogin prohibit-password
prohibit-password(禁止密码)选项与较旧的选项相同,但without-password选项令人困惑。
用户可以以root身份登录,只要他们不使用密码。一旦进入公钥身份验证,就没有任何东西禁止用户将他们的密钥添加到允许使用root帐户的密钥列表中。
我们建议不要使用prohibit-password。
将PermitRootLogin设置为no不允许root用户直接登录。大多数操作系统默认设置此选项。
如果必须允许远程root登录,请考虑将PermitRootLogin设置为forced-commands-only。第十二章讨论了ForceCommand选项,允许你将必须以root身份运行的自动化任务限制为仅执行某些命令。
通过SSH以root身份登录几乎总是意味着你解决了错误的问题。退一步,寻找其他方法来实现你的真正目标。
sshd_config中的某些关键字可以使用表示某些变量的标记和符号。
令牌(token)使这些关键字更加灵活。
当我们讨论可以使用令牌的关键字时,我们将讨论令牌的使用,但从一开始,你就需要一眼能认出它们。在下一节中,我们将在构建chroot时使用令牌,然后贯穿本书。
所有令牌都以百分号(%)开头。最简单的标记是%%,它代表一个实际的百分号。如果你的文件路径中有%,可以使用这种方式。
大多数其他令牌仅在非常特殊的情况下使用,即,使用不太常见的函数。sshd_config(5)手册页列出了所有令牌可供参考。
有时用户需要访问命令提示符或特定程序,但你不希望用户访问其主目录之外的任何内容。
用户无法逃脱的目录称为chroot。chroot对SFTP也很有用,在第六章中介绍。
OpenSSH支持使用ChrootDirectory选项对用户进行chroot操作:
xxxxxxxxxx
ChrootDirectory none
默认情况下,sshd不chroot用户。
chroot用户无法访问chroot之外的任何内容。你创建的任何chroot都不会有设备节点、shell或其他程序,除非你将它们放置在那里。
当你的受限用户登录时,sshd将无法找到shell或主目录,并立即断开它们的连接。要授予chroot用户shell访问权限,你必须至少在chroot目录上设置权限,为被监禁的用户创建主目录,创建设备节点,并安装shell。
只有当用户需要shell访问权限时,才需要填充chroot。如果用户仅通过SFTP获得文件副本访问权限,则第六章中讨论的ForceCommand关键字比填充的chroot更可取。
chroot目录必须由root拥有,并且不可由受限用户写入,就像你不允许非特权用户写入主机的根目录一样。如果受限用户可以写入chroot目录,sshd将不允许他们登录。
用户的主目录(如/etc/passwd中所示)预计在chroot中可用。如果用户pkdick的主目录列为/home/pkdick,并且他被chroot到/usr/prisonroot,则必须创建目录/usr/prisonroot/home/pkdick。这个目录应该由用户拥有,就像普通的主目录一样,并且应该包含任何必要的点文件(疑似.shrc之类的配置文件?)。
在chroot中创建一个设备节点目录。如果chroot目录为/usr/prisonroot,则需要/usr/prisonroot/dev。
现在,你需要用设备节点填充它。chroot不需要完整的设备节点,但大多数chroot应用程序至少需要/dev/random、/dev/stdin、/dev/stdout、/dev/stderr、/dev/tty和/dev/zeor。
创建设备节点的方法因操作系统而异。OpenBSD和许多Linux使用shell脚本/dev/MAKEDEV,而FreeBSD和许多商业类Unix系统使用设备文件系统。检查你的操作系统,了解chroot需要哪些设备节点以及如何创建它们。一些操作系统包含易于填充chroot的工具。
最后,用户需要一个shell。将静态链接的shell复制到chroot的/bin目录中。还可以复制用户需要的任何其他程序的静态版本。如果你想使用动态链接程序,你还必须复制任何必要的文件。
使用ChrootDirectory选项建立chroots:
xxxxxxxxxx
ChrootDirectory /home/djm
这适用于单个用户帐户,或者如果所有SSH用户都有相同的chroot目录,但这是令牌发挥作用的地方。
如果chroot目录路径包含文字百分号,请使用%%标记。此例中,我们chroot进入目录/home/disk%1/djm:
xxxxxxxxxx
ChrootDirectory /home/disk%%1/djm
%h宏展开到用户的主目录,就像/etc/passwd中所指定的一样。
xxxxxxxxxx
ChrootDirectory %h
登录时,djm被锁定到/home/djm。请注意,他需要在这个目录中有一个chrooted主目录,所以你需要创建/home/djm/home/djm。
%u宏展开为用户的用户名。这允许你在中央chroot目录下为一组用户分配唯一的主目录。
xxxxxxxxxx
ChrootDirectory /usr/prisonroot/%u
您需要分别填充每个用户的chroot。
你可以chroot所有人,但这会使你的系统管理员难以进行维护。很可能你只想chroot一部分用户。使用Match语句有选择地chroot用户:
xxxxxxxxxx
…
ChrootDirectory none
…
Match Group billing
ChrootDirectory %h
如果你的大多数用户都被chroot,请反转默认设置,只允许你的系统管理员完全访问:
xxxxxxxxxx
…
ChrootDirectory %h
…
Match Group wheel
ChrootDirectory none
选择对您的环境有意义的方法。
chroot很难管理,因为它们通常缺乏完整的用户空间。
如果chroot的用户无法登录,请在调试模式下运行sshd,连接到终端窗口。让chroot的用户尝试登录,并查看调试输出;你可能会看到问题。
常见的问题包括缺少设备节点、目录权限不正确或缺少shell。
任何面向互联网的服务器都会受到很多随机的探测。蠕虫、脚本小子和其他各种渣滓都想闯入你的电脑。
有些人建议更改sshd使用的TCP端口。这是一个通过隐蔽性实现安全的完美例子,但隐蔽性是行不通的。
扫描仪不断探测所有互联网连接的IP地址的所有端口,它们非常擅长找出哪个端口上运行的是什么服务。更改端口可能会为你赢得几分钟的时间来对抗专用入侵者,但现在不是了。更改端口可以减少日志中的随机噪声量,增加你注意到实际问题的几率。
你也会在互联网上看到有人建议使用不同的协议横幅,这是个糟糕的建议。当你使用netcat连接到SSH守护进程时,你将看到协议横幅。横幅标识服务器的类型。所有SSH服务器都略有不同,可能需要特殊的客户端设置。SSH客户端使用协议横幅来检测与服务器可靠连接所需要的任何怪癖。随意修改协议横幅会剥夺客户端可靠连接所需的信息。
你可以考虑附加解决方案来阻止重复连接但无法进行身份验证的IP地址,例如fail2ban和黑名单。实现这些的细节因平台而异,值得考虑。
在某种程度上,sshd通过(privilege separation)特权分离来保护自己。只有一小部分服务以root权限运行。大多数服务器以无特权用户身份运行。这意味着,如果入侵者成功入侵服务器守护进程,他只能对你的系统造成有限的损害。这仍然很烦人,但不是毁灭性的。
此外,sshd通过sandbox(沙箱)限制了无特权进程。沙箱限制了sshd在用户进行身份验证之前可以调用哪些系统调用。OpenSSH支持几种不同的沙箱方法,从苹果的sandbox到Linux的seccomp。如果操作系统不提供任何其他沙箱方法,sshd会使用rlimit将打开的文件和子进程的设置为零。
与所有面向互联网的服务一样,降低SSH服务风险的一个简单方法是减少可以访问它的IP地址的数量。OpenSSH尊重TCP包装器(/etc/hosts.allow)。如果你的服务器或网络有数据包过滤器,请使用它。通过只允许授权的IP地址访问你的SSH服务器,你可以阻止绝大多数攻击者。
然而,保护服务器的最有效方法是禁用密码,只允许通过密钥登录。我们将在第七章SSH密钥中介绍通过密钥的访问。