Skip to content

Commit 0b73f1a

Browse files
committed
feat(client): WithRegistrationDelay
adds optional delay before initial registration (when no cert exists yet)
1 parent 84b671c commit 0b73f1a

File tree

2 files changed

+48
-8
lines changed

2 files changed

+48
-8
lines changed

client/acme.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type P2PForgeCertMgr struct {
3333
cancel func()
3434
forgeDomain string
3535
forgeRegistrationEndpoint string
36+
registrationDelay time.Duration
3637
ProvideHost func(host.Host)
3738
hostFn func() host.Host
3839
hasHost func() bool
@@ -84,6 +85,7 @@ type P2PForgeCertMgrConfig struct {
8485
allowPrivateForgeAddresses bool
8586
produceShortAddrs bool
8687
renewCheckInterval time.Duration
88+
registrationDelay time.Duration
8789
}
8890

8991
type P2PForgeCertMgrOptions func(*P2PForgeCertMgrConfig) error
@@ -189,6 +191,14 @@ func WithRenewCheckInterval(renewCheckInterval time.Duration) P2PForgeCertMgrOpt
189191
}
190192
}
191193

194+
// WithRegistrationDelay allows delaying initial registration to ensure node was online for a while before requesting TLS cert.
195+
func WithRegistrationDelay(registrationDelay time.Duration) P2PForgeCertMgrOptions {
196+
return func(config *P2PForgeCertMgrConfig) error {
197+
config.registrationDelay = registrationDelay
198+
return nil
199+
}
200+
}
201+
192202
// WithAllowPrivateForgeAddrs is meant for testing or skipping all the
193203
// connectivity checks libp2p node needs to pass before it can request domain
194204
// and start ACME DNS-01 challenge.
@@ -284,6 +294,7 @@ func NewP2PForgeCertMgr(opts ...P2PForgeCertMgrOptions) (*P2PForgeCertMgr, error
284294
log: mgrCfg.log,
285295
allowPrivateForgeAddresses: mgrCfg.allowPrivateForgeAddresses,
286296
produceShortAddrs: mgrCfg.produceShortAddrs,
297+
registrationDelay: mgrCfg.registrationDelay,
287298
}
288299

289300
// NOTE: callback getter is necessary to avoid circular dependency
@@ -382,11 +393,21 @@ func (m *P2PForgeCertMgr) Start() error {
382393
}
383394
m.ctx, m.cancel = context.WithCancel(context.Background())
384395
go func() {
396+
start := time.Now()
385397
log := m.log.Named("start")
386398
h := m.hostFn()
387399
name := certName(h.ID(), m.forgeDomain)
388400
certExists := localCertExists(m.ctx, m.certmagic, name)
389401
startCertManagement := func() {
402+
// respect WithRegistrationDelay if no cert exists
403+
if !certExists && m.registrationDelay != 0 {
404+
remainingDelay := m.registrationDelay - time.Since(start)
405+
if remainingDelay > 0 {
406+
log.Infof("registration delay set to %s, sleeping for remaining %s", m.registrationDelay, remainingDelay)
407+
time.Sleep(remainingDelay)
408+
}
409+
}
410+
// start internal certmagic instance
390411
if err := m.certmagic.ManageAsync(m.ctx, []string{name}); err != nil {
391412
log.Error(err)
392413
}
@@ -417,6 +438,7 @@ func (m *P2PForgeCertMgr) Start() error {
417438
}
418439

419440
// withHostConnectivity executes callback func only after certain libp2p connectivity checks / criteria against passed host are fullfilled.
441+
// It will also delay registration to ensure user-set registrationDelay is respected.
420442
// The main purpose is to not bother CA ACME endpoint or p2p-forge registration endpoint if we know the peer is not
421443
// ready to use TLS cert.
422444
func withHostConnectivity(ctx context.Context, log *zap.SugaredLogger, h host.Host, callback func()) {

e2e_test.go

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -495,25 +495,27 @@ func TestLibp2pACMEE2E(t *testing.T) {
495495
isValidShortForgeAddr := func(addr string) bool {
496496
return strings.Contains(addr, "libp2p.direct/tcp/") && strings.Contains(addr, "/tls/ws")
497497
}
498+
defaultAddrCheck := isValidResolvedForgeAddr
498499

499500
tests := []struct {
500-
name string
501-
clientOpts []client.P2PForgeCertMgrOptions
502-
isValidForgeAddr func(addr string) bool
503-
caCertValidityPeriod uint64 // 0 means default from letsencrypt/pebble/ca/v2#defaultValidityPeriod will be used
504-
awaitOnCertRenewed bool // include renewal test
501+
name string
502+
clientOpts []client.P2PForgeCertMgrOptions
503+
isValidForgeAddr func(addr string) bool
504+
caCertValidityPeriod uint64 // 0 means default from letsencrypt/pebble/ca/v2#defaultValidityPeriod will be used
505+
awaitOnCertRenewed bool // include renewal test
506+
expectRegistrationDelay time.Duration // include delayed registration test that fails if registration occured sooner
505507
}{
506508
{
507509
name: "default opts",
508510
clientOpts: []client.P2PForgeCertMgrOptions{},
509-
isValidForgeAddr: isValidResolvedForgeAddr,
511+
isValidForgeAddr: defaultAddrCheck,
510512
},
511513
{
512514
name: "expired cert gets renewed and triggers OnCertRenewed",
513515
clientOpts: []client.P2PForgeCertMgrOptions{
514516
client.WithRenewCheckInterval(5 * time.Second),
515517
},
516-
isValidForgeAddr: isValidResolvedForgeAddr,
518+
isValidForgeAddr: defaultAddrCheck,
517519
caCertValidityPeriod: 30, // letsencrypt/pebble/v2/ca uses int as seconds
518520
awaitOnCertRenewed: true,
519521
},
@@ -527,6 +529,12 @@ func TestLibp2pACMEE2E(t *testing.T) {
527529
clientOpts: []client.P2PForgeCertMgrOptions{client.WithShortForgeAddrs(false)},
528530
isValidForgeAddr: isValidResolvedForgeAddr,
529531
},
532+
{
533+
name: "WithRegistrationDelay() produces a delay",
534+
clientOpts: []client.P2PForgeCertMgrOptions{client.WithRegistrationDelay(15 * time.Second)},
535+
isValidForgeAddr: defaultAddrCheck,
536+
expectRegistrationDelay: 15 * time.Second,
537+
},
530538
}
531539

532540
for _, tt := range tests {
@@ -602,6 +610,7 @@ func TestLibp2pACMEE2E(t *testing.T) {
602610
if err != nil {
603611
t.Fatal(err)
604612
}
613+
start := time.Now()
605614
certMgr.Start()
606615
defer certMgr.Stop()
607616

@@ -652,10 +661,19 @@ func TestLibp2pACMEE2E(t *testing.T) {
652661

653662
select {
654663
case <-certLoaded:
655-
case <-time.After(time.Second * 30):
664+
case <-time.After(time.Second*30 + tt.expectRegistrationDelay):
656665
t.Fatal("timed out waiting for certificate")
657666
}
658667

668+
// optional WithRegistrationDelay test
669+
// confirms registration took longer than the delay defined
670+
if tt.expectRegistrationDelay != 0 {
671+
remainingDelay := tt.expectRegistrationDelay - time.Since(start)
672+
if remainingDelay > 0 {
673+
t.Fatalf("WithRegistrationDelay was expected to delay registration by %s", tt.expectRegistrationDelay)
674+
}
675+
}
676+
659677
var dialAddr multiaddr.Multiaddr
660678
hAddrs := h.Addrs()
661679
for _, addr := range hAddrs {

0 commit comments

Comments
 (0)