我让 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)
我所看到的一切都是(例如,这里和这里),启用此功能的说明说向 nginx 发出请求,让 SELinux 拒绝该请求,然后运行audit2allow
以允许将来的请求。我找不到任何chcon
或semanage
命令明确允许此行为。
这是唯一的方法吗?您无法设置允许 nginx 写入套接字的策略,而无需先拒绝尝试,然后运行启用被拒绝的操作的工具,这似乎很荒谬。您如何确切知道启用了什么?如果您在自动化下设置机器,这应该如何工作?
我正在使用 CentOS 7。
答案1
这看起来很荒谬,你不能设置一个策略,允许 nginx 写入套接字,而无需首先拒绝尝试,然后运行启用被拒绝的操作的工具。
嗯,不是,SELinux 是强制访问控制,默认情况下会拒绝某些操作,您必须明确允许某些操作。如果策略作者没有考虑特定(franken)堆栈,或者守护进程的作者没有使其了解 SELinux 并为其编写策略,那么您就得靠自己了。您必须分析您的服务正在做什么以及它们如何与 SELinux 交互,并制定自己的策略来允许它。有可用的工具可以帮助您审计为什么,audit2allowETC。
...这是唯一的办法吗?
不,但这取决于您要做什么以及您如何尝试解决它。例如,您可能希望将 nginx (httpd_t) 绑定到端口 8010 (unreserved_port_t)。当您启动 nginx 时,它会失败
Starting nginx: nginx: [emerg] bind() to 0.0.0.0:8010 failed (13: Permission denied)
然后你(最终)查看审计日志并发现
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
您可以通过 audit2alllow 运行此操作并天真地接受它的调查结果
allow httpd_t port_t:tcp_socket name_bind;
然后允许 httpd_t 连接到任何 tcp 端口。这可能不是您想要的。
您可以使用搜寻调查该策略并查看 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 ;
...
在其他类型中,http_t 可以绑定到 http_port_t。现在您可以使用塞马纳盖进一步深入挖掘。
semanage port -l | grep http_port_t
http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
...
未列出端口 8010。由于我们希望 nginx 绑定到端口 8010,因此将其添加到 http_port_t 列表中并非不合理
semanage port -a -t http_port_t -p tcp 8010
现在 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;
这看起来相当不言自明。
其中第一个指的是 inum 为 76151 的文件。您可以使用 find 来获取它的名称(find / -inum 76151),然后使用它semanage fcontext -a -t ...
来更改策略并使用 restorecon 来修复上下文。
第二个与再次具有错误上下文有关/run/gunicorn/socket
。使用 sesearch 我们可以看到 http_t 可以连接到 unix_stream_sockets,类型为 http_t(等等)。因此我们可以相应地更改上下文,例如
semanage fcontext -a -t httpd_t "/run/gunicorn(/.*)?"
restorecon -r /run
这会将 /run/gunicorn 及其下方的 tree| 文件的上下文设置为 httpd_t。
如果您设置自动化机器,这应该如何工作?
您需要分析系统并在测试中进行适当的更改。然后使用自动化工具部署更改,puppet 和 ansible 对此提供支持。
当然,你可以在生产环境中将 SElinux 设置为宽容,从而完成所有操作。收集所有消息,分析它们,决定你的更改并部署它们。
关于 SELinux 还有很多需要了解,但这是我的技术极限,Michael Hampton 更好,Mathew Ife 更好,他们可能还有更多可以补充。
答案2
您要使用的类型不是httpd_sys_content_t
。这适用于 Web 服务器要提供给用户代理的静态文件。
对于用于进程间通信的套接字,您正在寻找的类型是httpd_var_run_t
。
不过,请注意,由于您运行的 gunicorn 不受限制,因此与其通信时可能会出现其他问题。
答案3
我尝试了以前的答案但没有成功,就我而言,我使用 nginx 服务器作为 uwsgi 应用程序的前端,使用 unix 套接字与它们进行通信,我的操作系统是 Fedora 服务器 26。
unix套接字在目录中创建/var/local/myapp
:
/var/local/myapp/server.sock
/var/local/myapp/stats.sock
要配置 SELinux,我必须添加上下文类型:httpd_sys_rw_content_t
semanage fcontext -at httpd_sys_rw_content_t "/var/local/myapp(/.*)?"
restorecon -R -v '/var/local/myapp'