Certbot Error - Type: unauthorized Detail: Invalid response from (node.js, parse-server)


#1

I’ve spent countless hours trying to get certbot to work for my application. I’m running a nodejs (Ubuntu Desktop 14.04) server in a virtual box at home. Eventually, it will need to serve https on port 4443. I’ve also registered for a duckdns sub-domain.

I can access my site through a browser locally and via my cell phone’s browser while connected to the cell network. I was able to do this in each of the scenarios below.

Initially, I ran in to the issue where I had to use iptables to take port 443 traffic to port 4443 (the port where my https app resides). The next issue I ran in to was… I was already using self-signed certs there and learned that was an issue. Then I figured out that certbot can also use port 80 and that it doesn’t try to validate existing certs when doing so and that it only needs access to a working port 80. Sooooo…I created a separate nodejs server on port 1337 to serve up a static page, setup iptables to send port 80 traffic to 1337 (random), and ran certbot using --standalone-supported-challenges http-01. I used standalone in all of the steps above.

The last error I’m getting via certbot is:

IMPORTANT NOTES:

Does this mean I need to serve up a static page at the route it is specifying in the error message? I’ve poured through docs most of the day resolving all of the issues mentioned above, and now I am here and don’t know what else to do.

Thanks in advance for any help that you can provide.


#2

That’s correct. You mentioned using the standalone plugin, which tells certbot to spawn its own web server (on port 80 in your case). That web server would handle serving the challenge files at that route. There would be no need to use a separate nodejs server on a different port in that scenario.

What I suspect is happening is that certbot happily spawns a web server on port 80 and is ready to serve the challenge files, but your iptable rules redirect that traffic to your nodejs server, which doesn’t know anything about the challenge file. Getting rid of that iptables redirection should fix your problem.


#3

I got the certs by shutting down all servers and leaving port 80 open. Now, how in the world do I use them? I tried moving/copying them over in to my server folder, but I notice they are symlinks. Not only that, they are owned by root. My server doesn’t run as root. I’m not a linux ninja so I’m sort of lost here. Is/was there a way to specify an install path with certbot during the process at the command line and also not create them as root ownership?

I feel like I’m a bit closer…

Thanks.


#4

There are some node-specific instructions in this topic:

(I wasn’t sure if your question was just about the permission problem, or about how to configure SSL in node in general.)

The permissions are a feature - you don’t want every user on your system to be able to read your private key. If you need to read the files using a different user, I would recommend chown-ing them to that user in a separate step after you run the certbot command. I would advise against copying the files somewhere else and rather point your code to the files in /etc/letsencrypt/live/example.com directly.


#5

I have node configured and working using my self-signed certificates. I just need to get the ones created by certbot over in to the folder where my self-signed certs reside currently (and where my node.js code point to). I’m not sure I can have the node.js code point to a folder structure outside of my project structure. Even if I could, the files were all created with root permissions for even reading them.

Obviously I’m going to move my self-signed ones out of the way first. But really, my problem is - what do I copy over to my server folder…will the symlinks work? Or do I need to move the actual pem files over? If so, I can’t figure out how to do either. As I mentioned, the entire directory structure and files that certbot created, were all created as owned and locked down by root.

Thanks for staying engaged with questions.

PS. It would have been a cool feature to have specified an install path at the certbot cli.


#6

I would definitely recommend pointing your node server to /etc/letsencrypt/live/example.com either way. There should be no reason why node would have a problem with that (it’s just a path after all). Mixing code and keys makes it easy to accidentally do things like committing private keys to git, which is generally a bad idea. You also lose out on certbot’s automatic management of those files during renewal, meaning you would have to copy those files again whenever you renew (the certificate changes when you renew).

The file permission is not set in stone, that’s what my chown recommendation was about. chown allows you to change the permissions for a file, so if your node app runs as user “node”, chown node:node /etc/letsencrypt/live/example.com/privkey.pem (etc.) would change the owner of that file and allow the node user to read it.

With regards to symlinks, you can treat them like regular files, and they’ll behave like one in just about every scenario. Pretend it’s a real file. :smile:


#7

I am ALL - MOST - THERE. The last error I’m getting is a node error - it can’t find root.pem. Obviously there isn’t a root.pem. Out of the files certbot created, which file do I need to point to for that one?


#8

How are you using root.pem in your code? If that’s the file you’re using for the ca option, you can use chain.pem (in your /etc/letsencrypt/live/example.com directory).


#9

That’s exactly the option it addresses. I commented out that part of my code and everything is working now. I’ll try uncommenting it and pointing to chain.pem when I have time. Thanks for all of your guidance.

For anyone that may stumble across this post, here is the node.js releveant code I used for the node.js/express app to access the certs provided by Certbot:

var options = {
  key: fs.readFileSync('/etc/letsencrypt/live/myapp.mydns.org/privkey.pem', 'ascii')
, cert: fs.readFileSync('/etc/letsencrypt/live/myapp.mydns.org/cert.pem', 'ascii')

/*
  // only for verification - comment next two lines out to not use
, ca: [
    fs.readFileSync('/etc/letsencrypt/live/myapp.mydns.org/chain.pem'),
  ]
, requestCert: false
*/
, rejectUnauthorized: false
, SNICallback: function (domainname, cb) {
    // normally we would check the domainname choose the correct certificate,
    // but for this demo we'll always use this one (the default) instead
    cb(null, require('tls').createSecureContext(options));
  }
, NPNProtcols: ['http/1.1']

};

var port = 4443
var httpsServer = require('https').createServer(options, app);
httpsServer.listen(port, function() {
console.log('parse-server-example running on port ' + port + '.');
});

ParseServer.createLiveQueryServer(httpsServer);

This application is for a self-hosted Parse Server. I use iptables to take traffic in on port 443 and put it on 4443 due to node not being able to bind to port 80 or 443 without root permission.
I hope this helps someone in the future.

pfg - Is there a way to mark this as solved or do I just make an edit to the title?


#10

Additional note: For android to accept the cert, I had to un-comment the above code:

ca: [
    fs.readFileSync('/etc/letsencrypt/live/myapp.mydns.org/chain.pem'),
  ]
, requestCert: false
*/
, rejectUnauthorized: false`

Here’s an excerpt as to why:

The third case of SSLHandshakeException
occurs due to a missing intermediate CA. Most public
CAs don’t sign server certificates directly. Instead, they use their main CA certificate,
referred to as the root CA, to sign intermediate CAs. They do this so the root CA can be stored
offline to reduce risk of compromise. However, operating systems like Android typically
trust only root CAs directly, which leaves a short gap of trust between the server
certificate—signed by the intermediate CA—and the certificate verifier,
which knows the root CA. To solve
this, the server doesn’t send the client only it’s certificate during the SSL handshake, but
a chain of certificates from the server CA through any intermediates necessary to reach a
trusted root CA.

The excerpt can be found here: https://developer.android.com/training/articles/security-ssl.html


#11

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