#################################################### # 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