Let’s Encrypt se puso necio: cómo reparamos certificados vencidos en IIS con win-acme
Read Time:11 Minute, 6 Second

Let’s Encrypt se puso necio: cómo reparamos certificados vencidos en IIS con win-acme

0 0

¿Qué P#%&2$ pasó?

Hace algún tiempo publicamos en BaleadaGeek cómo instalar certificados gratuitos de Let’s Encrypt en IIS utilizando win-acme.

Todo quedó bonito:

  • HTTPS funcionando.
  • Candadito seguro en el navegador.
  • Certificados instalados en IIS.
  • Renovación automática configurada.
  • Una tarea programada encargada de que nunca más tuviéramos que preocuparnos por el asunto.

La promesa era básicamente:

Configuralo una vez y olvidate del certificado.

Pues resulta que el servidor se tomó demasiado en serio la parte de “olvidate”.

En julio de 2026 descubrimos que el certificado de apps.zelvait.com había vencido, el de n8n.zelvait.com estaba a pocos días de correr la misma suerte y win-acme había decidido comunicarse con nosotros mediante jeroglíficos como estos:

Unable to read renewal:
missing TargetPluginOptions

Binding null not yet found in IIS

Target plugin did not generate a target

O sea: el candadito verde se había ido de vacaciones y nos había dejado pagando el hotel.

Lo que parecía una renovación rápida terminó convertido en una pequeña odisea entre IIS, PowerShell, bindings, archivos JSON, almacenes de certificados, tareas programadas y dos versiones distintas de win-acme.

La buena noticia es que logramos reparar los tres certificados sin incendiar producción.

Y como toda tragedia informática debe dejar al menos una buena documentación, aquí va el procedimiento completo.


📍 Entorno de batalla

Este era nuestro escenario:

  • Sistema operativo: Windows Server 2022
  • Servidor web: IIS 10
  • Cliente ACME original: win-acme 2.0.10.444
  • Cliente ACME actualizado: win-acme 2.2.9.1701
  • Autoridad certificadora: Let’s Encrypt
  • Almacén de certificados: LocalMachine\WebHosting
  • Dominios involucrados:
    • apps.zelvait.com
    • n8n.zelvait.com
    • cieloschipe.zelvait.com

win-acme es un cliente ACMEv2 para Windows que permite crear, instalar y renovar automáticamente certificados, con integración específica para IIS. La versión recomendada por el proyecto al momento de esta reparación era la rama 2.2.9.1.


🚨 El primer síntoma: la renovación automática dejó de hacer su chamba

Ejecutamos manualmente la renovación desde la instalación existente:

C:\inetpub\win-acme.v2.0.10.444\wacs.exe `
    --renew `
    --baseuri "https://acme-v02.api.letsencrypt.org/"

Y recibimos esta alegre colección de errores:

Unable to read renewal:
missing TargetPluginOptions

Binding null not yet found in IIS,
create it or use the Manual target plugin instead

Target plugin did not generate a target

Renewal for [IIS] n8n,
n8n.zelvait.com failed

La primera sospecha era bastante lógica:

Seguramente alguien eliminó o cambió los bindings en IIS.

Entramos al Administrador de IIS y revisamos.

Los bindings sí existían.

Default Web Site
http     *:80:apps.zelvait.com
https    *:443:apps.zelvait.com

n8n
http     *:80:n8n.zelvait.com
https    *:443:n8n.zelvait.com

Entonces el mensaje:

Binding null not yet found in IIS

no significaba necesariamente que el binding visible hubiera desaparecido.

El problema podía estar en la configuración interna guardada por win-acme: la renovación ya no podía relacionar correctamente su fuente original con el binding actual.

Ahí comenzó la investigación de verdad.


🔎 Paso 1: confirmar qué certificados estaban vencidos

Primero consultamos los bindings HTTPS y sus certificados asociados:

Import-Module WebAdministration

Get-WebBinding -Protocol "https" |
    Where-Object {
        $_.bindingInformation -match `
        "apps\.zelvait\.com|n8n\.zelvait\.com|cieloschipe\.zelvait\.com"
    } |
    Select-Object bindingInformation,
                  sslFlags,
                  certificateStoreName,
                  certificateHash

Los resultados mostraron que los certificados estaban almacenados en:

Cert:\LocalMachine\WebHosting

No en:

Cert:\LocalMachine\My

Este detalle importa porque buscar únicamente en LocalMachine\My puede hacerte creer que IIS no tiene ningún certificado instalado, aunque realmente esté utilizando el almacén WebHosting.

Después consultamos las fechas de expiración.

DominioExpiraciónEstado
apps.zelvait.com25 de julio de 2026Vencido
n8n.zelvait.com14 de agosto de 202615 días restantes
cieloschipe.zelvait.com21 de octubre de 202683 días restantes

¡Ajá!

Ya teníamos:

  • un muerto;
  • uno tosiendo;
  • y otro todavía comiendo baleada tranquilo.

🗃️ Paso 2: revisar las renovaciones registradas por win-acme

win-acme guarda la configuración de sus renovaciones en archivos con extensión:

*.renewal.json

En nuestro servidor estaban en:

C:\ProgramData\win-acme\
    acme-v02.api.letsencrypt.org\

Encontramos tres archivos:

0WGIJ7MsK0C4WCHqB1OeIg.renewal.json
A-2OJtiIBU6uITBOU5iLKw.renewal.json
CM6OnGtZk0eAUK7rbv_3kA.renewal.json

Para identificar a qué dominio correspondía cada renovación, buscamos los nombres de host dentro de los archivos:

$RenewalPath = `
    "C:\ProgramData\win-acme\acme-v02.api.letsencrypt.org"

Get-ChildItem $RenewalPath -Filter "*.renewal.json" |
    ForEach-Object {

        $Raw = Get-Content $_.FullName -Raw

        [PSCustomObject]@{
            File        = $_.Name
            Apps        = $Raw -match "apps\.zelvait\.com"
            N8N         = $Raw -match "n8n\.zelvait\.com"
            CielosChipe = $Raw -match "cieloschipe\.zelvait\.com"
        }
    }

El mapa terminó así:

RenovaciónSitio o dominio
0WGIJ7...Default Web Site, configurado como (any host)
A-2OJti...n8n.zelvait.com
CM6OnG...cieloschipe.zelvait.com

El primer archivo no mencionaba literalmente apps.zelvait.com, por lo que al inicio parecía una renovación desconocida.

Pero su historial contenía este thumbprint:

6EF361BFD6C75CC351577810B379B34E89A0572A

Ese era exactamente el thumbprint asociado en IIS a:

apps.zelvait.com

Misterio resuelto.

El archivo supuestamente desconocido sí era la renovación de apps.zelvait.com.

Solo que estaba configurado de una forma demasiado genérica.


🧟 El verdadero problema

Encontramos tres debilidades principales.

1. Se seguía utilizando una versión vieja de win-acme

La tarea programada ejecutaba:

C:\inetpub\win-acme.v2.0.10.444\wacs.exe

En el servidor también existía otra versión descargada, pero no era la que utilizaba la tarea automática.

Tener una herramienta nueva guardada en una carpeta no significa que el servidor la esté usando.

La actualización desde cualquier versión 2.x.x hacia 2.2.x está documentada por win-acme como una actualización tipo xcopy: se puede extraer la versión nueva en otra ubicación y conservar la configuración existente en ProgramData.

2. La renovación de apps estaba configurada como “any host”

La renovación aparecía así:

[IIS] Default Web Site, (any host)

Y su fuente incluía el sitio completo:

IncludeSiteIds : 1
IncludeTypes   : http

Eso era demasiado amplio.

Si se agregaba otro binding HTTP al Default Web Site, la renovación podía comenzar a considerar también ese hostname.

Cuando un certificado corresponde a un único dominio, es más seguro seleccionar expresamente ese binding. La documentación del plugin IIS permite filtrar y elegir bindings específicos en lugar de incluir todos los bindings de un sitio.

3. La renovación de n8n había perdido la relación correcta con IIS

El binding visible existía, pero win-acme respondía:

Binding null not yet found in IIS

La solución más limpia era volver a configurar la fuente de la renovación y seleccionar otra vez:

Sitio: n8n
Binding: n8n.zelvait.com

💾 Paso 3: hacer respaldo antes de tocar producción

Antes de modificar certificados, bindings o tareas programadas, respaldamos la configuración de win-acme:

$BackupRoot = "C:\Backup\win-acme-20260729"

New-Item `
    -ItemType Directory `
    -Path $BackupRoot `
    -Force

Copy-Item `
    -Path "C:\ProgramData\win-acme" `
    -Destination "$BackupRoot\ProgramData-win-acme" `
    -Recurse `
    -Force

También respaldamos IIS:

& "$env:windir\System32\inetsrv\appcmd.exe" `
    add backup BeforeWinAcmeRepair_20260729

El servidor respondió:

BACKUP object "BeforeWinAcmeRepair_20260729" added

📌 Regla BaleadaGeek:

Si no tenés respaldo, no estás reparando. Estás apostando.


⬆️ Paso 4: instalar la versión nueva sin borrar la anterior

Descargamos win-acme 2.2.9.1, compilación 2.2.9.1701, y lo extrajimos en:

C:\inetpub\win-acme.v2.2.9.1

Conservamos temporalmente la instalación anterior:

C:\inetpub\win-acme.v2.0.10.444

win-acme recomienda extraer el programa en una ubicación permanente, no en una carpeta temporal, para que la tarea programada pueda encontrar el ejecutable posteriormente.

Desde PowerShell intentamos ejecutar:

wacs.exe

Y PowerShell respondió:

The term 'wacs.exe' is not recognized...

Pero el archivo estaba allí.

No era otro misterio de IIS.

PowerShell no ejecuta automáticamente programas desde el directorio actual. Hay que anteponer:

.\wacs.exe

También funciona la ruta completa:

C:\inetpub\win-acme.v2.2.9.1\wacs.exe

La versión nueva inició correctamente:

Software version 2.2.9.1701
Connecting to Let's Encrypt...
Connection OK!

R: Run renewals (2 currently due)
A: Manage renewals (3 total)

La buena noticia era que la versión nueva podía leer las tres renovaciones.


🛠️ Paso 5: reparar apps.zelvait.com

Desde el menú principal seleccionamos:

A: Manage renewals

La lista mostraba:

1: [IIS] Default Web Site, (any host)
2: [IIS] n8n, n8n.zelvait.com
3: [IISBinding] cieloschipe.zelvait.com

Seleccionamos únicamente la primera renovación y entramos a:

E: Edit renewal
2: Source
1: Read bindings from IIS

Luego seleccionamos:

Site 1: Default Web Site
Binding 1: apps.zelvait.com

Confirmamos la selección y aceptamos el nuevo nombre:

[IIS] Default Web Site, apps.zelvait.com

win-acme sobrescribió la configuración anterior y comenzó la emisión:

Plugin IIS generated source apps.zelvait.com
Force renewing [IIS] Default Web Site, apps.zelvait.com

[apps.zelvait.com] Authorizing...
Authorization result: valid

Downloading certificate
Installing certificate in WebHosting
Updating existing https binding apps.zelvait.com:443

Finalmente eliminó el certificado vencido:

Removing certificate
[IIS] Default Web Site, (any host)

¡Primer paciente resucitado!


⚙️ Paso 6: reparar n8n.zelvait.com

Repetimos el procedimiento con la renovación de n8n:

A: Manage renewals
Seleccionar renovación n8n
E: Edit renewal
2: Source
1: Read bindings from IIS

Seleccionamos:

Site 2: n8n
Binding 1: n8n.zelvait.com

La renovación quedó registrada como:

[IIS] n8n, n8n.zelvait.com

Y win-acme emitió el certificado:

[n8n.zelvait.com] Authorizing...
Authorization result: valid

Installing certificate in WebHosting
Updating existing https binding n8n.zelvait.com:443

En este caso el binding conservó:

flags: 1

Ese valor representa el uso de SNI en el binding HTTPS, útil cuando varios sitios comparten la misma dirección IP y el puerto 443.


🕒 Paso 7: actualizar la tarea programada

La versión nueva detectó que la tarea existente apuntaba a otra ubicación:

Scheduled task points to different location
Scheduled task does not look healthy

Durante la instalación de los nuevos certificados, win-acme eliminó la tarea anterior y creó otra:

Name:
win-acme renew (acme-v02.api.letsencrypt.org)

Path:
C:\inetpub\win-acme.v2.2.9.1

Command:
wacs.exe --renew

La dejamos ejecutándose con la cuenta predeterminada del sistema, sin asignarle un usuario particular.

win-acme administra las configuraciones de renovación como unidades independientes y utiliza una tarea programada para comprobar cuándo corresponde reemplazar cada certificado.


🧪 Paso 8: ejecutar una prueba final

Después de reparar apps y n8n, el menú mostraba:

R: Run renewals (0 currently due)

Ejecutamos R para verificar el ciclo completo.

Curiosamente, win-acme decidió renovar también:

cieloschipe.zelvait.com

La operación terminó correctamente:

Updating existing https binding
cieloschipe.zelvait.com:443

Renewal for [IIS] cieloschipe,
cieloschipe.zelvait.com succeeded

Las otras dos renovaciones informaron:

apps.zelvait.com is due after 2026/9/23
n8n.zelvait.com is due after 2026/9/23

Y finalmente:

R: Run renewals (0 currently due)
A: Manage renewals (3 total)

Ahora sí:

  • cero certificados vencidos;
  • cero renovaciones pendientes;
  • cero pacientes en cuidados intensivos.

✅ Estado final

DominioResultado
apps.zelvait.comCertificado nuevo instalado
n8n.zelvait.comCertificado nuevo instalado
cieloschipe.zelvait.comCertificado nuevo instalado
Renovaciones registradas3
Renovaciones pendientes0
Tarea programadaActualizada
Ejecutable activowin-acme 2.2.9.1701
Próxima renovaciónDespués del 23 de septiembre de 2026

🧠 Lo que aprendimos de esta odisea

1. Que un binding exista no significa que la renovación esté sana

IIS puede mostrar correctamente el dominio y aun así win-acme conservar una referencia interna vieja, incompleta o demasiado genérica.

2. --force no repara una mala configuración

Ejecutar:

.\wacs.exe --renew --force

solo obliga a win-acme a ejecutar las configuraciones existentes.

Si la configuración está dañada, vas a forzar el mismo error, pero con más entusiasmo.

Además, Let’s Encrypt limita normalmente a cinco certificados nuevos para el mismo conjunto exacto de identificadores dentro de siete días. No conviene emitir certificados repetidamente mientras todavía estás diagnosticando.

3. Evitá “any host” cuando solo necesitás un hostname

Si la renovación corresponde únicamente a:

apps.zelvait.com

seleccioná expresamente ese binding.

Menos automatización indiscriminada significa menos sorpresas a las nueve de la noche.

4. Revisá qué ejecutable utiliza realmente la tarea programada

Podés tener la versión más nueva descargada y seguir operando durante meses con la anterior porque la tarea apunta a otra carpeta.

5. Revisá el almacén correcto de certificados

En IIS, los certificados pueden encontrarse en:

LocalMachine\WebHosting

Buscar únicamente en:

LocalMachine\My

puede darte un falso negativo.

6. El respaldo se hace antes del susto

Respaldá:

  • %ProgramData%\win-acme;
  • la configuración de IIS;
  • y, cuando sea posible, la máquina virtual o el servidor.

📋 Checklist rápido

[ ] Confirmar bindings HTTP y HTTPS en IIS
[ ] Revisar fechas de expiración
[ ] Identificar los archivos *.renewal.json
[ ] Verificar el thumbprint de cada binding
[ ] Buscar certificados en WebHosting y My
[ ] Respaldar ProgramData\win-acme
[ ] Respaldar IIS con appcmd
[ ] Instalar la versión recomendada de win-acme
[ ] Revisar qué ejecutable usa la tarea programada
[ ] Editar la fuente de cada renovación dañada
[ ] Seleccionar un hostname específico
[ ] Emitir e instalar el certificado
[ ] Confirmar el binding HTTPS
[ ] Ejecutar una prueba normal de renovaciones
[ ] Verificar que existan cero renovaciones pendientes

🔥 Conclusión

El certificado vencido era solamente el síntoma.

El problema real era una combinación de:

  • una versión vieja de win-acme;
  • renovaciones almacenadas con referencias problemáticas;
  • una fuente configurada como (any host);
  • y una tarea programada que seguía apuntando al ejecutable anterior.

La solución no fue darle --force a todo y rezar.

Fue diagnosticar cada capa:

Dominio
→ Binding
→ Certificado
→ Renovación
→ Ejecutable
→ Tarea programada

Esa es probablemente la lección más importante de esta historia:

Cuando la automatización deja de funcionar, no forcés más automatización. Revisá primero qué demonios está automatizando.

Ahora los tres sitios tienen certificados vigentes, las renovaciones quedaron correctamente asociadas y el servidor puede volver a dormir tranquilo.

El administrador no.

Ese ya quedó traumado.

Home » Let’s Encrypt se puso necio: cómo reparamos certificados vencidos en IIS con win-acme
Happy
Happy
0 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %

Average Rating

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Optimizar Build lento en GeneXus GX18 con MSBuild y ToolsVersion Entrada anterior El relajo del MSBuild en GeneXus GX18: cómo le bajamos la calentura al Build de ZelvaERP/ZelvaPOS