博客 | NGINX

将 NGINX 和 NGINX Plus 与 SELinux 结合使用

NGINX-F5-horiz-black-type-RGB 的一部分
欧文·加勒特缩略图
欧文·加勒特
2018 年 8 月 17 日发布

编辑——博客文章标题为“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 ) 中的内容。 它不允许许多其他操作,例如代理到上游位置或通过套接字与其他进程通信。

暂时禁用 NGINX 的 SELinux

要暂时禁用httpd_t上下文的 SELinux 限制,以便 NGINX 可以执行与非 SELinux 操作系统相同的所有操作,请将httpd_t上下文分配给宽容域。 详细信息请参阅下一节。

# semanage 允许 -a httpd_t

更改 SELinux 模式

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 在强制模式下运行时发生的问题,请查看在宽容模式下记录的异常并更新安全策略以允许它们。

问题 1: 代理连接被禁止

默认情况下,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_relayhttpd_can_network_connect布尔选项中的一个或两个来允许 NGINX 建立代理连接。 您可以暂时或永久启用它们,后者通过添加‑P标志来启用,如输出中所示。

了解布尔选项

sesearch命令提供有关布尔选项的更多信息,如果您安装了setools包( yum install setools ),则该命令可用。 这里我们展示了httpd_can_network_relayhttpd_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

问题 2: 禁止文件访问

默认情况下,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 生成可加载模块来允许此访问。

当文件访问被禁止时,您有两个选择。

选项 1: 修改文件标签

修改文件标签,以便 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

选项 2: 扩展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

此更改在重启后仍然有效。

问题 3: NGINX 无法绑定到其他端口

默认情况下,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 配置中的httpstreammail上下文中的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 重新加载

问题 4: 打开的文件过多错误

当超出打开文件数( RLIMIT_NOFILE )的限制时,错误日志中会出现以下消息:

打开的文件过多

当 NGINX 工作进程生成错误时

在大多数情况下,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 主进程报告错误,则需要更新 NGINX 的systemd单元文件。这会设置主进程和工作进程的文件描述符限制。

  1. nginx.service配置创建一个目录:

    $ mkdir /etc/systemd/system/nginx.service.d
    
  2. 将以下行添加到/etc/systemd/system/nginx.service.d/nofile_limit.conf

    [服务]限制NOFILE=65535
    
  3. 重新加载 systemd 守护进程配置并重新启动 NGINX:

    $ systemctl daemon-reload $ systemctl restart nginx.service
    

其他资源

SELinux 是一个复杂而强大的管理操作系统权限的工具。 下列文档中提供了更多信息。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”