mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 12:41:30 +00:00
0729ee46e0
Post-transfer cosmetic + release-critical URL refresh after moving the
repo from github.com/shankar0123/certctl to github.com/certctl-io/certctl
(2026-05-03). GitHub HTTP redirects continue to forward old URLs forever,
so existing operators are not broken — but aligns the canonical
references with the new owner so:
- procurement engineers / contributors browsing the docs see the right
URL on first read
- operators copying the agent install one-liner hit the new path
directly without going through a redirect
- the Helm chart's default image repository points at the canonical org
registry path
- the OnboardingWizard rendered to first-run UI users shows the new
URL in the install snippets and doc anchor links
- the GitHub Actions release workflow pushes container images to
ghcr.io/certctl-io/certctl-{server,agent} (was: shankar0123)
- the release-notes Markdown body in release.yml — which gets stamped
into every future release page — references the post-transfer
cert-identity (cosign keyless signing now uses the certctl-io
workflow URL) and the post-transfer SLSA provenance source-uri.
Without this, every cosign verify / slsa-verifier command on a
v2.1.0+ release would fail because the cert-identity-regexp would
not match the signing identity GitHub Actions OIDC issues post-
transfer. Old releases (v2.0.67 and earlier) keep their immutable
release-notes pointing at the shankar0123 path and remain
verifiable via their own published instructions.
Customer impact:
- Operators on ghcr.io/shankar0123/certctl-{server,agent}:latest
silently freeze on whatever tag was current at transfer time. They
get no errors; they just stop receiving updates. The next release
notes need a one-line callout (Phase 3.1 of cowork/transfer-
certctl-to-org.md) telling them to update their image path to
ghcr.io/certctl-io/certctl-{server,agent}.
- All other URLs (git clone, install one-liner, raw.githubusercontent
URLs, browser links, GitHub API) continue to resolve via permanent
HTTP redirects. The sweep is cosmetic for those.
Files swept (30 total):
.github/workflows/release.yml — IMAGE_NAMESPACE, source-uri,
cosign cert-identity-regexp, IMAGE= snippet (5 refs total).
CHANGELOG.md, README.md — anchor links, badges, install one-liner,
cosign verify snippets in operator-facing sections.
api/openapi.yaml — info / externalDocs URLs.
install-agent.sh — GITHUB_REPO const + systemd unit Documentation=
field.
deploy/ENVIRONMENTS.md, deploy/helm/{CHART_SUMMARY,INDEX,
INSTALLATION,README}.md, deploy/helm/certctl/{Chart.yaml,
README.md,values.yaml}, deploy/helm/examples/values-*.yaml —
chart docs + image repository defaults across dev / prod-ha
overrides.
docs/{certctl-for-cert-manager-users,connector-iis,connectors,
migrate-from-acmesh,migrate-from-certbot,quickstart,test-env,
why-certctl}.md — operator-facing doc URLs.
examples/{acme-nginx,acme-wildcard-dns01,multi-issuer,
private-ca-traefik,step-ca-haproxy}/docker-compose.yml +
examples/step-ca-haproxy/step-ca-haproxy.md — example image:
paths and accompanying narrative.
web/src/pages/OnboardingWizard.tsx — first-run-UI URL refs (curl
install one-liners, agent docker image path, doc anchor links).
Files intentionally NOT swept (Choice A from cowork/transfer-certctl-
to-org.md):
go.mod, go.sum — module declaration stays github.com/shankar0123/
certctl. Existing imports compile because Go uses the path
declared in go.mod, not the URL it was fetched from. Internal-
only project; no external Go consumers; rename will land as a
mechanical sed when one materializes.
~250 *.go files — every import remains github.com/shankar0123/
certctl/internal/...
deploy/test/f5-mock-icontrol/go.mod — separate test sub-module;
same Choice A logic; module path stays.
Files intentionally NOT swept (other reasons):
README.md lines 244-245 — Scarf-pixel docker-pull commands.
shankar0123.docker.scarf.sh/... is a Scarf-account hostname
(per-user, not per-repo) and the pixel keeps tracking pulls
against the operator's personal Scarf account. Migrating to a
certctl-io Scarf account is a separate decision (create org
Scarf account → re-create package → update README).
deploy/test/f5-mock-icontrol/f5-mock-icontrol — checked-in
compiled binary with shankar0123/certctl baked into Go build
info via the sub-module path. Out of scope for a URL sweep;
will refresh on the next `make test-integration` rebuild.
Verification:
gofmt: clean (no .go files touched).
go vet ./...: clean (verified at this SHA in 1.3 of the transfer
checklist; no .go changes since).
go build ./...: clean (same).
go test -short on representative packages: green (same).
Diff shape: 30 files, 74 insertions / 74 deletions, net-zero size,
pure URL substitution.
196 lines
6.6 KiB
Markdown
196 lines
6.6 KiB
Markdown
# Microsoft IIS Connector — Operator Deep-Dive
|
|
|
|
> Per Phase 14 of the deploy-hardening II master bundle.
|
|
|
|
## Overview
|
|
|
|
The IIS connector (`internal/connector/target/iis/`) deploys TLS
|
|
certs to Windows IIS servers via PowerShell (`Import-PfxCertificate`
|
|
+ `New-WebBinding` + SNI binding). Pre-deploy snapshot of the
|
|
existing thumbprint allows rollback if the new binding fails.
|
|
|
|
## Vendor versions tested
|
|
|
|
- **Windows Server 2019** with IIS 10
|
|
- **Windows Server 2022** with IIS 10
|
|
|
|
## CI runner constraint
|
|
|
|
Per frozen decision 0.4: Windows containers run only on Windows
|
|
hosts. Linux CI runners CAN'T run the IIS sidecar. IIS e2e tests
|
|
run on a separate `windows-vendor-e2e` GitHub Actions matrix job
|
|
on `windows-latest` runners. Operators on Linux-only CI use
|
|
`//go:build integration && !no_iis` to skip.
|
|
|
|
## Per-quirk operator guidance
|
|
|
|
### App-pool recycle (opt-in)
|
|
|
|
`TestVendorEdge_IIS_AppPoolRecycle_OptInForCertChange_E2E`
|
|
|
|
By default, IIS picks up new SSL bindings without app-pool
|
|
recycle (the binding-edit path is hot). Some sites need recycle
|
|
to fully reload (e.g., apps that cache cert handles).
|
|
|
|
**Operator action:** set `AppPoolRecycle: true` per-target. The
|
|
connector then runs `Restart-WebAppPool <pool>` after binding update.
|
|
|
|
### SNI multi-binding per site
|
|
|
|
`TestVendorEdge_IIS_SNIMultiBindingPerSite_DeployUpdatesCorrectBinding_E2E`
|
|
|
|
When a site has multiple SNI bindings (different hostnames on
|
|
the same site), connector targets the binding matching the
|
|
operator-supplied hostname. Other bindings unchanged.
|
|
|
|
### CCS (Centralized Certificate Store)
|
|
|
|
`TestVendorEdge_IIS_CCSCentralizedCertStoreVariant_DeployToSharedStore_E2E`
|
|
|
|
CCS is the file-based variant where multiple IIS servers share
|
|
a UNC path of cert files. Connector writes to the shared path;
|
|
all IIS servers pick it up automatically.
|
|
|
|
### WinRM remote vs local PowerShell
|
|
|
|
`TestVendorEdge_IIS_WinRMRemotePath_vs_LocalPowerShellPath_BothWork_E2E`
|
|
|
|
Two code paths produce equivalent cert installs:
|
|
- `WinRMHost: ""` → local PowerShell (agent runs on the IIS server)
|
|
- `WinRMHost: "iis.example"` → remote PowerShell via WinRM
|
|
|
|
Both rotate the same way. WinRM path requires network reachability
|
|
to port 5985/5986.
|
|
|
|
### Server 2019 vs 2022 PowerShell compat
|
|
|
|
`TestVendorEdge_IIS_WindowsServer2019_vs_2022_PowerShellCompat_E2E`
|
|
|
|
`Import-PfxCertificate` + `New-WebBinding` semantics are stable
|
|
across server versions. PowerShell 5.1 (2019) + PowerShell 7.x
|
|
(2022) both work.
|
|
|
|
### Friendly name
|
|
|
|
`TestVendorEdge_IIS_FriendlyNameUpdatedOnRotation_E2E`
|
|
|
|
Connector preserves operator-supplied `FriendlyName` on the cert
|
|
across rotation. Useful for IIS GUI identification.
|
|
|
|
### HTTP/2 + ALPN
|
|
|
|
`TestVendorEdge_IIS_HTTP2ALPNPreserved_E2E`
|
|
|
|
IIS h2 negotiation preserved across cert rotation. The
|
|
`netsh http show sslcert` ALPN attribute survives the binding swap.
|
|
|
|
### Binding-type validation
|
|
|
|
`TestVendorEdge_IIS_BindingTypeHttpsValidated_E2E`
|
|
|
|
Connector refuses to deploy to non-`https` bindings (e.g., `http`,
|
|
`net.tcp`). Surfaces actionable error.
|
|
|
|
### ARR reverse-proxy
|
|
|
|
`TestVendorEdge_IIS_ARRReverseProxyCertRotation_E2E`
|
|
|
|
Sites using Application Request Routing as reverse proxy: cert
|
|
rotation does not invalidate ARR routes. The cert-binding edit
|
|
is independent of the ARR config.
|
|
|
|
### Atomic SNI binding swap
|
|
|
|
`TestVendorEdge_IIS_RemovePreviousBindingOnRotate_E2E`
|
|
|
|
Connector removes the previous SNI binding BEFORE inserting the
|
|
new one (atomicity at the IIS API level). Prevents brief
|
|
window where two bindings serve different certs for the same
|
|
hostname.
|
|
|
|
## Troubleshooting matrix
|
|
|
|
| Symptom | Test name | Operator action |
|
|
|---|---|---|
|
|
| Cert installed but app pool serving old cert | `AppPoolRecycle_OptInForCertChange_E2E` | set `AppPoolRecycle: true` |
|
|
| Wrong SNI binding updated | `SNIMultiBindingPerSite_E2E` | verify hostname selector |
|
|
| Permission denied on cert install | n/a | agent must run as administrator |
|
|
| WinRM connection failed | `WinRMRemotePath_vs_LocalPowerShellPath_E2E` | check WinRM port 5985/5986 reachability |
|
|
| h2 negotiation broken post-rotate | `HTTP2ALPNPreserved_E2E` | re-run `netsh http add sslcert` with `appid + clientcertnegotiation=enable` |
|
|
|
|
## V3-Pro deferrals
|
|
|
|
- IIS Application Initialization module integration (warm cert
|
|
cache after rotation).
|
|
- Azure Key Vault + IIS integration (operator opt-in).
|
|
|
|
## Related docs
|
|
|
|
- [Atomic deploy + post-verify + rollback](deployment-atomicity.md)
|
|
- [Vendor compatibility matrix](deployment-vendor-matrix.md)
|
|
|
|
## Operator validation playbook (Windows host)
|
|
|
|
CI no longer runs the IIS + WinCertStore vendor-e2e tests on every
|
|
push. Per ci-pipeline-cleanup bundle frozen decision 0.5 (which
|
|
revises Bundle II decision 0.4), the Windows matrix was deleted
|
|
because (a) it couldn't physically work on `windows-latest` GitHub
|
|
runners (Docker not started in Windows-containers mode by default;
|
|
`bridge` network driver doesn't exist on Windows Docker — uses
|
|
`nat`), and (b) all IIS + WinCertStore vendor-edge tests are
|
|
`t.Log` placeholder stubs that exercise no IIS-specific behavior.
|
|
|
|
The real IIS connector validation lives in:
|
|
|
|
1. `internal/connector/target/iis/` unit tests (run on Linux in the
|
|
regular Go Build & Test job — already green on every push).
|
|
2. This playbook — operator manual smoke against a real Windows host
|
|
pre-release.
|
|
|
|
### Prerequisites
|
|
|
|
- Windows Server 2019 or 2022 host (or Windows 10/11 Pro with Hyper-V)
|
|
- Docker Desktop in Windows containers mode
|
|
(Settings → "Switch to Windows containers")
|
|
- Go 1.25.9 + git
|
|
|
|
### Procedure
|
|
|
|
```powershell
|
|
# Clone + checkout
|
|
git clone https://github.com/certctl-io/certctl.git
|
|
cd certctl
|
|
git fetch --tags
|
|
git checkout v2.X.0 # whichever release is being validated
|
|
|
|
# Bring up the Windows IIS sidecar
|
|
docker compose --profile deploy-e2e-windows `
|
|
-f deploy/docker-compose.test.yml `
|
|
up -d windows-iis-test
|
|
Start-Sleep -Seconds 30
|
|
|
|
# Run IIS + WinCertStore vendor-edge tests
|
|
$env:INTEGRATION = "1"
|
|
go test -tags integration -race -count=1 `
|
|
-run 'VendorEdge_(IIS|WinCertStore)' `
|
|
./deploy/test/... | Tee-Object -FilePath iis-validation.log
|
|
|
|
# Tear down
|
|
docker compose --profile deploy-e2e-windows `
|
|
-f deploy/docker-compose.test.yml `
|
|
down -v
|
|
```
|
|
|
|
### Acceptance
|
|
|
|
Per Bundle II frozen decision 0.14, the IIS / WinCertStore cells in
|
|
`docs/deployment-vendor-matrix.md` flip from "CI" / "pending" → "✓"
|
|
only when ALL of the following are true:
|
|
|
|
- ≥1 happy-path e2e passes against the real Windows IIS sidecar
|
|
- ≥1 specific-quirk test for that Windows Server version passes
|
|
- This playbook's full procedure ran clean once on a real Windows host
|
|
|
|
Operator records the validation date + Windows Server version in
|
|
`cowork/<bundle>/iis-validation-receipts.md` for audit trail.
|