realaravinth
07d368dac2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
407 lines
11 KiB
Markdown
407 lines
11 KiB
Markdown
+++
|
|
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)](https://debian.org)
|
|
2. [Nginx](https://www.nginx.com/) (webs server)
|
|
3. [Let's Encrypt](https://letsencrypt.org/) (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](https://www.digitalocean.com) (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:
|
|
|
|
```bash
|
|
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`)](https://wiki.ubuntu.com/UncomplicatedFirewall) 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`.
|
|
|
|
```bash
|
|
$ 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](https://wiki.archlinux.org/title/OpenSSH#Protecting_against_brute_force_attacks),
|
|
so we should [disable password logins on our server and only allow
|
|
public-key authentication
|
|
only](https://wiki.archlinux.org/title/OpenSSH#Force_public_key_authentication).
|
|
|
|
### 1.3.1) Generate key pair
|
|
|
|
On your local computer, generate an SSH key pair:
|
|
|
|
```bash
|
|
$ 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:
|
|
|
|
```bash
|
|
$ 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:
|
|
|
|
```bash
|
|
$ sudo systemctl restart sshd
|
|
```
|
|
|
|
### 1.3) Install and setup `fail2ban`
|
|
|
|
We will be using
|
|
[`fail2ban`](https://www.fail2ban.org/wiki/index.php/Main_Page) for
|
|
intrusion prevention by blacklisting entities (users, bots, etc.) based
|
|
on failed login attempts.
|
|
|
|
#### 1.3.1) Install `fail2ban`
|
|
|
|
```bash
|
|
$ sudo apt install fail2ban
|
|
```
|
|
|
|
#### 1.3.2) Enable `fail2ban` for `sshd`
|
|
|
|
```yml
|
|
[sshd]
|
|
enabled = true
|
|
```
|
|
|
|
#### 1.3.3) Configure `fail2ban` to start on boot
|
|
|
|
```bash
|
|
$ 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`:
|
|
|
|
```bash
|
|
$ 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:
|
|
|
|
```bash
|
|
$ 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
|
|
|
|
```bash
|
|
$ sudo systemtl enable nginx # automatically start nginx on boot
|
|
$ sudo systemtl start nginx # start nginx server
|
|
```
|
|
|
|
And verify it works:
|
|
|
|
```bash
|
|
$ 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:
|
|
|
|
```html
|
|
<!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:
|
|
|
|
```bash
|
|
$ 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>`
|
|
|
|
```bash
|
|
$ sudo ln -s /etc/nginx/sites-available/<your-domain> /etc/nginx/sites-available/<your-domain>
|
|
```
|
|
|
|
Verify configurations before deploying, `nginx` has a command
|
|
to do it:
|
|
|
|
```bash
|
|
$ sudo nginx -t
|
|
```
|
|
|
|
If there are no errors, reload `nginx` to deploy the website:
|
|
|
|
```bash
|
|
$ 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](https://letsencrypt.org/) 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`](https://certbot.eff.org/)
|
|
|
|
#### 2.3.1) Install `certbot`:
|
|
|
|
```bash
|
|
$ sudo apt install certbot python3-certbot-nginx
|
|
```
|
|
|
|
#### 2.3.2) Get a certificate for `<your-domain>`
|
|
|
|
```bash
|
|
$ 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](https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment).
|
|
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
|
|
|
|
```bash
|
|
$ 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>`!
|