编辑——博客文章标题为“NGINX: 升级到 RHEL 6.6 / CentOS 6.6 时 SELinux 的变化”重定向到此处。 本文提供了更新的和概括的信息。
现代 Red Hat Enterprise Linux (RHEL) 和相关发行版上的安全增强型 Linux (SELinux) 的默认设置可能非常严格,偏向安全性而不是便利性。 虽然默认设置不会限制 NGINX Open Source 和 NGINX Plus 在其默认配置下的运行,但您配置的其他功能可能会被阻止,除非您在 SELinux 中明确允许它们。 本文介绍了可能出现的问题以及解决这些问题的建议方法。
[编辑——本文适用于 NGINX 开源和 NGINX Plus。 为了方便阅读,通篇使用术语“NGINX”。
CentOS 是一个相关发行版,最初源自 RHEL,并由 NGINX 和 NGINX Plus 支持。 此外,NGINX Plus 还支持相关的 Amazon Linux 和 Oracle Linux 发行版。 它们的默认 SELinux 设置可能与 CentOS 和 RHEL 不同;请查阅供应商文档。]
SELinux 概述
SELinux 在现代 RHEL 和 CentOS 服务器上默认启用。 每个操作系统对象(进程、文件描述符、文件等)都标有一个 SELinux 上下文,该上下文定义了该对象可以执行的权限和操作。 在 RHEL 6.6/CentOS 6.6 及更高版本中,NGINX 标有httpd_t
上下文:
#ps auZ | grep nginx unconfined_u:system_r: httpd_t :s0 3234? Ss 0:00 nginx:主进程 /usr/sbin/nginx \ -c /etc/nginx/nginx.conf unconfined_u:system_r: httpd_t :s0 3236? SS 0:00 nginx:工作进程
httpd_t
上下文允许 NGINX 监听常见的 Web 服务器端口、访问/etc/nginx中的配置文件以及访问标准文档根位置 ( /usr/share/nginx ) 中的内容。 它不允许许多其他操作,例如代理到上游位置或通过套接字与其他进程通信。
要暂时禁用httpd_t
上下文的 SELinux 限制,以便 NGINX 可以执行与非 SELinux 操作系统相同的所有操作,请将httpd_t
上下文分配给宽容域。 详细信息请参阅下一节。
# semanage 允许 -a httpd_t
SELinux 可以在强制、宽容或禁用模式(也称为域)下运行。 在对 NGINX 配置进行可能违反默认(严格)权限的更改之前,您可以在测试环境(如果可用)或生产环境中将 SELinux 从强制模式更改为宽容模式。 在宽容模式下,SELinux 允许所有操作,但会记录在强制模式下违反安全策略的操作。
要将httpd_t
添加到允许域的列表中,请运行以下命令:
# semanage 允许 -a httpd_t
要从允许域列表中删除httpd_t
,请运行:
# semanage 宽容 -d httpd_t
要将模式全局设置为宽容,请运行:
#设置强制 0
要将模式全局设置为强制执行,请运行:
#设置强制 1
解决 SELinux 安全异常
在宽容模式下,安全异常会记录到默认的 Linux 审计日志/var/log/audit/audit.log中。 如果您遇到仅在 NGINX 在强制模式下运行时发生的问题,请查看在宽容模式下记录的异常并更新安全策略以允许它们。
默认情况下,SELinux 配置不允许 NGINX 连接到远程 HTTP、FastCGI 或其他服务器,如以下审计日志消息所示:
type=AVC msg=audit(1415714880.156:29): avc: 拒绝 pid=1349 的 { name_connect } \
comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \
tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
type=SYSCALL msg=audit(1415714880.156:29): arch=c000003e syscall=42 成功=否 \
exit=-115 a0=b \a1=16125f8 a2=10 a3=7fffc2bab440 items=0 ppid=1347 pid=1349 \
auid=1000 uid=497 gid=496 euid=497 suid=497 fsuid=497 egid=496 sgid=496 fsgid=496 \
tty=(无) ses=1 comm="nginx" exe="/usr/sbin/nginx" \
subj=unconfined_u:system_r:httpd_t:s0 key=(空)
audit2why
命令解释消息代码( 1415714880.156:29
):
# grep 1415714880.156:29 /var/log/audit/audit.log | audit2why type=AVC msg=audit(1415714880.156:29): avc: denied { name_connect } for pid=1349 \ comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \ tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket 导致的原因如下:
以下布尔值之一设置不正确。
描述:
允许httpd作为中继 通过执行以下命令允许访问:# setsebool -P httpd_can_network_relay 1 描述:
允许 HTTPD 脚本和模块使用 TCP 连接到网络。
通过执行以下命令允许访问:# setsebool -P httpd_can_network_connect 1
audit2why
的输出表明您可以通过启用httpd_can_network_relay
和httpd_can_network_connect
布尔选项中的一个或两个来允许 NGINX 建立代理连接。 您可以暂时或永久启用它们,后者通过添加‑P
标志来启用,如输出中所示。
sesearch
命令提供有关布尔选项的更多信息,如果您安装了setools包( yum
install
setools
),则该命令可用。 这里我们展示了httpd_can_network_relay
和httpd_can_network_connect
选项的输出。
httpd_can_network_relay
布尔选项以下是sesearch
命令有关httpd_can_network_relay
选项的输出:
# sesearch -A -s httpd_t -b httpd_can_network_relay找到了 10 条语义 av 规则:允许 httpd_t gopher_port_t:tcp_socket name_connect;允许 httpd_t http_cache_client_packet_t:数据包 { 发送 recv };允许 httpd_t ftp_port_t:tcp_socket name_connect;允许 httpd_t ftp_client_packet_t:数据包 { 发送 recv };允许 httpd_t http_client_packet_t:数据包 { 发送 recv };允许 httpd_t squid_port_t:tcp_socket name_connect;允许 httpd_t http_cache_port_t:tcp_socket name_connect;允许 httpd_t http_port_t:tcp_socket name_connect;允许httpd_t gopher_client_packet_t:数据包{发送recv}; 允许httpd_t memcache_port_t:tcp_socket name_connect;
此输出表明httpd_can_network_relay
允许标有httpd_t
上下文的进程(例如 NGINX)连接到各种类型的端口,包括类型http_port_t
:
# semanage 端口 -l | grep http_port_t http_port_t tcp 80、81、443、488、8008、8009、8443、9000
要添加更多端口(此处,8082
)添加到http_port_t
允许的端口集,运行:
# semanage 端口 -a -t http_port_t -p tcp 8082
如果该命令的输出表明某个端口已
定义
,如以下示例所示,则表示该端口包含在另一组中。 请勿将其重新分配给http_port_t
,因为其他服务可能会受到负面影响。
# semanage 端口 -a -t http_port_t -p tcp 8080 /usr/sbin/semanage: 端口 tcp/8080 已定义 # semanage port -l | grep 8080 http_cache_port_t tcp 3128, 8080, 8118, 8123, 10001-10010
httpd_can_network_connect
布尔选项以下是sesearch
命令有关httpd_can_network_connect
选项的输出:
# sesearch -A -s httpd_t -b httpd_can_network_connect找到 1 条语义 av 规则:允许 httpd_t port_type:tcp_socket name_connect;
此输出表明httpd_can_network_connect
允许标有httpd_t
上下文(例如 NGINX)的进程连接到所有具有port_type
属性的 TCP 套接字类型。 要列出它们,请运行:
# seinfo-aport_type-x
默认情况下,SELinux 配置不允许 NGINX 访问众所周知的授权位置之外的文件,如以下审计日志消息所示:
type=AVC msg=audit(1415715270.766:31): avc: 拒绝 pid=1380 的 { getattr } \
comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \
scontext=unconfined_u:system_r:httpd_t:s0 \
tcontext=unconfined_u:object_r:default_t:s0 tclass=file
audit2why
命令解释消息代码( 1415715270.766:31
):
# grep 1415715270.766:31 /var/log/audit/audit.log | audit2why type=AVC msg=audit(1415715270.766:31): avc: denied { getattr } for pid=1380 \ comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \ scontext=unconfined_u:system_r:httpd_t:s0 \ tcontext=unconfined_u:object_r:default_t:s0 tclass=file 导致的原因如下:
缺少类型强制 (TE) 允许规则。
您可以使用 audit2allow 生成可加载模块来允许此访问。
当文件访问被禁止时,您有两个选择。
修改文件标签,以便 NGINX(作为标有httpd_t
上下文的进程)可以访问该文件:
# chcon -v --type=httpd_sys_content_t /www/t.txt
默认情况下,当文件系统重新标记时,此修改会被删除。 要使更改永久生效,请运行:
# semanage fcontext -a -t httpd_sys_content_t /www/t.txt # restorecon -v /www/t.txt
要修改文件组的文件标签,请运行:
# semanage fcontext -a -t httpd_sys_content_t '/www(/.*)?' # restorecon -Rv /www
httpd_t
域权限扩展httpd_t
的策略以允许访问其他文件位置:
# grep nginx /var/log/audit/audit.log | audit2allow -m nginx > nginx.te # cat nginx.te module nginx 1.0; 需要 { 类型 httpd_t; 类型 default_t; 类型 http_cache_port_t; 类 tcp_socket name_connect; 类文件 { 读取 getattr 打开 }; } #============= httpd_t =============== 允许 httpd_t default_t:file { 读取 getattr 打开 }; #!!!! 可以使用以下布尔值之一来允许此 avc:# httpd_can_network_relay, httpd_can_network_connect allow httpd_t http_cache_port_t:tcp_socket name_connect;
要生成编译策略,请包含-M
选项:
# grep nginx /var/log/audit/audit.log | audit2allow -M nginx
要加载策略,请运行semodule
-i
,然后使用semodule
-l
验证是否成功:
# semodule -i nginx.pp # semodule -l | grep nginx nginx 1.0
此更改在重启后仍然有效。
默认情况下,SELinux 配置不允许 NGINX 监听( bind()
)除http_port_t
类型中允许列出的默认端口之外的 TCP 或 UDP 端口:
# semanage port -l | grep http_port_t http_port_t tcp 80, 443, 488, 8008, 8009, 8443
如果您尝试将 NGINX 配置为侦听非允许列表端口(使用 NGINX 配置中的http
、 stream
或mail
上下文中的listen
指令),则在验证( nginx
-t
)或重新加载 NGINX 配置时会收到错误,如下 NGINX 日志条目所示:
YYYY / MM / DD hh : mm : ss [emerg] 46123#0: bind() 至 0.0.0.0:8001 失败 (13: 没有权限)
您可以使用semanage
将所需的端口(此处为 8001)添加到http_port_t
类型:
# semanage 端口 -a -t http_port_t -p tcp 8001
使用新配置重新加载 NGINX。
# nginx -s 重新加载
打开的文件过多
错误当超出打开文件数( RLIMIT_NOFILE
)的限制时,错误日志中会出现以下消息:
打开的文件过多
在大多数情况下,NGINX 工作进程会报告此错误,但您无法使用 NGINX worker_rlimit_nofile
指令来增加限制,因为 SELinux 不允许setrlimit()
系统调用,如错误和审计日志中的以下消息所报告。
在 CentOS/RHEL 7.4+ 的/var/log/nginx/error.log中:
YYYY / MM / DD hh : mm : ss [警报] 12066#0: setrlimit(RLIMIT_NOFILE,2342) 失败 (13: 没有权限)
在 CentOS/RHEL 8.0+ 的/var/log/nginx/error.log中:
YYYY / MM / DD hh : mm : ss [警报] 3327#0: setrlimit(RLIMIT_NOFILE,65535) 失败 (1: 操作不允许)
在 CentOS/RHEL 7.4+ 的/var/log/audit/audit.log中以及在 CentOS/RHEL 8.0+ 的/var/log/messages中:
type=AVC msg=audit(1437731200.211:366): avc: 拒绝 pid=12066 的 { setrlimit } \ comm="nginx" scontext=system_u:system_r:httpd_t:s0 \
tcontext=system_u:system_r:httpd_t:s0 tclass=process
要增加限制,请以root
用户身份运行此命令:
$ setsebool -P httpd_setrlimit 1
如果 NGINX 主进程报告错误,则需要更新 NGINX 的systemd
单元
文件。这会设置主进程和工作进程的文件描述符限制。
为nginx.service
配置创建一个目录:
$ mkdir /etc/systemd/system/nginx.service.d
将以下行添加到/etc/systemd/system/nginx.service.d/nofile_limit.conf :
[服务]限制NOFILE=65535
重新加载 systemd 守护进程配置并重新启动 NGINX:
$ systemctl daemon-reload $ systemctl restart nginx.service
其他资源
SELinux 是一个复杂而强大的管理操作系统权限的工具。 下列文档中提供了更多信息。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”