@@ -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