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

feat: improve acme-challenge handling #2446

Merged
merged 1 commit into from May 13, 2024

Conversation

pini-gh
Copy link
Contributor

@pini-gh pini-gh commented May 9, 2024

So that there is no need anymore for the Let's Encrypt companion to fiddle with vhosts nginx configuration.

Bcause it creates http servers to handle acme challenge requests, a few tests which previously refused http connexion now return 503.

nginx.tmpl Outdated
@@ -668,7 +668,7 @@ server {
{{ template "upstream" (dict "globals" $globals "Path" $path "VPath" $vpath) }}
{{- end }}

{{- if and $vhost.cert_ok (eq $vhost.https_method "redirect") }}
{{- if and (or $vhost.cert_ok $globals.default_cert_ok) (or (eq $vhost.https_method "redirect") (eq $vhost.https_method "nohttp")) }}
Copy link
Member

@buchdag buchdag May 9, 2024

Choose a reason for hiding this comment

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

I'm not certain about silently running an HTTP server for the sole purpose of the acme-challenge when using nohttp.

To me using nohttp when wanting to obtain ACME TLS certificate with the HTTP challenge should fail without exception, because not blocking port 80 at all is part of LE's best practice (with very sound arguments imho), and the RFC8555 section 8.3 itself indicate that first connection of an ACME HTTP challenge must happen on port 80, making it by design incompatible with attempt to block this port:

  1. Dereference the URL using an HTTP GET request. This request MUST be sent to TCP port 80 on the HTTP server

I think we should instead update the documentation to warn people that nohttp will be incompatible with the ACME HTTP challenge, like in acme-companion's documentation.

Explicit use of nohttp by users should be done knowingly (ie not for security misconceptions) and observed entirely by nginx-proxy.

Copy link
Member

Choose a reason for hiding this comment

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

The use case of people absolutely having to block port 80 yet still wanting ACME TLS certificate should rather be handled by making acme-companion compatible with DNS challenge (the next "big" feature I'd like to get merged).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a very simple solution when rigorously no http server is wanted: do not bind port 80.

What makes me uncomfortable is acme-companion fiddling into the nginx configuration. It shouldn't have to.

How about enabling acme challenge if and only if:

  • the nginx-proxy container has ACME_CHALLENGE=true
  • or the app container has ACME_CHALLENGE=true?
  • or the app container defines the LETSENCRYPT_HOST variable,

If you agree on none of these proposals, I'd be OK with not enabling acme challenge when HTTPS_METHOD=nohttp.

What do you think?

Copy link
Member

@buchdag buchdag May 9, 2024

Choose a reason for hiding this comment

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

What makes me uncomfortable is acme-companion fiddling into the nginx configuration. It shouldn't have to.

Agreed, that's been a long standing rough edge of the integration between acme-companion and nginx-proxy.

Regarding nohttp, in addition to the above arguments I think the proxy should honor it even if it result in a "faulty" configuration (kind of like what you did for VIRTUAL_PORT a few years ago), rather than automagically correcting the user's error without its knowledge (ie the previous behaviour of VIRTUAL_PORT).

But I might be missing valid use cases of using nohttp and still wanting to be able to complete ACME HTTP challenge, do you have some examples ?

If we decide to enable ACME challenge when HTTPS_METHOD=nohttp, I'd go with the third solution (based on wether the app container defines the LETSENCRYPT_HOST variable or not).

Copy link
Contributor Author

@pini-gh pini-gh May 9, 2024

Choose a reason for hiding this comment

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

But I might be missing valid use cases of using nohttp and still wanting to be able to complete ACME HTTP challenge, do you have some examples ?

Unfortunately I don't know any use case for HTTPS_METHOD=nohttp.

If we decide to enable ACME challenge when HTTPS_METHOD=nohttp, I'd go with the third solution (based on wether the app container defines the LETSENCRYPT_HOST variable or not).

If you want to give it more thinking I could split this PR in two:

  1. Handle acme challenge but for HTTPS_METHOD=nohttp
  2. When HTTPS_METHOD=nohttp handle acme challenge only if the app container defines LETSENCRYPT_HOST.

Actually I'm not sure (2) is needed, So I'll go for the split.

Copy link
Member

@buchdag buchdag May 9, 2024

Choose a reason for hiding this comment

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

That was maybe unclear but the only gripe I had was with nohttp (so only with 2.), the rest of the PR make sense to me and I was about to ping you on this issue. I don't have any issue merging a PR for 1. 👍

Edit : I think we might have to enable the challenge only for app containers that have the LETSENCRYPT_HOST variable set, I think I remember a discussion about this a while ago with someone saying that a hardcoded ACME challenge location was undesirable to them because they were doing their challenge validation themselves (ie not with acme-companion), with a challenge location setup differently. I'll try to search for this discussion.

Copy link
Contributor Author

@pini-gh pini-gh May 9, 2024

Choose a reason for hiding this comment

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

Edit : I think we might have to enable the challenge only for app containers that have the LETSENCRYPT_HOST variable set,

This might break existing use cases where acme-companion is not used but acme challenge is needed. We could do the other way around: do not enable acme challenge when NO_ACME_HTTP_CHALLENGE=true.

EDIT: But I'd prefer to implement this via another PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

EDIT: But I'd prefer to implement this via another PR.

This is #2447.

Copy link
Member

@buchdag buchdag left a comment

Choose a reason for hiding this comment

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

I feel like we're trying to change behaviours that aren't really related to ACME challenge here. Could we list the incorrect ACME challenge behaviours we want to fix ?

("nohttp-with-missing-cert.yml", "https://missing-cert.nginx-proxy.test/", 500, None),
("nohttp-with-missing-cert.yml", "http://unknown.nginx-proxy.test/", 503, None),
("nohttp-with-missing-cert.yml", "http://unknown.nginx-proxy.test/", None, CONNECTION_REFUSED_RE),
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure about this new behaviour. In my mind an unknown hostname should always be a 503, regardless of the configuration of the proxied service containers. Having a container with HTTPS_METHOD=nohttp should not change how the proxy respond to non configured hostnames.

Maybe there is a confusion around HTTPS_METHOD=nohttp but to me it means disable HTTP entirely for this vhost, and this vhost only.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure about this new behaviour. In my mind an unknown hostname should always be a 503, regardless of the configuration of the proxied service containers. Having a container with HTTPS_METHOD=nohttp should not change how the proxy respond to non configured hostnames.

This behavior is not new and not my idea. It was introduced by #2186 and could be reverted by unconditionally enabling the http fallback server.

Maybe there is a confusion around HTTPS_METHOD=nohttp but to me it means disable HTTP entirely for this vhost, and this vhost only.

Agreed.

Copy link
Contributor Author

@pini-gh pini-gh May 12, 2024

Choose a reason for hiding this comment

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

Could we list the incorrect ACME challenge behaviours we want to fix ?

Only one case: when HTTPS_METHOD=noredirect.

EDIT: this is highlighted by this change

Copy link
Member

Choose a reason for hiding this comment

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

I'm sorry, I think I'm getting a bit lost here and making you circle around the issue this PR is trying to fix.

I feel like this (inserted between L764 and L765):

        {{- if (eq $vhost.https_method "noredirect") }}
    location /.well-known/acme-challenge/ {
        auth_basic off;
        allow all;
        root /usr/share/nginx/html;
        try_files $uri =404;
        break;
    }
        {{- end }}

Should be enough to fix the ACME challenge location for HTTPS_METHOD=noredirect. Am I missing something or misunderstanding the current template ?

From what I remember of #2186, the aim was mostly to stop the proxy from serving incorrect TLS certificates in some cases. If it introduced unwanted behaviour, could we discuss and fix them in a separate PR ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay then, let's go step by step.

So that there is no need anymore for the Let's Encrypt companion to fiddle
with vhosts nginx configuration.

When `HTTPS_METHOD=nohttp` and the certificate is missing, enforce nohttp
instead of switching to `HTTPS_METHOD=redirect`.
@buchdag buchdag merged commit 2564a93 into nginx-proxy:main May 13, 2024
2 checks passed
@pini-gh pini-gh deleted the pini-acme-challenge branch May 14, 2024 18:33
@buchdag buchdag added the type/feat PR for a new feature label May 15, 2024
@buchdag buchdag changed the title Improve acme-challenge handling feat: improve acme-challenge handling May 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/feat PR for a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants