使已编译的 Python mod_wsgi 模块在具有 SElinux 强制模式的 Apache 服务器上运行

使已编译的 Python mod_wsgi 模块在具有 SElinux 强制模式的 Apache 服务器上运行

尝试在理想环境下使我的 Python/Django 应用程序在 RedHat Server 上运行。无法使用包中的 mod_wsgi,因为在使用 python 3.6 的虚拟环境中检查模块时存在一些依赖性问题(> python 2.x 上的一些依赖性检查错误似乎在 numpy 和 pandas 之间),所以我被迫使用 PIP 编译我的 mod_wsgi,效果很好。因为我没有使用其中一个可识别的目录(出于全新安装 + 安全原因,我有一个自定义目录)。如果我将 SELinux 设置为宽容,一切都会很好,但我希望我的应用程序在强制模式下与 SELinux 一起工作。我研究了 SELinux 文件上下文并尝试应用它们,但似乎只有部分起作用:

当我在虚拟环境中设置 mod_wsgi 模块的文件上下文时:

sudo chcon -t httpd_modules_t (特殊位置)/(virtual_env)/lib/python(版本)/site-packages/mod_wsgi_server/mod_wsgi-py36/cpython-36m-x86_64-linux-gun.so

当 selinux 处于强制执行状态时,在设置新的文件上下文之前我收到的 httpd 启动错误将会消失并且服务将正常启动,或者看起来是这样......当我在 httpd 启动后访问我的页面时,我得到:

错误 500

当 selinux 处于强制模式时。我检查了 audit.log,没有看到任何内容。

有人对此有什么想法吗?我的 mod_wsgi 文件当前具有以下设置:

-rwxrwxr-x.root root system_u:object_r:httpd_exec_t:s0 mod_wsgi-py(版本).cpython-(版本)m-x86_64-linux-gnu.so

这感觉是对的,因为任何人都可以读取和执行模块,并且 httpd 启动时不会出现任何问题。如果我在启动 apache web 服务器后将 Selinux 设置为强制执行,则在下次 apache web 服务器重新启动之前,一切仍然正常,这表明所有内容最初都已加载到内存中以进行 mod_wsgi 操作。

我在这里查找了 SELinux 的文件上下文完整列表:

https://linux.die.net/man/8/httpd_selinux

大家对此有什么想法吗?我遗漏了什么?谢谢!

向那些希望提供帮助的人添加信息:

运行 httpd 或访问网页时,我的 audit.log 文件中没有从我的网页获取其他日志(并出现错误 500)。当我转到 http 请求时(我的 settings.py 转发到 443),我在 Apache error_log 中收到此条目:

[Fri Apr 26 18:10:58.313753 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864] mod_wsgi (pid=6294): Failed to exec Python script file '/var/www/(custom_django_dir)/(custom_section)/(site)/(app)/wsgi.py'.
[Fri Apr 26 18:10:58.313840 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864] mod_wsgi (pid=6294): Exception occurred processing WSGI script '/var/www/(custom_django_dir)/(custom_section)/(site)/(app)/wsgi.py'.
[Fri Apr 26 18:10:58.314117 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864] Traceback (most recent call last):
[Fri Apr 26 18:10:58.314191 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864]   File "/var/www/(custom_django_dir)/(custom_section)/(site)/(app)/wsgi.py", line 18, in <module>
[Fri Apr 26 18:10:58.314206 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864]     application = get_wsgi_application()
[Fri Apr 26 18:10:58.314231 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864]   File "/var/www/(custom_django_dir)/(custom_section)/virtual_env/lib64/python3.6/site-packages/django/core/wsgi.py", line 12, in get_wsgi_application
[Fri Apr 26 18:10:58.314250 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864]     django.setup(set_prefix=False)
[Fri Apr 26 18:10:58.314275 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864]   File "/var/www/(custom_django_dir)/(custom_section)/virtual_env/lib64/python3.6/site-packages/django/__init__.py", line 24, in setup
[Fri Apr 26 18:10:58.314285 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864]     apps.populate(settings.INSTALLED_APPS)
[Fri Apr 26 18:10:58.314307 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864]   File "/var/www/(custom_django_dir)/(custom_section)/virtual_env/lib64/python3.6/site-packages/django/apps/registry.py", line 81, in populate
[Fri Apr 26 18:10:58.314317 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864]     raise RuntimeError("populate() isn't reentrant")
[Fri Apr 26 18:10:58.314350 2019] [wsgi:error] [pid 6294] [remote 142.150.251.220:51864] RuntimeError: populate() isn't reentrant

答案1

我曾经遇到过类似的情况,发现 chcon 只说对了一半。问题是,使用 chcon 所做的更改不是永久性的,最终会被 SELinux 覆盖。

在我的例子中,大多数文件都正确标记,因为我的虚拟环境位于 /var/www 下。但共享对象需要是 httpd_exec_t,这样就可以了:

$ semanage fcontext -a -t httpd_exec_t '/var/www/wsgi/env/.*\.so(\..*)?'
$ restorecon -r /var/www/wsgi/env

您将在 /etc/selinux/targeted/contexts/files/file_contexts.local 中发现这些变化。

想出这个东西需要做大量的研究。大多数人,包括 WSGI 框架提供商,似乎都建议直接停用 SELinux。

[编辑] 相关信息RedHat SELinux 文档

答案2

好的,我似乎已经明白了这一点,它可能对其他人有帮助(在适用的情况下纠正我):

我认为关键是功能文件上下文:

对于与 apache 脚本类似的脚本,我将其视为 CGI 的变体(默认情况下,php 脚本似乎以类似的方式处理),因此我按以下方式设置了 django 核心安装位置:

sudo chcon system_u:object_r:httpd_sys_script_exec_t:s0 (your folder name)

我没有以递归方式执行此操作,因为我有一些静态内容和按应用程序分组的上传目录,因此在执行 django_root 之后,我进入并以递归方式对我的 apps_directories (应用程序,而不是站点目录,因为它们包含静态和可能的上传目录) 应用命令,如果为了速度起见,您想使用“-R”递归选项执行此操作:

sudo chcon -R system_u:object_r:httpd_sys_script_exec_t:s0 (your folder name)

请务必检查每个站点文件上传目录和任何读/写文件(例如 sqlite3 文件)并应用此设置:

sudo chcon system_u:object_r:httpd_sys_rw_content_t:s0 (upload folder name)
sudo chcon system_u:object_r:httpd_sys_rw_content_t:s0 (sqlite3 file name)

并且静态文件目录(包含 css、javascript、图像、django 模板文件(带有模板代码的 *.html))应该使用递归选项给出此设置:

sudo chcon -R -t httpd_sys_content_t (folder name)

注意:这会使这些文件夹/文件保留在其初始的 unconfined_u:object_r 分类中,这似乎没问题。

在我安装虚拟环境的目录中我应用了以下上下文:

sudo chcon -R system_u:object_r:httpd_sys_script_exec_t:s0 (virtual environment directory name)

这似乎是让我的 mod_wsgi 完全运行的关键部分。我之前在 error_log 中收到的错误没有提供直观的线索,基本上它无法访问关键库文件,因为 selinux 限制,我们通过设置 sys_script_exec 上下文来消除这些限制。

如果您已经编译了 *.so 库(就像我对 mod_wsgi 所做的那样),您需要在包含 *.so 文件的目录中最后执行此步骤(在我的情况下,我编译的 mod_wsgi.so 位于(django app 文件夹)/(category 文件夹)/(virtual_env)/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so 使用以下命令:

sudo chcon system_u:object_r:httpd_modules_t:s0 (module filename)

完成后,当您在 *.so 目录中使用 ls -Z 进行确认时,您的 *.so 文件应如下所示:

-rwxrwx-x.root apache system_u:object_r:httpd_modules_t:s0(您的模块)

我设置了 root|apache 只是为了确保权限安全。

我把除了 manage.py 文件之外的所有 django 文件都设置为 apache 组。我将其保留为 root|root。

现在我将 selinux 保持在强制模式,并且我的 django 应用程序使用我编译的 mod_wsgi 模块在自定义位置运行。

希望这对每个人都有帮助。请随时发表评论并进一步教育我们。通过运行以下命令查看 selinux 上的配置,我获得了相当多的帮助:

grep httpd /etc/selinux/targeted/contexts/files/file_contexts | less

您可以看到与 httpd 相关的预配置选项。

这篇文章非常有帮助:

管理受限服务,Apache Http 服务器类型

相关内容