Solaris 11 is the latest Operating System in the Solaris server OS range from Oracle, previously Sun Microsystems. It incorporates many features from Solaris 10 such as the Service Management Framework, but also pulls a lot from the now defunct OpenSolaris project including a new packaging system and a whole new suite of *adm administrative commands to configure the operating system.
This article will cover security configuration of the OS after a standard text-based installation. I’m using Solaris 11.1 x86_64 running as a VMware Fusion guest, but almost all of the steps will be applicable for the SPARC architecture too.
|
1 2 |
$ uname -a SunOS lithium 5.11 11.1 i86pc i386 i86pc |
|
1 2 |
$ isainfo -kv 64-bit amd64 kernel modules |
Only core security concepts will be covered. For full details, review the appropriate CISecurity.org benchmark for Solaris 11.1, as well as the comprehensive documentation on the operating system available from the Oracle site. I’ll be working along with those benchmarks and covering only the main points (the full benchmark is 94 pages).
Operating system hardening is good practice, and will assist in a defense-in-depth architecture by adding another layer of security along with firewalls, well-written code, secure application servers, and so on. It is also a requirement of several financial accreditation standards such as PCI-DSS.
This article presumes the reader is proficient at administering the Solaris 11 operating system.
Pre-hardening Considerations
Ensure that the system is fully patched prior to starting hardening. This will ensure that all of the latest security errata have been applied. The latest patch cluster can be downloaded and applied to the system, or you can use pkg update. For more information on patching Solaris 11, consult the following article (http://www.oracle.com/technetwork/articles/servers-storage-admin/o11-018-howto-update-s11-1572261.html).
Prior to starting the hardening procedure it is recommended that an alternate boot environment be created for backup purposes.
|
1 2 3 4 5 6 7 8 9 10 |
# beadm list BE Active Mountpoint Space Policy Created -- ------ ---------- ----- ------ ------- solaris NR / 2.18G static 2013-11-14 14:50 # beadm create pre-hardening # beadm list BE Active Mountpoint Space Policy Created -- ------ ---------- ----- ------ ------- pre-hardening - - 67.0K static 2013-11-22 21:01 solaris NR / 2.18G static 2013-11-14 14:50 |
Now, should it be required, the pre-hardening boot environment can be selected from the GRUB menu at boot. Issuing the beadm activate pre-hardening command will activate the environment for future reboots.
We can now begin hardening the OS. You may find that some of the hardening is not applicable for your environment - e.g. you cannot disable RPC keyserv if using Secure RPC - so make sure you fit these guidelines around your real-world platform and application requirements.
Disable Services
It is always desirable to run a minimal service set so as to limit avenues of potential exploit. Solaris 11 uses a Secure By Default approach. For example, there is no GUI by default, only a limited set of system services are set to start, Sendmail is configured to listen locally only, and so on.
You can check whether the graphical login manager is running by issuing the following command:
|
1 |
# svcs -Ho state svc:/application/graphical-login/gdm:default |
If you see no output, then try the following:
|
1 2 3 4 5 6 7 8 9 |
# svcs -xv svc:/application/graphical-login/gdm svc:/application/graphical-login/gdm:default (?) State: - Reason: Service is incomplete, defined only by profile /etc/svc/profile/generic.xml. To install this service, identify and install the package which provides the service's primary manifest. Use "pkg search 'svc\:/application/graphical-login/gdm\:default'" to identify the package, then "pkg install <pkg>" to install the indicated package. Impact: This service is not running. |
This indicates that the service isn’t fully installed so there’s nothing to worry about. If there is output, and it isn’t “disabled“, issue the following command to disable GDM if you’re not planning on using a GUI:
|
1 |
# svcadm disable svc:/application/graphical-login/gdm:default |
Sendmail will be configured to listen locally by default. If you have no need to forward/receive mail, then that configuration is fine and secure. If Sendmail is listening on other interfaces and you don’t need it to be you should set the config/local_only property to true. You can verify with the following command (note the use of ggrep - GNU grep):
|
1 2 3 |
# netstat -an | ggrep '\.25[[:space:]].*LISTEN' 127.0.0.1.25 *.* 0 0 128000 0 LISTEN ::1.2 *.* 0 0 128000 0 LISTEN |
And verify the SMF configuration for the Sendmail service directly:
|
1 2 |
# svccfg -v -s svc:/network/smtp:sendmail listprop config/local_only config/local_only boolean true |
Again, with the mindset of “minimal service exposure means less attack vectors which means more secure”, verify that the RPC keyserv service is disabled if you don’t have a requirement for it - if you’re using secure RPC for things like Secure NFS then you will need to leave this enabled.
|
1 2 |
# svcs -Ho state svc:/network/rpc/keyserv disabled |
Likewise, NIS should be disabled if you’re not using it:
|
1 2 3 4 |
# svcs -Ho state svc:/network/nis/server svcs: Pattern 'svc:/network/nis/server' doesn't match any instances # svcs -Ho state svc:/network/nis/domain disabled |
We see here that the appropriate package for the NIS server isn’t even installed in a default Solaris 11 installation. We also check that nis/domain is disabled as LDAP is not in use.
We will also check that the NIS client is disabled:
|
1 2 |
# svcs -Ho state svc:/network/nis/client disabled |
If any of these services are enabled, issue the svcadm disable <service_fmri> command to disable it.
If Kerberos has not been configured on the system, then check that any Kerberos-related services are disabled as they are not required. Check that the ktkt_warn service, which warns users when their Kerberos tickets are about to expire, is disabled:
|
1 2 |
# svcs -Ho state svc:/network/security/ktkt_warn online |
Out-of-the-box it’s online and enabled. Let’s disable it:
|
1 2 3 |
# svcadm disable svc:/network/security/ktkt_warn # svcs -Ho state svc:/network/security/ktkt_warn disabled |
The GSS API is only required in Kerberos environments and other edge cases, so you should disable it if you don’t need it:
|
1 2 3 4 5 |
# svcs -Ho state svc:/network/rpc/gss online # svcadm disable svc:/network/rpc/gss # svcs -Ho state svc:/network/rpc/gss disabled |
The next set of services we need to disable are to prevent users from mounting and accessing data on removable media. This would allow for the physical introduction of malice, both via incoming means as well as outgoing (stealing sensitive data, perhaps).
|
1 2 3 4 5 6 7 8 9 10 |
# svcs -Ho state svc:/system/filesystem/rmvolmgr online # svcs -Ho state svc:/network/rpc/smserver online # svcadm disable svc:/system/filesystem/rmvolmgr # svcadm disable svc:/network/rpc/smserver # svcs -Ho state svc:/system/filesystem/rmvolmgr disabled # svcs -Ho state svc:/network/rpc/smserver disabled |
With these services disabled, we can also disable automount (svc:/system/filesystem/autofs) if it isn’t required. It’s most often employed to automatically mount NFS file systems from remote file servers when needed, as well as loopback filesystems (user home directories, etc.). If you don’t need it, disable it.
Check that the bundled Apache 2.2 service is disabled:
|
1 2 |
# svcs -Ho state svc:/network/http:apache22 disabled |
Configure TCP Wrappers
TCP Wrappers allows access to various network daemons that support it via administrator-controlled ACLs based upon remote IP addresses. Therefore, we can use this as another layer of security along with network and host-based firewalls – if one layer is compromised there are still other ACL points. It may mean slightly more administration, but it’s a good pay off in terms of security. It also performs syslog logging regarding successful and unsuccessful connection attempts - information that would be vital in determining the source IP address.
By default, tcp_wrappers is disabled:
|
1 2 |
# inetadm -p | grep tcp_wrappers tcp_wrappers=FALSE |
The default can be overridden by individual inetadm enabled services. The following command will display a report of the tcp_wrappers variable of each service. These are all set to FALSE by default:
|
1 2 3 4 5 6 7 8 9 10 |
# inetadm | awk '/svc/ {print $NF}' | while read svc; do > echo "svc: ${svc}" > inetadm -l ${svc} | grep tcp_wrappers; > done | paste - - svc: svc:/application/cups/in-lpd:default default tcp_wrappers=FALSE svc: svc:/network/finger:default default tcp_wrappers=FALSE svc: svc:/network/time:dgram default tcp_wrappers=FALSE svc: svc:/network/time:stream default tcp_wrappers=FALSE svc: svc:/network/rexec:default default tcp_wrappers=FALSE ... |
TCP Wrappers for the RPC portmapping service are also disabled by default:
|
1 2 |
# svcprop -p config/enable_tcpwrappers rpc/bind false |
To implement TCP Wrappers, you’ll need to create two files: /etc/hosts.allow and /etc/hosts.deny. Configure ACLs in /etc/hosts.allow as appropriate for your needs. The version of SSH that ships with Solaris 11 supports TCP Wrappers too if these files exist, so ensure you have a rule for sshd: <network>/<mask> in /etc/hosts.allow.
|
1 2 3 |
# vi /etc/hosts.allow service: 192.168.122.0/255.255.255.0 ... |
And implement a default deny-any policy:
|
1 |
# echo "ALL: ALL" > /etc/hosts.deny |
To enable TCP Wrappers for all services started by inetd, issue the following command and verify the results:
|
1 2 3 |
# inetadm -M tcp_wrappers=TRUE # inetadm -p | grep tcp_wrappers tcp_wrappers=TRUE |
Finally, enable TCP wrappers for the RPC portmapping service, remembering to refresh the rpc/bind service:
|
1 2 |
# svccfg -s svc:/network/rpc/bind setprop config/enable_tcpwrappers=true # svcadm refresh svc:/network/rpc/bind |
A final check that telnet is disabled, and we’re done configuring services.
|
1 2 |
# svcs -Ho state svc:/network/telnet disabled |
Kernel Tuning
Core dumps of processes may contain sensitive information and as such the OS must be correctly configured to handle them.
First, verify the OOTB configuration:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# coreadm global core file pattern: global core file content: default init core file pattern: core init core file content: default global core dumps: disabled per-process core dumps: enabled global setid core dumps: disabled per-process setid core dumps: disabled global core dump logging: disabled # ls -ld /var/share/cores drwxr-xr-x 2 root sys 2 Nov 22 20:57 /var/share/cores |
We can always configure the OS, via coreadm, to dump to this directory - however it’s always a safer bet to just disable core dumps altogether if they’re not going to be utilised.
|
1 2 3 4 5 6 7 8 9 10 11 |
# coreadm -d global -d global-setid -d process -d proc-setid # coreadm global core file pattern: global core file content: default init core file pattern: core init core file content: default global core dumps: disabled per-process core dumps: disabled global setid core dumps: disabled per-process setid core dumps: disabled global core dump logging: disabled |
Stack protection helps protect against certain types of buffer overflow attacks. Whilst it doesn’t protect against all types of buffer overflow, it is still a significant security feature and it should be implemented. To check that it is enabled, look for the following:
|
1 2 3 |
# fgrep noexec_user_stack /etc/system set noexec_user_stack=1 set noexec_user_stack_log=1 |
If this isn’t returned, add the appropriate entries to /etc/system and reboot.
The TCP_STRONG_ISS variable sets the TCP initial sequence number generation parameters. By default, it’s set to 1 (Improved sequential generation, with random variance in increment), but for a hardened server this should be set to 2 (RFC 1948 compliant number generation). This will make remote session-hijacking attempts more difficult as well as any attack that relies upon predictable sequence number generation.
|
1 2 3 |
# vi /etc/default/inetinit # grep '^TCP_STRONG_ISS' /etc/default/inetinit TCP_STRONG_ISS=2 |
Verify the running system:
|
1 2 |
# ipadm show-prop -p _strong_iss -co current tcp 1 |
Let’s change it on the fly:
|
1 2 3 |
# ipadm set-prop -p _strong_iss=2 tcp # ipadm show-prop -p _strong_iss -co current tcp 2 |
Verify that the host isn’t configured to route packets by checking the following properties:
|
1 2 3 4 |
# ipadm show-prop -p _forward_src_routed -co current ipv4 0 # ipadm show-prop -p _forward_src_routed -co persistent ipv4 <nothing returned> |
If it is enabled, and is not required, disable it with ipadm set-prop as appropriate. There are a myriad of other variables that can be tuned depending upon your needs. A value of 0 is disabled, 1 is enabled. The _forward_directed_broadcasts property in the ip protocol should be set to 0 to prevent denial-of-service attacks. You should set _respond_to_timestamp in the ip protocol to 0 to hamper host discovery, as well as _respond_to_timestamp_broadcast in protocol ip. To Other variables to check the documentation for are _respond_to_address_mask_broadcast, _respond_to_echo_broadcast, _respond_to_echo_multicast, _ignore_redirect, and more. Note that not all of these variables may be present on your system. ipadm can also be set to restrict the maximum number of incoming connections for the tcp protocol with:
|
1 |
# ipadm set-prop -p _conn_req_max_q=<value> tcp |
If this value is to be configured you must take great care in ensuring there are enough connections to comfortably serve the application to the users.
Disable Routing
If you don’t have a need for the Routing Internet Protocol (RIP) then you should ensure it is disabled. Again, this provides one less thing to be exploited on the system, and one less thing to administer.
|
1 2 3 4 5 |
# routeadm -p | grep current ipv4-routing persistent=disabled default=disabled current=disabled ipv6-routing persistent=disabled default=disabled current=disabled ipv4-forwarding persistent=disabled default=disabled current=disabled ipv6-forwarding persistent=disabled default=disabled current=disabled |
If any routing is enabled, disable it with
|
1 2 3 |
# routeadm -d ipv4-forwarding -d ipv4-routing # routeadm -d ipv6-forwarding -d ipv6-routing # routeadm -u |
SSH Configuration
X11Forwarding via SSH should be disabled if you do not have a need for it. Verify with the following command:
|
1 2 |
# grep '^X11Forwarding' /etc/ssh/sshd_config X11Forwarding yes |
By default, it’s enabled. Disable it:
|
1 2 3 |
# gsed -i '/^X11Forwarding/ s/yes/no/' /etc/ssh/sshd_config # grep '^X11Forwarding' /etc/ssh/sshd_config X11Forwarding no |
Restart sshd for the change to take effect:
|
1 |
# svcadm restart svc:/network/ssh |
The MaxAuthTries parameter in /etc/ssh/sshd_config should be set to an appropriately low value for your needs - between 3 and 6 normally suffices. This setting controls how many times a user can enter incorrect credentials before being forced to reconnect. This can stop many types of brute force attack by disconnecting a malicious login exchange.
By default, root is disallowed direct login via sshd which is something we wish to maintain:
|
1 2 |
# fgrep PermitRootLogin /etc/ssh/sshd_config PermitRootLogin no |
Neither are empty passwords:
|
1 2 |
# grep '^PermitEmptyPasswords' /etc/ssh/sshd_config PermitEmptyPasswords no |
Retry Limit
As MaxAuthTries does for sshd, the RETRIES parameter can be set at the OS-level in /etc/default/login. There also is configuration available to enforce account locking after a certain number of retries which may be something your site needs - read /etc/security/policy.conf for further documentation.
Access to at/cron
Access to the at and cron commands should be limited to authorised users only. Scheduling jobs should be performed at the discretion of the System Administrator, and perhaps a few delegates and service accounts. If not, appropriate monitoring of user crontab files should be performed to check for errant jobs that could compromise system security or stability.
The /etc/cron.d/at.{allow,deny} and /etc/cron.d/cron.{allow,deny} files control this behaviour. The default configuration is as follows:
|
1 2 3 4 5 6 7 8 9 10 |
# ls /etc/cron.d/ at.deny cron.deny queuedefs # cat /etc/cron.d/at.deny daemon bin nuucp # cat /etc/cron.d/cron.deny daemon bin nuucp |
It is recommended to perform the following remediation:
|
1 2 3 4 |
# echo "root" > /etc/cron.d/cron.allow # echo "root" > /etc/cron.d/at.allow # rm /etc/cron.d/*.deny # chmod 400 /etc/cron.d/*.allow |
Any other users that require at/cron access can be added to the appropriate files.
Console Access
Direct root login should only be permitted from the system console. To configure this, ensure that the CONSOLE variable is set in /etc/default/login as follows:
|
1 2 |
# grep '^CONSOLE' /etc/default/login CONSOLE=/dev/console |
All other access must be made via unprivileged user accounts, and then escalated to root via sudo or su.
User Accounts
Solaris has a great number of tunable parameters relating to user accounts, password expiration, password creation policies, and so on.
The logins -ox command displays user and system login information in a parse-able format. For example:
|
1 2 3 4 |
# logins -ox ... test:101:staff:10::/export/home/test:/usr/bin/bash:UP:000000:-1:-1:-1 ... |
Here we use the passwd command to force password changes every ten weeks (70 days) and prevent password changes for one week thereafter. Warnings will start to be sent to the user 2 weeks before their password expires. Obviously, you will need to change these parameters as per the security policy at your site:
|
1 |
# passwd -x 70 -n 7 -w 14 test |
Checking logins -ox once again:
|
1 |
test:101:staff:10::/export/home/test:/usr/bin/bash:UP:112313:7:70:14 |
The last three fields have updated appropriately. You’ll need to do this for all active users except the root account and any accounts with NL or LK in the fifth-to-last field as they represent non-login and locked accounts respectively. If you have more than a handful of accounts you’d be best to script this operation.
Update /etc/default/password:
|
1 2 3 4 5 6 |
# vi /etc/default/passwd ... MAXWEEKS=10 MINWEEKS=1 WARNWEEKS=2 ... |
There are a variety of variables in /etc/default/passwd that can be used to set up a strong password creation policy. Every site will have different needs, but here are the NSA/DISA compliant complexity rules:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# grep "^PASSLENGTH=" /etc/default/passwd PASSLENGTH=8 # grep "^NAMECHECK=" /etc/default/passwd NAMECHECK=YES # grep "^HISTORY=" /etc/default/passwd HISTORY=10 # grep "^MINDIFF=" /etc/default/passwd MINDIFF=3 # grep "^MINALPHA=" /etc/default/passwd MINALPHA=2 # grep "^MINUPPER=" /etc/default/passwd MINUPPER=1 # grep "^MINLOWER=" /etc/default/passwd MINLOWER=1 # grep "^MINNONALPHA=" /etc/default/passwd MINNONALPHA=1 # grep "^MAXREPEATS=" /etc/default/passwd MAXREPEATS=0 # grep "^WHITESPACE=" /etc/default/passwd WHITESPACE=YES # grep "^DICTIONDBDIR=" /etc/default/passwd DICTIONDBDIR=/var/passwd # grep "^DICTIONLIST=" /etc/default/passwd DICTIONLIST=/usr/share/lib/dict/words |
If these are too restrictive, you can tune them appropriately. Check man -s 1 passwd for more information on these options, although the variable names are fairly self-descriptive.
The default umask for users is 0022 which leads to files being created with 0644 permissions. It is more desirable to have the default umask set to 0027 so that “other” do not have access. To do this, modify /etc/default/login:
|
1 2 |
# vi /etc/default/login UMASK=027 |
You can be even more restrictive and set the default umask to 077 if need be. You’ll also need to change “umask 022” to “umask <your_desired_umask>” in /etc/profile. Ensure that you don’t break things by being too restrictive either.
Warning Banners
Displaying a warning banner may be a legal requirement at your site. It is also good to ward off potential attackers and let them know that their activity on the system may/will be monitored.
Update /etc/motd, and replace its contents with the following:
|
1 |
Authorised users only. All activity may be monitored and reported. |
Add the same to /etc/issue. We can now have the SSH service display this warning banner by including the following in /etc/ssh/sshd_config:
|
1 |
Banner /etc/issue |
and restarting sshd:
|
1 |
# svcadm restart svc:/network/ssh |
User checks
Check that only one user exists with UID 0 - i.e. root:
|
1 2 |
# gawk -vFS=: '$3 == 0 { print }' /etc/passwd root:x:0:0:Super-User:/root:/usr/bin/bash |
If any other lines are returned, you should investigate immediately, as some other account has root privileges on the server. Even if it is legitimate, it is poor practice.
The logins command has a couple of options for searching for bad logins. For example, the -d option selects logins with duplicate UIDs and the -p option selects logins with no passwords.
Checking for World-Writable Files
A find command should be regularly run across all filesystems to check for world-writable files that are unexpected. For example, the following is seen on a fresh installation of Solaris 11.1:
|
1 2 3 |
# find / ! -type l -perm -0002 -ls 14003666 4 drwxrwxrwt 3 root sys 189 Nov 23 00:00 /tmp ... |
This list should be retained and regularly compared to ensure that new world-writable files and directories are not created without careful consideration. Any world-writable directories that must exist (/tmp and /var/tmp, for example) should have their sticky-bit set to prevent file deletion by non-owners. World-writable files and directories are a quick target for denial-of-service attacks, as any user could potentially fill the filesystem on which the directory or file resides.
You should also check for SETUID and SETGID files with the following commands:
|
1 2 |
# find / -perm -4000 -ls # find / -perm -2000 -ls |
Retain the file listings taken after a fresh installation of Solaris 11.1 has been performed, and regularly check the live system against these original lists and investigate any changes. There should only be use of SETUID and SETGID binaries where strictly necessary.
Check for unowned files, i.e. those being files owned by a numeric UID and GID where no corresponding user or group entries exist on the current system.
|
1 2 |
# find / -nouser -ls # find / -nogroup -ls |
These should be investigated as a new user may inherit a UID (or group a GID) and unintentionally get access to data.
Solaris Zones
Solaris zones are an operating system-level virtualisation technology. Using zones, it is possible to cordon off applications into their own containers, thus minimising any damage an exploited application will do. Solaris Zones provide an easy way to either provide a sparse chroot-like environment so that applications can run in an environment that will not be detrimental to global system resources, or a full branded environment (running a Solaris 10 branded zone, for example). Zones provide an excellent way to split a system into several logical units, each with their own filesystem resources, system resources and management. The “physical” host (which in our case is actually a VMware VM) is known as the global zone in Solaris zone nomenclature.
Before creating a zone, I’ll create a new dataset and zfs filesystem to hold the zone root - this is an optional step but will have the benefit of further isolating the zones. I have two disks in the system, with only one (c8t0d0) in use:
|
1 2 3 4 5 6 7 8 9 |
# format < /dev/null Searching for disks...done AVAILABLE DISK SELECTIONS: 0. c8t0d0 <VMware,-VMware Virtual S-1.0-20.00GB> /pci@0,0/pci15ad,1976@10/sd@0,0 1. c8t1d0 <VMware,-VMware Virtual S-1.0 cyl 2608 alt 2 hd 255 sec 63> /pci@0,0/pci15ad,1976@10/sd@1,0 Specify disk (enter its number): |
I format and label the additional disk:
|
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 |
# fdisk /dev/rdsk/c8t1d0s2 No fdisk table exists. The default partition for the disk is: a 100% "SOLARIS System" partition Type "y" to accept the default partition, otherwise type "n" to edit the partition table. y # format Searching for disks...done AVAILABLE DISK SELECTIONS: 0. c8t0d0 <VMware,-VMware Virtual S-1.0-20.00GB> /pci@0,0/pci15ad,1976@10/sd@0,0 1. c8t1d0 <VMware,-VMware Virtual S-1.0 cyl 2607 alt 2 hd 255 sec 63> /pci@0,0/pci15ad,1976@10/sd@1,0 Specify disk (enter its number): 1 selecting c8t1d0 [disk formatted] FORMAT MENU: ... label - write label to the disk ... format> label Ready to label disk, continue? y format> quit |
Next, create a zpool on the new device:
|
1 |
# zpool create datapool /dev/dsk/c8t1d0 |
Verify the result:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# zpool list NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT datapool 19.9G 85K 19.9G 0% 1.00x ONLINE - rpool 19.6G 4.15G 15.5G 21% 1.00x ONLINE - # zpool status datapool pool: datapool state: ONLINE scan: none requested config: NAME STATE READ WRITE CKSUM datapool ONLINE 0 0 0 c8t1d0 ONLINE 0 0 0 errors: No known data errors |
Create a ZFS filesystem that will form the root of the zonepaths:
|
1 2 3 4 5 6 7 |
# zfs create -o mountpoint=/zones datapool/zonefs # zfs list datapool/zonefs NAME USED AVAIL REFER MOUNTPOINT datapool/zonefs 31K 19.6G 31K /zones # df -h /zones Filesystem Size Used Available Capacity Mounted on datapool/zonefs 20G 31K 20G 1% /zones |
Using zonecfg, we can now begin creating a zone. Zones are tightly integrated with ZFS and a new ZFS filesystem will be created for each new zone created. Create the zone:
|
1 |
# zonecfg -z testzone "create; set zonepath=/zones/testzone" |
You’ll notice that the zone has now been configured:
|
1 2 3 4 |
# zoneadm list -cv ID NAME STATUS PATH BRAND IP 0 global running / solaris shared - testzone configured /zones/testzone solaris excl |
Install the zone. This will take some time as the packages are downloaded and installed. For larger installations you’ll need to look at configuring your own package repositories and/or cloning zones to save time for future zone provisioning. We could also specify the -c option here and pass a system configuration template generated with sysconfig create-profile to skip the post-installation screens after zone creation.
|
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 31 32 33 34 35 36 37 38 |
# zoneadm -z testzone install The following ZFS file system(s) have been created: datapool/zonefs/testzone Progress being logged to /var/log/zones/zoneadm.20131123T154157Z.testzone.install Image: Preparing at /zones/testzone/root. Creating IPS image Startup linked: 1/1 done Installing packages from: solaris origin: http://pkg.oracle.com/solaris/release/ DOWNLOAD PKGS FILES XFER (MB) SPEED Completed 183/183 33556/33556 222.2/222.2 221k/s PHASE ITEMS Installing new actions 46825/46825 Updating package state database Done Updating image state Done Creating fast lookup database Done Installation: Succeeded Note: Man pages can be obtained by installing pkg:/system/manual done. Done: Installation completed in 1188.361 seconds. Next Steps: Boot the zone, then log into the zone console (zlogin -C) to complete the configuration process. Log saved in non-global zone as /zones/testzone/root/var/log/zones/zoneadm.20131123T154157Z.testzone.install |
Verify the zone has been created:
|
1 2 3 4 |
# zoneadm list -cv ID NAME STATUS PATH BRAND IP 0 global running / solaris shared - testzone installed /zones/testzone solaris excl |
You can see that the status of testzone has changed from configured to installed. Boot the zone:
|
1 2 3 4 5 |
# zoneadm -z testzone boot # zoneadm list -cv ID NAME STATUS PATH BRAND IP 0 global running / solaris shared 1 testzone running /zones/testzone solaris excl |
dladm show-link will now show a new VNIC created for the zone:
|
1 2 3 4 |
# dladm show-link LINK CLASS MTU STATE OVER net0 phys 1500 up -- testzone/net0 vnic 1500 up net0 |
During the boot up of the zone you may see the following on the zone console if you’re running your zones on a virtualised global zone (i.e. the VMware VM):
|
1 2 |
Warning: Unable to verify add of static route on net0/v4 Error code=3 |
To fix this, place the global zone’s physical interface in promiscuous mode.
|
1 |
# snoop -d net0 >/dev/null 2>&1 & |
You can use dladm show-link to identify the correct physical interface to place into promiscuous mode.
Connect to the zone console with zlogin:
|
1 |
# zlogin -C -e '#.' testzone |
Start working through the post-installation dialogs. You will need to enter system identification information such as hostname, networking configuration, location and root password - much the same as during the configuration stages of a fresh Solaris 11 installation. Disconnect from the zone console with the hash-dot (#.) break sequence we specified with the -e option to zlogin. Once the zone is booted, you should be able to log in normally via zlogin:
|
1 2 3 4 |
# zlogin testzone [Connected to zone 'testzone' pts/2] Oracle Corporation SunOS 5.11 11.1 September 2012 root@testzone:~# |
You can also try ssh-ing to the zone once you’ve created a user:
|
1 |
# ssh <user>@<ip-address-of-zone> |
Isolated services can now be deployed to the zone. Zones are a complex topic and this has only scratched the surface. There are many more features of zones, such as resource controls, that should be investigated when deploying zones. If full-root rather than sparse-root zones are used then there will be some additional hardening that will need to take place within the zone as well as within the global zone.
You can view a full article on the configuration of Solaris 11 zones here.
Testing the Hardening
You can now run network security tools such as nmap and Nessus against the host and any zones created. You can install nmap under Solaris 11 as follows:
|
1 |
# pkg install nmap |
You can then scan your global zone and any child zones.
|
1 |
# nmap <options> |
By default, nmap will perform a SYN stealth scan:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# nmap 10.1.1.140 -v Starting Nmap 5.51 ( http://nmap.org/ ) at 2013-11-24 03:22 EST Initiating SYN Stealth Scan at 03:22 Scanning sol11test (10.1.1.140) [1000 ports] Stats: 0:00:06 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan SYN Stealth Scan Timing: About 2.50% done; ETC: 03:26 (0:03:54 remaining) SYN Stealth Scan Timing: About 17.50% done; ETC: 03:26 (0:02:50 remaining) SYN Stealth Scan Timing: About 32.15% done; ETC: 03:26 (0:02:19 remaining) SYN Stealth Scan Timing: About 47.05% done; ETC: 03:26 (0:01:48 remaining) SYN Stealth Scan Timing: About 62.05% done; ETC: 03:26 (0:01:17 remaining) SYN Stealth Scan Timing: About 76.55% done; ETC: 03:26 (0:00:48 remaining) Completed SYN Stealth Scan at 03:26, 202.61s elapsed (1000 total ports) Nmap scan report for sol11test (10.1.1.140) Host is up. All 1000 scanned ports on sol11test (10.1.1.140) are filtered Read data files from: /usr/share/nmap Nmap done: 1 IP address (1 host up) scanned in 202.72 seconds Raw packets sent: 2000 (88.000KB) | Rcvd: 0 (0B) |
Performing a TCP connect() scan yields:
|
1 2 3 4 5 6 7 |
# nmap -sT 10.1.1.140 -v Starting Nmap 5.51 ( http://nmap.org/ ) at 2013-11-24 03:28 EST Initiating Connect Scan at 03:28 Scanning sol11test (10.1.1.140) [1000 ports] Discovered open port 22/tcp on 10.1.1.140 ... |
Other Considerations
Depending on the needs at your site, you may wish to look at the Solaris audit service which is enabled by default on Solaris 11. The CIS Benchmark contains full details on how to configure the service as per recommendation.
Role-Based Access Control is also an option, and allows tasks that would normally be restricted to the root role to be delegated to other users. The Oracle documentation covers RBAC in great detail (http://docs.oracle.com/cd/E23824_01/html/821-1456/rbac-1.html).
ZFS also includes encryption capabilities which you can read more about here.
Conclusion
This article has covered the main aspects of securing the Oracle Solaris 11 operating system. Where it has not been possible to cover hardening in full detail (around the TCP/IP stack for example) due to the sheer number of tunable parameters, the reader is advised to consult the relevant CIS benchmark for the OS, as well as the Oracle documentation which can be considered authoritative.
This article was previously published in Pentest Magazine.
