Files
admin/.wiki/concepts/traefik-acme-json-to-iis-cert-import.md
vitya 22786e1865 wiki(ingest): RUVDS IIS migration + daily backup pipeline
- entities/ruvds-iis-host (NEW) — 80.64.31.36, Win Server 2025 Core,
  25 SNI HTTPS bindings, 2/24 hostnames DNS-flipped
- sources/iis-migration-to-ruvds-2026-05-23 (NEW) — chronology,
  SSH/scp pivot после home-ISP outbound 445 block
- sources/ruvds-backup-daily-kreknin-2026-05-24 (NEW) — rclone+SFTP
  SYSTEM task daily 04:30, ntfy общий канал
- concepts/traefik-acme-json-to-iis-cert-import (NEW) — PFX + SNI
  recipe
- concepts/windows-server-2025-core-bootstrap — SMB deprecate,
  HTTP middlebox warning, HTTP/2 note; backup/cert open-Qs закрыты
- entities/windows-recovery-host — partial-cutover state,
  imgproxy SPOF carve-out, tandemmebel indefinitely здесь
- overview / index / log — catalog refresh

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 23:31:14 +03:00

7.2 KiB
Raw Blame History

title, type, tags, sources, updated
title type tags sources updated
Экспорт LE certs из traefik acme.json в IIS (PFX + SNI bindings) concept
traefik
iis
letsencrypt
certs
pfx
sni
windows
../sources/iis-migration-to-ruvds-2026-05-23.md
2026-05-24

traefik acme.json → IIS cert import

Recipe для one-shot переноса Let's Encrypt certs из traefik acme.json (single-file ACME store) в IIS на Windows host с SNI multi-binding'ами. Использован при миграции ../entities/ruvds-iis-host 2026-05-23 для 14 LE certs → 25 HTTPS hostnames bound via SNI. Это не renewal pipeline, а bootstrap-перенос — для long-term renewal recommend win-acme standalone на IIS host'е (см. footnote).

Когда применять

  • Migration: traefik-on-A → IIS-on-B, certs нужны до того как ACME-validation с нового host'а станет возможной (DNS ещё указывает на старый host, HTTP-01 challenge bounce'нется).
  • Bootstrap пилотного IIS-сервера с готовыми certs для smoke testing до DNS swap'а.

Не применять для regular renewal — каждый renewal цикл traefik будет генерировать новый cert, и manual re-export не масштабируется. После full DNS swap → win-acme HTTP-01 на IIS-side directly.

Входные данные

  • acme.json — обычно в <traefik-data>/letsencrypt/acme.json. Содержит JSON с массивом Certificates[].domain.main, .sans, .certificate (base64 PEM bundle), .key (base64 PEM private key).
  • Target IIS host с PowerShell + OpenSSL (для cert conversion) + access to Cert:\LocalMachine\My store.

Recipe (per cert)

1. Распаковать пару PEM из acme.json

$acme = Get-Content C:\path\to\acme.json | ConvertFrom-Json
# Resolver key — обычно "letsencrypt" или имя из traefik.yml
$resolver = $acme.letsencrypt
foreach ($cert in $resolver.Certificates) {
    $domain = $cert.domain.main
    $sans = $cert.domain.sans  # может быть $null
    $safeName = $domain -replace '\*','wildcard'

    [System.Convert]::FromBase64String($cert.certificate) | Set-Content "C:\temp\$safeName.crt" -AsByteStream
    [System.Convert]::FromBase64String($cert.key)         | Set-Content "C:\temp\$safeName.key" -AsByteStream
}

2. Convert PEM pair → PFX (OpenSSL)

$pfxPass = 'pfximport'  # temp passphrase, only for Import step
& openssl pkcs12 -export `
    -inkey "C:\temp\$safeName.key" `
    -in    "C:\temp\$safeName.crt" `
    -out   "C:\temp\$safeName.pfx" `
    -name  $domain `
    -password "pass:$pfxPass"

3. Import PFX → Cert:\LocalMachine\My

$securePass = ConvertTo-SecureString $pfxPass -AsPlainText -Force
$imported = Import-PfxCertificate `
    -FilePath "C:\temp\$safeName.pfx" `
    -CertStoreLocation 'Cert:\LocalMachine\My' `
    -Password $securePass
# $imported.Thumbprint — нужен для следующего шага

4. SNI binding в IIS (per hostname)

Один cert может покрывать несколько SANs — для каждого hostname создать separate binding с SslFlags=1 (SNI):

$allHosts = @($domain) + @($sans | Where-Object { $_ })
foreach ($hostname in $allHosts) {
    # 4a. Create binding without cert
    New-WebBinding -Name 'snolla' `
        -IPAddress '*' -Port 443 -Protocol 'https' `
        -HostHeader $hostname -SslFlags 1   # 1 = SNI

    # 4b. Attach cert via thumbprint (нужен netsh-стиль через IIS:\)
    $binding = Get-WebBinding -Name 'snolla' -Port 443 -HostHeader $hostname
    $binding.AddSslCertificate($imported.Thumbprint, 'My')
}

SslFlags=1 обязателен. Без SNI один port 443 = один cert per IP — Windows откажется bind'ить второй cert на тот же *:443. С SNI можно навесить ~unlimited hostnames на один *:443.

Verification

# Список bindings:
Get-WebBinding -Port 443 | Format-Table protocol, bindingInformation, sslFlags

# Сертификат за binding:
$binding.attributes['certificateHash'].Value     # thumbprint
$binding.attributes['certificateStoreName'].Value # "My"

# External smoke (с другого host'а — home middlebox может mangle Host header):
curl -k --resolve "$hostname:443:80.64.31.36" "https://$hostname/" -I
# Должен вернуть chain LE R13 + correct Subject CN

HTTP/2 negotiate'ится auto (IIS 10 + Windows Server 2019+ → ALPN supported из коробки).

Gotchas

  • acme.json permission на Linux: chmod 600, traefik не запустится с другим. На Windows copy-out для conversion'а — нужны Admin perms.
  • PEM bundle order в acme.json: certificate поле содержит leaf + chain (Let's Encrypt R13 + ISRG Root X1). OpenSSL pkcs12 импортит весь bundle корректно — IIS отдаст полный chain.
  • PFX password — только для transit между PEM и cert store, после import не используется. pfximport (или любой placeholder) — OK; не путать с pass для encrypted PFX-on-disk storage.
  • HostHeader case-sensitivity в New-WebBinding: IIS lowercase'ит автоматически, но скрипт принимай canonical lowercase.
  • Wildcard SANs: если cert *.example.com — нужно отдельный binding для каждого конкретного subdomain'а (Windows не делает wildcard matching на binding'ах автоматически). Либо использовать default-cert на listener'е (netsh http add sslcert) — но это break'нет multi-cert SNI scenario.
  • Cert duplicates: повторный import того же cert'а создаёт duplicate в Cert:\LocalMachine\My. Idempotency — check thumbprint exists перед import'ом.
  • netsh http show sslcert — для low-level audit что binding'и реально привязаны (PowerShell Get-WebBinding иногда показывает stale state после ручных правок IIS Manager'ом).

Limits / когда recipe мал

  • >30 hostnamesImport-PfxCertificate + New-WebBinding цикл становится медленным (~5 sec/cert). Batch'ить через appcmd add binding /commit:apphost.
  • TLS 1.3 — IIS 10 на Server 2022+ supports; на Server 2019 — Edge case. Не зависит от recipe — от Windows feature.
  • Renewal — этот recipe не покрывает. Variant A (win-acme HTTP-01 standalone) предпочтительнее, см. ../sources/iis-migration-to-ruvds-2026-05-23 Outstanding (post-soak).

Cross-refs