Node.js + Nginx + HTTPS + Cloudflare Server setup ( Google .app domain )

Adrian Hsu
9 min readMay 16, 2018

--

Introduction

“.app is a more *secure* domain for apps. From games to news to education, .app is the perfect home for your app.” (https://get.app)

Recently I’m working on my web programming homework, which is a online-chatroom with express/socket.io as backend. And personally I want to make my chatroom support the Facebook Login API & Github Login API, but both of them required a domain name (url), and I couldn’t access them with a static IP only… Therefore I decided to buy a domain name myself. You can check my results here:

What will be covered

  • Node js HTTPS server setup (not HTTP)
  • Google Domain console & how to use
  • How to deal with SSL/TLS key, and HTTPS
  • What is DNS & How to choose
  • Cloudflare (Crypto): Full SSL certificate
  • Nginx server configuration: port forwarding, proxy_pass, reverse proxy
  • (CentOS 7) Firewall setting, add/remove ports for HTTP/HTTPS
  • (CentOS 7) SELinux configuration
  • REST API

*The steps in this article require the user to have root privileges. & static IP address (for me is relied on PPPoE), to complete these configurations. please make sure you are using your own workstation (e.g. azure, aws)*

1. Node.js installation on CentOS

This is fairly easy and straightforward. You could use these scripts for these certain Linux distros: Red Hat® Enterprise Linux® / RHEL, CentOS and Fedora.

ref: https://nodejs.org/en/download/package-manager/#enterprise-linux-and-fedora

$ curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash - $ sudo yum -y install nodejs $ sudo yum install gcc-c++ make

2. Nginx installation on CentOS

Nginx is a high performance web server software. It is a much more flexible and lightweight program than Apache HTTP Server.

ref: https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-centos-7

sudo yum install epel-release
sudo yum install nginx
sudo nginx -t # to check if successfully installed

3. firewall setup

we have to add port 80 for HTTP and port 443for HTTPS, in order to make other users to access our server.

$ netstat -tpln # list all ports $ sudo firewall-cmd --list-ports # 9012/tcp is my ssh port
9012/tcp
# You must open and enable port 80 and 443 using the firewall-cmd command: $ sudo firewall-cmd --permanent --zone=public --add-service=http
$ sudo firewall-cmd --permanent --zone=public --add-service=https
$ sudo firewall-cmd --reload

Most importantly, we don’t need to add the port 8080 even though our local host use this port as default. Why? because our Nginx will do the port forwarding (443 -> 8080 and 80 -> 8080) in order to make the 8080 port to stay intact.

4. Google Domain (.app) registering & config.

First of all, we have to talk about the difference between .appand others (e.g. com, org, …etc).

Briefly speaking, .appdomains support only “HTTPS” and therefore it’s more secure, since that you need TLS/SSL certificate or other crypto (e.g. Cloudflare Crypto: Flexible SSL) to access them.

ref: https://www.instantssl.com/ssl-certificate-products/https.html

You could learn more in this clear-illustrated website: https://www.instantssl.com/ssl-certificate-products/https.html

In May 2018, Google started to provide some awesome .app domain for developers, and it’s not expensive nor difficult to setup. However, it seems that google still not provide SSL for their .app HTTPS domain, so I choose to use Cloudflare as my DNS server. I will talk about this later.

It’s 14 USD per year for .app and 12 USD for .com, so why not choose .app !

There are a lot of companies providing similar services, and Google is not the cheapest one. However, I would rather choose Google since it is more trustable for me. You could enroll your domain in the link below:

I’ve encountered one problem in the procedure: for “County” and “City”, I have to use 台北市 (but not 台北) and 內湖區 ( this is not even a city at all!) to pass, I still don’t figure it out…

BTW, please make sure you are using “Privacy protection ON” to ensure your private info will be protected.

If you need more detailed tutorial, you could check this: (in Traditional Chinese)

5. Manage our Google domain!

So now we get into our google domain console, if you’re successfully registered. And the first thing we have to do is to change the default Google DNS server into custom one, says, Cloudflare.

The reason why I didn’t use google DNS is that I couldn’t find the SSL key provided by Google, and moreover I cannot generate my own SSL key (https://www.sslforfree.com/) even though I followed the lets-encrypt tutorial. Also I don’t want to use openssl or other techniques to generate my own key, and it will be more safe if we could use third-party provided key-gen. Therefore, I choose to use Cloudflare because it’s easy to download the provided Certificate (key, pem), it’s provided by third-party, and moreover Cloudflare provides lots of CDN features. (Also Cloudflare it is another option for DNS server.)

If you want to use Cloudflare, just click on “Use custom name servers” and type in the same two server domain name as I did.

- Set up the “Custom resource records”

You need to provide Type A record and your IP address to bind your server IP with the domain name. And also, www should also be included because we want user to access our website both from https://adrianhsu.app/ and https://www.adrianhsu.app/

You could leave other form blank, as we don’t need them.

6. Register the Cloudflare & transfer DNS server from google to Cloudflare

What is cloudflare?

CloudFlare is a network of data centers that sits between your web server and the rest of the internet. This does two things: CloudFlare can serve cached static web content to the visitor, and screen visitors to make certain they are good and not traffic coming from an attack, malicious bots, or other bad things. Because CloudFlare’s network is made up of 32 global data centers, that means we can serve your visitors web content very fast regardless of the distance between your origin server and the viewer. (ref: https://www.dreamhost.com/blog/heres-why-you-should-start-using-cloudflare-today/)

Click on the link below and follow the steps, (which are all straightforward), and in the end you will get the result as the image below: a green box with Status Active. It don’t really take too much time to transfer the DNS server from google to cloudflare, even though the website says that 24 hrs is required, I wait for only 10 mins and it works.

https://www.cloudflare.com/

7. Generate Your SSL Key from third-party (Cloudflare)

click on “Create Certificate”

After checking that the DNS status is active, we can move on to download our key/pem. please set the SSL from “Flexible SSL” to “Full (Strict)” as we will use key file later on.

Click on the “Crypto” in the top bar (which is a lock icon), and then click “Create Certificate”.

Copy and paste the two hash code into ‘cert.key’ and ‘cert.pem’ denotes for private key and certificate. After that, use scp to move these two files into your server /etc/nginx/ssl/cert.key and /etc/nginx/ssl/cert.pem

8. Nginx Configuration

This is the most important section and it requires lots of web-related domain knowledges. In case you are not familiar with, check these tutorial first:

open your command line and use su to change to super user.

These is my directory tree:

[adrianhsu@localhost]:/etc/nginx
$ tree .
.
├── conf.d
├── default.d
├── fastcgi.conf
├── fastcgi.conf.default
├── fastcgi_params
├── fastcgi_params.default
├── koi-utf
├── koi-win
├── mime.types
├── mime.types.default
├── nginx.conf
├── nginx.conf.default
├── scgi_params
├── scgi_params.default
├── ssl
│ ├── cert.key
│ └── cert.pem
├── uwsgi_params
├── uwsgi_params.default
└── win-utf
3 directories, 17 files

What we need to modify is only the nginx.conf and put ssl key into ./ssl , that’s all!

The most important part in the config file is shown below:

server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name adrianhsu.app;
ssl_certificate "ssl/cert.pem";
ssl_certificate_key "ssl/cert.key";
...... location / {
proxy_pass https://0.0.0.0:8080;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

}
...
}
  1. listen to port 443 for HTTPS ssl
  2. server name setup
  3. ssl certificate / key
  4. proxy_pass: we will use port forwarding to make port 8080 be forwarded into port 443
  5. proxy_set_header is set for http standard packet header
  6. Done!

After that, please run the command:

sudo systemctl restart nginx
sudo systemctl status nginx -l

If successfully setup, you can see active in the status.

You can check your result using curl -k https://localhost and it should give a html file for the default nginx static website.

Difficulties: [502 bad gateway] scp certificate (key, pem) permission denied for nginx

These bugs made me stuck for a few hours since that I don’t understand SELinux at all… In brief, SELinux is a more advanced security tools to protect your centOS server. We have to

# original error
[adrianhsu@localhost]:/etc/nginx
$ sudo systemctl status nginx -l
nginx[8728]: nginx: [emerg] BIO_new_file("/etc/nginx/ssl/cert.pem") failed (SSL: error:0200100D:system library:fopen:Permission denied:fopen('/etc/nginx/ssl/cert.pem','r') error:2006D002:BIO routines:BIO_new_file:system lib)
[adrianhsu@localhost]:/etc/nginx
$ /usr/sbin/sestatus -v | grep SELinux
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
[adrianhsu@localhost]:/etc/nginx
$ ls -lrtZ .
drwxr-xr-x. root root system_u:object_r:httpd_config_t:s0 default.d
drwxr-xr-x. root root unconfined_u:object_r:user_home_t:s0 ssl
drwxr-xr-x. root root system_u:object_r:httpd_config_t:s0 conf.d
-rw-r--r--. root root system_u:object_r:httpd_config_t:s0 nginx.conf
...
# how to fix?[adrianhsu@localhost]:/etc/nginx
$ sudo chcon -R -u system_u ssl/
$ sudo chcon -R -t httpd_config_t ssl/
[adrianhsu@localhost]:/etc/nginx
$ sudo systemctl restart nginx # ok!

Difficulties:[523 origin unreachable] Proxy Pass not working due to SELinux

You can fix the problem in just one line:

$sudo setsebool httpd_can_network_connect 1 -P

BTW, do NOTsudo setenforce 0 because it will make your server exposed to others.

9. Node js server setup

The final step! just create the https server and listen on 0.0.0.0:8080 as we mentioned in nginx config.

index.js


// curl -k https://localhost:8080/
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('/etc/nginx/ssl/cert.key'),
cert: fs.readFileSync('/etc/nginx/ssl/cert.pem')
};
https.createServer(options, (req, res) => {
fs.readFile('index.html',function (err, data){
res.writeHead(200, {'Content-Type': 'text/html','Content-Length':data.length});
res.write(data);
res.end();
});
}).listen(8080, '0.0.0.0');

index.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Adrian Hsu</title>
</head>
<body>
<div> Hello World! </div>
<img src='https://http.cat/200' alt='load img failed'>
</body>
</html>

ok, so now run sudo node index.js on server.

  • First check: curl from server, using https://localhost which is the 443 port by default.
[adrianhsu@localhost]:/etc/nginx
$ curl -k https://localhost
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Adrian Hsu</title>
</head>
<body>
<div> Hello World! </div>
<img src='https://http.cat/200' alt='load img failed'>
</body>
</html>
  • second check: curl from client (my macbook)
(python3) adrianhsu:~/Desktop
$ curl -k https://adrianhsu.app
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Adrian Hsu</title>
</head>
<body>
<div> Hello World! </div>
<img src='https://http.cat/200' alt='load img failed'>
</body>
</html>

--

--

Adrian Hsu

Software engineer at X/Twitter@SF working on Recommendation System. Also an entrepreneur, enjoys financial analysis and cognitive/social psychology.