diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index d71283c46..e95229f3b 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -10,7 +10,7 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom Use Let's Encrypt staging server with the [`caServer`](#caserver) configuration option when experimenting to avoid hitting this limit too fast. - + ## Certificate Resolvers Traefik requires you to define "Certificate Resolvers" in the [static configuration](../getting-started/configuration-overview.md#the-static-configuration), @@ -408,6 +408,35 @@ certificatesResolvers: [ACME V2](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579) supports wildcard certificates. As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/staging-endpoint-for-acme-v2/49605) wildcard certificates can only be generated through a [`DNS-01` challenge](#dnschallenge). +## External Account Binding + +- `kid`: Key identifier from External CA +- `hmacEncoded`: HMAC key from External CA, should be in Base64 URL Encoding without padding format + +```toml tab="File (TOML)" +[certificatesResolvers.myresolver.acme] + # ... + [certificatesResolvers.myresolver.acme.eab] + kid = "abc-keyID-xyz" + hmacEncoded = "abc-hmac-xyz" +``` + +```yaml tab="File (YAML)" +certificatesResolvers: + myresolver: + acme: + # ... + eab: + kid: abc-keyID-xyz + hmacEncoded: abc-hmac-xyz +``` + +```bash tab="CLI" +# ... +--certificatesresolvers.myresolver.acme.eab.kid=abc-keyID-xyz +--certificatesresolvers.myresolver.acme.eab.hmacencoded=abc-hmac-xyz +``` + ## More Configuration ### `caServer` diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index fc764bead..81a4e84fa 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -69,6 +69,12 @@ Use a DNS-01 based challenge provider rather than HTTPS. `--certificatesresolvers..acme.dnschallenge.resolvers`: Use following DNS servers to resolve the FQDN authority. +`--certificatesresolvers..acme.eab.hmacencoded`: +Base64 encoded HMAC key from External CA. + +`--certificatesresolvers..acme.eab.kid`: +Key identifier from External CA. + `--certificatesresolvers..acme.email`: Email address used for registration. diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 03537d232..08c0b9c74 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -69,6 +69,12 @@ Use a DNS-01 based challenge provider rather than HTTPS. `TRAEFIK_CERTIFICATESRESOLVERS__ACME_DNSCHALLENGE_RESOLVERS`: Use following DNS servers to resolve the FQDN authority. +`TRAEFIK_CERTIFICATESRESOLVERS__ACME_EAB_HMACENCODED`: +Base64 encoded HMAC key from External CA. + +`TRAEFIK_CERTIFICATESRESOLVERS__ACME_EAB_KID`: +Key identifier from External CA. + `TRAEFIK_CERTIFICATESRESOLVERS__ACME_EMAIL`: Email address used for registration. diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index d92473b80..c3db6051a 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -103,7 +103,7 @@ namespaces = ["foobar", "foobar"] labelSelector = "foobar" ingressClass = "foobar" - throttleDuration = "10s" + throttleDuration = "42s" [providers.kubernetesIngress.ingressEndpoint] ip = "foobar" hostname = "foobar" @@ -251,9 +251,6 @@ addEntryPointsLabels = true addServicesLabels = true -[pilot] - token = "foobar" - [ping] entryPoint = "foobar" manualRouting = true @@ -343,6 +340,9 @@ preferredChain = "foobar" storage = "foobar" keyType = "foobar" + [certificatesResolvers.CertificateResolver0.acme.eab] + kid = "foobar" + hmacEncoded = "foobar" [certificatesResolvers.CertificateResolver0.acme.dnsChallenge] provider = "foobar" delayBeforeCheck = 42 @@ -358,6 +358,9 @@ preferredChain = "foobar" storage = "foobar" keyType = "foobar" + [certificatesResolvers.CertificateResolver1.acme.eab] + kid = "foobar" + hmacEncoded = "foobar" [certificatesResolvers.CertificateResolver1.acme.dnsChallenge] provider = "foobar" delayBeforeCheck = 42 @@ -367,6 +370,9 @@ entryPoint = "foobar" [certificatesResolvers.CertificateResolver1.acme.tlsChallenge] +[pilot] + token = "foobar" + [experimental] [experimental.plugins] [experimental.plugins.Descriptor0] diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index ef86c570e..7eb174505 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -270,8 +270,6 @@ metrics: password: foobar addEntryPointsLabels: true addServicesLabels: true -pilot: - token: foobar ping: entryPoint: foobar manualRouting: true @@ -358,6 +356,9 @@ certificatesResolvers: preferredChain: foobar storage: foobar keyType: foobar + eab: + kid: foobar + hmacEncoded: foobar dnsChallenge: provider: foobar delayBeforeCheck: 42 @@ -375,6 +376,9 @@ certificatesResolvers: preferredChain: foobar storage: foobar keyType: foobar + eab: + kid: foobar + hmacEncoded: foobar dnsChallenge: provider: foobar delayBeforeCheck: 42 @@ -385,6 +389,8 @@ certificatesResolvers: httpChallenge: entryPoint: foobar tlsChallenge: {} +pilot: + token: foobar experimental: plugins: Descriptor0: diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go index b5674f373..3af7e1599 100644 --- a/pkg/provider/acme/provider.go +++ b/pkg/provider/acme/provider.go @@ -33,14 +33,16 @@ var oscpMustStaple = false // Configuration holds ACME configuration provided by users. type Configuration struct { - Email string `description:"Email address used for registration." json:"email,omitempty" toml:"email,omitempty" yaml:"email,omitempty"` - CAServer string `description:"CA server to use." json:"caServer,omitempty" toml:"caServer,omitempty" yaml:"caServer,omitempty"` - PreferredChain string `description:"Preferred chain to use." json:"preferredChain,omitempty" toml:"preferredChain,omitempty" yaml:"preferredChain,omitempty" export:"true"` - Storage string `description:"Storage to use." json:"storage,omitempty" toml:"storage,omitempty" yaml:"storage,omitempty" export:"true"` - KeyType string `description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'." json:"keyType,omitempty" toml:"keyType,omitempty" yaml:"keyType,omitempty" export:"true"` - DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge." json:"dnsChallenge,omitempty" toml:"dnsChallenge,omitempty" yaml:"dnsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge." json:"httpChallenge,omitempty" toml:"httpChallenge,omitempty" yaml:"httpChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - TLSChallenge *TLSChallenge `description:"Activate TLS-ALPN-01 Challenge." json:"tlsChallenge,omitempty" toml:"tlsChallenge,omitempty" yaml:"tlsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Email string `description:"Email address used for registration." json:"email,omitempty" toml:"email,omitempty" yaml:"email,omitempty"` + CAServer string `description:"CA server to use." json:"caServer,omitempty" toml:"caServer,omitempty" yaml:"caServer,omitempty"` + PreferredChain string `description:"Preferred chain to use." json:"preferredChain,omitempty" toml:"preferredChain,omitempty" yaml:"preferredChain,omitempty" export:"true"` + Storage string `description:"Storage to use." json:"storage,omitempty" toml:"storage,omitempty" yaml:"storage,omitempty" export:"true"` + KeyType string `description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'." json:"keyType,omitempty" toml:"keyType,omitempty" yaml:"keyType,omitempty" export:"true"` + EAB *EAB `description:"External Account Binding to use." json:"eab,omitempty" toml:"eab,omitempty" yaml:"eab,omitempty"` + + DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge." json:"dnsChallenge,omitempty" toml:"dnsChallenge,omitempty" yaml:"dnsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge." json:"httpChallenge,omitempty" toml:"httpChallenge,omitempty" yaml:"httpChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + TLSChallenge *TLSChallenge `description:"Activate TLS-ALPN-01 Challenge." json:"tlsChallenge,omitempty" toml:"tlsChallenge,omitempty" yaml:"tlsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // SetDefaults sets the default values. @@ -63,7 +65,13 @@ type Certificate struct { Key []byte `json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"` } -// DNSChallenge contains DNS challenge Configuration. +// EAB contains External Account Binding configuration. +type EAB struct { + Kid string `description:"Key identifier from External CA." json:"kid,omitempty" toml:"kid,omitempty" yaml:"kid,omitempty"` + HmacEncoded string `description:"Base64 encoded HMAC key from External CA." json:"hmacEncoded,omitempty" toml:"hmacEncoded,omitempty" yaml:"hmacEncoded,omitempty"` +} + +// DNSChallenge contains DNS challenge configuration. type DNSChallenge struct { Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS." json:"provider,omitempty" toml:"provider,omitempty" yaml:"provider,omitempty" export:"true"` DelayBeforeCheck ptypes.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers." json:"delayBeforeCheck,omitempty" toml:"delayBeforeCheck,omitempty" yaml:"delayBeforeCheck,omitempty" export:"true"` @@ -71,12 +79,12 @@ type DNSChallenge struct { DisablePropagationCheck bool `description:"Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended]" json:"disablePropagationCheck,omitempty" toml:"disablePropagationCheck,omitempty" yaml:"disablePropagationCheck,omitempty" export:"true"` } -// HTTPChallenge contains HTTP challenge Configuration. +// HTTPChallenge contains HTTP challenge configuration. type HTTPChallenge struct { EntryPoint string `description:"HTTP challenge EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"` } -// TLSChallenge contains TLS challenge Configuration. +// TLSChallenge contains TLS challenge configuration. type TLSChallenge struct{} // Provider holds configurations of the provider. @@ -233,9 +241,7 @@ func (p *Provider) getClient() (*lego.Client, error) { // New users will need to register; be sure to save it if account.GetRegistration() == nil { - logger.Info("Register...") - - reg, errR := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + reg, errR := p.register(ctx, client) if errR != nil { return nil, errR } @@ -324,6 +330,22 @@ func (p *Provider) initAccount(ctx context.Context) (*Account, error) { return p.account, nil } +func (p *Provider) register(ctx context.Context, client *lego.Client) (*registration.Resource, error) { + logger := log.FromContext(ctx) + + if p.EAB != nil { + logger.Info("Register with external account binding...") + + eabOptions := registration.RegisterEABOptions{TermsOfServiceAgreed: true, Kid: p.EAB.Kid, HmacEncoded: p.EAB.HmacEncoded} + + return client.Registration.RegisterWithExternalAccountBinding(eabOptions) + } + + logger.Info("Register...") + + return client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) +} + func (p *Provider) resolveDomains(ctx context.Context, domains []string, tlsStore string) { if len(domains) == 0 { log.FromContext(ctx).Debug("No domain parsed in provider ACME")