How a !webdev built a personal website
Note: This is a quickly written and poorly structured post, however there’s still some information that may help you get a website up-and running with Hugo without having to spend as much time as me.
For some years I’ve been running my blog on blogger.com owned and operated by Google.
Feeling bad about being part of tracking everyone have been looking for something to spin up behind an NGINX or Apache web server as that is fast and easy to do in a (relatively) secure way. Also wanted the site to be static for the same reason.
Played with MDWiki which is nice, but wanted something a bit more modern/fancy looking. Read about Hugo somewhere, but the first theme I tried required Node (WTF). Mistaking that for a requirement for Hugo i started playing with other stuff, only later brain said “That can’t be true Ya ol' fool, look again!” Played with the Ananke Theme, but then stumbled across the Hero Theme which looked kinda neat to me (Beauty is in the eye of the beholder).
Glossing over the multiple stupid mistakes made during the “build” of this website, here’s the basic steps.
1) Followed the Hugo Quick Start
2) Installed the Hero Theme instead of the Ananke theme used in the quickstart
3) Converted blogger to Hugo with BloggerToHugo ¹
4) Spun up a Debian 11 Server on DigitalOcean ²
5) Setup DNS A and AAAA RRs pointing to www and blog.infosecworrier.dk
6) Installed NGINX
7) Installed Lets Encrypts Certbot
8) Requested certificates:
1
certbot run -n --agree-tos --nginx -m webmaster@infosecworrier.dk --domains www.infosecworrier.dk --domains blog.infosecworrier.dk --domains infosecworrier.dk
9) Uploaded the public directory of the local site to the servers /var/www/ directory³ with rsync. More on how that was built later.
Bam, the web site is now runnning, but read below for some of the learnings i had.
10) Installed Crowdsec, massive participative IPS for extra protection.
11) Added redirects to NGINX configuration to avoid 404 errors on changed blog post directories.
¹ This required a decent amount of manual adjustment to get the blogs in the location and to have the look I wanted.
² The link to DigitalOcean above is a referral link: “Everyone you refer gets $200 in credit over 60 days. Once they’ve spent $25 with us, you’ll get $25.”
³ This directory likely should be linked to a location with the space required if your website grows big size-wise.
I wanted a real simple site, so only kept parts of the example site and added blog pages (see below), then created a favicon as well as logos for computer and mobile with Inkscape (admittedly they need to be redone). All three are under static/ - use the same pixelsettings as the originals and they will work fine on all devices.
During the fail fast phase(s) of the development of the site, the following command was used a lot:
|
|
This allowed me to break everything and immediately see (and sometimes understand) what stupid mistakes I’d made, by browsing to http://localhost:1313/
⁴ --buildDrafts mainly for having a look at the blog before publishing the site.
I liked the general look of the services part of the example site, so used that as a template for the blog part, copying everything in layout/services to layout/blog and content/services to content/blog then changed them to my liking.
The same is true for the Useful Stuff part of the site btw, however the icons used there are the ones provided with the example site, whereas (somewhat) relevant pictures for the individual blogpost.
I wanted to show date published and word count at the top of every blog post, so added the following to single.html (layouts/blog)
|
|
So now the “main” section of that file now look like this. I chose a small font for that.
|
|
The frontmatter⁵ for this blogpost, as shown below, describe metadata of the post itself, including if it is a draft. The critical metadata below settings below are: a) date is the date that will be shown at the top of the post, b) url is used to locate the posts in a year/month structure within /blog/. c) draft when true hugo will not add the post (unless using --buildDrafts) this post had this state from the day I started writing it and until it was published.
The rest should be self-explanatory, but there’s good examples to be found on web.
|
|
⁵ Front matter allows you to keep metadata attached to an instance of a content type, i.e., embedded inside a content file, and is one of the many features that gives Hugo its strength.
Found out about shortcodes when needing to (re)add Youtube videos to two old blog posts, so created a file called youtube.html with the following content under layout/shortcodes/
|
|
Then inserting a Youtube video is as easy as adding this with the id of the video of choice (example below).
|
|
Of course we all want to be verified by adding it to the footer.html file under layouts/partials. Below is a snippet of that file with the modification in place,
|
|
Now it is just a matter of copying the contents of the yoursite/public directory to the webserver.
To make sure everything is exactly as the source, use rsync with --delete:
|
|
To add an extra layer of protection, crowdsec was used.
Installation required the following steps:
1a) curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | tee -a crowdsec.sh
1b) remember to verify that the script looks “right”
2) chmod 755 crowdsec.sh
3) ./crowdsec.sh
4) apt install crowdsec
5) apt install crowdsec-firewall-bouncer-nftables
6) cscli collections install crowdsecurity/nginx
7) cscli collections install crowdsecurity/base-http-scenarios
8) cscli collections install crowdsecurity/nginx-log-parser
9) cscli collections install crowdsecurity/nginx-logs
10) cscli collections install crowdsecurity/linux
11) cscli collections install crowdsecurity/http-cve
Blogger.com creates a /year/month directory structure for all blog posts. For the migration the choice was to do the same, however locate that in the /blog/ directory, creating this structure /blog/year/month. To ensure that old links still worked and didn’t end up with the dreaded 404 - Page not found, simple return statements were added to the sites configuration file (/etc/nginx/sites-available/default). Below is a simplified example using a single year:
|
|