Skip to content

Commit 0fe66b6

Browse files
test: Copy challtestsrv management API from pebble (#8094)
- Copy https://pkg.go.dev/github.com/letsencrypt/pebble/v2/cmd/pebble-challtestsrv to `test/chall-test-srv` - Rename pebble-challtestsrv to chall-test-srv, consistent with other test server naming in Boulder - Replace Dockerfile go install with Makefile compilation of `chall-test-srv` - Run chall-test-srv from `./bin/chall-test-srv` - Bump `github.com/letsencrypt/challtestsrv` from `v1.2.1` to `v1.3.2` in go.mod - Update boulder-ci GitHub workflow to use `go1.24.1_2025-04-02` Part of #7963
1 parent 13f98da commit 0fe66b6

File tree

22 files changed

+1278
-22
lines changed

22 files changed

+1278
-22
lines changed

.github/workflows/boulder-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
matrix:
3737
# Add additional docker image tags here and all tests will be run with the additional image.
3838
BOULDER_TOOLS_TAG:
39-
- go1.24.1_2025-03-10
39+
- go1.24.1_2025-04-02
4040
# Tests command definitions. Use the entire "docker compose" command you want to run.
4141
tests:
4242
# Run ./test.sh --help for a description of each of the flags.

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ VERSION ?= 1.0.0
66
EPOCH ?= 1
77
MAINTAINER ?= "Community"
88

9-
CMDS = admin boulder ceremony ct-test-srv pardot-test-srv
9+
CMDS = admin boulder ceremony ct-test-srv pardot-test-srv chall-test-srv
1010
CMD_BINS = $(addprefix bin/, $(CMDS) )
1111
OBJECTS = $(CMD_BINS)
1212

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
1717
github.com/jmhodges/clock v1.2.0
1818
github.com/letsencrypt/borp v0.0.0-20240620175310-a78493c6e2bd
19-
github.com/letsencrypt/challtestsrv v1.2.1
19+
github.com/letsencrypt/challtestsrv v1.3.2
2020
github.com/letsencrypt/pkcs11key/v4 v4.0.0
2121
github.com/letsencrypt/validator/v10 v10.0.0-20230215210743-a0c7dfc17158
2222
github.com/miekg/dns v1.1.61

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
159159
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
160160
github.com/letsencrypt/borp v0.0.0-20240620175310-a78493c6e2bd h1:3c+LdlAOEcW1qmG8gtkMCyAEoslmj6XCmniB+926kMM=
161161
github.com/letsencrypt/borp v0.0.0-20240620175310-a78493c6e2bd/go.mod h1:gMSMCNKhxox/ccR923EJsIvHeVVYfCABGbirqa0EwuM=
162-
github.com/letsencrypt/challtestsrv v1.2.1 h1:Lzv4jM+wSgVMCeO5a/F/IzSanhClstFMnX6SfrAJXjI=
163-
github.com/letsencrypt/challtestsrv v1.2.1/go.mod h1:Ur4e4FvELUXLGhkMztHOsPIsvGxD/kzSJninOrkM+zc=
162+
github.com/letsencrypt/challtestsrv v1.3.2 h1:pIDLBCLXR3B1DLmOmkkqg29qVa7DDozBnsOpL9PxmAY=
163+
github.com/letsencrypt/challtestsrv v1.3.2/go.mod h1:Ur4e4FvELUXLGhkMztHOsPIsvGxD/kzSJninOrkM+zc=
164164
github.com/letsencrypt/pkcs11key/v4 v4.0.0 h1:qLc/OznH7xMr5ARJgkZCCWk+EomQkiNTOoOF5LAgagc=
165165
github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag=
166166
github.com/letsencrypt/validator/v10 v10.0.0-20230215210743-a0c7dfc17158 h1:HGFsIltYMUiB5eoFSowFzSoXkocM2k9ctmJ57QMGjys=

test/boulder-tools/Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ RUN curl "https://dl.google.com/go/go${GO_VERSION}.$(echo $TARGETPLATFORM | sed
1313
RUN go install github.com/rubenv/sql-migrate/[email protected]
1414
RUN go install google.golang.org/protobuf/cmd/[email protected]
1515
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@bb9882e6ae58f0a80a6390b50a5ec3bd63e46a3c
16-
RUN go install github.com/letsencrypt/pebble/v2/cmd/pebble-challtestsrv@17d64a3
1716
RUN go install github.com/golangci/golangci-lint/cmd/[email protected]
1817
RUN go install honnef.co/go/tools/cmd/[email protected]
1918
RUN go install github.com/jsha/[email protected]

test/chall-test-srv/README.md

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Boulder Challenge Test Server
2+
3+
**Important note: The `chall-test-srv` command is for TEST USAGE ONLY. It
4+
is trivially insecure, offering no authentication. Only use
5+
`chall-test-srv` in a controlled test environment.**
6+
7+
The standalone `chall-test-srv` binary lets you run HTTP-01, HTTPS HTTP-01,
8+
DNS-01, and TLS-ALPN-01 challenge servers that external programs can add/remove
9+
challenge responses to using a HTTP management API.
10+
11+
For example this is used by the Boulder integration tests to easily add/remove
12+
TXT records for DNS-01 challenges for the `chisel.py` ACME client, and to test
13+
redirect behaviour for HTTP-01 challenge validation.
14+
15+
### Usage
16+
17+
```
18+
Usage of chall-test-srv:
19+
-defaultIPv4 string
20+
Default IPv4 address for mock DNS responses to A queries (default "127.0.0.1")
21+
-defaultIPv6 string
22+
Default IPv6 address for mock DNS responses to AAAA queries (default "::1")
23+
-dns01 string
24+
Comma separated bind addresses/ports for DNS-01 challenges and fake DNS data. Set empty to disable. (default ":8053")
25+
-http01 string
26+
Comma separated bind addresses/ports for HTTP-01 challenges. Set empty to disable. (default ":5002")
27+
-https01 string
28+
Comma separated bind addresses/ports for HTTPS HTTP-01 challenges. Set empty to disable. (default ":5003")
29+
-management string
30+
Bind address/port for management HTTP interface (default ":8055")
31+
-tlsalpn01 string
32+
Comma separated bind addresses/ports for TLS-ALPN-01 and HTTPS HTTP-01 challenges. Set empty to disable. (default ":5001")
33+
```
34+
35+
To disable a challenge type, set the bind address to `""`. E.g.:
36+
37+
* To run HTTP-01 only: `chall-test-srv -https01 "" -dns01 "" -tlsalpn01 ""`
38+
* To run HTTPS-01 only: `chall-test-srv -http01 "" -dns01 "" -tlsalpn01 ""`
39+
* To run DNS-01 only: `chall-test-srv -http01 "" -https01 "" -tlsalpn01 ""`
40+
* To run TLS-ALPN-01 only: `chall-test-srv -http01 "" -https01 "" -dns01 ""`
41+
42+
### Management Interface
43+
44+
_Note: These examples assume the default `-management` interface address, `:8055`._
45+
46+
#### Mock DNS
47+
48+
##### Default A/AAAA Responses
49+
50+
You can set the default IPv4 and IPv6 addresses used for `A` and `AAAA` query
51+
responses using the `-defaultIPv4` and `-defaultIPv6` command line flags.
52+
53+
To change the default IPv4 address used for responses to `A` queries that do not
54+
match explicit mocks at runtime run:
55+
56+
curl -d '{"ip":"10.10.10.2"}' http://localhost:8055/set-default-ipv4
57+
58+
Similarly to change the default IPv6 address used for responses to `AAAA` queries
59+
that do not match explicit mocks run:
60+
61+
curl -d '{"ip":"::1"}' http://localhost:8055/set-default-ipv6
62+
63+
To clear the default IPv4 or IPv6 address POST the same endpoints with an empty
64+
(`""`) IP.
65+
66+
##### Mocked A/AAAA Responses
67+
68+
To add IPv4 addresses to be returned for `A` queries for
69+
`test-host.letsencrypt.org` run:
70+
71+
curl -d '{"host":"test-host.letsencrypt.org", "addresses":["12.12.12.12", "13.13.13.13"]}' http://localhost:8055/add-a
72+
73+
The mocked `A` responses can be removed by running:
74+
75+
curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/clear-a
76+
77+
To add IPv6 addresses to be returned for `AAAA` queries for
78+
`test-host.letsencrypt.org` run:
79+
80+
curl -d '{"host":"test-host.letsencrypt.org", "addresses":["2001:4860:4860::8888", "2001:4860:4860::8844"]}' http://localhost:8055/add-aaaa
81+
82+
The mocked `AAAA` responses can be removed by running:
83+
84+
curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/clear-aaaa
85+
86+
##### Mocked CAA Responses
87+
88+
To add a mocked CAA policy for `test-host.letsencrypt.org` that allows issuance
89+
by `letsencrypt.org` run:
90+
91+
curl -d '{"host":"test-host.letsencrypt.org", "policies":[{"tag":"issue","value":"letsencrypt.org"}]}' http://localhost:8055/add-caa
92+
93+
To remove the mocked CAA policy for `test-host.letsencrypt.org` run:
94+
95+
curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/clear-caa
96+
97+
##### Mocked CNAME Responses
98+
99+
To add a mocked CNAME record for `_acme-challenge.test-host.letsencrypt.org` run:
100+
101+
curl -d '{"host":"_acme-challenge.test-host.letsencrypt.org", "target": "challenges.letsencrypt.org"}' http://localhost:8055/set-cname
102+
103+
To remove a mocked CNAME record for `_acme-challenge.test-host.letsencrypt.org` run:
104+
105+
curl -d '{"host":"_acme-challenge.test-host.letsencrypt.org", "target": "challenges.letsencrypt.org"}' http://localhost:8055/clear-cname
106+
107+
##### Mocked SERVFAIL Responses
108+
109+
To configure the DNS server to return SERVFAIL for all queries for `test-host.letsencrypt.org` run:
110+
111+
curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/set-servfail
112+
113+
Subsequently any query types (A, AAAA, TXT) for the name will return a SERVFAIL response, overriding any A/AAAA/TXT/CNAME mocks that may also be configured.
114+
115+
To remove the SERVFAIL configuration for `test-host.letsencrypt.org` run:
116+
117+
curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/clear-servfail
118+
119+
#### HTTP-01
120+
121+
To add an HTTP-01 challenge response for the token `"aaaa"` with the content `"bbbb"` run:
122+
123+
curl -d '{"token":"aaaa", "content":"bbbb"}' http://localhost:8055/add-http01
124+
125+
Afterwards the challenge response will be available over HTTP at
126+
`http://localhost:5002/.well-known/acme-challenge/aaaa`, and HTTPS at
127+
`https://localhost:5002/.well-known/acme-challenge/aaaa`.
128+
129+
The HTTP-01 challenge response for the `"aaaa"` token can be deleted by running:
130+
131+
curl -d '{"token":"aaaa"}' http://localhost:8055/del-http01
132+
133+
##### Redirects
134+
135+
To add a redirect from `/.well-known/acme-challenge/whatever` to
136+
`https://localhost:5003/ok` run:
137+
138+
curl -d '{"path":"/.well-known/whatever", "targetURL": "https://localhost:5003/ok"}' http://localhost:8055/add-redirect
139+
140+
Afterwards HTTP requests to `http://localhost:5002/.well-known/whatever/` will
141+
be redirected to `https://localhost:5003/ok`. HTTPS requests that match the
142+
path will not be served a redirect to prevent loops when redirecting the same
143+
path from HTTP to HTTPS.
144+
145+
To remove the redirect run:
146+
147+
curl -d '{"path":"/.well-known/whatever"}' http://localhost:8055/del-redirect
148+
149+
#### DNS-01
150+
151+
To add a DNS-01 challenge response for `_acme-challenge.test-host.letsencrypt.org` with
152+
the value `"foo"` run:
153+
154+
curl -d '{"host":"_acme-challenge.test-host.letsencrypt.org.", "value": "foo"}' http://localhost:8055/set-txt
155+
156+
To remove the mocked DNS-01 challenge response run:
157+
158+
curl -d '{"host":"_acme-challenge.test-host.letsencrypt.org."}' http://localhost:8055/clear-txt
159+
160+
Note that a period character is required at the end of the host name here.
161+
162+
#### TLS-ALPN-01
163+
164+
To add a TLS-ALPN-01 challenge response certificate for the host
165+
`test-host.letsencrypt.org` with the key authorization `"foo"` run:
166+
167+
curl -d '{"host":"test-host.letsencrypt.org", "content":"foo"}' http://localhost:8055/add-tlsalpn01
168+
169+
To remove the mocked TLS-ALPN-01 challenge response run:
170+
171+
curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/del-tlsalpn01
172+
173+
#### Request History
174+
175+
`chall-test-srv` keeps track of the requests processed by each of the
176+
challenge servers and exposes this information via JSON.
177+
178+
To get the history of HTTP requests to `example.com` run:
179+
180+
curl -d '{"host":"example.com"}' http://localhost:8055/http-request-history
181+
182+
Each HTTP request event is an object of the form:
183+
```
184+
{
185+
"URL": "/test-whatever/dude?token=blah",
186+
"Host": "example.com",
187+
"HTTPS": true,
188+
"ServerName": "example-sni.com"
189+
}
190+
```
191+
If the HTTP request was over the HTTPS interface then HTTPS will be true and the
192+
ServerName field will be populated with the SNI value sent by the client in the
193+
initial TLS hello.
194+
195+
To get the history of DNS requests for `example.com` run:
196+
197+
curl -d '{"host":"example.com"}' http://localhost:8055/dns-request-history
198+
199+
Each DNS request event is an object of the form:
200+
```
201+
{
202+
"Question": {
203+
"Name": "example.com.",
204+
"Qtype": 257,
205+
"Qclass": 1
206+
}
207+
}
208+
```
209+
210+
To get the history of TLS-ALPN-01 requests for the SNI host `example.com` run:
211+
212+
curl -d '{"host":"example.com"}' http://localhost:8055/tlsalpn01-request-history
213+
214+
Each TLS-ALPN-01 request event is an object of the form:
215+
```
216+
{
217+
"ServerName": "example.com",
218+
"SupportedProtos": [
219+
"dogzrule"
220+
]
221+
}
222+
```
223+
The ServerName field is populated with the SNI value sent by the client in the
224+
initial TLS hello. The SupportedProtos field is set with the advertised
225+
supported next protocols from the initial TLS hello.
226+
227+
To clear HTTP request history for `example.com` run:
228+
229+
curl -d '{"host":"example.com", "type":"http"}' http://localhost:8055/clear-request-history
230+
231+
Similarly, to clear DNS request history for `example.com` run:
232+
233+
curl -d '{"host":"example.com", "type":"dns"}' http://localhost:8055/clear-request-history
234+
235+
And to clear TLS-ALPN-01 request history for `example.com` run:
236+
237+
curl -d '{"host":"example.com", "type":"tlsalpn"}' http://localhost:8055/clear-request-history

test/chall-test-srv/dnsone.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package main
2+
3+
import "net/http"
4+
5+
// addDNS01 handles an HTTP POST request to add a new DNS-01 challenge TXT
6+
// record for a given host/value.
7+
//
8+
// The POST body is expected to have two non-empty parameters:
9+
// "host" - the hostname to add the mock TXT response under.
10+
// "value" - the key authorization value to return in the TXT response.
11+
//
12+
// A successful POST will write http.StatusOK to the client.
13+
func (srv *managementServer) addDNS01(w http.ResponseWriter, r *http.Request) {
14+
// Unmarshal the request body JSON as a request object
15+
var request struct {
16+
Host string
17+
Value string
18+
}
19+
if err := mustParsePOST(&request, r); err != nil {
20+
http.Error(w, err.Error(), http.StatusBadRequest)
21+
return
22+
}
23+
24+
// If the request has an empty host or value it's a bad request
25+
if request.Host == "" || request.Value == "" {
26+
w.WriteHeader(http.StatusBadRequest)
27+
return
28+
}
29+
30+
// Add the DNS-01 challenge response TXT to the challenge server
31+
srv.challSrv.AddDNSOneChallenge(request.Host, request.Value)
32+
srv.log.Printf("Added DNS-01 TXT challenge for Host %q - Value %q\n",
33+
request.Host, request.Value)
34+
w.WriteHeader(http.StatusOK)
35+
}
36+
37+
// delDNS01 handles an HTTP POST request to delete an existing DNS-01 challenge
38+
// TXT record for a given host.
39+
//
40+
// The POST body is expected to have one non-empty parameter:
41+
// "host" - the hostname to remove the mock TXT response for.
42+
//
43+
// A successful POST will write http.StatusOK to the client.
44+
func (srv *managementServer) delDNS01(w http.ResponseWriter, r *http.Request) {
45+
// Unmarshal the request body JSON as a request object
46+
var request struct {
47+
Host string
48+
}
49+
if err := mustParsePOST(&request, r); err != nil {
50+
http.Error(w, err.Error(), http.StatusBadRequest)
51+
return
52+
}
53+
54+
// If the request has an empty host value it's a bad request
55+
if request.Host == "" {
56+
w.WriteHeader(http.StatusBadRequest)
57+
return
58+
}
59+
60+
// Delete the DNS-01 challenge response TXT for the given host from the
61+
// challenge server
62+
srv.challSrv.DeleteDNSOneChallenge(request.Host)
63+
srv.log.Printf("Removed DNS-01 TXT challenge for Host %q\n", request.Host)
64+
w.WriteHeader(http.StatusOK)
65+
}

0 commit comments

Comments
 (0)