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:
http://www.samag.com/documents/s=9494/sam0502b/0502b.htm
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=10.0.0.23 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 "web.not.com"
- 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 "not.com.", so we adjust it. Look for "ServerName", remove "#" and make sure it values "web.not.com.".
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/httpd.pid":
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: \ basic,!proc_info,!proc_session,!file_link_any,net_privaddr 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, file_dac_execute,file_dac_read,file_dac_search,file_dac_write, file_link_any,file_owner,file_setid,ipc_dac_read,ipc_dac_write, ipc_owner,net_icmpaccess,net_privaddr,proc_audit,proc_chroot, proc_exec,proc_fork,proc_info,proc_owner,proc_session,proc_setid, proc_taskid,sys_acct,sys_admin,sys_audit,sys_mount,sys_nfs,sys_resource
Troubleshoot
If things don't work the way you want them to, the following logs may help:
/var/adm/messages /var/svc/log/network-http:apache2.log /var/apache2/logs/error_log
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.
Conclusion
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.