Skip to content

Commit 9b94d4f

Browse files
Roland Bracewell Shoemakerjsha
authored andcommitted
Add a orphan queue to the CA (letsencrypt#3832)
Retains the existing logging of orphaned certs until we are confident that this solution can fully replace it (even then we may want to keep it just for auditing etc). Fixes letsencrypt#3636.
1 parent 00be062 commit 9b94d4f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+19430
-11
lines changed

Godeps/Godeps.json

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ca/ca.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"strings"
1818
"time"
1919

20+
"github.com/beeker1121/goque"
2021
cfsslConfig "github.com/cloudflare/cfssl/config"
2122
cferr "github.com/cloudflare/cfssl/errors"
2223
"github.com/cloudflare/cfssl/ocsp"
@@ -36,6 +37,7 @@ import (
3637
"github.com/letsencrypt/boulder/goodkey"
3738
blog "github.com/letsencrypt/boulder/log"
3839
"github.com/letsencrypt/boulder/metrics"
40+
"github.com/letsencrypt/boulder/sa"
3941
"github.com/prometheus/client_golang/prometheus"
4042
)
4143

@@ -130,6 +132,7 @@ type CertificateAuthorityImpl struct {
130132
enableMustStaple bool
131133
signatureCount *prometheus.CounterVec
132134
csrExtensionCount *prometheus.CounterVec
135+
orphanQueue *goque.Queue
133136
}
134137

135138
// Issuer represents a single issuer certificate, along with its key.
@@ -195,6 +198,7 @@ func NewCertificateAuthorityImpl(
195198
issuers []Issuer,
196199
keyPolicy goodkey.KeyPolicy,
197200
logger blog.Logger,
201+
orphanQueue *goque.Queue,
198202
) (*CertificateAuthorityImpl, error) {
199203
var ca *CertificateAuthorityImpl
200204
var err error
@@ -273,6 +277,7 @@ func NewCertificateAuthorityImpl(
273277
enableMustStaple: config.EnableMustStaple,
274278
signatureCount: signatureCount,
275279
csrExtensionCount: csrExtensionCount,
280+
orphanQueue: orphanQueue,
276281
}
277282

278283
if config.Expiry == "" {
@@ -659,8 +664,74 @@ func (ca *CertificateAuthorityImpl) generateOCSPAndStoreCertificate(
659664
// changes here, you should make sure they are reflected in orphan-finder.
660665
ca.log.AuditErrf("Failed RPC to store at SA, orphaning certificate: serial=[%s] cert=[%s] err=[%v], regID=[%d], orderID=[%d]",
661666
core.SerialToString(serialBigInt), hex.EncodeToString(certDER), err, regID, orderID)
667+
if ca.orphanQueue != nil {
668+
ca.queueOrphan(&orphanedCert{
669+
DER: certDER,
670+
OCSPResp: ocspResp,
671+
RegID: regID,
672+
})
673+
}
662674
return core.Certificate{}, err
663675
}
664676

665677
return core.Certificate{DER: certDER}, nil
666678
}
679+
680+
type orphanedCert struct {
681+
DER []byte
682+
OCSPResp []byte
683+
RegID int64
684+
}
685+
686+
func (ca *CertificateAuthorityImpl) queueOrphan(o *orphanedCert) {
687+
if _, err := ca.orphanQueue.EnqueueObject(o); err != nil {
688+
ca.log.AuditErrf("failed to queue orphan for integration: %s", err)
689+
}
690+
}
691+
692+
// OrphanIntegrationLoop runs a loop executing integrateOrphans and then waiting a minute.
693+
// It is split out into a separate function called directly by boulder-ca in order to make
694+
// testing the orphan queue functionality somewhat more simple.
695+
func (ca *CertificateAuthorityImpl) OrphanIntegrationLoop() {
696+
for {
697+
if err := ca.integrateOrphan(); err != nil {
698+
if err == goque.ErrEmpty {
699+
time.Sleep(time.Minute)
700+
continue
701+
}
702+
ca.log.AuditErrf("failed to integrate orphaned certs: %s", err)
703+
}
704+
}
705+
}
706+
707+
// integrateOrpan removes an orphan from the queue and adds it to the database. The
708+
// item isn't dequeued until it is actually added to the database to prevent items from
709+
// being lost if the CA is restarted between the item being dequeued and being added to
710+
// the database. It calculates the issuance time by subtracting the backdate period from
711+
// the notBefore time.
712+
func (ca *CertificateAuthorityImpl) integrateOrphan() error {
713+
item, err := ca.orphanQueue.Peek()
714+
if err != nil {
715+
if err == goque.ErrEmpty {
716+
return goque.ErrEmpty
717+
}
718+
return fmt.Errorf("failed to peek into orphan queue: %s", err)
719+
}
720+
var orphan orphanedCert
721+
if err = item.ToObject(&orphan); err != nil {
722+
return fmt.Errorf("failed to marshal orphan: %s", err)
723+
}
724+
cert, err := x509.ParseCertificate(orphan.DER)
725+
if err != nil {
726+
return fmt.Errorf("failed to parse orphan: %s", err)
727+
}
728+
issued := cert.NotBefore.Add(-ca.backdate)
729+
_, err = ca.sa.AddCertificate(context.Background(), orphan.DER, orphan.RegID, orphan.OCSPResp, &issued)
730+
if err != nil && err != sa.ErrDuplicate {
731+
return fmt.Errorf("failed to store orphaned certificate: %s", err)
732+
}
733+
if _, err = ca.orphanQueue.Dequeue(); err != nil {
734+
return fmt.Errorf("failed to dequeue integrated orphaned certificate: %s", err)
735+
}
736+
return nil
737+
}

0 commit comments

Comments
 (0)