如何告诉 SELinux 在没有 audit2allow 的情况下允许 nginx 访问 unix 套接字?

drs*_*drs 13 nginx selinux

我有 nginx 通过 unix 套接字将请求转发到 gunicorn /run/gunicorn/socket。默认情况下,SELinux 不允许这种行为:

grep nginx /var/log/audit/audit.log
type=SERVICE_START msg=audit(1454358912.455:5390): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=nginx comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
type=AVC msg=audit(1454360194.623:7324): avc:  denied  { write } for  pid=9128 comm="nginx" name="socket" dev="tmpfs" ino=76151 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=sock_file
type=SYSCALL msg=audit(1454360194.623:7324): arch=c000003e syscall=42 success=no exit=-13 a0=c a1=1f6fe58 a2=6e a3=7ffee1da5710 items=0 ppid=9127 pid=9128 auid=4294967295 uid=995 gid=993 euid=995 suid=995 fsuid=995 egid=993 sgid=993 fsgid=993 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1454361591.701:13343): avc:  denied  { connectto } for  pid=9128 comm="nginx" path="/run/gunicorn/socket" scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=unix_stream_socket
type=SYSCALL msg=audit(1454361591.701:13343): arch=c000003e syscall=42 success=no exit=-13 a0=c a1=1f6fe58 a2=6e a3=7ffee1da5950 items=0 ppid=9127 pid=9128 auid=4294967295 uid=995 gid=993 euid=995 suid=995 fsuid=995 egid=993 sgid=993 fsgid=993 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)
Run Code Online (Sandbox Code Playgroud)

我所看到的任何地方(例如,herehere),启用此功能的说明都说向 nginx 发出请求,让请求被 SELinux 拒绝,然后运行audit2allow以允许将来的请求。我想不出任何chconsemanage明确允许这种行为的命令。

这是唯一的方法吗?似乎荒谬的是,您无法设置允许 nginx 写入套接字的策略,而无需先尝试被拒绝,然后再运行一个工具来启用被拒绝的事物。您如何确切知道正在启用什么?如果您在自动化下设置机器,这应该如何工作?

我正在使用 CentOS 7。

use*_*517 26

似乎荒谬的是,您无法设置允许 nginx 写入套接字的策略,而无需先尝试被拒绝,然后再运行一个工具来启用被拒绝的事物。

好吧,不,SELinux 是强制访问控制,默认情况下会拒绝某些内容,您必须明确允许某些内容。如果策略作者没有考虑一个特定的 (franken) 堆栈或者守护进程的作者没有让它了解 SELinux 并为其编写策略,那么你就靠自己了。您必须分析您的服务正在做什么以及它们如何与 SELinux 交互,并提出您自己的策略来允许它。有一些工具可以帮助您audit2whyaudit2allow等。

……这是唯一的办法吗?

不,但这取决于您要尝试做什么以及您如何尝试解决解决方案。例如,您可能希望将 nginx (httpd_t) 绑定到端口 8010 (unreserved_port_t)。当你启动 nginx 时它失败了

Starting nginx: nginx: [emerg] bind() to 0.0.0.0:8010 failed (13: Permission denied)
Run Code Online (Sandbox Code Playgroud)

你(最终)查看审计日志并找到

type=AVC msg=audit(1457904756.503:41673): avc:  denied  { name_bind } for
pid=30483 comm="nginx" src=8010 scontext=unconfined_u:system_r:httpd_t:s0
tcontext=system_u:object_r:port_t:s0 tclass=tcp_socket
Run Code Online (Sandbox Code Playgroud)

您可能会通过 audit2alllow 运行它并天真地接受它的发现

allow httpd_t port_t:tcp_socket name_bind;
Run Code Online (Sandbox Code Playgroud)

然后允许 httpd_t 连接到任何 tcp 端口。这可能不是您想要的。

您可以使用sesearch调查策略并查看 httpd_t 可以 name_bind 到哪些端口类型

sesearch --allow -s httpd_t | grep name_bind
...
allow httpd_t http_port_t : tcp_socket name_bind ;
allow httpd_t http_port_t : udp_socket name_bind ;
...
Run Code Online (Sandbox Code Playgroud)

在其他类型中,http_t 可以绑定到 http_port_t。现在您可以使用semanage进行更深入的挖掘。

semanage port -l | grep http_port_t
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
...
Run Code Online (Sandbox Code Playgroud)

未列出端口 8010。由于我们希望 nginx 绑定到端口 8010,因此将其添加到 http_port_t 列表中并非没有道理

semanage port -a -t http_port_t -p tcp 8010
Run Code Online (Sandbox Code Playgroud)

现在 nginx 将被允许将 name_bind 绑定到端口 8010,而不是如上所述的每个 tcp 端口。

您如何确切知道正在启用什么?

对政策的更改相当容易阅读,通过 audit2allow 运行上面的消息,我们得到

allow httpd_t httpd_sys_content_t:sock_file write;
allow httpd_t initrc_t:unix_stream_socket connectto;
Run Code Online (Sandbox Code Playgroud)

这似乎是不言自明的。

第一个是指带有 inum 76151 的文件。您可以使用 find 来获取它的名称(find / -inum 76151),然后使用semanage fcontext -a -t ...来更改策略和 restorecon 以修复上下文。

第二个涉及/run/gunicorn/socket再次具有错误的上下文。使用 sesearch,我们可以看到 http_t 可以连接到类型为(以及其他)http_t 的 unix_stream_sockets。所以我们可以相应地改变上下文,例如

semanage fcontext -a -t httpd_t "/run/gunicorn(/.*)?"
restorecon -r /run
Run Code Online (Sandbox Code Playgroud)

这将设置 /run/gunicorn 和树的上下文| 它下面的文件到 httpd_t。

如果您在自动化下设置机器,这应该如何工作?

您需要分析系统并在测试中进行适当的更改。然后使用自动化工具部署更改,puppet 和 ansible 对此提供支持。

当然,您可以在 SElinux 设置为 permissive 的情况下在生产中完成所有操作。收集所有消息,分析它们以决定您的更改并部署它们。

关于 SELinux 有很多东西要了解,但这就是我技能的极限,Michael Hampton 更好,Mathew Ife 再次更好,他们可能还有更多要补充的。


Mic*_*ton 3

您要使用的类型不是httpd_sys_content_t. 这是 Web 服务器为用户代理提供服务的静态文件。

对于用于进程间通信的套接字,您正在寻找的类型是httpd_var_run_t

不过,请注意,由于您不受限制地运行 Gunicorn,因此与其通信可能会出现其他问题。

  • 谢谢!看起来这已经解决了 SELinux 问题之一。关于如何设置gunicorn(或任何其他服务)受限的任何指示? (3认同)