Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACME V2 Integration #3063

Merged
merged 13 commits into from
Mar 26, 2018
Merged

ACME V2 Integration #3063

merged 13 commits into from
Mar 26, 2018

Conversation

nmengin
Copy link
Contributor

@nmengin nmengin commented Mar 22, 2018

What does this PR do?

This PR allows Træfik to use the new ACME version.
It uses the new lego branch to access to the ACME V2 API.

ACME V2 API main features :

  • Suppression of TLS-SNI-01 challenge
  • Adding wildcard certificates generation with DNS-02 challenge

Motivation

Fix #2674

Allowing users to do ACME wildcard certificates

More

  • Added/updated tests
  • Added/updated documentation

Additional Notes

  • For the moment, wildcard domains can only be defined with the ACME domains option.
  • SANs are not allowed in wildcard certificates.
  • Another PR with end-to-end tests for wildcard domains will come ASAP.

acme/acme.go Outdated
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q : ACME needs a DNSChallenge", strings.Join(domains, ","))
}
if len(domains) > 1 {
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q : SANs are nor allowed", strings.Join(domains, ","))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SANs are not allowed

Copy link
Member

@emilevauge emilevauge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job @nmengin !
Few comments though ;)


// Check if ACME Account is in ACME V1 format
if account != nil && account.Registration != nil {
isOldRegistration, err := regexp.MatchString(acme.OldRegistrationUrlPath, account.Registration.URI)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/OldRegistration/ACMEv1Registration may be more explicit

acme/acme.go Outdated
return nil, errors.New("unable to generate a certificate when no domain is given")
}
if strings.HasPrefix(domains[0], "*") {
if a.DNSChallenge == nil && len(a.DNSProvider) == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is an issue in this test

acme/acme.go Outdated
@@ -459,7 +459,7 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
a.jobs.In() <- func() {
log.Debugf("LoadCertificateForDomains %v...", domains)

domains, err := a.getValidDomain(domains)
domains, err := a.getValidDomain(domains, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/getValidDomain/getValidDomains

@@ -1214,10 +1214,11 @@
revision = "0c8571ac0ce161a5feb57375a9cdf148c98c0f70"

[[projects]]
branch = "master"
branch = "acmev2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any intentions to fork this for stability? Not really excited to use a feature branch on a third party repo as a basis for prod use...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @dtomcej.

We are not enthusiastic either but, for the moment it's the only way we have if we want to integrate ACME V2.
Moreover, I tested a lot and the branch seems to be stable.

We are following the LEGO repo and, if a big problem appears, we will be able to fork the repo with a stable version thanks to the vendoring.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -395,7 +387,7 @@ func dnsOverrideDelay(delay flaeg.Duration) error {

func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
log.Debug("Building ACME client...")
caServer := "https://acme-v01.api.letsencrypt.org/directory"
caServer := "https://acme-v02.api.letsencrypt.org/directory"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this fully backwards compatible? Is it something that we should be defaulting back to v1 somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dtomcej It's not backward compatible.

That's why getAccount methods have been modified.
They can detect V1 Account format and delete is from the ACME structure.

Thus, Træfik reads the new configuration and create a new V2 ACME Account.

However, of course, ACME certificates generated in V1 are compatibles

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@nmengin nmengin force-pushed the feature/acmev2-study branch 3 times, most recently from 65e605e to 89478ab Compare March 22, 2018 17:38

[[acme.domains]]
main = "*.local1.com"
sans = ["test1.local1.com", "test2.local1.com"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't do that (wildcard with sans) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No you can't do it for the moment!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really ?

Copy link
Member

@mmatur mmatur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @nmengin 👏
Few comments in the review

for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a new line please

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


### DNS-02 Challenge

As described in [Let's Encrypt post](https://community.letsencrypt.org/t/staging-endpoint-for-acme-v2/49605), wildcard certificates can only be generated thanks to a `DNS-02`Challenge.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space missing between DNS-02 and Challenge

}

// Store the data in new format into the file ven if account is nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even instead of ven

@@ -137,6 +139,10 @@ entryPoint = "https"
If `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through the port 80.
These are Let's Encrypt limitations as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).

!!!note
Wildcard certificates can be generated only if `acme.dnsChallenge`
option is enable.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a new line for this part ? The note in the documentation is broken

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's another note but a space is missing I add it.

[acme]
email = "[email protected]"
storage = "acme.json"
caServer = "http://172.18.0.1:4000/directory"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should maybe use "https://acme-staging-v02.api.letsencrypt.org/directory instead of http://172.18.0.1:4000/directory


[[acme.domains]]
main = "*.local1.com"
sans = ["test1.local1.com", "test2.local1.com"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can not do that. If main domain is a wildcard you can not use sans

for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please remove this line

acme/acme.go Outdated
// - Duplicated domains
// - Domains which are checked by wildcard domain
func (a *ACME) deleteUnecessariesDomains() {
var newDomains []types.Domain
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleteUnnecessariesDomains instead of deleteUnecessariesDomains

Copy link
Member

@mmatur mmatur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Great job @nmengin 👏

Copy link
Member

@emilevauge emilevauge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job @nmengin !
Very much awaited feature ;)
LGTM

@traefiker traefiker merged commit 16bb9b6 into traefik:master Mar 26, 2018
@nmengin nmengin deleted the feature/acmev2-study branch March 26, 2018 13:32
@sarkistlt
Copy link

thanks! literally much awaited feature in my project!
I just have one question, trying to defined it like so:

[[acme.domains]]
  main = "mydomain.com"
  sans = ["*.mydomain.com"]

and getting error Error getting ACME certificate for domain [mydomain.com *.mydomain.com]: cannot obtain certificates map[*.mydomain.com:acme: Error 400 - urn:acme:error:malformed - Error creating new authz :: Wildcard names not supported],

so who then should I define wildcard?

@nmengin
Copy link
Contributor Author

nmengin commented Mar 27, 2018

Hello @sarkistlt ,

Many thanks for your feedback.

For the moment, we do not support wildcard certificates with/in SANs.
Moreover, the error seems to come from ACME library.

You can use the configuration described below :

[[acme.domains]]
  main = "mydomain.com"
[[acme.domains]]
  main = ["*.mydomain.com"]

If you still have problems, I suggest you to open an issue or to join our Slack workspace for more community #support.

@sarkistlt
Copy link

sarkistlt commented Mar 27, 2018

thanks for response, I just downloaded source code of traefik and it still uses https://acme-v01.api.letsencrypt.org/directory, instead of https://acme-v02.api.letsencrypt.org/directory, I tried to use _ACME_SERVER_HOST: acme-staging-v02.api.letsencrypt.org but it ignores acme env. vars, so which tag of docker image does support acme2? I tried 1.5 and latest, no luck

@sarkistlt
Copy link

actually just saw version 1.6 deployed on docker hub 10hr ago and it works, thanks!

@sarkistlt
Copy link

getting another error unable to generate a wildcard certificate in ACME provider for domain \"*. mydomain.com\" : ACME needs a DNSChallenge, looks like it doesn't generate .well-known/acme-challenge
And for the main domain: Unable to obtain ACME certificate for domains \"mydomain.com\" : cannot obtain certificates map[mydomain.com:acme: Error 400 - urn:ietf:params:acme:error:connection - Fetching http://mydomain.com/.well-known/acme-challenge/some_hash: Timeout], should I open new issue?

@e-nikolov
Copy link

You can find info on how to configure the wildcard certs here: https://docs.traefik.io/v1.6/configuration/acme/.

Let's Encrypt uses a DNS challenge for issuing wildcard certificates, which requires setting a DNS TXT record. Traefik can automate this via the API of your DNS provider (if it is supported), for which it requires an API key.

@ldez
Copy link
Contributor

ldez commented Mar 27, 2018

⚠️ For all those who have problems with this feature, please open a dedicated issue. ⚠️

You can also join our Slack workspace for more community #support.

Remember, we dedicate the issue tracker to bug reports and feature requests only

[acme]
# ...
[[acme.domains]]
main = "*local1.com"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a typo?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/acme kind/enhancement a new or improved feature. size/L
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Let's Encrypt: use ACME API V2
10 participants