From 479257540537d437d0ec760ffc6c9347dc07eae3 Mon Sep 17 00:00:00 2001 From: realaravinth Date: Mon, 12 Sep 2022 14:52:18 +0530 Subject: [PATCH] Squashed commit of the following: commit 1241888f29ec90c03df4ffba7e250c8b42a0a45c Author: realaravinth Date: Mon Sep 12 14:51:34 2022 +0530 feat: set draft=false commit ba3d8eab8f8f410cb9ff5d7529f11d5da4d91097 Author: realaravinth Date: Sun Sep 11 19:13:52 2022 +0530 fix: more typos and errors commit 310950ab62bef46edfcfc86d00bb586a5ad2c3a9 Author: realaravinth Date: Sun Sep 11 19:13:39 2022 +0530 feat: add toc to blog commit 9fe83cdba08194f7c59cb4b36c45f5091caf1c15 Author: realaravinth Date: Sun Sep 11 18:35:08 2022 +0530 fix: styling and typo errors commit 6070f79b9be9ebc30c983651db94f0f7e76204cf Author: realaravinth Date: Sun Sep 11 18:33:11 2022 +0530 feat: first draft of manual website deployment blog post --- ...w-to-publish-website-without-librepages.md | 397 ++++++++++++++++++ templates/blog/_toc.html | 59 +++ templates/blog/post.html | 23 +- 3 files changed, 468 insertions(+), 11 deletions(-) create mode 100644 content/blog/_2022-09-10-how-to-publish-website-without-librepages.md create mode 100644 templates/blog/_toc.html diff --git a/content/blog/_2022-09-10-how-to-publish-website-without-librepages.md b/content/blog/_2022-09-10-how-to-publish-website-without-librepages.md new file mode 100644 index 0000000..fd4be91 --- /dev/null +++ b/content/blog/_2022-09-10-how-to-publish-website-without-librepages.md @@ -0,0 +1,397 @@ ++++ +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', 'git', '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) +4. Gitea (but any Git hosting works) + +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`) 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, 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: + +```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` for intrusion prevention by blackiisting 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 + + + +Welcome to nginx! + + + +

Welcome to nginx!

+

If you see this page, the nginx web server is successfully installed and +working. Further configuration is required.

+ +

For online documentation and support please refer to +nginx.org.
+Commercial support is available at +nginx.com.

+ +

Thank you for using nginx.

+ + +``` + +`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 + + + + My cool website! + + +

Welcome to my website! o/

+ + +``` + +The webpage should now be available on localhost, and we should see it when we run the following command: + +```bash +$ curl localhost + + + + My cool website! + + +

Welcome to my website! o/

+ + +``` + +### 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 +``` + +#### 2.2.3) Setup `nginx` to serve the website at `http://.error.log; + # write access logs to file + access_log /var/log/nginx/.access.log; + + # serve only on this domain: + server_name ; # 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 `` + +```bash +$ sudo ln -s /etc/nginx/sites-available/ /etc/nginx/sites-available/ +``` + +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://`! + +### 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, Let's Encrypt provides automation through `certbot` + +#### 2.3.1) Install `certbot`: + +```bash +$ sudo apt install certbot python3-certbot-nginx +``` + +#### 2.3.2) Get a certificate for `` + +```bash +$ sudo certbot --nginx -d +``` + +`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://`! diff --git a/templates/blog/_toc.html b/templates/blog/_toc.html new file mode 100644 index 0000000..bc4b672 --- /dev/null +++ b/templates/blog/_toc.html @@ -0,0 +1,59 @@ + diff --git a/templates/blog/post.html b/templates/blog/post.html index 35c46dc..2e73c1b 100644 --- a/templates/blog/post.html +++ b/templates/blog/post.html @@ -3,19 +3,20 @@ endblock meta %} {% block content %}
-

{{ page.title }}

- {% include "blog/_meta.html" %} +

{{ page.title }}

+ {% include "blog/_meta.html" %} -
+
+ {% include "blog/_toc.html" %} {{ page.content | safe }} -
-
-
- +
+
+
+
{% endblock content %}