Certificados SSL en Tomcat

Lidiando recientemente con las solicitudes del área de desarrollo, hubo que actualizar un certificado vencido en uno de sus maravillosos portales desarrollados en Java y servidos con Tomcat. La experiencia vivida con eso fue terrible, pues no tienen ni la más mínima idea de como hacer un sitio seguro. Solo se cercioran de descomentar los items relacionados con SSL en el archivo server.xml y colocar allí un archivo de certificados que tampoco saben como está hecho, ni por mera curiosidad relacionada a la herramienta de desarrollo que utilizan.

Por otro lado, no había documentación oficial en la organización de como se hizo el proceso, ni donde guardaron los archivos de solicitud de firma y la clave privada (CSR y Key file), mucho menos el equipo estrella de desarrollo.

Aunado a esto, el certificado firmado del dominio fue proporcionado por Godaddy, cuyos certificados no son reconocidos por Java (al menos no como lo dice la documentación de Tomcat) y de paso, el procedimiento explicado por ellos para colocarlos en servidores Tomcat NO FUNCIONA.

La raíz del problema

Las aplicaciones web desarrolladas en Java requieren un almacén de certificados para este tipo de lenguaje (Java Keystore). Este contenedor puede ser creado con Keytool, una herramienta similar a OpenSSL pero es propia de Java, bastante práctica pero con ciertas limitaciones. Con Keytool se pueden hacer:

  • El contenedor de claves (JKS)
  • La solicitud de firma de Certificado (archivo CSR)

El procedimiento para hacer un Keystore y la solicitud de firma es bastante simple:

1.- Crear el archivo de llaves:

keytool -genkey -alias midominio -keyalg RSA -keystore keystore.jks -keysize 2048

2.- Generar el archivo de solicitud de firma (CSR) basado en el nuevo keystore:

keytool -certreq -alias midominio -keystore keystore.jks -file midominio.csr

3.- El proceso siguiente es el que corresponde a responder las preguntas del certificado (que omitiré por mera practicidad), que al final debe confirmar afirmativamente para crear el archivo.

Ya con el archivo CSR creado, se hace la solicitud de firma ante cualquier Autoridad de Certificación (GlobalSign, Verisign, Norton, Godaddy). Una vez hecho eso, la empresa que firma le entregará el certificado firmado (CRT) junto a dos archivos: el Certificado Raíz (root) y el Certificado Intermedio, ambos necesarios, sin eso no es posible validar la autenticidad del certificado ya firmado.

4.- Importar los certificados obtenidos de manera ordenada dentro del almacén de claves (Keystore)

keytool -import -trustcacerts -alias root -file root.crt -keystore keystore.jks
keytool -import -trustcacerts -alias intermediate -file intermediate.crt -keystore keystore.jks
keytool -import -trustcacerts -alias midominio -file midominio.crt -keystore keystore.jks

(Importante: desde el proceso de creación del almacén se requiere una contraseña que no debe olvidarse, ojo)

Y voilá! Tiene un archivo keystore listo para ser usado en sus servidores web con Java.

Se verifica el contenido del almacen de certificados, y el Keytool deberá mostrar algo como esto:

keystore -list -keystore keystore.jks -storepass password

Tipo de Almacén de Claves: JKS
Proveedor de Almacén de Claves: SUN

Su almacén de claves contiene 3 entradas

root, 17/02/2016, trustedCertEntry,
Huella Digital de Certificado (SHA1): 27:AC:93:69:FA:F2:52:07:BB:26:27:CE:FA:CC:BE:4E:F9:C3:19:B8
midominio, 20/01/2017, trustedCertEntry,
Huella Digital de Certificado (SHA1): 35:A3:AB:6A:EA:11:4F:E4:1C:8D:4C:51:F4:82:3F:4E:31:B5:97:CA
intermediate, 17/02/2016, trustedCertEntry,
Huella Digital de Certificado (SHA1): 27:AC:93:69:FA:F2:52:07:BB:26:27:CE:FA:CC:BE:4E:F9:C3:19:B8

Hasta ahora parece sencillo, termina configurando su servidor Tomcat agregando en el archivo de configuración server.xml lo relacionado a la conexión por HTTPS:

<Connector port=»8443″ maxThreads=»200″ scheme=»https» secure=»true» SSLEnabled=»true»
keystoreFile=»/la/ruta/al/archivo/keystore.jks»
keystorePass=»contraseñadelarchivokeystore» clientAuth=»false» sslProtocol=»TLS»/>

¿Y donde está el problema?

Todo esto funciona muy bien cuando la CA que firma los certificados es cualquiera, menos Godaddy. Mi compadre y colega @eu53 en su amplia experiencia me explica que estos certificados por Godaddy son emitidos para servidores web como Apache2 (httpd), y servidores como Tomcat les da un tratamiento como tales. No son certificados que una app web hecha en Java reconocerá de manera inmediata.

La solución consiste en convertir los certificados en un formato que sea reconocido por las apps java (desde Tomcat 6 en adelante), como PKCS12.

Para esto se requiere en el mejor de los casos:

  • El certificado firmado por la Autoridad Certificadora (el archivo midominio.crt)
  • El certificado Raíz de la Autoridad Certificadora (root.crt)
  • El certificado Intermedio de la CA (supuestamente se requiere para Godaddy)
  • El archivo de clave privada con el que se generó la solicitud de firma originalmente (archivo .key)

Pero, keytool NO genera un archivo de clave privada (archivo.key) con el que originalmente se puede generar el archivo CSR (midominio.csr). Volvemos al problema: y entonces cómo?

La Solución: hacer las cosas bien desde el principio

Mi recomendación es hacer el proceso de generación de claves con OpenSSL.

1.- Crear los certificados de clave privada y requerimiento de firma:

openssl req -new -newkey rsa:2048 -nodes -out midominio.csr -keyout midominio.ke

(se responden las preguntas de la organización de origen, y se coloca la contraseña de la clave privada, que no debe olvidarse)

El resultado del proceso son los archivos midominio.key y midominio.csr

2.- Ya con el archivo CSR creado, se hace la solicitud de firma ante cualquier Autoridad de Certificación asi como se explicara en este documento. Para el caso en particular, Godaddy.

Una vez hecho eso, la empresa que firma le entregará el certificado firmado (CRT) junto a dos archivos: el Certificado Raíz (root) y el Certificado Intermedio empaquetados en un archivo ZIP que se descarga desde el portal de Godaddy.

3.- Crear el almacen de certificados (Keystore) con Keytool:

keytool -genkey -alias midominio -keyalg RSA -keystore tomcat.jks

(en este paso, responda las preguntas con los mismos datos proporcionados al ejecutar el paso 1 con openssl)

4.- Exportar el certificado firmado y la clave privada al formato PKCS12

openssl pkcs12 -export -in midominio.crt -inkey midominio.key -out midominio.p12 -name midominio -CAfile ca.crt -caname root

5.- Finalmente, importar el archivo PKCS12 en el almacen de certificados usando Keytool:

keytool -importkeystore -deststorepass <elpassworddelarchivoJKS> -destkeypass <elpassworddelarchivoKey> -destkeystore tomcat.jks -srckeystore midominio.p12 -srcstoretype PKCS12 -srcstorepass <elpassworddelarchivoJKS> -alias midominio

(para propósitos prácticos, use el mismo password utilizado en el archivo key para su keystore)

El contenido del almacen de certificados se verá de esta forma:

Tipo de Almacén de Claves: JKS
Proveedor de Almacén de Claves: SUN

Su almacén de claves contiene 1 entrada

midominio, 17/02/2016, PrivateKeyEntry,
Huella Digital de Certificado (SHA1): B3:6B:2B:D6:00:43:4E:B4:BF:03:53:F2:DE:FE:FE:2C:F2:F9:5D:53

Ahora si, el certificado se encuentra listo para ser usado en el servidor Tomcat, para es se modifica el archivo server.xml:

<Connector port=»8443″ maxThreads=»200″ scheme=»https» secure=»true» SSLEnabled=»true» keystoreFile=»/la/ruta/del/archivo/tomcat.jks»
keystorePass=»elpassworddelarchivoJKS» clientAuth=»false» sslProtocol=»TLS»/>

Para mantener un orden debido en las configuraciones de un servidor, ubique el archivo de certificados en un lugar especifico. Se puede crear un directorio adicional en /usr/local para guardar los certificados agregados.

Un problema (des)conocido

El equipo de desarrollo no está feliz: a pesar que ya el portal tiene un certificado que funciona en su sitio, en la ejecución de tan maravillosa obra de arte desplegado en el servidor muestra un error luego de autenticar y hacer llamadas a unos subprogramas dentro del mismo servidor:

2017-01-26 16:56:06,274 [ CommonUtils.java:http-bio-443-exec-3:getResponseFromServer:327] – sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Alegato del desarrollador: el certificado no sirve.

Luego de consultar foros y nuevamente acudir a la sabia experiencia de mi compadre @eu53 en materia de Tomcat, me explica lo siguiente:

«Básicamente el problema que tienes es confianza con el certificado raíz que firmó tu certificado público, posiblemente sea que el certificado de la entidad verificadora haya sido recientemente actualizado y no esté esa versión incluida en el cacert de tu version de Java. La solución es agregar el certificado al cacert y asi cualquier app java o servidor que levantes usando ese JRE tenga ya el certificado entre su truststore, pero de todas todas el certificado no esta malo, simplemente hay que actualizar el listado de certificados».

Y en efecto, lo recomendado es actualizar el certificado raíz de la CA con que se firmó el certificado del sitio en el almacén de claves de Java, en este caso de la versión que estará usando Tomcat. Para eso entonces nuevamente se hace uso de keytool:

keytool -importcert -file gd_bundle-g2-g1.crt -alias godaddy -keystore $JAVA_HOME/jre/lib/security/cacerts

(el password del almacén es «changeit»)

Problema resuelto.

Todo lo mencionado aquí funciona con las versiones de Tomcat 6, 7 y 8.

 

Referencias:
https://support.globalsign.com/customer/en/portal/articles/2121490-java-keytool—create-keystore
https://picodotdev.github.io/blog-bitix/2014/02/generar-y-convertir-claves-y-certificados-con-openssl/
http://robblake.net/post/18945733710/using-a-pem-private-key-and-ssl-certificate-with
https://raiolanetworks.es/blog/instalar-tomcat-e-importar-certificado-ssl-existente/

Configuración de Cobbler en CentOS 7

Mis actividades recientes consisten en la preparación de un laboratorio de computación para las pruebas que realizan las distintas unidades que tiene la empresa.  Algunos servicios requieren ser migrados pero otros ya hemos decidido empezar de cero.  Uno de los servicios necesarios es el de Aprovisionamiento para la instalación de sistemas operativos a través de la red.  Aqui la opción ha sido desde siempre utilizar una herramienta llamada Cobbler.

¿Qué es Cobbler?

La mejor explicación es la que se define en la Wikipedia*:

Cobbler es un servidor del aprovisionamiento Linux que centraliza y simplifica el control de servicios incluyendo DHCP, TFTP, y DNS con propósito de realizar instalaciones basadas en red de sistemas operativos.  Puede ser configurado para PXE, reinstalaciones, y huéspedes virtualizados usando Xen, KVM o VMware. Cobbler interactúa con un programa llamado Koan para el soporte de la reinstalación y la virtualización. Koan y Cobbler usan libvirt para integrarse con diferentes softwares de virtualización.

Cobbler está hecho sobre el mecanismo de Kickstart y ofrece perfiles de instalación que pueden ser aplicados a una o muchas máquinas. También ofrece la integración con Yum para ayudar en instalaciones de máquinas.

Aunque Cobbler está enfocado en instalaciones basadas en RPM vía Kickstart y el Anaconda, puede ser usado para configurar un servidor PXE para cargar varias imágenes tales como Knoppix y otros sabores de Debian.

Se decidió hacer la instalación en el servidor con CentOS 7 donde previamente configuramos los repositorios CentOS y Debian, así que ya algunos servicios fueron previamente configurados.

Requerimientos previos para la instalación y configuración:

  • Servidor con CentOS 7
  • Servicio dhcpd instalado
  • Servicio httpd instalado

Pasos a seguir

1.- deshabilitar SELINUX
2.- bajar iptables
3.- instalar repositorio EPEL (el link de descarga puede estar actualizado, se requiere verificar primero si el archivo existe)

[root@cobbler ~]# rpm -Uvh https://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm

Retrieving https://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch&#8230;
warning: /var/tmp/rpm-tmp.Cisj7E: Header V3 RSA/SHA256 Signature, key ID 352c64e5: NOKEY
Preparing… ################################# [100%]
Updating / installing…
1:epel-release-7-5 ################################# [100%]

4.- Instalar Kickstart, cobbler y dependencias:

[root@cobbler ~]# yum -y install cobbler cobbler-web pykickstart dhcp tftp xinetd

5.- habilitar tftp y rsync en xinetd, luego reiniciar xinetd y habilitarlo como inicio por defecto en el sistema:

[root@cobbler ~]# systemctl enable xinetd

6.- iniciar los servicios de cobbler

[root@cobbler ~]# systemctl enable cobblerd
ln -s ‘/usr/lib/systemd/system/cobblerd.service’ ‘/etc/systemd/system/multi-user.target.wants/cobblerd.service’
[root@cobbler ~]# systemctl start cobblerd

7.- Instalar cobbler loaders

[root@cobbler ~]# cobbler get-loaders

El resultado será algo similar a esto:

[root@mirror ~]# cobbler get-loaders
task started: 2016-01-27_142230_get_loaders
task started (id=Download Bootloader Content, time=Wed Jan 27 14:22:30 2016)
downloading http://cobbler.github.com/loaders/README to /var/lib/cobbler/loaders/README
downloading http://cobbler.github.com/loaders/COPYING.elilo to /var/lib/cobbler/loaders/COPYING.elilo
downloading http://cobbler.github.com/loaders/COPYING.yaboot to /var/lib/cobbler/loaders/COPYING.yaboot
downloading http://cobbler.github.com/loaders/COPYING.syslinux to /var/lib/cobbler/loaders/COPYING.syslinux
downloading http://cobbler.github.com/loaders/elilo-3.8-ia64.efi to /var/lib/cobbler/loaders/elilo-ia64.efi
downloading http://cobbler.github.com/loaders/yaboot-1.3.17 to /var/lib/cobbler/loaders/yaboot
downloading http://cobbler.github.com/loaders/pxelinux.0-3.86 to /var/lib/cobbler/loaders/pxelinux.0
downloading http://cobbler.github.com/loaders/menu.c32-3.86 to /var/lib/cobbler/loaders/menu.c32
downloading http://cobbler.github.com/loaders/grub-0.97-x86.efi to /var/lib/cobbler/loaders/grub-x86.efi
downloading http://cobbler.github.com/loaders/grub-0.97-x86_64.efi to /var/lib/cobbler/loaders/grub-x86_64.efi
* TASK COMPLETE *

8.- generar el hash del password que utilizará cobbler, el hash obtenido será definido en el archivo de configuración del servicio cobbler:

[root@cobbler ~]# openssl passwd -1 -salt ‘laboratorio.gs’ ‘Abcd1234’ | la salida es esta → $1$laborato$wzEZkT4MdOYVxA6L0UMKp1

9.- Configurar cobbler implica la modificación del archivo de configuración, que se encuentra en /etc/cobbler/settings. Este archivo tiene formato de datos YAML. las opciones a modificar en el archivo son las siguientes:

next_server: colocar la dirección IP del servidor siendo configurado
server: igual que en el item anterior
default_password_crypted: <aqui se coloca el hash generado con anterioridad>
manage_dhcp: cambiar de valor 0 y colocar 1
pxe_just_once: cambiar de valor 0 a 1

10.- Teniendo instalado previamente el servicio dhcp, al modificar el parámetro manage_dhcp en 1 le estamos indicando al servicio que la plantilla de configuración dhcpd.conf será la que le suministre cobbler. Esto sucederá cuando posteriormente se ejecute el comando cobbler sync. El contenido de la plantilla del archivo dhcpd.conf será por este estilo, y debe modificarse para indicarle las subredes que serán atendidas:

/etc/cobbler/dhcp.template

subnet 192.168.1.0 netmask 255.255.255.0 {

option routers 192.168.1.5;
option domain-name-servers 192.168.1.1;
option subnet-mask 255.255.255.0;
range dynamic-bootp 192.168.0.4 192.168.0.25;
default-lease-time 21600;
max-lease-time 43200;
next-server $next_server;
class «pxeclients» {
match if substring (option vendor-class-identifier, 0, 9) = «PXEClient»;
if option pxe-system-type = 00:02 {
filename «ia64/elilo.efi»;
} else if option pxe-system-type = 00:06 {
filename «grub/grub-x86.efi»;
} else if option pxe-system-type = 00:07 {
filename «grub/grub-x86_64.efi»;
} else {
filename «pxelinux.0»;
}
}

}

11.- Ejecutar cobbler sync. Teniendo en cuenta que todo está configurado debidamente, esta sería la salida esperada:

[root@mirror ~]# cobbler sync
task started: 2016-01-27_145553_sync
task started (id=Sync, time=Wed Jan 27 14:55:53 2016)
running pre-sync triggers
cleaning trees
removing: /var/lib/tftpboot/pxelinux.cfg/default
removing: /var/lib/tftpboot/grub/grub-x86.efi
removing: /var/lib/tftpboot/grub/images
removing: /var/lib/tftpboot/grub/grub-x86_64.efi
removing: /var/lib/tftpboot/grub/efidefault
removing: /var/lib/tftpboot/s390x/profile_list
copying bootloaders
trying hardlink /var/lib/cobbler/loaders/grub-x86.efi -> /var/lib/tftpboot/grub/grub-x86.efi
trying hardlink /var/lib/cobbler/loaders/grub-x86_64.efi -> /var/lib/tftpboot/grub/grub-x86_64.efi
copying distros to tftpboot
copying images
generating PXE configuration files
generating PXE menu structure
rendering DHCP files
generating /etc/dhcp/dhcpd.conf
rendering TFTPD files
generating /etc/xinetd.d/tftp
cleaning link caches
running post-sync triggers
running python triggers from /var/lib/cobbler/triggers/sync/post/*
running python trigger cobbler.modules.sync_post_restart_services
running: dhcpd -t -q
received on stdout:
received on stderr:
running: service dhcpd restart
received on stdout:
received on stderr: Redirecting to /bin/systemctl restart dhcpd.service

running shell triggers from /var/lib/cobbler/triggers/sync/post/*
running python triggers from /var/lib/cobbler/triggers/change/*
running python trigger cobbler.modules.scm_track
running shell triggers from /var/lib/cobbler/triggers/change/*
* TASK COMPLETE *

Con esto ya se tiene configurado el servicio cobbler. Para acceder a la interfaz web se coloca la url https://<direccionip>/cobbler_web/

Previo, con el comando cobbler check se puede verificar la lista de cosas que deben ser resueltas a fin de tener el servicio sin problemas. La salida puede mostrar algo como esto:

[root@mirror ~]# cobbler check
The following are potential configuration items that you may want to fix:

1 : reposync is not installed, need for cobbler reposync, install/upgrade yum-utils?
2 : fencing tools were not found, and are required to use the (optional) power management features. install cman or fence-agents to use them

Restart cobblerd and then run ‘cobbler sync’ to apply changes.

En el caso de este servidor, el resultado del chequeo indicó por ejemplo que reposync no se encuentra en el servidor (aunque si se encuentre instalado yum-utils). Esta herramienta sirve para sincronizar los mirrors de las distribuciones de las que cobbler hará uso si fuese necesario. Es una opción, no es obligatorio el uso de reposync.

Con esto ya se tiene entonces instalado y listo el servidor Cobbler en CentOS 7.