# Pebble Challenge Test Server

**Important note: The `pebble-challtestsrv` command is for TEST USAGE ONLY. It
is trivially insecure, offering no authentication. Only use
`pebble-challtestsrv` in a controlled test environment.**

The standalone `pebble-challtestsrv` binary lets you run HTTP-01, HTTPS HTTP-01,
DNS-01, and TLS-ALPN-01 challenge servers that external programs can add/remove
challenge responses to using a HTTP management API.

For example this is used by the Boulder integration tests to easily add/remove
TXT records for DNS-01 challenges for the `chisel.py` ACME client, and to test
redirect behaviour for HTTP-01 challenge validation.

### Usage

```
Usage of pebble-challtestsrv:
  -defaultIPv4 string
    Default IPv4 address for DNS responses to A queries (default "127.0.0.1")
  -defaultIPv6 string
    Default IPv6 address for DNS responses to AAAA queries (default "::1")
  -dnsserver string
    Comma separated bind addresses/ports for serving DNS queries. Set empty to disable. (default ":8053")
  -http01 string
    Comma separated bind addresses/ports for HTTP-01 challenges. Set empty to disable. (default ":5002")
  -https01 string
    Comma separated bind addresses/ports for HTTPS HTTP-01 challenges. Set empty to disable. (default ":5003")
  -management string
    Bind address/port for management HTTP interface (default ":8055")
  -tlsalpn01 string
    Comma separated bind addresses/ports for TLS-ALPN-01 and HTTPS HTTP-01 challenges. Set empty to disable. (default ":5001")
```

To disable a challenge type, set the bind address to `""`. E.g.:

* To run HTTP-01 only: `pebble-challtestsrv -https01 "" -dnsserver "" -tlsalpn01 ""`
* To run HTTPS-01 only: `pebble-challtestsrv -http01 "" -dnsserver "" -tlsalpn01 ""`
* To run DNS-01 only: `pebble-challtestsrv -http01 "" -https01 "" -tlsalpn01 ""`
* To run TLS-ALPN-01 only: `pebble-challtestsrv -http01 "" -https01 "" -dnsserver ""`

### Management Interface

_Note: These examples assume the default `-management` interface address, `:8055`._

#### DNS

##### Default A/AAAA Responses

You can set the default IPv4 and IPv6 addresses used for `A` and `AAAA` query
responses using the `-defaultIPv4` and `-defaultIPv6` command line flags.

To change the default IPv4 address used for responses to `A` queries that do not
match explicit records at runtime, run:

    curl -d '{"ip":"10.10.10.2"}' http://localhost:8055/set-default-ipv4

Similarly to change the default IPv6 address used for responses to `AAAA` queries
that do not match explicit records, run:

    curl -d '{"ip":"::1"}' http://localhost:8055/set-default-ipv6

To clear the default IPv4 or IPv6 address POST the same endpoints with an empty
(`""`) IP.

##### A/AAAA Responses

To add IPv4 addresses to be returned for `A` queries for
`test-host.letsencrypt.org` run:

    curl -d '{"host":"test-host.letsencrypt.org", "addresses":["12.12.12.12", "13.13.13.13"]}' http://localhost:8055/add-a

The `A` responses can be removed by running:

    curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/clear-a

To add IPv6 addresses to be returned for `AAAA` queries for
`test-host.letsencrypt.org` run:

    curl -d '{"host":"test-host.letsencrypt.org", "addresses":["2001:4860:4860::8888", "2001:4860:4860::8844"]}' http://localhost:8055/add-aaaa

The `AAAA` responses can be removed by running:

    curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/clear-aaaa

##### CAA Responses

To add a CAA policy for `test-host.letsencrypt.org` that allows issuance
by `letsencrypt.org` run:

    curl -d '{"host":"test-host.letsencrypt.org", "policies":[{"tag":"issue","value":"letsencrypt.org"}]}' http://localhost:8055/add-caa

To remove the CAA policy for `test-host.letsencrypt.org` run:

    curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/clear-caa

##### CNAME Responses

To add a CNAME record for `_acme-challenge.test-host.letsencrypt.org` run:

    curl -d '{"host":"_acme-challenge.test-host.letsencrypt.org", "target": "challenges.letsencrypt.org"}' http://localhost:8055/set-cname

To remove a CNAME record for `_acme-challenge.test-host.letsencrypt.org` run:

    curl -d '{"host":"_acme-challenge.test-host.letsencrypt.org", "target": "challenges.letsencrypt.org"}' http://localhost:8055/clear-cname

##### SERVFAIL Responses

To configure the DNS server to return SERVFAIL for all queries for `test-host.letsencrypt.org` run:

    curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/set-servfail

Subsequently any query types (A, AAAA, TXT) for the name will return a SERVFAIL response, overriding any configured A/AAAA/TXT/CNAME records.

To remove the SERVFAIL configuration for `test-host.letsencrypt.org` run:

    curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/clear-servfail

#### HTTP-01

To add an HTTP-01 challenge response for the token `"aaaa"` with the content `"bbbb"` run:

    curl -d '{"token":"aaaa", "content":"bbbb"}' http://localhost:8055/add-http01

Afterwards the challenge response will be available over HTTP at
`http://localhost:5002/.well-known/acme-challenge/aaaa`, and HTTPS at
`https://localhost:5002/.well-known/acme-challenge/aaaa`.

The HTTP-01 challenge response for the `"aaaa"` token can be deleted by running:

    curl -d '{"token":"aaaa"}' http://localhost:8055/del-http01

##### Redirects

To add a redirect from `/.well-known/acme-challenge/whatever` to
`https://localhost:5003/ok` run:

    curl -d '{"path":"/.well-known/whatever", "targetURL": "https://localhost:5003/ok"}' http://localhost:8055/add-redirect

Afterwards HTTP requests to `http://localhost:5002/.well-known/whatever/` will
be redirected to `https://localhost:5003/ok`. HTTPS requests that match the
path will not be served a redirect to prevent loops when redirecting the same
path from HTTP to HTTPS.

To remove the redirect run:

    curl -d '{"path":"/.well-known/whatever"}' http://localhost:8055/del-redirect

#### DNS-01

To add a DNS-01 challenge response for `_acme-challenge.test-host.letsencrypt.org` with
the value `"foo"` run:

    curl -d '{"host":"_acme-challenge.test-host.letsencrypt.org.", "value": "foo"}' http://localhost:8055/set-txt

To remove the DNS-01 challenge response run:

    curl -d '{"host":"_acme-challenge.test-host.letsencrypt.org."}' http://localhost:8055/clear-txt

Note that a period character is required at the end of the host name here.

#### TLS-ALPN-01

To add a TLS-ALPN-01 challenge response certificate for the host
`test-host.letsencrypt.org` with the key authorization `"foo"` run:

    curl -d '{"host":"test-host.letsencrypt.org", "content":"foo"}' http://localhost:8055/add-tlsalpn01

To remove the TLS-ALPN-01 challenge response run:

    curl -d '{"host":"test-host.letsencrypt.org"}' http://localhost:8055/del-tlsalpn01

#### Request History

`pebble-challtestsrv` keeps track of the requests processed by each of the
challenge servers and exposes this information via JSON.

To get the history of HTTP requests to `example.com` run:

    curl -d '{"host":"example.com"}' http://localhost:8055/http-request-history

Each HTTP request event is an object of the form:
```
   {
      "URL": "/test-whatever/dude?token=blah",
      "Host": "example.com",
      "HTTPS": true,
      "ServerName": "example-sni.com"
   }
```
If the HTTP request was over the HTTPS interface then HTTPS will be true and the
ServerName field will be populated with the SNI value sent by the client in the
initial TLS hello.

To get the history of DNS requests for `example.com` run:

    curl -d '{"host":"example.com"}' http://localhost:8055/dns-request-history

Each DNS request event is an object of the form:
```
   {
      "Question": {
         "Name": "example.com.",
         "Qtype": 257,
         "Qclass": 1
      }
   }
```

To get the history of TLS-ALPN-01 requests for the SNI host `example.com` run:

    curl -d '{"host":"example.com"}' http://localhost:8055/tlsalpn01-request-history

Each TLS-ALPN-01 request event is an object of the form:
```
   {
      "ServerName": "example.com",
      "SupportedProtos": [
         "dogzrule"
      ]
   }
```
The ServerName field is populated with the SNI value sent by the client in the
initial TLS hello. The SupportedProtos field is set with the advertised
supported next protocols from the initial TLS hello.

To clear HTTP request history for `example.com` run:

    curl -d '{"host":"example.com", "type":"http"}' http://localhost:8055/clear-request-history

Similarly, to clear DNS request history for `example.com` run:

    curl -d '{"host":"example.com", "type":"dns"}' http://localhost:8055/clear-request-history

And to clear TLS-ALPN-01 request history for `example.com` run:

    curl -d '{"host":"example.com", "type":"tlsalpn"}' http://localhost:8055/clear-request-history
