caddyserver / certmagic

Automatic HTTPS for any Go program: fully-managed TLS certificate issuance and renewal
https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc
Apache License 2.0
4.89k stars 278 forks source link

Allow specific issuers for specific on-demand domains #298

Open mbardelmeijer opened 5 days ago

mbardelmeijer commented 5 days ago

What is your question?

We use LetsEncrypt & ZeroSSL's API issuers. We prefer to configure to allow domains to use LetsEncrypt, and IP addresses the ZeroSSL API only. We can only specify a singular DecisionFunc and define the Issuers globally. Is it possible to define that some domains may use 1 issuer and other domains another?

What have you already tried?

No attempts have been made.

mohammed90 commented 5 days ago

It's actually doable using the JSON config. See here, you can enable on_demand on a specific automation policy, which includes specifying the issuer.

mholt commented 4 days ago

It is possible as Mohammed said, but note that this is the CertMagic repo not the Caddy repo. I'm mobile but I'll reply with details soonish

mholt commented 4 days ago

@mbardelmeijer So, the answer to your question: "Is it possible to define that some domains may use 1 issuer and other domains another?" -- is yes.

In your DecisionFunc, just return true for the domains or IPs that are allowed to get an on-demand cert, and false for the others.

We can only specify a singular DecisionFunc and define the Issuers globally

I don't see how this is the case, since these are specified only per-Config, e.g.: https://pkg.go.dev/github.com/caddyserver/certmagic#Config.OnDemand

Use the relevant Config for your domain(s); how you do this depends on your code/program, but without actual code I can't provide any more specifics that are helpful or relevant. Caddy does it, you could look at its source code. You may find tls.Config.GetConfigForClient() helpful.

mbardelmeijer commented 4 days ago

@mholt thanks for those details -- great to hear it's possible by using separate configs! Our implementation (cleaned up) looks like below.

In this format, I'm unsure how to best implement different configs per Issuer. Do we need to return a new config instance in the GetConfigForCert there, or is there another (recommend) way?

var cache *certmagic.Cache
cache = certmagic.NewCache(certmagic.CacheOptions{
    GetConfigForCert: func(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 will be completed with
        // any defaults that are set in the Default config.
        return certmagic.New(cache, certmagic.Config{
            OnEvent: onEvent,
        }), nil
    },
    Logger: logger.With(zap.String("component", "acme_cache")),
})

magic := certmagic.New(cache, certmagic.Config{
    OnEvent: onEvent,
})

magic.Logger = logger.With(zap.String("component", "acme"))
magic.Storage = NewCertStoreFromCertificatesConfig(&config.Certificates)
magic.OnDemand = &certmagic.OnDemandConfig{
    DecisionFunc: func(ctx context.Context, name string) error {
        // Decision logic. We're unsure about the `Issuer` here, as it shares the same config instance.
        log.Debugf("Accepted to request certificate for: %s", name)
        return nil
    },
}

if config.Certificates.LetsEncryptEnabled {
    magic.Issuers = append(magic.Issuers, certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
        CA:                   config.Certificates.LetsEncryptCa,
        Logger:               logger.With(zap.String("component", "letsencrypt_acme_issuer")),
        Email:                config.Certificates.AcmeEmail,
        Agreed:               true,
    }))
}

// @TODO: we wish only to allow this ZeroSSL API for IP address decisions.
if config.Certificates.ZeroSSLAPIEnabled {
    // https://github.com/caddyserver/certmagic#zerossl
    magic.Issuers = append(magic.Issuers, &certmagic.ZeroSSLIssuer{
        APIKey: config.Certificates.ZeroSSLAPIKey,
        Storage: magic.Storage, // https://github.com/caddyserver/certmagic/issues/291
        Logger: logger.With(zap.String("component", "zerossl_api_issuer")),
    })
}
mholt commented 1 day ago

That looks pretty good. Yeah, so if each domain has a different certmagic.Config (an issuer is just part of a Config), then you'll want to make a new Config for each one, with the right issuer(s) for each domain.