Skip to content

Commit 65ee56a

Browse files
authored
feat(client): WithRegistrationDelay (#58)
adds optional delay before initial registration (when no cert exists yet)
1 parent edd8ed2 commit 65ee56a

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
@@ -34,6 +34,7 @@ type P2PForgeCertMgr struct {
3434
cancel func()
3535
forgeDomain string
3636
forgeRegistrationEndpoint string
37+
registrationDelay time.Duration
3738
ProvideHost func(host.Host)
3839
hostFn func() host.Host
3940
hasHost func() bool
@@ -86,6 +87,7 @@ type P2PForgeCertMgrConfig struct {
8687
allowPrivateForgeAddresses bool
8788
produceShortAddrs bool
8889
renewCheckInterval time.Duration
90+
registrationDelay time.Duration
8991
}
9092

9193
type P2PForgeCertMgrOptions func(*P2PForgeCertMgrConfig) error
@@ -191,6 +193,14 @@ func WithRenewCheckInterval(renewCheckInterval time.Duration) P2PForgeCertMgrOpt
191193
}
192194
}
193195

196+
// WithRegistrationDelay allows delaying initial registration to ensure node was online for a while before requesting TLS cert.
197+
func WithRegistrationDelay(registrationDelay time.Duration) P2PForgeCertMgrOptions {
198+
return func(config *P2PForgeCertMgrConfig) error {
199+
config.registrationDelay = registrationDelay
200+
return nil
201+
}
202+
}
203+
194204
// WithAllowPrivateForgeAddrs is meant for testing or skipping all the
195205
// connectivity checks libp2p node needs to pass before it can request domain
196206
// and start ACME DNS-01 challenge.
@@ -300,6 +310,7 @@ func NewP2PForgeCertMgr(opts ...P2PForgeCertMgrOptions) (*P2PForgeCertMgr, error
300310
log: mgrCfg.log,
301311
allowPrivateForgeAddresses: mgrCfg.allowPrivateForgeAddresses,
302312
produceShortAddrs: mgrCfg.produceShortAddrs,
313+
registrationDelay: mgrCfg.registrationDelay,
303314
}
304315

305316
// NOTE: callback getter is necessary to avoid circular dependency
@@ -399,11 +410,21 @@ func (m *P2PForgeCertMgr) Start() error {
399410
}
400411
m.ctx, m.cancel = context.WithCancel(context.Background())
401412
go func() {
413+
start := time.Now()
402414
log := m.log.Named("start")
403415
h := m.hostFn()
404416
name := certName(h.ID(), m.forgeDomain)
405417
certExists := localCertExists(m.ctx, m.certmagic, name)
406418
startCertManagement := func() {
419+
// respect WithRegistrationDelay if no cert exists
420+
if !certExists && m.registrationDelay != 0 {
421+
remainingDelay := m.registrationDelay - time.Since(start)
422+
if remainingDelay > 0 {
423+
log.Infof("registration delay set to %s, sleeping for remaining %s", m.registrationDelay, remainingDelay)
424+
time.Sleep(remainingDelay)
425+
}
426+
}
427+
// start internal certmagic instance
407428
if err := m.certmagic.ManageAsync(m.ctx, []string{name}); err != nil {
408429
log.Error(err)
409430
}
@@ -434,6 +455,7 @@ func (m *P2PForgeCertMgr) Start() error {
434455
}
435456

436457
// withHostConnectivity executes callback func only after certain libp2p connectivity checks / criteria against passed host are fullfilled.
458+
// It will also delay registration to ensure user-set registrationDelay is respected.
437459
// The main purpose is to not bother CA ACME endpoint or p2p-forge registration endpoint if we know the peer is not
438460
// ready to use TLS cert.
439461
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
@@ -504,25 +504,27 @@ func TestLibp2pACMEE2E(t *testing.T) {
504504
isValidShortForgeAddr := func(addr string) bool {
505505
return strings.Contains(addr, "libp2p.direct/tcp/") && strings.Contains(addr, "/tls/ws")
506506
}
507+
defaultAddrCheck := isValidResolvedForgeAddr
507508

508509
tests := []struct {
509-
name string
510-
clientOpts []client.P2PForgeCertMgrOptions
511-
isValidForgeAddr func(addr string) bool
512-
caCertValidityPeriod uint64 // 0 means default from letsencrypt/pebble/ca/v2#defaultValidityPeriod will be used
513-
awaitOnCertRenewed bool // include renewal test
510+
name string
511+
clientOpts []client.P2PForgeCertMgrOptions
512+
isValidForgeAddr func(addr string) bool
513+
caCertValidityPeriod uint64 // 0 means default from letsencrypt/pebble/ca/v2#defaultValidityPeriod will be used
514+
awaitOnCertRenewed bool // include renewal test
515+
expectRegistrationDelay time.Duration // include delayed registration test that fails if registration occured sooner
514516
}{
515517
{
516518
name: "default opts",
517519
clientOpts: []client.P2PForgeCertMgrOptions{},
518-
isValidForgeAddr: isValidResolvedForgeAddr,
520+
isValidForgeAddr: defaultAddrCheck,
519521
},
520522
{
521523
name: "expired cert gets renewed and triggers OnCertRenewed",
522524
clientOpts: []client.P2PForgeCertMgrOptions{
523525
client.WithRenewCheckInterval(5 * time.Second),
524526
},
525-
isValidForgeAddr: isValidResolvedForgeAddr,
527+
isValidForgeAddr: defaultAddrCheck,
526528
caCertValidityPeriod: 30, // letsencrypt/pebble/v2/ca uses int as seconds
527529
awaitOnCertRenewed: true,
528530
},
@@ -536,6 +538,12 @@ func TestLibp2pACMEE2E(t *testing.T) {
536538
clientOpts: []client.P2PForgeCertMgrOptions{client.WithShortForgeAddrs(false)},
537539
isValidForgeAddr: isValidResolvedForgeAddr,
538540
},
541+
{
542+
name: "WithRegistrationDelay() produces a delay",
543+
clientOpts: []client.P2PForgeCertMgrOptions{client.WithRegistrationDelay(15 * time.Second)},
544+
isValidForgeAddr: defaultAddrCheck,
545+
expectRegistrationDelay: 15 * time.Second,
546+
},
539547
}
540548

541549
for _, tt := range tests {
@@ -624,6 +632,7 @@ func TestLibp2pACMEE2E(t *testing.T) {
624632
if err != nil {
625633
t.Fatal(err)
626634
}
635+
start := time.Now()
627636
certMgr.Start()
628637
defer certMgr.Stop()
629638

@@ -667,10 +676,19 @@ func TestLibp2pACMEE2E(t *testing.T) {
667676

668677
select {
669678
case <-certLoaded:
670-
case <-time.After(time.Second * 30):
679+
case <-time.After(time.Second*30 + tt.expectRegistrationDelay):
671680
t.Fatal("timed out waiting for certificate")
672681
}
673682

683+
// optional WithRegistrationDelay test
684+
// confirms registration took longer than the delay defined
685+
if tt.expectRegistrationDelay != 0 {
686+
remainingDelay := tt.expectRegistrationDelay - time.Since(start)
687+
if remainingDelay > 0 {
688+
t.Fatalf("WithRegistrationDelay was expected to delay registration by %s", tt.expectRegistrationDelay)
689+
}
690+
}
691+
674692
var dialAddr multiaddr.Multiaddr
675693
hAddrs := h.Addrs()
676694
for _, addr := range hAddrs {

0 commit comments

Comments
 (0)