Acme-client - SAN cert for FreeBSD with NGINX

# uname -a
FreeBSD peets 11.0-RELEASE-p9 FreeBSD 11.0-RELEASE-p9 #0: Tue Apr 11 
08:48:40 UTC 2017     root@amd64-
builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC  amd64

Domain is lazygranch.site, basically a place holder to get the server working, then I will use lazygranch.com and another domain.

Relevant portion of nginx.conf

 server {
    listen       80;
    server_name  lazygranch.site;
    #charset koi8-r;
    #access_log  logs/host.access.log  main;

# # Letsencrypt needs http for acme challenges
location ^~ /.well-known/acme-challenge/ {
proxy_redirect off;
default_type "text/plain";
#     root /usr/local/www/.well-known/acme-challenge ;
root /usr/local/www/ ;
allow all;
}

I verified that www.lazygranch.site/.well-known/acme-challenge is “reachable” by placing a file called test.txt there with the word “howdy” in it.

http://www.lazygranch.site/.well-known/acme-challenge/test.txt

returns “howdy!”

Initiating the certificate process:

# sh /usr/local/etc/periodic/weekly/000.acme-client.sh
acme-client: /usr/local/etc/acme/mydomain.site/privkey.pem: account key exists 
(not creating)
acme-client: /usr/local/etc/ssl/acme/private/mydomain.site/privkey.pem: domain 
key exists (not creating)
acme-client: adding SAN: www.mydomain.site
acme-client: https://acme-v01.api.letsencrypt.org/directory: directories
acme-client: acme-v01.api.letsencrypt.org: DNS: 104.92.124.56
acme-client: acme-v01.api.letsencrypt.org: DNS: 2600:1406:1a:393::3d5
acme-client: acme-v01.api.letsencrypt.org: DNS: 2600:1406:1a:389::3d5
acme-client: https://acme-v01.api.letsencrypt.org/acme/new-authz: req-auth: 
mydomain.site
acme-client: https://acme-v01.api.letsencrypt.org/acme/new-authz: req-auth: 
www.mydomain.site
acme-client: /usr/local/www/acme/hash1: created
acme-client: https://acme-
v01.api.letsencrypt.org/acme/challenge/hash2/1319938077: challenge
acme-client: /usr/local/www/acme/hash3: created
acme-client: https://acme-
v01.api.letsencrypt.org/acme/challenge/hash4/1319938089: challenge
acme-client: https://acme-
v01.api.letsencrypt.org/acme/challenge/hash2/1319938077: status
acme-client: https://acme-
v01.api.letsencrypt.org/acme/challenge/hash2/1319938077: bad response
acme-client: transfer buffer: [{ "type": "http-01", "status": "invalid", "error": { 
"type": "urn:acme:error:unauthorized", "detail": "Invalid response from 
http://mydomain.site/.well-known/acme-challenge/hash1: 
\"\u003chtml\u003e\r\n\u003chead\u003e\u003ctitle\u003e404 Not 
Found\u003c/title\u003e\u003c/head\u003e\r\n\u003cbody 
bgcolor=\"white\"\u003e\r\n\u003ccenter\u003e\u003ch1\u003e404 Not Found\u003c/h1\u003e\u003c/center\u003e\r\n\u003chr\u003e\u003ccenter\u003e\"", "status": 403 }, "uri": "https://acme-v01.api.letsencrypt.org/acme/challenge/hash2/1319938077", "token": "hash1", "keyAuthorization": "hash1.hash5", "validationRecord": [ { "url": "http://mydomain.site/.well-known/acme-challenge/hash1", "hostname": "mydomain.site", "port": "80", "addressesResolved": [ "myip" ], "addressUsed": "myip", "addressesTried": [] } ] }] (1147 bytes)
acme-client: bad exit: netproc(62023): 1

I had sanitized the above text not knowing that the domain name should be shown. Also I have no idea what “hash” can be safely shown, so I replaced long coded with hash1, hash2, etc.

From the nginx access.log file:

66.133.109.36 - - [11/Jun/2017:19:38:53 +0000] "GET /.well-known/acme-challenge/hash1 HTTP/1.1" 404 169 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
66.133.109.36 - - [11/Jun/2017:19:38:53 +0000] "GET /.well-known/acme-challenge/hash3 HTTP/1.1" 404 169 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"

File permissions:

# pwd
/usr/local/www
# ls -l
total 24
drwxr-xr-x  3 root  wheel  512 Jun 10 00:19 .well-known
drwxr-xr-x  2 root  www    512 Jun 11 19:38 acme

Directories /usr/local/www/.well-known/ and root /usr/local/www/.well-known/acme-challenge are empty.

In the future, can the long hash codes be safely uploaded?

Hello @gariac,

You pasted the relevant portion of nginx conf that is only valid for lazygranch.site but then you said that http://www.lazygranch.site/.well-known/acme-challenge/test.txt is reachable… your nginx conf doesn’t show any conf for www.lazygranch.site.

I don’t know what is the client you are using to issue the certificates but seems it is trying to left the challenge here:

acme-client: /usr/local/www/acme/hash1: created

and checking your conf it should be:

/usr/local/www/.well-known/acme-challenge/hash1

So it is worth to check the doc of the client you are using. Or you change the webroot path in your acme client or you change your nginx conf to point to the acme dir used by your client.

Cheers,
sahsanu

I forgot to say that If you want to change the webroot location for /.well-known/acme-challenge/ and point it to /usr/local/www/acme/ you can use something like this:

location /.well-known/acme-challenge/ {
    proxy_redirect off;
    default_type "text/plain";
    alias /usr/local/www/acme/;
    allow all;
}

With the above conf, a request to http://yourdomain.site/.well-known/acme-challenge/randomChallenge will be pointed to /usr/local/www/acme/randomChallenge.

I hope this helps.
sahsanu

Does this line look right?
acme-client: adding SAN: www.mydomain.site

As a person who is just starting out with letsencrypt, “.well-known” seems out of left field. However the guide I used indicated that is where the request from the letsencrypt server will do the “get”.

I used a combination of two guides, since neither seems to work by itself.
https://brnrd.eu/security/2016-12-30/acme-client.html

suggests the following for adding to the nginx.conf

 # Letsencrypt needs http for acme challenges
 location ^~ /.well-known/acme-challenge/ {
 proxy_redirect off;
 default_type "text/plain";
 root /usr/local/www/.well-known/acme-challenge ;
 allow all;

}

However I think this is wrong and in fact when I put a test file in the acme-challenge directory, it could not be found. I just changed the root to the wrong way and got this in the access.log file:

17.142.142.51 - - [12/Jun/2017:00:48:02 +0000] "GET /.well-known/acme-
challenge HTTP/1.1" 404 169 "-" "AppleNewsBot"
66.133.109.36 - - [12/Jun/2017:00:48:04 +0000] "GET /.well-known/acme-challenge/2nxtNLTEJ09nTYb_heqc_9MLVgEjp1qLOxUy8lN0hFw HTTP/1.1" 404 169 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
66.133.109.36 - - [12/Jun/2017:00:48:04 +0000] "GET /.well-known/acme-challenge/CK0W0kEmtMfNmxFnKH7vjaCp-y_z8lh9s3PPGWOFQo4 HTTP/1.1" 404 169 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
17.133.7.116 - - [12/Jun/2017:00:48:43 +0000] "GET /.well-known/acme-challenge HTTP/1.1" 404 169 "-" "AppleNewsBot"

And using my simple text file:
http://www.lazygranch.site/.well-known/acme-challenge/test.txt
it 404s.

I don’t think the challenge files are being written, but I don’t know if the acme-client writes them then immediately deletes them after use.

Now this guide
https://medium.com/chris-opperwall/using-acme-client-for-letsencrypt-on-freebsd-db0ee643ef1f

suggests

# acme.conf
location ^~ /.well-known/acme-challenge {
alias /usr/local/www/acme;
  try_files $uri =404;
}

This fails to find the test file I put in /usr/local/www/acme .

Now for a third variation, I tried your code, repeated here for clarity:

location /.well-known/acme-challenge/ {
 proxy_redirect off;
default_type "text/plain";
alias /usr/local/www/acme/;
allow all;
}

Well your code does use the /usr/local/www/acme directory, and I guess you are saying I should have done that first! :wink: (The test file returns “greetings” instead of "howdy.)

Running the /usr/local/etc/acme/acme-client.sh shell, it seems to create the keys. I tried to put the output here but I don’t undstand markdown enough. The formatting was a mess. Is it bad form to use pastebin for long “code”?

However, it seems that a real test is to run /usr/local/etc/periodic/weekly/000.acme-client.sh

# /usr/local/etc/periodic/weekly/000.acme-client.sh
flag 1
flag 2

Checking Let's Encrypt certificate status:
flag 3
Using hostname: peets
acme-client: /usr/local/etc/ssl/acme/private/privkey.pem: -k file must exist
acme-client: /usr/local/etc/acme/privkey.pem: -f file must exist
Skipped, deploy script does not exist or is not executable

I put in a few echoes to tried the execution. Here is the script:

# cat 000.acme-client.sh
#!/bin/sh

if [ -r /etc/defaults/periodic.conf ]
then
. /etc/defaults/periodic.conf
source_periodic_confs
echo "flag 1"
fi

PATH=$PATH:/usr/local/bin:/usr/local/sbin
export PATH

case "$weekly_acme_client_enable" in
[Yy][Ee][Ss])
    echo "flag 2"
    echo
    echo "Checking Let's Encrypt certificate status:"

    if [ -x "$weekly_acme_client_renewscript" ] ; then
            $weekly_acme_client_renewscript
    else
            : ${weekly_acme_client_args:="-b"}
            echo "flag 3"
            if [ -z "$weekly_acme_client_domains" ] ; then
                    weekly_acme_client_domains=$(hostname -f)
                    echo "Using hostname: $weekly_acme_client_domains"
            fi
            if [ -n "$weekly_acme_client_challengedir" ] ; then
                    weekly_acme_client_args="$weekly_acme_client_args -C $weekly_acme_client_challengedir"
            fi
            /usr/local/bin/acme-client $weekly_acme_client_args $weekly_acme_client_domains
    fi

    if [ -n "$weekly_acme_client_deployscript" ] ; then
            if [ -x "$weekly_acme_client_deployscript" ] ; then
                    echo "Deploying Let's Encrypt certificates:"
                    $weekly_acme_client_deployscript
            else
                    echo 'Skipped, deploy script does not exist or is not executable'
            fi
    fi
    ;;
*)
    ;;
esac

Suggestions?

Where is the private key file?
cp /your/file/location/private.key.file /usr/local/etc/ssl/acme/private/privkey.pem

I suspect the problem is I am creating multiple certs and the script is for one cert. I will be hosting two websites on the server. Also since this is all automated, I figured using different certs for the two mail accounts.

That is the final target. Right now is just have the lazygranch.site and mail.lazygranch.site domains for development purposes.

I listed all the relevant files. However the privatekey.pem files under the same domain or subdomain are not identical. I ran a diff and then eyeballed them just to make sure since the file sizes and time stamps are identical. The files begin to differ in the first line, though not immediately.

# pwd
/usr/local/etc/acme/mail.lazygranch.site
# ls -l
total 8
-r--------  1 root  wheel  3272 Jun 12 01:16 privkey.pem

# pwd
/usr/local/etc/ssl/acme/private/mail.lazygranch.site
# ls -l
total 8
-r--------  1 root  wheel  3268 Jun 12 01:16 privkey.pem

# pwd
/usr/local/etc/ssl/acme/mail.lazygranch.site
# ls -l
total 24
-r--r--r--  1 root  wheel  2155 Jun 12 01:16 cert.pem
-r--r--r--  1 root  wheel  1647 Jun 12 01:16 chain.pem
-r--r--r--  1 root  wheel  3802 Jun 12 01:16 fullchain.pem


# pwd
/usr/local/etc/ssl/certs
# ls -l
total 0

# pwd
/usr/local/etc/ssl/private
# ls -l
total 0

# pwd
/usr/local/etc/ssl/acme
# ls -l
total 24
drwxr-xr-x  2 root  wheel  512 Jun 12 01:16 lazygranch.site
drwxr-xr-x  2 root  wheel  512 Jun 12 01:16 mail.lazygranch.site
drwx------  4 root  wheel  512 Jun 12 01:16 private

Looks like you have obtained a cert for the mail site only.
But not with that name...
https://crt.sh/?q=mail.lazygranch.site
And it is not reachable from the Internet.
as port 443 on IP 138.68.45.241 is not responding

please show both virtual host files
and the contents of the public cert file:
/usr/local/etc/ssl/acme/mail.lazygranch.site/cert.pem

I should have said I just showed one domain since the other is identical. Sorry about that.

I just set up the website for https.
https://www.lazygranch.site/
It shows up as secure in chromium.

However the check you indicated doesn’t show the cert.
https://crt.sh/?q=www.lazygranch.site

I tried SSL labs:
https://www.ssllabs.com/ssltest/analyze.html?d=lazygranch.site
Some things to improve, but it is a start.

But I think there is a problem with the renewal script, which might not be recognizing that I have multiple certs.

hi @gariac

I suggest reviewing the concept of SAN cetificates (it’s not a mulitple certificate but a single certificate covering multiple sub domains).

Looks like you have things sorted now. Be careful because domain.tld is not the same as www.domain.tld so if you want to cover www and domain.tld versions of your sites then make sure you add both of these to your certificate request as you have done.

Andrei

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