Appearance
Proxy nginx compartido
Todos los dominios públicos del proyecto se sirven a través de un proxy inverso nginx compartido con otros consultores de la EJE (Debian, nginx + certbot). El mismo proxy atiende test y producción a la vez: termina TLS y reenvía cada dominio al puerto publicado del servicio en el host del entorno correspondiente.
Proxy compartido
El proxy aloja sitios de otros consultores. El despliegue automatizado solo puede tocar los dominios propios: cada dominio debe estar en la lista permitida del servidor y los archivos ajenos (sin marcador EJE-MANAGED) nunca se sobrescriben.
Convenciones del proxy
Un archivo por dominio en
/etc/nginx/sites-available/<dominio>(sin extensión), con enlace simbólico en/etc/nginx/sites-enabled/.Un certificado por dominio, emitido con
certbot certonly --nginx -d <dominio>. El timer de certbot del servidor renueva todos los certificados.Los archivos gestionados por el pipeline empiezan con el marcador:
# EJE-MANAGED: infra-deploy/proxy - <dominio> ...Ese marcador identifica qué archivos puede sobrescribir el despliegue. Los archivos sin marcador se consideran de otros consultores y son intocables.
Configuración versionada (infra-deploy/proxy/)
La configuración ya no se edita a mano en el servidor: se versiona en el repositorio infra-deploy y se renderiza por entorno.
infra-deploy/proxy/
├── sites/
│ ├── test.txt # dominios de test → puerto en DEV_DEPLOY_HOST
│ └── prod.txt # dominios de prod → puerto en PROD_DEPLOY_HOST
├── templates/
│ ├── site-https.conf.template # fase definitiva (443 + redirección 80)
│ └── site-http.conf.template # fase 1: dominio aún sin certificado
├── scripts/
│ ├── render.sh # renderiza un entorno (sustituye dominio/upstream/puerto)
│ └── validate.sh # validación en CI: lint + nginx -t con certificados falsos
└── server/
└── eje-proxy-deploy # script root del servidor (instalación única manual)sites/<entorno>.txtdeclara una línea<dominio> <puerto>por sitio. La ramadeveloppublica los sitios detest.txtcon upstreamDEV_DEPLOY_HOST; la ramamain, los deprod.txtconPROD_DEPLOY_HOST.- Las plantillas replican la convención del proxy (bloque 443 con certificados de Let's Encrypt + bloque 80 con redirección,
client_max_body_size 30m, cabecerasX-Forwarded-*).
Dos fases por dominio nuevo
Un dominio nuevo no tiene certificado todavía, y nginx no puede cargar un bloque ssl sin certificado. Por eso cada dominio se renderiza en dos fases y eje-proxy-deploy elige automáticamente según exista /etc/letsencrypt/live/<dominio>/fullchain.pem:
- Fase HTTP (
site-http): primer despliegue; sirve el sitio por el puerto 80. El job avisa que falta el certificado. - Emisión manual del certificado en el proxy (único paso manual por dominio):
certbot certonly --nginx -d <dominio>. - Fase HTTPS (
site-https): al volver a ejecutar el despliegue, detecta el certificado e instala la configuración definitiva.
El script eje-proxy-deploy
Fuente: infra-deploy/proxy/server/eje-proxy-deploy. Se instala una sola vez a mano como root en /usr/local/sbin/eje-proxy-deploy (ver runbook). El usuario de despliegue (deploy-eje, sin sudo general) solo puede ejecutar ese script vía sudo.
En cada ejecución el script:
- Valida el directorio recibido: manifest de dominios, marcador
EJE-MANAGEDen cada archivo, lista blanca de directivas nginx (proxy_passsolo haciahttp://<host>:<puerto>,includesolo del archivo de opciones de certbot, certificados solo bajo/etc/letsencrypt/live/<dominio>/, etc.). - Autoriza: todos los dominios deben estar en
/etc/eje-proxy/allowed-domains.txt(root-only, en el servidor). - Protege: si
sites-available/<dominio>existe sin marcadorEJE-MANAGED, aborta sin tocarlo. - Respalda los archivos que va a modificar en
/var/backups/eje-proxy/<timestamp>/(conserva los últimos 10 respaldos). - Aplica solo los archivos que cambiaron (idempotente: sin cambios no se recarga nginx) y crea los enlaces en
sites-enabledsi faltan. - Verifica con
nginx -t; si falla, revierte todos los cambios desde el respaldo y termina con error. - Recarga nginx (
systemctl reload nginx) solo si hubo cambios.
El modo --lint ejecuta solo el paso 1 sin tocar el sistema; es el que usa el CI para validar con exactamente las mismas reglas que el servidor.
Jobs del pipeline
Ver también Pipelines CI/CD.
| Job | Stage | Cuándo corre |
|---|---|---|
validate-proxy | validate | Todo pipeline de rama o MR, salvo trigger con SERVICE_NAME=<servicio> |
deploy-proxy | deploy | Todo pipeline de develop/main (push, web o trigger), salvo SERVICE_NAME=<servicio> |
validate-proxyrenderiza test y prod, ejecutaeje-proxy-deploy --linty correnginx -tcon certificados falsos para ambas fases. No toca servidores.deploy-proxyrenderiza el entorno de la rama, vuelve a hacer lint, sube la configuración porscpy ejecutasudo /usr/local/sbin/eje-proxy-deploy <dir>en el proxy.- Antes de aplicar, el job compara el SHA-256 del script instalado en el servidor con el del repositorio y muestra un aviso si difieren (no falla; el script se reinstala a mano según el runbook).
SERVICE_NAME=<servicio>despliega solo ese servicio (el proxy no corre);SERVICE_NAME=proxydespliega solo el proxy (el orquestador no corre); un trigger sinSERVICE_NAMEdespliega orquestador y proxy.
Runbook: instalación única en el proxy
Pasos que se ejecutan una sola vez. Salvo que se indique lo contrario, los comandos se ejecutan como root en el proxy compartido.
Generar el par de claves SSH (en su máquina local, no en el proxy):
shssh-keygen -t ed25519 -N "" -C "deploy-eje@infra-deploy" -f ./deploy-eje_ed25519Genera dos archivos:
deploy-eje_ed25519(privada): se carga en GitLab comoPROXY_SSH_PRIVATE_KEY(File, Protected). No la guarde en ningún repositorio; elimine la copia local al terminar.deploy-eje_ed25519.pub(pública): se pega en elauthorized_keysdel paso siguiente.
Crear el usuario dedicado (sin sudo general):
shadduser --disabled-password --gecos "Deploy EJE" deploy-eje install -d -o deploy-eje -g deploy-eje -m 700 /home/deploy-eje/.ssh # Pegar el contenido de deploy-eje_ed25519.pub, restringido (sin forwarding ni pty): echo 'restrict ssh-ed25519 AAAA... deploy-eje@infra-deploy' \ > /home/deploy-eje/.ssh/authorized_keys chown deploy-eje:deploy-eje /home/deploy-eje/.ssh/authorized_keys chmod 600 /home/deploy-eje/.ssh/authorized_keysNo use
command=fijo en la clave: el job necesita ejecutarmkdir,scp,sha256sumy el comandosudodel despliegue.Instalar el script root. El script se copia desde la estación del administrador y se verifica su SHA-256 antes de instalarlo.
a. En su máquina local, desde el clon de
infra-deployen la rama que se va a desplegar (normalmentemain), calcule el hash y copie el script a su propio usuario administrador en el proxy (no adeploy-eje, que no debe poder modificar el archivo antes de instalarlo):shgit -C infra-deploy pull sha256sum infra-deploy/proxy/server/eje-proxy-deploy scp infra-deploy/proxy/server/eje-proxy-deploy <admin>@<proxy>:eje-proxy-deploy.nuevob. En el proxy, como root: copie el archivo a un directorio temporal propiedad de root, verifique el hash contra el valor del paso anterior e instálelo desde esa copia verificada:
shEXPECTED=<sha256 obtenido en el paso a> TMP=$(mktemp -d) # directorio 700 propiedad de root cp /home/<admin>/eje-proxy-deploy.nuevo "$TMP/eje-proxy-deploy" echo "$EXPECTED $TMP/eje-proxy-deploy" | sha256sum -c - \ && install -o root -g root -m 755 "$TMP/eje-proxy-deploy" /usr/local/sbin/eje-proxy-deploy rm -rf "$TMP" /home/<admin>/eje-proxy-deploy.nuevo sha256sum /usr/local/sbin/eje-proxy-deploySi
sha256sum -cno muestraOK, el archivo no se instala: vuelva a copiarlo.Hash de referencia
El job
deploy-proxyimprime el hash del script del repositorio (repo: <sha256>) cuando difiere del instalado. Sirve como segunda referencia, independiente de su máquina local, para el valor deEXPECTED.Sin acceso por scp
Si solo dispone de una consola en el proxy, ejecute como root
EXPECTED=...yTMP=$(mktemp -d), cree el archivo pegando el contenido en un editor (nano "$TMP/eje-proxy-deploy") y continúe con el paso b desde la líneasha256sum -c(omitiendo elcp). El hash detecta cualquier error de copiado o pegado.Autorizar solo ese comando por sudo:
shcat > /etc/sudoers.d/deploy-eje <<'EOF' deploy-eje ALL=(root) NOPASSWD: /usr/local/sbin/eje-proxy-deploy * EOF chmod 440 /etc/sudoers.d/deploy-eje visudo -cCrear la lista de dominios permitidos (root-only; es la frontera que impide que el pipeline toque dominios ajenos):
shinstall -d -m 755 /etc/eje-proxy cat > /etc/eje-proxy/allowed-domains.txt <<'EOF' backend.idp-test.eje.gob.bo auth.idp-test.eje.gob.bo admin.idp-test.eje.gob.bo cuenta.idp-test.eje.gob.bo backend.sps-test.eje.gob.bo admin.sps-test.eje.gob.bo comisiones.sps-test.eje.gob.bo postulantes.sps-test.eje.gob.bo estacion.sps-test.eje.gob.bo tecnico.docs-test.eje.gob.bo usuario.docs-test.eje.gob.bo backend.idp.eje.gob.bo auth.idp.eje.gob.bo admin.idp.eje.gob.bo cuenta.idp.eje.gob.bo backend.sps.eje.gob.bo admin.sps.eje.gob.bo comisiones.sps.eje.gob.bo postulantes.sps.eje.gob.bo estacion.sps.eje.gob.bo tecnico.docs.eje.gob.bo usuario.docs.eje.gob.bo EOF chmod 644 /etc/eje-proxy/allowed-domains.txtConfigurar las variables CI/CD en GitLab (
infra-deploy), ver tabla de variables.Adoptar sitios existentes (si un dominio ya estaba configurado a mano): el primer despliegue fallará con "existe y NO tiene el marcador EJE-MANAGED". Verifique que el archivo actual no tenga personalizaciones necesarias, respáldelo y elimínelo (o agréguele como primera línea el marcador
# EJE-MANAGED: infra-deploy/proxy); el siguiente despliegue lo regenerará desde el repositorio.
Cuando el aviso de SHA-256 del pipeline indique que el script difiere del repositorio, repita únicamente el paso 3.
Cómo añadir un dominio
- Publicar el servicio en un puerto del host del entorno (
docker-compose.ymleninfra-deploy). - Agregar la línea
<dominio> <puerto>enproxy/sites/test.txty/oproxy/sites/prod.txt. - En el proxy, como root: agregar el dominio a
/etc/eje-proxy/allowed-domains.txt(y crear el registro DNS si no existe). - Hacer push a
develop(omain). El pipeline valida y despliega la fase HTTP del dominio nuevo; el job avisa que falta el certificado. - En el proxy, como root:
certbot certonly --nginx -d <dominio>. - Volver a ejecutar el despliegue del proxy (re-run del job
deploy-proxy, o trigger conSERVICE_NAME=proxy). Se instala la fase HTTPS definitiva. - Actualizar el mapa de subdominios y puertos del manual.
Rollback
Automático: si
nginx -tfalla tras aplicar,eje-proxy-deployrestaura los archivos desde el respaldo, vuelve a recargar nginx y el job falla.Manual (volver a una configuración anterior): los respaldos quedan en
/var/backups/eje-proxy/<timestamp>/(últimos 10). Como root:shls /var/backups/eje-proxy/ cp /var/backups/eje-proxy/<timestamp>/<dominio> /etc/nginx/sites-available/<dominio> nginx -t && systemctl reload nginxVía Git (preferido): revertir el commit en
infra-deployy dejar que el pipeline aplique la configuración anterior (es idempotente).
Solución de problemas
| Síntoma | Causa probable / acción |
|---|---|
dominio no autorizado en este servidor | Falta el dominio en /etc/eje-proxy/allowed-domains.txt (paso root en el proxy) |
existe y NO tiene el marcador EJE-MANAGED | El dominio ya tenía un archivo manual: adoptarlo (runbook, paso 7) |
directiva no permitida / proxy_pass inválido | La plantilla o el render generaron algo fuera de la lista blanca; corregir en proxy/templates/ |
Aviso aún no tiene certificado; se instala la fase HTTP | Normal en dominios nuevos: emitir el certificado y redesplegar (fases 2 y 3) |
nginx -t falló; los cambios fueron revertidos | Revisar el log del job; el servidor quedó con la configuración anterior |
| Aviso de SHA-256 distinto | El script instalado quedó desactualizado: reinstalar (runbook, paso 3) |
sudo: a password is required en el job | Falta o está mal /etc/sudoers.d/deploy-eje |
Error loading key "(stdin)": error in libcrypto | La clave no llegó como clave OpenSSH válida. El job acepta PROXY_SSH_PRIVATE_KEY como File o Variable; verifique que contenga la clave privada completa (-----BEGIN OPENSSH PRIVATE KEY----- … END), sin passphrase |
| El certificado no renueva | La renovación es del timer de certbot del servidor (compartido); revisar systemctl list-timers y certbot renew --dry-run como root |
Variables CI/CD del proxy
Se configuran en infra-deploy → Settings → CI/CD → Variables. No llevan prefijo de entorno porque un solo proxy sirve test y producción.
| Variable | Tipo | Protected / Masked | Ejemplo |
|---|---|---|---|
PROXY_DEPLOY_HOST | Variable | Protected | 172.17.1.10 |
PROXY_DEPLOY_USER | Variable | Protected | deploy-eje |
PROXY_SSH_PRIVATE_KEY | File | Protected | Contenido de la clave privada ed25519 |
Además, deploy-proxy reutiliza DEV_DEPLOY_HOST y PROD_DEPLOY_HOST (ya configuradas para el orquestador) como upstream de los sitios de test y producción respectivamente.
