]> go.fuhry.dev Git - runtime.git/commitdiff
[mint] support new mtls config provider API
authorDan Fuhry <dan@fuhry.com>
Sun, 15 Mar 2026 00:08:57 +0000 (20:08 -0400)
committerDan Fuhry <dan@fuhry.com>
Sun, 15 Mar 2026 01:17:55 +0000 (21:17 -0400)
mint/BUILD.bazel
mint/client.go
mint/mtls_driver.go
mint/servicer/BUILD.bazel
mint/servicer/client.go [new file with mode: 0644]

index d5a9662932fe32563fb88374df946e4f8fc81cec..84460a492445dc7482d584d856b135dc95ced2df 100644 (file)
@@ -18,5 +18,6 @@ go_library(
         "//utils/log",
         "//utils/option",
         "@com_github_quic_go_quic_go//:quic-go",
+        "@in_gopkg_yaml_v3//:yaml_v3",
     ],
 )
index 932e544671ddca698e600f1ff43066a9e675c574..6306b96c2ab1782a8eed3c4a9afb931440d566bd 100644 (file)
@@ -49,6 +49,8 @@ type clientImpl struct {
 type Client interface {
        GetCertificate(service string) (*MintCertificate, error)
        GetRootCertificate() (*x509.Certificate, error)
+
+       Logger() log.Logger
 }
 
 type ClientOption = option.Option[*clientImpl]
@@ -59,11 +61,14 @@ var mintQuicConfig = &quic.Config{
        KeepAlivePeriod:      5 * time.Second,
 }
 
-var _ mtls.CertificateProvider = &MintCertificate{}
-var DefaultClientContext context.Context
-var defaultIdentityStr string
-var defaultClient Client
-var defaultClientMu sync.Mutex
+var (
+       _                    mtls.CertificateProvider = &MintCertificate{}
+       DefaultClientContext context.Context
+       defaultIdentityStr   string
+       defaultClient        Client
+       defaultClientMu      sync.Mutex
+       renewRetryInterval   = 15 * time.Second
+)
 
 func defaultIdentity() mtls.Identity {
        if defaultIdentityStr != "" {
@@ -206,14 +211,7 @@ func (c *clientImpl) GetCertificate(subject string) (*MintCertificate, error) {
        c.log.Noticef("GetCertificate(%q): ok: obtained cert with serial %s valid until %s",
                subject, leaf.SerialNumber.String(), leaf.NotAfter.UTC().Format(time.RFC3339))
 
-       mc := &MintCertificate{
-               client:  c,
-               service: subject,
-               private: privateKey,
-               leaf:    leaf,
-               chain:   chain,
-               root:    root,
-       }
+       mc := NewCertificate(c, subject, privateKey, leaf, chain, root)
        c.cache[subject] = mc
        return mc, nil
 }
@@ -269,6 +267,70 @@ retry:
        return mintCl, nil
 }
 
+func (c *clientImpl) Logger() log.Logger {
+       return c.log
+}
+
+func NewCertificate(client Client, service string, privateKey crypto.PrivateKey, leaf *x509.Certificate, chain []*x509.Certificate, root *x509.Certificate) *MintCertificate {
+       mc := &MintCertificate{
+               client:  client,
+               service: service,
+               private: privateKey,
+               leaf:    leaf,
+               chain:   chain,
+               root:    root,
+       }
+       mc.scheduleRenew()
+
+       return mc
+}
+
+func (m *MintCertificate) renew() error {
+       newCert, err := m.client.GetCertificate(m.service)
+       if err != nil {
+               return err
+       }
+
+       m.mu.Lock()
+       defer m.mu.Unlock()
+
+       m.root = newCert.root
+       m.leaf = newCert.leaf
+       m.chain = newCert.chain
+       m.private = newCert.private
+       m.scheduleRenew()
+
+       return nil
+}
+
+func (m *MintCertificate) scheduleRenew() {
+       log := m.client.Logger()
+
+       now := time.Now()
+       expires := now
+       if m.leaf != nil {
+               expires = m.leaf.NotAfter
+       }
+
+       // renew in 2/3 of time between now and expiration time
+       renewIn := ((2 * expires.Sub(now)) / 3).Round(100 * time.Millisecond)
+       if now.Equal(expires) || now.After(expires) {
+               // but if already expired, renew immediately
+               renewIn = 0
+       }
+
+       go (func() {
+               log.Infof("certificate %q will be renewed in %s", m.service, renewIn.String())
+               time.Sleep(renewIn)
+               err := m.renew()
+               if err != nil {
+                       log.Errorf("error renewing certificate for service %q, retrying in %s", m.service, renewRetryInterval)
+                       time.Sleep(renewRetryInterval)
+                       m.scheduleRenew()
+               }
+       })()
+}
+
 // RootCertificate implements mtls.CertificateProvider.
 func (m *MintCertificate) RootCertificate() (*x509.Certificate, error) {
        m.mu.RLock()
@@ -291,7 +353,9 @@ func (m *MintCertificate) LeafCertificate() (*x509.Certificate, error) {
        defer m.mu.RUnlock()
 
        if err := certutil.ValidNow(m.leaf); err != nil {
-               return nil, err
+               if err = m.renew(); err != nil {
+                       return nil, err
+               }
        }
 
        return m.leaf, nil
index 9512e05aa36c10d9fabaf5a02123c2bdb68ca39b..d47a8092f38994e9f8520198d9c821e6dc607adb 100644 (file)
@@ -6,8 +6,11 @@ import (
 
        "go.fuhry.dev/runtime/mtls"
        "go.fuhry.dev/runtime/utils/log"
+       "gopkg.in/yaml.v3"
 )
 
+type mintIdentityProviderFactory struct{}
+
 var depth atomic.Int32
 
 func tryGetMintIdentity(cls mtls.PrincipalClass, name string) (mtls.CertificateProvider, error) {
@@ -35,6 +38,11 @@ func tryGetMintIdentity(cls mtls.PrincipalClass, name string) (mtls.CertificateP
        return cert, nil
 }
 
+func (f *mintIdentityProviderFactory) New(node *yaml.Node) (mtls.IdentityLoaderFunc, error) {
+       return tryGetMintIdentity, nil
+}
+
 func init() {
        mtls.RegisterIdentityDriver("mint", tryGetMintIdentity)
+       mtls.RegisterProviderFactory("mint", &mintIdentityProviderFactory{})
 }
index 874482c9577b86d37ae1a5473e43787487049699..416ed0ff9a576e450e70e334e02772694b5c88e5 100644 (file)
@@ -4,6 +4,7 @@ go_library(
     name = "servicer",
     srcs = [
         "acl.go",
+        "client.go",
         "servicer.go",
         "signer.go",
     ],
@@ -13,6 +14,7 @@ go_library(
         "//config_watcher",
         "//ephs",
         "//grpc",
+        "//mint",
         "//mint/remote_signer",
         "//mtls",
         "//mtls/certutil",
diff --git a/mint/servicer/client.go b/mint/servicer/client.go
new file mode 100644 (file)
index 0000000..c0c4c48
--- /dev/null
@@ -0,0 +1,29 @@
+package servicer
+
+import (
+       "crypto/x509"
+       "errors"
+
+       "go.fuhry.dev/runtime/mint"
+       "go.fuhry.dev/runtime/mtls"
+       "go.fuhry.dev/runtime/utils/log"
+)
+
+type mintServerClient struct {
+       serverId mtls.Identity
+       log      log.Logger
+}
+
+var _ mint.Client = &mintServerClient{}
+
+func (c *mintServerClient) GetCertificate(service string) (*mint.MintCertificate, error) {
+       return nil, errors.New("not implemented")
+}
+
+func (c *mintServerClient) GetRootCertificate() (*x509.Certificate, error) {
+       return c.serverId.RootCertificate()
+}
+
+func (c *mintServerClient) Logger() log.Logger {
+       return c.log
+}