Files
admin/.wiki/concepts/winacme-iis-owin-catchall-http01.md
vitya 3c7ca3416b feat(iis-migration): win-acme HTTP-01 auto-renewal on RUVDS IIS
Stood up a permanent self-renewing Let's Encrypt pipeline on the RUVDS
IIS host, replacing the manual traefik acme.json -> PFX import and
closing the 2026-07-22 cert-expiry deadline (new 25-SAN cert valid to
2026-09-03, SYSTEM scheduled task renews 55 days before expiry).

Key obstacle: the MoreThenCms OWIN catch-all (owin:HandleAllRequests)
swallowed /.well-known/acme-challenge/. Solved by carving the challenge
path into a separate IIS application in a No-Managed-Code app pool, plus
patching win-acme's Web_Config.xml template to remove the inherited Owin
handler. Staging + prod validation green for all 25 hostnames; live TLS
smoke confirms the new cert is served (incl degraded maljarka/rimiz).

- scripts/iis-migration-to-ruvds/03-ruvds-winacme.ps1 (idempotent setup)
- scripts/iis-migration-to-ruvds/winacme-Web_Config.xml (patched template)
- .wiki/concepts/winacme-iis-owin-catchall-http01.md (recipe + gotchas)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 21:08:41 +03:00

6.9 KiB
Raw Blame History

title: win-acme HTTP-01 авто-renewal на IIS под OWIN-catch-all CMS status: live tags: [windows, iis, ruvds, letsencrypt, win-acme, acme, http-01, owin, cms, ops] related: ruvds-iis-host, traefik-acme-json-to-iis-cert-import, windows-server-2025-core-bootstrap, iis-migration-2026-05-19-postmortem

win-acme HTTP-01 авто-renewal на IIS под OWIN-catch-all CMS

Постоянный self-renewing LE-pipeline на RUVDS IIS-хосте (ruvds-iis-host) для сайта snolla (25 hostname через SNI). Заменяет ручной метод traefik-acme-json-to-iis-cert-import (экспорт PFX из домашнего traefik acme.json + ручной Import-PfxCertificate) — тот делал RUVDS зависимым от домашней машины по сертификатам и требовал ручного продления.

Поднято 2026-06-05, после full DNS cutover на RUVDS (HTTP-01 challenge до cutover'а отскакивал на windows-source — LE не мог достучаться).

Что развёрнуто

  • win-acme v2.2.9 в C:\win-acme\ (скачан с GitHub releases, x64.pluggable).
  • Один SAN-cert на 25 hostname в store WebHosting, установлен во все 25 *:443:<host> SNI-биндинга.
  • Scheduled task win-acme-renew-snolla — daily 09:00 + random delay 4h, runs as SYSTEM, wacs.exe --renew --baseuri https://acme-v02.api.letsencrypt.org/. Renewal due 55 дней до expiry.
  • Idempotent setup-скрипт: scripts/iis-migration-to-ruvds/03-ruvds-winacme.ps1 (фазы download/app/probe/task/verify).

Главный gotcha: OWIN-catch-all жрёт /.well-known/acme-challenge/

Сайт = MoreThenCms на OWIN. В корневом web.config: owin:HandleAllRequests=true + handler Owin path="*" + runAllManagedModulesForAllRequests="true"весь трафик уходит в managed OWIN-pipeline. HTTP-01 токен (статический файл) отдаётся не статикой, а CMS → 404/500/redirect. LE-валидация падает (Invalid response ...: 500).

Снятие Owin-handler'а в дочернем web.config НЕ помогает — OWIN перехватывает на уровне модуля (HandleAllRequests). Что НЕ сработало:

  • web.config с <rewrite><rules><clear/></rules> — рерайта-то и нет, маршрутит managed-pipeline.
  • web.config с <handlers><remove name="Owin"/></handlers> в обычном (managed) пуле — модуль всё равно жив.

Рабочее решение: отдельное IIS-приложение в пуле «No Managed Code»

.well-known/acme-challenge вынесен в отдельное IIS Application под сайтом snolla с app-pool'ом acme-challenge, у которого managedRuntimeVersion='' (No Managed Code). В таком пуле .NET/OWIN не стартует вообще — токен отдаётся нативным StaticFileModule.

Нюанс: в дочернем web.config всё равно нужно <handlers><remove name="Owin"/></handlers> — иначе унаследованный managed Owin-handler даёт 500 (managed handler в unmanaged пуле). Плюс extensionless-mime (токены без расширения). Проверенный web.config:

<configuration>
  <system.webServer>
    <handlers><remove name="Owin" /></handlers>
    <staticContent>
      <remove fileExtension="." /><mimeMap fileExtension="." mimeType="text/plain" />
    </staticContent>
    <modules runAllManagedModulesForAllRequests="false" />
  </system.webServer>
</configuration>

Результат: токен отдаётся 200 для любого Host — даже для деградировавших CMS-тенантов (maljarka.tandemmebel.ru/rimiz.ru, которые на app-уровне отдают 502/404), т.к. статика обходит CMS-роутинг.

Патч шаблона win-acme

win-acme при filesystem-валидации перезаписывает дочерний web.config своим шаблоном C:\win-acme\Web_Config.xml (а при CleanupFolders=true потом удаляет токен+web.config). Дефолтный шаблон делает <staticContent><clear/> + mime, но не снимает Owin-handler → снова 500.

Durable-фикс = пропатчить сам шаблон C:\win-acme\Web_Config.xml, добавив <remove name="Owin"/>

  • runAllManagedModulesForAllRequests="false". Трекаемая копия: scripts/iis-migration-to-ruvds/winacme-Web_Config.xml. Тогда на каждом renewal win-acme сам кладёт рабочий web.config в (постоянное) unmanaged-приложение, валидирует, чистит. Между renewal'ами папка пустая — трафика на неё нет.

Процедура де-риска (повторяемо)

  1. --validationmode http-01 --validation filesystem локальный probe токена через localhost + Host:-заголовок (обходит home-DPI middlebox).
  2. Staging через --baseuri https://acme-staging-v02.api.letsencrypt.org/ (НЕ --test — он включает интерактивные промпты, которые вешают non-interactive SSH). pemfiles store + --installation none — без правок IIS.
  3. Прод: --store certificatestore --installation iis --installationsiteid 1.

Грабли

  • --test ≠ staging-only. --test тащит интерактив («Try in default browser?», «Quit?») → виснет под SSH. Для staging без интерактива — --baseuri <staging>.
  • pemfiles path должен существовать заранее, иначе abort до валидации.
  • Scheduled task руками, не через win-acme. --notaskscheduler при выпуске; таск создаётся отдельно как SYSTEM (win-acme-овский setup может спросить креды интерактивно). win-acme потом пишет «Scheduled task not configured yet» — косметика, ищет свой таск.
  • win-acme renewal'ы раздельны по --baseuri (разные ConfigurationPath): staging-renewal не трогается прод-таском. Staging-renewal всё равно убран для чистоты.

Команда боевого выпуска (reference)

C:\win-acme\wacs.exe --source iis --siteid 1 --validationmode http-01 --validation filesystem ^
  --webroot C:\sites\snolla --store certificatestore --installation iis --installationsiteid 1 ^
  --accepttos --emailaddress <ops-email> --force --notaskscheduler