derive the certificates a server needs from what it routes

The hostnames are already declared, once, in the fabio route tags. A
domains.txt would be a second source of truth free to disagree with what is
actually served, so myos cert reads the tags instead and decides on its own
which name needs a wildcard: one is asked for where a tag uses one, and it
absorbs the concrete names it covers. A wildcard covers a single label, so
a.b.example.org keeps its own certificate.

dehydrated issues them: a shell script, no python, which fits a tool that has
to install on any server. It answers http-01 itself on a port bound to the
loopback and routed by fabio, and delegates dns-01 to a provider hook. The
deploy hook writes the two file names fabio watches for, through a temporary
name so fabio never reads half a certificate.

Fixed on the way: the port parser wanted six spaces of indentation and the
catalogue writes four, so every stack that binds its ports was reported
unbound.
This commit is contained in:
Yann Autissier
2026-09-05 13:41:16 +02:00
parent be777fc9e6
commit f429b8c38d
14 changed files with 355 additions and 9 deletions
+1
View File
@@ -91,3 +91,4 @@ cmd-recreate | host-project | recreate host/consul
cmd-status | host-project | status host/consul
expose-none | host-project | expose host/consul
expose-default | app-git | expose
cert-none | host-project | cert list host/consul
+2
View File
@@ -0,0 +1,2 @@
no routed hostname: nothing to certify
[exit 0]
+4
View File
@@ -0,0 +1,4 @@
WARNING: myos[0] cert-rule-exists: target cert unavailable in app myos
WARNING: myos[0] list-rule-exists: target list unavailable in app myos
WARNING: myos[0] host/consul-rule-exists: target host/consul unavailable in app myos
[exit 0]
+4
View File
@@ -0,0 +1,4 @@
#shellcheck shell=sh
MYOS_CERT_MODE=$1
myos_cert_names() { printf 'urlprefix-*.ipns.ex.org/*\nurlprefix-a.ex.org/*\n' | myos_cert_parse; }
myos_cert_needs_dns && echo yes || echo no
+10
View File
@@ -0,0 +1,10 @@
#shellcheck shell=sh
# myos_cert_names normally reads the compose configuration; here it is replaced
# by a fixed list so the grouping can be checked on its own.
MYOS_CERT_MODE=$1
_fixture=${2-'urlprefix-ipfs.ex.org/*
urlprefix-*.ipns.ex.org/*
urlprefix-a.ipns.ex.org/*
urlprefix-ex.org/*'}
myos_cert_names() { printf '%s' "$_fixture" | myos_cert_parse; }
myos_cert_groups
+2
View File
@@ -0,0 +1,2 @@
#shellcheck shell=sh
printf 'urlprefix-ipfs.ex.org/api/*\nurlprefix-*.ipns.ex.org/*\nurlprefix-ex.org:443/* proto=https\nurlprefix-*/*\n' | myos_cert_parse
+74
View File
@@ -0,0 +1,74 @@
#shellcheck shell=sh
Include lib/core.sh
Include lib/str.sh
Include lib/var.sh
Include lib/tags.sh
Include lib/naming.sh
Include lib/stack.sh
Include lib/cert.sh
# The hostnames a server must certify are already declared in the fabio route
# tags. Reading them there rather than in a domains.txt keeps one source of
# truth, and decides on its own which name needs a wildcard.
Describe 'lib/cert.sh'
Describe 'myos_cert_parse'
one_tag() { printf 'urlprefix-a.ex.org/x/*\n' | myos_cert_parse; }
It 'reads a hostname out of a route tag'
When call one_tag
The output should equal "a.ex.org"
End
It 'drops the path, the port and the options'
When run source spec/unit/cert_parse_helper.sh
The line 1 should equal "*.ipns.ex.org"
The line 2 should equal "ex.org"
The line 3 should equal "ipfs.ex.org"
The lines of output should equal 3
End
End
Describe 'myos_cert_covers'
Parameters
"ex.org" "a.ex.org" success
"ex.org" "ex.org" failure
"ex.org" "a.b.ex.org" failure
"ex.org" "other.org" failure
End
It "*.$1 against $2"
When call myos_cert_covers "$1" "$2"
The status should be "$3"
End
End
Describe 'myos_cert_groups'
It 'asks for a wildcard where a tag uses one, and absorbs what it covers'
When run source spec/unit/cert_groups_helper.sh auto
The line 1 should equal "ipns.ex.org *.ipns.ex.org"
The output should include "ipfs.ex.org"
The output should not include "a.ipns.ex.org *"
End
It 'never asks for a wildcard in per-site mode'
When run source spec/unit/cert_groups_helper.sh per-site
The output should not include "*"
The output should include "a.ipns.ex.org"
End
It 'asks for one per domain in wildcard mode'
When run source spec/unit/cert_groups_helper.sh wildcard
The output should include "ex.org *.ex.org"
End
It 'says nothing when nothing is routed'
When run source spec/unit/cert_groups_helper.sh auto ""
The output should equal ""
End
End
Describe 'myos_cert_needs_dns'
It 'is true when a wildcard is asked for'
When run source spec/unit/cert_dns_helper.sh auto
The output should equal "yes"
End
It 'is false without one'
When run source spec/unit/cert_dns_helper.sh per-site
The output should equal "no"
End
End
End