Enhancements to make ZeroSSL issuer more usable in Caddy
This commit is contained in:
parent
74862ff45a
commit
f7ea6fb698
@ -522,7 +522,7 @@ To use ZeroSSL's API instead, use the [`ZeroSSLIssuer`](https://pkg.go.dev/githu
|
|||||||
magic := certmagic.NewDefault()
|
magic := certmagic.NewDefault()
|
||||||
|
|
||||||
magic.Issuers = []certmagic.Issuer{
|
magic.Issuers = []certmagic.Issuer{
|
||||||
certmagic.NewZeroSSLIssuer(magic, certmagic.ZeroSSLIssuer{
|
certmagic.ZeroSSLIssuer{
|
||||||
APIKey: "<your ZeroSSL API key>",
|
APIKey: "<your ZeroSSL API key>",
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -161,8 +161,8 @@ func (iss *ACMEIssuer) newACMEClient(useTestCA bool) (*acmez.Client, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if u.Scheme != "https" && !isLoopback(u.Host) && !isInternal(u.Host) {
|
if u.Scheme != "https" && !SubjectIsInternal(u.Host) {
|
||||||
return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL)
|
return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required for non-internal CA)", caURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &acmez.Client{
|
client := &acmez.Client{
|
||||||
|
@ -323,14 +323,32 @@ func (iss *ACMEIssuer) isAgreed() bool {
|
|||||||
|
|
||||||
// PreCheck performs a few simple checks before obtaining or
|
// PreCheck performs a few simple checks before obtaining or
|
||||||
// renewing a certificate with ACME, and returns whether this
|
// renewing a certificate with ACME, and returns whether this
|
||||||
// batch is eligible for certificates if using Let's Encrypt.
|
// batch is eligible for certificates. It also ensures that an
|
||||||
// It also ensures that an email address is available.
|
// email address is available if possible.
|
||||||
|
//
|
||||||
|
// IP certificates via ACME are defined in RFC 8738.
|
||||||
func (am *ACMEIssuer) PreCheck(ctx context.Context, names []string, interactive bool) error {
|
func (am *ACMEIssuer) PreCheck(ctx context.Context, names []string, interactive bool) error {
|
||||||
publicCA := strings.Contains(am.CA, "api.letsencrypt.org") || strings.Contains(am.CA, "acme.zerossl.com") || strings.Contains(am.CA, "api.pki.goog")
|
publicCAsAndIPCerts := map[string]bool{ // map of public CAs to whether they support IP certificates (last updated: Q1 2024)
|
||||||
|
"api.letsencrypt.org": false, // https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt
|
||||||
|
"acme.zerossl.com": false, // only supported via their API, not ACME endpoint
|
||||||
|
"api.pki.goog": true, // https://pki.goog/faq/#faq-IPCerts
|
||||||
|
"api.buypass.com": false, // https://community.buypass.com/t/h7hm76w/buypass-support-for-rfc-8738
|
||||||
|
"acme.ssl.com": false,
|
||||||
|
}
|
||||||
|
var publicCA, ipCertAllowed bool
|
||||||
|
for caSubstr, ipCert := range publicCAsAndIPCerts {
|
||||||
|
if strings.Contains(am.CA, caSubstr) {
|
||||||
|
publicCA, ipCertAllowed = true, ipCert
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
if publicCA {
|
if publicCA {
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if !SubjectQualifiesForPublicCert(name) {
|
if !SubjectQualifiesForPublicCert(name) {
|
||||||
return fmt.Errorf("subject does not qualify for a public certificate: %s", name)
|
return fmt.Errorf("subject '%s' does not qualify for a public certificate", name)
|
||||||
|
}
|
||||||
|
if !ipCertAllowed && SubjectIsIP(name) {
|
||||||
|
return fmt.Errorf("subject '%s' cannot have public IP certificate from %s (if CA's policy has changed, please notify the developers in an issue)", name, am.CA)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
cache.go
2
cache.go
@ -16,7 +16,7 @@ package certmagic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
weakrand "math/rand" // seeded elsewhere
|
weakrand "math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -386,8 +386,8 @@ func SubjectQualifiesForCert(subj string) bool {
|
|||||||
|
|
||||||
// SubjectQualifiesForPublicCert returns true if the subject
|
// SubjectQualifiesForPublicCert returns true if the subject
|
||||||
// name appears eligible for automagic TLS with a public
|
// name appears eligible for automagic TLS with a public
|
||||||
// CA such as Let's Encrypt. For example: localhost and IP
|
// CA such as Let's Encrypt. For example: internal IP addresses
|
||||||
// addresses are not eligible because we cannot obtain certs
|
// and localhost are not eligible because we cannot obtain certs
|
||||||
// for those names with a public CA. Wildcard names are
|
// for those names with a public CA. Wildcard names are
|
||||||
// allowed, as long as they conform to CABF requirements (only
|
// allowed, as long as they conform to CABF requirements (only
|
||||||
// one wildcard label, and it must be the left-most label).
|
// one wildcard label, and it must be the left-most label).
|
||||||
@ -395,13 +395,9 @@ func SubjectQualifiesForPublicCert(subj string) bool {
|
|||||||
// must at least qualify for a certificate
|
// must at least qualify for a certificate
|
||||||
return SubjectQualifiesForCert(subj) &&
|
return SubjectQualifiesForCert(subj) &&
|
||||||
|
|
||||||
// localhost, .localhost TLD, and .local TLD are ineligible
|
// loopback hosts and internal IPs are ineligible
|
||||||
!SubjectIsInternal(subj) &&
|
!SubjectIsInternal(subj) &&
|
||||||
|
|
||||||
// cannot be an IP address (as of yet), see
|
|
||||||
// https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt
|
|
||||||
!SubjectIsIP(subj) &&
|
|
||||||
|
|
||||||
// only one wildcard label allowed, and it must be left-most, with 3+ labels
|
// only one wildcard label allowed, and it must be left-most, with 3+ labels
|
||||||
(!strings.Contains(subj, "*") ||
|
(!strings.Contains(subj, "*") ||
|
||||||
(strings.Count(subj, "*") == 1 &&
|
(strings.Count(subj, "*") == 1 &&
|
||||||
@ -416,12 +412,55 @@ func SubjectIsIP(subj string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SubjectIsInternal returns true if subj is an internal-facing
|
// SubjectIsInternal returns true if subj is an internal-facing
|
||||||
// hostname or address.
|
// hostname or address, including localhost/loopback hosts.
|
||||||
|
// Ports are ignored, if present.
|
||||||
func SubjectIsInternal(subj string) bool {
|
func SubjectIsInternal(subj string) bool {
|
||||||
|
subj = strings.ToLower(strings.TrimSuffix(hostOnly(subj), "."))
|
||||||
return subj == "localhost" ||
|
return subj == "localhost" ||
|
||||||
strings.HasSuffix(subj, ".localhost") ||
|
strings.HasSuffix(subj, ".localhost") ||
|
||||||
strings.HasSuffix(subj, ".local") ||
|
strings.HasSuffix(subj, ".local") ||
|
||||||
strings.HasSuffix(subj, ".home.arpa")
|
strings.HasSuffix(subj, ".home.arpa") ||
|
||||||
|
isInternalIP(subj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isInternalIP returns true if the IP of addr
|
||||||
|
// belongs to a private network IP range. addr
|
||||||
|
// must only be an IP or an IP:port combination.
|
||||||
|
func isInternalIP(addr string) bool {
|
||||||
|
privateNetworks := []string{
|
||||||
|
"127.0.0.0/8", // IPv4 loopback
|
||||||
|
"0.0.0.0/16",
|
||||||
|
"10.0.0.0/8", // RFC1918
|
||||||
|
"172.16.0.0/12", // RFC1918
|
||||||
|
"192.168.0.0/16", // RFC1918
|
||||||
|
"169.254.0.0/16", // RFC3927 link-local
|
||||||
|
"::1/7", // IPv6 loopback
|
||||||
|
"fe80::/10", // IPv6 link-local
|
||||||
|
"fc00::/7", // IPv6 unique local addr
|
||||||
|
}
|
||||||
|
host := hostOnly(addr)
|
||||||
|
ip := net.ParseIP(host)
|
||||||
|
if ip == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, privateNetwork := range privateNetworks {
|
||||||
|
_, ipnet, _ := net.ParseCIDR(privateNetwork)
|
||||||
|
if ipnet.Contains(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostOnly returns only the host portion of hostport.
|
||||||
|
// If there is no port or if there is an error splitting
|
||||||
|
// the port off, the whole input string is returned.
|
||||||
|
func hostOnly(hostport string) string {
|
||||||
|
host, _, err := net.SplitHostPort(hostport)
|
||||||
|
if err != nil {
|
||||||
|
return hostport // OK; probably had no port to begin with
|
||||||
|
}
|
||||||
|
return host
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchWildcard returns true if subject (a candidate DNS name)
|
// MatchWildcard returns true if subject (a candidate DNS name)
|
||||||
|
@ -143,7 +143,8 @@ func TestSubjectQualifiesForPublicCert(t *testing.T) {
|
|||||||
{"Sub.Example.COM", true},
|
{"Sub.Example.COM", true},
|
||||||
{"127.0.0.1", false},
|
{"127.0.0.1", false},
|
||||||
{"127.0.1.5", false},
|
{"127.0.1.5", false},
|
||||||
{"69.123.43.94", false},
|
{"1.2.3.4", true},
|
||||||
|
{"69.123.43.94", true},
|
||||||
{"::1", false},
|
{"::1", false},
|
||||||
{"::", false},
|
{"::", false},
|
||||||
{"0.0.0.0", false},
|
{"0.0.0.0", false},
|
||||||
@ -166,7 +167,7 @@ func TestSubjectQualifiesForPublicCert(t *testing.T) {
|
|||||||
{"foo.bar.home.arpa", false},
|
{"foo.bar.home.arpa", false},
|
||||||
{"192.168.1.3", false},
|
{"192.168.1.3", false},
|
||||||
{"10.0.2.1", false},
|
{"10.0.2.1", false},
|
||||||
{"169.112.53.4", false},
|
{"169.112.53.4", true},
|
||||||
{"$hostname", false},
|
{"$hostname", false},
|
||||||
{"%HOSTNAME%", false},
|
{"%HOSTNAME%", false},
|
||||||
{"{hostname}", false},
|
{"{hostname}", false},
|
||||||
|
46
certmagic.go
46
certmagic.go
@ -302,52 +302,6 @@ type OnDemandConfig struct {
|
|||||||
hostAllowlist map[string]struct{}
|
hostAllowlist map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isLoopback returns true if the hostname of addr looks
|
|
||||||
// explicitly like a common local hostname. addr must only
|
|
||||||
// be a host or a host:port combination.
|
|
||||||
func isLoopback(addr string) bool {
|
|
||||||
host := hostOnly(addr)
|
|
||||||
return host == "localhost" ||
|
|
||||||
strings.Trim(host, "[]") == "::1" ||
|
|
||||||
strings.HasPrefix(host, "127.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// isInternal returns true if the IP of addr
|
|
||||||
// belongs to a private network IP range. addr
|
|
||||||
// must only be an IP or an IP:port combination.
|
|
||||||
// Loopback addresses are considered false.
|
|
||||||
func isInternal(addr string) bool {
|
|
||||||
privateNetworks := []string{
|
|
||||||
"10.0.0.0/8",
|
|
||||||
"172.16.0.0/12",
|
|
||||||
"192.168.0.0/16",
|
|
||||||
"fc00::/7",
|
|
||||||
}
|
|
||||||
host := hostOnly(addr)
|
|
||||||
ip := net.ParseIP(host)
|
|
||||||
if ip == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, privateNetwork := range privateNetworks {
|
|
||||||
_, ipnet, _ := net.ParseCIDR(privateNetwork)
|
|
||||||
if ipnet.Contains(ip) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// hostOnly returns only the host portion of hostport.
|
|
||||||
// If there is no port or if there is an error splitting
|
|
||||||
// the port off, the whole input string is returned.
|
|
||||||
func hostOnly(hostport string) string {
|
|
||||||
host, _, err := net.SplitHostPort(hostport)
|
|
||||||
if err != nil {
|
|
||||||
return hostport // OK; probably had no port to begin with
|
|
||||||
}
|
|
||||||
return host
|
|
||||||
}
|
|
||||||
|
|
||||||
// PreChecker is an interface that can be optionally implemented by
|
// PreChecker is an interface that can be optionally implemented by
|
||||||
// Issuers. Pre-checks are performed before each call (or batch of
|
// Issuers. Pre-checks are performed before each call (or batch of
|
||||||
// identical calls) to Issue(), giving the issuer the option to ensure
|
// identical calls) to Issue(), giving the issuer the option to ensure
|
||||||
|
@ -599,7 +599,7 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
|
|||||||
// are compliant, so their CSR requirements just needlessly add friction, complexity,
|
// are compliant, so their CSR requirements just needlessly add friction, complexity,
|
||||||
// and inefficiency for clients. CommonName has been deprecated for 25+ years.
|
// and inefficiency for clients. CommonName has been deprecated for 25+ years.
|
||||||
useCSR := csr
|
useCSR := csr
|
||||||
if _, ok := issuer.(*ZeroSSLIssuer); ok {
|
if issuer.IssuerKey() == zerosslIssuerKey {
|
||||||
useCSR, err = cfg.generateCSR(privKey, []string{name}, true)
|
useCSR, err = cfg.generateCSR(privKey, []string{name}, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -281,7 +281,7 @@ func hashCertificateChain(certChain [][]byte) string {
|
|||||||
func namesFromCSR(csr *x509.CertificateRequest) []string {
|
func namesFromCSR(csr *x509.CertificateRequest) []string {
|
||||||
var nameSet []string
|
var nameSet []string
|
||||||
// TODO: CommonName should not be used (it has been deprecated for 25+ years,
|
// TODO: CommonName should not be used (it has been deprecated for 25+ years,
|
||||||
// but Sectigo CA still requires it to be filled out and not overlap SANs...)
|
// but ZeroSSL CA still requires it to be filled out and not overlap SANs...)
|
||||||
if csr.Subject.CommonName != "" {
|
if csr.Subject.CommonName != "" {
|
||||||
nameSet = append(nameSet, csr.Subject.CommonName)
|
nameSet = append(nameSet, csr.Subject.CommonName)
|
||||||
}
|
}
|
||||||
|
4
go.mod
4
go.mod
@ -2,8 +2,10 @@ module github.com/caddyserver/certmagic
|
|||||||
|
|
||||||
go 1.22.0
|
go 1.22.0
|
||||||
|
|
||||||
|
toolchain go1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/caddyserver/zerossl v0.1.1
|
github.com/caddyserver/zerossl v0.1.2
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7
|
github.com/klauspost/cpuid/v2 v2.2.7
|
||||||
github.com/libdns/libdns v0.2.2
|
github.com/libdns/libdns v0.2.2
|
||||||
github.com/mholt/acmez/v2 v2.0.0-beta.2
|
github.com/mholt/acmez/v2 v2.0.0-beta.2
|
||||||
|
4
go.sum
4
go.sum
@ -1,5 +1,5 @@
|
|||||||
github.com/caddyserver/zerossl v0.1.1 h1:yQL7QXZnEb/ddH6JsNPGBANETUMHPFlAV5+a+Epxgbo=
|
github.com/caddyserver/zerossl v0.1.2 h1:tlEu1VzWGoqcCpivs9liKAKhfpJWYJkHEMmlxRbVAxE=
|
||||||
github.com/caddyserver/zerossl v0.1.1/go.mod h1:wtiJEHbdvunr40ZzhXlnIkOB8Xj4eKtBKizCcZitJiQ=
|
github.com/caddyserver/zerossl v0.1.2/go.mod h1:wtiJEHbdvunr40ZzhXlnIkOB8Xj4eKtBKizCcZitJiQ=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
|
@ -26,26 +26,17 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/caddyserver/zerossl"
|
"github.com/caddyserver/zerossl"
|
||||||
|
"github.com/mholt/acmez/v2"
|
||||||
"github.com/mholt/acmez/v2/acme"
|
"github.com/mholt/acmez/v2/acme"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewZeroSSLIssuer returns a ZeroSSL issuer with default values filled in
|
|
||||||
// for empty fields in the template.
|
|
||||||
func NewZeroSSLIssuer(cfg *Config, template ZeroSSLIssuer) *ZeroSSLIssuer {
|
|
||||||
if cfg == nil {
|
|
||||||
panic("cannot make valid ZeroSSLIssuer without an associated CertMagic config")
|
|
||||||
}
|
|
||||||
template.config = cfg
|
|
||||||
template.logger = defaultLogger.Named("zerossl")
|
|
||||||
return &template
|
|
||||||
}
|
|
||||||
|
|
||||||
// ZeroSSLIssuer can get certificates from ZeroSSL's API. (To use ZeroSSL's ACME
|
// ZeroSSLIssuer can get certificates from ZeroSSL's API. (To use ZeroSSL's ACME
|
||||||
// endpoint, use the ACMEIssuer instead.) Note that use of the API is restricted
|
// endpoint, use the ACMEIssuer instead.) Note that use of the API is restricted
|
||||||
// by payment tier.
|
// by payment tier.
|
||||||
type ZeroSSLIssuer struct {
|
type ZeroSSLIssuer struct {
|
||||||
// The API key (or "access key") for using the ZeroSSL API.
|
// The API key (or "access key") for using the ZeroSSL API.
|
||||||
|
// REQUIRED.
|
||||||
APIKey string
|
APIKey string
|
||||||
|
|
||||||
// How many days the certificate should be valid for.
|
// How many days the certificate should be valid for.
|
||||||
@ -63,8 +54,13 @@ type ZeroSSLIssuer struct {
|
|||||||
// validation, set this field.
|
// validation, set this field.
|
||||||
CNAMEValidation *DNSManager
|
CNAMEValidation *DNSManager
|
||||||
|
|
||||||
config *Config
|
// Where to store verification material temporarily.
|
||||||
logger *zap.Logger
|
// Set this on all instances in a cluster to the same
|
||||||
|
// value to enable distributed verification.
|
||||||
|
Storage Storage
|
||||||
|
|
||||||
|
// An optional (but highly recommended) logger.
|
||||||
|
Logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue obtains a certificate for the given csr.
|
// Issue obtains a certificate for the given csr.
|
||||||
@ -72,7 +68,12 @@ func (iss *ZeroSSLIssuer) Issue(ctx context.Context, csr *x509.CertificateReques
|
|||||||
client := iss.getClient()
|
client := iss.getClient()
|
||||||
|
|
||||||
identifiers := namesFromCSR(csr)
|
identifiers := namesFromCSR(csr)
|
||||||
logger := iss.logger.With(zap.Strings("identifiers", identifiers))
|
|
||||||
|
logger := iss.Logger
|
||||||
|
if logger == nil {
|
||||||
|
logger = zap.NewNop()
|
||||||
|
}
|
||||||
|
logger = logger.With(zap.Strings("identifiers", identifiers))
|
||||||
|
|
||||||
logger.Info("creating certificate")
|
logger.Info("creating certificate")
|
||||||
|
|
||||||
@ -134,16 +135,19 @@ func (iss *ZeroSSLIssuer) Issue(ctx context.Context, csr *x509.CertificateReques
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
distSolver := distributedSolver{
|
var solver acmez.Solver = httpVerifier
|
||||||
storage: iss.config.Storage,
|
if iss.Storage != nil {
|
||||||
storageKeyIssuerPrefix: "zerossl",
|
solver = distributedSolver{
|
||||||
solver: httpVerifier,
|
storage: iss.Storage,
|
||||||
|
storageKeyIssuerPrefix: iss.IssuerKey(),
|
||||||
|
solver: httpVerifier,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = distSolver.Present(ctx, acme.Challenge{}); err != nil {
|
if err = solver.Present(ctx, acme.Challenge{}); err != nil {
|
||||||
return nil, fmt.Errorf("presenting token for verification: %v", err)
|
return nil, fmt.Errorf("presenting token for verification: %v", err)
|
||||||
}
|
}
|
||||||
defer distSolver.CleanUp(ctx, acme.Challenge{})
|
defer solver.CleanUp(ctx, acme.Challenge{})
|
||||||
} else {
|
} else {
|
||||||
verificationMethod = zerossl.CNAMEVerification
|
verificationMethod = zerossl.CNAMEVerification
|
||||||
logger = logger.With(zap.String("verification_method", string(verificationMethod)))
|
logger = logger.With(zap.String("verification_method", string(verificationMethod)))
|
||||||
@ -248,9 +252,7 @@ func (iss *ZeroSSLIssuer) getHTTPPort() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IssuerKey returns the unique issuer key for ZeroSSL.
|
// IssuerKey returns the unique issuer key for ZeroSSL.
|
||||||
func (iss *ZeroSSLIssuer) IssuerKey() string {
|
func (iss *ZeroSSLIssuer) IssuerKey() string { return zerosslIssuerKey }
|
||||||
return "zerossl"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revoke revokes the given certificate. Only do this if there is a security or trust
|
// Revoke revokes the given certificate. Only do this if there is a security or trust
|
||||||
// concern with the certificate.
|
// concern with the certificate.
|
||||||
@ -274,6 +276,7 @@ func (iss *ZeroSSLIssuer) Revoke(ctx context.Context, cert CertificateResource,
|
|||||||
const (
|
const (
|
||||||
zerosslAPIBase = "https://" + zerossl.BaseURL + "/acme"
|
zerosslAPIBase = "https://" + zerossl.BaseURL + "/acme"
|
||||||
zerosslValidationPathPrefix = "/.well-known/pki-validation/"
|
zerosslValidationPathPrefix = "/.well-known/pki-validation/"
|
||||||
|
zerosslIssuerKey = "zerossl"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Interface guards
|
// Interface guards
|
||||||
|
Loading…
Reference in New Issue
Block a user