First run on server with http to https automatic redirection with nginx

I have this nginx.conf that simply redirects http to htttps. Now for first runs of acme-client, I assume the redirection to https will be a problem given there is no cert nor nginx server block for the new https domain. Does acme-client and letsencrypt have some secret sauce to handle this situation?

Since this will be an infrequent situation, I set up the nginx.conf file with lines to comment out and enable, which is hopefully self-evident.

Comments? Tips? Am I over thinking this? File follows:

user www;
#user  nobody;
worker_processes  1;

# This default error log path is compiled-in to make sure configuration parsing
# errors are logged somewhere, especially during unattended boot when stderr
# isn't normally logged anywhere. This path will be touched on every nginx
# start regardless of error log location configured here. See
# https://trac.nginx.org/nginx/ticket/147 for more info. 
#
#error_log  /var/log/nginx/error.log;
#

#pid        logs/nginx.pid;


events {
    worker_connections  512;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip  on;
# This "server kicks the user from http to https #
    server {
        listen       80;
        server_name  lazygranch.site www.lazygranch.site;
        #access_log  logs/host.access.log  main;
######## When running acme for the first time, comment out this redirect####
  return 301 https://www.lazygranch.site$request_uri;
######## and enable (remove #) this "location" #########
#location /.well-known/acme-challenge/ {
#    proxy_redirect off;
#    default_type "text/plain";
#    alias /usr/local/www/acme/;
#    allow all;
}


    # HTTPS server
    #
    server {
        listen       443 ssl http2;
        server_name  lazygranch.site www.lazygranch.site ;

        ssl_certificate      /usr/local/etc/ssl/acme/lazygranch.site/fullchain.pem;
        ssl_certificate_key  /usr/local/etc/ssl/acme/private/lazygranch.site/privkey.pem;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

location /.well-known/acme-challenge/ {
    proxy_redirect off;
    default_type "text/plain";
    alias /usr/local/www/acme/;
    allow all;
}
        location / {
            root   /usr/local/www/nginx/lazygranch.site/public_html;
            index  index.html index.htm;
        }
    }

}

I get where your going with this, but has this approach worked with renewals?
Do you feel uncomfortable leaving the .well-known/acme-challenge requests in http and redirecting all other requests to https?

‎First time trying this reply to email thing. Here goes.

I still need to debug the renewal script. But I meditated on this chicken and egg problem for a while, read some similar posts but not exactly my question, and figured I’d consult the “hive.” That is, I knew I would have a http to https redirect in the final product, so I wanted to understand the issues.

I worked on setting up some logical trap to catch the acme-client request to a new nginx server, only to realize the nginx.conf would need editing anyway, so no use trying to be clever.

You can keep the http to https redirection in the conf from day 1.
If you make the exceptions for the .well-known/* (challenges) requests.

If you control the entire server…
I’ve had success in Apache2 with this global setting:
Alias /.well-known/acme-challenge/ /local/folder/acme-challenge/

forcing all vhosts challenges into just one folder :smile:

I will go on the nginx list and see if that is possible. Still I would need to take care of http versus https.

Is there any advantage to renewing certs under https? If not, maybe a fancier redirection could be set up to never redirect the acme-client challenge to https.

Yes you will still need to redirect http to https if you don’t want any http traffic.
That is its’ own issue.
The problem is without proper consideration, after forcing https the http acme-challenges may fail.
Having the ability to exclude only the challenges from https redirection seems to satisfy both requirements.

OK. For clarity here, plain http is preferred for the acme-client.

In a browser, this nginx.conf does the following:

1) http://www.domain.com/.well-known/acme-challenge/yadayada goes to the 
acme directory.
2) https://www.domain.com/.well-known/acme-challenge/yadayada gets converted 
to  http://www.domain.com/.well-known/acme-challenge/yadayada then goes to 
the acme directory
3) http//www.domain.com/notwellknown becomes 
https://www.domain.com/notwellknown
4) https://www.domain.com/notwellknown is processed normally as https

I haven’t tested this with the client yet since I don’t want to be up all night. You can see I used a 307 return for the acme https to http. I didn’t want the browser to remember the redirect.

user www;
#user  nobody;
worker_processes  1;

# This default error log path is compiled-in to make sure configuration parsing
# errors are logged somewhere, especially during unattended boot when stderr
# isn't normally logged anywhere. This path will be touched on every nginx
# start regardless of error log location configured here. See
# https://trac.nginx.org/nginx/ticket/147 for more info. 
#
#error_log  /var/log/nginx/error.log;
#

#pid        logs/nginx.pid;


events {
    worker_connections  512;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip  on;
# This "server" kicks the user from http to https unless an acme challenge#
    server {
        listen       80;
        server_name  lazygranch.site www.lazygranch.site;
        #access_log  logs/host.access.log  main;
# select all port 80 acme requests o 
location /.well-known/acme-challenge/ {
    proxy_redirect off;
    default_type "text/plain";
    alias /usr/local/www/acme/;
    allow all;
}

#at this point, the goal is just to convert http to https
location / {
  return 301 https://www.lazygranch.site$request_uri;
}
}


    # HTTPS server
    #
    server {
        listen       443 ssl http2;
        server_name  lazygranch.site www.lazygranch.site ;

        ssl_certificate      /usr/local/etc/ssl/acme/lazygranch.site/fullchain.pem;
        ssl_certificate_key  /usr/local/etc/ssl/acme/private/lazygranch.site/privkey.pem;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;
#The assumption here is the acme challege has run successfully at least once
#since the sys admin has written the code to handle this particular domain under port 443.
#However  the cert could have issues, so better to switch to port 80
#But we can't lose the the challege code number  
location /.well-known/acme-challenge/ {
return 307 http://www.lazygranch.site$request_uri ;
}
#otherwise normal processing
        location / {
            root   /usr/local/www/nginx/lazygranch.site/public_html;
            index  index.html index.htm;
        }
    }

}

Here is the actual expected results based on your config above (#2 differs):

  1. http://www.domain.com/.well-known/acme-challenge/yadayada goes to the
    acme directory.
  2. https://www.domain.com/.well-known/acme-challenge/yadayada remains
    https://www.domain.com/.well-known/acme-challenge/yadayada but does not get to the acme directory (404 error)
  3. http//www.domain.com/notwellknown becomes
    https://www.domain.com/notwellknown
  4. https://www.domain.com/notwellknown is processed normally as https

I didn’t get a 404. I think it works. However there is an issue with the acme-challenge data coming from browser cache. I added an expires -1 to the “location”, but that didn’t force a fresh download.

I logged all my testing here. No use clogging up the forum since I think the nginx.conf still needs a tweek to get around the cache issue.
https://pastebin.com/NgUm6ZEH

I was reluctant to put the actual links here since they will eventually 404. But this is the link in question so there is no miscommunication.
https://www.lazygranch.site/.well-known/acme-challenge/test.txt

You should get switched to plain http. If your cache is clear, I will eventually see a 200 response on my server. You can tell me the exact time you clicked on the link and I will post the response minus your IP address.

I also added a simple html file in the event cosmic rays would cause index.html to be displayed.
https://www.lazygranch.site/acmetest.html

It also can do the http to https thing.

http://www.lazygranch.site/.well-known/acme-challenge/test.txt
https://www.lazygranch.site/.well-known/acme-challenge/test.txt
both return: “greetings!”

But why are you redirecting HTTPS acme-challenges to HTTP?

In the event there is an issue with the cert, I rather have the challenge go to the http. Apparently letsencrypt has three modes (challenges?). One is DNS, which I didn’t see offered but I saw referenced in posts. The other two being https and http. I ran across some post where https was causing a renewal issue. Since letencrypt works with http, I figured I would make that the default.

Also note that if I set up an automatic http to https for the websites in general, I needed to prevent the challenge from being redirected to https, which on the first run wouldn’t even exist.

‎Basically I’m covering my bases here since this is supposedly to be all automatic.

I doubt the letsencrypt challenge even does a browser cache, but might as well learn how to force fresh downloads as a useful skill. I can do it in a header, but not sure how to do it for files in general.

The request to get new or renew cert can specify which method (http|https|dns) you prefer.
So, redirecting any protocol to any other simply reduces your available choices.
If the sites needs to be https then redirect all traffic to https; but exclude challenge requests from redirection. So that you have that option open.
The idea is you decide which method you want the challenge to be and the site should always accept it that way.

Yes, redirecting can, and should, work but why should you have to redirect anything? when you could just request a challenge in whichever way you want.
Or even request it twice (once in http and then immediately after in https - the chance of success is doubled)

It isn’t clear to me given the instructions on that FreeBSD reference that there is a first run command or procedure. The instruction was to just run the weekly script by hand.

As [quote=“gariac, post:14, topic:35991”]
The instruction was to just run the weekly script by hand
[/quote]

As you should.
Although I would script/automate that process.

Can you show weekly script you intend to run?

My only addition will most likely be to:
run the script once for http
run the script once for https
(That way it will have reduced chance of failure.)

/usr/local/etc/periodic/weekly is
https://svnweb.freebsd.org/ports/head/security/acme-client/files/000.acme-client.sh.in?view=markup&pathrev=425172

usr/local/etc/acme/acme-client.sh uses

#!/bin/sh -e                                                                                                                                                                       

BASEDIR="/usr/local/etc/acme"                                                                                                                                                      

SSLDIR="/usr/local/etc/ssl/acme"                                                                                                                                                   

DOMAINSFILE="/usr/local/etc/letsencrypt/domains.txt"

ACME_FLAGS="-v -e -m -b -n -N"                                                                                                                                                     

cat "${DOMAINSFILE}" | while read domain line ; do                                                                                                                                 
   set +e # RC=2 when time to expire > 30 days                                                                                                                                     
   acme-client ${ACME_FLAGS} ${domain} ${line}                                                                                                                                     
   RC=$?                                                                                                                                                                           
  set -e                                                                                                                                                                          
  [ $RC -ne 0 -a $RC -ne 2 ] && exit $RC                                                                                                                                          

done

Basically from
https://medium.com/chris-opperwall/using-acme-client-for-letsencrypt-on-freebsd-db0ee643ef1f

I had started from a different guide, so I domain.txt is in a different location.

I made my certs by running the acme-client.sh shell, not the weekly. The weekly is the one with the error.

To try to expand a little bit on @rg305's observation about this, there are the three methods you mentioned, but the method that is used on a particular occasion is chosen by the client that you run. The certificate authority doesn't simply try them at random or in some order, but rather according to what your client says it's chosen to use.

Only the HTTP-01 method (which initially makes a connection to HTTP on port 80) is willing to follow HTTP redirects to retry the validation at another URL. (This may be an HTTP or HTTPS URL on the same server or on a different server, including with an arbitrarily specified port.) However, the initial connection will always be in HTTP using port 80, and must be answered with some valid reply, either the requested file contents or an HTTP redirect message.

The TLS-SNI-01 method (which initially makes a connection using TLS on port 443) does not really use HTTP in any meaningful way and is not willing to follow redirects. It expects a particular custom certificate to be presented immediately. If this certificate isn't presented, the validation fails. This means, for example, that if you have a CDN or firewall terminating HTTPS connections on behalf of your server, this validation method can't be used, regardless of what kind of HTTP redirects you might have set up. If the CDN or firewall responds with anything other than the expected custom certificate, that's the end of the process.

I did something novel known as RTMF, well technically RTF Manual page. For acme-client, http port 80 is the default. You still need the nginx.conf location trap to prevent http to https conversion, but the nginx.conf code to kick https back to http for the challenge shouldn’t be needed.

Odd that the manual suggests a daily check for cert renewal. Given the 30 day grace period, weekly should be enough.

We suggest once a day, partly because certificates can be revoked. Also, some sites have lots of certificates and should be renewing them gradually over time, rather than all on the same day. Also, we thought knowing about any renewal problems with a 30-day grace period would be valuable, particularly because people who might be responsible for fixing the problems could be on vacation at some point.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.