La autenticación por token en Db2, introducida en 11.5.4, permite que un cliente se conecte con un token en lugar de usar una contraseña. Esto resulta útil en integraciones en torno a SSO y en flujos de aplicación donde la identidad ya se estableció aguas arriba.
Este artículo es deliberadamente práctico. Cada paso siguiente muestra los archivos exactos que hay que crear, los comandos que hay que ejecutar y las verificaciones que hay que hacer. La lab se pensó para reproducirse en una máquina Linux con una instancia Db2.
Autenticación en Db2
Antes de abordar la autenticación por token, conviene enumerar los aspectos de seguridad importantes para Db2:
- Autenticación valida y establece la identidad
- Autorización determina lo que está permitido hacer bajo una determinada identidad
- Establecimiento de la conexión es el handshake que transporta tanto las credenciales como la política de seguridad
En una conexión Db2 tradicional, el cliente proporciona un nombre de usuario y una contraseña, Db2 los valida y el Authorization ID resultante pasa a ser la identidad de la sesión. La autenticación por token modifica el primer paso: en lugar de una contraseña, el cliente proporciona un token firmado para demostrar su identidad.
Esa diferencia tiene impactos operativos importantes. La aplicación ya no tiene que gestionar la contraseña. En su lugar, Db2 valida un token emitido y firmado en otro sistema y después mapea uno de los claims del token al authid de Db2 usado por la sesión.
La forma en que se validan las autorizaciones permanece inalterada. Db2 sigue aplicando privilegios, roles y authorities de base de datos e instancia a la identidad que quedó establecida.
JWT
Un JSON Web Token (JWT) es una forma compacta de transportar claims sobre una identidad y firmarlos para que el receptor pueda confiar en el contenido.
El JWT tiene tres partes:
- un header, que describe el algoritmo de firma
- un payload, que transporta claims como issuer, subject y expiración
- una signature, que demuestra que el token fue firmado por una entidad de confianza
Para esta lab, los claims importantes son:
iss, que identifica al issuerusername, que mapeamos al authid de Db2iat, que indica cuándo se creó el tokenexp, que indica cuándo deja de ser válido
Db2 solo necesita suficiente estructura del JWT para confiar en el issuer, extraer el claim mapeado y rechazar tokens expirados o malformados. Por eso el resto del artículo usa un JWT firmado simple en lugar de una plataforma completa de establecimiento de identidad.
Lo que construye esta lab
Al final de la lab tendrás:
- un par de claves local para firmar JWTs
- un keystore PKCS#12 con el certificado del issuer para validación en Db2
- un archivo
db2token.cfgque confía en el issuer y mapea un claim al authid de Db2 - Db2 configurado para aceptar conexiones de servidor basadas en token
- una base de datos que acepta una conexión CLP basada en token
- un ejemplo JDBC que usa el mismo token
- una prueba de fallo reproducible para tokens inválidos o expirados
Prerrequisitos
Usa un servidor Db2 en versión 11.5.4 o posterior.
También necesitas:
- acceso shell a la cuenta propietaria de la instancia Db2
opensslgsk8capicmd_64- el CLP de Db2
- soporte de ejecución de Java y el driver JDBC de Db2 si quieres ejecutar el ejemplo Java
1. Crear el par de claves del issuer
El issuer es el sistema que firma el JWT. Para la lab vamos a mantenerlo local para que el flujo sea fácil de reproducir.
Inicia sesión como propietario de la instancia Db2, crea un directorio de trabajo y genera una clave RSA de 3072 bits:
mkdir -p "$HOME/db2-token-lab"
cd "$HOME/db2-token-lab"
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out jwt-issuer.keyCrea un certificado autofirmado a partir de esa clave:
openssl req -new -x509 \
-key jwt-issuer.key \
-out jwt-issuer.crt \
-days 365 \
-subj "/CN=PYXIS-ISSUER/O=Pyxis/L=Lisbon/C=PT"El subject del certificado no es la cadena del issuer del JWT. Es solo la identidad del certificado en la que Db2 va a confiar.
2. Crear el keystore que Db2 va a usar
Db2 necesita un keystore local PKCS#12 con el certificado del issuer.
Crea el keystore:
gsk8capicmd_64 -keydb -create \
-db "$HOME/db2-token-lab/jwtkeys.p12" \
-pw "Db2Token123!" \
-stashImporta el certificado del issuer en ese keystore con una etiqueta que referiremos más adelante:
gsk8capicmd_64 -cert -add \
-db "$HOME/db2-token-lab/jwtkeys.p12" \
-pw "Db2Token123!" \
-label db2jwt \
-file "$HOME/db2-token-lab/jwt-issuer.crt" \
-format asciiVerifica la etiqueta si quieres confirmar que se guardó correctamente:
gsk8capicmd_64 -cert -list \
-db "$HOME/db2-token-lab/jwtkeys.p12" \
-pw "Db2Token123!"Db2 va a usar este keystore para validar la firma del JWT.
3. Crear db2token.cfg
Ahora, crea el archivo de configuración de tokens en el directorio de configuración de la instancia:
cat > "$HOME/sqllib/cfg/db2token.cfg" <<EOF
VERSION=1
TOKEN_TYPES_SUPPORTED=JWT
JWT_KEYDB=$HOME/db2-token-lab/jwtkeys.p12
JWT_IDP_ISSUER=PYXIS-ISSUER
JWT_IDP_AUTHID_CLAIM=username
JWT_IDP_RSA_CERTIFICATE_LABEL=db2jwt
EOF
chmod 600 "$HOME/sqllib/cfg/db2token.cfg"Explicando el contenido:
VERSION=1declara el formato del archivo de configuraciónTOKEN_TYPES_SUPPORTED=JWTle dice a Db2 que los tokens JWT están permitidosJWT_KEYDBapunta al keystore localJWT_IDP_ISSUERdebe coincidir exactamente con el claimissJWT_IDP_AUTHID_CLAIMle dice a Db2 cuál es el claim que contiene el authidJWT_IDP_RSA_CERTIFICATE_LABELidentifica el certificado en el keystore
Verifica el archivo:
cat "$HOME/sqllib/cfg/db2token.cfg"4. Activar la autenticación por token en el servidor
Si la instancia aún no está usando TCP/IP, activa el protocolo ahora configurando la variable DB2COMM del Registry:
db2set DB2COMM=TCPIPDefine el modo de autenticación de conexiones de servidor como cifrado basado en token:
db2 update dbm cfg using srvcon_auth SERVER_ENCRYPT_TOKENReinicia la instancia para que se aplique la nueva configuración:
db2stop force
db2startConfirma el parámetro después del reinicio:
db2 get dbm cfg | egrep -i "svcename|srvcon_auth"Determina el puerto TCP real en el que Db2 está escuchando. Guardaremos el valor en la variable de entorno DB2_PORT:
SVCENAME=$(db2 get dbm cfg | awk -F= '/TCP\/IP Service name/ {gsub(/[[:space:]]/, "", $2); print $2}')
if [[ "$SVCENAME" =~ ^[0-9]+$ ]]; then
DB2_PORT="$SVCENAME"
else
DB2_PORT=$(awk -v svc="$SVCENAME" '$1 == svc && $2 ~ /tcp/ {split($2, a, "/"); print a[1]; exit}' /etc/services)
fi
echo "$DB2_PORT"
ss -ltn | grep "$DB2_PORT"Si DB2_PORT queda vacío, el nombre de servicio obtenido de svcename no está mapeado en /etc/services y JDBC todavía no sabrá qué puerto usar.
5. Crear una base de datos de prueba
Crea una base de datos para la demostración:
db2 create database TOKDBConéctate una vez para confirmar que la base de datos está disponible y otorga connect al usuario DEMO1:
db2 connect to TOKDB
db2 "grant connect on database to user DEMO1"
db2 connect reset
db2 terminate6. Generar un JWT firmado
El token debe contener al menos:
isscorrespondiente al nombre del issuer endb2token.cfg- un claim que mapea al authid
- la hora de emisión
- la hora de expiración
Guarda el siguiente script como make-jwt.sh en el directorio de la lab:
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -lt 4 ]; then
echo "Usage: $0 <private-key> <issuer> <authid> <ttl-seconds>" >&2
exit 1
fi
key_file="$1"
issuer="$2"
authid="$3"
ttl_seconds="$4"
now=$(date +%s)
exp=$((now + ttl_seconds))
header='{"alg":"RS256","typ":"JWT"}'
payload=$(printf '{"username":"%s","sub":"%s","iss":"%s","iat":%s,"exp":%s}' \
"$authid" "$authid" "$issuer" "$now" "$exp")
b64url() {
openssl base64 -A | tr '+/' '-_' | tr -d '='
}
header_b64=$(printf '%s' "$header" | b64url)
payload_b64=$(printf '%s' "$payload" | b64url)
unsigned_token="${header_b64}.${payload_b64}"
signature_b64=$(printf '%s' "$unsigned_token" \
| openssl dgst -sha256 -sign "$key_file" -binary \
| b64url)
printf '%s.%s\n' "$unsigned_token" "$signature_b64"Hazlo ejecutable:
chmod +x make-jwt.shGenera el token para el authid DEMO1 asignándole una validez de una hora:
./make-jwt.sh "$HOME/db2-token-lab/jwt-issuer.key" PYXIS-ISSUER DEMO1 3600 > token.jwtInspecciona el archivo del token:
TOKEN=$(cat token.jwt)
base64 -d <<< $(echo $TOKEN | cut -d'.' -f1 | tr '_-' '/+')
base64 -d <<< $(echo $TOKEN | cut -d'.' -f2 | tr '_-' '/+')El claim username es importante porque db2token.cfg mapea ese claim al authorization ID de Db2.
7. Conectar desde el CLP de Db2
Usa el token en una conexión explícita del CLP:
db2 connect to TOKDB accesstoken "$(cat token.jwt)" accesstokentype jwtSi la conexión tiene éxito, valida la identidad mapeada:
db2 "values current user"Deberías ver el authid mapeado del token en mayúsculas, que en esta demostración es DEMO1.
Termina la conexión y la sesión de CLP:
db2 connect reset
db2 terminate8. Conectar desde JDBC
La demostración JDBC muestra el flujo de token desde una aplicación Java.
Guarda el siguiente código como TokenAuthDemo.java:
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import com.ibm.db2.jcc.DB2BaseDataSource;
import com.ibm.db2.jcc.DB2SimpleDataSource;
public class TokenAuthDemo {
public static void main(String[] args) throws Exception {
if (args.length < 3) {
throw new IllegalArgumentException("Usage: TokenAuthDemo <jwt> <host> <port>");
}
String token = args[0];
String host = args[1];
int port = Integer.parseInt(args[2]);
DB2SimpleDataSource ds = new DB2SimpleDataSource();
ds.setDriverType(4);
ds.setServerName(host);
ds.setPortNumber(port);
ds.setDatabaseName("TOKDB");
ds.setLoginTimeout(15);
ds.setSecurityMechanism(DB2BaseDataSource.TOKEN_SECURITY);
ds.setAccessToken(token);
ds.setAccessTokenType("JWT");
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("VALUES CURRENT USER")) {
while (rs.next()) {
System.out.println("CURRENT USER = " + rs.getString(1));
}
}
}
}Compílalo con el driver JDBC de Db2 en el classpath:
javac -cp "$HOME/sqllib/java/db2jcc4.jar" TokenAuthDemo.javaSi ves errores como package com.ibm.db2.jcc does not exist, el classpath no está apuntando al archivo jar real del driver JDBC de Db2. En una instalación típica de Db2 en el servidor, db2jcc4.jar está disponible en $HOME/sqllib/java/.
Ejecútalo con el mismo token:
java -cp ".:$HOME/sqllib/java/db2jcc4.jar" TokenAuthDemo "$(cat token.jwt)" localhost "$DB2_PORT"Si la configuración es correcta, el programa mostrará el usuario mapeado por el token.
El ejemplo define un timeout de login de 15 segundos para fallar en vez de quedarse colgado indefinidamente.
Si obtienes Connection refused, Db2 no está escuchando en el host/puerto usados por JDBC. Revisa el valor de svcename, confirma que la instancia se reinició, resuelve el puerto real y valida con ss -ltn que el listener está realmente activo.
9. Prueba con varios escenarios de fallo
Probamos ahora con varios escenarios de fallo.
Issuer incorrecto
Crea un token con una cadena de issuer distinta:
./make-jwt.sh "$HOME/db2-token-lab/jwt-issuer.key" BAD-ISSUER DEMO1 3600 > bad-issuer.jwt
db2 connect to TOKDB accesstoken "$(cat bad-issuer.jwt)" accesstokentype jwtLa prueba debe fallar porque BAD-ISSUER no coincide con JWT_IDP_ISSUER.
Token expirado
Crea un token con un tiempo de vida negativo:
./make-jwt.sh "$HOME/db2-token-lab/jwt-issuer.key" PYXIS-ISSUER DEMO1 -60 > expired.jwt
db2 connect to TOKDB accesstoken "$(cat expired.jwt)" accesstokentype jwtLa prueba debe fallar porque el token ya expiró.
Claim de authid incorrecto
Crea un token para otro usuario e intenta conectarte a la base de datos:
./make-jwt.sh "$HOME/db2-token-lab/jwt-issuer.key" PYXIS-ISSUER OTHERUSER 3600 > wrong-user.jwt
db2 connect to TOKDB accesstoken "$(cat wrong-user.jwt)" accesstokentype jwtLa conexión falla. Solo puede tener éxito si ese authid es aceptable en tu entorno. En caso contrario, esta es una forma clara de mostrar que el mapeo de claim a authid importa.
Resolución de problemas
Si la conexión no funciona, verifica primero si:
- el
db2token.cfgestá en el directorio de configuración de la instancia - el claim
isscoincide conJWT_IDP_ISSUER - el token está firmado con la misma clave que el certificado confiable
- el token aún no ha expirado
- Db2 está escuchando en el puerto TCP/IP esperado
srvcon_authestá definido comoSERVER_ENCRYPT_TOKEN- la instancia se reinició después de los cambios
Si JDBC falla pero CLP funciona, verifica:
- la versión del driver
- la configuración
TOKEN_SECURITY - el valor
accessTokenType
Conclusiones clave
- La autenticación por token en Db2 está disponible a partir de la 11.5.4.
- El servidor usa
db2token.cfgpara confiar en el issuer y mapear claims a authids. - CLP y JDBC pueden autenticarse con un JWT.
Documentación útil de IBM
Para profundizar, las referencias de IBM más útiles para este tema son: