Skip to content

attestantio/go-certmanager

go-certmanager

Tag License Go Reference Lint Go Report Card

Go library providing certificate management capabilities for both server and client TLS configurations.

The library supports:

  • Server certificate loading with manual reload via ReloadCertificate() (e.g., on SIGHUP)
  • Client certificate loading for gRPC and TLS connections
  • DNS-based SAN identity extraction with CN fallback (RFC 1123/6125)
  • Flexible certificate fetching via majordomo service
  • Thread-safe operations for concurrent access

This library is used by Attestant projects such as Vouch (Ethereum validator client) and Dirk (distributed remote keymanager) for certificate management in Ethereum staking infrastructure.

Package Overview

Package Description
server/standard Server certificate manager with reload support
client/standard Client certificate manager with optional CA pool
credentials gRPC credential helpers (NewGRPCClientCredentials, NewServerTLSConfig)
san X.509 SAN identity extraction and DNS name validation
testing Pre-generated test certificates and mock majordomo

Table of Contents

Requirements

  • Go 1.25.5 or later
  • go-majordomo for certificate fetching
  • gRPC-Go (required by the credentials package)

Install

go-certmanager is a standard Go module which can be installed with:

go get github.com/attestantio/go-certmanager

Usage

Certificate Fetching

Certificate data is fetched via go-majordomo, which supports pluggable "confidants" for files, HTTP endpoints, secret vaults, etc. You must create a majordomo service and pass it to certificate managers via WithMajordomo().

Setting up a file-based majordomo service:

import (
    "github.com/wealdtech/go-majordomo"
    fsc "github.com/wealdtech/go-majordomo/confidants/fs"
)

confidant, err := fsc.New(ctx)
if err != nil {
    return err
}

majordomoSvc, err := majordomo.New(ctx,
    majordomo.WithConfidants(map[string]majordomo.Confidant{"file": confidant}),
)
if err != nil {
    return err
}

// Use majordomoSvc with certificate managers (see below)

Certificate URIs follow the majordomo format: file:///path/to/cert.pem, https://vault.example.com/secret/cert, etc.

Server Certificate Management

The server package provides certificate management for TLS servers with manual reloading capabilities. Use this for long-running services that need to reload certificates without restarting.

import servercert "github.com/attestantio/go-certmanager/server/standard"

certMgr, err := servercert.New(ctx,
    servercert.WithMajordomo(majordomoSvc),
    servercert.WithCertPEMURI("file:///path/to/server.crt"),
    servercert.WithCertKeyURI("file:///path/to/server.key"),
    servercert.WithLoadTimeout(30*time.Second), // Optional: timeout for certificate fetch operations
)
if err != nil {
    return err
}

// Use in TLS server config
tlsConfig, err := certMgr.GetTLSConfig(ctx)

// Trigger reload (e.g., on SIGHUP)
if err := certMgr.ReloadCertificate(ctx); err != nil {
    log.Warn().Err(err).Msg("Certificate reload failed")
}

For peer-to-peer scenarios where the same certificate is used for both server and client roles, use GetClientTLSConfig() to get a static certificate config suitable for client connections:

// Use the same cert manager for client connections
clientTLSConfig, err := certMgr.GetClientTLSConfig(ctx)
conn, err := grpc.NewClient("peer:port",
    grpc.WithTransportCredentials(credentials.NewTLS(clientTLSConfig)))

This is useful for peer-to-peer communication where a single certificate serves both roles.

Important: GetClientTLSConfig() returns a point-in-time snapshot — it will not reflect subsequent ReloadCertificate() calls. After a SIGHUP reload, callers must re-fetch the client TLS config and re-establish connections, as gRPC does not support in-place credential replacement.

Recommended pattern for SIGHUP handlers:

// In your SIGHUP handler:
if err := certMgr.ReloadCertificate(ctx); err != nil {
    log.Warn().Err(err).Msg("Certificate reload failed")
    return
}

// Re-fetch client TLS config after successful reload
newClientTLSConfig, err := certMgr.GetClientTLSConfig(ctx)
if err != nil {
    log.Error().Err(err).Msg("Failed to get updated client TLS config")
    return
}

// gRPC does not support in-place credential replacement;
// close and re-establish the connection with the new config.
oldConn.Close()
conn, err = grpc.NewClient("peer:port",
    grpc.WithTransportCredentials(credentials.NewTLS(newClientTLSConfig)))

Client Certificate Management

The client package provides certificate loading for client connections.

import clientcert "github.com/attestantio/go-certmanager/client/standard"

certMgr, err := clientcert.New(ctx,
    clientcert.WithMajordomo(majordomoSvc),
    clientcert.WithCertPEMURI("file:///path/to/client.crt"),
    clientcert.WithCertKeyURI("file:///path/to/client.key"),
    clientcert.WithCACertURI("file:///path/to/ca.crt"),      // Optional: CA for server verification
    clientcert.WithLoadTimeout(30*time.Second),               // Optional: timeout for certificate fetch operations
)
if err != nil {
    return err
}

// Get TLS config for client connections
tlsConfig, err := certMgr.GetTLSConfig(ctx)

gRPC Credentials

The credentials package provides helpers for setting up TLS in gRPC services.

Client credentials from a client certificate manager:

import (
    clientcert "github.com/attestantio/go-certmanager/client/standard"
    certcreds "github.com/attestantio/go-certmanager/credentials"
)

clientCertMgr, err := clientcert.New(ctx,
    clientcert.WithMajordomo(majordomoSvc),
    clientcert.WithCertPEMURI("file:///path/to/client.crt"),
    clientcert.WithCertKeyURI("file:///path/to/client.key"),
)
if err != nil {
    return err
}

creds, err := certcreds.NewGRPCClientCredentials(ctx, clientCertMgr)
if err != nil {
    return err
}

conn, err := grpc.NewClient("server:9091", grpc.WithTransportCredentials(creds))

Server TLS with mutual authentication (client certificate verification):

import (
    servercert "github.com/attestantio/go-certmanager/server/standard"
    certcreds "github.com/attestantio/go-certmanager/credentials"
    grpccreds "google.golang.org/grpc/credentials"
)

serverCertMgr, err := servercert.New(ctx,
    servercert.WithMajordomo(majordomoSvc),
    servercert.WithCertPEMURI("file:///path/to/server.crt"),
    servercert.WithCertKeyURI("file:///path/to/server.key"),
)
if err != nil {
    return err
}

tlsCfg, err := certcreds.NewServerTLSConfig(ctx, serverCertMgr, caCertPEM)
if err != nil {
    return err
}

grpcServer := grpc.NewServer(grpc.Creds(grpccreds.NewTLS(tlsCfg)))

SAN Extraction

The san package extracts DNS-based identity from X.509 certificates with CN fallback. DNS names are validated against RFC 1123 and RFC 6125; invalid names are skipped.

import "github.com/attestantio/go-certmanager/san"

// Extract primary identity from certificate
identity, source := san.ExtractIdentity(cert)
// source indicates: IdentitySourceSANDNS, IdentitySourceCN, or IdentitySourceUnknown

// Convenience wrapper that returns just the identity string
name := san.IdentityString(cert)

// Extract all DNS Subject Alternative Names (returned as a CertificateSANs struct)
allSANs := san.ExtractAllSANs(cert)
// Access: allSANs.DNSNames

// Validate a DNS name against RFC 1123 and RFC 6125
if err := san.ValidateDNSName("example.com"); err != nil {
    log.Error().Err(err).Msg("Invalid DNS name")
}

Maintainers

@AntiD2ta @Bez625

Contributing

Contributions are welcome. Please see CONTRIBUTING.md for guidelines.

License

Apache-2.0 - see LICENSE for the full text.

About

Go library for managing server and client TLS certificates with reload support, SAN identity extraction, and gRPC credential helpers.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors