####################################################
# Serving httpd content from non-standard location #
####################################################
####
#### Objective: Install, configure, and start httpd to serve content from /webcontent
####
#### Basic steps:
#### - Install httpd
#### - Start and enable service
#### - Add port to firewall
#### - Update httpd config to serve from /webcontent
#### - Add correct label for /webcontent to default policy
#### - Start/restart httpd
#### Full solution:
#### Install packages for apache (httpd)
[root@localhost ~]# yum group install "basic web server"
#### start/enable httpd service, and make hole in firewall for tcp/8001
[root@localhost ~]# systemctl enable httpd
ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/multi-user.target.wants/httpd.service'
[root@localhost ~]# systemctl start httpd
[root@localhost ~]# firewall-cmd --list-all
public (default, active)
interfaces: enp0s3
sources:
services: dhcpv6-client ssh
ports:
masquerade: no
forward-ports:
icmp-blocks:
rich rules:
[root@localhost ~]# firewall-cmd --permanent --add-service=http
success
[root@localhost ~]# firewall-cmd --reload
success
[root@localhost ~]# firewall-cmd --list-all
public (default, active)
interfaces: enp0s3
sources:
services: dhcpv6-client http ssh
ports:
masquerade: no
forward-ports:
icmp-blocks:
rich rules:
#### httpd is now listening on 80 and 443
[root@localhost ~]# ss -lntp | grep httpd
LISTEN 0 128 :::80 :::* users:(("httpd",9724,4),("httpd",9723,4),("httpd",9722,4),("httpd",9721,4),("httpd",9720,4),("httpd",9719,4),("httpd",9718,4))
LISTEN 0 128 :::443 :::* users:(("httpd",9724,6),("httpd",9723,6),("httpd",9722,6),("httpd",9721,6),("httpd",9720,6),("httpd",9719,6),("httpd",9718,6))
#### Observe in /etc/httpd/conf/httpd.conf what your document root is
[root@localhost ~]# grep ^DocumentRoot /etc/httpd/conf/httpd.conf
DocumentRoot "/webcontent/html"
#### Create a recognizable page in /var/www/html
[root@localhost ~]# echo "original" > /var/www/html/index.html
#### Verify that you get the original page from the web server
[root@localhost ~]# curl http://localhost
original
#### Create a new directory for web content and create new index page
[root@localhost ~]# mkdir -p /webcontent/html
[root@localhost ~]# echo "new" > /webcontent/html/index.html
#### Update httpd configuration to point to new location
#### change 'DocumentRoot "/var/www/html"' to 'DocumentRoot "/webcontent/html"'
#### and add the following right below it
-----
Require all granted
-----
vi /etc/httpd/conf/httpd.conf
#### Reload httpd config and verfiy service is still up
[root@localhost ~]# systemctl reload httpd
[root@localhost ~]# ss -lntp | grep httpd
LISTEN 0 128 :::80 :::* users:(("httpd",3128,4),("httpd",3127,4),("httpd",3126,4),("httpd",3125,4),("httpd",3124,4),("httpd",3123,4),("httpd",2690,4))
LISTEN 0 128 :::443 :::* users:(("httpd",3128,6),("httpd",3127,6),("httpd",3126,6),("httpd",3125,6),("httpd",3124,6),("httpd",3123,6),("httpd",2690,6))
#### Try to get the webpage
#### You should get the default CentOS apache landing page
[root@localhost ~]# curl http://localhost
[trimmed]
#### From /var/log/httpd/error_log
[Sat Oct 03 18:39:11.113711 2015] [core:error] [pid 3458] (13)Permission denied: [client ::1:58071] AH00035: access to /index.html denied (filesystem path '/webcontent/html/index.html') because search permissions are missing on a component of the path
#### From /var/log/audit/audit.log
type=AVC msg=audit(1443911951.112:923): avc: denied { getattr } for pid=3458 comm="httpd" path="/webcontent/html/index.html" dev="dm-0" ino=1083512 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:default_t:s0 tclass=file
type=SYSCALL msg=audit(1443911951.112:923): arch=c000003e syscall=4 success=no exit=-13 a0=7f607a495dc8 a1=7fffaba5cfb0 a2=7fffaba5cfb0 a3=7f606f31d792 items=0 ppid=2690 pid=3458 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=system_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1443911951.112:924): avc: denied { getattr } for pid=3458 comm="httpd" path="/webcontent/html/index.html" dev="dm-0" ino=1083512 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:default_t:s0 tclass=file
type=SYSCALL msg=audit(1443911951.112:924): arch=c000003e syscall=6 success=no exit=-13 a0=7f607a495ea8 a1=7fffaba5cfb0 a2=7fffaba5cfb0 a3=1 items=0 ppid=2690 pid=3458 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=system_u:system_r:httpd_t:s0 key=(null)
#### Nothing in /var/log/messages or service logs (systemctl status httpd)
#### The audit log errors look like file label issues
#### Let's check the label on our index file, doesn't look right
[root@localhost ~]# ls -laZR /webcontent/
/webcontent/:
drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 .
dr-xr-xr-x. root root system_u:object_r:root_t:s0 ..
drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 html
/webcontent/html:
drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 .
drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 ..
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 index.html
#### Check to see what the label is in the default policy, but wait no semanage command
[root@localhost ~]# semanage -l | grep httpd_sys_content_t
-bash: semanage: command not found
#### What package is that from?
[root@localhost ~]# yum provides semanage
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
* base: mirror.team-cymru.org
* extras: mirror.team-cymru.org
* updates: mirror.team-cymru.org
policycoreutils-python-2.2.5-15.el7.x86_64 : SELinux policy core python utilities
Repo : base
Matched from:
Filename : /usr/sbin/semanage
#### Great, let's install it
[root@localhost ~]# yum install -y policycoreutils-python
#### Now let's check the default policy for httpd_sys_content_t
[root@localhost ~]# semanage fcontext -l | grep httpd_sys_content_t
/etc/htdig(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/srv/([^/]*/)?www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/srv/gallery2(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/doc/ghc/html(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/drupal.* all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/glpi(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/htdig(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/icecast(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/ntop/html(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/openca/htdocs(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/selinux-policy[^/]*/html(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/z-push(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/lib/cacti/rra(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/lib/htdig(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/lib/trac(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/www/icons(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/www/svn/conf(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
#### Update default policy to include correct label for /webcontent and it's sub-directories, and check it's there
[root@localhost ~]# semanage fcontext -a -t httpd_sys_content_t '/webcontent(/.*)?'
[root@localhost ~]# semanage fcontext -l | grep httpd_sys_content_t
/etc/htdig(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/srv/([^/]*/)?www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/srv/gallery2(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/doc/ghc/html(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/drupal.* all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/glpi(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/htdig(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/icecast(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/ntop/html(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/openca/htdocs(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/selinux-policy[^/]*/html(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/z-push(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/lib/cacti/rra(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/lib/htdig(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/lib/trac(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/www/icons(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/www/svn/conf(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/webcontent(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
#### Apply the labels and verify
[root@localhost ~]# restorecon -vvFR /webcontent/
restorecon reset /webcontent context unconfined_u:object_r:default_t:s0->system_u:object_r:httpd_sys_content_t:s0
restorecon reset /webcontent/html context unconfined_u:object_r:default_t:s0->system_u:object_r:httpd_sys_content_t:s0
restorecon reset /webcontent/html/index.html context unconfined_u:object_r:default_t:s0->system_u:object_r:httpd_sys_content_t:s0
[root@localhost ~]# ls -laZR /webcontent/
/webcontent/:
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 .
dr-xr-xr-x. root root system_u:object_r:root_t:s0 ..
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 html
/webcontent/html:
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 .
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 ..
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html
#### You don't even have to reload httpd config so just verify that it works
[root@localhost ~]# curl http://localhost
new
#### Success!
############################
# Troubleshooting Appendix #
############################
#### If you're familiar with the error, then the output above from audit.log may give you enough clues
#### But here is one thing that you can do to make unknown errors more understandable
#### install setroubleshoot-server package
yum install -i setroubleshoot-server
#### Run sealert on /var/log/audit/audit.log
[root@localhost ~]# sealert -a /var/log/audit/audit.log
100% done'list' object has no attribute 'split'
100% done
found 1 alerts in /var/log/audit/audit.log
--------------------------------------------------------------------------------
SELinux is preventing /usr/sbin/httpd from getattr access on the file /webcontent/html/index.html.
***** Plugin catchall_labels (83.8 confidence) suggests *******************
If you want to allow httpd to have getattr access on the index.html file
Then you need to change the label on /webcontent/html/index.html
Do
# semanage fcontext -a -t FILE_TYPE '/webcontent/html/index.html'
where FILE_TYPE is one of the following: NetworkManager_exec_t, NetworkManager_log_t, NetworkManager_tmp_t, abrt_dump_oops_exec_t, abrt_etc_t, abrt_exec_t, abrt_handle_event_exec_t, abrt_helper_exec_t,
[trimmed]
Then execute:
restorecon -v '/webcontent/html/index.html'
***** Plugin catchall (17.1 confidence) suggests **************************
If you believe that httpd should be allowed getattr access on the index.html file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# grep httpd /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp
Additional Information:
Source Context system_u:system_r:httpd_t:s0
Target Context system_u:object_r:default_t:s0
Target Objects /webcontent/html/index.html [ file ]
Source httpd
Source Path /usr/sbin/httpd
Port
Host
Source RPM Packages httpd-2.4.6-31.el7.centos.1.x86_64
Target RPM Packages
Policy RPM selinux-policy-3.13.1-23.el7.noarch
Selinux Enabled True
Policy Type targeted
Enforcing Mode Enforcing
Host Name localhost.localdomain
Platform Linux localhost.localdomain 3.10.0-229.el7.x86_64
#1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64
Alert Count 1
First Seen 2015-10-03 18:39:11 EDT
Last Seen 2015-10-03 18:39:11 EDT
Local ID 26911621-bfcb-44e6-bad2-80e2d09b1630
Raw Audit Messages
type=AVC msg=audit(1443911951.112:924): avc: denied { getattr } for pid=3458 comm="httpd" path="/webcontent/html/index.html" dev="dm-0" ino=1083512 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:default_t:s0 tclass=file
type=SYSCALL msg=audit(1443911951.112:924): arch=x86_64 syscall=lstat success=no exit=EACCES a0=7f607a495ea8 a1=7fffaba5cfb0 a2=7fffaba5cfb0 a3=1 items=0 ppid=2690 pid=3458 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm=httpd exe=/usr/sbin/httpd subj=system_u:system_r:httpd_t:s0 key=(null)
Hash: httpd,httpd_t,default_t,file,getattr
##################
#### The sealert output actually gives me some commands to run to fix it. Let's try the second method
#### This looks like it was trying to fix http trying to get file attributes on a file
#### Its method fixing it is to allow httpd_t to get attributes on default_t (basically most) files
#### This is probably not the fix that you want, so beware
[root@localhost ~]# grep httpd /var/log/audit/audit.log | audit2allow -M mypol
******************** IMPORTANT ***********************
To make this policy package active, execute:
semodule -i mypol.pp
[root@localhost ~]# cat mypol.te
module mypol 1.0;
require {
type httpd_t;
type default_t;
class file getattr;
}
#============= httpd_t ==============
allow httpd_t default_t:file getattr;
#### The method in the first alert was actually what we want to do, though that may not
#### have been very clear from the way it was stated