Coder Social home page Coder Social logo

letsencrypt / pebble Goto Github PK

View Code? Open in Web Editor NEW
600.0 26.0 147.0 4.82 MB

A miniature version of Boulder, Pebble is a small RFC 8555 ACME test server not suited for a production certificate authority.

License: Mozilla Public License 2.0

Go 96.64% Python 3.36%
acme rfc-8555 letsencrypt acme-server testing certificate-authority pki https x509

pebble's Introduction

Pebble

Checks Tests

Coverage Status Go Report Card

A miniature version of Boulder, Pebble is a small ACME test server not suited for use as a production CA.

!!! WARNING !!!

WARNING

Pebble is NOT INTENDED FOR PRODUCTION USE. Pebble is for testing only.

By design Pebble will drop all of its state between invocations and will randomize keys/certificates used for issuance.

Goals

Pebble has several top level goals:

  1. Provide a simplified ACME testing front end
  2. Provide a test-bed for new and compatibility breaking ACME features
  3. Encourage ACME client best-practices
  4. Aggressively build in guardrails against non-testing usage

Pebble aims to address the need for ACME clients to have an easier to use, self-contained version of Boulder to test their clients against while developing ACME v2 support. Boulder is multi-process, requires heavy dependencies (MariaDB, gRPC, etc), and is operationally complex to integrate with other projects.

Where possible Pebble aims to be a test-bed for new ACME protocol features that can be used to inform later Boulder support. Pebble provides a way for Boulder developers to test compatibility breaking changes more aggressively than is appropriate for Boulder.

In places where the ACME specification allows customization/CA choice Pebble aims to make choices different from Boulder. For instance, Pebble changes the path structures for its resources and directory endpoints to differ from Boulder. The goal is to emphasize client specification compatibility and to avoid "over-fitting" on Boulder and the Let's Encrypt production service.

Lastly, Pebble will enforce it's test-only usage by aggressively building in guardrails that make using it in a production setting impossible or very inconvenient. Pebble will not support non-volatile storage or persistence between executions. Pebble will also randomize keys/certificates used for issuance. Where possible Pebble will make decisions that force clients to implement ACME correctly (e.g. randomizing /directory endpoint URLs to ensure clients are not hardcoding URLs.)

Limitations

Pebble is missing some ACME features (PRs are welcome!). It does not presently support subproblems, or pre-authorization. Pebble does not support revoking a certificate issued by a different ACME account by proving authorization of all of the certificate's domains.

Pebble does not perform all of the same input validation as Boulder. Some domain names that would be rejected by Boulder/Let's Encrypt may work with Pebble.

Pebble does not enforce any rate limits. It is not presently an appropriate tool for testing that your client handles Boulder/Let's Encrypt rate limits correctly.

Install

  1. Set up Go
  2. Add ~/go/bin to your $PATH, or set GOBIN to a directory that is in your $PATH already, so that pebble will be in your $PATH for easy execution.
    • One way to do this is to add export PATH=$PATH:$HOME/go/bin to your ~/.profile
  3. git clone https://github.com/letsencrypt/pebble/
  4. cd pebble
  5. go install ./cmd/pebble

Usage

Binary

Assuming pebble is easily accessible in your $PATH:

pebble -config ./test/config/pebble-config.json

(otherwise replace pebble with ~/go/bin/pebble or $GOBIN/pebble)

Afterwards you can access the Pebble server's ACME directory at https://localhost:14000/dir.

Docker

Pebble includes a docker-compose file that will create a pebble instance that uses a pebble-challtestsrv instance for DNS resolution.

To download and start the containers run:

docker-compose up

Afterwards you can access the ACME API from your host machine at https://localhost:14000/dir, pebble's management interface at https://localhost:15000 and the pebble-challtestsrv's management interface at http://localhost:8055.

To get started you may want to update the pebble-challtestsrv mock DNS data with a new default IPv4 address to use to respond to A queries from pebble:

curl --request POST --data '{"ip":"172.20.0.1"}' http://localhost:8055/set-default-ipv4

See the pebble-challtestsrv README for more information.

Prebuilt Docker Images

Pebble releases are published as Docker images to the Github Container Registry

With a docker-compose file:

version: '3'

services:
 pebble:
  image: ghcr.io/letsencrypt/pebble:latest
  command: pebble -config /test/my-pebble-config.json
  ports:
    - 14000:14000  # ACME port
    - 15000:15000  # Management port
  environment:
    - PEBBLE_VA_NOSLEEP=1
  volumes:
    - ./my-pebble-config.json:/test/my-pebble-config.json

With a Docker command:

docker run -e "PEBBLE_VA_NOSLEEP=1" ghcr.io/letsencrypt/pebble
# or
docker run -e "PEBBLE_VA_NOSLEEP=1" --mount src=$(pwd)/my-pebble-config.json,target=/test/my-pebble-config.json,type=bind ghcr.io/letsencrypt/pebble pebble -config /test/my-pebble-config.json

Default validation ports

To make it easier to test ACME clients and run challenge response servers without root privileges Pebble defaults to validating ACME challenges using unprivileged high ports:

  • Default HTTP-01 Port: 5002
  • Default TLS-ALPN-01 Port: 5001

These ports can be changed by editing the "httpPort" and "tlsPort" values of the Pebble -config file provided to pebble.

Strict Mode

Pebble's goal to aggressively support new protocol features and backwards compatibility breaking changes is slightly at odds with its goal to provide a simple, light-weight ACME test server for clients to use in integration tests. On the one hand we want to introduce breaking changes quickly and use Pebble as a test-bed for this. On the other we want to make sure we don't break client integration tests using Pebble too often.

As a balance to meet these two needs Pebble supports a -strict flag. By running Pebble with -strict false changes known to break client compatibility are disabled.

Presently we default -strict to false but this will change in the future. If you are using Pebble for integration tests and favour reliability over learning about breaking changes ASAP please explicitly run Pebble with -strict false.

DNS Server

By default Pebble uses the system DNS resolver, this may mean that caching causes problems with DNS-01 validation. It may also mean that no DNSSEC validation is performed. You should configure your system's recursive DNS resolver according to your needs or use the -dnsserver flag to define an address to a DNS server.

pebble -dnsserver 10.10.10.10:5053
pebble -dnsserver 8.8.8.8:53
pebble -dnsserver :5053

You may find it useful to set pebble's -dnsserver to the address you used as the -dns01 argument when starting up a pebble-challtestsrv instance. This will let you easily mock DNS data for Pebble. See the included docker-compose.yml and the pebble-challtestsrv README for more information.

Testing at full speed

By default Pebble will sleep a random number of seconds (from 0 to 15) between individual challenge validation attempts. This ensures clients don't make assumptions about when the challenge is solved from the CA side by observing a single request for a challenge response. Instead clients must poll the challenge to observe the state since the CA may send many validation requests.

To test issuance "at full speed" with no artificial sleeps set the environment variable PEBBLE_VA_NOSLEEP to 1. E.g.

PEBBLE_VA_NOSLEEP=1 pebble -config ./test/config/pebble-config.json

The maximal number of seconds to sleep can be configured by defining PEBBLE_VA_SLEEPTIME. It must be set to a positive integer.

Skipping Validation

If you want to avoid the hassle of having to stand up a challenge response server for real HTTP-01, DNS-01 or TLS-ALPN-01 validation requests Pebble supports a mode that always treats challenge validation requests as successful. By default this mode is disabled and challenge validation is performed.

To have all challenge POST requests succeed without performing any validation run:

PEBBLE_VA_ALWAYS_VALID=1 pebble

Invalid Anti-Replay Nonce Errors

The urn:ietf:params:acme:error:badNonce error type is meant to be retry-able. When receiving this error a client should make a subsequent request to the /new-nonce endpoint (or use the nonce from the error response) to retry the failed request, rather than quitting outright.

Experience from Boulder indicates that many ACME clients do not gracefully retry on invalid nonce errors. To help ensure future ACME clients are able to gracefully handle these errors by default Pebble rejects 5% of all valid nonces as invalid.

The percentage of valid nonces that are rejected can be configured using the environment variable PEBBLE_WFE_NONCEREJECT. E.g. to reject 90% of good nonces as invalid instead of 15% run:

PEBBLE_WFE_NONCEREJECT=90 pebble

To never reject a valid nonce as invalid run:

PEBBLE_WFE_NONCEREJECT=0 pebble

Object Reuse

The RFC allows for several objects to be re-used.

Clients should be prepared an ACME server may re-use any given object type, regardless of Pebble implementing a reuse policy for that object.

Pebble and Boulder may or may not implement the same object re-use policies at any given time. There exists an ACME Implementation Details document for Boulder which contains some information on how Boulder handles this.

Order Reuse

The RFC allows ACME servers to reuse an Order. Pebble does not reuse Orders at this time; however Boulder does reuse Orders in at least one scenario:

  • If an Account requests a new Order that is identical to an already existing "pending" or "ready" Order for that same Account, the Order will be re-used.

Authorization Reuse

ACME servers may choose to reuse authorizations from previous orders in new orders. ACME clients should always check the status of a new order and its authorizations to confirm whether they need to respond to any challenges.

Valid Authorization Reuse

Pebble will reuse valid authorizations in new orders, if they exist, 50% of the time.

The percentage may be controlled with the environment variable PEBBLE_AUTHZREUSE, e.g. to always reuse authorizations:

PEBBLE_AUTHZREUSE=100 pebble

Pending Authorization Reuse

Pebble does not currently reuse Pending Authorizations across Orders, however other ACME servers - notably Boulder - will reuse Pending Authorizations.

Avoiding Client HTTPS Errors

Pebble is accessible over HTTPS only and uses a test certificate generated using a test CA (See the test/certs/ directory for more information).

Since the Pebble test CA isn't part of any default CA trust stores you must add the test/certs/pebble.minica.pem certificate to your client's trusted root configuration to avoid HTTPS errors. Your client should offer a runtime option to specify a list of trusted root CAs.

IMPORTANT: Do not add the pebble.minica.pem CA to the system-wide trust store or to any production systems/codebases. The private key for this CA is intentionally made publicly available in this repo.

Management interface

In order to ease the interaction of Pebble with testing systems, a specific HTTP management interface is exposed on a different port than the ACME protocol, and offers several useful testing endpoints.

These endpoints are specific to Pebble and its internal behavior, and are not part of the RFC 8555 that defines the ACME protocol.

The management interface is configured by the managementListenAddress field in pebble-config.json that defines the address and the port on which the management interface will listen on. Set managementListenAddress to an empty string or null to disable it.

The default configuration for this management interface as defined in test/config/pebble-config.json is to listen on any address on port 15000:

  "managementListenAddress": "0.0.0.0:15000",

CA Root and Intermediate Certificates

Note that the CA's root and intermediate certificates are regenerated on every launch. They can be retrieved by a GET request to https://localhost:15000/roots/0 and https://localhost:15000/intermediates/0 respectively.

You might need the root certificate to verify the complete trust chain of generated certificates, for example in end-to-end tests.

The private keys of these certificates can also be retrieved by a GET request to https://localhost:15000/root-keys/0 and https://localhost:15000/intermediate-keys/0 respectively.

IMPORTANT: Do not add Pebble's root or intermediate certificate to a trust store that you use for ordinary browsing or that is used for non-testing purposes, since Pebble and its generated keys are not audited or held to the same standards as the Let's Encrypt production CA and their keys. Moreover these keys are exposed by Pebble and will be lost as soon as the process terminates: so they are not safe to use for anything other than testing.

In case alternative root chains are enabled by setting PEBBLE_ALTERNATE_ROOTS to a positive integer, the root certificates for these can be retrieved by doing a GET request to https://localhost:15000/roots/0, https://localhost:15000/root-keys/1 https://localhost:15000/intermediates/2, https://localhost:15000/intermediate-keys/3 etc. These endpoints also send Link HTTP headers for all alternative root and intermediate certificates and keys.

The length of certificate chains can be controlled using PEBBLE_CHAIN_LENGTH, which has a default and minimum value of 1 (leaf + 1 intermediate). For higher values, Pebble will include extra intermediate certificates between the leaf and the root. Extra intermediate certificates are not exposed via the management interface.

Certificate Status

The certificate (in PEM format) and its revocation status can be queried by sending a GET request to https://localhost:15000/cert-status-by-serial/<serial>, where <serial> is the hexadecimal representation of the certificate's serial number (no 0x prefix). It can be obtained via:

openssl x509 -in cert.pem -noout -serial | cut -d= -f2

The endpoint returns the information as a JSON object:

$ curl -ki https://127.0.0.1:15000/cert-status-by-serial/66317d2e02f5d3d6
HTTP/2 200
cache-control: public, max-age=0, no-cache
content-type: application/json; charset=utf-8
link: <https://127.0.0.1:15000/dir>;rel="index"
content-length: 1740
date: Fri, 12 Jul 2019 22:14:21 GMT

{
   "Certificate": "-----BEGIN CERTIFICATE-----\nMIIEVz...tcw=\n-----END CERTIFICATE-----\n",
   "Reason": 4,
   "RevokedAt": "2019-07-13T00:13:20.418489956+02:00",
   "Serial": "66317d2e02f5d3d6",
   "Status": "Revoked"
}

OCSP Responder URL

Pebble does not support the OCSP protocol as a responder and so does not set the OCSP Responder URL in the issued certificates. However, if you setup a proper OCSP Responder run side by side with Pebble, you may want to set this URL. This is possible by setting the field ocspResponderURL of the pebble-config.json consummed by Pebble to a non empty string: in this case, this string will be use in the appropriate field of all issued certificates.

For instance, to have Pebble issue certificates that instruct a client to check the URL http://127.0.0.1:4002 to retrieve the OCSP status of a certificate, run Pebble with a pebble-config.json that includes:

  "ocspResponderURL": "http://127.0.0.1:4002",

Listing orders

Pebble has support for enumerating all orders for an ACME account object according to RFC 8555, Section 7.1.2. By default, three orders are returned per page, to make it easy to test pagination. This number can be modified by setting the PEBBLE_WFE_ORDERS_PER_PAGE environment variable to a positive integer. For example, to have 15 orders per page, run

PEBBLE_WFE_ORDERS_PER_PAGE=15 pebble

pebble's People

Contributors

aarongable avatar adferrand avatar alexzorin avatar calavera avatar cpu avatar danielmorsing avatar dependabot[bot] avatar eggsampler avatar felixfontein avatar icing avatar jsha avatar ldez avatar mcpherrinm avatar mdebski avatar meyskens avatar munnerz avatar orangepizza avatar osirisinferi avatar pgporada avatar remilapeyre avatar rmbolger avatar ryansouza avatar sergioaugrod avatar sheurich avatar shred avatar squizzling avatar szepeviktor avatar titanous avatar wgreenberg avatar zyphlar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pebble's Issues

String Format Bug in wfe.go

Lines 979-980

fmt.Sprintf("Authorization expired %s %s",
authz.ExpiresDate.Format(time.RFC3339)))

Either %s too much or parameter missing.

Details on revocation

I noticed that certificate recovation is now supported in Pebble. Great :-) I've noticed two things:

  1. ACME defines no behavior if one tries to revoke a certificate which has already been revoked. Boulder returns error type urn:ietf:params:acme:error:malformed (or urn:acme:error:malformed for ACME v1) with detail Certificate already revoked, while Pebble simply returns a 404. Are there plans to define a common behavior in the ACME protocol, or will this be left for implementations to handle as they want?

  2. When trying to revoke a certificate with its private key, Boulder does not require the Key ID field, while Pebble does: {'type': 'urn:ietf:params:acme:error:malformedRequest', 'detail': 'Key ID (kid) in JWS header missing expected URL prefix', 'status': 400} Which of these behaviors is correct? Or is revocation by certificate's private key simply not supported yet in Pebble?

System DNS resolver caching fails DNS-01 challenges

After fixing up some e2e tests in cert-manager, I've realised Pebble is not validating DNS01 domains correctly.

My suspicious is this is caused by the use of net.LookupTXT in va: https://github.com/letsencrypt/pebble/blob/master/va/va.go#L270

From what I understand, boulder will pick a random authoritative nameserver to query against when performing the DNS01 validation. I have designed cert-manager to therefore check each authoritative NS for the expected TXT record before actually accepting the challenge.

I appear to be hitting L272 (https://github.com/letsencrypt/pebble/blob/master/va/va.go#L272), even after waiting an additional 60s after the DNS record has propagated to all authoritative nameservers.

Am I correct in thinking we shouldn't expect recursive servers to be up to date? This sounds very difficult to achieve cleanly 😃

FWIW, I have changed the e2e test to use the letsencrypt staging endpoint, and it passes fine (hence my suspicions!)

Account key roll-over

Pebble does not support account key rollover.

(I know this is stated in the README, but I think it would be nice to have an issue for this as well :-) )

Update README now that chisel2 is in Boulder repo

Currently the README mention using chisel2 to test pebble (btw, pebble now runs on windows using the latest Bash on Ubuntu for Windows).

The chisel2.py file no longer seems to be part of the certbot acme-v2 branch, it may be renamed or removed?

Version numbers/identifiers for Docker image tags

It would be nice if there would be some kind of version numbers or identifiers for Pebble which can be used to identify specific versions, for example for tags in the official Pebble repository (https://hub.docker.com/r/letsencrypt/pebble/tags/). Having only latest is not so useful if you want to target a specific version :-)

Ideas for version identifiers:

  • commit IDs (the ones from master) (I'm using that right now for a ACME testing container);
  • dates (version is built from the latest commit to master on that day);
  • some integer or "regular" version number (which someone has to increment for every commit/set of commits which should define a new version). This requires manual work (or some clever scripting, with storing the version number somewhere).

I think I'd prefer the first solution, since it is the simplest to implement. Any suggestions/thoughts/ideas?

Challenge hosts and ports should be configurable

In order to use Pebble for integration tests, there should be more configuration options for challenges.

At the moment, only the ports for http and tls-sni challenges can be configured. The host should be configurable, too, so all http and tls-sni verifications are always requested from the configured host, irregarding of the domain to be validated.

There are also no configurations for the dns challenge yet. Host name and port of the DNS server should be configurable, so Pebble can be configured to query a mock DNS server running on an unprivileged port.

Possibility to decrease sleep time (without disabling it)

For integration tests, it would be nice if it is possible to decrease the random sleep intervals (to maybe at most 5 seconds), so that tests will take less long, without removing the sleep intervals completely (by setting PEBBLE_VA_NOSLEEP to 1).

Implement account deactivation

Per https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.3.7

A client can deactivate an account by posting a signed update to the
server with a status field of "deactivated." Clients may wish to do
this when the account key is compromised or decommissioned.

Pebble currently seems to ignore an account update with {"status":"deactivated"}. The resulting account object returned still has "status":"valid".

I realize this likely falls under the category of stuff like revocation and account key rollover that's not implemented yet. I just wanted to throw it out there for tracking.

Challenge update expect "type"

Pebble says

Challenge update was type , existing challenge is type dns-01

The corresponding code is

pebble/wfe/wfe.go

Lines 1107 to 1114 in 878eac3

// Check that the challenge update is the same type as the challenge
// NOTE: Boulder doesn't do this at the time of writing and instead increments
// a "StartChallengeWrongType" stat
if update.Type != chal.Type {
return nil, acme.MalformedProblem(
fmt.Sprintf("Challenge update was type %s, existing challenge is type %s",
update.Type, chal.Type))
}

But the ACME example just reads

  "payload": base64url({
    "keyAuthorization": "IlirfxKKXA...vb29HhjjLPSggwiE"
  }),

https://ietf-wg-acme.github.io/acme/draft-ietf-acme-acme.html#responding-to-challenges

I think pebble shouldn't expect or check a "type" field here.

Hardcoded scheme in expectedURL at wfe.verifyPost()

I just deployed Pebble behind an HTTPS termination, but communication attempts were rejected because of these lines at the aforementioned method

expectedURL := url.URL{
	Scheme: "http",
	Host:   request.Host,
	Path:   request.RequestURI,
}

I changed it to "https" and it works like a charm. As an enhancement, I'd suggest just allowing for both schemes in the url parameter of the JWS header, to allow pebble to be agnostic where it is exactly running. What do you think?

PR #96 boulder-incompatible change enforcement

We could ignore this if sent (and that's what Boulder will do) but for Pebble we'd like to be more aggressive about pushing clients implementations in the right direction, so we treat this as a malformed request.

What seems to be a good idea on the first glimpse, as discussed in shred/acme4j#59, doesn't push clients "in the right direction", it enforces LE-/boulder-incompatible changes, which is rather horrible...
I suggest to silently ignore the KeyAuthorization field until live LE boulder instances properly support ACME v2 draft 10.

Implement wildcard issuance

Pebble should implement a wildcard issuance policy that ~= matches what Boulder does.

Orders with wildcard identifiers should be accepted. The returned authorization for wildcard names should only include a DNS-01 challenge type.

WFE: Improve input error handling for newOrder identifiers

Pebble seems more than happy to create an order that contains a DNS type identifier with an illegal value (e.g. [email protected]):

$ pebble-client -email [email protected]
welcome to the pebble shell
Requesting directory from "https://localhost:14000/dir"
Requesting nonce from "https://localhost:14000/nonce-plz"
Registering new account with "https://localhost:14000/sign-me-up"
Requesting nonce from "https://localhost:14000/nonce-plz"
Your account ID is "https://localhost:14000/my-account/286f9d57028ed685e9b8d32a370c9437e5ed483c4c121d35b0e8f2d7cee1eb2c"
Starting REPL environment...
$> Enter a directory endpoint or a URL to POST: newOrder 
$> Enter JSON body, empty line to finish : 
{
  "identifiers": [ { "type": "dns", "value": "[email protected]" } ]
}

Requesting nonce from "https://localhost:14000/nonce-plz"
Response:
&http.Response{Status:"201 Created", StatusCode:201, Proto:"HTTP/1.1", ProtoMajor:1, ProtoMinor:1, Header:http.Header{"Content-Type":[]string{"application/json; charset=utf-8"}, "Location":[]string{"https://localhost:14000/my-order/RBfgJj1HZ3NzeaoxhZUeZaUSQgMAXkmBPHs5setXDsU"}, "Replay-Nonce":[]string{"wl7nKUvgF2GCaahk21o1wg"}, "Date":[]string{"Thu, 26 Apr 2018 20:52:04 GMT"}, "Content-Length":[]string{"374"}, "Cache-Control":[]string{"public, max-age=0, no-cache"}}, Body:(*http.bodyEOFSignal)(0xc4200646c0), ContentLength:374, TransferEncoding:[]string(nil), Close:false, Uncompressed:false, Trailer:http.Header(nil), Request:(*http.Request)(0xc4203a4300), TLS:(*tls.ConnectionState)(0xc4200c2370)}

{
  "status": "pending",
  "expires": "2018-04-27T20:52:04Z",
  "identifiers": [
    {
      "type": "dns",
      "value": "[email protected]"
    }
  ],
  "finalize": "https://localhost:14000/finalize-order/RBfgJj1HZ3NzeaoxhZUeZaUSQgMAXkmBPHs5setXDsU",
  "authorizations": [
    "https://localhost:14000/authZ/iQ4_fvPCMDAYADkczFfZXgTtbFJYc0MFq5jWiGc_8LE"
  ]
}
$> Enter a directory endpoint or a URL to POST: ^C

This results in failed validation attempts later on to non-sense encoded URLs.

Pebble should reject this at the newOrder stage, like Boulder does.

newAccount returning status as empty string

Currently testing against commit eb01c4f using Windows PowerShell 5.1's Invoke-WebRequest.

When I submit a newAccount request to the /sign-me-up endpoint, everything goes as expected except the value of status in the returned account object. The HTTP response code is 201. The headers include a new nonce and Location Url of the new account. And the body is returned as:

{
   "status": "",
   "contact": [
      "mailto:[email protected]"
   ],
   "termsOfServiceAgreed": true,
   "onlyReturnExisting": false
}

Section 7.3 of the 09 spec would have me believe status should be set to "valid". Or am I misinterpreting?

Here are the associated Pebble logs:

Pebble 2018/02/07 14:48:07 Generated new root issuer with serial 7b3527e7d7cfcc40
Pebble 2018/02/07 14:48:07 Generated new intermediate issuer with serial 160a8981343113e1
Pebble 2018/02/07 14:48:07 Configured to reject 15% of good nonces
Pebble 2018/02/07 14:48:07 Pebble running, listening on: 0.0.0.0:14000
Pebble 2018/02/07 14:48:23 HEAD /nonce-plz -> calling handler()
Pebble 2018/02/07 14:52:23 POST /sign-me-up -> calling handler()
Pebble 2018/02/07 14:52:23 There are now 1 accounts in memory

JWS header parameter 'url' required.

Hi,

I'm trying to use pebble with certbot and dehydrated clients but I'm getting the following error:

with certbot :

certbot --config-dir ~/letsencrypt --work-dir ~/letsnecrypt --logs-dir ~/letsencrypt  --server "${SERVER:-http://localhost:14000/dir}" --no-verify-ssl --tls-sni-01-port 5001 --http-01-port 5002 --manual-public-ip-logging-ok --non-interactive --no-redirect  --agree-tos --register-unsafely-without-email  --debug certonly -a standalone -d example.com

Error: urn:ietf:params:acme:error:malformedRequest :: JWS header parameter 'url' required.

with dehydrated using nginx lua

 + ERROR: An error occurred while sending post-request to http://pebble:14000/sign-me-up (Status 400)

Details:
{
   "type": "urn:ietf:params:acme:error:malformedRequest",
   "detail": "JWS header parameter 'url' required.",
   "status": 400
}

rm: cannot remove '/etc/resty-auto-ssl/letsencrypt/domains.txt': No such file or directory

I have downloaded pebble today.
what is missing in my request ?

Deadlock when issuing for multiple names

I pushed some trivial changes to the Certbot acme-v2 branch, fixing field names, which should have fixed the tests. And indeed, locally a run of:

python ./tools/chisel2.py le.wtf

Works (with le.wtf aliased to localhost in /etc/hosts). But in Travis, a request for /my-order/xyz times out. A little poking suggests that this is because Travis attempts issuance for multiple names at once, and there is a deadlock related to locking the order object.

How to get hold of root certificate?

As part of integration tests, I'm verifying the generated certificate chain. However, since #148, I also need the root certificate to validate the chain.

Is there a way to get hold of it? (I think there currently isn't.)

It would be really nice to be able to get the root certificate from somewhere (i.e. Pebble writes it to disk, or provides an endpoint where it can be read with a GET).

(ACME itself doesn't provide a way to retrieve the root certificate for an order AFAIK; is that correct? If not, that would also solve this ;-) )

Valid authorizations still have all challenges

challenges (required, array of objects): For pending authorizations,
the challenges that the client can fulfill in order to prove
possession of the identifier. For final authorizations (in the
"valid" or "invalid" state), the challenges that were used.

For final authorizations, it contains the challenges that were
successfully completed.

Note, that I have proposed to change this to removing all other challenges in the moment of a successful response to a challenge.

{
   "status": "valid",
   "identifier": {
      "type": "dns",
      "value": "localhost"
   },
   "challenges": [
      {
         "type": "http-01",
         "url": "https://localhost:14000/chalZ/f6mGRSNQZ8BhrPnly_lyIsvPZX9PT-7dPRpfbv1GfpU",
         "token": "kzOKCd_DWD8wMbld35v5kn352Y_JebrgH2vkkHHz42c",
         "status": "valid",
         "validated": "2018-01-09T16:30:35Z"
      },
      {
         "type": "tls-sni-02",
         "url": "https://localhost:14000/chalZ/eG6J4H-CB2nELjdbVidbbgBn_QwGlp5tn2rwxmjuyCc",
         "token": "l45tM-Gp2F61oUkmKANcsQCSdF59AS591C5u4s1Wxrg",
         "status": "pending"
      },
      {
         "type": "dns-01",
         "url": "https://localhost:14000/chalZ/blaIxirG-OM_2WnMOTE4YjDEnyo22YwMt1KjtksIBjA",
         "token": "Zs_Zuc3kyCUekGLH66cxp_jxjtsmXLe1uT8ZfwKRwWU",
         "status": "pending"
      }
   ],
   "expires": "2018-01-09T17:30:35Z"
}

ENV variable to force validation to suceed

Often time, the validation process itself is outside the scope of the client.

It would be great if there was a way to skip the validation for testing purpose for this type of client.

Something like PEBBLE_VERIFICATION_ALWAYS_SUCCEED=1.

Setting header 'kid' to account ID rather than account URL works

Per https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-6.2

For all other requests, the request is signed using an existing
account and there MUST be a "kid" field. This field MUST contain the
account URL received by POSTing to the newAccount resource.

This probably gets filed as unintentional feature, but technically a bug for strict adherence to the current draft 10 spec.

Pebble (and Boulder) currently includes an ID field in Account object output as well as the Location header value with full Account URL. For example, "ID": "abcd1234" and https://localhost:14000/my-account/abcd1234.

Subsequent requests that require a kid value in the header will accept the raw ID value "kid":"abcd1234" in addition to the URL which is technically wrong. It seems to be because the code blindly trims the account prefix from whatever kid was sent before checking it.

pebble/wfe/wfe.go

Lines 403 to 405 in cae1c35

accountURL := header.KeyID
prefix := wfe.relativeEndpoint(request, acctPath)
accountID := strings.TrimPrefix(accountURL, prefix)

Ironically, I only figured this out after realizing that my client code was wrong and then wondering why it hadn't been throwing an error.

'new-account' vs 'new-reg' in discovery info (aka supporting multiple versions of spec)

Hi there

I'm working on switching to using pebble for our e2e tests in cert-manager (previously we've used letsencrypt/boulder, which is quite expensive to run!)

However my e2e tests fail against Pebble due to differences between the two. I understand that Pebble is meant to closely follow the ACME specification, whereas boulder makes some departures.

When querying https://acme-v01.api.letsencrypt.org/directory, the following discovery info is returned:

{
  "hsX66jWA6lI": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
  "key-change": "https://acme-v01.api.letsencrypt.org/acme/key-change",
  "meta": {
    "terms-of-service": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf"
  },
  "new-authz": "https://acme-v01.api.letsencrypt.org/acme/new-authz",
  "new-cert": "https://acme-v01.api.letsencrypt.org/acme/new-cert",
  "new-reg": "https://acme-v01.api.letsencrypt.org/acme/new-reg",
  "revoke-cert": "https://acme-v01.api.letsencrypt.org/acme/revoke-cert"
}

However when doing the same against my pebble instance, I get:

{
   "meta": {
      "terms-of-service": "data:text/plain,Do%20what%20thou%20wilt"
   },
   "new-account": "http://test-pebble/sign-me-up",
   "new-nonce": "http://test-pebble/nonce-plz",
   "new-order": "http://test-pebble/order-plz"
}

From what I can tell, Pebble implements acme-08 (the latest draft of the spec), however boulder seems to be using some older implementation that has different field names. Is there some way I can discovery the version in use? As it stands I'm not sure how I can support older ACME servers at the same time as being up to date 😢

I'm using golang.org/x/crypto/acme as my ACME library, which appears to implement a pre-08 version of the spec (as it does not recognise new-account, new-nonce or new-order)

/cc @cpu (as I've seen you working on this repo and you are very helpful!! 😄 )

Wildcard orders being rejected with "illegal character: '*'" error

I believe PR #122 accidentally introduced a bug that now rejects wildcard domain identifiers in new order requests.

pebble/wfe/wfe.go

Lines 767 to 772 in 2462e27

// isDNSCharacter is ported from Boulder's `policy/pa.go` implementation.
func isDNSCharacter(ch byte) bool {
return ('a' <= ch && ch <= 'z') ||
('A' <= ch && ch <= 'Z') ||
('0' <= ch && ch <= '9') ||
ch == '.' || ch == '-'

pebble/wfe/wfe.go

Lines 811 to 815 in 2462e27

for _, ch := range []byte(rawDomain) {
if !isDNSCharacter(ch) {
return acme.MalformedProblem(fmt.Sprintf(
"Order included DNS identifier with a value containing an illegal character: %q",
ch))

Should the * character be added to the allowed list in isDNSCharacter?

NewAccount doesn't return an Account resource if the account already exists

This causes checks to ensure the account is valid (after the account has already been created) to fail if they check the status of the Account (as they likely will want to).

We should return existingAcct here to do so: https://github.com/letsencrypt/pebble/blob/master/wfe/wfe.go#L624

From the ACME draft:

7.3.1.  Finding an Account URL Given a Key

   If the server already has an account registered with the provided
   account key, then it MUST return a response with a 200 (OK) status
   code and provide the URL of that account in the Location header
   field.  This allows a client that has an account key but not the
   corresponding account URL to recover the account URL.

   If a client wishes to find the URL for an existing account and does
   not want an account to be created if one does not already exist, then
   it SHOULD do so by sending a POST request to the new-account URL with
   a JWS whose payload has an "onlyReturnExisting" field set to "true"
   ({"onlyReturnExisting": true}).  If a client sends such a request and
   an account does not exist, then the server MUST return an error
   response with status code 400 (Bad Request) and type
   "urn:ietf:params:acme:error:accountDoesNotExist".

This doesn't actually seem clear to me - should the ACME server only respond with a Location header pointing to the Account resource, or should it also include the Account resource itself?
Given onlyReturnExisting exists, it 'feels' like newAccount should always return the full account resource if it exists, and this would make developing clients easier. The ACME server could respond with 200 if the resource already exists, or 201 for created (as per current spec)

Attempts to clear contact on an account don't succeed and don't throw error.

I'm trying to implement a feature in my client to remove an account's contact information.

Per https://tools.ietf.org/html/draft-ietf-acme-acme-11#section-7.3.2

If the client wishes to update this information in the future, it
sends a POST request with updated information to the account URL.
The server MUST ignore any updates to the "orders" field or any other
fields it does not recognize. If the server accepts the update, it
MUST return a response with a 200 (OK) status code and the resulting
account object.

I've tried two different methods to empty the contact field. Within the payload of the request:

  • "contact":null
  • "contact":[]

Both of these return a 200 (OK) status as if they were accepted. But the resulting account object still has the old value for the contact field.

I tried testing against the Boulder v2 staging server as well. "contact":[] works and does clear the field. "contact":null works the same as Pebble and doesn't modify the account or throw an error.

It would be awesome if both methods worked. But I can settle for only "contact":[] working. Though, perhaps it would be more appropriate to generate an error on "contact":null in that case?

Listen on HTTPS with minica issued leaf cert

Pebble should include a miniCA generated CA certificate in the repo. We should also include a leaf certificate issued from the CA for 127.0.0.1.

Users requiring trusted TLS for their test code (don't do this for anything else!) can import the CA certificate into their client trust store. Users with more complicated needs (e.g. a different subject SAN in the leaf certificate) should replace the CA & leaf cert in the repo with their own.

Support IP address identifiers

Pebble should support IP type identifiers in addition to DNS type identifiers by implementing draft-ietf-acme-ip-04.

This will involve:

  • - creating a new identifier type constant in acme/common.go
  • - updating the WFE to allow IP type identifiers in newOrder requests
  • - updating the WFE to validate the IP identifiers
  • - updating the TLS-ALP-01 implementation to properly handle IP address identifiers
  • - updating the HTTP-01 implementation to properly handle IP address identifiers
  • - updating the DNS-01 implementation to forbid IP address identifiers
  • - updating the CA to issue certificates for orders with IP address identifiers using appropriate SAN entries with iPAddress type
  • - ??? I'm probably forgetting something!

This will be the first time we've supported an identifier type other than DNS and we should be doubly cautious that we don't miss any details or assumptions in the existing code.

tls-sni-02, "connection reset by peer" on PEBBLE_VA_NOSLEEP

I am not sure if this is an issue with Pebble or with my tests... However, if I start Pebble with PEBBLE_VA_NOSLEEP=1, the tls-sni-02 challenge always fails immediately.

Pebble makes a certificate request to the web server. However, it then fails with an error "Failed to connect to [...] for the [...] challenge". The cause of the failure is a "read tcp 172.17.0.3:59908->172.17.0.2:5001: read: connection reset by peer". I had to add a debug log output at va.go fetchCerts, line 361 (right after the "Return better err" todo 😉) to get that error message.

When I leave out the PEBBLE_VA_NOSLEEP=1, the tls-sni-02 challenge works as expected. The other challenges are not affected by the NOSLEEP.

[Feature] Pebble should return Content-Type headers with charset

The ACME specs' examples give Content-Type headers like Content-Type: application/json, and Pebble also responds with that header. However, Content-Type: application/json; charset=utf-8 or Content-Type: application/json; charset="utf-8" would be perfectly valid response content types as well.

If a client is implemented to only expect "application/json" as content type, it would fail if a charset is given along with the type.

Maybe Pebble should add the charset parameter to its response. Or randomly respond either with or without charset.

Cannot update challenge with status valid, only status pending

Testing our ACME v2 client against Pebble.

During the challenge authorization stage, pebble completes the challenge and the authorization status is changed to "valid" (according to the Pebble console output) but in the HTTP response we still get the "pending" status.
After several retries, we get the following error:

HTTP/1.1 400 Bad Request
{
"type": "urn:ietf:params:acme:error:malformedRequest",
"detail": "Cannot update challenge with status valid, only status pending",
"status": 400
}

Output of the Pebble console:

C:\Users\iftah.HRHINOS\go\src\github.com\letsencrypt\pebble [master ≡ +1 ~0 -0 !]> pebble -config ./test/config/pebble-config.json
Pebble 2018/06/05 12:48:31 Generated new root issuer with serial 2cb70fcb9d0a40b0
Pebble 2018/06/05 12:48:32 Generated new intermediate issuer with serial 1c9fd2a1f310967c
Pebble 2018/06/05 12:48:32 Configured to reject 15% of good nonces
Pebble 2018/06/05 12:48:32 Pebble running, listening on: 0.0.0.0:14000
Pebble 2018/06/05 12:50:00 GET /dir -> calling handler()
Pebble 2018/06/05 12:50:00 POST /sign-me-up -> calling handler()
Pebble 2018/06/05 12:50:00 There are now 1 accounts in memory
Pebble 2018/06/05 12:50:00 POST /order-plz -> calling handler()
Pebble 2018/06/05 12:50:00 There are now 1 authorizations in the db
Pebble 2018/06/05 12:50:00 Added order "xJnDF8Le3KogZoC5vNqVft-kTqKp8Wu4QhmXJH0zxek" to the db
Pebble 2018/06/05 12:50:00 There are now 1 orders in the db
Pebble 2018/06/05 12:50:00 GET /authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs -> calling handler()
Pebble 2018/06/05 12:50:48 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:48 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:48 Starting 3 validations.
Pebble 2018/06/05 12:50:48 Sleeping for 3s seconds before validating
Pebble 2018/06/05 12:50:48 Sleeping for 1s seconds before validating
Pebble 2018/06/05 12:50:48 Sleeping for 14s seconds before validating
Pebble 2018/06/05 12:50:49 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:49 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:49 Starting 3 validations.
Pebble 2018/06/05 12:50:49 Sleeping for 5s seconds before validating
Pebble 2018/06/05 12:50:49 Sleeping for 1s seconds before validating
Pebble 2018/06/05 12:50:49 Sleeping for 0s seconds before validating
Pebble 2018/06/05 12:50:49 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:49 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:49 Starting 3 validations.
Pebble 2018/06/05 12:50:49 Sleeping for 1s seconds before validating
Pebble 2018/06/05 12:50:49 Sleeping for 12s seconds before validating
Pebble 2018/06/05 12:50:49 Sleeping for 14s seconds before validating
Pebble 2018/06/05 12:50:50 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:50 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:50 Starting 3 validations.
Pebble 2018/06/05 12:50:50 Sleeping for 14s seconds before validating
Pebble 2018/06/05 12:50:50 Sleeping for 6s seconds before validating
Pebble 2018/06/05 12:50:50 Sleeping for 5s seconds before validating
Pebble 2018/06/05 12:50:50 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:50 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:50 Starting 3 validations.
Pebble 2018/06/05 12:50:50 Sleeping for 11s seconds before validating
Pebble 2018/06/05 12:50:50 Sleeping for 5s seconds before validating
Pebble 2018/06/05 12:50:50 Sleeping for 6s seconds before validating
Pebble 2018/06/05 12:50:51 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:51 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:51 Starting 3 validations.
Pebble 2018/06/05 12:50:51 Sleeping for 13s seconds before validating
Pebble 2018/06/05 12:50:51 Sleeping for 12s seconds before validating
Pebble 2018/06/05 12:50:51 Sleeping for 7s seconds before validating
Pebble 2018/06/05 12:50:51 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:51 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:51 Starting 3 validations.
Pebble 2018/06/05 12:50:51 Sleeping for 13s seconds before validating
Pebble 2018/06/05 12:50:51 Sleeping for 0s seconds before validating
Pebble 2018/06/05 12:50:51 Sleeping for 0s seconds before validating
Pebble 2018/06/05 12:50:52 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:52 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:52 Starting 3 validations.
Pebble 2018/06/05 12:50:52 Sleeping for 3s seconds before validating
Pebble 2018/06/05 12:50:52 Sleeping for 2s seconds before validating
Pebble 2018/06/05 12:50:52 Sleeping for 11s seconds before validating
Pebble 2018/06/05 12:50:52 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:52 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:52 Starting 3 validations.
Pebble 2018/06/05 12:50:52 Sleeping for 11s seconds before validating
Pebble 2018/06/05 12:50:52 Sleeping for 12s seconds before validating
Pebble 2018/06/05 12:50:52 Sleeping for 1s seconds before validating
Pebble 2018/06/05 12:50:53 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:53 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:53 Starting 3 validations.
Pebble 2018/06/05 12:50:53 Sleeping for 6s seconds before validating
Pebble 2018/06/05 12:50:53 Sleeping for 13s seconds before validating
Pebble 2018/06/05 12:50:53 Sleeping for 10s seconds before validating
Pebble 2018/06/05 12:50:53 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:50:53 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"ravenclustertestiftahwork.development.run", Challenge:(*core.Challenge)(0xc0424ae140), Account:(*core.Account)(0xc042446230)}
Pebble 2018/06/05 12:50:53 Starting 3 validations.
Pebble 2018/06/05 12:50:53 Sleeping for 13s seconds before validating
Pebble 2018/06/05 12:50:53 Sleeping for 3s seconds before validating
Pebble 2018/06/05 12:50:53 Sleeping for 12s seconds before validating
Pebble 2018/06/05 12:50:54 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:50:54 POST /chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY -> calling handler()
Pebble 2018/06/05 12:51:01 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:51:02 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:51:03 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:51:03 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:51:04 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:51:04 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:51:04 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:51:04 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:51:06 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY
Pebble 2018/06/05 12:51:06 authz 1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs set VALID by completed challenge 6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY

And the network traffic capture by Fiddler:

GET https://localhost:14000/dir HTTP/1.1
Host: localhost:14000


HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Replay-Nonce: yYI8vtoBdcy2YgPpKe9BIQ
Date: Tue, 05 Jun 2018 09:50:00 GMT
Content-Length: 302

{
   "meta": {
      "termsOfService": "data:text/plain,Do%20what%20thou%20wilt"
   },
   "newAccount": "https://localhost:14000/sign-me-up",
   "newNonce": "https://localhost:14000/nonce-plz",
   "newOrder": "https://localhost:14000/order-plz",
   "revokeCert": "https://localhost:14000/revoke-cert"
}

------------------------------------------------------------------

POST https://localhost:14000/sign-me-up HTTP/1.1
Content-Type: application/jose+json
Content-Length: 2221
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJraWQiOm51bGwsInVzZSI6bnVsbCwibiI6InpzZ0RtTFFZVVRBYXhlNmhSUWpYYmtIS08tdVpDRmNsdEE4Y3B1Wlp0ZHZlU0dJT2pxelVfRmQ3azlBTjRmaVNuekxRRFpBNVZJV1A1SjVzRTF4bW9EMDVtY0EzeGdJcnllVlVEdGFCcG5CSEppczFfQk1ZTlJKQk83c0hvN1lwTC0zenl4eEMyVXNxVkZRY1QyNWQ0VWxkS0hGVlk3YjZpLVF1UExKbU9fVFdJM2dmbmx1RkpGd3RVTGVWNTV6a2ZQZGRmTTdIbnhaYlhaRDNlMUYtU0ZoQlpIWXVrc256ZW1qTXRpQ1NPRnVET1lDN1YzMmszbFkyNzV0clhGWldOMjN0MnB4ZmwzUWp3eEI2X0RNdF9VU1hFVEwydXRxZ3FlejVzeENlcER3czBUeWZ5eloxUVNvR1l0ZjN5cUhzZjRTZkQ0RXhuM1IyWnFibU5nX2NNY0NEZjZlMUlzSHNUWTBfVTFzRU9hUlUwTEtnTk1ZOUtqZDNnUF9iZG5KRnZxMldzbnRlNTF5UXZ3RWpMc1lzdXBRZjYwUzlzMTZSR1FRNk0zT3JfNFZDY0NFa0g1MHJQajQ5ZnFJakp1bl9RNUpfRUdKNGRZcVBTUGJHejJHeVVRTXlPNkM2NE43djYyeWd0VDBtck84Vll4YUxLUjhJUngyRVBCMmwxeVFtNTV0LXB3eXBMREcwaUpmdnJXbTJiOEJVZGFZa3B4REVJYndIc3VTRy1DZHJLQzlvSkgzVkpLczQ4TmxzNmR0N0plV0xnRXVSZVB6bEMtSFotVk5IU2JaR0NzM1ZFb1NrVDlVNFA1dDlRcWxLcnkxRGFCYmthNy1CQWNNb2ljb0JVdE9nb1lDYnFfS1J1NUo4OVpoalQ5M3ZObEhkOGRMeFVZUXZ2OWpJOE5FIiwiZSI6IkFRQUIiLCJkIjpudWxsLCJwIjpudWxsLCJxIjpudWxsLCJkcCI6bnVsbCwiZHEiOm51bGwsInFpIjpudWxsLCJhbGciOm51bGx9LCJraWQiOm51bGwsIm5vbmNlIjoieVlJOHZ0b0JkY3kyWWdQcEtlOUJJUSIsInVybCI6Imh0dHBzOi8vbG9jYWxob3N0OjE0MDAwL3NpZ24tbWUtdXAifQ",
  "payload": "eyJ0ZXJtc09mU2VydmljZUFncmVlZCI6dHJ1ZSwiY29udGFjdCI6WyJtYWlsdG86aWZ0YWhAaGliZXJuYXRpbmdyaGlub3MuY29tIl0sInN0YXR1cyI6bnVsbCwiaWQiOm51bGwsImNyZWF0ZWRBdCI6IjAwMDEtMDEtMDFUMDA6MDA6MDAiLCJrZXkiOm51bGwsImluaXRpYWxJcCI6bnVsbCwib3JkZXJzIjpudWxsLCJMb2NhdGlvbiI6bnVsbH0",
  "signature": "ZsVzHaC3S-Vs8z9osWfEyt7Ja9JXiKIQkqamybd2S3K79UjTtFIWoOtkFwv-q40kRj8ryvtVuMM1vymSryyUOnk4yGkWtNYeRk32F0PXlVCVeF5-MXwB0lrSzQEPVSve9--UUzXIJsI9mEKEk9KBThmi-rXbffE3ooDQ6Vbw_9y4YXLo77zRBzPcAVsyMzxWfNvidlXr62o4-fsDG55d7pzY8B5ZypmQ3Cjd5Ei7cyFs3crFPYFoOAJa-ZfX0jYn7IzbIpfQiTFX_QHPkq5kZtAe7axDF085OPVrSe2O_1L966A6F8aB1j3KO-KqC2UmaR0UlikzNgYyU1TQxcDIgq8q4sBadzdDCwFzHYqm_-f8PKSuCSzCyxnzZjMQf2TGnJEGgjlbMmlA1oVnkiMS82CMKlll5yke2LtV_irzB5-N3ZRRMpRtNPkcD_cfQ4qs2jJzPW4fL-ytSZEXswlpxrbmmT55KfVYARf8zRjaHK7nWWarFYrzw_XxdVLhBMD8ZuAFDJJwE4clWpZ4Fiys1eBxdHmKmnDUcdWn501XIQmP9hVRPYZLq3sUf4MNSdH8uLsVLMJih8p9A_En4ioytuYLr4HaE6PHIXpP--3ZEph3IvEKmXKe5iY_h4Yy1BZTeeeNtyeh7eFRzRdf_G4AJkMv738qfahx-RclktwGw1c"
}
HTTP/1.1 201 Created
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Location: https://localhost:14000/my-account/7a2bb08ca0b4da3d6e85d21a04ce5ed0292aa001e81e492cc4048191f15627b7
Replay-Nonce: 6o8kktGJj4oWo6P3g6I5KQ
Date: Tue, 05 Jun 2018 09:50:00 GMT
Content-Length: 920

{
   "status": "valid",
   "contact": [
      "mailto:[email protected]"
   ],
   "key": {
      "kty": "RSA",
      "n": "zsgDmLQYUTAaxe6hRQjXbkHKO-uZCFcltA8cpuZZtdveSGIOjqzU_Fd7k9AN4fiSnzLQDZA5VIWP5J5sE1xmoD05mcA3xgIryeVUDtaBpnBHJis1_BMYNRJBO7sHo7YpL-3zyxxC2UsqVFQcT25d4UldKHFVY7b6i-QuPLJmO_TWI3gfnluFJFwtULeV55zkfPddfM7HnxZbXZD3e1F-SFhBZHYuksnzemjMtiCSOFuDOYC7V32k3lY275trXFZWN23t2pxfl3QjwxB6_DMt_USXETL2utqgqez5sxCepDws0TyfyzZ1QSoGYtf3yqHsf4SfD4Exn3R2ZqbmNg_cMcCDf6e1IsHsTY0_U1sEOaRU0LKgNMY9Kjd3gP_bdnJFvq2Wsnte51yQvwEjLsYsupQf60S9s16RGQQ6M3Or_4VCcCEkH50rPj49fqIjJun_Q5J_EGJ4dYqPSPbGz2GyUQMyO6C64N7v62ygtT0mrO8VYxaLKR8IRx2EPB2l1yQm55t-pwypLDG0iJfvrWm2b8BUdaYkpxDEIbwHsuSG-CdrKC9oJH3VJKs48Nls6dt7JeWLgEuRePzlC-HZ-VNHSbZGCs3VEoSkT9U4P5t9QqlKry1DaBbka7-BAcMoicoBUtOgoYCbq_KRu5J89ZhjT93vNlHd8dLxUYQvv9jI8NE",
      "e": "AQAB"
   },
   "ID": "7a2bb08ca0b4da3d6e85d21a04ce5ed0292aa001e81e492cc4048191f15627b7"
}

------------------------------------------------------------------

POST https://localhost:14000/order-plz HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1370
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiI2bzhra3RHSmo0b1dvNlAzZzZJNUtRIiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvb3JkZXItcGx6In0",
  "payload": "eyJMb2NhdGlvbiI6bnVsbCwic3RhdHVzIjpudWxsLCJleHBpcmVzIjoiMjAxOC0wNi0wN1QwOTo1MDowMC43Nzg0ODkyWiIsImlkZW50aWZpZXJzIjpbeyJ0eXBlIjoiZG5zIiwidmFsdWUiOiIqLnJhdmVuY2x1c3RlcnRlc3RpZnRhaHdvcmsuZGV2ZWxvcG1lbnQucnVuIn1dLCJub3RCZWZvcmUiOm51bGwsIm5vdEFmdGVyIjpudWxsLCJlcnJvciI6bnVsbCwiYXV0aG9yaXphdGlvbnMiOm51bGwsImZpbmFsaXplIjpudWxsLCJjZXJ0aWZpY2F0ZSI6bnVsbH0",
  "signature": "VoIyxnkcBpVxp50t9ySoBxClNSG6YgFtCq-qEw7SWgaTWghrS1MKkgfnU4ZShebDTJ5B7D7Wg6Mh-SYsxWdozohlZMIE7TzM7oY74AisevXHn9iVzkx5sgnUV0EAiDFP1WZ5mof2-1FZWiCinYHQlLiGkGQo9NhakffxIbsgysBFDGzX7a14npfczufdSVb6yyN7PDagJY1z7YPnMA02Fb4ILWqRFumExFPJcgTXoGHPWkCOstFlp20mdBBrhFSM7-Zxh7UNlotjpB-v3IZxKhAjqmrNKofR2vPymKvbUGWAIpF0Wi8jy75QIeQgCfD6csNLGrloUvBGO5tA_tAUmLsWuhYN_e3poAxs_xRac0dazhnir4Mn5oLzLDlpd2X8NPcGl8bje1aF3He7Rdj1mvwnL6s0ZSZFqSyF5InLuD2OYLAsn5G4Hd7sZ63cKsSkMoTtvdXXqG9vMWLB4yBdukphZ_3EKJ1Txif_fnN-x9cV0mquK_GSBPQS0jNLwNBh3wUo42SQomR2fWjgXXRRQAy80OIc_33z48_WbMv7uLLiyt5sjdR8JMdC5DmnaZ-6aLaMvQftKzGIMVEg8mjtxfWhouDcSswPl1SOcQrOq4J_EzMH9x7TjK3cYiw8hwBfZJgrA7L1N52gsHXhQ0VFT7GOj1hx_ydUtiftkPtgm1M"
}
HTTP/1.1 201 Created
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Location: https://localhost:14000/my-order/xJnDF8Le3KogZoC5vNqVft-kTqKp8Wu4QhmXJH0zxek
Replay-Nonce: NaqDcK-VGeLELApktfITLA
Date: Tue, 05 Jun 2018 09:50:00 GMT
Content-Length: 406

{
   "status": "pending",
   "expires": "2018-06-06T09:50:00Z",
   "identifiers": [
      {
         "type": "dns",
         "value": "*.ravenclustertestiftahwork.development.run"
      }
   ],
   "finalize": "https://localhost:14000/finalize-order/xJnDF8Le3KogZoC5vNqVft-kTqKp8Wu4QhmXJH0zxek",
   "authorizations": [
      "https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs"
   ]
}

------------------------------------------------------------------

GET https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs HTTP/1.1
Host: localhost:14000


HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Replay-Nonce: wXiHH2yvlUFO2liV7lRpdg
Date: Tue, 05 Jun 2018 09:50:00 GMT
Content-Length: 445

{
   "status": "pending",
   "identifier": {
      "type": "dns",
      "value": "ravenclustertestiftahwork.development.run"
   },
   "challenges": [
      {
         "type": "dns-01",
         "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
         "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
         "status": "pending"
      }
   ],
   "expires": "2018-06-05T10:50:00Z",
   "wildcard": true
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJ3WGlISDJ5dmxVRk8ybGlWN2xScGRnIiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "ZX6TRR0tlyMJ7rtqRLzcvaY_S7GzCTHOr0iI43HEABfGyx087ZEr3DLxUWr-j3QALBLj_bUQch33WtKw_Fuw8HNYgKTKwnJu-3MKwHK7E6yKil2schMjdi3LgW3ckGbsU-gxJuPyTpVCpaAWwMZOcm-1VQuKDkLSvL0Ca3m6ev4xSV2iVEFsc8LciHdRDu7ZNnc5dHwFinkjxvLzL0oNXL7aEUo7tHk6U0dFe6Az2c8FibXCNe00_yA1Y8Evxsv4xwYGJIdQZ65hEFEDKOgFf6jvOL38aDHd8PYCGF1WXQ0n6eI53ADDZThX4erdY_Grb_sPy552pIdrptWsQ548BgMG0c-3ziVKV3OoGiSM0irtdIXgeNVwUp5OQ3wR2DfM6ubGQQX3mcoaqwwmH7f1xqH0ryq138_PEGwyeOcwz_P1D0NKHfWfzbmECqXDtSxh-5hcmP1rt7Us-qyA0aj0D2eEiggapGgPSh1KYoq93hNJddDOwUEsfmMVb7J3OcnzwD8l6Nrdn93U46iNyqpPx431-H4p9yCN21ECguH7NqBV14o60QbZf0ZNincyHfDoV-RRXrUf-M7vqnRkeV9QdyHzhvvk8iGqxOK-s5IMl5Yr65to-2jM0lYXhWMkv7FVr61JMbsGumCYOMawMPgTcaHxOJES6UA4dgXhUEGCzVA"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: jvDw8p-6XWgfSb1b5rnXsg
Date: Tue, 05 Jun 2018 09:50:48 GMT
Content-Length: 193

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJqdkR3OHAtNlhXZ2ZTYjFiNXJuWHNnIiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "ua-GrECItbcNqq04jDQtEc28lSfvSeYN28NtDhtNGsKVQB-gAEJhMANGLABvlvFKa7CCuKpETE5kqIgsl2Jpi0mc1kfJ3EgQ-1jlT7YAh5ZTwMAN-ISgMGpt3Uy2D-yBmaeO_v9tXYGB45aV3-fDtG0JR7WrCeEIBEm8mPrX0s1Lzy7t_ngpV1_pLF1D65_K7FOzqaEkI5ozzsfD__ak-AEe8POkp2Po5nlT3gyXqJeJ_DQubBIvYemDVC-4q04Jt7OhNoqFbAUxJrqqw1m055o8gADbm6_zCGwxk31A0acBA0pp9FWkkCxvzqFObVX6OqQXon46CUP1szHKRfsnWaNozwIIeoP6YkoHeo_8ocg805ogXPhusEXGoS7I-pjw_aW0iqUjJKQ7IB50JOM4VuY_bfuV8U7_tj3GzF_RGQ180hjgpJvoLEzuNio0jexnGtJCxobTpS-eVBwz3jWoneBA_nr8YbfEw8zU52R-MlkGbmdC6bDBHKCvt2o7ItmI3l4GAk_KL_ftv3BdM8fWp58fvnZlr9B7Iz-UX8y6Wcco1EhcjfR-64spOxhY9ppCopYV_H6jCpFKWK7s0hqTFU9UGNEF3BnHJFb4f99BAH81SEGqIMURB0Day4zJV0S-kYNwEjkR8wt_-u8pMSjqsfQhVT0OZ7vTX9TmSfth7hY"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: GKhnzzyxIamq1M_2P9dKog
Date: Tue, 05 Jun 2018 09:50:49 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:48Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJHS2huenp5eElhbXExTV8yUDlkS29nIiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "tKoNVYU39JKNYllIXalAzJGYA_oDdp9D70GGHaJMVogFwiP08dUUjJ6r1c3nVowtgFh-npEfC3kYK7Px1PQxgFQZP37VoC0QS5kGkx0Rqk3M05Uoc9hluKUy5f7A0gzURawoU2rCACHFvT6PWAomthe9phZvEnCEpKXkV3nO-AIZC3lE3tYpylvfnN4pjwVoUEXbWqUlZfuN08O4swALq84GwdImpRD0-ISWKGP0oIGHDSN19Vby2p7LCRoQTqOOWsph7xFODagkm-ia5W9_XkwN-RJGrsOp9piPETdoz6sdRe3nQpJFzewO2pDp35Qy4sCMacEk1-4N-tTUKLEwHKQKsMrOOCIn76kbJ4th3QmRK-84KVtVqfwo-Rk8V4u9ieDCInZSbU-LI3AT4u7zr3qaMO21L4NBO_ObYlSuXLk5CQ030O-0H6HYHuimIsAH4JyN0esHyyyEjq58mWogeTnSe1nyqRMGoZVtj7DiQQxCIn2CzxGB47cloJO_nXPixdSaD8RHymfz2YCnU9vWG5SOZTxl-EsRcsN32bo7I_gdWecILRNJb4NaiCpXK9bsdxQpTE16l5jjvIjcMqDXIjx3pXs-tOfO6x-8TSC5J0DAHWa5xrIgO7svuUvsV-xXHaTvPNNnEMbRosyJQKdCsS4spYDu30YsymC7ZBraLVM"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: fbJ3CRdFVAIu5O3upx623w
Date: Tue, 05 Jun 2018 09:50:49 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:49Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJmYkozQ1JkRlZBSXU1TzN1cHg2MjN3IiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "FSn3UBe7n35tCAKvfyx3qnEuKBox4cAuy6mhqQe0arl47R5YPqcesX9ltTgjp6K6eebqUbY6zsF1aDL9oYjmcYbSuudEsB6bPS9tS0ZFg_4DQ1oAuN_NVoaPjb659apPrUe_xM4D2tL401SZbYvXuHYrRbijMLlWbbCV8_XT7_Dme9YRzQG4MRUaY4ULH6UeySOgD9q5Pz77Y40DFMzfjd5g9Px0XHLW-d1pzTeiEh9byL4iy_Bi47bQDIFCQNOWifHDCKvEq4Ft3gi0ypbZ75p061-XHn2HGxt9fEm4SMByfdoWDwX6sfE-e3LDWk3H4H5vOxl4irBcKyKwM_iGJjFYgLzWq8dL-Jt1K7vavTSL0WvHB3fnest2Y4voB6U7iGS4Tcl4MPvMEmUaCExcoLmAJjSBrTjX5HMWzv7piRTPlkCkou478jZHy_F0EgVppqxcEGu9aaq7Yh-13P5l777_11GREFr2cg47ThO41JOflQ4KggdUBmu7FIF7TmGKEIu7K2Sx6WOLmZ_tlG3wMQAfG2G-aun-qiZCveanY5wgwU8bgQQCOQEQjXqDas0sg4O-iozsKuaLCZrd2UI1Ez-a1iv27jnseFlf7xxtKP7BtAuU8oYw50UPyNEzvhKiMe8QPozh7-RkWzfZZ6bc6hmTJ90ATyyOAROYCsHxUSc"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: uOI46FmVupbQk1aKIibOAg
Date: Tue, 05 Jun 2018 09:50:50 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:49Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJ1T0k0NkZtVnVwYlFrMWFLSWliT0FnIiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "Eu9IKLKM4r91FxAy_PrJwzubhc2WUgQwAFiFFEDbxtZse1DtCsQs1_RziOhUieXLKe8obOWomkjf7iY6Dpkl4dpGVdjXEgAQM5WEoWoSJlttzN9A9mM4tQAMnMbSOYIspdUacclW5DzRFB3ddpKSeJVxLmCfvf5MjPpJdziQU-ZESBeame4RHJSMsYwWFtMbnEFwHRBAJvQh71rKKXn4_Vsgq0vsJfPTcVBZjVZzDFbf5u2dSkhCR8CU1s0OdkTiWsnQ4T54_owXT-TlYRMAFroDPU9h51bpmYgdZBTEyIO00HMgxrmK_AcduU5UZnjLvWbMT3_cP6-3pwIeW70ocTaL6yEDspCi_vm0nJhlYvXKjIpu-pP5UD9QbOTmOfbXCaTaGXtyiR5mBHsCePU_8dWmkqnJ_35N1kSckcFXvKV98CQ6P9P70IEnoeZVV0NPeV0389nb4DLEUgwxtWx4gC5B1LkyqCxPv2sTPq7f47y2CEcQHEFAtFvxgefJ-iKXndhohx-Yz20I9qVM98h97MDwLWnQqMCuGaVNYaNCCBq318T8M6RzulZxeeRaWcJq3protupDg1DZaCsWnVJd3LeXac45IJcIK7iKQSpyWpMmFl_7rvDD3Gvmi-FDImyGu3eDBAQBiUZoE64ub9jSX1pQv9DtHZQu9zb6OkyM4dM"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: lh0-k4Fzf8GfO3OkIQZFBw
Date: Tue, 05 Jun 2018 09:50:50 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:50Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJsaDAtazRGemY4R2ZPM09rSVFaRkJ3IiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "wc4mXtWq38lJLnhhLY8ZX140QJYmGZQr8m7PbWETPg2sBKzqxdM4wb-133GSBc0ljhmcffu1CEmunhQ6clzq1WxocQbncNE80Zg6j59mSfkeOUO2G2tS8lWUQgZ8eEk40esRpjAe27pa1dKT-5UTWcPXSRwYEbkvMQQErorzoZ_77JywNbRwFZErdgJ-Q1HuCj-s70D-aXk5LQRW70Yle5mWXM31fLDx4AfmAQTzmLv3P68Lqkp2rpcd5wz_Kie4gqt1Ecde2EAMk9h1HSgohxq_ESYLmEappUpKWcbXNRRuyrXNNjFwcC-VZj5EKSM1TV1wxOVxIwsfQgJCwwVBm5DWYN623oBl_Gr6bwgjXKMp2AVl1bue1Lu1yWrZ7DKzYr11LhZm_13MscfSPzt7i73kWeT-CNMAkWHwHsJpR15Sufy6f_i6j9jfa6ZNekVfqV3Fo1lEghoW9xu54fg18uWp2sMq8FxRokfLwz--5b-J6ImGjS_DoGgQTMqajED7Nz8H00ot6A3NAhjfTVKGXlPhVOGqMS-UcNLQ-RrYVZJdESVk-ePYc8YqzIEpim-W7XZa0_sFV0mIECng8WvN8VBDjTgwIhYnK1SR5rwmEffO6ctNO01TUcqRFgGMV0g0O28-AVpC-bzh_7gxX7aZwl9AjwhAti_cIp9bIn98zq0"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: HfUZei38dF2ZKNKJVgifXw
Date: Tue, 05 Jun 2018 09:50:51 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:50Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJIZlVaZWkzOGRGMlpLTktKVmdpZlh3IiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "InHxy_n3O1siV4-fKGkOe9TY2eu38iVLtXQBChIQM52MaE8redAneRan-4mB4hMP8cw0RZ2rObRJA6MO8a48GZH2Gs5echMIErSXTZWb9hVwt0S5anKTG7Kc4-yyTraXmWNrxCPfiVout5VNEf_pgybSyceN7ILMRFYoqEIV8filu0xDuJVfQCAJIxP-UKK-9SZurnNCquWzTTsJdvDGz95W60ZnAH7pnd_B25s-nYlT4Z1QubyZP_n_JgCgs37RvslXQlpU7heCa4iYuNIn3zk4JY49EWtIoS6AOeHzu9kVfKXE1Dxxd77jlwaY7zMbZ4jymK3qhtMKgcrzhyJ8gk73wK5qLsEY77lOqXob9uT9O0mBpeHlIpqC-Sh5xmJPVxN6NH8beTCXoAfcE4r7GiQ7qJ6rX3ejsWXJdnDNeyrutY-drPZ9Wytxr9DkCLi7QuYZWWpbKSeXeeqloFaXH1lqOgmr8d0VEgMQJk8kqFINTrgKIOlAPy2UTNTPei0KhJ0gHSevfqC3OxMgwaHJFWGuIdGZ3vbpAQAskaC8Gl695PS8mvDZkO8nIrCv_ccQJGgDmtUfpXGTpwi_p9JFYxAlLj5FZ2b5GwArYNKJ4brKyuQavdT9aTEnHuhxBO8QCKUCyHbLgLnhxmQZca6kqOwAeBvf9dbDFu-YmdN-EhA"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: Na2pLKKZBv-K4nDV0rHW0g
Date: Tue, 05 Jun 2018 09:50:51 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:51Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJOYTJwTEtLWkJ2LUs0bkRWMHJIVzBnIiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "p0bPDkGbPtmgx9UWLCw351-bSCcvdK47gu_XHgCB1-DbX28GPvMyWLsOvRCdtY-2tqS3vRlT-IPQ89RtWeenmBdRe7piCHgHyhMTpZW1AKW9BjHYF-tfPh3-vLCrOLZdKIh0-QSr3UxkvwcpaXw615i4qdm52yl228oE0PNT4DX9oFdzPKFu0n38PU97Bao6SNX6hCPyHIWKIQcKZbp8OYoXrdjsoDbh3iM5dQHqVf1x-kWkqzrUaxAtSAlEyhMXzOLTxflbhlTGzCLcOuVmajCBadF6LwDDhGqiqJ6fw-zHBz-NqAtMxNvd5zaJkcoR-dH6RiwXctYXFJL8EL3pDlJyeLrv3pafKruuV5YWGXSeQRS2j1reFKDBIPg6jQMxhJ36kV6ptr_JU46RemJRUl41N4b1miIODCn0QDgUvJmi6adtq_dAF0XMFKyas4LolMfhFyYPsZN7ogsBT-8WOfM03yv91ZlSTERa1RJWE0DtA7fb4h86kM3QAKgRG9gcaY_PE9eVVqipqQyf9Htybw_TZgZMkDhZrcZJPXlZk_JpLY6at1Ad4TrskEm8AS4463iJT8XcOr0Z1G5Zs9rlQIc_Pon6eXrO07IdVmOpA07HhUAtKYBI92hhXynmDQwiCn9W1G1wm_jq0QOFXPlP1N5q5SqdRbLB7iWpZ-6cj2s"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: EVSi_Uu0bx0-LR71K_vdug
Date: Tue, 05 Jun 2018 09:50:52 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:51Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJFVlNpX1V1MGJ4MC1MUjcxS192ZHVnIiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "AmTSfqO59mpn-objy6foSaSjWQL0n_eWdI6Rt2W0niLvfE8n012OcdY3H6A1NWFQiUe_QP-aHcIsQtv8HOmWQrw7Cnb44rumAbY3KF_yVWuHdu9_zF_dVC5EpCm-82YMWjkPb8UaT4uyBIIh2OlFqC76OV52pr5xpDQrQ6Y8QUuEgKlsZOA0TkREmBY3WXgcddoXyDLLYj5vEDoEI5-Y3XFhGv1aGlgMIbAUF2huZLR4K5mEYUw0WK3tFUOYc1_DTdFhfp5YHGxKnJzLYgoRsYi5UWeBQDGDkzwEPFW9Csik6rRUsBpNEeQfBQxd4J3FsdfH3foKZ8XA-AUWo3e8mFzf_Smu2h7gV2kfRBmk1GphKG35WKNrvecCkdoqFFfYfrWUJ5AtTymnyL-nLvXx-aOIwAN7uQxrCVkDg4cVqvRgTGYZyFY6cLFOnRc2bXJ3LvIFZo4E7ayT4Ci2tRdnRugSQ_HCzaFq5EAqer_MeDUtc-JmYguDEn3G9tXVdWmpz7tsqRPnyBKCx7tWQOASntFUI6vYhDS86VwV44F1wGNanpGIM_21haPVz3AiwGkc-JzUy4hPS132TWPiva-icUFVSiwIy5rmLZD2no1gM0kNBj4KOoePbmqVRdQC3CzBEGLWksLWczfIL6W5UkQbL3LIuahtBhhzayA1Q6HU55Y"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: qDMxUARHNaRlq_XcHFd7Tw
Date: Tue, 05 Jun 2018 09:50:52 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:52Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJxRE14VUFSSE5hUmxxX1hjSEZkN1R3IiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "p3FpkghO6-g3S43qJxKBr2OJ_UADYLjrZS1yyEwYfdLlFxMRjk2nzmLkXNOBYpWbmQP8vQTJbWsRRSl2ZgSLQ-CLzpu4JKKud-A8Z01OV21_ItJ4C_GBjxpDPLw9fFQFeXktxSV7KGXnk4TzVL4npweDDiX00AaehdFqg_WCaRmlMyiABhenxX35iMncJFzfBTjxKehvzBCG2o-F_cfKakC6y6LsgZEasgI7kVDcaIwRZnXDm0lX1jrek0PYJPEcqrdQCCreVL9y-Gom6XXg5MdCDZYC-VRP92yWHwvBRj9jaSaCv-bwWoyeRJRM4S507TPQk3Q2f24QX-q7Oj-duxNDrUvhwGwBoIMRlm3dlpRvbqedg_gvkwXzwd4-jpv8ycGgktohtQI6ymdUIPcOzmmhrxfcB_AyOnDpnl5lWTo_5q6ibkxgD8i4GCJsj_7aQmw3vDNhlo2vQhS0IBLn9TAqMaze1CzoxUmtJPgFfzmjbccElyqTLgDJW8UZjZf78JvQWKekIzi7jD7d96rDrKCLa16JVF97pwKUFwufzyCeS0Vze8GlS-ksQdj5ZNdZ4-j8lqzbPsaY6eMQW46CjUfgf0KA_I6yHiGyHRD5DqXv9Ct1MnjhRs9j_43bBRfjTqRtYrgz5e5AApN1UHBJDN8K56zi6rqZyhp1hDDpVAU"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: nZUkMY9WzCPPAWpcjk9aZA
Date: Tue, 05 Jun 2018 09:50:53 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:52Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJuWlVrTVk5V3pDUFBBV3Bjams5YVpBIiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "BYRk2ID_8_9b1sO_dLjWQFia_rVTYjEWSYljxdDt6bH5RB16QVlXxgUzJ8ByvT8oYdpBUvnAIoq1qLYzzJB9jPRuLJgvS3GcIcqZe2_6sHOJ6FOnjc2RUxNdZQtI9cl8dt1zcb4zlWunAxKmhK6l-P17_4nH52ddkualTZVNF2QplMC7334N2QDrQ6rcPRc-2WidrLv9zrpTVoNLLZXKhrJ6Cp_DXvDHC_Pq-_6rlaxc0q2Y7O_jqBRP-EBuCkIcavOBBKq1dPnqEVMySDL3ov4jOHRGhFYO4fqoO1MWirLyyzpn9bbS3gNog_6DkCgYMaJjfa6yG9YJcYCUpu_hwxkFYmv0erxkK513PKcbVj688lvO65g5r_JUL0Qx26fctqPUMAxtaNcxDnHVSWHQb1pX6E569zYy8L-VMKzmn3HoK_p_OudWQqcqp0zi7kss3YOqN69iruGxoRDf837m5XViWFcp5WRNdwVbWXIUmTXwJ3FrJ_0Nkb5-8uwtaBOCuAy1GBMf82Ne64qETNOHmxYH7knBvd5OOms195Bgol3dJCqyiJ3-C2l-1HcBFbFp0Rhlou7PYeganNavL-5TLEScqDk6Fw68RnWt9oq7dKKiPulU7IQ15-nmaKCdD88Kg2PAfdjBJtu2JvAhpXsJaYYVt1vYKHQvd0U0Kmz-q4A"
}
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/authZ/1_eRzXIKRmpAjsnLJolkluQOojF6qfMC154cSbZnyqs>;rel="up"
Replay-Nonce: B-lBSAqIY2vd4YUz4NGlng
Date: Tue, 05 Jun 2018 09:50:53 GMT
Content-Length: 233

{
   "type": "dns-01",
   "url": "https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY",
   "token": "kdqJ5zMxdi0pDrtIkPonUkKtirea8tnIm2t4qx0BopY",
   "status": "pending",
   "validated": "2018-06-05T09:50:53Z"
}

------------------------------------------------------------------

POST https://localhost:14000/chalZ/6jI9G-lXt4dEmFaNV9xLtdKexuLsDM6yp4DIVJ6BknY HTTP/1.1
Content-Type: application/jose+json
Content-Length: 1223
Host: localhost:14000

{
  "protected": "eyJhbGciOiJSUzI1NiIsImp3ayI6bnVsbCwia2lkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvbXktYWNjb3VudC83YTJiYjA4Y2EwYjRkYTNkNmU4NWQyMWEwNGNlNWVkMDI5MmFhMDAxZTgxZTQ5MmNjNDA0ODE5MWYxNTYyN2I3Iiwibm9uY2UiOiJCLWxCU0FxSVkydmQ0WVV6NE5HbG5nIiwidXJsIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvY2hhbFovNmpJOUctbFh0NGRFbUZhTlY5eEx0ZEtleHVMc0RNNnlwNERJVko2QmtuWSJ9",
  "payload": "eyJrZXlBdXRob3JpemF0aW9uIjoia2RxSjV6TXhkaTBwRHJ0SWtQb25Va0t0aXJlYTh0bkltMnQ0cXgwQm9wWS5tMGhCa3hyczNobU9fMC1aSUJteU05blFCamRNc1lqUW1KOXJPdzJjc0NFIn0",
  "signature": "O5TUjAKz5TbLUqIlEFOrqlW2whX6ZLyDNzIpRXCgtnRJR0l1eLoBI5wRRMjqygLWgEdAdAv5gf_6VhMyvwVvDYyhctYV8qnI0P72__JjaOR0sI_xBS9G2yf2UQSSEzMAAq8GeSNiaNbnzy_nfY2MqXIJCbPTMumRfl7v5-RbiGjMuiza4fS5vxNg_M7I-_LRzc_Fjr5YVrmXim0L4JEO9D-pFrF_cO9WMibnB6hyTUrrztb7Vjj2df8Xe5z_2D24xMhNskN-yV4L_gJQNkbAfFRo_M71eNrVqHaffBvUwUHwCvnKKb55lC7vf_te0bBuui1-HDTzof0Oj73PCl4YN5_p3o1RJfecoank4fMAB66ZTBH0-7u9SlNHrGN15_GGP4_xWigtAIZ4bfSJDdESW1arsNeHLm7ky-knP008SzscAevQsXJ5kHx4eY-XDmu1ogg_9BvLvd0Jsv9OQ3D0BHMis8SQu4nUfnjIp2xA4FwLT8NLeKwUW6O8V6Yv8U1uWvoifCzRVh3aS4HI7rRbh5IStD8h49QmjgIUZ6pw7cgQCV-aVlYh6xQRU0O9c_Yq8CemM9vRrGVLuwwr_qBQGpNQkySIeLp-9tFJvTv-NaahDMXeYjYhOdezKz_tCkaSGqzMtCqzr8yFyImFDJK7GBZHFuCcIJ_XD5H9k7G1Cxg"
}
HTTP/1.1 400 Bad Request
Cache-Control: public, max-age=0, no-cache
Content-Type: application/problem+json; charset=utf-8
Replay-Nonce: cLtemededPyNjzqWBQUoaQ
Date: Tue, 05 Jun 2018 09:50:54 GMT
Content-Length: 157

{
   "type": "urn:ietf:params:acme:error:malformedRequest",
   "detail": "Cannot update challenge with status valid, only status pending",
   "status": 400
}

"kid" field is not accepted

According to draft-05, the "kid" JWK protected header field must always be used, except on new-reg and revoke-cert. The "kid" field must contain the account URL that was returned by new-account.

When setting the "kid" header to a valid account URL, pebble does not seem to be able to locate the account: Account http://localhost:14000/my-reg/a838...2ca6 not found.

When I only set the account hash a838...2ca6 as "kid", pebble crashes with a nil pointer dereference.

There seem to be two issues with wfe.go's verifyPOST():

  • when "kid" contains a valid account URL, wfe.db.GetRegistrationByID(keyID) is unable to find the account, but it should.
  • when "kid" only contains the account hash, wfe.db.GetRegistrationByID(keyID) finds the account. However, it then leaves the pubkey variable unset, which leads to a nil pointer dereference a few lines below.

The only way to make pebble work at the moment is to provide the "jwk" field all the time.

Randomize order of identifiers in Order objects returned to clients.

Boulder stores the identifiers for newOrder requests after normalizing them to be unique, lowercase, and in alphabetical order. This means clients get back order objects with the identifiers in alphabetical order.

That's fine for Boulder/Let's Encrypt but the ACME specification doesn't promise this behaviour. Clients shouldn't rely upon it for things like determining which name should be the subject common name (CN) in a finalization CSR.

Pebble could nudge folks in the right direction by randomizing the order of identifiers inside of an order object before presenting it to the client.

HTTP Management Interface

Discussion on #39 indicated that there are users who would find it valuable to be able to inject test data into Pebble to help support integration testing with an ACME client.

Similarly, in letsencrypt/boulder#2697 there was a desire for a way to signal "Reject the next n requests as if they had a badNonce".

Pebble should offer a basic HTTP "admin"/"test" interface on a different path/port than the ACME API that can be used to do management tasks like the two listed above.

Authorizations are not required to be already validated on 'finalize'

If I am immediately POSTing to "finalize" after I have posted to the challenge "url", pebble says

Authorization for "localhost" is not status valid

But ACME-09 reads

Once the client believes it has fulfilled the server’s requirements, it should send a POST request to the order resource’s finalize URL.

This is not "Once the client believes the server has validated …"

Further

A valid request to finalize an order will return the order to be finalized. The client should begin polling the order

“pending”: The server does not believe that the client has fulfilled the requirements. Check the “authorizations” array for entries that are still pending.

which suggests, that there might be pending authorizations after POSTing to finalize.

That's why I removed the polling for authorizations in the ACME example. The order should give an overall status after finalization:

ietf-wg-acme/acme@4ac605e#diff-8430e2aa241beb4ac49b252db20d4ee8L668

Only jwk provided but "jwk/kid mutually exclusive" message

POSTing to new-order with jwk instead of a kid should give an error, but this message is misleading:

ProblemDetail 
  { problemDetailType = Just urn:ietf:params:acme:error:malformedRequest
  , problemDetailTitle = Nothing
  , problemDetailStatus = Just 400
  , problemDetailDetail = Just "jwk and kid header fields are mutually exclusive."
  , problemDetailInstance = Nothing
  } 

Posting this issue since it sounds like something with input checks could be wrong.

Orders with unvalidated and expired authorizations remain in state 'pending'

Pebble currently appears to set the expires field on a newly created order to 24 hours past when the order was created. The associated authorizations appear to get 1 hour before they expire. No problem there.

However, if the 1 hour time limit elapses on the authorizations without being validated, they seem to remain in a pending state forever. But you get an "Authorization expired" error if you try to POST to a challenge's URL.

I would think that querying the authorization's status returns "invalid" once the expiration time has been reached. I would think that also then sets the order status to "invalid" as well.

Have I misread the spirit of the spec? Or is there a way to generate new authorizations for an existing order?

Current LetsEncrypt API compatibility?

Hi there,

I see this repository seems geared towards the upcoming ACME RFC spec. Is there a sister-project (or fork, or branch) of pebble which codifies the behavior of the current ACME-v01 API being hosted for the Let's Encrypt service?

tls-sni-02: bad SAN-B calculation

In va.go, the tls-sni-02 challenge's SAN-B is calculated by accessing task.Challenge.KeyAuthorization. However, that value seems to be empty there, so the calculated SAN-B is bad and the challenge fails.

After I replaced it with this code:

	// Compute the digest for the SAN B that will appear in the certificate
	expectedKeyAuthorization := task.Challenge.ExpectedKeyAuthorization(task.Registration.Key)
	hb := sha256.Sum256([]byte(expectedKeyAuthorization))
	zb := hex.EncodeToString(hb[:])
	sanBName := fmt.Sprintf("%s.%s.%s.%s", zb[:32], zb[32:], tlsSNIKaID, tlsSNISuffix)

the challenge could be completed successfully. I'm not fluent with Go, so I don't know if this is an acceptable solution.

dns-01 also accesses the task.Challenge.KeyAuthorization field, so I guess it would fail as well, but I couldn't test it.

(Background: I am the author of the Java client acme4j. I am preparing the client to be compliant with the latest ACME draft, and I'd like to use pebble for integration tests.)

Concurrency error when trying to get a certificate.

Hi,

Launching a pebble server and trying to get a certificate with cerbot gives me the following stacktrace:

[root@acme-v01 pebble]# pebble                                                                                                                                                                                     
Pebble 2018/04/25 19:29:01 Generated new root issuer with serial 30da9aed2261ac54                                                                                                                                  
Pebble 2018/04/25 19:29:02 Generated new intermediate issuer with serial 3669a10806ec4f                                                                                                                            
Pebble 2018/04/25 19:29:02 Configured to reject 15% of good nonces                                                                                                                                                 
Pebble 2018/04/25 19:29:02 Pebble running, listening on: 0.0.0.0:443                                                                                                                                               
Pebble 2018/04/25 19:34:13 GET /directory -> calling handler()                                                                                                                                                     
Pebble 2018/04/25 19:34:13 POST /sign-me-up -> calling handler()                                                                                                                                                   
Pebble 2018/04/25 19:34:13 There are now 1 accounts in memory                                                                                                                                                      
Pebble 2018/04/25 19:34:13 POST /order-plz -> calling handler()                                                                                                                                                    
Pebble 2018/04/25 19:34:13 There are now 1 authorizations in the db                                                                                                                                                
Pebble 2018/04/25 19:34:13 Added order "4PoZTdyfthd602Kjhhh8bqqGgrix2UUqm97mKgmkuVw" to the db                                                                                                                     
Pebble 2018/04/25 19:34:13 There are now 1 orders in the db                                                                                                                                                        
Pebble 2018/04/25 19:34:13 GET /authZ/ckogQJPdBf4suZAhqEGwEoAPo2bqoomU8J7dd7hrDJg -> calling handler()                                                                                                             
Pebble 2018/04/25 19:34:13 POST /chalZ/kePHA1sW3_BxeTn1P88XTRLKzOGTwhetMWZI6Or0IeU -> calling handler()                                                                                                            
Pebble 2018/04/25 19:34:13 Pulled a task from the Tasks queue: &va.vaTask{Identifier:"nyarlathotep.benschubert.me", Challenge:(*core.Challenge)(0xc42006f860), Account:(*core.Account)(0xc420064500)}              
Pebble 2018/04/25 19:34:13 Starting 3 validations.                                                                                                                                                                 
Pebble 2018/04/25 19:34:13 Sleeping for 14s seconds before validating                                                                                                                                              
Pebble 2018/04/25 19:34:13 Sleeping for 1s seconds before validating                                                                                                                                               
Pebble 2018/04/25 19:34:13 Sleeping for 3s seconds before validating                                                                                                                                               
Pebble 2018/04/25 19:34:14 Attempting to validate w/ HTTP: http://nyarlathotep.benschubert.me:5002/.well-known/acme-challenge/slSSG71_vquWaWQ2qnWRK5xDvKsMsVQAaNOpdbmyOTI                                          
fatal error: sync: Unlock of unlocked RWMutex                                                                                                                                                                      
                                                                                                                                                                                                                   
goroutine 15 [running]:                                                                                                                                                                                            
runtime.throw(0x770a27, 0x20)                                                                                                                                                                                      
        /usr/lib/golang/src/runtime/panic.go:605 +0x95 fp=0xc42004be20 sp=0xc42004be00 pc=0x42b9f5                                                                                                                 
sync.throw(0x770a27, 0x20)                          
        /usr/lib/golang/src/runtime/panic.go:594 +0x35 fp=0xc42004be40 sp=0xc42004be20 pc=0x42b955       
sync.(*RWMutex).Unlock(0xc42006f860)                
        /usr/lib/golang/src/sync/rwmutex.go:125 +0xa1 fp=0xc42004be70 sp=0xc42004be40 pc=0x46ed91        
github.com/letsencrypt/pebble/va.VAImpl.setAuthzInvalid(0xc420064280, 0x8ea100, 0x944600, 0x138a, 0x1389, 0xc4200542a0, 0x1, 0xc420102480, 0xc42006f860, 0xc4203f1e60)                                             
        /root/go/src/github.com/letsencrypt/pebble/va/va.go:189 +0x106 fp=0xc42004be98 sp=0xc42004be70 pc=0x6a3fe6                                                                                                 
github.com/letsencrypt/pebble/va.VAImpl.process(0xc420064280, 0x8ea100, 0x944600, 0x138a, 0x1389, 0xc4200542a0, 0x1, 0xc4203ee100)                                                                                 
        /root/go/src/github.com/letsencrypt/pebble/va/va.go:214 +0x3c1 fp=0xc42004bfa0 sp=0xc42004be98 pc=0x6a4431                                                                                                 
runtime.goexit()                                    
        /usr/lib/golang/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42004bfa8 sp=0xc42004bfa0 pc=0x459441    
created by github.com/letsencrypt/pebble/va.VAImpl.processTasks                                          
        /root/go/src/github.com/letsencrypt/pebble/va/va.go:136 +0x8b                                    

goroutine 1 [IO wait]:                              
internal/poll.runtime_pollWait(0x7fa57b7adf70, 0x72, 0xffffffffffffffff)                                 
        /usr/lib/golang/src/runtime/netpoll.go:173 +0x57                                                 
internal/poll.(*pollDesc).wait(0xc4200e6098, 0x72, 0xc42004d900, 0x0, 0x0)                               
        /usr/lib/golang/src/internal/poll/fd_poll_runtime.go:85 +0xae                                    
internal/poll.(*pollDesc).waitRead(0xc4200e6098, 0xffffffffffffff00, 0x0, 0x0)                           
        /usr/lib/golang/src/internal/poll/fd_poll_runtime.go:90 +0x3d                                    
internal/poll.(*FD).Accept(0xc4200e6080, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)                              
        /usr/lib/golang/src/internal/poll/fd_unix.go:335 +0x1e2                                          
net.(*netFD).accept(0xc4200e6080, 0xc420020070, 0xc420020000, 0xc420064190)                              
        /usr/lib/golang/src/net/fd_unix.go:238 +0x42
net.(*TCPListener).accept(0xc42000e008, 0x42ac66, 0x77e870, 0xc42004dae0)                                
        /usr/lib/golang/src/net/tcpsock_posix.go:136 +0x2e                                               
net.(*TCPListener).AcceptTCP(0xc42000e008, 0x402c01, 0xc42006e120, 0x7016c0)
        /usr/lib/golang/src/net/tcpsock.go:234 +0x49                                                                                                                                                        [27/80]
net/http.tcpKeepAliveListener.Accept(0xc42000e008, 0x434b2b, 0xc42004db20, 0x61f4b8, 0x455f00)
        /usr/lib/golang/src/net/http/server.go:3120 +0x2f
crypto/tls.(*listener).Accept(0xc4203efc00, 0x77e218, 0xc42006e0a0, 0x8e94c0, 0xc4203f0e70)
        /usr/lib/golang/src/crypto/tls/tls.go:52 +0x37
net/http.(*Server).Serve(0xc42006c680, 0x8e8b40, 0xc4203efc00, 0x0, 0x0)
        /usr/lib/golang/src/net/http/server.go:2695 +0x1b2
net/http.(*Server).ServeTLS(0xc42006c680, 0x8e9000, 0xc42000e008, 0xc42001c2a0, 0x30, 0xc42001c2d0, 0x2f, 0x0, 0xc42006c680)
        /usr/lib/golang/src/net/http/server.go:2764 +0x293
net/http.(*Server).ListenAndServeTLS(0xc42006c680, 0xc42001c2a0, 0x30, 0xc42001c2d0, 0x2f, 0x0, 0x0)
        /usr/lib/golang/src/net/http/server.go:2943 +0xd1
net/http.ListenAndServeTLS(0xc4200185f0, 0xb, 0xc42001c2a0, 0x30, 0xc42001c2d0, 0x2f, 0x8e47c0, 0xc4203f0780, 0xc4200502c0, 0xc4200115c0)
        /usr/lib/golang/src/net/http/server.go:2915 +0xb1
main.main()
        /root/go/src/github.com/letsencrypt/pebble/cmd/pebble/main.go:58 +0x5d6

goroutine 6 [chan receive]:
github.com/letsencrypt/pebble/va.VAImpl.processTasks(0xc420064280, 0x8ea100, 0x944600, 0x138a, 0x1389, 0xc4200542a0, 0x1)
        /root/go/src/github.com/letsencrypt/pebble/va/va.go:135 +0xa3
created by github.com/letsencrypt/pebble/va.New
        /root/go/src/github.com/letsencrypt/pebble/va/va.go:120 +0x22c

goroutine 7 [IO wait]:
internal/poll.runtime_pollWait(0x7fa57b7adeb0, 0x72, 0x0)
        /usr/lib/golang/src/runtime/netpoll.go:173 +0x57
internal/poll.(*pollDesc).wait(0xc4200e6018, 0x72, 0xffffffffffffff00, 0x8e6180, 0x8e2550)
        /usr/lib/golang/src/internal/poll/fd_poll_runtime.go:85 +0xae
internal/poll.(*pollDesc).waitRead(0xc4200e6018, 0xc4202b2000, 0x1000, 0x1000)
        /usr/lib/golang/src/internal/poll/fd_poll_runtime.go:90 +0x3d
internal/poll.(*FD).Read(0xc4200e6000, 0xc4202b2000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
        /usr/lib/golang/src/internal/poll/fd_unix.go:126 +0x18a
net.(*netFD).Read(0xc4200e6000, 0xc4202b2000, 0x1000, 0x1000, 0x7a6693e40ef45edc, 0x7e75dcbb9c348bb2, 0x7a6693e40ef45edc)
        /usr/lib/golang/src/net/fd_unix.go:202 +0x52
net.(*conn).Read(0xc42000e030, 0xc4202b2000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
        /usr/lib/golang/src/net/net.go:176 +0x6d
crypto/tls.(*block).readFromUntil(0xc42005ecf0, 0x7fa57b7bb840, 0xc42000e030, 0x5, 0xc42000e030, 0xc420001800)
        /usr/lib/golang/src/crypto/tls/conn.go:488 +0x95
crypto/tls.(*Conn).readRecord(0xc42007a380, 0x77e817, 0xc42007a4a0, 0xc4202d5b00)
        /usr/lib/golang/src/crypto/tls/conn.go:590 +0xe0
crypto/tls.(*Conn).Read(0xc42007a380, 0xc4200fc000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
        /usr/lib/golang/src/crypto/tls/conn.go:1134 +0x110
net/http.(*connReader).Read(0xc42005f0b0, 0xc4200fc000, 0x1000, 0x1000, 0xc420001800, 0x44abe8, 0x7e13b83b9a)
        /usr/lib/golang/src/net/http/server.go:753 +0x105
bufio.(*Reader).fill(0xc420054540)
        /usr/lib/golang/src/bufio/bufio.go:97 +0x11a
bufio.(*Reader).Peek(0xc420054540, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0)
        /usr/lib/golang/src/bufio/bufio.go:129 +0x3a
net/http.(*conn).readRequest(0xc42006e0a0, 0x8e9400, 0xc420050780, 0x0, 0x0, 0x0)
        /usr/lib/golang/src/net/http/server.go:930 +0xc77
net/http.(*conn).serve(0xc42006e0a0, 0x8e9400, 0xc420050780)
        /usr/lib/golang/src/net/http/server.go:1739 +0x50e
created by net/http.(*Server).Serve
        /usr/lib/golang/src/net/http/server.go:2720 +0x288
goroutine 20 [runnable]:
net/http.setRequestCancel.func3(0x0, 0xc4203f1740, 0xc420051040, 0xc42001975c, 0xc42001ecc0)
        /usr/lib/golang/src/net/http/client.go:320 +0x118
created by net/http.setRequestCancel
        /usr/lib/golang/src/net/http/client.go:319 +0x2bf

goroutine 18 [sleep]:
time.Sleep(0xb2d05e00)
        /usr/lib/golang/src/runtime/time.go:65 +0x130
github.com/jmhodges/clock.(*sysClock).Sleep(0x944600, 0xb2d05e00)
        /root/go/src/github.com/jmhodges/clock/clock.go:76 +0x2b
github.com/letsencrypt/pebble/va.VAImpl.performValidation(0xc420064280, 0x8ea100, 0x944600, 0x138a, 0x1389, 0xc4200542a0, 0x1, 0xc4203ee100, 0xc420054f60)
        /root/go/src/github.com/letsencrypt/pebble/va/va.go:229 +0x551
created by github.com/letsencrypt/pebble/va.VAImpl.process
        /root/go/src/github.com/letsencrypt/pebble/va/va.go:208 +0x31a

goroutine 19 [sleep]:
time.Sleep(0x342770c00)
        /usr/lib/golang/src/runtime/time.go:65 +0x130
github.com/jmhodges/clock.(*sysClock).Sleep(0x944600, 0x342770c00)
        /root/go/src/github.com/jmhodges/clock/clock.go:76 +0x2b
github.com/letsencrypt/pebble/va.VAImpl.performValidation(0xc420064280, 0x8ea100, 0x944600, 0x138a, 0x1389, 0xc4200542a0, 0x1, 0xc4203ee100, 0xc420054f60)
        /root/go/src/github.com/letsencrypt/pebble/va/va.go:229 +0x551
created by github.com/letsencrypt/pebble/va.VAImpl.process
        /root/go/src/github.com/letsencrypt/pebble/va/va.go:208 +0x31a

I sadly don't know enough of go to debug this but I can help in reproducing/testing if needed.

Return malformed problem for already revoked certificates

Reported by @felixfontein in #134:

ACME defines no behavior if one tries to revoke a certificate which has already been revoked. Boulder returns error type urn:ietf:params:acme:error:malformed (or urn:acme:error:malformed for ACME v1) with detail Certificate already revoked, while Pebble simply returns a 404

Pebble should return a problem here instead of a bare 404. It seems like matching Boulder and using a problem with type urn:ietf:params:acme:error:malformed is the best bet.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.