Commit Graph

25 Commits

Author SHA1 Message Date
Aaron Gable 9a99831fce
Truncate ARI timestamps to millisecond resolution (#485)
It's not useful, and somewhat surprising, for ARI timestamps to have
nanosecond precision. Truncate them to millisecond precision to make
life easier on clients, and to do something different from Boulder
(which truncates to whole-second precision).

Fixes https://github.com/letsencrypt/pebble/issues/483
2025-02-21 15:01:28 -08:00
Jonathan Vanasco bc21177aca
return logical and compliant ARI windows for expiring certs (#484)
While testing ARI functionality with extremely short lived certs, Pebble
returned illogical data - such as the windowEnd appearing in the future
for already expired certs, or after the cert's notAfter.

This PR adjusts `RenewalInfoSimple` to do the following:

* if the cert has expired, just return `RenewalInfoImmediate`
* if the cert will expire before the `windowEnd`, set the `windowEnd` to
the cert's `notAfter`
* cleanup: RFC requires the `windowStart` to be before `windowEnd`, so
remove a second from the `windowStart` if needed.

I also made the `RenewalInfoImmediate` window change from `-60:-30`
minutes to `-60:-59.59` minutes; the sole reason for this change is that
it is visually easier to see the change (and not need to do mental math)
and recognize this is a `RenewalInfoImmediate` from logs.
2025-02-21 09:46:22 -08:00
Phil Porada db1f5873a0
Implement latest draft-ietf-acme-ari spec (#461)
The draft spec version at the time of this PR was
draft-ietf-acme-ari-03, but failed replacement order handling is from
the [yet-to-be-released
draft-ietf-acme-ari-04](1813de294a/draft-ietf-acme-ari.md (L177)).

* Add a `renewalInfo` entry to the directory object which provides the
base URL for ARI requests.
* Add a new WFE handlefunc which parses incoming requests and returns
reasonable `renewalInfo` for determining when the client should attempt
renewal of a certificate.
* Add support for marking orders as `replaced`. Replacement orders can
be chained, but there can be no duplicate replacement of orders, just
like boulder.
* Restructured the asynchronous finalization anonymous go func to handle
storing replaced orders. To be replaced, an order must previously have
been finalized and have an issued certificate.
2024-05-24 12:06:36 -04:00
Matthew McPherrin ace954224d
Switch to go-jose v4 (#445)
Switch to go-jose v4

This required a change to specify the supported signing algorithms, as
part of go-jose/v4's breaking changes to defend against some attacks on
JWS.

Because Pebble already limited the supported algorithms, it was easy to
see what the supported algorithms are.
2024-02-27 11:37:10 -08:00
Ludovic Fernandez 5b7dc87635
Use GitHub Actions (#442)
Add lint, build (multi-platform), and test (linux-only) GitHub Actions. Fix
a variety of golangci-lint findings.

For now, does not modify the windows Appveyor setup, and does not
run the loadtester.

Fixes #356
2024-02-26 17:58:53 -08:00
Jacob Hoffman-Andrews e4d28c7f09
Modernize (#376)
- Use Go 1.18.x in Travis, and set go.mod version to 1.17.
 - Update golangci-lint to 1.45.2 and ignore new lints.
 - Update import paths to include /v2/. This fixes a problem
  introduced by fixing the go.mod module path to use /v2/.
 - Remove golangci-lint from appveyor.
2022-04-14 15:45:48 -07:00
alexzorin b60b0b677c
Add PEBBLE_CHAIN_LENGTH. (#320)
This commit adds a way to control how long the certificate issuance
chains that Pebble uses are. The default and minimum value of 1 means
that there is one intermediate certificate in the final certificate
chain.

Fixes #318.


The resulting certificate chain (at a value of `2`) looks like this:

    $ cat b.pem | openssl crl2pkcs7 -nocrl -certfile /dev/stdin | openssl pkcs7 -print_certs -noout
    subject=CN = xfoobazx.com
    issuer=CN = Pebble Intermediate CA 2f1dd1
    subject=CN = Pebble Intermediate CA 2f1dd1
    issuer=CN = Pebble Intermediate CA 5a564f
    subject=CN = Pebble Intermediate CA 5a564f
    issuer=CN = Pebble Root CA 0335fe

The change is a little bit awkward because Pebble has been, in parts, written with an
assumption about there being one intermediate.

The additional intermediates are not exposed via the management interface. It might
be possible to make a non-breaking change to `:15000/intermediate-keys/`, but I
haven't been able to think of a use-case for exposing them.
2020-08-27 13:17:51 -07:00
Felix Fontein ddbb755c42 management: add /cert-status-by-serial/ endpoint (#252)
This adds a `/cert-status-by-serial/` endpoint to the management interface. The
certificate is identified by its serial number (in hexadecimal).
The endpoint returns the certificate itself (in PEM format), the serial
(hexadecimal) and the revocation status (`Valid` or `Revoked`) as a JSON object.

Example usage:
```
$ curl -ki https://127.0.0.1:15000/cert-status-by-serial/5ab954d39632fd78
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: 1671
date: Fri, 12 Jul 2019 21:39:24 GMT

{
     "Certificate": "-----BEGIN CERTIFICATE-----\nMIIEVz...tcw=\n-----END CERTIFICATE-----\n",
     "Serial": "5ab954d39632fd78",
     "Status": "Valid"
}

$ curl -ki https://127.0.0.1:15000/cert-status-by-serial/7811e0b42b98cc1c
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: 1728
date: Fri, 12 Jul 2019 22:14:17 GMT

{
     "Certificate": "-----BEGIN CERTIFICATE-----\nMIIEVz...tcw=\n-----END CERTIFICATE-----\n",
     "RevokedAt": "2019-07-13T00:14:13.719654003+02:00",
     "Serial": "7811e0b42b98cc1c",
     "Status": "Revoked"
}

$ 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"
}
```
2019-07-26 14:17:38 -04:00
Felix Fontein 0abe0523af Implement and offer alternate chains (RFC 8555 7.4.2) (#234)
Allows to offer alternate chains for a certificate according to RFC8555, Section
7.4.2. For this, multiple root and intermediate certs are created (all with the
same intermediate private key), and `alternative` relation links are offered
during certificate download. A `/roots/` endpoint has also been added to
allow downloading the alternate roots (`/roots/0`, `/roots/1` etc.); Pebble also
provides `link` headers for its alternate forms in responses to certificate
requests.

This feature can be enabled by setting the environment variable
`PEBBLE_ALTERNATE_ROOTS` to something larger than 0. For example, with
`PEBBLE_ALTERNATE_ROOTS=2`:

```.sh
$ curl -i -k https://0.0.0.0:14000/root
HTTP/2 200 
cache-control: public, max-age=0, no-cache
content-type: application/pem-certificate-chain; charset=utf-8
link: <https://0.0.0.0:14000/dir>;rel="index"
link: <https://0.0.0.0:14000/roots/0>;rel="alternate"
link: <https://0.0.0.0:14000/roots/1>;rel="alternate"
content-length: 1107
date: Sun, 12 May 2019 15:05:37 GMT

-----BEGIN CERTIFICATE-----
...

$ curl -i -k https://0.0.0.0:14000/roots/0
HTTP/2 200 
cache-control: public, max-age=0, no-cache
content-type: application/pem-certificate-chain; charset=utf-8
link: <https://0.0.0.0:14000/dir>;rel="index"
link: <https://0.0.0.0:14000/root>;rel="alternate"
link: <https://0.0.0.0:14000/roots/0>;rel="alternate"
content-length: 1107
date: Sun, 12 May 2019 15:06:07 GMT

-----BEGIN CERTIFICATE-----
...
```
2019-06-21 12:40:44 -04:00
orangepizza bc4da68d49 Support draft-acme-ip and IP address identifiers (#221)
Implements https://datatracker.ietf.org/doc/draft-ietf-acme-ip/ support in Pebble.
2019-06-03 13:41:16 -04:00
Daniel McCarney 22e0a4bcb4 fix dataraces, add -race install, add load-gen. (#232)
The `Authz()` method of the WFE was racey. First because it didn't lock the authorizations and orders it was working with. Second because the handling of displaying authorization challenges was working with `acme.Challenge` objects owned by `core.Challenge`'s that should have been locked for reading but were not. This mean the VA would datarace with the WFE when updating a validated challenge status.

To prevent future occurrences `travis.yml` is updated to install Pebble with the race detector enabled, and to run Pebble such that it will exit non-zero if a race is detected.

Since `Chisel2.py` is single threaded the Boulder `load-generator` is used for a short duration to drive concurrent request traffic. In practice before fixing the dataraces I found this would crash Pebble <30s.

Resolves https://github.com/letsencrypt/pebble/issues/230
Resolves https://github.com/letsencrypt/pebble/issues/228
2019-04-15 10:53:25 -07:00
Daniel McCarney 8bc2d5654a
dep: remove jmhodges/clock dependency. (#231)
It was inherited from Boulder but was never used anywhere in Pebble.
2019-04-12 12:38:18 -04:00
Daniel McCarney 51ec98e481 core: remove Account ID field from JSON responses. (#225)
The `core.Account` type has an `ID` string field that is used to
internally track the ID of the ACME account. It should not be returned
in JSON `Account` resource responses, it's not an RFC 8555 specified
field. Boulder may return a similar field but that's a convenience and
not a protocol feature. The RFC 8555 method of learning the account's ID
is from the `Location` header.
2019-04-01 09:38:19 -07:00
Daniel McCarney 703daa840c WFE: Fix revocation authentication. (#137)
This commit brings over more of Boulder's WFE2 revocation handling to
Pebble and supports **both** authenticating revocation requests with:
* a JWS with a KeyID specifing the account that previously issued the certificate
* a JWS carrying an embedded JWK containing the certificate to be revoked's public key.

Previously the revocation implementation specified it only
supported authenticating the request with the certificate's key and an
embedded JWK. In practice the revocation logic was still expecting a key
ID and authentication from the ACME account. It also did not verify that 
the certificate to be revoked was originally issued by the account that
authenticates the revocation request.

Resolves https://github.com/letsencrypt/pebble/issues/134
2018-06-27 15:16:33 -07:00
Daniel McCarney fd24bc8615
Handle order status as a property of assoc. authz status. (#128)
This PR updates the way Pebble handles an Order's status field to closer match the way we handle it in Boulder. This means the order's status is considered to be a calculated field derived based on the order's error/beganProcessing/certificate fields and the status/expires/error of the order's associated authorizations. Along the way the new "Ready" status was implemented. This PR also addresses the problem of the order status not being updated when an authorization is failed. Now, an order has a problem details field that is set when an authorization associated with the order has a failed challenge validation.

Resolves #110 and #98
2018-05-22 09:02:37 -04:00
Daniel McCarney a4a69de761 Move to Go 1.10, add `go vet` to CI, fix vet errors. (#103)
This commit updates the `.travis.yml` to move to Go 1.10 to match Boulder's Go version. Some `go vet` errors were fixed (thanks to @shred for pointing those out initially!). To keep these minor mistakes from piling up again over time this commit also adds an explicit `go vet` step to the CI tasks.
2018-03-14 12:41:27 -07:00
Daniel McCarney e90b88062a Remove proactive issuance and add order finalization. (#47)
This commit implements https://github.com/ietf-wg-acme/acme/pull/342 - replacing proactive issuance and CSR as part of new-order with an explicit order finalization step that delivers the CSR.

This is largely a port of the work done to add order finalization to the WIP ACMEv2 support in Boulder:
https://github.com/letsencrypt/boulder/pull/3169 

I haven't tested this end-to-end yet - There are likely bugs lurking :-)
2017-12-05 14:57:09 -08:00
Daniel McCarney 8121ff9d08 Removes "registration" references in favour of "account" (#28)
The ACME spec no longer talks about "registrations" and Pebble shouldn't either. This PR updates all registration occurrences to use the term "account" instead.
2017-07-05 12:01:22 -07:00
Daniel McCarney f471a5b567 Multi-validation & true async issuance (#17)
**Updates the README**
5491a24 updates the README to include Chisel usage instructions 
& reference the current-most draft.

**Adds multi-validation and true async issuance.**
7d615b5 splits the operation of the CA and VA across multiple
go-routines instead of doing all work on the WFE's handler routine.

The VA now performs multiple validations, sleeping a random amount of
time between each. This forces the ACME client to poll the
authorizations to learn when they have switched from pending to invalid
or valid. All validation attempts must succeed for the authorization to
be valid.

The WFE no longer communicates directly with the CA, instead when the VA
updates the last authorization on an order to valid status it invokes
the CA's CompleteOrder function in a separate goroutine. This will
complete the order & update it with a certificate pointer. The WFE uses
this pointer to construct the certificate URL as required.
2017-04-13 15:44:51 -07:00
Daniel McCarney 5112962d3c Certificate issuance (#16)
This PR adds the initial CA skeleton for doing order issuance.

Pebble generates a root & intermediate keypair/certificate at startup. The intermediate is used for certificate signing purposes and the root issues the intermediate and is otherwise just there to mimick production. In the future we should introduce additional intermediates & chain options to use the full specification. 

Couple other changes:
* Fixed the embedded challenges in authorizations to use a pointer so that the embedded contents are updated when the challenge is completed.
* Replaced a few WFE `Printf`'s with log statements.
* Updated the VA to log whether an HTTP validation was a success or a failure.
* Added a pointer from Authorizations to the Order they belong to
* Added enforcement of Order expiry for authz updates.
* Moved the MemoryStore out of the `wfe` package. This was primarily to let the CA store the root & intermediate certificates in the DB. This allows using the `/certZ/` endpoint to retrieve the root & intermediate.
2017-03-22 08:54:49 -07:00
Jacob Hoffman-Andrews cba621486a More validation fixes. (#14)
Add an HTTPPort config and plumb it through.
Calculate the KeyAuthorization based on regID, rather than storing it on
challenge (wasn't getting stored previously).
Show URL in validation records for ease of debugging.
2017-03-17 15:15:19 -07:00
Jacob Hoffman-Andrews cccbf50cda Various fixes noticed during chisel integration. (#11)
Move Key from ACME type to internal type, since this was made internal-only in
the most recent draft.

Make NotBefore/NotAfter omitempty.

Make Challenges a list of objects rather than a list of URLs, per the spec (yep,
this is somewhat inconsistent with how authorizations are list of URLs).

Change Challenge.URL to Challenge.URI.

Format Order and Authorization Expires properly (RFC3339), and in UTC.

Write out the ACME object for Orders rather than the internal object.
2017-03-14 10:01:39 -04:00
Jacob Hoffman-Andrews 71e32014bb Fix build. (#10)
Two inflight changes recently conflicted to produce a build break.

Also introduces a trivial .travis.yml that just checks for build success.
2017-03-14 09:58:04 -04:00
Daniel McCarney eb092043e2 Support HTTP-01 challenge validation. (#8)
This commit adds initial support for HTTP-01 challenge validation. No
other challenge types are implemented at this time.

The implementation is largely a pared down version of what Boulder does
presently. For simplicities sake there are a number of important
differences that make Pebble *absolutely* unappropriate for production
uses:
  1) The validation is performed in the same goroutine as the WFE's HTTP
     handler instead of in a separate go routine. It might be worth
     revamping this in the near future.
  2) There is no read limit on the HTTP request body and timeouts are
     not as strict
  3) The validity of both pending and valid authzs is hardcoded at 1h.

As compared to Boulder the Pebble challenge design chooses to use
a separate endpoint (e.g. `/chalZ/9999` for chal 9999) instead of
referring to challenges subindexed from authorizations (e.g.
`/authZ/9999/1` for the first challenge of authz 9999). I believe this
is still within compliance with the overall specification and will
further exercise clients to not expect Boulder's choices to be
universal. The pebble client is updated to allow specifying a URL
to POST in addition to a directory endpoint to allow POSTing these
separate non-directory defined challenge URLs.
2017-03-13 10:15:27 -07:00
Daniel McCarney be500adb10 Adds authorizations to orders. (#7)
Refactors the `acme` package to clearly separate out `core` objects that 
are used by Pebble internally vs those that are read to/from protocol messages.

Few misc fixes:
 * fixed returning the parsedCSR for the order endpoint
 * fixed the pebble-client shell to not bail on the "meta" directory
 * updated the in-memory DB's "add" functions to return a count to avoid
   needing funcs like `countRegistration`

Added the authorization types, challenge types, identifier types, and a 
"pending" status type.

New orders now have a pending authorization created for each of the
identifiers present in the CSR's SAN fields. Each authorization has a
http-01 challenge created for it. Authorizations, orders and challenges
are persisted in memory and have GET methods.

TODO: Create TLS-SNI-02 and DNS-01 challenges
2017-02-24 10:54:16 -08:00