Skip to content

Commit 901f997

Browse files
committed
fix(traefik): use tls acme challenge
1 parent 73afb96 commit 901f997

5 files changed

Lines changed: 44 additions & 5 deletions

File tree

ansible/roles/traefik/templates/traefik.yml.j2

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ certificatesResolvers:
1818
acme:
1919
email: {{ terrarium_acme_email }}
2020
storage: /var/lib/traefik/acme.json
21-
httpChallenge:
22-
entryPoint: web
21+
tlsChallenge: {}
2322

2423
log:
2524
level: INFO

ansible/site.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,12 +349,41 @@
349349
delay: 10
350350
until: terrarium_auth_public_tls_retry.rc == 0
351351
changed_when: false
352+
failed_when: false
352353
when:
353354
- terrarium_idp_mode == 'local'
354355
- terrarium_auth_domain | default('') | length > 0
355356
- terrarium_auth_public_tls is defined
356357
- terrarium_auth_public_tls.rc | default(0) != 0
357358

359+
- name: Show local auth TLS diagnostics after ACME retry failure
360+
ansible.builtin.shell: |
361+
set +e
362+
echo "== presented certificate =="
363+
timeout 10s openssl s_client -connect 127.0.0.1:443 -servername "{{ terrarium_auth_domain }}" -showcerts </dev/null 2>/dev/null \
364+
| openssl x509 -noout -subject -issuer -ext subjectAltName 2>&1 || true
365+
echo
366+
echo "== Traefik static TLS resolver config =="
367+
sed -n '/certificatesResolvers:/,$p' "{{ terrarium_traefik_config_dir }}/traefik.yml" 2>&1 || true
368+
echo
369+
echo "== recent Traefik ACME logs =="
370+
journalctl -u traefik --no-pager -n 300 2>&1 | grep -Ei 'acme|certificate|challenge|letsencrypt|unable|error|self-signed' || true
371+
changed_when: false
372+
when:
373+
- terrarium_idp_mode == 'local'
374+
- terrarium_auth_domain | default('') | length > 0
375+
- terrarium_auth_public_tls_retry is defined
376+
- terrarium_auth_public_tls_retry.rc | default(0) != 0
377+
378+
- name: Fail when local auth domain still does not serve public TLS
379+
ansible.builtin.fail:
380+
msg: "Local auth domain {{ terrarium_auth_domain }} still does not serve public TLS after retiring bootstrap TLS; see Traefik ACME diagnostics above."
381+
when:
382+
- terrarium_idp_mode == 'local'
383+
- terrarium_auth_domain | default('') | length > 0
384+
- terrarium_auth_public_tls_retry is defined
385+
- terrarium_auth_public_tls_retry.rc | default(0) != 0
386+
358387
- name: Enable Traefik sync timer after dependent services are configured
359388
ansible.builtin.systemd:
360389
name: terrarium-traefik-sync.timer

scripts/terrarium-traefik-sync.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,9 +1328,7 @@ function buildStaticConfig(config: Record<string, unknown>, extraEntrypoints: Re
13281328
acme: {
13291329
email: configString(config, "terrarium_acme_email") || configString(config, "terrarium_email"),
13301330
storage: "/var/lib/traefik/acme.json",
1331-
httpChallenge: {
1332-
entryPoint: "web"
1333-
}
1331+
tlsChallenge: {}
13341332
}
13351333
}
13361334
},

tests/ansible-bootstrap-cert.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@ describe("Traefik bootstrap certificate template", () => {
2525
test("limits bootstrap TLS to local-IDP auth and removes it when unused", () => {
2626
const tasks = readFileSync(join(repoRoot, "ansible/roles/traefik/tasks/main.yml"), "utf8");
2727
const playbook = readFileSync(join(repoRoot, "ansible/site.yml"), "utf8");
28+
const traefikConfig = readFileSync(join(repoRoot, "ansible/roles/traefik/templates/traefik.yml.j2"), "utf8");
2829
const certConfig = readFileSync(join(repoRoot, "ansible/roles/traefik/templates/bootstrap-cert.yml.j2"), "utf8");
2930
const dynamicConfig = readFileSync(join(repoRoot, "ansible/roles/traefik/templates/terrarium-dynamic.yml.j2"), "utf8");
3031
const bootstrapRoutes = readFileSync(join(repoRoot, "ansible/roles/traefik/templates/bootstrap-routes.yml.j2"), "utf8");
3132

3233
expect(tasks).toContain("[terrarium_auth_domain] if terrarium_bootstrap_tls_enabled else []");
3334
expect(tasks).not.toContain("'*.' ~ terrarium_bootstrap_tls_root_domain");
35+
expect(traefikConfig).toContain("tlsChallenge: {}");
36+
expect(traefikConfig).not.toContain("httpChallenge:");
3437
expect(certConfig).toContain("certificates:");
3538
expect(certConfig).not.toContain("defaultCertificate");
3639
expect(dynamicConfig).not.toContain("zitadel-root-bootstrap");
@@ -51,6 +54,9 @@ describe("Traefik bootstrap certificate template", () => {
5154
expect(playbook).toContain("Wait for local auth domain to serve public TLS");
5255
expect(playbook).toContain("Restart Traefik to retry local auth ACME after public TLS wait failure");
5356
expect(playbook).toContain("Wait again for local auth domain to serve public TLS after ACME retry");
57+
expect(playbook).toContain("Show local auth TLS diagnostics after ACME retry failure");
58+
expect(playbook).toContain("Fail when local auth domain still does not serve public TLS");
59+
expect(playbook).toContain("journalctl -u traefik");
5460
expect(playbook).toContain("terrarium_bootstrap_tls_removed is changed");
5561
expect(playbook.indexOf("Remove local auth bootstrap TLS material before requesting public TLS")).toBeLessThan(
5662
playbook.indexOf("Wait for local auth domain to serve public TLS")

tests/terrarium-traefik-sync.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ describe("terrarium route auth generation", () => {
7676
expect(source).toContain("readLocalZitadelPat(config)");
7777
});
7878

79+
test("uses TLS-ALPN ACME challenges in generated Traefik static config", () => {
80+
const source = readFileSync(join(repoRoot, "scripts/terrarium-traefik-sync.ts"), "utf8");
81+
82+
expect(source).toContain("tlsChallenge: {}");
83+
expect(source).not.toContain("httpChallenge:");
84+
});
85+
7986
test("treats ZITADEL no-op updates as successful idempotent responses", () => {
8087
expect(
8188
isZitadelNoChangesResponse(

0 commit comments

Comments
 (0)