Node.js + Nginx + HTTPS + Cloudflare Server setup ( Google .app domain )
Introduction
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 443
for 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 .app
and others (e.g. com, org, …etc).
Briefly speaking, .app
domains 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.
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.
7. Generate Your SSL Key from third-party (Cloudflare)
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:
- Reverse Proxy: https://www.nginx.com/resources/glossary/reverse-proxy-server/
- Port Forwarding: https://gist.github.com/soheilhy/8b94347ff8336d971ad0
- CA certificate config: https://support.cloudflare.com/hc/en-us/articles/217471977-How-to-install-an-Origin-CA-certificate-in-NGINX
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-utf3 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;
}
...
}
- listen to port 443 for HTTPS ssl
- server name setup
- ssl certificate / key
- proxy_pass: we will use port forwarding to make port 8080 be forwarded into port 443
proxy_set_header
is set for http standard packet header- 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>
- Third check: open your browser and click on https://adrianhsu.app, …cheers! :-)