Setting-up Secure WebSockets on Ubuntu Servers
The purpose of this guide is to help someone setup a secure websocket server on any Ubuntu VM (AWS/Linode/Digital Ocean/Azure etc.).
Given the current state of the world, supporting features such as collaborative editing and real-time chat became almost essential to TunePad's long term success.
All modern browsers support websockets and a lot of progress has been made since the time socket.io was the only option. The simplest way to get real-time chat up and running was to use a Node package called 'ws' to set up a websocket-server (written in Javascript, of course), and then write a client in any language we wish to (in our case, Dart).
The documentation – and it is excellent – for setting up a local client + server is already available in the 'ws' repository; however, I found that figuring out a production-level/cloud VM setup required collating multiple guides. In addition, if your site runs on an https connection, then the websocket also needs to be encrypted. Most browsers block mixed content (i.e. content from http and https sources is frowned upon), and in this case, the 'wss' protocol is preferred.
Without further ado, let's dive in.
Step 1: Install Node and the WS Package
Install Node/npm (node package manager)
sudo apt-get install nodejs npm #install node/npm
sudo ln -s /usr/bin/nodejs /usr/local/bin/node
Then, create a directory on the VM where you wish to store/run the websocket. In my case, I made a directory called "dropin".
The "npm init" command will create a project.json file, you can breeze through it by pressing the return key, and then edit the generated file later. Here's a sample from my setup
Step 2: Decide on the websocket port & get/use SSL certificate
Choose a port for the websocket, as long as it is not one already in use by the system, you should be okay; my tip would be to select a port that is not commonly used. Say, in this case, the port is 56112; allow TCP connections:
#open socket port
sudo ufw allow 56112/tcp
In order to setup a secure socket, you need access to SSL certificates. You can get new ones using Certbot, or you can reuse the certificates that have already been issued for your domain. In either case, we need the path to the cert.pem and the key.pem files for the websocket server setup. If you used LetsEncrypt to generate the certificate for your domain, this is what you'll have to do:
sudo certbot certificates #lists all current certificates
#--- if you want a new domain/A-record certificate, run this---#
sudo certbot --domains your_domain_name
#------#
#in the path from the "certbot certificates" command, list the *.pem files (the default would look like this below)
sudo ls /etc/letsencrypt/live/your_domain_name
#jot down the names of the certificate (usually cert.pem) and the private key (usually key.pem or privkey.pem)
Now, in the editor of your choice, say Vim, Nano etc., paste the code for the server given below and save it (name the file the same as the main/entry-point name in the package.json file, i.e. the name we used after running npm init), in this case the filename is server.js
Server Code
Keeping fingers and toes crossed, you are now ready to run the websocket server and test it!
Now, let's test our setup via WebSocket.org. If all went well, you should be able to connect and send a message.
Step 3: Running the websocket server via systemd
Now that we have successfully setup the websocket server, we need to make sure that it runs as a service (that means, you don't need to run "sudo node server.js" each time, and it starts up upon server reboot automatically).
To do that, we would use systemd. Here are the commands/setup:
The command above would open up an empty file. Edit the contents of the file to be something similar to what I have here:
Now, just try starting the service, and afterwards check its status to see if it worked.
sudo systemctl start dropin
sudo systemctl status dropin
You should see something similar to this:
Websockets are really neat, with a little bit of front end code, you can now make a real-time chat client!