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.



