Configuración SSL en Apache Tomcat: Implementación Directa y con Proxy Nginx

Al interactuar con servicios que exponen APIs mediante HTTPS, es común encontrarse con errores de conectividad que señalan problemas con la negociación del protocolo SSL/TLS. Un error recurrente es javax.net.ssl.SSLException: Received fatal alert: protocol_version. Este mensaje indica una incompatibilidad en las versiones de TLS soportadas entre el cliente que realiza la llamada y el servidor que aloja el servicio HTTPS. Para asegurar la máxima compatibilidad y seguridad, el servidor debe estar configurado para admitir un rango adecuado de versiones de TLS.

Para diagnosticar las versiones de TLS y los conjuntos de cifrado que un servidor web soporta, se puede utilizar la herramienta Nmap con el script ssl-enum-ciphers. Este comando ofrece una visión detallada de las capacidades SSL/TLS del servidor:

nmap --script ssl-enum-ciphers -p 443 mi-dominio.com

Gestión de Peticiones de Usuario Detrás de un Proxy Inverso con Tomcat

Cuando Tomcat opera detrás de un proxy inverso como Nginx, es fundamental asegurar que reciba la información correcta sobre la petición original del cliente. Sin una configuración adecuada, Tomcat interpreta todas las peticiones como provenientes directamente de Nginx, lo que puede llevar a resultados erróneos en la aplicación:

  • request.getScheme(): Siempre devolverá "http", incluso si la petición original era HTTPS.
  • request.isSecure(): Siempre será false.
  • request.getRemoteAddr(): Mostrará la dirección IP de Nginx, no la del cliente final.
  • request.getRequestURL(): Reflejará la URL de la petición a Nginx, no la URL real solicitada por el usuario.
  • response.sendRedirect(rel_url): Las redirecciones relativas se construirán usando "http", lo que puede causar fallos de seguridad o bucles de redirección.

Para rectificar esto sin modificar el código de la aplicación, se deben configurar los encabezados de Nginx y el RemoteIpValve de Tomcat.

Configuración de Encabezados en Nginx

En el bloque location de Nginx que reenvía peticiones a Tomcat, se deben añadir los siguientes encabezados para pasar la información del cliente original:

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Configuración de RemoteIpValve en Tomcat

Dentro del elemetno <Engine> en el archivo server.xml de Tomcat, se añade la siguiente válvula. Esta válvula procesa los encabezados X-Forwarded-* enviados por Nginx:

<Valve className="org.apache.catalina.valves.RemoteIpValve"
       remoteIpHeader="X-Forwarded-For"
       protocolHeader="X-Forwarded-Proto"
       protocolHeaderHttpsValue="https"/>

Con esta configuración, Tomcat podrá identificar correctamente el esquema (HTTP/HTTPS) de la petición original, la dirección IP del cliente y las URLs solicitadas, como si el cliente accediera directamente al servidor.

Preparación de Certificados SSL para Tomcat: Conversión de PFX a JKS

Tomcat requiere que los certificados SSL estén en formato Java KeyStore (JKS) o PKCS12. Si sus certificados están en formato PEM (archivos .crt y .key), primero deberá convertirlos a PKCS12 y luego a JKS.

Paso 1: Convertir Certifiacdo y Clave a PFX con OpenSSL

Utilice OpenSSL para combinar su certificado de servidor (.crt), clave privada (.key) y, si aplica, la cadena de certificados intermedia (.crt) en un único archivo PFX. Se le pedirá una contraseña para proteger el archivo PFX.

openssl pkcs12 -export -in certificado_servidor.crt -inkey clave_privada.key -out mi_certificado.pfx -certfile cadena_certificados.crt -name "alias_del_certificado"

Ejemplo con una contraseña:

openssl pkcs12 -export -in servidor.crt -inkey servidor.key -out servidor.pfx -certfile DigiCertCA.crt -name "server_alias"
# Establecer contraseña: MiContrasenaSegura

Paso 2: Convertir PFX a JKS con Keytool

Una vez que tenga el archivo PFX, use la utilidad keytool de Java para importarlo a un nuevo almacén JKS. Se le solicitará la contraseña del archivo PFX y luego una nueva contraseña para el almacén JKS de destino.

keytool -importkeystore -srckeystore mi_certificado.pfx -srcstoretype pkcs12 -srcstorepass "MiContrasenaSegura" -destkeystore almacen_ssl.jks -deststoretype JKS -deststorepass "OtraContrasenaMasSegura"

Ejemplo con las contraseñas utilizadas antes:

keytool -importkeystore -v -srckeystore servidor.pfx -srcstoretype pkcs12 -srcstorepass "MiContrasenaSegura" -destkeystore servidor.jks -deststoretype jks -deststorepass "OtraContrasenaMasSegura"

Configuración Directa de SSL en Apache Tomcat

Para habilitar SSL directamente en Tomcat, debe modificar el archivo server.xml, que se encuentra en el directorio conf de su instalación de Tomcat. Localice el conector HTTP/1.1 para SSL, que a menudo está comentado.

Busque una sección similar a esta (puede estar comentada):

<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
           maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
           clientAuth="false" sslProtocol="TLS"/>
-->

Descomente esta sección y modifíquela para incluir los detalles de su almacén de claves JKS, la contraseña y las configuraciones de protocolo SSL/TLS y cifrado recomendadas. Asegúrese de especificar un protocolo moderno y suites de cifrado seguras.

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="200" SSLEnabled="true" scheme="https" secure="true"
           clientAuth="false"
           keystoreFile="/opt/tomcat/certs/almacen_ssl.jks" keystorePass="OtraContrasenaMasSegura"
           sslEnabledProtocols="TLSv1.2,TLSv1.3"
           ciphers="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"/>

Nota: Es crucial eliminar los comentarios (\<!-- y -->) para que la configuración del conector sea activa. Los atributos clave son:

  • port: El puerto HTTPS (comúnmente 443 o 8443).
  • protocol: Se recomienda Http11NioProtocol para un mejor rendimiento.
  • SSLEnabled: Debe ser true para activar SSL.
  • keystoreFile: Ruta absoluta a su archivo JKS.
  • keystorePass: La contraseña del archivo JKS.
  • sslEnabledProtocols: Especifica las versiones de TLS que se permitirán (por ejemplo, TLSv1.2,TLSv1.3).
  • ciphers: Define las suites de cifrado permitidas, proirizando las más seguras.

Después de guardar los cambios y reiniciar el servicio de Tomcat, debería poder acceder a su aplicación a través de HTTPS.

Configuración de Redirección Automática de HTTP a HTTPS en Tomcat

Para forzar que todas las peticiones HTTP se redirijan automáticamente a HTTPS en Tomcat, se necesitan dos pasos principales:

1. Modificar web.xml para Garantizar Confidencialidad

Edite el archivo web.xml (ubicado en conf/web.xml) y añada el siguiente fragmento para indicar que ciertas rutas requieren confidencialidad en el transporte. Normalmente, esto se coloca después de </welcome-file-list> o en un lugar apropiado dentro del <web-app>:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Área Segura</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

Este fragmento le dice a Tomcat que todas las URLs (/*) requieren un transporte confidencial (HTTPS).

2. Configurar el Conector HTTP en server.xml

En el archivo server.xml, localice el conector HTTP (generalmente en el puerto 8080) y añada o modifique el atributo redirectPort para que apunte al puerto HTTPS configurado:

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" /> <!-- O 443 si es el puerto HTTPS final -->

Ahora, cualquier petición entrante al puerto HTTP (por ejemplo, 8080) que caiga bajo la restricción de seguridad definida en web.xml será automáticamente redirigida al puerto HTTPS especificado (8443 en este caso).

Configuración de SSL con Nginx como Terminador de SSL para Tomcat

Un método común y recomendado es utilizar Nginx como proxy inverso para manejar las conexiones SSL/TLS, reenviando las peticiones a Tomcat a través de HTTP estándar. Esto permite que Tomcat se enfoque en servir el contenido de la aplicación, mientras Nginx se encarga de la seguridad del tráfico, el balanceo de carga y la entrega de archivos estáticos. En este escenario, Tomcat no necesita configurar SSL directamente.

Ejemplo de Configuración Nginx

A continuación, se presenta un ejemplo de configuración para Nginx. Este ejemplo incluye un bloque upstream para el balanceo de carga hacia múltiples instancias de Tomcat, una redirección de HTTP a HTTPS y la configuración SSL para el puerto 443, incluyendo un manejo de archivos estáticos.

upstream backend_tomcat_cluster {
    server 192.168.1.10:8080 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 weight=1 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;
    server_name api.miaplicacion.com;
    return 301 https://$host$request_uri; # Redirección HTTP a HTTPS
}

server {
    listen 443 ssl http2;
    server_name api.miaplicacion.com;

    access_log /var/log/nginx/api.miaplicacion.com_access.log;
    error_log  /var/log/nginx/api.miaplicacion.com_error.log;

    ssl_certificate      /etc/nginx/ssl/api.miaplicacion.com.crt;
    ssl_certificate_key  /etc/nginx/ssl/api.miaplicacion.com.key;
    ssl_session_cache    shared:SSL:10m;
    ssl_session_timeout  10m;
    ssl_protocols        TLSv1.2 TLSv1.3;
    ssl_ciphers          "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://backend_tomcat_cluster;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
        proxy_buffering off;
        proxy_read_timeout 90s;
    }

    # Caché y servicio directo de archivos estáticos
    location ~* \.(css|js|gif|jpg|jpeg|png|webp|ico|svg|eot|ttf|woff|woff2)$ {
        root /var/www/api.miaplicacion.com/static;
        expires 30d;
        add_header Cache-Control "public, no-transform";
        try_files $uri =404;
    }
}

En este ejemplo:

  • El bloque upstream define un grupo de servidores Tomcat a los que Nginx puede reenviar las peticiones.
  • Un bloque server para el puerto 80 se encarga de redirigir todo el tráfico HTTP a HTTPS (puerto 443).
  • El bloque server para el puerto 443 configura SSL/TLS utilizando los certificados especificados y define las versiones de protocolo y cifrados seguros.
  • Las directivas proxy_set_header son cruciales para reenviar la información correcta del cliente a Tomcat.
  • Las reglas location ~* \.(...) permiten a Nginx servir archivos estáticos directamente con encabezados de caché optimizados, descargando esta tarea de Tomcat.

Etiquetas: tomcat SSL TLS Nginx certificados

Publicado el 10-26 14:01