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 5 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"`
}
44 changes: 43 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,44 @@ 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 {
// Lock the authorization for reading
authz.RLock()
if authz.Status != acme.StatusValid {
return
}
authz.RUnlock()
}

order.RLock()
Copy link
Contributor

Choose a reason for hiding this comment

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

You need to lock earlier, before you fetch order.AuthorixationObjects, Also I think a full lock would be fine; we're not worried about performance here, and there's no deadlock possibility, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch on locking too late here. Thanks.

if order.Status != acme.StatusPending {
ca.log.Printf("Error: Asked to complete order %s is not status pending, was status %s",
order.ID, order.Status)
return
}
order.RUnlock()

// Update the order to reflect that we're now processing it
order.Lock()
defer order.Unlock()
ca.log.Printf("Order %s is fully authorized. Ready to issue", order.ID)
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
// issue a certificate for the csr
cert, err := ca.newCertificate(csr.DNSNames, csr.PublicKey)
if err != nil {
ca.log.Printf("Error: unable to issue order: %s", err.Error())
return
}
ca.log.Printf("Issued certificate serial %s for order %s\n", cert.ID, order.ID)

// Update the order to valid status and store a cert ID for the wfe to use to
// render the certificate URL for the order
order.Status = acme.StatusValid
order.CertID = cert.ID
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 you need to store a copy of cert.ID? Why not use it directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"Why not use it directly" -> You mean a pointer to the certificate?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah; I wrote this comment before the earlier one. Is the certificate stored in memory separately from the order? If so, then yes a pointer to the certificate would be good. Otherwise, DER should work.

Copy link
Contributor

Choose a reason for hiding this comment

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

Aha, should have read the latest diffs before replying. This looks great.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since this field is only used to check whether a certificate is done, maybe it would be better to create a field that contains the cert DER, since we'll actually need that later. Otherwise, it's a bit confusing why there are two "ID" fields.

}
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
11 changes: 11 additions & 0 deletions core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ import (
"encoding/base64"
"encoding/pem"
"fmt"
"sync"
"time"

"github.com/letsencrypt/pebble/acme"
"gopkg.in/square/go-jose.v2"
)

type Order struct {
sync.RWMutex
acme.Order
ID string
ParsedCSR *x509.CertificateRequest
ExpiresDate time.Time
AuthorizationObjects []*Authorization
CertID string
}

type Registration struct {
Expand All @@ -28,6 +31,7 @@ type Registration struct {
}

type Authorization struct {
sync.RWMutex
acme.Authorization
ID string
URL string
Expand All @@ -36,6 +40,7 @@ type Authorization struct {
}

type Challenge struct {
sync.RWMutex
acme.Challenge
ID string
Authz *Authorization
Expand Down Expand Up @@ -98,3 +103,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
}
6 changes: 6 additions & 0 deletions db/memorystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ func (m *MemoryStore) AddOrder(order *core.Order) (int, error) {
m.Lock()
defer m.Unlock()

order.RLock()
orderID := order.ID
if len(orderID) == 0 {
return 0, fmt.Errorf("order must have a non-empty ID to add to MemoryStore")
}
order.RUnlock()

if _, present := m.ordersByID[orderID]; present {
return 0, fmt.Errorf("order %q already exists", orderID)
Expand All @@ -90,10 +92,12 @@ func (m *MemoryStore) AddAuthorization(authz *core.Authorization) (int, error) {
m.Lock()
defer m.Unlock()

authz.RLock()
authzID := authz.ID
if len(authzID) == 0 {
return 0, fmt.Errorf("authz must have a non-empty ID to add to MemoryStore")
}
authz.RUnlock()

if _, present := m.authorizationsByID[authzID]; present {
return 0, fmt.Errorf("authz %q already exists", authzID)
Expand All @@ -113,7 +117,9 @@ func (m *MemoryStore) AddChallenge(chal *core.Challenge) (int, error) {
m.Lock()
defer m.Unlock()

chal.RLock()
chalID := chal.ID
chal.RUnlock()
if len(chalID) == 0 {
return 0, fmt.Errorf("challenge must have a non-empty ID to add to MemoryStore")
}
Expand Down
Loading