.well-known ACME challenge files blocked 403 Forbidden in some Nginx configurations #221

Closed
centminmod opened this Issue Aug 29, 2015 · 8 comments

Comments

Projects
None yet
9 participants
@centminmod

Hi this is related to Letsencrypt manual authenticator mode with the ACME challenge file having a dot prefix letsencrypt/letsencrypt#730. This can be blocked with 403 Forbidden access by some Nginx configurations which block dot prefix files/folders from web access by default.

i.e.

location ~ /\.          { access_log off; log_not_found off; deny all; }

For a workaround for me, I had to add this to my Nginx vhost

location ~ /.well-known {
      location ~ /.well-known/acme-challenge/(.*) {
              more_set_headers    "Content-Type: application/jose+json";
      }
}   

Probably need to document this for folks as to requirements needed for Nginx to allow dot prefix file for .well-known requests

not sure if you just add a curl check of the ACME challenge file for the status code so if it's anything other than 200 status, you can show a more detailed explanation ? i.e. if it's 403 status for the curl header check, say

ensure nginx vhost allows dot prefix directory access to /.well-known
@riobard

This comment has been minimized.

Show comment
Hide comment
@riobard

riobard Jan 4, 2016

You should use the following in nginx config

location ^~ /.well-known/acme-challenge/ {
  # the usual settings
}

riobard commented Jan 4, 2016

You should use the following in nginx config

location ^~ /.well-known/acme-challenge/ {
  # the usual settings
}
@mnpenner

This comment has been minimized.

Show comment
Hide comment
@mnpenner

mnpenner Mar 6, 2016

Thank you @riobard. I was trying with

location '/.well-known/acme-challenge' { # <-- does not work
    default_type "text/plain";
    root        /tmp/letsencrypt-auto;
}

But that didn't do it... I guess nginx keeps processing the other restrictions. Yours seems to do the trick though.

mnpenner commented Mar 6, 2016

Thank you @riobard. I was trying with

location '/.well-known/acme-challenge' { # <-- does not work
    default_type "text/plain";
    root        /tmp/letsencrypt-auto;
}

But that didn't do it... I guess nginx keeps processing the other restrictions. Yours seems to do the trick though.

@kb-

This comment has been minimized.

Show comment
Hide comment
@kb-

kb- Apr 8, 2016

Hi, my free webhost is blocking the " .well-known too", the dot being the problem. They only give very limited control with htaccess files and the apache configuration is of course locked... So I can't find a workaround.

Is there a way to change the challenge URL to something else who doesn't contain a dot?

kb- commented Apr 8, 2016

Hi, my free webhost is blocking the " .well-known too", the dot being the problem. They only give very limited control with htaccess files and the apache configuration is of course locked... So I can't find a workaround.

Is there a way to change the challenge URL to something else who doesn't contain a dot?

@augnustin

This comment has been minimized.

Show comment
Hide comment
@augnustin

augnustin Apr 20, 2016

After some struggle, here's my working config:

server {
  listen                80;
  server_name           example.com;
  root /vagrant/www/current/public;

  # Necessary for Let's Encrypt Domain Name ownership validation
  location /.well-known/acme-challenge/ {
    try_files $uri /dev/null =404;
  }
  location / {
    return 301 https://$host$request_uri;
  }
}

After some struggle, here's my working config:

server {
  listen                80;
  server_name           example.com;
  root /vagrant/www/current/public;

  # Necessary for Let's Encrypt Domain Name ownership validation
  location /.well-known/acme-challenge/ {
    try_files $uri /dev/null =404;
  }
  location / {
    return 301 https://$host$request_uri;
  }
}
@wturrell

This comment has been minimized.

Show comment
Hide comment
@wturrell

wturrell May 30, 2016

Despite plenty of servers/configs where this works fine…

location ~ /.well-known {
  allow all;
}

on one where /.well-known is an alias, I have to add an ^ otherwise I get 403 Forbidden. (I can't understand why – I commented out all the other location blocks in turn, none of which match anyway, and it made no difference…)

location ^~ /.well-known {
  allow all;
  auth_basic off;
  alias /path/to/.well-known/;
}

wturrell commented May 30, 2016

Despite plenty of servers/configs where this works fine…

location ~ /.well-known {
  allow all;
}

on one where /.well-known is an alias, I have to add an ^ otherwise I get 403 Forbidden. (I can't understand why – I commented out all the other location blocks in turn, none of which match anyway, and it made no difference…)

location ^~ /.well-known {
  allow all;
  auth_basic off;
  alias /path/to/.well-known/;
}
@rowanthorpe

This comment has been minimized.

Show comment
Hide comment
@rowanthorpe

rowanthorpe Jul 8, 2016

The reason @riobard's version works is because of the order and manner in which nginx works through the location matches. I personally find the way it is documented at nginx.org adds an unnecessary layer of temporal convolution (unless there is some nuance that I have missed when reordering the guts of points 3 and 4 below), so this is my version:

  1. If there is an exact path-match (with location = XXX {})
    • this block is processed
    • match-searching stops
  2. If there are one or more preferential prefix-matches (with location ^~ XXX {})
    • the block of the longest (most explicit) of those matches is processed
    • match-searching stops
  3. If there are one or more case-sensitive regex-matches (with location ~ XXX {}), or case-insensitive regex-matches (with location ~* XXX {})
    • the block of the first matching regex found (when scanning the config-file top-to-bottom) is processed
    • match-searching stops
  4. If there are one or more non-preferential prefix-matches (with location XXX {})
    • the block of the longest (most explicit) of those matches is processed
    • match-searching stops

The three biggest sources of confusion I have seen relevant to these kind of issues with Nginx configs are:

  • ^~ does not signify a form of regex-match despite including the ~ symbol. This surprises people coming from languages like Perl, etc.
  • The non-regex match-types are fully declarative - order of definition in the config doesn't matter - but the winning regex-match (if processing even gets that far) is entirely based on its order of entry in the config file. These two very different methods of evaluation combined within the same file raises the cognitive load a lot.
  • the files checked by try_files, error_page, etc are constructed based on the path in the context of any given root and alias directives, which sometimes become "action at a distance" gotchas

I find the following example useful (good to leave all of /.well-known open, as acme isn't the only protocol to use that directory), and of course add things like @centminmod's added header for an acme-challenge sub-match within the .well-known block if you want:

root /var/www/my-server-directory;
index index.php;  # or whatever your index files are...

location ^~ /.well-known/ {
    limit_req            [tighter per-ip settings here];
    access_log           off;
    log_not_found        off;
    root                 /var/www/html;
    autoindex            off;
    index                index.html; # "no-such-file.txt",if expected protos don't need it
    try_files            $uri $uri/ =404;
}

location / {
    limit_req            [looser per-ip settings here];
    ...
}

location ~ /\. {
    return               403;
}

The reason @riobard's version works is because of the order and manner in which nginx works through the location matches. I personally find the way it is documented at nginx.org adds an unnecessary layer of temporal convolution (unless there is some nuance that I have missed when reordering the guts of points 3 and 4 below), so this is my version:

  1. If there is an exact path-match (with location = XXX {})
    • this block is processed
    • match-searching stops
  2. If there are one or more preferential prefix-matches (with location ^~ XXX {})
    • the block of the longest (most explicit) of those matches is processed
    • match-searching stops
  3. If there are one or more case-sensitive regex-matches (with location ~ XXX {}), or case-insensitive regex-matches (with location ~* XXX {})
    • the block of the first matching regex found (when scanning the config-file top-to-bottom) is processed
    • match-searching stops
  4. If there are one or more non-preferential prefix-matches (with location XXX {})
    • the block of the longest (most explicit) of those matches is processed
    • match-searching stops

The three biggest sources of confusion I have seen relevant to these kind of issues with Nginx configs are:

  • ^~ does not signify a form of regex-match despite including the ~ symbol. This surprises people coming from languages like Perl, etc.
  • The non-regex match-types are fully declarative - order of definition in the config doesn't matter - but the winning regex-match (if processing even gets that far) is entirely based on its order of entry in the config file. These two very different methods of evaluation combined within the same file raises the cognitive load a lot.
  • the files checked by try_files, error_page, etc are constructed based on the path in the context of any given root and alias directives, which sometimes become "action at a distance" gotchas

I find the following example useful (good to leave all of /.well-known open, as acme isn't the only protocol to use that directory), and of course add things like @centminmod's added header for an acme-challenge sub-match within the .well-known block if you want:

root /var/www/my-server-directory;
index index.php;  # or whatever your index files are...

location ^~ /.well-known/ {
    limit_req            [tighter per-ip settings here];
    access_log           off;
    log_not_found        off;
    root                 /var/www/html;
    autoindex            off;
    index                index.html; # "no-such-file.txt",if expected protos don't need it
    try_files            $uri $uri/ =404;
}

location / {
    limit_req            [looser per-ip settings here];
    ...
}

location ~ /\. {
    return               403;
}

dorianpula added a commit to dorianpula/acme-nginx that referenced this issue Dec 8, 2016

Add preferential prefix for NGINX.
As detailed towards the end of this [issue for the Let's Encrypt ACME spec](letsencrypt/acme-spec#221).
@cpu

This comment has been minimized.

Show comment
Hide comment
@cpu

cpu Apr 4, 2017

Member

This repository is deprecated & un-maintained. Closing this issue. If applicable, please move discussion to the replacement IETF owned repo and the mailing list.

Member

cpu commented Apr 4, 2017

This repository is deprecated & un-maintained. Closing this issue. If applicable, please move discussion to the replacement IETF owned repo and the mailing list.

@cpu cpu closed this Apr 4, 2017

@helok

This comment has been minimized.

Show comment
Hide comment
@helok

helok Apr 29, 2017

location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}

helok commented Apr 29, 2017

location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}

@letsencrypt letsencrypt locked and limited conversation to collaborators Apr 29, 2017

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.