3

I have a Node.js server set up and I am trying to use HTTPS with my Let's Encrypt certificate on Apache. Here is the relevant code I have set up:

var fs = require('fs'),
https = require('https'),
express = require('express'),
options = {
  key: fs.readFileSync('/etc/letsencrypt/live/www.mysite.com/privkey.pem'),
  cert: fs.readFileSync('/etc/letsencrypt/live/www.mysite.com/fullchain.pem'),
  ca: fs.readFileSync('/etc/letsencrypt/live/www.mysite.com/chain.pem')
}, ...

When I try starting up the script, I get this error:

Error: EACCES: permission denied, open '/etc/letsencrypt/live/www.mysite.com/privkey.pem'

It is because only the root owner owns the Let's Encrypt directories and files for the SSL, which is currently used with my 30+ regular websites on Apache. I am trying to set up a Node App for one website. My node app.js project files are owned by mysite1:mysite1 and I know it is supposed to be safer to run the node app as a non-root user (like "mysite1").

What is the proper way to make my Let's Encrypt HTTPS work securely with the Node.js App (on Apache) without having to give my /etc/letsencrypt directory 755 permissions?

EDIT - CLARIFICATION:

Node.js and Apache are running side-by-side. In my virtualHost .conf file for the website, 443 DocumentRoot is /home/mysite1/mysite1.com , which is what my Let's Encrypt SSL was installed for.

My Node.js app is set up in /home/mysite1/app . On the website, I use socket.io as a site-wide chat room and it connects/works perfectly fine with the following (if the app is started as a root user), no ProxyPass is required:

<script src="//www.mysite1.com:3000/socket.io/socket.io.js"></script>
<script>var socket = io('https://www.mysite1.com:3000');</script>

When I run the app as a non-root user (for security purposes), the only error that I see is my node app has a permission error trying to access/configure SSL files. What specifically should I do, I'm not very clear how to run this "behind" Apache, or if there is a simpler solution?

It seems like there must be a simpler solution, maybe node.js runs as root to get the SSL data and get the server running before handing it off to the user, like Apache does?

EDIT 2:

Based on Michael's and ezra-s's suggestion, I've searched on Google for awhile and have changed a few things around but can't get it to work. In Apache, I have done something like this:

<VirtualHost *:443>
  ServerAlias mysite1.com
  DocumentRoot /home/mysite1/mysite1.com
  DirectoryIndex index.html index.php
  <Directory /home/mysite1/mysite1.com>
    .. web config stuff
  </Directory>

  ProxyRequests Off
  ProxyPreserveHost On
  ProxyVia Full
  <Proxy *>
    Require all granted
  </Proxy>

  <Location /node>
    ProxyPass http://127.0.0.1:3000
    ProxyPassReverse http://127.0.0.1:3000
  </Location>

  SSLEngine on
  Include /etc/letsencrypt/options-ssl-apache.conf
  SSLCertificateFile /etc/letsencrypt/live/www.letshangout.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/www.letshangout.com/privkey.pem
</VirtualHost>

In my node app.js project file, I've removed the require('https') and certificate options and replaced it with the "http" package and attempt to initialize the script.

On a page on my website, I have scripts that attempt to load socket.io and connect with the node app:

<script src="https://www.mysite1.com/node/socket.io/socket.io.js"></script>
<script>var socket = io('https://www.mysite1.com/node/');</script>

When I load the page, there is a 500 error and the script doesn't load up. What should I change in my Apache config or the script URLs? I've never used proxy with node before, this is my first node app.

SOLUTION:

I figured it out, it was a combination of other things that needed to be done involving web sockets and javascript:

1) Apache Config for dealing with websockets https://stackoverflow.com/a/41685748/2317571

2) JavaScript Socket Connection Config for client https://stackoverflow.com/questions/22919276/how-to-connect-socket-io-through-a-reverse-proxy

3) (bonus tip): Thanks to advice from Michael and ezra-s for getting the node app behind Apache, I can use the ufw firewall and lock down the node/socket.io port 3000 to localhost - one less port (and security hole) open to the world. Hopefully this information can help someone else.

peppy
  • 73
  • 2
  • 10
  • 2
    Run the Node.js app behind Apache, which terminates SSL and reverse proxies to it. – Michael Hampton Mar 21 '18 at 19:10
  • I've edited the question to clarify a bit more how my app runs, maybe there is a simpler solution? I'm not exactly sure how running the app behind Apache would work as it works great except for getting SSL set up during initialization on the app. – peppy Mar 21 '18 at 19:46
  • @peppy What Michael tries to tell you is that "apps" should rarely ever be running side by side with http server but rather have the http server as front end, dealing with SSL and probably static stuff and reverse proxying to the "internal app" mainly through "http" so you don't expose it and just use it for what it does best, that is, generating dynamic content. Consider that, by nature (if this can be said), applications or dynamic content generators will always be weaker than http servers to exploits, and all sorts of bugs and weaknesses. – Daniel Ferradal Mar 21 '18 at 20:25
  • Thanks for the explanation. Do I add this to apache for my site's VirtualHost 443 conf: " ProxyPass https://www.mysite1.com:3000 ProxyPassReverse https://www.mysite1.com:3000 ? I changed ownership of the app directory to: "mysite1:www-data", reloaded apache and then used "nodejs app" from the command line to initiate the app, but I am still getting the permission error. – peppy Mar 21 '18 at 21:06
  • or am I supposed to remove the "https" npm package from the node app? – peppy Mar 21 '18 at 21:19
  • It's just like proxying any other node.js app. But since Apache is terminating SSL, you do not do this in your app. – Michael Hampton Mar 21 '18 at 21:34
  • Thanks for the clues. I've been doing more reading about it and tried to set up proxy configurations on Apache. Unfortunately, none of the attempts are working. I've never proxied to any node.js app before, this is my first node app, so I really don't know what to do. I've edited the question and second time to show the changes I made on Apache and my script URLs trying to call the file from my app. – peppy Mar 21 '18 at 22:28
  • Thanks for pushing me in the right direction. I found the solution which involved a few other things that I needed to track down. I've added these elements to the question to complete the solution. – peppy Mar 22 '18 at 00:25
  • So is it solved? – Algo7 Apr 19 '20 at 17:07

1 Answers1

3

1. Run a Node.js app on a port by itself.

2. Reverse proxies to it.

Note: The connection between the Node.js app and the Apache will be using HTTP. However, it is only going to take place on a "localhost" level. Therefore, it will be fine.

The VirtualHost Configuration:

<VirtualHost *:443>
        ServerName nodeapp.com
        ServerAdmin webmas@localhost
        DocumentRoot /var/www/nodeapp

        SSLEngine on
        SSLCertificateFile      /path/to/cert
        SSLCertificateKeyFile   /path/to/key


        ProxyRequests off
        ProxyPass "/" "http://127.0.0.1:node_app_port/"
        ProxyPassReverse "/" "http://127.0.0.1:node_app_port/"

        ErrorLog ${APACHE_LOG_DIR}/nodeapp_error.log
        CustomLog ${APACHE_LOG_DIR}/nodeapp_access.log combined

</VirtualHost>
Algo7
  • 297
  • 1
  • 8