Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-validation & true async issuance #17

Merged
merged 7 commits into from
Apr 13, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@ randomize keys/certificates used for issuance.

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-04 support. Boulder is multi-process, requires heavy dependencies (MariaDB,
ACME-06 support. Boulder is multi-process, requires heavy dependencies (MariaDB,
RabbitMQ, etc), and is operationally complex to integrate with other projects.

Where possible Pebble aims to produce code that can be used to inform the
pending Boulder support for ACME-06, through contribution of code as well as
design lessons learned. Development of Pebble is meant to be rapid, and to
produce a minimum working prototype on a short schedule.

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
Expand All @@ -48,3 +54,17 @@ clients are not hardcoding URLs.)
## Usage

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

## Issuance

The easiest way to test issue with Pebble is to use `chisel2` from the
`acme-v2` certbot branch (this is a work in progress).

1. `git clone -b acme-v2 https://github.com/certbot/certbot`
2. `cd certbot`
3. `letsencrypt-auto-source/letsencrypt-auto --os-packages-only`
4. `./tools/venv.sh`
5. `. venev/bin/activate`
6. `python ./certbot/tools/chisel2.py example.com`


19 changes: 7 additions & 12 deletions acme/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,11 @@ type Authorization struct {

// A Challenge is used to validate an Authorization
type Challenge struct {
Type string `json:"type"`
URI string `json:"uri"`
Token string `json:"token"`
Status string `json:"status"`
Validated string `json:"validated,omitempty"`
ValidationRecords []ValidationRecord
KeyAuthorization string `json:"keyAuthorization,omitempty"`
Error *ProblemDetails `json:"error,omitempty"`
}

type ValidationRecord struct {
URL string
Type string `json:"type"`
URI string `json:"uri"`
Token string `json:"token"`
Status string `json:"status"`
Validated string `json:"validated,omitempty"`
KeyAuthorization string `json:"keyAuthorization,omitempty"`
Error *ProblemDetails `json:"error,omitempty"`
}
40 changes: 39 additions & 1 deletion ca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"math/big"
"time"

"github.com/letsencrypt/pebble/acme"
"github.com/letsencrypt/pebble/core"
"github.com/letsencrypt/pebble/db"
)
Expand Down Expand Up @@ -152,7 +153,7 @@ func (ca *CAImpl) newIntermediateIssuer() error {
return nil
}

func (ca *CAImpl) NewCertificate(domains []string, key crypto.PublicKey) (*core.Certificate, error) {
func (ca *CAImpl) newCertificate(domains []string, key crypto.PublicKey) (*core.Certificate, error) {
var cn string
if len(domains) > 0 {
cn = domains[0]
Expand Down Expand Up @@ -218,3 +219,40 @@ func New(log *log.Logger, db *db.MemoryStore) *CAImpl {
}
return ca
}

func (ca *CAImpl) CompleteOrder(order *core.Order) {
// Check the authorizations - this is done by the VA before calling
// CompleteOrder but we do it again for robustness sake.
for _, authz := range order.AuthorizationObjects {
if authz.Status != acme.StatusValid {
return
}
}

if order.Status != acme.StatusPending {
ca.log.Printf("Error: Asked to complete orrder %s is not status pending, was status %s",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*order %s, which is not

order.ID, order.Status)
return
}

ca.log.Printf("Order %s is fully authorized. Ready to issue", order.ID)
// Update the order to reflect that we're now processing it
order.Status = acme.StatusProcessing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to hold a lock on the order.


csr := order.ParsedCSR
domains := make([]string, len(csr.DNSNames))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to make a copy?

copy(domains, csr.DNSNames)

// issue a certificate for the csr
cert, err := ca.newCertificate(domains, csr.PublicKey)
if err != nil {
ca.log.Printf("Error: unable to issue order: %s", err.Error())
return
}
ca.log.Printf("Issued certificate serial %s\n", cert.ID)

// Update the order to valid status with a certificate URL
order.Status = acme.StatusValid
order.Certificate = order.CertPathPrefix + cert.ID
ca.log.Printf("Order %s has Certificate %s", order.ID, order.Certificate)
}
4 changes: 2 additions & 2 deletions cmd/pebble/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ func main() {

clk := clock.Default()
db := db.NewMemoryStore()
va := va.New(logger, clk, c.Pebble.HTTPPort)
ca := ca.New(logger, db)
wfe := wfe.New(logger, clk, db, va, ca)
va := va.New(logger, clk, c.Pebble.HTTPPort, ca)
wfe := wfe.New(logger, clk, db, va)
muxHandler := wfe.Handler()

srv := &http.Server{
Expand Down
7 changes: 7 additions & 0 deletions core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Order struct {
ParsedCSR *x509.CertificateRequest
ExpiresDate time.Time
AuthorizationObjects []*Authorization
CertPathPrefix string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than embedding a public-facing path (WFE's responsibility) in the internal Order object, let's embed a certificate ID, and make WFE responsible for rendering that into a certificate path.

}

type Registration struct {
Expand Down Expand Up @@ -98,3 +99,9 @@ func (c Certificate) Chain() []byte {
// Return the chain, leaf cert first
return bytes.Join(chain, nil)
}

type ValidationRecord struct {
URL string
Error *acme.ProblemDetails
ValidatedAt time.Time
}
Loading