CertMagic Documentation

repository·master·Indexed 26 days ago

https://github.com/caddyserver/certmagic

CertMagic is a high-level TLS automation library for Go that provides automated certificate issuance, renewal, and ACME challenge handling. It supports multiple issuers, On-Demand TLS, OCSP stapling, and various ACME challenges (HTTP, TLS-ALPN, and DNS via libdns). Developers can serve HTTPS with a single call using certmagic.HTTPS() or integrate it into existing applications via certmagic.Listen() and certmagic.TLS().

Tokens
15.9K
Snippets
23
Records
123
Agent score
90%

What's inside CertMagic

  1. Overview of CertMagic features

    master

    CertMagic is a powerful ACME client integration for Go that provides:

    • Automated Certificate Management: Full lifecycle including issuance, renewal, and revocation.
    • Multiple Issuers: Support for multiple CAs for redundancy.
    • ACME Challenge Support: Solves HTTP, TLS-ALPN, and DNS challenges (via libdns).
    • On-Demand TLS: Issuance of certificates during the TLS handshake.
    • Advanced Reliability: Robust error handling, exponential backoff, and retries for up to 30 days.
    • OCSP Stapling: Automatic stapling with support for replacing revoked certificates.
    • Pluggable Architecture: Supports custom storage backends and key sources.
    • Scalability: Capable of managing hundreds of thousands of names per instance.
  2. Solve the HTTP ACME Challenge

    master

    The HTTP challenge requires port 80 to be accessible. If you are running your own HTTP server, you can solve this challenge in two ways:

    1. Wrap your handler: Use HTTPChallengeHandler to wrap your existing http.Handler.
    2. Manual handling: Call HandleHTTPChallenge inside your ServeHTTP method.

    If you are not running an HTTP server, you should either disable the HTTP challenge or run a dedicated HTTP server solely for solving this challenge.

  3. Run CertMagic in a cluster or behind a load balancer

    master

    To run multiple instances of CertMagic in a cluster (sharing the same certificates and OCSP staples), ensure all instances use the same Storage implementation.

    By default, CertMagic uses the file system. If you mount the same shared folder to all instances, they will function as a single cluster. You can implement custom Storage if needed, provided all instances use the same implementation.

  4. Solve the TLS-ALPN ACME Challenge

    master

    The TLS-ALPN challenge requires port 443. It is the most convenient challenge type as it uses the standard TLS port. To solve it, you must provide the CertMagic tls.Config to your TLS listener.

    You can either use magic.TLSConfig() directly or manually update an existing tls.Config by setting GetCertificate and appending acmez.ACMETLS1Protocol to NextProtos.

  5. Bind to low ports (80/443) without root on Linux

    master

    To allow your application to listen on ports 80 and 443 without running as root on Linux, use setcap to grant the binary the cap_net_bind_service capability.

    $ sudo setcap cap_net_bind_service=+ep /path/to/your/binary
  6. Advanced usage: Custom Cache and Config

    master

    For complex requirements—such as managing certificates differently based on their name—you must implement a custom Cache and Config.

    In this pattern:

    1. Create a certmagic.Cache with a GetConfigForCert callback.
    2. Inside the callback, use certmagic.New(cache, ...) to create a Config that is correctly associated with that cache.
    3. Use magic.ManageSync() to trigger certificate acquisition/renewal.
    4. Use magic.TLSConfig() or magic.GetCertificate to integrate with your existing TLS listeners.
    5. If using the HTTP-01 challenge, wrap your HTTP multiplexer with myACME.HTTPChallengeHandler(httpMux).
    // First make a pointer to a Cache as we need to reference the same Cache in
    // GetConfigForCert below.
    var cache *certmagic.Cache
    cache = certmagic.NewCache(certmagic.CacheOptions{
    	GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) {
    		// Here we use New to get a valid Config associated with the same cache.
    		// The provided Config is used as a template and is completed with
    		// any defaults that are set in the Default config.
    		return certmagic.New(cache, certmagic.Config{
    			// ...
    		}), nil
    	},
    	...
    })
    
    magic := certmagic.New(cache, certmagic.Config{
    	// any customizations you need go here
    })
    
    myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
    	CA:     certmagic.LetsEncryptStagingCA,
    	Email:  "you@yours.com",
    	Agreed: true,
    	// plus any other customizations you need
    })
    
    magic.Issuers = []certmagic.Issuer{myACME}
    
    // this obtains certificates or renews them if necessary
    err := magic.ManageSync(context.TODO(), []string{"example.com", "sub.example.com"})
    if err != nil {
    	return err
    }
    
    // to use its certificates and solve the TLS-ALPN challenge, 
    // you can get a TLS config to use in a TLS listener!
    tlsConfig := magic.TLSConfig()
    
    // be sure to customize NextProtos if serving a specific
    // application protocol after the TLS handshake, for example:
    tlsConfig.NextProtos = append([]string{"h2", "http/1.1"}, tlsConfig.NextProtos...)
    
    //// OR ////
    
    // if you already have a TLS config you don't want to replace, 
    // we can simply set its GetCertificate field and append the
    // TLS-ALPN challenge protocol to the NextProtos
    myTLSConfig.GetCertificate = magic.GetCertificate
    myTLSConfig.NextProtos = append(myTLSConfig.NextProtos, acmez.ACMETLS1Protocol)
    
    // the HTTP challenge has to be handled by your HTTP server; 
    // if you don't have one, you should have disabled it earlier 
    // when you made the certmagic.Config
    httpMux = myACME.HTTPChallengeHandler(httpMux)
  7. Solve the DNS ACME Challenge

    master

    The DNS challenge allows obtaining certificates (including wildcards) without requiring the server to be publicly accessible on low ports. It works by setting a DNS record via an API.

    To enable it, set the DNS01Solver field on a certmagic.ACMEIssuer or the certmagic.DefaultACME.DNS01Solver variable. CertMagic supports any DNS provider that implements the libdns interface.

    import "github.com/libdns/cloudflare"
    
    certmagic.DefaultACME.DNS01Solver = &certmagic.DNS01Solver{
    	DNSManager: certmagic.DNSManager{
    		DNSProvider: &cloudflare.Provider{
    			APIToken: "topsecret",
    		},
    	},
    }
  8. Configure CertMagic Cache

    master

    CertMagic caches certificates in memory for performance. This cache must be backed by a persistent Storage implementation.

    Most users should simply set certmagic.Default.Storage to define how the cache is persisted. If you require different storage for different Config instances, use certmagic.NewCache(storage) and then create configs via certmagic.NewWithCache(cache).

  9. Configure CertMagic defaults and best practices

    master

    CertMagic uses a template-based configuration system. You should modify the package-level certmagic.Default or certmagic.DefaultACME variables to set your preferences, then use certmagic.NewDefault() to create a valid, usable Config object.

    Best Practices:

    • Provide an email address: Set certmagic.DefaultACME.Email to receive expiration notices and allow CA engineers to contact you if issues arise.
    • Agree to terms: Set certmagic.DefaultACME.Agreed = true to accept the CA's legal documents.
    • Use staging for development: To avoid hitting Let's Encrypt production rate limits during testing, set certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA.

    Rate Limiting: CertMagic has built-in rate limiting (default: 10 transactions per 1 minute). You can adjust this using certmagic.RateLimitEvents and certmagic.RateLimitEventsWindow.

    // read and agree to your CA's legal documents
    certmagic.DefaultACME.Agreed = true
    
    // provide an email address
    certmagic.DefaultACME.Email = "you@yours.com"
    
    // use the staging endpoint while we're developing
    certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA
  10. Enable On-Demand TLS

    master

    On-Demand TLS allows a server to obtain certificates for arbitrary domain names during the TLS handshake. This is useful when domain names are not known ahead of time.

    To enable it, set the OnDemand field of a Config (or the package-level certmagic.Default.OnDemand) to a non-nil *certmagic.OnDemandConfig.

    Security Note: Because On-Demand TLS can be used for DoS attacks against CAs, you should use a DecisionFunc to implement a policy (e.g., a whitelist) to control which domains are allowed to trigger issuance.

    // Simple enablement
    certmagic.Default.OnDemand = new(certmagic.OnDemandConfig)
    
    // Advanced control with DecisionFunc
    certmagic.Default.OnDemand = &certmagic.OnDemandConfig{
    	DecisionFunc: func(name string) error {
    		if name != "example.com" {
    			return fmt.Errorf("not allowed")
    		}
    		return nil
    	},
    }
  11. Configure CertMagic Storage

    master

    CertMagic requires persistent storage for certificates and TLS assets (OCSP staples, locks, etc.). Using ephemeral storage will likely lead to CA rate limiting.

    • Default Storage: Local file system at $HOME/.local/share/certmagic (or $XDG_DATA_HOME).
    • Custom Storage: Set certmagic.Default.Storage to an implementation of the Storage interface.
    • Clustering: Multiple CertMagic instances form a 'cluster' if they share the same storage configuration (e.g., a shared network drive or a distributed KV store).