Exploring the roots and future of Internet Relay Chat

TLS en tu servidor IRC con Let's Encrypt

En 2006, un administrador de una red IRC mediana me enseno los logs de lo que un sniffer habia capturado en el enlace entre dos de sus servidores. Contrasenas de NickServ en texto plano. Mensajes privados. Comandos de oper con credenciales de administracion. Todo legible. Le pregunte por que no usaba SSL. Me dijo que era complicado y que nadie se quejaba. Tres semanas despues, alguien uso exactamente esa informacion para tomar el control de su red. No me dio la razon. No hizo falta.

Por que TLS en IRC no es opcional

IRC transmite todo en texto plano por defecto. Cada PRIVMSG, cada PASS, cada comando de oper viaja como texto legible por cualquiera que tenga acceso al trafico de red. En los 90, esto era aceptable porque Internet era mas pequena y la mentalidad era diferente. Hoy es negligencia.

Los escenarios de riesgo son concretos. Un usuario en una red WiFi publica — aeropuerto, cafeteria, universidad — que se conecta a tu servidor IRC sin TLS esta exponiendo su contrasena de NickServ a cualquiera que este sniffeando esa red. Un enlace server-to-server sin cifrar expone todo el trafico interno de la red, incluyendo credenciales de operadores. Un ISP o gobierno que inspecciona trafico puede leer todas las conversaciones.

La buena noticia: implementar TLS en un servidor IRC moderno es mucho mas facil de lo que era hace diez anos. Let's Encrypt elimino el coste economico. Certbot elimino la complejidad de gestion. Los IRCds modernos tienen soporte nativo. No hay excusa para no hacerlo.

TLS vs SSL: aclaracion rapida

SSL (Secure Sockets Layer) es el nombre viejo. TLS (Transport Layer Security) es el nombre actual. SSL 3.0 fue el ultimo SSL y tiene vulnerabilidades conocidas que lo hacen inutilizable. TLS 1.0 y 1.1 tambien estan deprecados. Lo que debes usar en 2025 es TLS 1.2 o TLS 1.3. Nada mas.

En el mundo IRC, la gente sigue diciendo "SSL" por inercia. Los puertos se llaman "SSL ports". Las opciones de configuracion dicen "ssl=true". Pero lo que se negocia por debajo es TLS. Cuando leas "SSL" en contexto de IRC, lee "TLS" mentalmente.

El puerto estandar para IRC con TLS es 6697, igual que 6667 es el estandar sin cifrar. Algunos servidores tambien soportan STARTTLS en el puerto 6667 — el cliente se conecta sin cifrar y luego negocia el upgrade a TLS. Pero la practica recomendada es usar directamente el puerto 6697 con TLS implicito, no STARTTLS. La razon: STARTTLS es vulnerable a ataques de downgrade donde un MITM elimina la negociacion y la conexion queda en plano sin que el usuario lo note.

Let's Encrypt: lo basico

Let's Encrypt es una autoridad certificadora gratuita y automatizada. Emite certificados de dominio validados (DV) — verifica que controlas el dominio, te da un certificado, y lo renueva automaticamente cada 60-90 dias. No cobra. No requiere interaccion humana despues de la configuracion inicial.

Para IRC, un certificado Let's Encrypt funciona perfectamente. No necesitas un certificado EV (Extended Validation) ni nada mas caro. El objetivo es cifrar la conexion, y un certificado DV hace exactamente eso. La unica limitacion es que Let's Encrypt no emite certificados wildcard facilmente — necesitas validacion DNS para eso — pero para un servidor IRC normalmente solo necesitas un certificado para un dominio (irc.tudominio.com).

Consejo: Si tu servidor IRC usa un subdominio (irc.tudominio.com), asegurate de que el registro DNS apunta a la IP del servidor antes de ejecutar Certbot. Let's Encrypt valida el dominio conectandose a esa IP.

Obtener el certificado con Certbot

# Instalar Certbot (Debian/Ubuntu):
sudo apt install certbot

# Obtener certificado standalone (sin web server):
# Certbot levanta su propio servidor HTTP temporal en el puerto 80
sudo certbot certonly --standalone -d irc.tudominio.com

# Si ya tienes un web server en el puerto 80:
# Opcion A: usar el plugin de nginx/apache
sudo certbot certonly --nginx -d irc.tudominio.com
# Opcion B: usar webroot
sudo certbot certonly --webroot -w /var/www/html -d irc.tudominio.com

# Los archivos se guardan en:
# /etc/letsencrypt/live/irc.tudominio.com/fullchain.pem  (certificado + cadena)
# /etc/letsencrypt/live/irc.tudominio.com/privkey.pem    (clave privada)
# /etc/letsencrypt/live/irc.tudominio.com/chain.pem      (cadena intermedia)
# /etc/letsencrypt/live/irc.tudominio.com/cert.pem       (certificado solo)

El metodo standalone es el mas simple si tu servidor no tiene un web server corriendo. Certbot levanta un servidor HTTP temporal en el puerto 80, Let's Encrypt se conecta a el para verificar que controlas el dominio, y Certbot descarga el certificado. El proceso dura segundos.

Si tienes un web server en el puerto 80 (comun si el servidor hace mas cosas que solo IRC), usa el plugin correspondiente o webroot. Lo importante es que el puerto 80 este accesible desde Internet durante la validacion.

Un detalle critico: los archivos en /etc/letsencrypt/live/ son symlinks, no archivos reales. Los archivos reales estan en /etc/letsencrypt/archive/. Esto importa cuando configuras permisos: el usuario que corre el IRCd necesita poder seguir los symlinks y leer los archivos destino.

Configurar TLS en UnrealIRCd

UnrealIRCd usa OpenSSL y tiene soporte TLS integrado. La configuracion va en el bloque tls de unrealircd.conf:

# En unrealircd.conf:

set {
  tls {
    certificate "/etc/letsencrypt/live/irc.tudominio.com/fullchain.pem";
    key "/etc/letsencrypt/live/irc.tudominio.com/privkey.pem";
    options {
      no-client-certificate;
    };
    /* Solo TLS 1.2+ */
    protocols "TLSv1.2,TLSv1.3";
    ciphers "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES256-GCM-SHA384";
  };
};

/* Listener con TLS en puerto 6697 */
listen {
  ip *;
  port 6697;
  options { tls; };
};

/* Listener sin TLS en puerto 6667 (opcional, para compatibilidad) */
listen {
  ip *;
  port 6667;
};

/* Link entre servidores: SIEMPRE con TLS */
link hub.tudominio.com {
  /* ... */
  tls-options {
    certificate "/etc/letsencrypt/live/irc.tudominio.com/fullchain.pem";
    key "/etc/letsencrypt/live/irc.tudominio.com/privkey.pem";
    options { verify-certificate; };
  };
};

Despues de modificar la configuracion:

# Recargar configuracion sin desconectar usuarios:
/REHASH

# O desde la linea de comandos:
kill -HUP $(pidof unrealircd)

El /REHASH recarga la configuracion incluyendo los certificados TLS. Esto es clave para la renovacion automatica: cuando Let's Encrypt renueva el certificado, basta con hacer rehash para que UnrealIRCd use el nuevo certificado. No necesitas reiniciar el servidor ni desconectar a nadie.

Atencion: El usuario que ejecuta UnrealIRCd necesita permisos de lectura sobre los archivos de Let's Encrypt. Los permisos por defecto de /etc/letsencrypt/ son restrictivos (solo root). Opciones: ejecutar un deploy-hook que copie los archivos, o anadir el usuario del IRCd al grupo ssl-cert si existe.

Configurar TLS en InspIRCd

InspIRCd soporta dos backends TLS: GnuTLS y OpenSSL. Cada uno tiene su modulo. Carga el que prefieras — no ambos.


















Mi recomendacion: GnuTLS para InspIRCd. La configuracion de prioridad de GnuTLS es mas clara que la de ciphers de OpenSSL, y la integracion con InspIRCd esta mejor probada historicamente. Si vienes de InspIRCd 3, la configuracion de v4 es ligeramente diferente — consulta la documentacion oficial.

InspIRCd tambien permite recargar los modulos TLS en caliente con /RELOADMODULE ssl_gnutls. Esto recarga el modulo completo incluyendo los certificados. Es mas agresivo que un /REHASH pero funciona cuando /REHASH no detecta el cambio de certificado.

Automatizar la renovacion

Los certificados de Let's Encrypt expiran cada 90 dias. Certbot intenta renovarlos automaticamente cuando quedan 30 dias. La renovacion se ejecuta via un cron o un timer de systemd que Certbot instala automaticamente. Lo que Certbot no hace automaticamente es avisar a tu IRCd de que hay un certificado nuevo. Para eso necesitas un deploy-hook.

# Opcion 1: deploy-hook en la renovacion de Certbot
# Crear /etc/letsencrypt/renewal-hooks/deploy/ircd-rehash.sh:

#!/bin/bash
# Solo actuar si se renueva el cert del IRC
if [ "$RENEWED_DOMAINS" = "irc.tudominio.com" ]; then
  # Para UnrealIRCd:
  kill -HUP $(cat /home/ircd/unrealircd/data/unrealircd.pid)
  
  # Para InspIRCd:
  # kill -HUP $(cat /home/ircd/inspircd/data/inspircd.pid)
  
  logger "IRCd rehashed after Let's Encrypt renewal"
fi

# Hacer ejecutable:
chmod +x /etc/letsencrypt/renewal-hooks/deploy/ircd-rehash.sh

# Opcion 2: cron semanal (mas simple, menos elegante)
# Cada lunes a las 4am, renovar y rehash:
0 4 * * 1 certbot renew --quiet && kill -HUP $(cat /path/to/ircd.pid)

# Opcion 3: para ZNC (bouncer)
# ZNC usa un archivo .pem combinado:
0 4 * * 1 certbot renew --quiet && \
  cat /etc/letsencrypt/live/tudominio/privkey.pem \
      /etc/letsencrypt/live/tudominio/fullchain.pem \
  > /home/znc/.znc/znc.pem && \
  chmod 600 /home/znc/.znc/znc.pem && \
  kill -HUP $(pidof znc)

La opcion 1 es la correcta. El deploy-hook solo se ejecuta cuando Certbot efectivamente renueva un certificado, no cada vez que intenta renovar. Esto significa que tu IRCd solo hace rehash cuando hay un certificado nuevo, no innecesariamente.

Si el rehash falla por cualquier razon, el IRCd sigue usando el certificado anterior hasta que alguien lo arregle. No se cae. No pierde conexiones. El peor escenario es que el certificado viejo expire y los clientes empiecen a recibir warnings — pero tienes 30 dias de margen para darte cuenta.

Consejo: Anade una alerta de monitorizacion que compruebe la fecha de expiracion del certificado TLS en el puerto 6697. Un simple echo | openssl s_client -connect irc.tudominio.com:6697 2>/dev/null | openssl x509 -noout -dates en un cron te avisa si algo fallo.

Errores comunes y como resolverlos

Despues de ayudar a configurar TLS en decenas de servidores IRC, estos son los problemas que veo una y otra vez:

  • Permission denied al leer el certificado: El error mas comun. El usuario que ejecuta el IRCd no tiene acceso a /etc/letsencrypt/live/. Solucion: crear un deploy-hook que copie los archivos a un directorio accesible por el IRCd con los permisos correctos.
  • Certificado expirado sin que nadie lo note: El certificado caduco, Certbot no lo renovo (quiza el puerto 80 esta bloqueado por un firewall), y nadie se dio cuenta. Los usuarios ven un warning de certificado invalido pero la mayoria lo ignora. Solucion: monitorizacion activa de la expiracion.
  • Chain incompleta: El IRCd sirve el certificado pero no la cadena intermedia. Algunos clientes validan correctamente y otros no, dependiendo de si tienen la cadena cacheada. Solucion: usar fullchain.pem siempre, no cert.pem.
  • DH params debiles o ausentes: Algunos backends TLS requieren DH parameters. Si no los generas o usas parametros de 1024 bits, clientes modernos rechazan la conexion. Solucion: generar DH params de 2048+ bits con openssl dhparam -out dhparams.pem 2048.
  • Ciphers demasiado restrictivos: Configuras solo ciphers de TLS 1.3 y usuarios con clientes viejos no pueden conectar. O configuras ciphers demasiado permisivos e incluyes protocolos vulnerables. Solucion: TLS 1.2 + TLS 1.3 cubre el 99% de los clientes sin comprometer seguridad.
  • STARTTLS sin fallback seguro: Ofreces STARTTLS en el puerto 6667 pero no fuerzas TLS. Los usuarios creen que estan cifrados pero un MITM puede suprimir la negociacion STARTTLS. Solucion: ofrecer el puerto 6697 con TLS implicito y documentarlo como el metodo recomendado.
# Verificar que TLS funciona correctamente desde fuera:
openssl s_client -connect irc.tudominio.com:6697 -servername irc.tudominio.com

# Verificar la cadena completa:
openssl s_client -connect irc.tudominio.com:6697 -showcerts

# Verificar fecha de expiracion:
echo | openssl s_client -connect irc.tudominio.com:6697 2>/dev/null | \
  openssl x509 -noout -dates

# Verificar protocolos soportados:
nmap --script ssl-enum-ciphers -p 6697 irc.tudominio.com

# Desde un cliente IRC, verificar la conexion TLS:
# En irssi: /whois tunick -> muestra "is using a secure connection"
# En WeeChat: /whois tunick -> misma info

Preguntas frecuentes

Puedo usar un certificado autofirmado en vez de Let's Encrypt?

Tecnicamente si. El cifrado sera el mismo. Pero los clientes IRC mostraran un warning de certificado no confiable, y usuarios paranoicos (que son los que mas les importa la seguridad) rechazaran conectarse. Let's Encrypt es gratuito — no hay razon para usar autofirmados en produccion. Solo para entornos de test.

Necesito un certificado diferente para cada servidor de la red?

Si. Cada servidor debe tener su propio certificado para su propio hostname. Si tu red tiene hub.tudominio.com y leaf.tudominio.com, necesitas dos certificados. Puedes usar un certificado wildcard (*.tudominio.com) si prefieres, pero requiere validacion DNS en vez de HTTP, lo que complica un poco la automatizacion.

TLS afecta al rendimiento del servidor IRC?

Minimamente. El handshake TLS anade unos milisegundos a cada nueva conexion. El cifrado/descifrado del trafico usa algo de CPU, pero en un servidor moderno es imperceptible incluso con miles de usuarios. IRC es un protocolo de texto ligero — el overhead de TLS es trivial comparado con lo que cualquier web server maneja.

Debo cerrar el puerto 6667 (sin TLS)?

Idealmente si. Si fuerzas TLS en todos los puertos, garantizas que nadie se conecta sin cifrar. En la practica, algunos clientes viejos o configuraciones legacy solo soportan conexion plana. La decision depende de tu red: si puedes permitirtelo, cierra el 6667 y obliga TLS. Si no, mantenlo abierto pero documenta que el 6697 es el recomendado.