编者按:题为“NGINX:升级到 RHEL 6.6 或 CentOS 6.6 时的 SELinux 变更”的博客文章会重定向至此。本文提供了更新且更具通用性的信息。
现代Red Hat Enterprise Linux(RHEL)及相关发行版中的安全增强型Linux(SELinux)默认设置可能非常严格,更侧重安全性而非便利性。虽然默认设置不会限制采用默认配置的NGINX Open Source和NGINX Plus正常运行,但您配置的其他功能可能会被阻止,除非在SELinux中明确允许这些功能。本文介绍了可能出现的问题及建议的解决方法。
[编者注:本文同时适用于NGINX Open Source和NGINX Plus。为便于阅读,全文统一使用“NGINX”一词。
CentOS是最初从RHEL衍生而来的相关发行版,受NGINX和NGINX Plus支持。此外,NGINX Plus还支持相关的Amazon Linux和Oracle Linux发行版。这些发行版的默认SELinux设置可能与CentOS和RHEL不同;请参阅供应商文档。]
SELinux概述
现代RHEL和CentOS服务器默认启用SELinux。每个操作系统对象(进程、文件描述符、文件等)都标有SELinux上下文,用于定义该对象可执行的操作及其权限。在RHEL 6.6/CentOS 6.6及更高版本中,NGINX标有httpd_t上下文:
# ps auZ | grep nginxunconfined_u:system_r:httpd_t:s0 3234 ? Ss 0:00 nginx: master process /usr/sbin/nginx \
-c /etc/nginx/nginx.conf
unconfined_u:system_r:httpd_t:s0 3236 ? Ss 0:00 nginx: worker processThe httpd_t上下文允许NGINX侦听常用Web服务器端口、访问/etc/nginx中的配置文件,以及访问标准文档根目录(/usr/share/nginx)中的内容。但它不允许执行许多其他操作,例如代理到上游地址,或通过套接字与其他进程通信。
暂时为NGINX禁用SELinux
要暂时解除SELinux对httpd_t上下文的限制,使NGINX能够执行与非SELinux操作系统中相同的所有操作,请将httpd_t上下文分配给宽容域。有关详细信息,请参阅下一节。
# semanage permissive -a httpd_t更改 SELinux 模式
SELinux可以在强制、宽容或禁用模式(也称为域)下运行。在测试环境(如有)或生产环境中进行可能违反默认(严格)权限的NGINX配置更改之前,可以将SELinux从强制模式切换为宽容模式。在宽容模式下,SELinux允许所有操作,但会记录在强制模式下会违反安全策略的操作。
要将“httpd_t”添加到“宽容”域列表,请运行以下命令:
# semanage permissive -a httpd_t要从httpd_t域列表中删除宽容模式,请运行:
# semanage permissive -d httpd_t要将全局模式设置为permissive,请运行:
# setenforce 0要将全局模式设置为“enforcing”,请运行:
# setenforce 1解决SELinux安全异常
在permissive模式下,安全例外会记录到默认的Linux审计日志/var/log/audit/audit.log中。如果问题仅在NGINX以enforcing模式运行时出现,请查看在permissive模式下记录的例外,并更新安全策略以允许这些例外。
问题 1:代理连接被禁止
默认情况下,SELinux配置不允许NGINX连接到远程HTTP、FastCGI或其他服务器。以下审计日志消息表明了这一点:
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
type=SYSCALL msg=audit(1415714880.156:29): arch=c000003e syscall=42 success=no \
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=(none) ses=1 comm="nginx" exe="/usr/sbin/nginx" \
subj=unconfined_u:system_r:httpd_t:s0 key=(null)The audit2why命令用于解释消息代码(1415714880.156:29):
# grep 1415714880.156:29 /var/log/audit/audit.log | audit2whytype=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
Was caused by:
One of the following booleans was set incorrectly.
Description:
Allow httpd to act as a relay
Allow access by executing:
# setsebool -P httpd_can_network_relay 1
Description:
Allow HTTPD scripts and modules to connect to the network using TCP.
Allow access by executing:
# setsebool -P httpd_can_network_connect 1The output from audit2why 的输出表明,启用以下一个或两个布尔选项,即可允许NGINX建立代理连接:httpd_can_network_relay 和 httpd_can_network_connect。可以临时或永久启用这些选项;如输出所示,添加 ‑P 标志可永久启用。
了解布尔选项
The sesearch命令可提供有关布尔选项的更多信息。安装setools软件包(yum install setools)后即可使用该命令。下面显示httpd_can_network_relay和httpd_can_network_connect选项的输出。
The httpd_can_network_relay布尔选项
以下是sesearch命令针对httpd_can_network_relay选项的输出:
# sesearch -A -s httpd_t -b httpd_can_network_relayFound 10 semantic av rules:
allow httpd_t gopher_port_t : tcp_socket name_connect ;
allow httpd_t http_cache_client_packet_t : packet { send recv } ;
allow httpd_t ftp_port_t : tcp_socket name_connect ;
allow httpd_t ftp_client_packet_t : packet { send recv } ;
allow httpd_t http_client_packet_t : packet { send recv } ;
allow httpd_t squid_port_t : tcp_socket name_connect ;
allow httpd_t http_cache_port_t : tcp_socket name_connect ;
allow httpd_t http_port_t : tcp_socket name_connect ;
allow httpd_t gopher_client_packet_t : packet { send recv } ;
allow httpd_t memcache_port_t : tcp_socket name_connect ;此输出表明,httpd_can_network_relay允许标记为httpd_t上下文的进程(例如NGINX)连接到多种类型的端口,包括http_port_t类型的端口:
# semanage port -l | grep http_port_thttp_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000要将更多端口(此处为8082)添加到http_port_t允许使用的端口集合中,请运行:
# semanage port -a -t http_port_t -p tcp 8082如果此命令的输出显示某个端口已在其他位置定义(如下例所示),则表示该端口已包含在另一个集合中。请勿将其重新分配给http_port_t,否则可能会对其他服务造成不利影响。
# semanage port -a -t http_port_t -p tcp 8080
/usr/sbin/semanage: Port tcp/8080 already defined
# semanage port -l | grep 8080
http_cache_port_t tcp 3128, 8080, 8118, 8123, 10001-10010The httpd_can_network_connect 布尔选项
以下是sesearch命令针对httpd_can_network_connect选项的输出:
# sesearch -A -s httpd_t -b httpd_can_network_connectFound 1 semantic av rules:
allow 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: 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=fileThe audit2why命令用于解释消息代码(1415715270.766:31):
# grep 1415715270.766:31 /var/log/audit/audit.log | audit2whytype=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
Was caused by:
Missing type enforcement (TE) allow rule.
You can use audit2allow to generate a loadable module to allow this access.文件访问被禁止时,有两种处理方式。
选项 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;
require {
type httpd_t;
type default_t;
type http_cache_port_t;
class tcp_socket name_connect;
class file { read getattr open };
}
#============= httpd_t ==============
allow httpd_t default_t:file { read getattr open };
#!!!! This avc can be allowed using one of these booleans:
# 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_thttp_port_t tcp 80, 443, 488, 8008, 8009, 8443如果尝试将NGINX配置为侦听不在允许列表中的端口(在NGINX配置的listen指令中,该指令位于http、stream或mail上下文中),则在验证(nginx -t)或重新加载NGINX配置时会出现错误,如以下NGINX日志条目所示:
YYYY/MM/DD hh:mm:ss [emerg] 46123#0: bind() to 0.0.0.0:8001 failed (13: Permission denied)可以使用semanage将所需端口(此处为8001)添加到http_port_t类型:
# semanage port -a -t http_port_t -p tcp 8001使用新配置重新加载NGINX。
# nginx -s reload问题4:打开的文件过多错误
当打开文件数超过限制(RLIMIT_NOFILE)时,错误日志中会显示以下消息:
Too many files are openNGINX工作进程生成错误时
在大多数情况下,NGINX工作进程会报告此错误,但无法使用NGINX的worker_rlimit_nofile指令提高限制,因为SELinux不允许执行setrlimit()系统调用。错误日志和审计日志中的以下消息对此进行了说明。
- 在/var/log/nginx/error.log中(适用于CentOS/RHEL 7.4及更高版本):
YYYY/MM/DD hh:mm:ss [alert] 12066#0: setrlimit(RLIMIT_NOFILE, 2342) failed (13: Permission denied)- 在/var/log/nginx/error.log中(适用于CentOS/RHEL 8.0及更高版本):
YYYY/MM/DD hh:mm:ss [alert] 3327#0: setrlimit(RLIMIT_NOFILE, 65535) failed (1: Operation not permitted)- 对于/var/log/audit/audit.log(CentOS/RHEL 7.4+)和/var/log/messages(CentOS/RHEL 8.0+):
type=AVC msg=audit(1437731200.211:366): avc: denied { setrlimit } for pid=12066 \ 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 unit文件。此操作会为主进程和工作进程设置文件描述符限制。
- 为
nginx.service配置创建目录:
$ mkdir /etc/systemd/system/nginx.service.d- 将以下行添加到/etc/systemd/system/nginx.service.d/nofile_limit.conf:
[Service]LimitNOFILE=65535- 重新加载systemd守护进程配置并重启NGINX:
$ systemctl daemon-reload$ systemctl restart nginx.service其他资源
SELinux 是一项复杂而强大的操作系统权限管理机制。以下文档提供了更多信息。
- 安全增强型Linux,RHEL 6或RHEL 7
- SELinux(CentOS操作指南)
- 安全增强型 Linux 用户指南(Fedora 项目)
- SELinux 项目主页
- 安全增强型Linux(美国国家安全局)
关于作者

相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
