Server setup guide

A quick step by step guide on setting up an empty server, with a website, with a few extras


0. Introduction

Hello!
This site (pufac.dev) was hosted originally on Github Pages, which is a free static webpage hosting service. It allows for static .html and .css files, but practically no other extras, such as an actual system behind it, which you can access and add more functionality to.

I also have a working server at home, which is what I used to do the [Malware Analysis] and the [C2 Server] infrastructure. I used that server as a net to catch malicious files in the first post, and I used it as a Command center in the second post. It has been quite handy so far.

But there is a big disadvantage to this (at least I think so), that the server needs to be running 24/7 here in my room, to be actually useful. Eating up electricity, humming and clicking constantly (it's an old hardware).

Therefore, I have decided that I'm going to move this website to an actual remote server (VPS - Virtual Private Server), instead of Github Pages, and I will bring over some functionality from my home server as well.
This way, it will be up 24/7, and I can run anything else on it. Win-win.


1. Renting the server

First and foremost, of course I have to rent the actual server. After some consideration and a little bit of research, I have found that the cheapest and most convenient option was to use Racknerd.

Their base prices are quite high, or at least seem to be: A server, equipped with 3 cores, 2 GB RAM and 75GB of storage at ~21$/month is not exactly a cheap price.

But if you dig a little deeper, you can find the Racknerd Specials which is a collection of featured packs, at a much much lower price: 2 cores, 35GB storage, 2GB RAM, and 5TB monthly transfer at only $36/YEAR (~$45 including tax in my country) is a much better price. It roughly translates to $3/month (or $3.75 with my tax).
This is of course a lower end server, with one less core and 40GB less storage (but more bandwith), but in my opinion, this is a much better deal to take.

So this is what I purchased. Shortly after I got an email containing the IP address, and the root password, and credentials to a web based control panel to the server, which I didn't really use.


2. First steps

First, let's connect to it using SSH: The first step on a new linux machine is to always run: sudo apt update && sudo apt upgrade -y. (If you have root privileges, sudo is not needed.) A system update. I have already done this, so it would have no effect right now.

Next, we have to install docker, so we can run containers for our services. This is a 2 part command:

  • curl -fsSL https://get.docker.com -o get-docker.sh
  • sh get-docker.sh
The first one downloads the get-docker.sh script, the second one executes it. This is the easiest way to install docker.

Next very important step: ufw! Our first line of defense.
If it is not installed: sudo apt install ufw
The ports we have to open are the following:
  • 22 - The SSH port. If you close this, you will lose SSH access to your server. If that happens, the web VPS control panel can help.
  • 80 - HTTP
  • 443 - HTTPS
You can do this by ufw allow 80/tcp for example. After setting all ports (especially port 22!), you can enable the firewall with ufw enable. ufw status lists the current setup.
It should look something like this: If you ever change your SSH port to something else (which is advised, because of the bots constantly spamming everying), do NOT forget to set a new ufw rule to let yourself in on the different port, before you close the connection.


3. Nginx

Since I already have a webpage setup, I'll just copy over the files to a newly made directory: scp -r ./* root@my-ip:/root/website
(This command copies all files from the current (./*) directory to the target directory)

Next up, we are going to create an basic Nginx docker, to hold our webpage (in a docker-compose.yaml file): The setting - ./:/usr/share/nginx/html:ro allows the docker to read the current directory (./), and use it as it's own html directory, so in other words, nginx can now see the web files in this folder.

You can launch the docker with docker compose up -d, while in the same directory as the docker-compose.yaml file.

At this point, if you go to your server IP:80 url in a browser, you should see your webpage. But this is halfway done, because the browser will complain that it's not safe (http), and you can only run one service at a time, on port 80, and nothing else.

Let's fix that.

Another docker is needed: a hub of some sort, which can redirect to all of our services, and to make it safe (https). We will use Nginx Proxy Manager (npm): I used a little trickery here with port 81, which I will explain in a second.
Port 81 is the admin page / control panel of the nginx reverse proxy. You can only set up the SSL (http -> https, so traffic is encrypted), AFTER you have created an account and logged in. Which means, you can only create an account without https, therefore your credentials flow through the internet while NOT encrypted. This is a big security risk, and should be avoided.

Technical details

Usually the setup looks like this: '81:81', which is technically '0.0.0.0:81:81', which means that traffic comes from ANYWHERE targeting port 81, it will be routed to the npm docker's internal port 81, which is where it waits for data.
By using '127.0.0.1:81:81' instead, I tell the container to only accept traffic from localhost. This way, the admin panel is not accessible from outside of the server itself. Except one way, which is explained under this textbox.

Since the admin panel is now localhost access only ('127.0.0.1:81:81'), we can do something interesting:
ssh -L 8181:127.0.0.1:81 root@my-ip This command connects us to the remote server using SSH, and connects our localhost:8181 to the server's 127.0.0.1:81.
Essentially. this makes it seem like that the localhost:81 on the server is actually our 8181 port from the server's perspective. Using this method, we can access the nginx admin panel with high security. This is called an SSH Tunnel.

We connect to localhost:8181 using a browser, and we get the admin panel.
(I'm already logged in, but you can simply create an account with an email and a password in the form, then press register) We will be back here soon, but we have to do something else first.

4. Cloudflare

Currently our website can only be accessed using the IP itself, which is obviously not a solution. Here comes Cloudflare into play.

First, we have to buy a domain, but don't worry, they are usually very cheap. You can search for, and purchase a domain on Cloudflare Registrar. The cheaper domains are usually these: .uk, .org, .ca, .com, .net, .dev, but there are many of them, worth looking around.

Once you purchased your preferred domain, you get a dashboard, where you can see many statistics of your domain. On the left, under DNS, click on Records.

Here, you have to add 3 records, exactly as these down here. Except of course use your own domain, and you VPS's IP address. Don't forget to set all 3 to Proxied for an extra layer of security.

Now, your domain will always point to your IP! Just as pufac.dev always points to my VPS's IP.

5. Final touches

Now back to the Nginx reverse proxy admin panel.
Click on proxy hosts (leftmost green button), then click on Add Proxy Host. Here, you have to fill out the form:

  • Domain names: This is your newly purchased domain
  • Scheme: Keep this at http. Let's Encrypt (later), and Cloudflare will take care of this
  • Forward Hostname / IP: This is your VPS's IP address
  • Forward Port: This is the port we've given to Nginx to pay attention to. (It's in the docker image under the 3. section of this article), 8080
  • Access List: Publicly Accesible for now
  • Block Common Exploits: ON
Switch over to the SSL tab on the top.
Press on Request a new Certificate. This may fail the first time, just try again after a few seconds. Also tick Force SSL and HTTP/2 Support After we do these steps, we are pretty much done.
Our site is available at our very own domain, with a backend server we can configure to our liking. We can run countless (limited by the server resources obviously) services and dockers, including VPNs, File hosting services, voice chats etc. You can even configure an API, which you can hardcode into any game you make, and can possibly upload leaderboards there, and you pretty much have your own "game server".
Basically anything you can think of.

In the Extras section, I will show 3 more functionalities. We will set up another service using docker, a basic passphrase authentication, and we will set up an Access List in the admin panel.

6. Conclusion

I started with a website, and I ended up with a website. Some may say that this entire project has no point and that is was unnecessary, because from the outside you end up at the same place you started from.
While it is partially true that from the outside nothing much changed, the interior is completely different.

First of all, I don't have to depend on whether my home server is running or not. Now it's on. 24/7. That alone, is worth it for me.
Secondly, Github Pages only allowed for static webpages, basically html, css, and javascript files maybe, but nothing too complex. But now, I can set up anything I want, I can run any service I want, I can even make my own. All this for a few $/month.

Another idea for the future: if I ever build some sort of IoT automation, or DIY device at home, I can connect it up to my server, I can control it from there, I can collect data and statistics. All this would not be possible without something running all day and night.
So overall, it was worth it.

Anyway, thank you for reading! You can read the 3 extras down below, but the server works completely fine without them, but they serve as neat extensions.



7. Extras I. - Another service

Some extra functionality I deemed interesting enough to be here. I will keep them brief, as they are just previews of what you can do.

First, another service: a map service, which I'm using to mark points of interest on the map. This docker is a little bit more complicated than the others, it basically creates a database for the map, as well as the map itself.
The only interesting thing is the ports part, where once again, I've set the port to 172.17.0.1:8850:8000 which is the internal port of the docker network. (This could also be done with setting the dockers on one network internally but I didn't bother with that.)
This way, direct access (ip:port from browser) is not possible. It can only be routed through our reverse proxy.
I've also set the SITE_URL to my subdomain, which we will get to in a second.
You can set the SECRET_KEY to whatever, just make it long and unguessable. It will be used for encryption.

Finally, you can go back to the NPM admin panel, and add a new Proxy Host: You can freely set whatever subdomain you'd like (eg. anything.pufac.dev). It will redirect to whatever you're pointing it to.

Whether you need to enable websocket support, depends entirely on the service. It's usually required when there is real-time two way datastream involved (like chats, dashboards, graphs etc.).
Also don't forget to request a new certificate in the SSL menu on the right.

And we are done! Your new service is (hopefully) up and running on your specified domain.

8. Extras II. - Access List

Access Lists are very easy to enable, and allows you to protect a subdomain or site with a username-password combination. You can add a new Access List on the right with the blue button. (I'm editing my already existing one here). You can keep Satisfy Any off. (If it's turned off, connecting from a whitelisted IP will still require a password, but if turned on, it will bypass the password prompt for whitelisted IPs.)
The IP white and blacklists are in the Rules tab on the popup window. On the Authorizations tab you can set a username and a password. You can set the Access List in any of the proxy hosts.

9. Extras III. - Passphrase Authentication

I'll keep this very brief, as the article is getting way too long already...

Access Lists are cool and all, but it bothered me that you must have a username as well. Very inconvenient in my opinion, that you can't do a quick login with a simple phrase, you HAVE to use a complete account's worth of credentials.

So I put together a simple javascript service, which does exactly that. It puts an authorization cookie in the browser, when you have entered the right passphrase. Without it, some proxy hosts, (which I set this up in), will redirect you back to the login prompt if the cookie is missing.

These are the cookie's settings (these are all in a server.js file): This is the login, where it checks against the passphrase, and decides what to do (it also sends a Discord alert to my server with a webhook hardcoded into the server file): And this is where it checks if it's authenticated. If not, it redirects back to the passphrase prompt.