Por qué los webhooks de GitHub agotan el tiempo contra un Jenkins detrás de una VPN
GitHub envía los webhooks hacia dentro, así que una VPN que protege Jenkins los rechaza. Reenvía la entrega hacia fuera con un relay o sustituye el webhook por un agente que sondee.
La página de entrega muestra un tiempo de espera y Jenkins no registra nada, que es la firma de una conexión que nunca llegó. GitHub envía un webhook como un HTTP POST saliente desde su propia infraestructura a la URL de payload que configuraste. Una VPN protege Jenkins rechazando exactamente ese tipo de conexión. Nada está roto en ninguno de los dos lados. El problema es la dirección de la petición.
Qué intenta realmente la entrega
GitHub resuelve el nombre de host de la URL de payload y se conecta a él desde internet público. Si esa URL apunta a una dirección privada, o a un nombre que solo se resuelve dentro de tu red, el intento falla antes de que se haga ninguna petición. Si apunta a un nombre público que tu borde descarta, el intento agota el tiempo en su lugar.
La distinción importa para el diagnóstico. Un fallo de DNS y un puerto filtrado se parecen ambos a que no pasó nada en el lado de Jenkins, y ambos aparecen en GitHub como una entrega fallida con un motivo distinto adjunto. Lee ese motivo antes de cambiar la URL.
Un túnel que marca hacia fuera no crea un escucha. Esa es la parte que la gente espera que funcione de otra manera.
Tres formas de corregir la dirección de la conexión
Cada solución cambia quién inicia la conexión, y solo hay tres formas.
| Approach | Who initiates | What you give up |
|---|---|---|
| Relay service | An outbound client inside the network | A third party sees every payload |
| Reverse tunnel or ingress | The public edge, into a controlled listener | An inbound surface you must secure |
| Runner or polling | The agent, outbound to GitHub | Instant delivery, replaced by a schedule |
El sondeo es el menos elegante y el más fácil de operar. Un relay mantiene retroalimentación casi instantánea sin abrir un puerto. Un runner suele ser la respuesta correcta cuando la confidencialidad del payload descarta a un tercero, porque el trabajo puede ejecutarse en una máquina que ya está dentro de la red.
Elige por lo que puedes operar, no por lo que suena moderno.
Reenvía una entrega hacia fuera con smee
Crea un canal en el servicio de relay y guarda su URL. Dentro de la red, instala el cliente y reenvía las entregas a la ruta que Jenkins espera. La URL de payload en los ajustes del repositorio se convierte en la URL del canal, con el tipo de contenido establecido en application/json.
sudo npm install --global smee-client
smee -u CHANNEL_URL --path /github-webhook/ --port 8080Tres detalles deciden si funciona. La ruta debe coincidir con lo que Jenkins espera para los eventos de GitHub, que por defecto es /github-webhook/. Jenkins debe estar escuchando en el puerto que le diste al cliente. Y la URL de Jenkins configurada en el trabajo tiene que concordar con esa ruta, o el evento llega y luego se descarta.
Una barra final que no coincide produce un 404 que se lee como una entrega fallida y no lo es.
Ejecutarlo de forma que sobreviva a la semana
Un cliente en primer plano muere con el terminal, y los webhooks funcionan un día y luego se detienen. Dale una unit con Restart=always, ejecútala como el usuario Jenkins y lee el journal después de la primera entrega real. Ese último paso es lo que separa un relay que funciona de uno configurado.
[Unit]
Description=smee webhook relay
After=network.target
[Service]
Type=simple
Restart=always
RestartSec=2
User=jenkins
ExecStart=/usr/bin/smee -u CHANNEL_URL --path /github-webhook/ --port 8080
[Install]
WantedBy=multi-user.targetPrueba con un push real en lugar del botón de repetición. Una repetición ejercita el payload almacenado del relay, no el camino de GitHub a tu canal, así que puede pasar mientras la entrega en vivo sigue fallando.
Avisa cuando la unit se detenga. Un relay caído es silencioso por construcción, y el silencio es justo lo que ya estás depurando.
Qué puede ver el relay y cuándo no usarlo
Un relay termina el payload, así que ve nombres de repositorios, datos de commit y todo lo demás que lleva el evento. Trata la URL del canal como un punto final público. Mantén configurado el token secreto de Jenkins para que se rechacen las entregas falsificadas, y limita el token que usa Jenkins al mínimo que necesita.
Si la confidencialidad del payload lo descarta, un runner autoalojado o un túnel inverso autenticado es la respuesta.
Un relay ahora es infraestructura, y merece la misma revisión que la build que dispara. Rota el canal si se filtra y anota la fecha en que verificaste por última vez una entrega en vivo. Pon esa fecha en el runbook junto a la URL del canal. Un relay que nadie ha revisado durante un año es un punto final público que nadie posee.