So far, all we’ve looked at is what happens when we have an incorrect SELinux type set on a file and what to do to set the correct type. Another problem we may have would comes about if we need to allow an action that is prohibited by the active SELinux policy.
Booleans are part of what makes up an SELinux policy, and each Boolean represents a binary choice. In SELinux policies, a Boolean either allows something or it prohibits something. To see all Booleans on your system, run the getsebool -a command. (It’s a long list, so I’ll only show partial output here.):
[donnie@localhost ~]$ getsebool -a abrt_anon_write --> off abrt_handle_event --> off abrt_upload_watch_anon_write --> on antivirus_can_scan_system --> off antivirus_use_jit --> off auditadm_exec_content --> on . . . . . . zarafa_setrlimit --> off zebra_write_config --> off zoneminder_anon_write --> off zoneminder_run_sudo --> off [donnie@localhost ~]$
To view more than one Boolean, the -a switch is mandatory. If you just happen to know the name of the Boolean that you want to see, leave the -a out and list it. In keeping with the Apache web server theme that we’ve had going, let’s see whether we’re allowing Apache to access files in users’ home directories:
[donnie@localhost html]$ getsebool httpd_enable_homedirs httpd_enable_homedirs --> off [donnie@localhost html]$
The fact that this Boolean is off means that the Apache server daemon isn’t allowed to access any content within the users’ home directories. This is an important protection, and you really don’t want to change it. Instead, just put web content files elsewhere so that you don’t have to change this Boolean.
Most likely, you’ll rarely want to look at the entire list, and you likely won’t know the name of the specific Boolean that you want to see. Rather, you’ll probably want to filter the output through grep in order to look at just certain things. For example, to see all of the Booleans that affect a web server, do this:
[donnie@localhost html]$ getsebool -a | grep 'http' httpd_anon_write --> off httpd_builtin_scripting --> on httpd_can_check_spam --> off httpd_can_connect_ftp --> off httpd_can_connect_ldap --> off . . . . . . httpd_use_nfs --> off httpd_use_openstack --> off httpd_use_sasl --> off httpd_verify_dns --> off named_tcp_bind_http_port --> off prosody_bind_http_port --> off [donnie@localhost html]$
It’s also a rather long list, but scroll down a little and you’ll find the Boolean that you seek.
Realistically, you’ll likely never have reason to allow users to serve web content out of their home directories. It’s much more probable that you’ll set up something like a Samba server, which would allow users on Windows machines to use their graphical Windows Explorer to access their home directories on Linux servers. But if you set up a Samba server and don’t do anything with SELinux, users will complain about how they don’t see any of their files in their home directories of the Samba server. Because you’re the proactive type and you want to avoid the pain of listening to complaining users, you’ll surely just go ahead and configure SELinux to allow the Samba daemon to access users’ home directories. You might not know the exact name of the Boolean, but you can find it easily enough, as follows:
[donnie@localhost html]$ getsebool -a | grep 'home' git_cgi_enable_homedirs --> off git_system_enable_homedirs --> off httpd_enable_homedirs --> off mock_enable_homedirs --> off mpd_enable_homedirs --> off openvpn_enable_homedirs --> on samba_create_home_dirs --> off samba_enable_home_dirs --> off . . . use_samba_home_dirs --> off xdm_write_home --> off [donnie@localhost html]$
Okay, you knew that the Boolean name probably had the word home in it, so you filtered for that word. About half-way down the list, you see samba_enable_home_dirs --> off. You’ll need to change this to on to let users access their home directories from their Windows machines:
[donnie@localhost html]$ sudo setsebool samba_enable_home_dirs on [sudo] password for donnie: [donnie@localhost html]$ getsebool samba_enable_home_dirs samba_enable_home_dirs --> on [donnie@localhost html]$
Users can now access their home directories as they should be able to, but only until you do a system reboot. Without the -P option, any changes you make with setsebool will only be temporary. So, let’s make the change permanent with -P:
[donnie@localhost html]$ sudo setsebool -P samba_enable_home_dirs on [donnie@localhost html]$ getsebool samba_enable_home_dirs samba_enable_home_dirs --> on [donnie@localhost html]$
Congratulations, you’ve just made your first change to SELinux policy.
Look at the output of the getsebool -a | grep 'http' command again, and you’ll see that most httpd-related Booleans are turned off by default, with only a few turned on. There are two of them that you’ll commonly need to turn on when setting up a web server.
If you ever need to set up a website with some sort of PHP-based content management system, such as Joomla or WordPress, you may have to turn on the httpd_unified Boolean. With this Boolean turned off, the Apache web server won’t be able to interact properly with all of the components of the PHP engine:
[donnie@localhost ~]$ getsebool httpd_unified httpd_unified --> off [donnie@localhost ~]$ sudo setsebool -P httpd_unified on [sudo] password for donnie: [donnie@localhost ~]$ getsebool httpd_unified httpd_unified --> on [donnie@localhost ~]$
The other Boolean that you’ll commonly need to turn on is the httpd_can_sendmail Boolean. If you ever need a website to send mail out through a form (or if you need to set up a mail server with a web-based frontend), you’ll definitely need to set this to on:
[donnie@localhost ~]$ getsebool httpd_can_sendmail httpd_can_sendmail --> off [donnie@localhost ~]$ sudo setsebool -P httpd_can_sendmail on [donnie@localhost ~]$ getsebool httpd_can_sendmail httpd_can_sendmail --> on [donnie@localhost ~]$
On the other hand, there are some Booleans that are turned on by default, and you might want to consider whether you really need them turned on. For example, allowing CGI scripts to run on a web server does represent a potential security risk. If an intruder were to somehow upload a malicious CGI script to the server and run it, much damage could occur as a result. Yet, for some bizarre reason, the default SELinux policy allows CGI scripts to run. If you’re absolutely certain that nobody who hosts websites on your server will ever need to run CGI scripts, you might want to consider turning this Boolean off:
[donnie@localhost ~]$ getsebool httpd_enable_cgi httpd_enable_cgi --> on [donnie@localhost ~]$ sudo setsebool -P httpd_enable_cgi off [donnie@localhost ~]$ getsebool httpd_enable_cgi httpd_enable_cgi --> off [donnie@localhost ~]$
Each network daemon that’s running on your system has a specific network port or set of network ports assigned to it, on which it will listen. The /etc/services file contains a list of common daemons and their associated network ports, but it doesn’t prevent someone from configuring a daemon to listen on some non-standard port. So, without some mechanism to prevent it, some sneaky intruder could potentially plant some sort of malware that would cause a daemon to listen on a non-standard port, possibly listening for commands from its master.
SELinux protects against this sort of malicious activity by only allowing daemons to listen on certain ports. Use semanage to look at the list of allowed ports:
[donnie@localhost ~]$ sudo semanage port -l SELinux Port Type Proto Port Number afs3_callback_port_t tcp 7001 afs3_callback_port_t udp 7001 afs_bos_port_t udp 7007 . . . . . . zented_port_t udp 1229 zookeeper_client_port_t tcp 2181 zookeeper_election_port_t tcp 3888 zookeeper_leader_port_t tcp 2888 zope_port_t tcp 8021 [donnie@localhost ~]$
This is yet another of those very long lists, so I’m only showing partial output. However, let’s narrow things down a bit. Let’s say that I only want to look at a list of ports on which the Apache web server can listen. For this, I’ll use my good friend grep:
[donnie@localhost ~]$ sudo semanage port -l | grep 'http' [sudo] password for donnie: http_cache_port_t tcp 8080, 8118, 8123, 10001-10010 http_cache_port_t udp 3130 http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000 pegasus_http_port_t tcp 5988 pegasus_https_port_t tcp 5989 [donnie@localhost ~]$
Several http items come up, but I’m only interested in the http_port_t item because it’s the one that affects normal web server operation. We see here that SELinux will allow Apache to listen on ports 80, 81, 443, 488, 8008, 8009, 8443, and 9000. Since the Apache server is one of the few daemons you’d ever have a legitimate reason for adding a non-standard port to, let’s demo with it.
First, let’s go into the /etc/httpd/conf/httpd.conf file and look at the ports on which Apache is currently listening. Search for Listen, and you’ll see the following line:
I don’t have the SSL module installed on this machine, but if I did I would have an ssl.conf file in the /etc/httpd/conf.d directory with this line:
So for normal, non-encrypted website connections, the default configuration only has Apache listening on port 80. For secure, encrypted website connections, Apache listens on port 443. Now, let’s go into the httpd.conf file and change Listen 80 to a port number that SELinux doesn’t allow, for example, port 82:
After saving the file, I’ll restart Apache to read in the new configuration:
[donnie@localhost ~]$ sudo systemctl restart httpd Job for httpd.service failed because the control process exited with error code. See "systemctl status httpd.service" and "journalctl -xe" for details. [donnie@localhost ~]$
Yes, I have a problem. I’ll look in the /var/log/messages file to see if setroubleshoot gives me a clue:
Nov 29 16:39:21 localhost python: SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket port 82.#012#012***** Plugin bind_ports (99.5 confidence) suggests ************************#012#012If you want to allow /usr/sbin/httpd to bind to network port 82#012Then you need to modify the port type.#012Do#012# semanage port -a -t PORT_TYPE -p tcp 82#012 where PORT_TYPE is one of the following: http_cache_port_t, http_port_t, jboss_management_port_t, jboss_messaging_port_t, ntop_port_t, puppet_port_t.#012#012***** Plugin catchall (1.49 confidence) suggests **************************#012#012If you believe that httpd should be allowed name_bind access on the port 82 tcp_socket by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'httpd' --raw | audit2allow -M my-httpd#012# semodule -i my-httpd.pp#012
The problem that details how SELinux is preventing httpd from binding to port 82 is defined in the first line of the message. The first suggestion we see for fixing this is to use semanage to add the port to the list of allowed ports. So, let’s do that and look at the list of Apache ports:
[donnie@localhost ~]$ sudo semanage port -a 82 -t http_port_t -p tcp [donnie@localhost ~]$ sudo semanage port -l | grep 'http_port_t' http_port_t tcp 82, 80, 81, 443, 488, 8008, 8009, 8443, 9000 pegasus_http_port_t tcp 5988 [donnie@localhost ~]$
It’s not clear in the setroubleshoot message, but you need to specify the port number that you want to add after port -a. -t http_port_t specifies the type for which you want to add the port, and -p tcp specifies that you want to use the TCP protocol.
Now for the moment of truth. Will the Apache daemon start this time? Let’s see:
[donnie@localhost ~]$ sudo systemctl restart httpd [sudo] password for donnie: [donnie@localhost ~]$ sudo systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled) Active: active (running) since Wed 2017-11-29 20:09:51 EST; 7s ago Docs: man:httpd(8) . . . . . .
It works, and we have achieved coolness. But now, I’ve decided that I no longer need this oddball port. Deleting it is just as easy as adding it:
[donnie@localhost ~]$ sudo semanage port -d 82 -t http_port_t -p tcp [donnie@localhost ~]$ sudo semanage port -l | grep 'http_port_t' http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000 pegasus_http_port_t tcp 5988 [donnie@localhost ~]$
All I had to do was to replace port -a with port -d. And of course, I still need to go into the /etc/httpd/conf/httpd.conf file to change Listen 82 back to Listen 80.
Sometimes, you’ll run into a problem that you can’t fix either by changing the type or by setting a Boolean. In times like these, you’ll need to create a custom policy module, and you’ll use the audit2allow utility to do that.
The following is a screenshot of a problem I had several years ago, when I was helping a client set up a Postfix mail server on CentOS 7:
So, for some strange reason that I never understood, SELinux wouldn’t allow Dovecot, the Mail Delivery Agent (MDA) component of the mail server, to read its own dict file. There’s no Boolean to change and there wasn’t a type problem, so setroubleshoot suggested that I create a custom policy module. It’s easy enough to do, but you do need to be aware that this won’t work with sudo on your normal user account. This is one of those rare times when you’ll just have to go to the root user command prompt, and you’ll also need to be in the root user’s home directory:
sudo su -
Before you do it, be sure to put SELinux into permissive mode and then do something to induce the SELinux error. This way, you’ll be sure that one problem isn’t masking others.
When you run the command to create the new policy module, be sure to replace mypol with a custom policy name of your own choosing. In my case, I named the module dovecot_dict, and the command looked like the following:
grep dict /var/log/audit/audit.log | audit2allow -M dovecot_dict
What I’m doing here is using grep to search through the audit.log file for SELinux messages that contain the word dict. I then pipe the output of that into audit2allow and use the -M option to create a custom module with the name dovecot_dict.
After I created the new policy module, I inserted it into the SELinux policy like so:
semodule -i dovecot_dict.pp
There was a also a second problem that required another custom module, but I just repeated this procedure to produce another module of a different name. After I got all that done, I reloaded the SELinux policy, in order to get my new modules to take effect:
With semodule, the -R switch stands for reload, rather than recursive, as it does with most Linux commands.
With all that done, I put SELinux back into enforcing mode and exited back to my own user account. And I tested the setup to make sure that I had fixed the problem.
Of course, you also want to bear in mind that you don’t want to just modify SELinux policy or contexts every time you see an sealert message in the log files. For example, consider this snippet from the messages file of my Oracle Linux 7 machine, which I set up mainly to run Docker and Docker containers:
Jun 8 19:32:17 docker-1 setroubleshoot: SELinux is preventing /usr/bin/docker from getattr access on the file /etc/exports. For complete SELinux messages. run sealert -l b267929a-d3ad-45d5-806e-907449fc2739 Jun 8 19:32:17 docker-1 python: SELinux is preventing /usr/bin/docker from getattr access on the file /etc/exports.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that docker should be allowed getattr access on the exports file by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# grep docker /var/log/audit/audit.log | audit2allow -M mypol#012# semodule -i mypol.pp#012 Jun 8 19:32:17 docker-1 setroubleshoot: SELinux is preventing /usr/bin/docker from getattr access on the file /etc/shadow.rpmnew. For complete SELinux messages. run sealert -l . . .
These messages were caused by an early version of Docker trying to access resources on the host machine. As you can see, Docker is trying to access some rather sensitive files, and SELinux is preventing Docker from doing so. With Docker, and without some sort of MAC, it can be a trivial matter for a normal, unprivileged user to escape from the Docker container and have root user privileges on the host system. Naturally, when you see these sorts of message, you don’t want to automatically tell SELinux to allow the prohibited actions. It just may be that SELinux is preventing something truly bad from taking place.
In this lab, you’ll view the effects of having Apache try to listen on an unauthorized port:
- View the ports that SELinux allows the Apache web server daemon to use:
sudo semanage port -l | grep 'http'
- Open the /etc/httpd/conf/httpd.conf file in your favorite text editor. Find the line that says Listen 80 and change it to Listen 82. Restart Apache by entering the following:
sudo systemctl restart httpd
- View the error message you receive by entering:
sudo tail -20 /var/log/messages
- Add port 82 to the list of authorized ports and restart Apache:
sudo semanage port -a 82 -t http_port_t -p tcp sudo semanage port -l sudo systemctl restart httpd
- Delete the port that you just added:
sudo semanage -d 82 -t http_port_t -p tcp
- Go back into the /etc/httpd/conf/httpd.conf file and change Listen 82 back to Listen 80. Restart the Apache daemon to return to normal operation.
- End of lab.
Okay, you’ve seen how SELinux can protect you against various bad things, and how to troubleshoot things that go wrong. Let’s turn our attention to AppArmor.