Solaris privileges in perspective (2)

System Security in SolarisTM 10: Privileges and Zones in Perspective -- Part 2

Peter van der Weerd

In part one, I discussed the improvements made to system security by using privileges as opposed to using the su-command. It showed that priviliges can be granted to processes, which overrules UIDs and file-permissions. This shifts security control from UID to the process level. Privileges in combination with RBAC give us a finer-grained security model on Solaris 10. But that is not all; in combination with Solaris Zones, they offer even more security by isolating application environments and restricting file system access. After installing Solaris 10, you already have one zone: the global zone. This global zone doesn't really differ from Solaris without zones. The global zone is simply there, up and running when you boot your machine. On top of this global zone, you can install multiple local zones:


solx# zoneadm list -v 
ID NAME            STATUS         PATH 
   0 global        running        /

A Solaris local zone is a lightweight virtual machine, a technique to logically separate applications from one another. Typically, a local zone has an IP address, based on a physical interface in the global zone, usually shares certain file systems or directories with the global zone, and has its own process tree. A local zone has its own console login, can be installed, rebooted, halted, and uninstalled, without affecting any other local zones or the global zone. Previously in Sys Admin, there have been articles that concentrate on Solaris zones, so I will not dwell on that. If you are new to Zones, "The Solaris 10 Zone Defense" by Kevin Wenchel can be of help:

The problem we face is twofold. First, we do not want to run Apache as root. A compromised httpd process would mean that the culprit is root on our system. So, we make sure that all httpd daemons will run with the UID of a user called "webservd" with UID 80. Also, we want to limit the privileges that these httpd daemons have. We can use the privilege sets to do so.

Second, what if we get hacked? We want to minimize the damage. So, we are going to run our Web server in a Solaris Container, safeguarding the global zone and other zones from damage done by the intruder.

The Apache2 Directory Tree

Since we might want to run multiple Web servers in different zones, we cannot use the standard location of the Apache2 configuration and documents environment. That is why we copy all relevant files to a new directory that will later be used by the Web server zone "web". For each new zone with a Web server with different content, we would have to do the same. The first new zone will get its Apache2 config and data files from the /apache directory:


solx# mkdir /apache 
solx# mkdir /apache/config 
solx# mkdir /apache/files 
solx# mkdir /apache/files/run 
solx# cp -R /etc/apache2/* /apache/config 
solx# cp -R /var/apache2/* /apache/files 
solx# mkdir /apache/logs 
solx# mkdir /apache/run

Create the Zone

The name of the zone will be "web", and it will have its root directory in the directory "/web":


solx# mkdir /web 
solx# chmod 700 /web 

solx# zonecfg -z web 
apache: No such zone configured. Use 'create' to begin 
        configuring a new zone. 
zonecfg:apache create 
zonecfg:apache set zonepath=/web 
zonecfg:apache set autoboot=true 
zonecfg:apache add fs 
zonecfg:apache:fs set dir=/etc/apache2 
zonecfg:apache:fs set special=/apache/config 
zonecfg:apache:fs set options=[ro,nodevices,nosuid,noexec] 
zonecfg:apache:fs set type=lofs 
zonecfg:apache:fs end 
zonecfg:apache add fs 
zonecfg:apache:fs set dir=/var/apache2 
zonecfg:apache:fs set special=/apache/files 
zonecfg:apache:fs set options=[ro,nodevices,nosuid,noexec] 
zonecfg:apache:fs set type=lofs 
zonecfg:apache:fs end 
zonecfg:apache add fs 
zonecfg:apache:fs set dir=/var/apache2/logs 
zonecfg:apache:fs set special=/apache/logs 
zonecfg:apache:fs set options=[rw,nodevices,nosuid,noexec] 
zonecfg:apache:fs set type=lofs 
zonecfg:apache:fs end 
zonecfg:apache add fs 
zonecfg:apache:fs set dir=/var/apache2/run 
zonecfg:apache:fs set special=/apache/run 
zonecfg:apache:fs set options=[rw,nodevices,nosuid,noexec] 
zonecfg:apache:fs set type=lofs 
zonecfg:apache:fs end 
zonecfg:apache add net 
zonecfg:apache:net set address= 
zonecfg:apache:net set physical=rtls0 
zonecfg:apache:net end 
zonecfg:apache verify 
zonecfg:apache commit 
zonecfg:apache exit 
solx# zoneadm -z web install 
Preparing to install zone apache. 
Creating list of files to copy from the global zone. 
Copying 2986 files to the zone. 
Initializing zone product registry. 
Determining zone package initialization order. 
preparing to initialize <923 packages on the zone.

The above may seem an endless list of settings, but what really happens is that some mounts are created and an IP address is configured. The attribute "dir" specifies the mountpoint from within the local zone. The attribute "special" specifies the directory that will be mounted from the global zone.

It is important to notice that the first two mounts will be read-only. We do not want anybody to be able to change the configuration from within the "web" zone. If changes are to be made, this will have to be done from the global zone by accessing the necessary files in /apache/config and /apache/files.

Since the Web server should be able to write its PID and logs, we have to mount /var/apache2/run and /var/apache2/logs read-write.

The IP address is the address via which the Apache zone will be accessible from the outside. The use of a Private Address may seem confusing. But obviously, we are behind a firewall that will forward http packets to the zone.

Now that we have installed the "web" zone, we can boot it:


solx# zoneadm -z web boot

Log in to the Web console:


solx# zlogin -C web

This will log you into the console of the "web" zone. Here, you will have to do some system identification like:


  • Setting the hostname ""
  • Setting the time zone
  • Setting the security policy (no Kerberos)
  • Setting the name service
  • Setting the root password

At the end, your zone will be rebooted automatically.

At the login prompt, type "~." to leave the console because we have some things to do in the global zone again.

Configure httpd.conf

Apache2 comes with an example httpd.conf and a standard httpd.conf. For ease of configuration, we copy the standard configuration file to httpd.conf and change it:


solx# cd /apache/config 
solx# cp httpd-std.conf httpd.conf

Next, we modify httpd.conf. Our DNS domainname is "", so we adjust it. Look for "ServerName", remove "#" and make sure it values "".

The default file where the Web server will write its PID is different from our Web server, so we change it. Look for "PidFile" and change the pathname into "/var/apache2/run/":

The pathname of the LockFile is ok, but it is commented out. Look for "LockFile" and uncomment the line that points to "/var/apache2/logs/accept.lock" by removing "#".

Since we will not run the httpd daemons as root user, we change ownership of the two directories that will be mounted read-write:


solx# chown -R webservd:webservd /apache/run 
solx# chown -R webservd:webservd /apache/logs

Set Properties for the apache2 Service

In a normal setup, apache2 will get started by root. A total of 7 httpd daemons will run; the first will be a root process, and the others will be "webservd" processes. We do not want any httpd daemon to be owned by root. Since apache2 will be under the control of the Solaris 10 Service Management Facility, we will use "svccfg" to modify some properties.


solx# zlogin web 
# svccfg -s apache2 
svc:/network/http:apache2 setprop start/user = astring: webservd 
svc:/network/http:apache2 setprop start/group = astring: webservd 
svc:/network/http:apache2 end

There are a lot more properties you can set. Have a look at them!


# svccfg -s apache2 
svc:/network/http:apache2 listprop 
(output skipped). 
svc:/network/http:apache2 exit

If we wanted to use an RBAC profile to set properties for apache2, we would need to specify which profile we wanted to use by setting the "profile" property. But in this example, we are not going to use a profile so we set the "use_profile" property to "false". This means that we have to set some other properties manually for apache2 to start successfully. The most important one is the "privileges" property. We take away some privileges to limit process control, and add a privilege that will allow the httpd process to bind to a private port, since the webservd will be the owner of the first httpd process and is by default not allowed to bind to port 80, which is a private port:

# svccfg -s apache2 
svc:/network/http:apache2 setprop start/use_profile = boolean: false 
svc:/network/http:apache2 setprop start/privileges = astring: \
svc:/network/http:apache2 setprop start/limit_privileges  = astring: :default 
svc:/network/http:apache2 setprop start/supp_groups = astring: :default 
svc:/network/http:apache2 setprop start/working_directory = astring: :default 
svc:/network/http:apache2 setprop start/project = astring: :default 
svc:/network/http:apache2 setprop start/resource_pool = astring: :default 
svc:/network/http:apache2 end

Refresh Apache:


# svcadm -v refresh apache2 
Action refresh set for svc:/network/http:apache2.

Reduce the Number of Services

Our zone is to be connected to the Internet and will function as a Web server. So there is little need for all kinds of services that don't matter; we do not want to invite the general hacker. You can manually configure your zone not to run these services. You can also simply copy an xml file that excludes all non-needed services:


# cd /var/svc/profile 
# mv generic.xml generic.orig.xml 
# ln -s generic_limited_net.xml generic.xml 
# svccfg apply /var/svc/profile/generic.xml

Finally, get your daemons running:


solx# svcadm enable apache2

Check whether "webservd" is the owner of all httpd processes:


# ps -ef | grep httpd | grep -v grep 
webservd 12972 12968   0 01:34:55 ?   0:00 /usr/apache2/bin/httpd -k start 
webservd 12969 12968   0 01:34:55 ?   0:00 /usr/apache2/bin/httpd -k start 
webservd 12970 12968   0 01:34:55 ?   0:00 /usr/apache2/bin/httpd -k start 
webservd 12971 12968   0 01:34:55 ?   0:00 /usr/apache2/bin/httpd -k start 
webservd 12976 12968   0 01:35:22 ?   0:00 /usr/apache2/bin/httpd -k start 
webservd 12968 11128   0 01:34:54 ?   0:00 /usr/apache2/bin/httpd -k start 
webservd 12973 12968   0 01:34:55 ?   0:00 /usr/apache2/bin/httpd -k start

Check the privileges and see that the Effective, Inherited, and Permitted sets now have very limited privileges:


# ppriv -v 12968 
12968:   /usr/apache2/bin/httpd -k start 
flags = none 
        E: net_privaddr,proc_exec,proc_fork 
        I: net_privaddr,proc_exec,proc_fork 
        P: net_privaddr,proc_exec,proc_fork 
        L: contract_event,contract_observer,file_chown,file_chown_self, 


If things don't work the way you want them to, the following logs may help:



Keep in mind that if you want to set up your HTML documents and cgi-bin, you will have to do that from within the global zone's /apacheapache2/files directory, since Apache mounts this directory read-only.


Zones and privileges do not replace RBAC or make RBAC superfluous. They do seem to add a major improvement to Solaris system security, however. The techniques can very well live side by side and complement one another.

Peter van der Weerd works as a freelance Solaris, HP-UX, and Linux trainer in Europe.

This entry was posted in Uncategorized. Bookmark the permalink.

Comments are closed.