If you are having trouble getting your web server to work or starting services on the system, SELinux could be at fault.
Diagnose problems caused by SELinux
Two major factors can contribute to a service not functioning properly:
- inappropriate SELinux security labels on files and directories,
- inappropriate SELinux rules applied to Unix system resources such as TCP sockets.
Get status of the malfunctioning service
First you need to get the status output for the malfunctioning service (or look up the logs). For example, for PHP 7 that delivers a 404 error in NGINX:
# systemctl status -l php70-php-fpm.service ● php70-php-fpm.service - The PHP FastCGI Process Manager Loaded: loaded (/usr/lib/systemd/system/php70-php-fpm.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Fri 2018-01-08 12:03:16 UTC; 5min ago Process: 13468 ExecStart=/opt/remi/php70/root/usr/sbin/php-fpm --nodaemonize (code=exited, status=78) Main PID: 13468 (code=exited, status=78) Jan 08 12:03:18 ip-16-0-0-40 systemd[1]: Starting The PHP FastCGI Process Manager... Jan 08 12:03:18 ip-16-0-0-40 php-fpm[13468]: [08-Jan-2018 12:03:16] ERROR: unable to bind listening socket for address '127.0.0.1:9002': Permission denied (13) Jan 08 12:03:18 ip-16-0-0-40 php-fpm[13468]: [08-Jan-2018 12:03:16] ERROR: FPM initialization failed Jan 08 12:03:18 ip-16-0-0-40 systemd[1]: php70-php-fpm.service: main process exited, code=exited, status=78/n/a Jan 08 12:03:18 ip-16-0-0-40 systemd[1]: Failed to start The PHP FastCGI Process Manager. Jan 08 12:03:18 ip-16-0-0-40 systemd[1]: Unit php70-php-fpm.service entered failed state. Jan 08 12:03:18 ip-16-0-0-40 systemd[1]: php70-php-fpm.service failed.
The system in the above example is unable to bind the TCP listening socket, as evidenced by this line:
ERROR: unable to bind listening socket for address '127.0.0.1:9002': Permission denied (13)
Correct SELinux security labels on the file system
Navigate to the directory containing the configuration files:
cd /etc/opt/remi/php70/php-fpm.d
View SELinux labels:
# ls -laZ drwxr-xr-x. root root system_u:object_r:etc_t:s0 . drwxr-xr-x. root root system_u:object_r:etc_t:s0 .. -rw-r--r--. root root system_u:object_r:etc_t:s0 www.conf -rw-r--r--. root root unconfined_u:object_r:etc_t:s0 www.website1.tld.conf
Fix the label on the configuration files of php-fpm pools:
chcon -R system_u:object_r:etc_t:s0 www.website1.tld.conf
Troubleshooting access to TCP sockets: build an SELinux module to use TCP sockets
To figure out the changes that are required for SELinux to permit legitimate activities of a service (such as php-fpm or nginx), switch SELinux to permissive mode and build the module it needs using audit2allow, a utility that can generate SELinux allow/dontaudit rules from logs of denied operations (it is contained in policycoreutils-devel). Here’s how to do it.
Step 1. Switch SELinux to permissive
Verify if SELinux is enforcing rules using:
# getenforce
If it is set to enforcing, switch SELinux to the permissive mode using the command:
# setenforce 0
In this mode of operation, SELinux won’t be enforcing its rules, but it will log information about activities it would have prevented if it had been enforcing existing rules.
Step 2. Start the service that failed to load in the SELinux enforcing mode
If the service wasn’t able to run at all because of SELinux, start it:
# service php70-php-fpm restart Redirecting to /bin/systemctl restart php70-php-fpm.service
Next, try to trigger the error you saw before. For example, visit the site in a web browser.
Step 3. Inspect log output of SELinux generated for the service in permissive mode
Check the audit log:
tail /var/log/audit/audit.log | more
You may find messages like this one that reports that php-fpm was denied access to a TCP socket:
type=AVC msg=audit(1529375627.092:172): avc: denied { name_bind } for pid=1822 comm="php-fpm" src=9009 scontext=system_u:system_r:httpd_t: s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=1
Based on the output of tail, you know what to look for when building your SELinux module.
Step 4. Pipe relevant messages to audit2allow
Pipe the relevant output of the SELinux audit.log for the service in question to a temporary file for further inspection:
grep php-fpm /var/log/audit/audit.log | audit2allow -M phpfpm > phpfpmlocal.tmp
Inspect the file you created (phpfpmlocal.tmp):
# cat phpfpmlocal.tmp module phpfpm 1.0; require { type tor_port_t; type unreserved_port_t; type hugetlbfs_t; type httpd_t; type httpd_sys_content_t; class process execmem; class tcp_socket name_bind; class dir write; class file { write append }; } #============= httpd_t ============== #!!!! This avc can be allowed using the boolean 'httpd_unified' allow httpd_t httpd_sys_content_t:dir write; #!!!! This avc can be allowed using the boolean 'httpd_unified' allow httpd_t httpd_sys_content_t:file append; allow httpd_t hugetlbfs_t:file write; #!!!! This avc can be allowed using the boolean 'httpd_execmem' allow httpd_t self:process execmem; allow httpd_t tor_port_t:tcp_socket name_bind; #!!!! This avc can be allowed using the boolean 'nis_enabled' allow httpd_t unreserved_port_t:tcp_socket name_bind;
Make any edits to the require directive above that seem necessary.
You have two options at this point. You can either build and activate a SELinux module (Step 5, option 2) or enable the corresponding booleans (Step 5 option 1).
Step 5, option 1. Set SELinux booleans
To set the corresponding boolean (following the directions from Step 4), for example:
semanage boolean -m --on nis_enabled
Here, nis_enabled is the boolean that was supplied in the output of audit2allow in Step 4 above. The change persists across reboots.
Step 5, option 2. Build and enable the SELinux module
Re-run audit2allow to build the module:
grep php-fpm /var/log/audit/audit.log | audit2allow -M phpfpmlocal ******************** IMPORTANT *********************** To make this policy package active, execute: semodule -i phpfpmlocal.pp
(If there is no policy to be activated based on the audit log snippets you supplied in Step 4., audit2allow will fail to create the module and then the command semodule will also fail.)
Activate the module:
semodule -i phpfpmlocal.pp
(Now you may remove the three phpfpmlocal.* files that were created as the system no longer needs them.)
Step 6. Reactivate SELinux enforcing mode and restart the service
Set enforce back on:
setenforce 1
Verify that SELinux is enforcing:
# getenforce Enforcing
Restart the service for which you fixed the rules:
systemctl restart nginx php-fpm
Verify that everything is working as it should for the service:
systemctl status -l php-fpm.service
You have granted php-fpm access to a TCP socket so it happily starts without complaints. However, NGINX may still keep giving you 404 errors for lack of access to the TCP socket.
A TCP socket allows two (or more) services to communicate with one another. For this communication to work, both services need unhindered access to the socket. As a result, you need to repeat the above procedure for NGINX.
Create a custom SELinux module for NGINX to use a TCP socket
Repeat the steps required for SELinux to grant NGINX access tot he TCP socket that PHP-FPM is listening on.
Step 1. Pipe audit.log messages referring to NGINX to audit2allow
Use the audit2allow utility to view relevant messages in the logs:
grep nginx /var/log/audit/audit.log | audit2allow
showing for example this output:
#============= httpd_t ==============
#!!!! This avc can be allowed using one of the these booleans: # nis_enabled, httpd_can_network_connect allow httpd_t unreserved_port_t:tcp_socket name_connect;
Pipe relevant SELinux AVC messages to audit2allow to create the SELinux module:
grep nginx /var/log/audit/audit.log | audit2allow -m nginx
The output may look like this:
module nginx 1.0; require { type httpd_t; type unreserved_port_t; class tcp_socket name_connect; } #============= httpd_t ============== #!!!! This avc can be allowed using one of the these booleans: # nis_enabled, httpd_can_network_connect allow httpd_t unreserved_port_t:tcp_socket name_connect;
Generate a local nginx Type Enforcement policy file (nginx.tmp):
grep nginx /var/log/audit/audit.log | audit2allow -m nginx > nginx.tmp cat nginx.tmp
Use audit2allow to create a custom policy module which allows NGINX access to the TCP socket:
grep nginx /var/log/audit/audit.log | audit2allow -M nginx
To load the policy package into the kernel, execute:
semodule -i nginx.pp
Wrap it up by switching SELinux back to its enforcing mode:
setenforce 1
Restart nginx.
Verify that SELinux is enforcing rules:
# getenforce Enforcing
List loaded modules
semodule -l
Congratulations, you are done.
Leave a Reply