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