SafeChildren Banner

Havoc Oracle Solaris Experts

lunes, 29 de noviembre de 2010

Cómo configurar HTTPS en Tomcat utilizando Roles y Privilegios

Introducción
Ya hemos visto en ocasiones anteriores Cómo Instalar Apache Tomcat en Solaris. Además, con los conocimientos que hemos adquirido sobre RBAC, Roles, Privilegios y Seguridad en Solaris, podemos realizar la instalación con un nivel de seguridad muy elevado.

En esta ocasión vamos a ver cómo podemos configurar un conector HTTPS en Apache Tomcat, utilizando un puerto no privilegiado, por ejemplo 8443, y luego, veremos cómo podemos utilizar el puerto privilegiado 443 sin tener que lanzar Apache Tomcat con <root>.

Instalación de Apache Tomcat
Para su instalación seguiremos los pasos del post Cómo Instalar Apache Tomcat en Solaris 10 utilizando SMF para su gestión, aunque haremos algunos cambios -ahora que conocemos RBAC y Roles-

Diferencias en la Instalación
Cuando hablábamos de Cómo Instalar Tomcat en Solaris 10 utilizando SMF, todavía no habíamos mencionado el tema de RBAC, Roles y Privilegios, por lo tanto, no podíamos entrar en mucha complicación. Ahora bien, ya estamos en disposición de poder instalarlo de una forma mucho más segura y eficiente utilizando: roles, privilegios y autorizaciones.


Creación de la Clave RSA
En nuestro ejemplo vamos a generar un clave <auto sellada>, es decir, que seremos nosotros mismos quienes validemos la firma.

Esta funcionalidad nos permite establecer certificados de forma sencilla, aunque no es recomendable para su puesta en producción -sobre todo si es un servicio a terceros-, principalmente porque al estar validado por nosotros mismos, el navegador nos mostrará un mensaje de aviso.

Sin embargo, para pruebas es una forma muy sencilla de trabajar -y barata-. En nuestro caso, queremos crear el keystore en <$TOMCAT_HOME/cert/havoctec-tomcat>
$ cd $TOMCAT_HOME
$ mkdir cert
$ chmod 700 cert
$ chown appserver:root cert
Ahora podemos utilizar la utilidad <keytool> de Java para generar la clave RSA que utilizaremos en Tomcat
$ keytool -keystore /var/sfchildren/appserver/tomcat-6.0/cert/havoctec-tomcat -genkey -keyalg RSA -alias havoctec
Escriba la contraseña del almacén de claves: havoctec 
Volver a escribir la contraseña nueva: havoctec
¿Cuáles son su nombre y su apellido?
  [Unknown]:  Urko Benito
¿Cuál es el nombre de su unidad de organización?
  [Unknown]:  HavocTec S.L.
¿Cuál es el nombre de su organización?
  [Unknown]:  HavocTec S.L.
¿Cuál es el nombre de su ciudad o localidad?
  [Unknown]:  MADRID
¿Cuál es el nombre de su estado o provincia?
  [Unknown]:  SPAIN
¿Cuál es el código de país de dos letras de la unidad?
  [Unknown]:  ES
¿Es correcto CN=Urko Benito, OU=HavocTec S.L., O=HavocTec S.L., L=MADRID, ST=SPAIN, C=ES?
  [no]:  si

Escriba la contraseña clave para
        (INTRO si es la misma contraseña que la del almacén de claves):  havoctec
Esto nos ha creado una nueva clave RSA en el almacén que hemos indicado, en nuestro caso
<$TOMCAT_HOME/cert>, sin embargo, los permisos son muy leves, y es interesante modifcarlos para que sólo el dueño pueda leer y escribir en el él, por ejemplo:
$ ls -ltr
total 4
-rw-r--r--   1 appserver asengine    1381 nov 29 09:18 havoctec-tomcat
$ chmod 600 havoctec-tomcat
$ ls -ltr
total 4
-rw-------   1 appserver asengine    1381 nov 29 09:18 havoctec-tomcat

Creación del Connector HTTPS en puerto NO PRIVILEGIADO (8443)
Por último, definiremos un conector HTTPS en el archivo de configuración <$TOMCAT_HOME/conf/server.xml> donde deberemos indicarle la ubicación del keystore, y su contraseña, por ejemplo, en nuestro caso la ubicación del certificado es <$TOMCAT_HOME/cert/havoctec-tomcat> y su contraseña <havoctec>

$ vi $TOMCAT_HOME/conf/server.xml

    <Connector
        port="8443"
        protocol="HTTP/1.1"
        SSLEnabled="true"
        maxThreads="150"
        scheme="https"
        secure="true"
        clientAuth="false"
        keystoreFile="/var/sfchildren/appserver/tomcat-6.0/cert/havoctec-tomcat"
        keystorePass="havoctec"

        sslProtocol="TLS" />

:wq

Conexión mediante HTTPS a Tomcat
Ya podemos iniciar nuestro servidor Apache Tomcat (si lo tenemos levantado, deberemos reiniciarlo) y con un navegador nos conectaremos al puerto 8443 utilizando HTTPS

Mi servidor de ejemplo está en la IP 192.168.1.13 y, cuando me conecto con Safari al servicio veo el siguiente mensaje de aviso -como ya os he comentado es debido a que hemos "auto firmado" el certificado-

 Si pulso sobre "Mostrar Certificado" puedo ver los datos que he introducido en le comando <keytool> para su generación.


Funciona! No ha sido tan difícil, no? Ahora bien, el puerto para el protocolo HTTPS es el 443, y no el 8443 así que tenemos diferentes opciones para poder hacer una conexión HTTPS utilizando la forma <https:// ....>
  • Utilizamos <ipfilter> para redirigir el tráfico
  • Configuramos el usuario (role) de Apache Tomcat para permitirle utilizar puertos privilegiados
  • Ponemos un <reverse proxy> con soporte SSL
Como veis son muchas las opciones, aunque hoy vamos a ver una muy sencilla -ahora que conocéis RBAC, Roles y Privilegios- vamos a otorgar el privilegio <net_privaddr>


Configuración Privilegios Role o Usuario
Para poder hacer que nuestro <role> o <user> levante el servicio Tomcat en un puerto privilegiado, deberemos añadir el privilegio <net_privaddr>, para ello simplemente deberemos ejecutar (el <role> encargado de levantar el servicio de Tomcat en mi caso es <appserver>)
root@openzooey:/# rolemod -K defaultpriv=basic,net_privaddr appserver

A continuación, modificaremos el conector para que, en vez de escuchar en el puerto 8443 lo haga en le 443 y levantaremos nuestro Tomcat

$ $TOMCAT_HOME/bin/shutdown.sh
$ vi $TOMCAT_HOME/conf/server.xml
    <Connector
        port="443"
        protocol="HTTP/1.1"
        SSLEnabled="true"
        maxThreads="150"
        scheme="https"
        secure="true"
        clientAuth="false"
        keystoreFile="/var/sfchildren/appserver/tomcat-6.0/cert/havoctec-tomcat"
        keystorePass="havoctec"

        sslProtocol="TLS" />
 :wq
$ $TOMCAT_HOME/bin/startup.sh


Conclusiones
En esta ocasión hemos visto como con el uso de privilegios nos permite hacer cosas que antes deberíamos delegar en <root>, además, de esta forma podemos tener un sistema muy seguro y controlado.



Referencias

miércoles, 17 de noviembre de 2010

Política de Contraseñas en OpenIndiana y Solaris

Introducción
Una característica no tan conocida de OpenIndiana y Solaris, es el uso de diccionarios de contraseñas dentro de las opciones de la política de contraseñas.

Por defecto, Solaris no establece ninguna política de contraseñas fuertes, es decir, longitudes mínimas, formato, que no sean igual que el login, etc. Sin embargo, todo esto es configurable.

En esta ocasión, veremos cómo podemos mejorar la seguridad modificando los parámetros de longitud, duración y, por último, utilizaremos un diccionario para que Solaris evalúe la contraseña antes de asignarla.

Algoritmo de Encriptación
En Solaris, el algoritmo de encriptación asignado por defecto era el original de UNIX, que se mantenía por compatibilidad, sin embargo, este algoritmo tenía muchas limitaciones, y a día de hoy, no es nada seguro.

En OpenIndiana, sin embargo, se utiliza el algoritmo SHA256 por defecto para hacer el "hashing" de las contraseñas mucho más seguro.

Asignar un Algoritmo Nuevo
La definición del algoritmo que Solaris u OpenIndiana utilizará para "crypt", se encuentra definido en el archivo </etc/security/policy.conf>, para ello, modificaremos el valor de la propiedad <CRYPT_DEFAULT> y pondremos un valor definido en el archivo de configuración </etc/security/crypt.conf>, veamos un ejemplo.

Vamos a definir que Solaris/OpenIndiana utilice como algoritmo SHA512, que, según el archivo </etc/security/crypt.conf> corresponde con el valor "6", por lo tanto,
root@openzooey:/# cat /etc/security/crypt.conf 
# The algorithm name __unix__ is reserved.

1       crypt_bsdmd5.so.1
2a      crypt_bsdbf.so.1
md5     crypt_sunmd5.so.1
5       crypt_sha256.so.1
6       crypt_sha512.so.1
root@openzooey:/# vi /etc/security/policy.conf
  ...
  CRYPT_DEFAULT=6
  ...

:wq
Por último, cambiamos la contraseña de nuestro usuario, en mi caso <webope>
root@openzooey:/# passwd webope
New Password:
Re-enter new Password:
passwd: password successfully changed for webope
root@openzooey:/# cat /etc/shadow |grep webope
webope:$5$5TGOqra.$WjEbK0J.v.9SjU61cYUrJmtaz3UuHS/FoE.CXApNF05:14920::::::

Longitud Mínima, Historial y Formato de contraseñas
La definición de las propiedades de la contraseña, es decir, longitud, formato, historial, etc. se encuentra definida en el archivo < /etc/default/passwd>.
Vamos a ver cómo podemos hacer nuestra política de contraseñas más segura incluyendo:
  • PASSLENGTH. Longitud Mínima, 8
  • MINALPHA. Letras, 2
  • MINDIGIT. Números, 2
  • MINSPECIAL. Caracteres Espaciales, 1
Editamos el archivo </etc/default/passwd> con esto valores, y comprobamos su funcionamiento
root@openzooey:/# vi /etc/default/passwd
  MINALPHA=2
  MINDIGIT=2
  MINSPECIAL=1
  NAMECHECK=YES
  PASSLENGTH=8
:wq
Una cosa importante a tener en cuenta, es que, como en muchas situaciones en Solaris, el usuario <root> puede hacer lo que quiera, y esta es una de ellas.

Me explico, por ejemplo, si he cambiado la política de contraseñas con los valores anteriores, teóricamente no puedo asignar la password <1234> -ya que incumple todas: longitud, digitos, caracteres especiales, etc.

Sin embargo, si yo lo hago con <root> no tendré ningún problema, por ejemplo:
root@openzooey:/# passwd test
New Password: test
Re-enter new Password: test
passwd: password successfully changed for test
Pero si lo intento con el usuario
root@openzooey:/# su - test
OpenIndiana     SunOS 5.11      oi_147  September 2010
test@openzooey:~$ passwd test
Enter existing login password: testNew Password: hola
passwd: Password too short - must be at least 8 characters.

Please try again
New Password: ^C
Utilizar un Diccionario
Por último, tenemos la posibilidad de utilizar un diccionario de contraseña para evitar que nuestros usuarios no sean creativos con sus contraseñas, :D.

Deberemos descomentar la opción <DICTIONDBDIR=/var/passwd> del archivo de configuración </etc/default/passwd> para indicarle al comando <passwd> que su diccionario está en </var/passwd>.

Este path es configurable, es decir, podemos asignar el que queramos, sin embargo, </var/passwd> es el path que Solaris utiliza por defecto
Para ello, utilizaremos el comando <mkpwdict> que tiene el siguiente formato:
# mkpwdict -s file1,file2,filen -d destino
Donde file1, file2, filen serán archivos que contienen la lista de palabras sobre las cuales vamos a crear el diccionario, y <destino> el lugar donde lo almacenaremos. Si no ponemos ningún destino, se utilizará el asignado en <DICTIONDBDIR>.

Comprobamos que está descomentada la opción DICTIONDBDIR del archivo </etc/default/passwd>, si no fuese así deberíamos descomentarla.
root@openzooey:/# cat /etc/default/passwd |grep DICTIONDBDIR|grep -v ^#
DICTIONDBDIR=/var/passwd
A continuación nos creamos un archivo con las palabras sobre las cuales queremos crear el diccionario, por ejemplo:
root@openzooey:/# vi /tmp/words
rootROOT
testTEST
12345678
qwerty12
abcdefgh
1234abcd
1234ABCD
Creamos el diccionario y probamos
root@openzooey:/# mkpwdict -s /tmp/words
mkpwdict: using default database location: /var/passwd.

root@openzooey:/# passwd test
New Password: test
Re-enter new Password: test
passwd: password successfully changed for test
root@openzooey:/# su - test
OpenIndiana     SunOS 5.11      oi_147  September 2010
test@openzooey:~$ passwd test
Enter existing login password: test
New Password: 1234ABCD
passwd: password is based on a dictionary word.

Please try again
New Password: ^C

Conclusiones

Aunque las versiones de Solaris 10, 9, 8 venían con una configuración muy baja en cuanto a temas de seguridad, en OpenIndiana, y, recientemente en Solaris 11 han mejorado mucho este tema.

Principalmente el algoritmo UNIX ya no es el usado por defecto, además de incluir una longitud mínima, aunque todavía no incluyen un diccionario por defecto.

Espero que con estas pequeñas aclaraciones, podamos mejorar la seguridad de nuestros sistemas Solaris y así hacerlo más "Rock Solid" todavía,


Referencias

viernes, 12 de noviembre de 2010

Modelo de Seguridad de Solaris: RBAC, Roles y Privilegios - Parte 7

Introducción
Continuando con nuestra serie de RBAC, Roles y Privilegios y cómo podemos aplicarlo a nuestras instalaciones. En esta última entrega vamos a ver el uso de <pfexec> -el sudo de Solaris- y cómo podemos aplicarlo en nuestro sistema

Que es <pfexec>
Podemos definir <pfexec> como una versión mejorada del comando <sudo>, el cual nos permite ejecutar comandos con unos privilegios diferentes a los que el usuario logeado tiene. Por ejemplo, podemos ejecutar un comando como si de <root> se tratase, o como otro.

<pfexec> nos permite asignar un grupo de propiedades como: uid, gid, euid, privileges, etc. De esta forma, podemos crear una forma sencilla de acceder a comando privilegiados por cuentas que no lo son.

La base de datos de <pfexec>
La definición de los comandos y sus opciones se encuentra en el archivo </etc/security/exec_attr> y su formato es el siguiente:
name:policy:type:res1:res2:id:attr
Donde
  • name, hace referencia al nombre de la política. Distingue entre mayúsculas y minúsculas
  • policy, hace referencia al tipo de política, puede tener los valores: suser o solaris. En versiones anteriores a Solaris 10, siempre era suser (Standar Solaris User), sin embargo, a partir de Solaris 10 se introdujo <solaris> que nos permite asignar <privileges>, cosa que con %lt;suser> no es posible. Además, <suser> es un subconjunto de <solaris>, por lo tanto, yo os recomiendo utilizar siempre <solaris> si utilizais la versión 10 o superior.
  •  type, hace referencia al tipo, donde puede tener los valores: act o cmd. El valor de <act> se utiliza cuando tenemos configurado <Trusted Extensions> y hace referencia a una acción de CDE. Si utilizamos el valor <cmd> hace referencia a un comando <shell>.
  • res1 y res2, están reservados para futuros usos
  • id, texto que identifica de forma única el objeto, es decir, si hemos definido <type> como <cmd>, entonces será el "full path" al comando. Podemos utilizar el "*" si queremos indicar "cualquier comando", por ejemplo, si queremos permitir la ejecución de todos los comandos de <sbin> del servidor de DHCP, pondremos <$DHCP_HOME/sbin/*>.
  • attr, son los atributos que quiero asignar a la política donde serán: euiduidegidgid, privs y limitprivs.
Por ejemplo, tenemos la siguiente entrada en la base de datos sobre el uso del comando <pfiles>
Process Management:solaris:cmd:::/usr/bin/pfiles:privs=proc_owner
Esta entrada, nos permitirá ejecutar el comando <pfiles> como utilizamos el <privilegio> >proc_owner> estaremos permitiendo enviar señales a otros procesos, a excepción de que tengan el uid=0.

Esto es un tema importante, es decir, si utilizamos el attr:uid=0, estaremos ejecutando el comando como <root>, sin embargo, al utilizar attr:privs=proc_owner, estamos diciendo que el comando se ejecuta con el uid del usuario, pero le concedemos el privilegio de enviar señales a los demás procesos siempre que no sean de <root&gt.  Esto lo explicaremos en el ejemplo, no os preocupeis.

Crear una nueva Política
El uso de <pfexec> está unido a las políticas, es decir, en la base de datos </etc/security/exec_attr> se encuentran "los detalles", sin embargo, la definición de una política se encuentra en </etc/security/prof_attr>, por lo tanto, para crear una nueva política deberemos definir primero la política en la base de datos </etc/security/prof_attr> y posteriormente añadir los comandos -o definición de la política- en la base de datos </etc/security/exec_attr>.

Por ejemplo, imaginemos que queremos crear una política nueva llamada <My Process Management> y queremos que esta política pueda ejecutar el comando </usr/bin/pfiles> con un <uid=0>, entonces haremos lo siguiente:

root@openzooey:/# echo "My Process Management:::Custom Process Management with UID to 0:help=RtProcessManage.html" >> /etc/security/prof_attr
root@openzooey:/# echo "My Process Management:solaris:cmd:::/usr/bin/pfiles:uid=0" >> /etc/security/exec_attr
De esta forma hemos creado una nueva política y hemos asignado unos atributos de ejecución.

Asignar una Política
La asignación de una política se realiza con el comando <rolemod> -si es un <role>- o &usermod> -si es un usuario- y la opción <-P '_policy_name1, _policy_name2, ...'>, por ejemplo:
root@openzooey:/# rolemod -P 'Process Management,Network Management' tomcat6
Podemos comprobar cómo se han añadido las nuevas políticas a nuestra base de datos de usuarios en </etc/user_attr>, por ejemplo,
root@openzooey:/# cat /etc/user_attr|grep tomcat6
tomcat6::::type=role;profiles=Process Management,Network Management
Eliminar una Política
Para eliminar una política, deberemos asignar una política vacía <-P ''>, es decir,
root@openzooey:/# rolemod -P '' tomcat6
root@openzooey:/# cat /etc/user_attr|grep tomcat6
tomcat6::::type=role

Ver los profiles asginados a un <role> o <user>
Para ver los profiles asignados utilizaremos el comando <profiles> y el nombre de usuario o role que queremos verificar, por ejemplo, si queremos ver los roles asignados al role <tomcat6> haremos lo siguiente:
root@openzooey:/# profiles tomcat6
tomcat6:
          Basic Solaris User
          All
Podemos utilizar el comando <-l> para ver de forma extendida la información, por ejemplo:

root@openzooey:/# profiles -l tomcat6
tomcat6:
      Basic Solaris User
          /usr/bin/cdda2wav.bin      privs=file_dac_read,sys_devices,proc_priocntl,net_privaddr
          /usr/bin/cdrecord.bin      privs=file_dac_read,sys_devices,proc_lock_memory,proc_priocntl,net_privaddr
          /usr/bin/readcd.bin        privs=file_dac_read,sys_devices,net_privaddr
      All
          *
Un Ejemplo Completo
Tenemos un <role> llamado <tomcat6> y queremos permitirle ver/modificar procesos del sistema. Para ello, hemos decidido añadirle la política <Process Management> que se encuentra ya definida en Solaris.

Esta política <Process Management> utiliza privilegios en vez de uid, por lo tanto, nos permitirá ver/modificar todos los procesos menos los que tengan uid=0 -es decir, de root-.

Lo primero que vamos a hacer es comprobar que <tomcat6> no tiene privilegios para modificar la prioridad del proceso <rcpbind> y luego asignaremos el <profile> "Process Management" para ver qué sucede:
root@openzooey:/# profiles tomcat6
tomcat6:
          Basic Solaris User
          All
root@openzooey:/# su - tomcat6
OpenIndiana     SunOS 5.11      oi_147  September 2010
tomcat6@openzooey:~$ ps -o pri,user,pid,nice,args -p 665
PRI     USER   PID NI COMMAND
 59   daemon   665  0 /usr/sbin/rpcbind
tomcat6@openzooey:~$ renice 10 665
renice: 665:setpriority: Not owner
Obviamente, Solaris no ha permitido a <tomcat6> modificar la prioridad del proceso porque no es su propietario, sin embargo, si asignamos el profile <Process Management>, veremos qué sucede,
root@openzooey:/# rolemod -P 'Process Management' tomcat6
root@openzooey:/# profiles tomcat6
tomcat6:
          Process Management
          Basic Solaris User
          All
root@openzooey:/# su - tomcat6
OpenIndiana     SunOS 5.11      oi_147  September 2010
tomcat6@openzooey:~$ ps -o pri,user,pid,nice,args -p 665
PRI     USER   PID NI COMMAND
 59   daemon   665  1 /usr/sbin/rpcbind
tomcat6@openzooey:~$ renice +19 665
tomcat6@openzooey:~$ ps -o pri,user,pid,nice,args -p 665
PRI     USER   PID NI COMMAND
 59   daemon   665 39 /usr/sbin/rpcbind
En esta ocasión hemos podido moficar el <nice> del proceso -aún cuando no es nuestro-, además este <profile> nos permite examinar los procesos aunque no sean nuestros -recordar lo que hemos hablado al principio sobre los privilegios priv=proc_owner-, vamos a verlo con detalle.

Ahora lo que vamos a utilizar el el comando <pfiles> para examinar los archivos abiertos de un proceso, para ello, vamos a examinar el proceso de <prostgres>
tomcat6@openzooey:~$ ps -o pri,user,pid,nice,args -p 614
PRI     USER   PID NI COMMAND
 59 postgres   614 20 /u01/app/postgres/9.0/db/bin/64/postgres -D /var/postgres/9.0/data
tomcat6@openzooey:~$ pfiles 614
614:    /u01/app/postgres/9.0/db/bin/64/postgres -D /var/postgres/9.0/data
  Current rlimit: 256 file descriptors
   0: S_IFCHR mode:0666 dev:524,0 ino:19922952 uid:0 gid:3 rdev:38,2
      O_RDONLY|O_LARGEFILE
      /devices/pseudo/mm@0:null
      offset:0
   1: S_IFREG mode:0600 dev:90,65538 ino:149346 uid:90 gid:90 size:156012
      O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE
      /var/log/postgres/server.log
      offset:156012
   2: S_IFREG mode:0600 dev:90,65538 ino:149346 uid:90 gid:90 size:156012
      O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE
      /var/log/postgres/server.log
      offset:156012
   3: S_IFDOOR mode:0444 dev:534,0 ino:47 uid:0 gid:0 size:0
      O_RDONLY|O_LARGEFILE FD_CLOEXEC  door to nscd[462]
      /var/run/name_service_door
   7: S_IFSOCK mode:0666 dev:533,0 ino:45078 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK
        SOCK_DGRAM
        SO_DGRAM_ERRIND,SO_SNDBUF(57344),SO_RCVBUF(57344)
        sockname: AF_INET6 ::1  port: 59936
        peername: AF_INET6 ::1  port: 59936
Sin embargo, si ahora hacemos lo mismo sobre un proceso con uid=0, por ejemplo <nscd> veremos qué sucede
tomcat6@openzooey:~$ ps -ef|grep nscd|grep -v grep
    root   462     1   0 11:29:37 ?           0:09 /usr/sbin/nscd
tomcat6@openzooey:~$ pfiles 462
pfiles: permission denied: 462
Oops! Acceso denegado! Si, esto es debido al uso de privilegios y no <uid=0>. Esta es una gran diferencia con <sudo> ya que nos permite tener una definición mayor y más segura.

Cómo puedo hacer que se comporte como <sudo>
Bien, en nuestro ejemplo de Cómo crear una nueva política, hemos definido <My Process Management> donde permitíamos ejecutar el comando </usr/bin/pfiles> con el atributo <uid=0>, vamos a asignar este <profile> a nuestro usuario <tomcat6> y volveremos a comprobar su salida.

root@openzooey:/# rolemod -P 'My Process Management' tomcat6
root@openzooey:/# profiles -l tomcat6
tomcat6:
      My Process Management
          /usr/bin/pfiles            uid=0
      Basic Solaris User
          /usr/bin/cdda2wav.bin      privs=file_dac_read,sys_devices,proc_priocntl,net_privaddr
          /usr/bin/cdrecord.bin      privs=file_dac_read,sys_devices,proc_lock_memory,proc_priocntl,net_privaddr
          /usr/bin/readcd.bin        privs=file_dac_read,sys_devices,net_privaddr
      All
          *
root@openzooey:/# su - tomcat6
OpenIndiana     SunOS 5.11      oi_147  September 2010
tomcat6@openzooey:~$ ps -ef|grep nscd|grep -v grep
    root   462     1   0 11:29:37 ?           0:09 /usr/sbin/nscd
tomcat6@openzooey:~$ pfiles 462
462:    /usr/sbin/nscd
  Current rlimit: unlimited file descriptors
   0: S_IFCHR mode:0666 dev:524,0 ino:19922952 uid:0 gid:3 rdev:38,2
      O_RDWR
      /devices/pseudo/mm@0:null
      offset:0
   1: S_IFCHR mode:0666 dev:524,0 ino:19922952 uid:0 gid:3 rdev:38,2
      O_RDWR
      /devices/pseudo/mm@0:null
      offset:0
   2: S_IFCHR mode:0666 dev:524,0 ino:19922952 uid:0 gid:3 rdev:38,2
      O_RDWR
      /devices/pseudo/mm@0:null
      offset:0
   3: S_IFDOOR mode:0777 dev:527,0 ino:0 uid:0 gid:0 size:0
      O_RDWR FD_CLOEXEC  door to nscd[462]
   4: S_IFSOCK mode:0666 dev:533,0 ino:23670 uid:0 gid:0 size:0
      O_RDWR
        SOCK_RAW
        SO_SNDBUF(8192),SO_RCVBUF(8192)
        sockname: AF_ROUTE
        peername: AF_ROUTE
En esta ocasión sí que Solaris nos ha permitido ver los archivos abiertos de <nscd> porque hemos utilizado <uid=0> en los atributos, haciendo que se ejecute como <root>, por lo tanto, no hay limitaciones.

Cuando debo utilizar <pfexec _comando_> y cuando no
Lo cierto es que ya hemos hablado de esto en la entrega dedicada a los Roles en RBAC, Roles y Seguridas Parte 5 pero para hacer un breve resumen os diré
  • Hay que utilizar <pfexec _comando_> cuando queramos lanzarlo desde un <usuario>, ya que la <shell> de un usuario no es <pfXYZ>.
  • Si lanzamos el comando desde un <role>, no es necesario, ya tenemos asignada una <shell> &pfXYZ>, por lo tanto, no es necesario.

Conclusiones
Con esta entrega ya hemos cerrado el círculo de RBAC y Roles en Solaris, sólo nos queda la parte más importante, la auditoría que será el último artículo de esta serie.

Espero que con todo esto, ahora podáis utilizar las seguridades avanzadas que nos ofrece Solaris, y, sobre todo, pongáis en práctica todas aquellas modificaciones que se os ocurran.

<< RBAC, Roles y Privilegios en Solaris 10 - Parte 1 
<< RBAC, Roles y Privilegios en Solaris 10 - Parte 2 
<< RBAC, Roles y Privilegios en Solaris 10 - Parte 3
<< RBAC, Roles y Privilegios en Solaris 10 - Parte 4
<< RBAC, Roles y Privilegios en Solaris 10 - Parte 5
<< RBAC, Roles y Privilegios en Solaris 10 - Parte 6


Referencias

viernes, 5 de noviembre de 2010

Intervalos y Fechas en PostgreSQL

Introducción
Muchas veces nos encontramos con la necesidad de realizar cálculos con fechas, y siempre son problemáticas. Ya hablamos en otra ocasión sobre Gestión de Husos Horarios en PostgreSQL y hoy vamos a ver algunas funcionalidades que nos ofrece PostgreSQL para movernos con fechas.

CURRENT_DATE y now()
La funcione <current_date> nos permite saber la fecha actual, sin embargo, no sabemos la hora ni su desplazamiento -ya que es un tipo date-.

Si utilizamos la función <now()> obtendremos la fecha-hora actual con su desplazamiento horario, por ejemplo

search=> select current_date;
    date   
------------
 2010-11-05
(1 row)

search=> select now();
              now             
-------------------------------
 2010-11-05 09:14:18.450761+01
(1 row)
También podemos utilizar <current_timestamp> que, a efectos prácticos, es lo mismo que <now()>, por ejemplo
search=> select current_timestamp;
              now             
-------------------------------
 2010-11-05 09:18:13.745486+01
(1 row)

search=> select now(), current_timestamp;
              now              |              now             
-------------------------------+-------------------------------
 2010-11-05 09:18:27.408072+01 | 2010-11-05 09:18:27.408072+01
(1 row)

Uso de Intervalos
Hasta aquí nada nuevo, sin embargo, si quiero obtener la fecha de ayer, cómo lo hago? Bueno, existen dos formas de hacerlo: bien y no tan bien. Hoy os explicaré la forma de hacerlo bien ... :D

PostgreSQL nos ofrece la posibilidad de utilizar Intervalos, por ejemplo, <hour>, <day>, <month> -que corresponden con hora, día, mes, etc- por lo tanto, como puedo utilizar funciones de suma y resta a las fechas, puedo hacer:

search=> select now()-'24 hour'::interval;
           ?column?           
-------------------------------
 2010-11-04 09:24:12.504315+01
(1 row)
He restado un intervalo de 24 horas a la fecha actual, por lo tanto a alguien se le puede estar ocurriendo la siguiente solución para saber la fecha de ayer:
search=> select (now()-'24 hour'::interval)::date;
    date   
------------
 2010-11-04
(1 row)
Bien, esta solución es válida pero no es la correcta. Para saber la fecha de ayer PostgreSQL nos ofrece la siguiente opción, utilizar <yesterday>, por ejemplo:

search=> select 'yesterday'::date;
    date   
------------
 2010-11-04
(1 row)
O mejor aún, con TimeZone
search=> select 'yesterday'::timestamp with time zone;
      timestamptz      
------------------------
 2010-11-04 00:00:00+01
(1 row)
También podemos saber la fecha de hoy utilizando
search=> select 'today'::date;
    date   
------------
 2010-11-05
(1 row)

Como veís el uso de intervalos -usando casting- nos puede ahorras muchas horas de sufrimiento, simplemente debemos recordar que para poder sumar o restar a <now()> debemos utilizar <interval> y, que si queremos saber la fecha de hoy o ayer, podemos utilizar las palabras <today> y <yesterday> haciendo un cast a <date> o <timestamp>.

Por ejemplo, en una de mis aplicaciones existe una tabla de eventos que guarda un registro de lo que ha sucedido, si quisiera saber cuántos eventos se han producido desde ayer utilizaría:

search=> select count(1) from audit.events where event_datetime > 'yesterday'::timestamp with time zone;
 count
-------
 38075
(1 row)
Sin embargo, si quiero saber cuántos eventos se han producido en las últimas 24h, haría
search=> select count(1) from audit.events where event_datetime > now()-'24 hour'::interval;
 count
-------
 37493
(1 row)
El resultado es diferente -como tiene que ser- porque si analizamos las fechas vemos que al utilizar <yesterday> siempre comenzamos desde las 00:00h, y con los intervalos dependemos de la hora en la que se ejecute
  search=> select 'yesterday'::timestamp with time zone, now()-'24 hour'::interval;
      timestamptz       |           ?column?           
------------------------+-------------------------------
 2010-11-04 00:00:00+01 | 2010-11-04 09:52:30.401646+01
(1 row)
Para concluir, os contaré un uso de un intervalo que, realmente a mí, me ha sacado de muchos apuros: week, month, year (semana, mes, año).

Fijaros cómo PostgreSQL se encarga de ajustar los cambios horarios. En España hemos realizado recientemente el cambio de hora, y por lo tanto, hace una semana estábamos con GMT+2, pero ahora estamos con GMT+1.
search=> select now()-'1 year'::interval;
           ?column?           
-------------------------------
 2009-11-05 09:58:15.050784+01
(1 row)

search=> select now()-'1 month'::interval;
           ?column?           
-------------------------------
 2010-10-05 09:58:25.412129+02
(1 row)

search=> select now()-'1 week'::interval;
           ?column?          
------------------------------
 2010-10-29 09:58:31.60385+02
(1 row)
Sólo una última cosa, el uso de nombre ne singular o plurar es el mismo, por ejemplo,  <week> y <weeks>
search=> select now()-'2 week'::interval;
           ?column?           
-------------------------------
 2010-10-22 09:59:20.024957+02
(1 row)

search=> select now()-'2 weeks'::interval;
           ?column?           
-------------------------------
 2010-10-22 09:59:22.601622+02
(1 row)

Conclusiones
PostgreSQL nos ofrece un montón de ventajas en el manejo de Fechas -con o sin intervalos- además de proporcionarnos un interface sencillo. Es cierto que muchas veces -por descuido- olvidamos el uso de los desplazamientos horarios, pero son vitales y, si entramos en el mundo de la auditoría de sistemas, no podemos olvidarlos.

Referencias

martes, 2 de noviembre de 2010

Copias de Seguridad y Recuperación en PostgreSQL - Parte 1

Introducción
En esta ocasión vamos a ver cómo realizar un procedimiento de backup utilizando las herramientas que nos proporciona PostgreSQL. En estas primeras partes, veremos cómo podemos utilizar <pg_dump> y las principales opciones.

Posteriormente, veremos cómo utilizar PostgreSQL PITR -Point In-Time Recovery- y qué ventajas nos ofrece este sistema de respaldo.

Tipos de Backups
En PostgreSQL existen diferentes tipos de Copias de Seguridad -Backups-: Completos y  Parciales.

En esta primera parte, veremos los backups completos y cómo podemos realizarlos, dejaremos los backups parciales para las siguientes entregas.

Backup Completo -Full Backup-
Este tipo de copia de seguridad es un volcado completo de nuestra base de datos en un instante "t", y como es natural, contiene la información hasta el instante que comenzamos el proceso de backup.

Para realizar este backup utilizaremos el comando <pg_dump> y, es posible realizar esta operación en caliente, es decir, podemos tener nuestra base de datos abierta.

Ventajas Backup Completo
  • Sencillo de realizar
  • Se puede realizar en caliente
  • Backup "consistente"
  • En bases de datos pequeñas en muy rápido
Problemas Backup Completo
  • Se restaura "todo" o "nada"
  • Consumo de I/O elevado
  • En bases de datos grandes puede no ser efectivo

Realizar un Backup
Ya hemos hablado que para realizar un "full backup" utilizaremos el comando <pg_dump> cuyo formato es el siguiente:
pg_dump [OPCIONES] [DATABASE]
Donde las opciones más importantes son
  • -f, Nombre del archivo de salida donde vamos a guardar nuestro backup
  • -Z, Salida comprimida con GZip. Por defecto el nivel de compresión es normal, pero podemos utilizar la compresión máxima si ponemos -Z 9
  • -a, Sólo datos, nada de estructura.
  • -b, Incluye objetos grandes en el volcado (blobs)
  • -c, Añade comandos de borrado (clean) antes de cada objeto, por ejemplo, "DROP SCHEMA ..."
  • -C, Añade los comandos de creación (create) antes de cada objecto, por ejemplo, "CREATE SCHEMA ..."
  • -E, Utiliza el tipo de caracteres indicado en el volcado, por ejemplo, "-E UTF8"
  • -n, Vuelca sólo el schema indicado, por ejemplo, "-n public"
  • -N, Vuelca todos los schemas menos el indicado, por ejemplo, "-N private"
  • -o, Incluye los OIDs en el volcado
  • -O, No restaura los propietarios a los objetos
  • -s, Vuelca sólo estructura nada de datos
  • -t, Vuelca sólo la tabla indicada, por ejemplo, "-t havoctec.users"
  • -T, Vuelca todas las tablas menos la indicada, por ejemplo "-T havoctec.options"
  • -X, No vuelva los privilegios, es decir, los GRANT y REVOKE
Por ejemplo, podemos realizar un backup completo utilizando
$ pg_dump -f /u07/backups/full/havoctec-db-FULL-02.NOV.2010.dump.gz -Z 9 -o -C havoctec
Restaurar un Full Backup
La restauración de un backup realizado con <pg_dump> es realmente sencilla, debemos tener en cuenta que al ser un backup completo vamos a "volcar todos los datos". En función de las opciones que hemos utilizado en el volcado, deberemos realizar algunos pasos diferentes, por ejemplo, si hemos utilizado la opción -C <opción Create>, dentro del propio dump tendremos los comandos SQL para la creación de la base de datos, y por lo tanto no será necesario crearla. Si no hemos utilizado esa opción, deberemos crear la base de datos primero -a no ser que queramos restaurar sobre otra base de datos diferente-

Para realizar la restauración de un volcado completo, utilizaremos el comando <psql> y como entrada nuestro archivo de backup, por ejemplo
$ psql -U postgres < /u07/backups/full/havoctec-db-FULL-02.NOV.2010.dump
Fijaros que no he puesto el archivo gzip (.gz) ya que el comando <psql> cree que lo que viene son instrucciones SQL en texto plano, si no queremos descromprimirlo podemos utilizar
$ gzip -dc /u07/backups/full/havoctec-db-FULL-02.NOV.2010.dump | psql -U postgres


Un Ejemplo Completo
En este ejemplo vamos a crear una base de datos llamada "havoctec" y en ella una tabla donde introduciremos varios datos. Posteriormente realizaremos una copia de seguridad y la restauraremos.

Recordar que si no tenemos asignado el valor de <PGDATA> deberemos exportarlo antes de poder trabajar con PostgreSQL.
$ export PGDATA=/var/postgres/8.4/data
Nos conectamos a PostgreSQL y crearemos la base de datos <havoctec>
$ psql -U postgres
Password for user postgres:
psql (8.4.1)
Type "help" for help.

postgres=# create database havoctec;
CREATE DATABASE
postgres=# \c havoctec;
psql (8.4.1)
You are now connected to database "havoctec".
havoctec=# create table urls(url_id serial, url_name character varying);
NOTICE:  CREATE TABLE will create implicit sequence "urls_url_id_seq" for serial column "urls.url_id"
CREATE TABLE
havoctec=# insert into urls(url_name) values('http://www.havoctec.com/');
INSERT 0 1
havoctec=# insert into urls(url_name) values('http://www.sfchildren.com/');
INSERT 0 1
havoctec=# insert into urls(url_name) values('http://sparcki.blogspot.com/');
INSERT 0 1
havoctec=# select * from urls;
 url_id |           url_name          
--------+------------------------------
      1 | http://www.havoctec.com/
      2 | http://www.sfchildren.com/
      3 | http://sparcki.blogspot.com/
(3 rows)
A continuación, realizaremos una copia de seguridad utilizando la opción <-C> (create) para poder restaurar en la misma base de datos con el mismo nombre.

$ time pg_dump -C -f /var/backups/havoctec-db-full.dump -U postgres havoctec
Password:

real    0m3.490s
user    0m0.029s
sys     0m0.012s
Ahora, borramos la base de datos
$ psql -U postgres
Password for user postgres:
psql (8.4.1)
Type "help" for help.

postgres=# drop database havoctec;
DROP DATABASE
postgres=# \q
Por último, restauraremos la base de datos
$ psql -U postgres < /var/backups/havoctec-db-full.dump
Password for user postgres:
SET
SET
SET
SET
SET
SET
CREATE DATABASE
ALTER DATABASE
You are now connected to database "havoctec".
SET
SET
SET
SET
SET
SET
CREATE LANGUAGE
ALTER LANGUAGE
SET
SET
SET
CREATE TABLE
ALTER TABLE
CREATE SEQUENCE
ALTER TABLE
ALTER SEQUENCE
 setval
--------
      3
(1 row)

ALTER TABLE
REVOKE
REVOKE
GRANT
GRANT
Comprobamos que tenemos los mismos registros, realizando un simple <SELECT>

$ psql -U postgres -d havoctec
Password for user postgres:
psql (8.4.1)
Type "help" for help.

havoctec=# select * from urls;
 url_id |           url_name          
--------+------------------------------
      1 | http://www.havoctec.com/
      2 | http://www.sfchildren.com/
      3 | http://sparcki.blogspot.com/
(3 rows)

havoctec=# \q
Automatizar la Copias usando el <cron>
Por último, podemos utilizar nuestro <crontab> para realizar una copia de seguridad completa diariamente, y así, evitar sorpresas de última hora.

El problema que nos podemos encontrar es, principalmente, que el comando <pg_dump> nos solicite una contraseña y no podamos automatizar el proceso.

No hay problema, para ello está el archivo <pgpass> PostgreSQL Password File, en el cual declararemos los usuarios y contraseñas necesarios para el acceso sin password. El formato del archivo es el siguiente:
maquina:puerto:base de datos:usuario:password
Si utilizamos un "*" en el campo de <base de datos> se podrán acceder a todas la bases de datos, por ejemplo, si queremos que el usuario backups pueda acceder a cualquier base de datos, ponemos
$ echo "db1.havoctec.com:*:backups:password" > $HOME/.pgpass
$ chmod 600 $HOME/.pgpass
$ pg_dump -U backups -Z9 -C -f /var/backups/havoctec-db-full.dump.gz havoctec
Ahora PostgreSQL no nos ha solicitado la contraseña, porque la ha encontrado en el archivo, y por lo tanto, la ha introducido por nosotros, así que ya podemos añadir nuestra entrada al cron para que haga copias de seguridad completas todos los días a las 02:00h, por ejemplo,
$ crontab -e
    00 02 * * * /u01/app/postgres/8.4/bin/pg_dump -U backups -Z9 -C -f /var/backups/havoctec-db-full.dump.gz havoctec

:wq


Conclusiones
Hemos visto un primer acercamiento a las opciones en cuanto a Backup y Restore que nos ofrece PostgreSQL, y además hemos definido una política básica de copias utilizando el cron del sistema, para, al menos, tener una copia diaria de nuestra base de datos.

En las siguientes entregas veremos cómo podemos crear una política de Backup y Restore Eficiente y Segura en PostgreSQL



Referencias