website/content/blog/_2022-09-10-how-to-publish-website-without-librepages.md
realaravinth 7c175be594
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fix: fix softlint cmd
2022-10-05 18:46:18 +05:30

11 KiB

+++ title = "How to deploy a website WITHOUT LibrePages" date = "2022-09-10" description = "Automation services like LibrePages exist to make lives easier but how do you do the same manually, on self-hosted hardware, or in the cloud?" draft=false

[taxonomies] tags = ['bare-metal', 'nginx', 'JAMStack', 'lets-encrypt', 'self-hosting']

[extra] author = 'realaravinth' +++

In this blog post tutorial, I'll show you how to deploy a personal website. LibrePages automates everything that is discussed in this tutorial and lets you focus on creating content. Automation is good but knowing how to do it manually using industry standard technologies always helps!

We will be using the following technologies to deploy our website:

  1. GNU/Linux server(Debian)
  2. Nginx (webs server)
  3. Let's Encrypt (for HTTPS)

Let's get started!

1. Setup Debian GNU/Linux

We are going to start with a fresh GNU/Linux installation, you could get one from a cloud provider like Digital Ocean (not affiliated).

1.1) Give your account sudo privileges

On GNU/Linux systems, the root account is the most powerful user account. It is good practice to avoid working as root since a careless mistake could wipe the entire system out.

sudo give the ability to execute commands with root capabilities from a lower-privileged account. Let's make our account sudo capable:

su # become root

# add `realaravinth`, my account` to `sudo` group to be able to use `sudo`
usermod -aG sudo realaravinth # my account is called `realaravinth`, replace it with yours
exit
$ exit

Log out and log back in.

1.2) Install and setup firewall(ufw)

Uncomplicated Firewall(ufw) is a popular firewall that is easy to set up and maintain. For most installations, this should be enough. System administrators use firewalls to open only the ports that they think should receive traffic from external networks. Without it, all ports will be open, causing a security nightmare.

We will require standard SSH (22), and the standard web ports (80 and 443). A comprehensive list of services and the list of ports the listen on is available at /etc/services.

$ sudo apt update && apt upgrade # update system
$ sudo apt install ufw # we are using `ufw` for the firewall
$ sudo ufw allow ssh # allow SSH traffic on port 22, required to log into the server
$ sudo ufw enable # deploy firewall

1.3) Secure SSH

SSH allows remote access to our servers over secure, encrypted channels. By default, users can log in with their password using SSH. But password authentication is susceptible to brute force attacks, so we should disable password logins on our server and only allow public-key authentication only.

1.3.1) Generate key pair

On your local computer, generate an SSH key pair:

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/realaravinth/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/realaravinth/.ssh/id_rsa
Your public key has been saved in /home/realaravinth/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:i2DE1b9BQb9DqV0r6O9MfPeVqUwfww1/T8wIXL2Xqdo realaravinth@myserver.com
The key's random art image is:
+---[RSA 3072]----+
|      .. .o.     |
|   . .  . .. . . |
|    o    o  + o .|
|   .      o* + .+|
|    o   S ooB o+.|
|   . . . o.. +o*=|
|      . . . ooo*X|
|           +=.ooB|
|           o+E .o|
+----[SHA256]-----+

Set a strong password the program prompts for one and save it somewhere safe. Your public key will be at ~/.ssh/id_rsa.pub and your private key at ~/.ssh/id_rsa. Never share the private key with anyone.

1.3.2) Setup public-key authentication

We have to copy the public key that we generated in the previous setup onto our server:

$ ssh-copy-id  -i ~/.ssh/id_rsa.pub myserver.com
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/realaravinth/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
realaravinth@myserver.com's password:

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'myserver.com'"
and check to make sure that only the key(s) you wanted were added.

1.3.3) Disable SSH password authentication

NOTE: Verify you can log into your account before proceeding

Now that we have a private-key authentication setup on both the client and the server, let's disable password authentication on the server:

Open /etc/ssh/sshd_config and add the following lines:

PubkeyAuthentication yes
PasswordAuthentication no

And restart the SSH server:

$ sudo systemctl restart sshd

1.3) Install and setup fail2ban

We will be using fail2ban for intrusion prevention by blacklisting entities (users, bots, etc.) based on failed login attempts.

1.3.1) Install fail2ban

$ sudo apt install fail2ban

1.3.2) Enable fail2ban for sshd

Open fail2ban configuration at /etc/fail2ban/jail.conf and add the following lines:

[sshd]
enabled = true

1.3.3) Configure fail2ban to start on boot

$ sudo systemctl enable fail2ban
$ sudo systemctl start fail2ban

1.4) Install and setup nginx

nginx is a popular web server that can be used to serve static sites. It is fast, stable, and easy to set up.

To install, run the following command:

1.4.1) Install nginx:

$ sudo apt install nginx

1.4.2) Allow web traffic: open ports 80 and 443

Ports 80 is the default for HTTP and 443 for HTTPS. To serve web traffic, we'll have to Configure ufw to accept traffic on them:

$ sudo ufw allow 80 # open ports 80 HTTP traffic
$ sudo ufw allow 443 # open ports 443 for HTTPS traffic

1.4.2) Configure nginx to start on boot

$ sudo systemctl enable nginx # automatically start nginx on boot
$ sudo systemctl start nginx # start nginx server

And verify it works:

$ curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

nginx is working!

2) Deploy website

For this demo, we'll deploy a single file(index.html) HTML website.

2.1) Install the webpage on the server

Edit /var/www/html/index.html and add the following HTML to it:

<!DOCTYPE html>
<html>
	<head>
		<title>My cool website!</title>
	</head>
	<body>
		<h1>Welcome to my website! o/</h1>
	</body>
</html>

The webpage should now be available on localhost, and we should see it when we run the following command:

$ curl localhost
<!DOCTYPE html>
<html>
    <head>
        <title>My cool website!</title>
    </head>
    <body>
        <h1>Welcome to my website! o/</h1>
    </body>
</html>

2.2) Serve webpage on a custom domain

2.2.1) Buy a domain if you don't own one already

2.2.2) Go to the domain's DNS dashboard and add the following record

@    A    300    <your server IP address>

2.2.3) Setup nginx to serve the website at http://<your-domain>

Open /etc/nginx/sites-available/your-domain and add the following:

server {
    # serve website on port 80
    listen [::]:80;
    listen 80;

    # write error logs to file
    error_log /var/log/nginx/<your-domain>.error.log;
    # write access logs to file
    access_log /var/log/nginx/<your-domain>.access.log;

    # serve only on this domain:
    server_name <your-domain>; # replace me


    # use files from this directory
    root /var/www/html/;

    # remove .html from URL; it is cleaner this way
    rewrite ^(/.*)\.html(\?.*)?$ $1$2 permanent;

    # when a request is received, try the index.html in the directory
    # or $uri.html
    try_files $uri/index.html $uri.html $uri/ $uri =404;
}

It is good practice to have all nginx deployment configurations in /etc/nginx/sites-available/ directory and link production websites to `/etc/nginx/sites-enabled directory. Doing so allows you to work-in-progress configurations or delete deployments without losing the configuration files.

Let's enable <your-domain>

$ sudo ln -s /etc/nginx/sites-available/<your-domain> /etc/nginx/sites-enabled/<your-domain>

Verify configurations before deploying, nginx has a command to do it:

$ sudo nginx -t

If there are no errors, reload nginx to deploy the website:

$ sudo nginx -s reload

Your webpage should now be accessible at http://<your-domain>!

2.3) Install certbot to set up HTTPS

HTTP is insecure. We'll have to set up SSL to serve our website using HTTPS. To do that, we will be using Let's Encrypt a popular nonprofit certificate authority to get our SSL certificates.

SSL certificates come with set lifetimes, so we renew them before they expire. The process, when done manually, is demanding: you will have to log in every three months and renew the certificate. If you fail or forget it, your visitors will see security warnings on your website.

Thankfully, there is a way to automate this process through certbot

2.3.1) Install certbot

$ sudo apt install certbot python3-certbot-nginx

2.3.2) Get a certificate for <your-domain>

$ sudo certbot --nginx -d <your-domain>

certbot will prompt you for an email ID, and ask you to accept their terms and conditions, privacy policy, etc. Be sure to read them before agreeing to them. It will then try to authenticate your domain ownership using the ACME protocol. By configuring the DNS to point to our server and by telling nginx at that domain.

When it has verified ownership, it will automatically issue, deploy the certificate on nginx and setup redirects.

2.3.3) Setup cronjob to automate SSL certificate renewals

Become root and edit crontab

$ su
crontab -e

Add the following job and exit:

0 */12 * * * certbot -n --nginx renew

It will attempt to renew SSL certificates every 12 hours. If a the certificate is due for renewal, certbot will go through the ACME challenge, get the new certificates and automatically deploy them for you.

Now our GNU/Linux server is configured and ready to serve our website at http://<your-website>!