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

Create db.Store interface to abstract actions to DB #412

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions ca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const (

type CAImpl struct {
log *log.Logger
db *db.MemoryStore
db db.Store
ocspResponderURL string

chains []*chain
Expand Down Expand Up @@ -347,7 +347,7 @@ func (ca *CAImpl) newCertificate(domains []string, ips []net.IP, key crypto.Publ
return newCert, nil
}

func New(log *log.Logger, db *db.MemoryStore, ocspResponderURL string, alternateRoots int, chainLength int, certificateValidityPeriod uint64) *CAImpl {
func New(log *log.Logger, db db.Store, ocspResponderURL string, alternateRoots int, chainLength int, certificateValidityPeriod uint64) *CAImpl {
ca := &CAImpl{
log: log,
db: db,
Expand Down
12 changes: 7 additions & 5 deletions cmd/pebble/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type config struct {
DomainBlocklist []string

CertificateValidityPeriod uint64

RetryAfter struct {
Authz int
Order int
Expand Down Expand Up @@ -75,21 +76,22 @@ func main() {
chainLength = int(val)
}

db := db.NewMemoryStore()
ca := ca.New(logger, db, c.Pebble.OCSPResponderURL, alternateRoots, chainLength, c.Pebble.CertificateValidityPeriod)
var dbStore db.Store
dbStore = db.NewMemoryStore()
ca := ca.New(logger, dbStore, c.Pebble.OCSPResponderURL, alternateRoots, chainLength, c.Pebble.CertificateValidityPeriod)
va := va.New(logger, c.Pebble.HTTPPort, c.Pebble.TLSPort, *strictMode, *resolverAddress)

for keyID, key := range c.Pebble.ExternalAccountMACKeys {
err := db.AddExternalAccountKeyByID(keyID, key)
err := dbStore.AddExternalAccountKeyByID(keyID, key)
cmd.FailOnError(err, "Failed to add key to external account bindings")
}

for _, domainName := range c.Pebble.DomainBlocklist {
err := db.AddBlockedDomain(domainName)
err := dbStore.AddBlockedDomain(domainName)
cmd.FailOnError(err, "Failed to add domain to block list")
}

wfeImpl := wfe.New(logger, db, va, ca, *strictMode, c.Pebble.ExternalAccountBindingRequired, c.Pebble.RetryAfter.Authz, c.Pebble.RetryAfter.Order)
wfeImpl := wfe.New(logger, dbStore, va, ca, *strictMode, c.Pebble.ExternalAccountBindingRequired, c.Pebble.RetryAfter.Authz, c.Pebble.RetryAfter.Order)
muxHandler := wfeImpl.Handler()

if c.Pebble.ManagementListenAddress != "" {
Expand Down
49 changes: 7 additions & 42 deletions db/memorystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package db

import (
"crypto"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"math/big"
Expand All @@ -22,15 +19,7 @@ import (
"github.com/letsencrypt/pebble/v2/core"
)

// ExistingAccountError is an error type indicating when an operation fails
// because the MatchingAccount has a key conflict.
type ExistingAccountError struct {
MatchingAccount *core.Account
}

func (e ExistingAccountError) Error() string {
return fmt.Sprintf("New public key is already in use by account %s", e.MatchingAccount.ID)
}
var _ Store = (*MemoryStore)(nil)

// Pebble keeps all of its various objects (accounts, orders, etc)
// in-memory, not persisted anywhere. MemoryStore implements this in-memory
Expand Down Expand Up @@ -84,7 +73,7 @@ func (m *MemoryStore) GetAccountByID(id string) *core.Account {
}

func (m *MemoryStore) GetAccountByKey(key crypto.PublicKey) (*core.Account, error) {
keyID, err := keyToID(key)
keyID, err := KeyToID(key)
if err != nil {
return nil, err
}
Expand All @@ -103,7 +92,7 @@ func (m *MemoryStore) UpdateAccountByID(id string, acct *core.Account) error {
if m.accountsByID[id] == nil {
return fmt.Errorf("account with ID %q does not exist", id)
}
keyID, err := keyToID(acct.Key)
keyID, err := KeyToID(acct.Key)
if err != nil {
return err
}
Expand All @@ -120,7 +109,7 @@ func (m *MemoryStore) AddAccount(acct *core.Account) (int, error) {
return 0, fmt.Errorf("account must not have a nil Key")
}

keyID, err := keyToID(acct.Key)
keyID, err := KeyToID(acct.Key)
if err != nil {
return 0, err
}
Expand All @@ -147,12 +136,12 @@ func (m *MemoryStore) ChangeAccountKey(acct *core.Account, newKey *jose.JSONWebK
m.Lock()
defer m.Unlock()

oldKeyID, err := keyToID(acct.Key)
oldKeyID, err := KeyToID(acct.Key)
if err != nil {
return err
}

newKeyID, err := keyToID(newKey)
newKeyID, err := KeyToID(newKey)
if err != nil {
return err
}
Expand Down Expand Up @@ -363,30 +352,6 @@ func (m *MemoryStore) RevokeCertificate(cert *core.RevokedCertificate) {
delete(m.certificatesByID, cert.Certificate.ID)
}

/*
* keyToID produces a string with the hex representation of the SHA256 digest
* over a provided public key. We use this to associate public keys to
* acme.Account objects, and to ensure every account has a unique public key.
*/
func keyToID(key crypto.PublicKey) (string, error) {
switch t := key.(type) {
case *jose.JSONWebKey:
if t == nil {
return "", fmt.Errorf("Cannot compute ID of nil key")
}
return keyToID(t.Key)
case jose.JSONWebKey:
return keyToID(t.Key)
default:
keyDER, err := x509.MarshalPKIXPublicKey(key)
if err != nil {
return "", err
}
spkiDigest := sha256.Sum256(keyDER)
return hex.EncodeToString(spkiDigest[:]), nil
}
}

// GetCertificateBySerial loops over all certificates to find the one that matches the provided
// serial number. This method is linear and it's not optimized to give you a quick response.
func (m *MemoryStore) GetCertificateBySerial(serialNumber *big.Int) *core.Certificate {
Expand Down Expand Up @@ -443,7 +408,7 @@ func (m *MemoryStore) AddExternalAccountKeyByID(keyID, key string) error {

// GetExternalAccountKeyByID will return the raw, base64 URL unencoded key
// value by its key ID pair.
func (m *MemoryStore) GetExtenalAccountKeyByID(keyID string) ([]byte, bool) {
func (m *MemoryStore) GetExternalAccountKeyByID(keyID string) ([]byte, bool) {
m.RLock()
defer m.RUnlock()
key, ok := m.externalAccountKeysByID[keyID]
Expand Down
77 changes: 77 additions & 0 deletions db/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package db

import (
"crypto"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"fmt"
"github.com/letsencrypt/pebble/v2/acme"
"github.com/letsencrypt/pebble/v2/core"
"gopkg.in/square/go-jose.v2"
"math/big"
)

// ExistingAccountError is an error type indicating when an operation fails
// because the MatchingAccount has a key conflict.
type ExistingAccountError struct {
MatchingAccount *core.Account
}

func (e ExistingAccountError) Error() string {
return fmt.Sprintf("New public key is already in use by account %s", e.MatchingAccount.ID)
}

/*
* KeyToID produces a string with the hex representation of the SHA256 digest
* over a provided public key. We use this to associate public keys to
* acme.Account objects, and to ensure every account has a unique public key.
*/
func KeyToID(key crypto.PublicKey) (string, error) {
switch t := key.(type) {
case *jose.JSONWebKey:
if t == nil {
return "", fmt.Errorf("Cannot compute ID of nil key")
}
return KeyToID(t.Key)
case jose.JSONWebKey:
return KeyToID(t.Key)
default:
keyDER, err := x509.MarshalPKIXPublicKey(key)
if err != nil {
return "", err
}
spkiDigest := sha256.Sum256(keyDER)
return hex.EncodeToString(spkiDigest[:]), nil
}
}

// Pebble keeps all of its various objects (accounts, orders, etc)
// in-memory, not persisted anywhere. MemoryStore implements this in-memory
// "database"
type Store interface {
GetAccountByID(id string) *core.Account
GetAccountByKey(key crypto.PublicKey) (*core.Account, error)
UpdateAccountByID(id string, acct *core.Account) error
AddAccount(acct *core.Account) (int, error)
ChangeAccountKey(acct *core.Account, newKey *jose.JSONWebKey) error
AddOrder(order *core.Order) (int, error)
GetOrderByID(id string) *core.Order
GetOrdersByAccountID(accountID string) []*core.Order
AddAuthorization(authz *core.Authorization) (int, error)
GetAuthorizationByID(id string) *core.Authorization
FindValidAuthorization(accountID string, identifier acme.Identifier) *core.Authorization
AddChallenge(chal *core.Challenge) (int, error)
GetChallengeByID(id string) *core.Challenge
AddCertificate(cert *core.Certificate) (int, error)
GetCertificateByID(id string) *core.Certificate
GetCertificateByDER(der []byte) *core.Certificate
GetRevokedCertificateByDER(der []byte) *core.RevokedCertificate
RevokeCertificate(cert *core.RevokedCertificate)
GetCertificateBySerial(serialNumber *big.Int) *core.Certificate
GetRevokedCertificateBySerial(serialNumber *big.Int) *core.RevokedCertificate
AddExternalAccountKeyByID(keyID, key string) error
GetExternalAccountKeyByID(keyID string) ([]byte, bool)
AddBlockedDomain(name string) error
IsDomainBlocked(name string) bool
}
6 changes: 3 additions & 3 deletions wfe/wfe.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (th *topHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

type WebFrontEndImpl struct {
log *log.Logger
db *db.MemoryStore
db db.Store
nonce *nonceMap
nonceErrPercent int
authzReusePercent int
Expand All @@ -172,7 +172,7 @@ const ToSURL = "data:text/plain,Do%20what%20thou%20wilt"

func New(
log *log.Logger,
db *db.MemoryStore,
db db.Store,
va *va.VAImpl,
ca *ca.CAImpl,
strict, requireEAB bool, retryAfterAuthz int, retryAfterOrder int) WebFrontEndImpl {
Expand Down Expand Up @@ -2787,7 +2787,7 @@ func (wfe *WebFrontEndImpl) verifyEAB(

//3. Retrieve the MAC key corresponding to the key identifier in the
// "kid" field
key, ok := wfe.db.GetExtenalAccountKeyByID(keyID)
key, ok := wfe.db.GetExternalAccountKeyByID(keyID)
if !ok {
return nil, acme.UnauthorizedProblem(
"the field 'kid' references a key that is not known to the ACME server")
Expand Down