Introducción
Debemos imaginar una zona de OpenSolaris como un contenedor de procesos, es decir la zona es una jaula cuyo contenido no puede ver lo que hay fuera de ella. Para que los procesos puedan ejecutarse el kernel debe proporcionar una serie de recursos a dicha zona, como pueden ser acceso a discos, interfaces de red, etc. Dichos recursos no pueden ser compartidos entre zonas.
La excepción es la zona global con id 0, esta se crea cuando arranca el sistema y tiene asociado todos los recursos sin restricciones de privilegios. Incluso en los sistemas que no se ha definido ninguna zona existe una zona global.
Cada zona esta identificada por un nombre y un id, el nombre global y el id 0 está reservados para la zona global.
Notas sobre el kernel
En nuestro sistema solo tenemos un Kernel ejecutándose, los procesos de las distintas zonas se ejecutan todos en el mismo que, lógicamente es el que se inicia al arrancar la zona global.
Esto tiene grandes beneficios a nivel de performance, especialmente si el diseño de las zonas ha sido bien pensado.
Teniendo en cuenta lo anterior interesa, desde el punto de vista de la performance, que las distintas zonas sean los mas parecidas posible y compartan el mayor número de directorios posibles.
P. ej. mientras que levantar el sistema consume en mi equipo 339 megas de la memoria física, arrancar una zona adicional consume solo 79 mb.
####sistema solo con la zona global bash-3.00# mdb -k Loading modules: [ unix genunix specfs dtrace uppc pcplusmp scsi_vhci ufs ip hook neti sctp arp usba fctl nca lofs mpt audiosup zfs random cpc crypto fcip nsctl ptm sppp ipc ] > ::memstat Page Summary Pages MB %Tot ------------ ---------------- ---------------- ---- Kernel 15066 58 12% Anon 52276 204 41% Exec and libs 11141 43 9% Page cache 5927 23 5% Free (cachelist) 24358 95 19% Free (freelist) 20142 78 16% Total 128910 503 Physical 128909 503 ###iniciamos una nueva zona > ::memstat Page Summary Pages MB %Tot ------------ ---------------- ---------------- ---- Kernel 17944 70 14% Anon 69002 269 54% Exec and libs 11305 44 9% Page cache 6139 23 5% Free (cachelist) 18639 72 14% Free (freelist) 5881 22 5% Total 128910 503 Physical 128909 503 >
Notas sobre la seguridad
Si bien ejecutar todos los hilos en el mismo kernel es un beneficio para la performance también supone un reto de cara a la seguridad. El modelo de permisos de OpenSolaris va más allá del tradicional en *nix, root (id =0) todos los privilegios, los demás usuarios ninguno. En su lugar tenemos un modelo mas granular, donde podemos asignar a los procesos/usuarios un set de privilegios específicos para realizar las tareas que necesiten.
Por ejemplo, ya no es necesario arrancar un servidor web como root, en su lugar podemos dar a un usuario privilegios para que sus procesos puedan hacer un bind al puerto 80. El hecho que cada proceso pueda ejecutarse con los mínimos privilegios necesarios, en contra del todo o nada tradicional, favorece enormemente la seguridad global del sistema. Los privilegios que tienen los procesos dentro de una zona no global están prefijados y no pueden ser alterados por el administrador, este set está diseñado de manera que evita la interacción de los threads entre zonas , así como el uso de algún privilegio para conseguir otros adicionales. Incluso los hilos que se ejecutan con id 0 tienen este set. Estas restricciones no se aplican en la zona global. Como consecuencia hay una serie de privilegios no podrán adquirir los procesos ejecutándose en una zona no global:Privilege Description PRIV_NET_RAWACCESS Allows a process to have direct access to the network layer. PRIV_PROC_CLOCK_HIGHRES Allows process to create high-resolution timers. PRIV_PROC_LOCK_MEMORY Allows process to lock pages in physical memory. PRIV_PROC_PRIOCNTL Allows process to change scheduling priority or class. PRIV_PROC_ZONE Allows process to control/signal other processes in different zones. [...]De esta manera los procesos se hallan aislados de forma efectiva de aquellos que no se están ejecutando en su misma zona. Obviamente, a parte de la restricción nivel de privilegios, es vital para la seguridad de las zonas, que los derechos de acceso a los distintos recursos sean correctos. Es decir, los privilegios nos indican que "acciones" podemos realizar (p. ej. Un chown), pero donde realizarlas está controlado a nivel de objetos (p. ej. Usando zonecfg damos acceso a los filesystem que nos interesa.). Es la combinación de ambas cosas, privilegios y restricciones de acceso , lo que finalmente garantiza la seguridad entre zonas.
Como pequeño ejemplo prático podemos comprobar como desde la zona global vemos los pids de las demás y no así a viceversa. En ambos casos el id del usuario es 0 (root).
#ps dentro de la zona test
bash-3.00# id
uid=0(root) gid=0(root)
bash-3.00# uname -a
SunOS test 5.11 snv_70b i86pc i386 i86pc
bash-3.00# ps
PID TTY TIME CMD
11227 console 0:01 bash
29665 console 0:02 sh
26806 console 0:00 ps
#ps desde la zona global con un grep para ver el proceso bash de la zona test
bash-3.00# id
uid=0(root) gid=0(root)
bash-3.00# ps -ef | grep 11227
root 11227 29665 1 Jan 18 zoneconsole 0:01 bash
root 26809 555 1 09:45:14 pts/2 0:00 grep 11227
#ps desde la zona global
bash-3.00# ps
PID TTY TIME CMD
555 pts/2 0:03 bash
551 pts/2 0:00 sh
26812 pts/2 0:00 ps
#ps desde la zona test buscando el pid del bash de la zona global
bash-3.00# ps -ef | grep 555 | grep -v grep
bash-3.00#
Como consecuencia de lo anterior existen algunas tareas que no están permitidas ejecutarse dentro de una zona:
Procesos que gestionan una Zona
Existen dos nuevos procesos por cada zona no global que tenemos en el sistema, su función gestionar los recursos de ellas:
El primero es el zoneadmd. Sus tareas son las siguientes:
Hay una completa descripción de este proceso en el fichero zoneadmd.c del código fuente.
El otro proceso es el zsched, sus tareas son:
Ciclo de vida de una zona
Estos son los estados (a nivel de kernel) en los que se puede encontrar una zona, ordenados por el flujo natural de arranque a parada.
ZONE_IS_UNINITIALIZED: la zona se ha añadido a la lista de zonas activas pero aun no es accesible.
ZONE_IS_READY: el proceso zsched esta preparado.
ZONE_IS_BOOTING: es un estado de transición el proceso zsched esta tratando de lanzar el init de la zona.
ZONE_IS_RUNNING: zsched a lanzado correctamente el init, la zona esta lista para trabajar. Permanece en este estado hasta que se le haga un shutdown.
ZONE_IS_SHUTTING_DOWN: se ha ejecutado la llamada zone_shutdown(), el sistema esta matando todos los procesos dentro de la zona.
ZONE_IS_EMPTY: no quedan mas procesos de esta zona ejecutándose.
ZONE_IS_DOWN: todos los threads del kernel relacionados con la zona han terminado.
La estructura zone_t
Por cada zona existente en el sistema hay una estructura zone_t en el kernel, en ella tenemos información acerca del estado y configuración de esta.
Podemos ver su definición en el fichero zone.h de nuestro sistema.
typedef struct zone {
/*
* zone_name is never modified once set.
*/
char *zone_name; /* zone's configuration name */
/*
* zone_nodename and zone_domain are never freed once allocated.
*/
char *zone_nodename; /* utsname.nodename equivalent */
char *zone_domain; /* srpc_domain equivalent */
/*
* zone_lock protects the following fields of a zone_t:
* zone_ref
* zone_cred_ref
* zone_ntasks
* zone_flags
* zone_zsd
*/
kmutex_t zone_lock;
/*
[...]
Dentro de ella encontramos los parámetros con los que hemos configurado la zona, entre ellos los que limitan el consumo, como por ejemplo el máximo de memoria, el número de semáforos, etc. Vamos hacer un breve repaso a los mas destacados:
*zone_name; /* nombre de la zona */ zoneid_t zone_id; /* ID de la zona */ rctl_qty_t zone_locked_mem_ctl; /* límite máximo de memoria*/ rctl_qty_t zone_max_swap_ctl; /* límite máximo de swap */ [...]
También hay información de la zona, como por ejemplo:
char *zone_rootpath; /* path de la raíz de la zona */ zone_status_t zone_status; /* en que estado esta actualmente la zona*/ rctl_qty_t zone_nlwps; /* número de hilos ejecutándose */ [...]
Podemos acceder fácilmente a ella usando mdb:
bash-3.00# mdb -k
Loading modules: [ unix genunix specfs dtrace uppc pcplusmp scsi_vhci ufs ip
hook neti sctp arp usba fctl nca lofs mpt audiosup zfs random sppp crypto ptm
md nfs cpc fcip fcp logindmux nsctl sdbc sv ii rdc ]
> ::walk zone
fec7f028
d5072b40
d884ac40
> ::walk zone
fec7f028
d5072b40
d884ac40
> d5072b40 ::zone
ADDR ID NAME PATH
d5072b40 1 linux /zone/root/
> d884ac40 ::zone
ADDR ID NAME PATH
d884ac40 3 test /zone_OS/root/
> d884ac40 ::print zone_t
{
zone_name = 0xd7ce69c0 "test"
zone_nodename = 0xd86e5400 "test"
zone_domain = 0xd86e57c0 ""
zone_lock = {
_opaque = [ 0, 0 ]
}
zone_linkage = {
list_next = zone_active+8
list_prev = 0xd5072b54
}
zone_id = 0x3
zone_ref = 0x13
zone_cred_ref = 0x1e
zone_rootvp = 0xd5074240
zone_rootpath = 0xd884ed10 "/zone_OS/root/"
zone_flags = 0
zone_status = 3 (ZONE_IS_RUNNING)
zone_ntasks = 0x11
zone_nlwps_lock = {
_opaque = [ 0, 0 ]
}
[...]