Attempting to renew cert... (Dry run)

Please fill out the fields below so we can help you better. Note: you must provide your domain name to get help. Domain names for issued certificates are all made public in Certificate Transparency logs (e.g. https://crt.sh/?q=example.com), so withholding your domain name here does not increase secrecy, but only makes it harder for us to provide help.

My domain is:
test.ericmauldin.info

I ran this command:
sudo certbot renew --dry-run

It produced this output:
Saving debug log to /var/log/letsencrypt/letsencrypt.log


Processing /etc/letsencrypt/renewal/test.ericmauldin.info.conf


Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator webroot, Installer None
Running pre-hook command: /etc/letsencrypt/renewal-hooks/pre/nginx.sh
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for test.ericmauldin.info
Using the webroot path /root/recipe-app-backend for all unmatched domains.
Waiting for verification…
Challenge failed for domain test.ericmauldin.info
http-01 challenge for test.ericmauldin.info
Cleaning up challenges
Attempting to renew cert (test.ericmauldin.info) from /etc/letsencrypt/renewal/test.ericmauldin.info.conf produced an unexpected error: Some challenges have failed… Skipping.
All renewal attempts failed. The following certs could not be renewed:
/etc/letsencrypt/live/test.ericmauldin.info/fullchain.pem (failure)


** DRY RUN: simulating ‘certbot renew’ close to cert expiry
** (The test certificates below have not been saved.)

All renewal attempts failed. The following certs could not be renewed:
/etc/letsencrypt/live/test.ericmauldin.info/fullchain.pem (failure)
** DRY RUN: simulating ‘certbot renew’ close to cert expiry
** (The test certificates above have not been saved.)


Running post-hook command: /etc/letsencrypt/renewal-hooks/post/nginx.sh
1 renew failure(s), 0 parse failure(s)

IMPORTANT NOTES:

  • The following errors were reported by the server:

    Domain: test.ericmauldin.info
    Type: connection
    Detail: Fetching
    http://test.ericmauldin.info/.well-known/acme-challenge/lrbCb0Lo5ynvG3Nw5BcTALKqvUJTQU8udAHmbywACgo:
    Connection refused

    To fix these errors, please make sure that your domain name was
    entered correctly and the DNS A/AAAA record(s) for that domain
    contain(s) the right IP address. Additionally, please check that
    your computer has a publicly routable IP address and that no
    firewalls are preventing the server from communicating with the
    client. If you’re using the webroot plugin, you should also verify
    that you are serving files from the webroot path you provided.

My web server is (include version):
nginx/1.17.10 (Ubuntu)

The operating system my web server runs on is (include version):
Ubuntu 20.04

My hosting provider, if applicable, is:
namecheap

I can login to a root shell on my machine (yes or no, or I don’t know):
yes

I’m using a control panel to manage my site (no, or provide the name and version of the control panel):
no

The version of my client is (e.g. output of certbot --version or certbot-auto --version if you’re using Certbot):
certbot 0.40.0

Is your web server allowing HTTP?
Is there an IPS or firewall that might be blocking these HTTP requests?
[perhaps GeoLocation blocking or IP reputation blocking]

Great questions. Apologies as I’m learning. Where should I check for these answers?

Well hard to say for sure from where I’m sitting.
But generally speaking…
Do you know any specific details about your “test.ericmauldin.info” server?
Like (for starters):

  • which services are running on your system
  • if it is a shared or dedicated system
  • if your “hosting” package includes IPS or firewall or DDoS protection services

UFW is inactive

Vultr Server Firewall is set to none

I believe I’m on a virtual (shared) machine
dmidecode | egrep -i ‘manufacturer|product’
Manufacturer: QEMU
Product Name: Standard PC (i440FX + PIIX, 1996)
Manufacturer: QEMU
Manufacturer: QEMU
Manufacturer: QEMU

current list of services
[ + ] apparmor
[ + ] apport
[ + ] atd
[ - ] console-setup.sh
[ + ] cron
[ - ] cryptdisks
[ - ] cryptdisks-early
[ + ] dbus
[ + ] grub-common
[ - ] hwclock.sh
[ - ] irqbalance
[ - ] iscsid
[ - ] keyboard-setup.sh
[ + ] kmod
[ - ] lvm2
[ - ] lvm2-lvmpolld
[ + ] multipath-tools
[ + ] nginx
[ - ] open-iscsi
[ - ] open-vm-tools
[ - ] plymouth
[ - ] plymouth-log
[ + ] postgresql
[ + ] procps
[ - ] rsync
[ + ] rsyslog
[ - ] screen-cleanup
[ + ] ssh
[ - ] sysstat
[ + ] udev
[ + ] ufw
[ + ] unattended-upgrades
[ - ] uuidd

Have you installed anything else?
Like: Fail2Ban

No. I did not install Fail2Ban

Since spinning this server up yesterday I have installed the following…

Postgresql
pm2
nginx
git
Node
NVM (lts)
NPM
Ubuntu’s Advanced Packaging Tool (APT)
certbot

Pretty sure that is it.

OK.
Then let’s focus on the web server and the renewal conf file settings.

But first, try the command once more:
sudo certbot renew --dry-run

[there is some chance that the connection refused was only temporary]

If that still fails:

  • with the same error = please show the file:
    /etc/letsencrypt/renewal/test.ericmauldin.info.conf
  • with any other or additional error = show the error(s).

If it passes = we are done! [wishful thinking]

root@atl:~/recipe-app-backend# sudo certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log


Processing /etc/letsencrypt/renewal/test.ericmauldin.info.conf


Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator webroot, Installer None
Running pre-hook command: /etc/letsencrypt/renewal-hooks/pre/nginx.sh
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for test.ericmauldin.info
Using the webroot path /root/recipe-app-backend for all unmatched domains.
Waiting for verification…
Challenge failed for domain test.ericmauldin.info
http-01 challenge for test.ericmauldin.info
Cleaning up challenges
Attempting to renew cert (test.ericmauldin.info) from /etc/letsencrypt/renewal/test.ericmauldin.info.conf produced an unexpected error: Some challenges have failed… Skipping.
All renewal attempts failed. The following certs could not be renewed:
/etc/letsencrypt/live/test.ericmauldin.info/fullchain.pem (failure)


** DRY RUN: simulating ‘certbot renew’ close to cert expiry
** (The test certificates below have not been saved.)

All renewal attempts failed. The following certs could not be renewed:
/etc/letsencrypt/live/test.ericmauldin.info/fullchain.pem (failure)
** DRY RUN: simulating ‘certbot renew’ close to cert expiry
** (The test certificates above have not been saved.)


Running post-hook command: /etc/letsencrypt/renewal-hooks/post/nginx.sh
1 renew failure(s), 0 parse failure(s)

IMPORTANT NOTES:

  • The following errors were reported by the server:

    Domain: test.ericmauldin.info
    Type: connection
    Detail: Fetching
    http://test.ericmauldin.info/.well-known/acme-challenge/qCaLC1FAvXszgHFpA80WhDJne4H_HnaRPaSLG8EOup4:
    Connection refused

    To fix these errors, please make sure that your domain name was
    entered correctly and the DNS A/AAAA record(s) for that domain
    contain(s) the right IP address. Additionally, please check that
    your computer has a publicly routable IP address and that no
    firewalls are preventing the server from communicating with the
    client. If you’re using the webroot plugin, you should also verify
    that you are serving files from the webroot path you provided.

/etc/letsencrypt/renewal/test.ericmauldin.info.conf

renew_before_expiry = 30 days

version = 0.40.0

archive_dir = /etc/letsencrypt/archive/test.ericmauldin.info

cert = /etc/letsencrypt/live/test.ericmauldin.info/cert.pem

privkey = /etc/letsencrypt/live/test.ericmauldin.info/privkey.pem

chain = /etc/letsencrypt/live/test.ericmauldin.info/chain.pem

fullchain = /etc/letsencrypt/live/test.ericmauldin.info/fullchain.pem

Options used in the renewal process

[renewalparams]

account = 43ce977f6264eeddb509e752678973e4

authenticator = webroot

webroot_path = /root/recipe-app-backend

server = https://acme-v02.api.letsencrypt.org/directory

[[webroot_map]]

test.ericmauldin.info = /root/recipe-app-backend

That is not timed out.
That implies two way conversation.

I get "301 moved perm" (to HTTPS):

curl -Iki http://test.ericmauldin.info/.well-known/acme-challenge/qCaLC1FAvXszgHFpA80WhDJne4H_HnaRPaSLG8EOup4
HTTP/1.1 301 Moved Permanently
Server: nginx/1.17.10 (Ubuntu)
Date: Thu, 07 May 2020 19:10:03 GMT
Content-Type: text/html
Content-Length: 179
Connection: keep-alive
Location: https://test.ericmauldin.info/.well-known/acme-challenge/qCaLC1FAvXszgHFpA80WhDJne4H_HnaRPaSLG8EOup4

Since the refused connection is still in HTTP, that means that LE doesn't even get the 301.

Let's have a look at the vhost config for HTTP.

Also, do you do any special handling based on headers, user agents, etc. ? ? ?

And let's also test this webroot path:

  • create a text test-file in that folder.
    [should be accessible from Internet via: http://test.ericmauldin.info/.well-known/acme-challenge/test-file]

server {
server_name test.ericmauldin.info;

location / {
proxy_pass http://localhost:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection ‘upgrade’;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/test.ericmauldin.info/fullchain.pem; # managed>
ssl_certificate_key /etc/letsencrypt/live/test.ericmauldin.info/privkey.pem; # manag>
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
if ($host = test.ericmauldin.info) {
return 301 https://$host$request_uri;
} # managed by Certbot

   server_name test.ericmauldin.info;
   listen 80;
   return 404; # managed by Certbot

}

ran: root@atl:~/recipe-app-backend# touch test-file.txt
contains text ‘TEST’

Although pretty sure that is not what you wanted :expressionless:

OK before we go down the road of what is on port 4000 and how to get that to work with LE.
Let’s avoid it altogether and handle the HTTP request in HTTP.

Please amend the HTTP section as follows:

server {
   listen 80;
   server_name test.ericmauldin.info;
   location /.well-known/acme-challenge/ {
      root /root/recipe-app-backend/;
      try_files $uri 404;
   }#location
   return 301 https://$host$request_uri;
}#server

Done and restarted Nginx

The test-file s NOT accessible from the Internet.
It (and all requests) returns:

<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/media/hot-pepper.png"/><link href="https://fonts.googleapis.com/css?family=Liu+Jian+Mao+Cao&di
splay=swap" rel="stylesheet"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><t
itle>C's Recipes</title><link href="/static/css/2.ae87030c.chunk.css" rel="stylesheet"><link href="/static/css/main.5dbe0de8.chunk.css" rel="stylesheet"></head><body><noscript>You need to
 enable JavaScript to run this app.</noscript><script>3e3!=window.location.port&&(__REACT_DEVTOOLS_GLOBAL_HOOK__.inject=function(){})</script><div id="root"></div><script>!function(e){fun
ction r(r){for(var n,p,f=r[0],i=r[1],l=r[2],c=0,s=[];c<f.length;c++)p=f[c],Object.prototype.hasOwnProperty.call(o,p)&&o[p]&&s.push(o[p][0]),o[p]=0;for(n in i)Object.prototype.hasOwnProper
ty.call(i,n)&&(e[n]=i[n]);for(a&&a(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,f=1;f<t.length;f++){var i=t[f];
0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=p(p.s=t[0]))}return e}var n={},o={1:0},u=[];function p(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,
t.exports,p),t.l=!0,t.exports}p.m=e,p.c=n,p.d=function(e,r,t){p.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},p.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&O
bject.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},p.t=function(e,r){if(1&r&&(e=p(e)),8&r)return e;if(4&r&&"object"==typeof e&&e
&&e.__esModule)return e;var t=Object.create(null);if(p.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)p.d(t,n,function(r){return e[
r]}.bind(null,n));return t},p.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return p.d(r,"a",r),r},p.o=function(e,r){return Object.prototype.hasOwn
Property.call(e,r)},p.p="/";var f=this["webpackJsonprecipe-app-frontend"]=this["webpackJsonprecipe-app-frontend"]||[],i=f.push.bind(f);f.push=r,f=f.slice();for(var l=0;l<f.length;l++)r(f[
l]);var a=i;t()}([])</script><script src="/static/js/2.8a550738.chunk.js"></script><script src="/static/js/main.f079f6dc.chunk.js"></script></body></html>

Yes. The site is a react build that reroutes to login if no session cookie.