Locking Down Apache

I noticed my Apache web server had one process that ran as root, which then forked other processes as user webservd:

bleonard@os200906:~$ ps -ef | grep httpd
webservd   853   850   0 14:05:43 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd   852   850   0 14:05:43 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd   851   850   0 14:05:43 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
    root   850     1   1 14:05:42 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd   854   850   0 14:05:43 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd   855   850   0 14:05:43 ?           0:00 /usr/apache2/2.2/bin/httpd -k start

The reason for this is that apache wants access to port 80, which traditionally requires root privileges. To improve upon this all-or-nothing security model, Solaris 10 introduced the concept of fine-grained privileges, and in OpenSolaris there are now 75 of them:

bleonard@os200906:~$ ppriv -l 
contract_event
contract_identity
contract_observer
cpc_cpu
dtrace_kernel
dtrace_proc
dtrace_user
file_chown
file_chown_self
file_dac_execute
file_dac_read
file_dac_search
file_dac_write
file_downgrade_sl
file_link_any
file_owner
file_setid
file_upgrade_sl
file_flag_set
graphics_access
graphics_map
ipc_dac_read
ipc_dac_write
ipc_owner
net_bindmlp
net_icmpaccess
net_mac_aware
net_observability
net_privaddr
net_rawaccess
proc_audit
proc_chroot
proc_clock_highres
proc_exec
proc_fork
proc_info
proc_lock_memory
proc_owner
proc_priocntl
proc_session
proc_setid
proc_taskid
proc_zone
sys_acct
sys_admin
sys_audit
sys_config
sys_devices
sys_ipc_config
sys_linkdir
sys_mount
sys_dl_config
sys_ip_config
sys_net_config
sys_nfs
sys_res_config
sys_resource
sys_smb
sys_suser_compat
sys_time
sys_trans_label
virt_manage
win_colormap
win_config
win_dac_read
win_dac_write
win_devices
win_dga
win_downgrade_sl
win_fontpath
win_mac_read
win_mac_write
win_selection
win_upgrade_sl
xvm_control

To get a description of each privilege, run ppriv -lv.

What this means is that I can now give a process, which has traditionally run with root privileges, just the privileges it needs to get its job done - a concept known as least privilege. The trick, of course, is figuring out which privileges a process needs.

If we look at the privileges for process 850 above, we see that it has them all:

bleonard@os200906:~$ pfexec ppriv 850
850:	/usr/apache2/2.2/bin/httpd -k start
flags = <none>
	E: all
	I: basic
	P: all
	L: all

Let's try to start Apache as a typical non-privileged user, webservd, to see what happens. First, make sure Apache's not already running:

bleonard@os200906:~$ svcadm disable apache22

Then, switch to user webservd and try to start the server:

bleonard@os200906:~$ pfexec su - webservd
webservd@os200906:~$ /usr/apache2/2.2/bin/apachectl start                       
(13)Permission denied: make_sock: could not bind to address [::]:80
(13)Permission denied: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
Unable to open log

To see exactly which permission is needed for Apache to bind to port 80, run the command again with privilege debugging:

webservd@os200906:~$ ppriv -eD /usr/apache2/2.2/bin/apachectl start
httpd[885]: missing privilege "proc_priocntl" (euid = 80, syscall = 112) needed at secpolicy_setpriority+0x24
httpd[885]: missing privilege "ALL" (euid = 80, syscall = 80) needed at zfs_zaccess+0x1fc
httpd[885]: missing privilege "net_privaddr" (euid = 80, syscall = 232) needed at tcp_bind_select_lport+0xbb
(13)Permission denied: make_sock: could not bind to address [::]:80
httpd[885]: missing privilege "net_privaddr" (euid = 80, syscall = 232) needed at tcp_bind_select_lport+0xbb
(13)Permission denied: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
Unable to open logs

You can see the description for the net_privaddr privilege as follows:

webservd@os200906:~$ ppriv -lv net_privaddr
net_privaddr
	Allows a process to bind to a privileged port
	number. The privilege port numbers are 1-1023 (the traditional
	UNIX privileged ports) as well as those ports marked as
	"udp/tcp_extra_priv_ports" with the exception of the ports
	reserved for use by NFS.

So, exit out of the webservd user account (the webservd user doesn't have the rights to add additional privileges) and add the net_privaddr privilege to user webservd:

bleonard@os200906:~$ pfexec usermod -K defaultpriv=basic,net_privaddr webservd

Then log in again to webservd and try starting Apache again:

bleonard@os200906:~$ pfexec su - webservd
webservd@os200906:~$ /usr/apache2/2.2/bin/apachectl start
(13)Permission denied: httpd: could not open error log file /var/apache2/2.2/logs/error_log.
Unable to open log

The remaining error is an easy one to fix. The /var/apache2/2.2/logs directory is already owned by webservd. It's just that root has previously created its log files there. Either delete these files or change their ownership and the server should start successfully:

webservd@os200906:~$ /usr/apache2/2.2/bin/apachectl start               
webservd@os200906:~$ ps -ef | grep httpd         
webservd   914   911   0 15:16:05 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd   916   911   0 15:16:05 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd   917   911   0 15:16:05 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd   915   911   0 15:16:05 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd   911     1   1 15:16:04 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd   913   911   0 15:16:05 ?           0:00 /usr/apache2/2.2/bin/httpd -k start

So, the next question is how do you bring these changes to Apache when started with SMF? Glenn Brunette has an excellent white paper that address this topic titled: Limiting Service Privileges in the Solaris 10 Operating System. However, I'll hit the highlights for you here.

First, stop Apache:

 /usr/apache2/2.2/bin/apachectl stop

Then look at the existing SMF properties defined for starting the service:

bleonard@os200906:~$ svccfg -s apache22
svc:/network/http:apache22> listprop start
start                  method
start/exec             astring  "/lib/svc/method/http-apache22 start"
start/timeout_seconds  count    60
start/type             astring  method

Now, save the additional properties that we want our service to have to a text file. I'm calling mine apache_cfg:

bleonard@os200906:~$ cat apache_cfg 
select apache22
setprop start/user = astring: webservd
setprop start/group = astring: webservd
setprop start/privileges = astring: basic,!proc_session,!proc_info,!file_link_any,net_privaddr
setprop start/limit_privileges = astring: :default
setprop start/use_profile = boolean: false
setprop start/supp_groups = astring: :default
setprop start/working_directory = astring: :default
setprop start/project = astring: :default
setprop start/resource_pool = astring: :default
end

In the above we're setting a bunch of additional properties for the Apache service, they key ones being start/user, start/group and start/privileges (note, the privileges changes we made earlier to user webservd are no longer necessary. The SMF framework does not consider a user's privileges when starting process, but rather the privileges as defined in the manifest.). However, all of the other properties (set to :default) need to be there for them to work properly.

Then run the following command to apply the property changes:

svccfg -f apache_cfg

To see the effect:

bleonard@os200906:~$ svccfg -s apache22
svc:/network/http:apache22> listprop start
start                    method
start/exec               astring  "/lib/svc/method/http-apache22 start"
start/timeout_seconds    count    60
start/type               astring  method
start/user               astring  webservd
start/group              astring  webservd
start/privileges         astring  basic,!proc_session,!proc_info,!file_link_any,net_privaddr
start/limit_privileges   astring  :default
start/use_profile        boolean  false
start/supp_groups        astring  :default
start/working_directory  astring  :default
start/project            astring  :default
start/resource_pool      astring  :default
svc:/network/http:apache22> quit

Then commit the changes and enable Apache:

bleonard@os200906:~$ svcadm refresh apache22
bleonard@os200906:~$ svcadm enable apache22
bleonard@os200906:~$ ps -ef | grep httpd
webservd  1037     1   2 15:52:14 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd  1038  1037   0 15:52:15 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd  1041  1037   0 15:52:15 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd  1039  1037   0 15:52:15 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd  1040  1037   0 15:52:15 ?           0:00 /usr/apache2/2.2/bin/httpd -k start
webservd  1042  1037   0 15:52:15 ?           0:00 /usr/apache2/2.2/bin/httpd -k start

Now check the privileges on one of the Apache processes:

bleonard@os200906:~$ pfexec ppriv 1037
1037:	/usr/apache2/2.2/bin/httpd -k start
flags = <none>
	E: basic,!file_link_any,net_privaddr,!proc_info,!proc_session
	I: basic,!file_link_any,net_privaddr,!proc_info,!proc_session
	P: basic,!file_link_any,net_privaddr,!proc_info,!proc_session
	L: all

Note, the basic privilege set includes:

file_link_any
proc_exec
proc_fork
proc_info
proc_session

The basic set was created for backwards compatibility. Prior to Solaris 10, all users had those 5 privileges, so going forward those 5 privileges are still given to all users as part of the basic privilege set. The difference is that now one or more of these privileges can be taken away, as was done to user webservd above with file_link_any, proc_info and proc_session.

Comments:

Hi Brian,

great article, I learned a lot about privileges from it!

Now, wouldn't it be nice if ppriv -eD also told you what privileges the process did _not_ use?

That would help eliminate the guesswork for getting rid of file_link_any, proc_info and proc_session in the example above...

Cheers,
Constantin

Posted by Constantin Gonzalez on May 11, 2010 at 08:01 AM GMT #

Hello Brian,

I'm very glad to see that Observatory blog is still alive :D

My best regards,

Pawel

Posted by ptecza on May 11, 2010 at 08:21 AM GMT #

Yes, sorry for the hiatus. I've just been slammed since the acquisition :-).

Posted by Brian Leonard on May 11, 2010 at 08:46 AM GMT #

@Constantin Gonzalez
I use privdebug[1] to determine which privileges the process actually uses, then just strip everything else.

[1] http://hub.opensolaris.org/bin/view/Community+Group+security/privdebug

Posted by niceness on May 11, 2010 at 10:31 AM GMT #

Thanks @niceness!

Posted by Constantin Gonzalez on May 11, 2010 at 10:48 AM GMT #

Hello Brian,
Very pleased To see The Observatory is back.
Thanks
Cyril

Posted by Cyril Galibern on May 11, 2010 at 10:47 PM GMT #

Brian,

you have restored my faith that OpenSolaris is not dead ;)

Thank you! :)

Posted by ptecza on May 12, 2010 at 02:38 AM GMT #

How do you get around the need to create /var/run/apache2/2.2 prior to starting apache?

If you start the service (prior to changing the start/user property), then stop, make the property changes, and restart, all is well, but /var/run gets cleared during a reboot, and when apachectl runs again (this time as webservd), it won't have permissions to create the directory + chown it.

I've been thinking of making a simple service that will take care of this (and making apache dependent on it), but was curious what solution you used.

Posted by Jason on May 26, 2010 at 08:41 PM GMT #

Jason,

Apache is starting successfully for me after a reboot, but I'm not sure I understand why at this point. /var/run/apache2 is somehow still created as root. Then the 2.2 directory and http.pid file are owned by webservd. Have you verified it doesn't also work for you?

Posted by Brian Leonard on May 27, 2010 at 06:56 AM GMT #

Yes -- in fact, I had made the smf changes prior to ever enabling http:apache22 (new install), and encountered it and stumbled across that.

Posted by Jason on May 27, 2010 at 04:21 PM GMT #

Thank you so much, it really helped me

Posted by Leo on November 24, 2011 at 09:59 AM GMT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

The Observatory is a blog for users of Oracle Solaris. Tune in here for tips, tricks and more as we explore the Solaris operating system from Oracle.

Connect with Oracle Solaris:


Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today