<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Jacob Jangles</title><description>Computers are hard.</description><link>https://jacobjangles.com/</link><item><title>Fixing Gluetuns inter-network DNS failures in Docker</title><link>https://jacobjangles.com/posts/fixing-gluetuns-inter-network-dns-failures-in-docker/</link><guid isPermaLink="true">https://jacobjangles.com/posts/fixing-gluetuns-inter-network-dns-failures-in-docker/</guid><description>Docker DNS won&apos;t resolve but you can ping the IP? Using Gluetun? Easy.</description><pubDate>Sun, 16 Feb 2025 00:00:00 GMT</pubDate><content:encoded>Docker container A is using container C in Network Mode. Container A can&apos;t resolve container B using DNS, but it works when you ping the IP address. I might have a fix.

One container is **[Gluetun](https://github.com/qdm12/gluetun)** and it is being used as the network for some other containers like **Seer**. That means you&apos;ve got `network_mode: &quot;service:gluetun&quot;` set for Seer, and if you want to access it, you gotta go via Gluetun. You&apos;ve also got a third container called **Fin**, except Fin is in another network called **Shared**, and it does not use the Gluetun network.

So we&apos;ve got 3 containers (**Gluetun**, **Seer**, and **Fin**) and 3 networks (**Shared**, and a local network for the containers **Gluetun** and **Fin**). Seer uses the Gluetun container as the network to reach out to the internet, and to access any containers on the **local-gluetun** network. Both the Gluetun and Fin containers sit in the Shared network, but you can&apos;t resolve DNS between them. Your Docker Compose file might look something like this:

```yaml
services:
  gluetun:
    networks:
      - local-gluetun
      - shared
  seer:
    network_mode: &quot;service:gluetun&quot;
  fin:
    networks:
      - local-fin
      - shared
```

If you ping Fin from the Seer container doing something like `ping 127.0.0.1`, you get the sweet reply like `Reply from 127.0.0.1: bytes=32 time=1ms TTL=64`. But as soon as you try and ping the container using it&apos;s hostname like `ping fin`, it can&apos;t resolve. While Gluetun has network connectivity, it can&apos;t resolve the host name.

[This post](https://forums.docker.com/t/container-name-doesnt-resolve-when-using-another-container-as-network-mode/141240) in the Docker forums will solve this problem (Thanks to **thedweller** for posting and coming back with their own fix - 10/10). In your Gluetun configuration, you need to set the environment variable `DNS_ADDRESS` to `127.0.0.11` - so your Docker Compose config might look something like this:

```yaml
services:
  gluetun:
    networks:
      - local-gluetun
      - shared
    environment:
        - DNS_ADDRESS=127.0.0.11
  seer:
    network_mode: &quot;service:gluetun&quot;
  fin:
    networks:
      - local-fin
      - shared
```

What we&apos;re doing here, is telling Gluetun to use Docker DNS, instead of Gluetun Unbound DNS. Gluetun Unbound defaults to [DOT](https://en.wikipedia.org/wiki/DNS_over_TLS) using Cloudflare (You can find the [configuration options here](https://github.com/qdm12/gluetun-wiki/blob/main/setup/options/dns.md)). So we still have network connectivity, but now we&apos;re using Docker DNS which _does_ know about the Fin container and its hostname, and so _will_ resolve it. 

The other benefit here is that your VPN provided DNS will also be used. This means we&apos;re not leaking IP addresses to a third party, although in the default configuration you will leak the CloudFlare DNS nodes closest to the exit node of your VPN, not your IP address. 

From [this Gluetun issues thread](https://github.com/qdm12/gluetun/issues/2023), the Gluetun maintainer thinks it&apos;s better to leak the DOT DNS nodes as it avoids putting all your eggs in one basket - I personally disagree and think it&apos;s a better option to route the requests through your trusted VPN provider instead of a third party. Either way, you aren&apos;t leaking your IP address. If you prefer to use a third party, I would recommend avoiding Cloudflare and instead use another provider like [Quad9](https://www.quad9.net/).

# References

- https://forums.docker.com/t/container-name-doesnt-resolve-when-using-another-container-as-network-mode/141240
- https://github.com/qdm12/gluetun-wiki/blob/main/setup/inter-containers-networking.md
- https://github.com/qdm12/gluetun/issues/2023</content:encoded></item><item><title>Reset your Supermicro IPMI password to default</title><link>https://jacobjangles.com/posts/reset-your-supermicro-ipmi-password-to-default/</link><guid isPermaLink="true">https://jacobjangles.com/posts/reset-your-supermicro-ipmi-password-to-default/</guid><description>Reset your Supermicro IPMI password back to the default.</description><pubDate>Mon, 25 Sep 2023 00:00:00 GMT</pubDate><content:encoded>If you&apos;re anything like me, you change default credentials because that&apos;s a great idea. If you&apos;re vaguely like me, you may have misplaced these credentials. So, you&apos;ve changed your Supermicro IPMI username and/or password and now you can&apos;t authenticate because you forgot them. Luckily, you have physical access to the server, which means you can reset them to their defaults.

You&apos;ll need to create a bootable USB with FreeDOS on it. Drop the IMPICFG utility onto the USB. Boot to FreeDOS on the USB and use the IPMICFG utility to reset the IPMI credentials back to default.

At a high level, here&apos;s what you&apos;ll do:
1. Create a bootable FreeDOS USB.
1. Get IPMICFG and drop it onto the USB.
1. Boot to FreeDOS.
1. Use IPMICFG to reset the username and password.

# Create a bootable FreeDOS USB
There&apos;s a few ways you can make a USB boot to FreeDOS. The often suggested tools are [Rufus](https://rufus.ie/) and [Etcher](https://etcher.balena.io/). Using Rufus is slightly easier because you can just set the **Boot selection** menu item to `FreeDOS` and away you go. If you&apos;re using Etcher, then you&apos;ll need to [download FreeDOS](https://freedos.org/download/) and write that image to the USB.

I won&apos;t go into more details, but there&apos;s a million and one guides on how to do this - just make sure the USB you&apos;re going to use doesn&apos;t have any data you want on it, because it will get destroyed.

# Get IPMICFG and drop it onto the USB
Download the IPMICFG utility from the [Supermicro download center](https://www.supermicro.com/en/support/resources/downloadcenter/smsdownload?category=IPMI). You&apos;ll need to set **Select OS:** to `DOS, Windows, Linux, UEFI, FreeBSD`, click **Download**, click **Continue as Guest**, and then enter a bunch of details. No need to bleed your personal information, go to [10 Minute Mail](https://10minutemail.com/) to get a random email and paste the email into the **Work Email**, **First Name**, **Last name**, and **Company Name** fields. Finally, acknowledge the EULA, satisfy the CAPTCHA, and click Submit.

Extract the .zip file you downloaded, and copy the whole directory into the root of your FreeDOS USB. You should have a directory called something like `IPMICFG_1.34.2_build.230224` sitting on your USB.

Safely eject your USB.

# Boot to FreeDOS
Plug the USB into your Supermicro server, and turn it on. When you see the Supermicro splash screen, mash `F11` like you&apos;ve already lost that QTE three times in a row to invoke the Boot Menu.
![Supermicro Boot screen](@/assets/blog-images/2023-09-25-reset-your-supermicro-ipmi-password-to-default/1-supermicro-boot.jpeg)

The boot devices you see might be slightly different to what I get but you want to boot to `UEFI: Built-in EFI Shell`. 
![Boot device selection screen](@/assets/blog-images/2023-09-25-reset-your-supermicro-ipmi-password-to-default/2-supermicro-boot-drive.jpeg)

The screen you want to see looks something like this. The exact details will be different depending on your server, but you should see a bunch of devices.
![Default FreeDOS menu](@/assets/blog-images/2023-09-25-reset-your-supermicro-ipmi-password-to-default/3-freedos-menu.jpeg)

# Use IPMICFG to reset the username and password
In the list of devices, you need to identify which one is the USB. In my case, it&apos;s `fs1`. To open the device, you type the name of the device, including the colon, and press enter. So we type `fs1:`, press **Enter**, and our prompt now shows `fs1:\`. We can run `ls` to list the directories, and we can see our `IPMICFG_1.34.2_build.230224` directory.
![Output after running ls in FreeDOS](@/assets/blog-images/2023-09-25-reset-your-supermicro-ipmi-password-to-default/4-freedos-ls.jpeg)

Now we want to navigate to the utility, and run it. We can `cd IPMICFG_1.34.2_build.230224` to change directory, and then `cd UEFI` to change directory again. Now we can `ls` to list what&apos;s in this directory, and we should see `IPMICFG.efi`, which is our utility.
![Showing the IPMICFG utility](@/assets/blog-images/2023-09-25-reset-your-supermicro-ipmi-password-to-default/5-freedos-show-ipmicfg.jpeg)

We can type `IPMICFG.efi -m` which will spit out our IP and MAC just to validate that everything is working correctly. To actually reset the IPMI to defaults, type `IPMICFG.efi -fd 2`. 

If you&apos;re interested, the **IPMICFG_UserGuide.pdf** in the download has all the commands for the IPMICFG utility.
![Reset to factory defaults](@/assets/blog-images/2023-09-25-reset-your-supermicro-ipmi-password-to-default/6-freedos-ipmicfg-commands.jpeg)

# Errors
If you get an error like `ls/dir: Cannot open current directory - No Mapping` it&apos;s likely because you are trying to `cd` to the USB - the command is just the device name. In this case, you would only type `fs1:` and then press enter.
 
# Resources
- [Serve The Home - Reset Supermicro IPMI password](https://www.servethehome.com/reset-supermicro-ipmi-password-default-lost-login/)
- [XenSpec - Configuring IPMI with IPMICFG](https://blog.xenspec.com/everything-you-need-to-know-about-configuring-ipmi-with-ipmicfg/)
- [IPMICFG Overview and commands](https://www.supermicro.com/en/solutions/management-software/ipmi-utilities)
- There was another resource which I&apos;ve lost :(</content:encoded></item><item><title>Free Web Analytics with umami</title><link>https://jacobjangles.com/posts/free-web-analytics-with-umami/</link><guid isPermaLink="true">https://jacobjangles.com/posts/free-web-analytics-with-umami/</guid><description>Get free web analytics using umami, PlanetScale, Vercel, and GitHub!</description><pubDate>Fri, 12 May 2023 00:00:00 GMT</pubDate><content:encoded>I previously [wrote](https://jacobjangles.com/posts/privacy-preserving-analytics-using-umami/) about using [umami analytics](https://umami.is/) where we needed a VPS, and we had to manage our own docker containers and database. But you can get other people to do that for you, and do it for _free_! That&apos;s assuming you are allowed to use, and stay within, the free tier of [PlanetScale](https://planetscale.com/), [Vercel](https://vercel.com/) and [GitHub](https://github.com/).

Let&apos;s get started.

# Accounts
To do any of this, you will need an account for [PlanetScale](https://auth.planetscale.com/sign-up), [Vercel](https://vercel.com/signup), and [GitHub](https://github.com/signup). Check the requirements of their free tiers, if you&apos;re breaching then you&apos;ll either lose the service or get charged money.

# Database
We&apos;ll create our database first. PlanetScale doesn&apos;t support [foreign keys](https://planetscale.com/docs/learn/operating-without-foreign-key-constraints), so we&apos;ll need to run a bootstrapping process first so umami can vibe with the database. 

## Create the database
Just in case my instructions become stale, you can find the PlanetScale documentation on how to [create a database here](https://planetscale.com/docs/onboarding/create-a-database). 

1. Log into PlanetScale.
1. Depending on where you land, click either **create** or **Create your first database**.
1. Name your database `umami-db`.
1. Choose your region - you&apos;ll probably want to pick whichever is closest to you. I chose `ap-southeast-2`.
1. Click **Create database**.
1. Click **Connect** (If you can&apos;t see that, click into the database you just made).
1. Click **New password**.
1. Enter a **Name** - something like `vercel-application`. Leave the **Branch** as `main`, and make sure the **Role** is `Admin`.
1. Click Create password. (Just in case, you might need to make sure you are **Connect with** `Prisma`.)
1. Copy the `.env` string somewhere safe - we&apos;ll need it later. It has the `DATABASE_URL` which contains the username and password that the umami application will use to connect to the database.  
	It looks like this:  
	```shell
	DATABASE_URL=&apos;mysql://fldn3vo06n:pscale_pw_ffsv42jfj4@aws-ap-southeast-2.connect.psdb.cloud/umami-db?sslaccept=strict&apos;`
	```

Now we&apos;ve setup our database and we&apos;ve got what we need to connect to it.

## Forking umami
Next we need to fork umami, clone it to our local machine, and setup some environment variables so we can connect to the PlanetScale database.

1. Log into GitHub.
1. Go to the [umami repository](https://github.com/umami-software/umami).
1. Click **Fork**.
1. Verify you&apos;re happy with the details - the defaults are fine, and click **Create fork**.

Now you&apos;ve got a copy of the umami project in your own GitHub account. We&apos;ll need to clone it so we have it locally, and make some adjustments.

1. In your terminal, clone your forked repo.  
	```console
	git clone git@github.com:yourGithubName/umami.git
	```
1. `cd` to the the root directory of your forked umami project.  
	```console
	cd ~/repos/umami
	```
1. Create a `.env` file.  
	```console
	touch .env
	```
1. Edit the `.env` file in your preferred editor (CLI or GUI), and paste in the `.env` string we copied earlier. We also need to add in `SKIP_DB_CHECK=1` on a new line.  
	```console
	nvim .env
	```
	The file should end up looking like this:  
	```shell
	DATABASE_URL=&apos;mysql://fldn3vo06n:pscale_pw_ffsv42jfj4@aws-ap-southeast-2.connect.psdb.cloud/umami-db?sslaccept=strict&apos;
	SKIP_DB_CHECK=1
	```

Ok so we&apos;ve got a forked copy of umami, and we&apos;ve cloned it locally. We&apos;ve also primed it so we can connect to the database on PlanetScale.

## Bootstrapping the database
Now we need to install some utilities and bootstrap the database. 

You&apos;re going to need to install
- `nodejs` (I use [asdf](https://asdf-vm.com/) to manage node)
	```
	asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
	asdf install nodejs latest
	asdf global nodejs latest
	```
- `yarn` (I use [brew](https://brew.sh/) to manage utilities)
	```
	brew install yarn
	```
- `npm-run-all` (You&apos;ll need node, which should include npm)
	I had to add the `--legacy-peers-deps` flag otherwise npm would error out with `ERESOLVE unable to resolve dependency tree`.
	```
	npm install npm-run-all --legacy-peers-deps
	```

We need to be in the `umami` directory we ended up in during the last step, so you should be somewhere like `~/repos/umami/`.

1. All we need to do here is build and update the database.
	```console
	yarn run build-db &amp;&amp; yarn run update-db
	```

You can go to PlanetScale and check as you should now have tables in there. You can also click **Promote to production** to let PlanetScale know this database is actually important. Nice, that&apos;s our database done.

# Application
First we gotta create a project and authorise Vercel against GitHub.
1. Log into Vercel.
1. On your Dashboard, click **Create a New Project**.
1. Click **Continue with GitHub**.
1. In the pop up, enter your GitHub username and password.
1. Click **Sign in**, and enter your second factor. If you don&apos;t have MFA enabled, you should also do that.
1. Review what Vercel wants access to, and click **Authorize Vercel**. You may get an error, just double check the browser hasn&apos;t prevented a pop up. If it has, allow the pop up.
1. In the new pop up, select the option `Only select repositories` and select your umami fork.
1. Click **Install**.

Now we can configure our Vercel project.
1. In Vercel, you should see your GitHub name and umami project listed - click **Import**.
1. On the **Configure Project** page, drop down the **Environment Variables** section.
1. var1: Set **name** to `DATABASE_URL`.
1. var1: Set **value** to what we copied a long time ago: `mysql://fldn3vo06n:pscale_pw_ffsv42jfj4@aws-ap-southeast-2.connect.psdb.cloud/umami-db?sslaccept=strict`
1. var2: Set **name** to `SKIP_DB_CHECK`.
1. var2: Set **value** to `1`.
1. var3: Set **name** to `HASH_SALT`.
1. var3: Set **value** to a randomly generated string. Something like `v5mVge03YHzoJwCj`.
1. Then click **Deploy**.

Wait a few minutes, and then your umami instance should be up and available. 

If you want, you can configure a custom domain inside Vercel so you can host umami under your own domain name. The [Vercel documentation](https://vercel.com/docs/concepts/projects/domains/add-a-domain) is a good start but TL;DR...

Go to project, Settings, Domains, enter your domain, click **Add**. Note the details and go wherever you manage DNS for your domain. You&apos;ll need to add a new DNS entry of type `CNAME`, and point it to `cname.vercel-dns.com`. Once your new domain is verified and working, you can remove the originally assigned DNS name.

# Configure umami
By default, umami has a default username and password which is.. not great. So we need to change that.

1. Go to your umami domain (either the Vercel provided one, or your custom domain if you configured it).
1. Login by using the username **admin** and the password **umami**.
1. Click **Settings** at the top of the page.
1. Click **Accounts**.
1. On the default admin user, click **Edit**.
1. Enter a new password.
1. Optionally, enter a new username.
1. Click **Save**.
1. Click the profile icon in the top right, and click **Logout**.

Now we&apos;ve change our default admin user, and we&apos;re ready to start using umami. Adding in websites isn&apos;t too hard, and the [umami documentation](https://umami.is/docs/add-a-website) is pretty solid so I won&apos;t cover that here.

# Clean-up

## Principle of least privilege
When you&apos;re setting everything up, you will need the database password to have the admin role, but you may only need the read/write role in day to day operations. I haven&apos;t tested this yet, but assuming that works then it&apos;s a sensible thing to do which more closely aligns to the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege).


## Local machine 
We don&apos;t really need our local copy of our forked umami project. We also don&apos;t need to keep the custom changes we made, so feel free to delete them. **You will want to hold on to your `DATABASE_URL` string in your password manager** though, as you cannot retrieve the username and password from PlanetScale.

You can also remove the command line utilities we installed, assuming you don&apos;t need them for anything else. However if you don&apos;t care so much, leave them hanging around for the next time you need to do some work on umami.

# Resources
- [umami Documentation - Hosting](https://umami.is/docs/hosting)
- [umami Documentation - Running on PlanetScale](https://umami.is/docs/running-on-planetscale)
- [umami Documentation - Running on Vercel](https://umami.is/docs/running-on-vercel)</content:encoded></item><item><title>Privacy Preserving Analytics using umami</title><link>https://jacobjangles.com/posts/privacy-preserving-analytics-using-umami/</link><guid isPermaLink="true">https://jacobjangles.com/posts/privacy-preserving-analytics-using-umami/</guid><description>How to use umami to get privacy preserving analytics.</description><pubDate>Sat, 26 Dec 2020 00:00:00 GMT</pubDate><content:encoded>---

**There&apos;s an easier way to do this now, I wrote about it here: [Free Web Analytics with umami](https://jacobjangles.com/posts/free-web-analytics-with-umami/)**

---


Recently I wanted some analytics on my blog to see what my popular pages were, and where the traffic was being referred from. Google Analytics doesn&apos;t float my boat as it&apos;s not exactly privacy preserving, and I don&apos;t want to feed the service sacrificing giant. I looked at a few options, and decided to go with [umami](https://umami.is/).

We&apos;ll go through how to set up all the related services first, and then install umami. You might be interested in standing up your analytics differently, or you might just use different services and so can&apos;t follow this guide step by step, but at the very least you can see how to deploy umami with Docker. 

As a high level architecture, umami will be installed onto a host along with a web server. We&apos;ll configure DNS to point our domain to the host, and the web server will proxy traffic from the internet to umami. This host services our umami administrator panel, as well as the location for the tracking JavaScript. The tracking code is generated by umami, which you&apos;ll need to inject into the head of the website that you&apos;d like analytics on. We will setup our analytics domain to be `analytics.mysite.com`, so if you ever see that domain, replace it with your own domain (or IP address).

# Requirements
## Minimum
At the bare minimum, you&apos;ll need an internet accessible host with a public IP, either by [DDNS](https://en.wikipedia.org/wiki/Dynamic_DNS) (I wrote about setting this up for [free with ddclient and Cloudflare](https://jacobjangles.com/posts/free-ddns-using-ddclient-and-cloudflare/)) or a static IP that you can install umami on. This is technically enough to get your analytics configured and working, but it&apos;s a bit rough around the edges. I use a [VPS](https://en.wikipedia.org/wiki/Virtual_private_server) with a static IP, and it&apos;s got [Ubuntu 20.04](https://releases.ubuntu.com/20.04/) installed.

## Recommended
To make our analytics setup more complete, here are the other components you&apos;ll want in addition to the minimum ones:
- A web server. We&apos;ll use [nginx](https://nginx.org/en/).
- Certificates. We&apos;ll use [acme.sh](https://github.com/acmesh-official/acme.sh) to get &apos;em (which uses [Let&apos;s Encrypt](https://letsencrypt.org/)).
- DNS management. We&apos;ll use [Cloudflare](https://www.cloudflare.com/).
- A domain. Where you buy it from doesn&apos;t matter.

# nginx
On our Ubuntu instance, we&apos;ll need to install [nginx](https://nginx.org/en/) so we can enable [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security), have a nice looking domain, and set ourselves up if we want other things on the host.

We&apos;ll grab the latest version of nginx instead of whatever the Ubuntu repository has. To do that, we&apos;ll want to grab nginx&apos;s signing key, and add the official nginx repository into our list of sources.

```bash
sudo wget https://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key
# clean up after ourselves
rm nginx_signing.key
```

Now we&apos;ll want to update our sources. To get the most recent stable version for Focal Fossa (Ubuntu 20.04) we&apos;ll edit our `/etc/apt/sources.list` file with:

```bash
sudo nano /etc/apt/sources.list
```

Then add the below snippet into the file. 

```bash
# nginx
deb [arch=amd64] https://nginx.org/packages/ubuntu/ focal nginx
deb-src https://nginx.org/packages/ubuntu/ focal nginx
```

Note that if you don&apos;t include `[arch=amd64]` you&apos;ll get the below warning when doing `apt update`. It&apos;s benign, but still annoying:  
`N: Skipping acquire of configured file &apos;nginx/binary-i386/Packages&apos; as repository &apos;https://nginx.org/packages/ubuntu focal InRelease&apos; doesn&apos;t support architecture &apos;i386&apos;`

Now we can actually install nginx.

```bash
sudo apt update
sudo apt install nginx
```

We don&apos;t have certificates yet, but we will soon. We can put some placeholder files in so that the nginx configuration validator doesn&apos;t complain. Don&apos;t forget to replace `analytics.mysite.com` with your domain.

```bash
sudo mkdir /etc/nginx/certs
sudo touch /etc/nginx/certs/analytics.mysite.com.key
sudo touch /etc/nginx/certs/analytics.mysite.com.cer
sudo touch /etc/nginx/conf.d/analytics.mysite.com.conf
sudo nano /etc/nginx/conf.d/analytics.mysite.com.conf
```

Now we&apos;re going to add in an nginx configuration file. Again, you&apos;ll need to change `analytics.mysite.com` to your domain name. I have some safe to deploy [security headers](https://securityheaders.com/) in there, with some commented out as they&apos;ll require customisation. I recommend reading about them so you can configure them for your site!

```bash
sudo touch /etc/nginx/conf.d/analytics.mysite.com.conf
sudo nano /etc/nginx/conf.d/analytics.mysite.com.conf
```

The snippet below is the nginx configuration file that you&apos;ll need to change up a bit.

```nginx
server {
    listen 80;
    listen [::]:80;
    server_name analytics.mysite.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name analytics.mysite.com;
    
    ssl_certificate /etc/nginx/certs/analytics.mysite.com.cer;
    ssl_certificate_key /etc/nginx/certs/analytics.mysite.com.key;
    
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;
    ssl_session_tickets off;

    ssl_protocols TLSv1.3;
    ssl_prefer_server_ciphers off;

    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header Referrer-Policy no-referrer-when-downgrade;
#   add_header Strict-Transport-Security &quot;max-age=63072000&quot; always;
#   add_header Content-Security-Policy &quot;default-src &apos;self&apos;;&quot;;
#   add_header Feature-Policy &quot;geolocation none;&quot;;
    
    location / {
        proxy_set_header   Host      $http_host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_pass         http://127.0.0.1:3001;
    }
}
```

We should be able to start nginx now.
```bash
sudo systemctl start nginx
```

# acme.sh
On our Ubuntu instance, we need to get signed certificates, and we can use [acme.sh](https://github.com/acmesh-official/acme.sh) to do that for us. In this case, we&apos;re going to install it as root, because we&apos;ll need the tool to have root permissions to reload the nginx configuration for us when it issues a new certificate. Naturally, we&apos;re going to continue avoiding all reasonable security practices, and download a random shell script from the internet and immediately run it. For real though, you can visit the URL and see what it&apos;s doing.

```bash
sudo su
cd ~
curl https://get.acme.sh | sh
```

We&apos;re going to use acme.sh to issue [domain validated certificates](https://en.wikipedia.org/wiki/Domain-validated_certificate) using CloudFlare&apos;s DNS API. acme.sh has documentation on DNS API&apos;s for various services which you can [find here](https://github.com/acmesh-official/acme.sh/wiki/dnsapi).

Once again, make sure to update `analytics.mysite.com` to your domain name, and also add in your correct values to be exported. You&apos;ll notice we&apos;re exporting the credentials but we&apos;d want these to hang around for issuing certificates in the future. The values will be saved in `~/.acme.sh/account.conf`, and will be reused when needed. Note that in this case, `~/` is root, not your user account.

```bash
export CF_Token=&quot;your-token&quot;
export CF_Account_ID=&quot;your-account-id&quot;
export CF_Zone_ID=&quot;you-zone-id&quot;
acme.sh --issue --dns dns_cf -d analytics.mysite.com
```
Now we&apos;ll &quot;install&quot; them to the correct location, and allow acme.sh to reload the nginx service as it needs to.
```bash
acme.sh --install-cert -d analytics.mysite.com \
--key-file       /etc/nginx/certs/analytics.mysite.com.key  \
--fullchain-file /etc/nginx/certs/analytics.mysite.com.cer \
--reloadcmd     &quot;service nginx force-reload&quot;
```

Make sure to drop out of root back into our standard user.

```bash
exit
```

# Docker and Docker Compose
On our Ubuntu instance, we&apos;ll need to install [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) as that&apos;s how we&apos;re going to deploy umami.

The curl command below for Docker Compose will grab version 1.27.4, so check the releases in the [GitHub repository](https://github.com/docker/compose/releases) to see if there&apos;s a more up to date version.

```bash
# get docker
sudo curl -sSL https://get.docker.com/ | sh
# get docker-compose
sudo curl -L https://github.com/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

# umami
Getting umami installed with Docker is easy, all we need is the repository on our Ubuntu instance (along with Docker and Docker Compose).

```bash
git clone https://github.com/mikecao/umami.git
cd umami/
```

The `docker-compose.yml` file is where we can configure umami. If you don&apos;t update the ports to be `&quot;127.0.0.1:3001:3000&quot;` then nginx won&apos;t be able to correctly proxy the connection. We made this change so we only expose `3001` internally on localhost. We added `&quot;restart: always&quot;` so the containers will come back up if the host dies and resurrects for any reason.  

You should change:
- The password - find and replace `mylengthypassword`.
- The hash salt - find and replace `thisismyrandomhashsalt`.

The password and the hash are for the umami database, so keep them strong, and keep them safely stored in a password manager. It&apos;s also worth noting that these credentials will be available in plaintext on the host so, you know, that&apos;s a thing.

```json
---
version: &apos;3&apos;
services:
  umami:
    restart: always
    image: ghcr.io/mikecao/umami:postgresql-latest
    ports:
      - &quot;127.0.0.1:3001:3000&quot;
    environment:
      DATABASE_URL: postgresql://umami:mylengthypassword@db:5432/umami
      DATABASE_TYPE: postgresql
      HASH_SALT: thisismyrandomhashsalt
    depends_on:
      - db
  db:
    restart: always
    image: postgres:12-alpine
    environment:
      POSTGRES_DB: umami
      POSTGRES_USER: umami
      POSTGRES_PASSWORD: mylengthypassword
    volumes:
      - ./sql/schema.postgresql.sql:/docker-entrypoint-initdb.d/schema.postgresql.sql:ro
      - umami-db-data:/var/lib/postgresql/data
volumes:
  umami-db-data:
```

Now we&apos;ll start umami!

```bash
sudo docker-compose up -d
```

## umami Backups
If you want to take a backup of your umami Postgres database, you can export to `/tmp/` with the below. It will place a file named something similar to `umami_db_dump_26-12-2020_18_00_00.gz`.

```bash
sudo docker exec -t umami_db_1 pg_dumpall -c -U umami | gzip &gt; /tmp/umami_db_dump_`date +%d-%m-%Y&quot;_&quot;%H_%M_%S`.gz
```

Then if you have a fresh install of umami, you can import your backup with the below snippet. You&apos;ll need to have the `.gz` backup file in your current directory, and have the correct filename in the command instead of `umami_db_dump_26-12-2020_18_00_00.gz` which is in the snippet.

```bash
gunzip umami_db_dump_26-12-2020_18_00_00.gz -k &amp;&amp; cat umami_db_dump_26-12-2020_18_00_00 | sudo docker exec -i umami_db_1 psql -U umami
```

# Content Security Policy
On the websites that you&apos;d like to use umami analytics with, if you&apos;ve got a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (CSP), then you will need to add your domain or IP into it. If you don&apos;t, your tracking JavaScript will not be loaded. If you don&apos;t have a CSP, I&apos;d recommend implementing one!

You&apos;ll want something like `Content-Security-Policy: default-src &apos;self&apos;; script-src &apos;self&apos; analytics.mysite.com; connect-src analytics.mysite.com;` along with your other requirements.

# Last Steps
Now you have all of your infrastructure and software ready to go, there&apos;s two more things you need to do. If you need more info about umami, you can check out the [official documentation](https://umami.is/docs/about) for more. 
## Change admin Password
Visit your domain and [log in to umami](https://umami.is/docs/login). The default username is `admin` with a default password of `umami`. Change the password immediately to something strong, and store it in a password manager.

## Add a Website
To get the analytics to umami, first [add a website](https://umami.is/docs/add-a-website). Then, [get the tracking code](https://umami.is/docs/collect-data), and add it to the head of the website you&apos;d like analytics on.

# Resources
- [Installing nginx - Official Documentation](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/)
- [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/#server=nginx&amp;version=1.18.0&amp;config=modern&amp;openssl=1.1.1f&amp;hsts=false&amp;ocsp=false&amp;guideline=5.6)
- [Backing up a dockerized postgresql database](https://stackoverflow.com/questions/24718706/backup-restore-a-dockerized-postgresql-database)
- [umami - Official Documentation](https://umami.is/docs/about)</content:encoded></item><item><title>Migrating vCenter</title><link>https://jacobjangles.com/posts/migrating-vcenter/</link><guid isPermaLink="true">https://jacobjangles.com/posts/migrating-vcenter/</guid><description>How to migrate the vCenter Server Appliance.</description><pubDate>Sat, 20 Jun 2020 00:00:00 GMT</pubDate><content:encoded>As it turns out, migrating the vCenter Server Appliance was not as straight forward as I thought. This was mostly because I didn&apos;t think about how the process would work, and just assumed that the [Veeam Quick Migration feature](https://helpcenter.veeam.com/docs/backup/vsphere/quick_migration.html?ver=100) would save me. Spoilers, it didn&apos;t, everything broke, and now I&apos;m writing this up.

In brief, everything broke because the live migration functionality depends on vCenter working (because Veeam itself points at vCenter), and when vCenter goes down to be migrated, Veeam can&apos;t hook into vCenter. So... _don&apos;t migrate the vCenter Server Appliance with Veeam Quick Migration_.

Before we get into how I performed the migration, I ran into a particular network issue. The vCenter virtual port was unavailable, which is problematic. This is because I&apos;m using a vSphere Distributed Switch (vDS), vCenter was down, and the vDS virtual port simply wasn&apos;t available - it may have resolved itself if I waited as in theory this shouldn&apos;t happen. However, I came across a solution that uses [ephemeral port groups](https://wahlnetwork.com/2015/01/30/vds-ephemeral-binding/) to provide a work around. Thanks to [Chris Wahl](https://wahlnetwork.com/about/) for making a blog post on it.

# The Migration

## Take a Backup

Backup the vCenter Server Appliance, just in case you somehow nuke it. Please. I don&apos;t care how. You could use the [Veeam Backup &amp; Replication Console](https://helpcenter.veeam.com/docs/backup/hyperv/install_console.html?ver=100).

## Export vCenter

1. [Log In to the vCenter Server Appliance Management Interface](https://docs.vmware.com/en/VMware-vSphere/6.7/com.vmware.vsphere.vcsa.doc/GUID-9831B635-DFFA-40FA-9DA9-CEF8A1729E54.html).
2. Shut down vCenter. Right click the VM -&gt; **Power** -&gt; **Shut Down Guest OS**.
3. [Log into the ESXi Host](https://docs.vmware.com/en/VMware-vSphere/6.7/com.vmware.vsphere.html.hostclient.doc/GUID-00DDC20B-3D74-4A15-8D42-065969DD0806.html) that the vCenter VM is on.
4. Under **Virtual Machines**, right click the VM -&gt; **Export**.
5. You&apos;re about to download a number of files, ensure popups are allowed from the host IP or FQDN. Check all the options, and click **Export**.
6. You may need to click **OK** for each file it wants you to download.  
   If you max out your available sockets the browser will appear to hang - a download will need to finish before another can start. Your downloads might take a bit depending on the size and your network.

## Import vCenter

 1. [Log into the ESXi Host](https://docs.vmware.com/en/VMware-vSphere/6.7/com.vmware.vsphere.html.hostclient.doc/GUID-00DDC20B-3D74-4A15-8D42-065969DD0806.html) that you want the vCenter VM on.
 2. Under **Virtual Machines**, click **Create / Register VM**.
 3. Select **Deploy a virtual machine from an OVF or OVA file**.
 4. Click **Next**.
 5. Enter the name of your vCenter VM.
 6. Drag _all_ the files you downloaded into the designated area (or click on the area, and select all of the files).
 7. Click **Next**.
 8. Select your storage.
 9. Click **Next**.
10. Select your deployment options. This is where you can pick your ephemeral-management port, if you decided to do that.
11. Click **Next**.
12. Click **Finish**.
13. Wait for the upload to complete, and boot the VM if you didn&apos;t enable auto start.

### Import Issues

On step 12, you might get an error message saying &quot;A required disk image was missing&quot;. I received this message and it turned out to be the ISO image attached via the emulated CD/ROM. To fix this, you&apos;ll have to edit the Virtual Machine and make sure there&apos;s no ISO images attached to it, then run through the steps again.

While the files are uploading, you should have a task per file to be uploaded. If you don&apos;t have that and it&apos;s a single task, it won&apos;t work. I&apos;m not sure what went wrong here, but you&apos;ll need to start from scratch and run through all of the steps again.

## Cleanup

Once you&apos;ve confirmed the migration was successful and everything is working as intended, you should clean up a little bit by removing the old vCenter VM from the old Host.

1. [Log into the ESXi Host](https://docs.vmware.com/en/VMware-vSphere/6.7/com.vmware.vsphere.html.hostclient.doc/GUID-00DDC20B-3D74-4A15-8D42-065969DD0806.html) where the old vCenter VM resides.
2. Delete vCenter. Under **Virtual Machines**, right click the VM -&gt; **Delete** -&gt; **Delete**.

# Migration Issues

## 503 Service Unavailable

After exporting and importing, vCenter never really came back up and was throwing a 503 Service Unavailable error. I found a post on [fixing the vCenter 503 Error](http://mvsourcecode.com/vmware-vcenter-6-7-503-service-unavailable-failed-to-connect-to-endpoint-n7vmacore4http16localservicespece0x00007fb6cc081b80-_servernamespace-ui-action-allow-_port-5090/) which I stepped through, but it didn&apos;t resolve my issue. So I shut down vCenter, and booted it again. That fixed this problem for me.

# References

* [Veeam Quick Migration](https://helpcenter.veeam.com/docs/backup/vsphere/quick_migration.html?ver=100)
* [Veeam Backup &amp; Replication Console](https://helpcenter.veeam.com/docs/backup/hyperv/install_console.html?ver=100)
* [Chris Wahl - Ephemeral port groups](https://wahlnetwork.com/2015/01/30/vds-ephemeral-binding/)
* [mvsourcecode - Fixing the vCenter 503 Error](http://mvsourcecode.com/vmware-vcenter-6-7-503-service-unavailable-failed-to-connect-to-endpoint-n7vmacore4http16localservicespece0x00007fb6cc081b80-_servernamespace-ui-action-allow-_port-5090/)
* [Log In to the vCenter Server Appliance Management Interface](https://docs.vmware.com/en/VMware-vSphere/6.7/com.vmware.vsphere.vcsa.doc/GUID-9831B635-DFFA-40FA-9DA9-CEF8A1729E54.html)
* [Log into the ESXi Host](https://docs.vmware.com/en/VMware-vSphere/6.7/com.vmware.vsphere.html.hostclient.doc/GUID-00DDC20B-3D74-4A15-8D42-065969DD0806.html)</content:encoded></item><item><title>Updating vSphere (vCenter and ESXi)</title><link>https://jacobjangles.com/posts/updating-vsphere/</link><guid isPermaLink="true">https://jacobjangles.com/posts/updating-vsphere/</guid><description>A guide on updating vSphere.</description><pubDate>Sat, 02 May 2020 00:00:00 GMT</pubDate><content:encoded># Updating vSphere
## vCenter 6.7
Updating things is always fun! It&apos;s a good thing that updating vCenter is pretty easy, and the VMware documentation is useful to boot. You can also check out [Mike Tabor][2], who has a good guide on [updating the vCenter Server Appliance][3] too. I upgraded from 6.7.0.20000 (Build 10244745) to 6.7.0.42000 (Build 15132721). I later upgraded from  6.7.0.42000 (Build 15132721) to 6.7.0.42100 (Build 15505668) using this process... because I needed to make sure my instructions were correct after waiting so long to actually write this post.

Word of caution, you probably don&apos;t want vCenter dying so TAKE A BACKUP. Legit, just install [Veeam Backup &amp; Replication Console][4] for _free_ and backup your vCenter Appliance. Then once you&apos;ve updated everything you will probably want to just use Veeam as your backup solution anyway. It sells itself.

1. [Log in to your vCenter Appliance Interface][1] (Not your VMware vSphere console) - navigate to something like **https://_appliance-ip-address-or-fqdn_:5480** and log in with your root credentials. 
2. Click **Update**.
3. It should automatically update and show your available updates, but you can force it by clicking the **CHECK UPDATES** drop down, then clicking **Check CD ROM + URL**.  After it loads you&apos;ll be presented with a list of available options.
4. Click the radio button for the update you&apos;d like to install.
5. Click **STAGE AND INSTALL**.
6. Check the &quot;I accept the terms of the license agreement&quot; box (after definitely reading and agreeing to them), and click **NEXT**.
7. Wait a moment for the pre-update checks to complete. If they don&apos;t complete successfully, then you&apos;ll need to sort out whatever that issue is.
8. Uncheck the &quot;Join VMware&apos;s Customer Experience Improvement Program (CEIP)&quot; box - or don&apos;t, then click **NEXT**.
9. Check the &quot;I have backed up vCenter Server and its associated databases&quot; box. Heed the _estimated_ downtime notification and _please actually backup your vCenter Server and its associated databases_. Then click **FINISH**.
10. Wait while the installation occurs. Expect the vCenter Appliance to restart, and also to not be able to log in despite being presented with a log in page, just wait a bit more. You can log in to the ESXi host and check the VM to see where it&apos;s at too.
11. Once successfully logged back in to the vCenter Appliance, click **CLOSE** to dismiss the installation succeeded message.
12. Confirm the upgrade on the Summary page by peeping the Version and Build number.

## ESXi
After updating vCenter, we can look at updating the ESXi installation on each host. There&apos;s this great site called [esxi-patches.v-front.de][5] which provides you with everything you need to know about VMware ESXi patches. We&apos;re specifically looking at the [ESXi 6.7 patches][6]. You can just click on the Image profile link and it will pop up a window showing you the commands you&apos;ll need to run. We&apos;ll do everything from the VMware ESXi host.

I upgraded from 6.7.0 Update 1 (Build 10302608) to 6.7.0 Update 3 (Build 15160138). There were also a few issues I had, which I will note after these main steps.

1. Log in to your ESXi host with your root credentials. 
2. Power down your virtual machines and put the host into maintenance mode. Click **Actions**, then from the drop down select **Enter maintenance mode**, click **Yes**.
3. Enable SSH. Click **Actions**, then mouse over **Services**, then click **Enable Secure Shell (SSH)**.
4. Open your favourite terminal and SSH into your ESXi host. ``ssh root@esxi-fqdn`` or ``ssh root@esxi-ip-address``.
5. We&apos;re now just following the [ESXi 6.7 patches][6] Image profile commands, execute ``esxcli network firewall ruleset set -e true -r httpClient``.
6. Execute ``esxcli software profile update -p ESXi-6.7.0-20191204001-standard -d https://hostupdate.vmware.com/software/VUM/PRODUCTION/main/vmw-depot-index.xml``.
7. Execute ``esxcli network firewall ruleset set -e false -r httpClient``.
8. Execute ``reboot now``.
9. Log in to your ESXi host with your root credentials again.
10. Confirm the upgrade on the Host page by peeping the Version and Build number.
11. Disable SSH - if it hasn&apos;t already disabled by itself. Click **Actions**, then mouse over **Services**, then click **Disable Secure Shell (SSH)**.
12. Remove the host from maintenance mode. Click **Actions**, then from the drop down select **Exit maintenance mode**, click **Yes**. Make sure to power up your VMs if they don&apos;t automatically turn on.

### ESXi Upgrade Issues
#### CPU Support Warning
Full Warning: 
``Hardware precheck of profile ESXi-6.7.0-20191204001-standard failed with warnings: &lt;CPU_SUPPORT WARNING: The CPU in this host may not be supported in future ESXi releases. Please plan accordingly.&gt;``

After attempting the profile update I hit a CPU support warning which halted progress. This can be suppressed by applying the``--no-hardware-warning`` flag. For example, instead of ``esxcli software profile update -p ESXi-6.7.0-20191204001-standard -d https://hostupdate.vmware.com/software/VUM/PRODUCTION/main/vmw-depot-index.xml`` you would execute ``esxcli software profile update --no-hardware-warning -p ESXi-6.7.0-20191204001-standard -d https://hostupdate.vmware.com/software/VUM/PRODUCTION/main/vmw-depot-index.xml``.

#### No space left on device error
Full Error:  
``[InstallationError][Errno 28] No space left on device. vibs = VMware_locker_tools-light_11.0.1.14773994-15160134``

This issue sent me to all sorts of places on the internet. Ultimately, [this reddit post on Error 28][7] saved me. Simply install **VMware Locker Tools Light** first.

Keep in mind the below steps were specifically for my version, confirm against [esxi-patches.v-front.de][5] and their tools-light listing for your specific command. After shelling into the ESXi host:

1. Execute ``cd /tmp``.
2. Execute ``wget http://hostupdate.vmware.com/software/VUM/PRODUCTION/main/esx/vmw/vib20/tools-light/VMware_locker_tools-light_11.0.1.14773994-15160134.vib``.
3. Execute ``esxcli software vib install -f -v /tmp/VMware_locker_tools-light_11.0.1.14773994-15160134.vib``.
4. Proceed from the start of the main steps. 

# References
- [Log in to your vCenter Appliance Interface][1]
- [Mike Tabor][2]
- [Mike Tabor - Update vCenter Server Appliance 6.7 to 6.7 Update 1][3]
- [Installing Veeam Backup &amp; Replication Console][4]
- [VMware ESXi Patch Tracker][5]
- [VMware ESXI Patch Tracker - ESXi Version 6.7.0][6]
- [reddit - No space left on device error/Error 28][7]

[1]: https://docs.vmware.com/en/VMware-vSphere/6.7/com.vmware.vsphere.vcsa.doc/GUID-9831B635-DFFA-40FA-9DA9-CEF8A1729E54.html
[2]: https://miketabor.com/
[3]: https://miketabor.com/update-vcenter-server-appliance-6-7-to-6-7-update-1/
[4]: https://helpcenter.veeam.com/docs/backup/hyperv/install_console.html?ver=100
[5]: https://esxi-patches.v-front.de/
[6]: https://esxi-patches.v-front.de/ESXi-6.7.0.html
[7]: https://www.reddit.com/r/vmware/comments/e7hh67/still_having_error_28_when_upgrading_enabled_swap/</content:encoded></item><item><title>Merging Internal USB Connectors</title><link>https://jacobjangles.com/posts/merging-internal-usb-connectors/</link><guid isPermaLink="true">https://jacobjangles.com/posts/merging-internal-usb-connectors/</guid><description>Need more internal USB connectors? No problem.</description><pubDate>Wed, 22 Jan 2020 00:00:00 GMT</pubDate><content:encoded>Recently I bought a new computer and while assembling it, I ran into a problem where I didn&apos;t have enough internal USB Type A 2.0 (The &quot;normal&quot; rectangle one you gotta flip three times) headers on the motherboard. The outside of the case has one Type C (USB C) and one Type A SuperSpeed(+)? (USB 3.x (the blue one)) port which consumed the respective internal headers on the motherboard. However, the motherboard only has one internal USB 2.0 header. It is a [mini-DTX][2] form factor, after all. There are two internal components that require a USB 2.0 header - the AIO CPU cooler, and the NZXT Smart Device V2. I wasn&apos;t happy with only having one or the other plugged in.

One thing I discovered was that a single internal USB 2.0 header can actually cater for **two** USB devices. If you check out the [Internal USB 2.0 Header Pinout][3] you can see there&apos;s ten pin holes, but USB Type A only has four pins. Well, internally we&apos;ve got two pins for power, two sets of pins for data, two pins for power ground, and then a [keyed pin][4] and a ground pin for [shield drain][5]. That is to say, USB Type A needs four pins to function, so the eight we have with an internal USB 2.0 header (excluding the keyed and ground pin for shield drain) means that the internal header can service two USB ports. An amazing revelation.

Conveniently, the AIO CPU cooler, and the NZXT Smart Device V2 consume one USB port each. Inconveniently, as isolated components, they&apos;d need to connect to a single USB 2.0 header but only consume one of the ports for functionality while just blocking the other port.

![USB connector consuming 4 out of 8 pins](@/assets/blog-images/2020-01-22-merging-internal-usb-connectors/merging-internal-usb-connectors_four-of-eight.jpg)

Luckily, taking the pins out and moving them to another connector is not a difficult task. We just need to make sure we maintain power to power, data+ to data+, data- to data-, and ground to ground.

There&apos;s a small wing/flap on the pin that pops out slightly which will hold the pin inside the connector. I used some electronics tweezers to press the pin in, and pulled the cable gently to slide the pin out. Make sure when you put the pin into the new connector that the wing pops out, locking the pin inside the connector.

![USB connector with pin removed](@/assets/blog-images/2020-01-22-merging-internal-usb-connectors/merging-internal-usb-connectors_pin-removed.jpg)

![USB connector almost merged](@/assets/blog-images/2020-01-22-merging-internal-usb-connectors/merging-internal-usb-connectors_halfway.jpg)

![USB connector with pin change complete](@/assets/blog-images/2020-01-22-merging-internal-usb-connectors/merging-internal-usb-connectors_completed.jpg)

Now I have both devices plugged into a single USB 2.0 header. The only cost involved was ten minutes of searching for a solution, then two minutes to swap the pins. A very tidy solution.

![USB connector with pin change installed](@/assets/blog-images/2020-01-22-merging-internal-usb-connectors/merging-internal-usb-connectors_installed.jpg)

As a side note, USB land is bonkers. Finding the precise terminology was difficult and I&apos;m not even convinced I&apos;ve got it all right. I know people think the USB specification isn&apos;t ok for other reasons, but even at a high level just trying to decipher how to name things was hard. For what it&apos;s worth, I haven&apos;t actually read the specification. Buuuuut, there&apos;s a lot of terminology and ideas which don&apos;t even seem to mean anything - like the blue insert is meant to indicate USB 3.x SuperSpeed/SuperSpeed+ (It&apos;s already ridiculous) but my motherboard has blue _and_ red ones. My computer case has a purple one. I&apos;m sure the manufacturers did that to visually indicate something, or because they reckon it&apos;s more aesthetically pleasing, but it doesn&apos;t _mean_ anything and I suppose technically it&apos;s _not_ USB 3.x SuperSpeed/SuperSpeed+ then. I&apos;m sure someone knows more and will yell at me for being wrong, but yeesh it was not straightforward.

Anyway, at the end of it all, does it really matter that much? Absolutely, it does.

# References
- [Wikipedia - USB Receptacle Identification](https://en.wikipedia.org/wiki/USB#Receptacle_(socket)_identification)
- [Wikipedia - DTX Form Factor](https://en.wikipedia.org/wiki/DTX_(form_factor))
- [Wikipedia - Keying](https://en.wikipedia.org/wiki/Electrical_connector#Keying)
- [Wikipedia - Shielded Cable](https://en.wikipedia.org/wiki/Shielded_cable)
- [PinOutGuide - USB 2.0/1.0 Pin Out](https://pinoutguide.com/Motherboard/usb_2_1_header_pinout.shtml)</content:encoded></item><item><title>Hushing the Dell R710 Fans</title><link>https://jacobjangles.com/posts/hushing-the-dell-r710-fans/</link><guid isPermaLink="true">https://jacobjangles.com/posts/hushing-the-dell-r710-fans/</guid><description>The Dell R710 fans are too loud - so here&apos;s how to make them scream less.</description><pubDate>Tue, 18 Jun 2019 00:00:00 GMT</pubDate><content:encoded>In my living room there&apos;s a server rack holding a Dell R710. This server is nice and tidy, has good airflow available, and has an acceptable amount of redundancy built into various aspects. It spends it&apos;s life powered down. In my closet there&apos;s a whitebox jammed in the corner. It literally can&apos;t contain the disks it has so the SSD&apos;s just limply hang out the side, and there&apos;s a material bag resting on the top of the otherwise open case to help the dubious airflow. I can only pray that the RAID doesn&apos;t fail completely. It runs 24/7.

If only the Dell R710 wasn&apos;t as loud, then I could migrate everything and decomission the shambling Tangela hiding in my closet that I pretend to ignore.

# Hushing Techniques
Below are some ways that I seriously considered in order to bring the noise levels down.

## Move the rack somewhere else. 
Considering my current premise, this wasn&apos;t practical. This would be one of the easiest ways, just wheel it to another room. I did consider mounting it vertically in some small dead space, but this never became realistic..

## Replace the stock fans with quieter ones.
There&apos;s a bunch of people that have tried and succeeded in replacing the stock fans. However, this involed making permanent modifications like soldering in the new fans due to the proprietary connector of the default fans. Some alternatives included taking out some of the stock fans, and soldering in resistors to the stock fans. 

## Apply noise deadening material around the rack.
The rack holding the server is an open air rack, so there was potential for adding some kind of sound deadening material around it. Logistically this was a pain and would require a reasonable amount of effort to do.

## Make the stock fans spin slower.
Simply make the fans spin slower. This is what I ended up doing.

There&apos;s also this neat post by [Michael SanAngelo](https://www.thelonegeek.net/) called [R710 Be Quiet!](https://www.thelonegeek.net/blog/2017/08/13/r710-be-quiet/) where they hook up an [Arduino](https://www.arduino.cc/) to control the fan speed, which is really nifty.

# Hushing Implementation

## Overview

You&apos;ll create a virtual machine which will communicate to the [iDRAC](https://en.wikipedia.org/wiki/Dell_DRAC) (which is an implementation of [IPMI](https://en.wikipedia.org/wiki/Intelligent_Platform_Management_Interface)), telling it to set the fans to a certain RPM. Then there&apos;s some fail safes, alerting, and automation applied. For some extra detail, the Dell R710 is running [ESXi](https://www.vmware.com/au/products/esxi-and-esx.html), with a Ubuntu 18.04 LTS guest which communicates via RCMP+ to the iDRAC6, which will control the fan speed.

Someone already went and did all the hard work. There&apos;s a [reddit thread](https://www.reddit.com/r/homelab/comments/779cha/manual_fan_control_on_r610r710_including_script/) which points to the [NoLooseEnds - R710-IPMI-TEMP](https://github.com/NoLooseEnds/Scripts/tree/master/R710-IPMI-TEMP) Github repository. There&apos;s some stuff we need to do first, but the actual hard part is already done.

## iDRAC Considerations

Considering we&apos;ll be talking to the iDRAC over a LAN, and access to an iDRAC is a pretty big deal, there&apos;s a few things we need to do and think about. We&apos;ll first create an account just for this task. Then we&apos;ll make sure the IMPI Over LAN (IOL) is actually enabled and set an encryption key. After that, we should consider segmenting the management interface off from the riff raff.

### iDRAC User

The iDRAC user will need to have the Administrator role for the &quot;IPMI User Privileges&quot; which appears under the column &quot;LAN&quot;. Keep in mind we&apos;re dealing with an iDRAC 6, specifically version 2.91.

Go to **iDRAC Settings** -&gt; **Network/Security** -&gt; **Users** then click on a free &quot;User ID&quot; number. Select the &quot;Configure User&quot; radio button, and click &quot;Next&quot;.

- The &quot;Enable User&quot; check box should be ticked.
- Enter a username (eg &quot;ipmi-admin&quot;).
- Enter a password (eg &quot;D2j9C63nj8DXrUmHP87p&quot; - lanplus won&apos;t let you have one longer than 20 bytes, and special characters might ruin your day).
- Set &quot;Maximum LAN User Privilege Granted&quot; to &quot;Administrator&quot;.
- The &quot;Enable Serial Over LAN&quot; check box should be ticked.
- Everything else should be default, which will effectively be no permissions.

Click &quot;Apply&quot; to create our new IPMI Administrator user.



### IPMI Over LAN

Now we can enable IPMI Over LAN by navigating to **iDRAC Settings** -&gt; **Network/Security** -&gt; **IPMI Settings** then ticking the &quot;Enable IPMI Over LAN&quot; check box. Ensure the &quot;Channel Privilege Level Limit&quot; is set to &quot;Administrator&quot;. 

It&apos;s also a good idea to configure the &quot;Encryption Key&quot;, so we can add a little bit more goodness. The Encryption Key is a hex value that needs to be supplied to the iDRAC along with a valid username and password in order for the communication to work. So... not really an encryption key and more like password number two but anyway, add in a 40 character hex value, something like &quot;3E140EC4DAE3339BEC3BCA83EDBDB022FDAB7AFB&quot;. As a quick aside, I couldn&apos;t find too much information on the &quot;Encryption Key&quot; and how it works, so it just seems to be an extra piece of information provided to validate the communication, but I might be wrong.

### Network Segmentation

The fun part is that some implementations of IPMI are just broken because the _(brace for impact)_ **R**MCP+ **A**uthenticated **K**ey-Exchange **P**rotocol (RAKP) authentication process will just tell you the [HMAC](https://en.wikipedia.org/wiki/HMAC) prior to actually authenticating you. Then you can go and crack the hash offline, and come back conveniently knowing all the required details.

So, segment the management interface somewhere most people can&apos;t get to, and only allow specific trusted machines (and people) access to it. Or just keep IOL disabled.

## Guest Virtual Machine Configuration

### Install Utilities

Assuming you already have a Ubuntu 18.04 LTS virtual machine, we&apos;ll want to install ipmitool so we can actually communicate with the iDRAC.

```bash
sudo apt update
sudo apt install ipmitool
```
You&apos;ll get an error saying that the [ipmievd.service](https://linux.die.net/man/8/ipmievd) failed with the error: ```Could not open device at /dev/ipmi0 or /dev/ipmi/0 or /dev/ipmidev/0: No such file or directory```

This error is benign for our use case, the ipmievd daemon doesn&apos;t need to run. I spent way too long trying to fix this, when we just need the utility installed. As it turns out, the daemon would listen for events _from_ the iDRAC, however we&apos;re sending requests _to_ the iDRAC and then receiving the response to our request. So we don&apos;t care about this error.

## Test Connections

Everything we need to configure is good to go, so let&apos;s test our connection. From our virtual machine, we can attempt to connect to the iDRAC and request temperature information. In my infinite wisdom, I tried to connect from the virtual machine to the virtual machine. Naturally it didn&apos;t work, I don&apos;t recommend it.

Some quick explanation to our command below: 
**-I** is the interface, in our case it&apos;s &quot;lanplus&quot;. **-H** is the host or address, so the iDRAC IP address 192.168.0.120. **-U** is our username, so &quot;ipmi-admin&quot;. **-P** is our password, so &quot;D2j9C63nj8DXrUmHP87p&quot;. **-y** is our hexadecimal encryption key, so &quot;3E140EC4DAE3339BEC3BCA83EDBDB022FDAB7AFB&quot;. **sdr** stands for Sensor Data Repository, so the **sdr type temperature** says to search the Sensor Data Repository for the type of temperature. If you forgo the type, then you&apos;ll just see all records from the SDR.

```bash
ipmitool -I lanplus -H 192.168.0.120 -U ipmi-admin -P D2j9C63nj8DXrUmHP87p -y 3E140EC4DAE3339BEC3BCA83EDBDB022FDAB7AFB sdr type temperature
```

Hopefully, you&apos;ll get a bunch of temperature related information. If not, you&apos;ll probably get something saying &quot;Error: Unable to establish IPMI v2 / RMCP+ session&quot;. If you get the error, double check you&apos;re communicating to the correct IP, and your credentials are correct.
If you&apos;re still getting an error, make sure the iDRAC account has the correct permissions, and that IOL is enabled.

# Hushing Execution

At this point, you&apos;re basically set as we have the means to slow the fan speed. Now we&apos;ll use the [NoLooseEnds - R710-IPMI-TEMP](https://github.com/NoLooseEnds/Scripts/tree/master/R710-IPMI-TEMP) scripts to do the hard yards for us. You have the Static script, which you run once and it will set the fan speed to manual control, then drop the fan speed to the configured RPM. Then there&apos;s the Temp script which you set to run periodically, which will set the fan speed to automatic control once the server breaches a certain temperature threshold.

Clone the two .sh scripts onto your machine from the repository - you can also get a zip which you&apos;ll need to extract from here: ```https://github.com/NoLooseEnds/Scripts/archive/master.zip```

Then edit both the IPMITemp and IMPIStatic scripts and plug your **IPMIHOST**, **IPMIUSER**, **IPMIPW**, and **IPMIEK** details into them. If you&apos;d like, you can also take the time to set up [slacktee](https://github.com/coursehero/slacktee) which is included in the IPMI scripts. Make sure the scripts are executable with ```chmod +x ./R710-IPMIStatic.sh &amp;&amp; chmod +x ./R710-IPMITemp.sh```. Nothing left to do now except execute the Static script ```./R710-IPMIStatic.sh``` and listen to the hush happen.

The quiet is deafening. 

You&apos;ll want to set up your cron jobs as noted, so that if things start getting toasty then automatic control can kick back in.
```bash
*/5 * * * * /bin/bash /path/to/script/R710-IPMITemp.sh &gt; /dev/null 2&gt;&amp;1
```

# References
- [NoLooseEnds - Reddit Thread](https://www.reddit.com/r/homelab/comments/779cha/manual_fan_control_on_r610r710_including_script/)
- [NoLooseEnds - R710-IPMI-TEMP](https://github.com/NoLooseEnds/Scripts/tree/master/R710-IPMI-TEMP)
- [Thomas Krenn - IPMI Basics](https://www.thomas-krenn.com/en/wiki/IPMI_Basics)
- [ipmitool man page](https://linux.die.net/man/1/ipmitool)
- [iDRAC User Roles and Privileges](https://www.dell.com/support/manuals/au/en/audhs1/poweredge-r640/idrac_3.21.21.21_ug/idrac-user-roles-and-privileges?guid=guid-d2ed6a79-c1b0-4342-93d1-22c731f6e087&amp;lang=en-us)
- [RMCP+ Packet Decryption](https://github.com/beingj/hash/blob/master/RMCP%2B%20Packet%20decrypt%20and%20authcode.org)
- [Penetration Tester&apos;s Guide to IPMI and BMCs](https://blog.rapid7.com/2013/07/02/a-penetration-testers-guide-to-ipmi/)</content:encoded></item><item><title>Free DDNS using ddclient and Cloudflare</title><link>https://jacobjangles.com/posts/free-ddns-using-ddclient-and-cloudflare/</link><guid isPermaLink="true">https://jacobjangles.com/posts/free-ddns-using-ddclient-and-cloudflare/</guid><description>A guide on how to get free DDNS using ddclient and Cloudflare.</description><pubDate>Thu, 09 May 2019 00:00:00 GMT</pubDate><content:encoded>I was on the hunt for a suitable [Dynamic DNS](https://en.wikipedia.org/wiki/Dynamic_DNS) solution. There&apos;s a whole bunch of paid and free services that can do this. Although... I&apos;m not too keen on paying or having a free account to some random service. That&apos;d mean making yet another account and then probably updating it every month to keep it active. Instead I can bleed time into setting up and maintaining my own stuff!

I use [Cloudflare](https://www.cloudflare.com/) to manage my DNS (and get a whole bunch of fun perks as a result) which means I can leverage their [API](https://api.cloudflare.com/) to _automagically_ update my DNS for me. Kinda.

Enter [ddclient](https://sourceforge.net/projects/ddclient/). I stumbled upon [this super helpful post](https://jenssegers.com/84/dynamic-dns-for-cloudflare-with-ddclient) written by [Jens Segers](https://jenssegers.com/). As it turns out, there&apos;s this purpose built utility to update Cloudflare DNS entries with dynamic IP addresses, and this purpose built blog post to tell me how to mash everything together and it will work seamlessly.

_Just kidding, could you imagine?_

At the time of writing:

- Cloudflare uses v4 of their API.
- ddclient updated from Cloudflare&apos;s API v1 to v4 from version [3.8.3](https://sourceforge.net/p/ddclient/git/ci/6c951a039543021295d9d809d7127ef06ccb1513/).
- The Ubuntu repository will install ddclient version 3.8.3.
- The latest ddclient version is [3.9.0](https://sourceforge.net/projects/ddclient/files/ddclient/ddclient-3.9.0/).

Here&apos;s how I got DDNS working on Ubuntu 18.04.1 LTS with Cloudflare and ddclient running as a daemon.

# Install ddclient
Make sure our repository is up to date.
```bash
sudo apt update
```

Download and install ddclient, and all the other pieces we need.
```bash
sudo apt install ddclient libdata-validate-ip-perl
```

While ddclient is getting installed, you&apos;ll get asked a whole host of questions. We mostly don&apos;t care as we&apos;re going to change everything anyway. The point is, installing it this way puts most of the stuff where it needs to be.

- **Dynamic DNS service provider:** select &quot;other&quot;.
- **Dynamic DNS server:** leave blank.
- **Dynamic DNS update protocol:** select &quot;dyndns2&quot; (it just happens to be the first entry).
- **Username for dynamic DNS service:** leave blank.
- **Password for dynamic DNS service:** leave blank.
- **Re-enter password to verify:** leave blank.
- **Network interface used for dynamic DNS service:** leave blank.
- **DynDNS fully qualified domain names:**  leave blank.

Curiously, there&apos;s some specific options I didn&apos;t get asked until I reconfigured the package. It&apos;ll ask you the same questions as above so just provide the same answers, but the additional questions are what we need to pay attention to.

```bash
sudo dpkg-reconfigure ddclient
```

- **Run ddclient on PPP connect?:** select &quot;No&quot; (the default is &quot;Yes&quot;).
- **Run ddclient as a daemon:** select &quot;Yes&quot; (the default is &quot;No&quot;).
- **Interval between ddclient runs:** leave as &quot;300&quot; (if you want a 5 minute interval).

Just so you know why we&apos;re installing **libdata-validate-ip-perl** as well as **ddclient**, it prevents you from doing something like:

```bash
sudo ddclient -help
```

 Then witnessing everything immediately detonate inside your shell:

```
Can&apos;t locate Data/Validate/IP.pm in @INC (you may need to install the Data::Validate::IP module) (@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.26.1 /usr/local/share/perl/5.26.1 /usr/lib/x86_64-linux-gnu/perl5/5.26 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.26 /usr/share/perl/5.26 /usr/local/lib/site_perl /usr/lib/x86_64-linux-gnu/perl-base) at /usr/sbin/ddclient line 27.
BEGIN failed--compilation aborted at /usr/sbin/ddclient line 27.
```

# Update ddclient

Since the Ubuntu repository will install ddclient version 3.8.3 ([released 2015-05-30](https://sourceforge.net/p/ddclient/news/2015/05/ddclient-383-released/)), and the latest version is 3.9.0 ([released 2018-08-09](https://sourceforge.net/p/ddclient/news/2018/08/ddclient-390-released/)), we should probably update it. Strictly speaking, 3.8.3 will work with Cloudflare. However I&apos;ve done this based on 3.9.0, and it&apos;s nice to have the latest software.

_The below steps mostly come from the purpose built blog post linked earlier: [Dynamic DNS for CloudFlare with ddclient](https://jenssegers.com/84/dynamic-dns-for-cloudflare-with-ddclient)._

Since the latest version isn&apos;t in our standard repositories, we&apos;ll need to download and extract it.

```bash
wget https://sourceforge.net/projects/ddclient/files/ddclient/ddclient-3.9.0/ddclient-3.9.0.tar.gz
tar -xvf ddclient-3.9.0.tar.gz
```

Then we overwrite the existing Perl script with the new one.

```bash
sudo cp -f ddclient-3.9.0/ddclient /usr/sbin/ddclient
```

The **ddclient.config** file location has changed in 3.9.0, so we need to resolve that issue.

```bash
sudo mkdir /etc/ddclient
sudo mv /etc/ddclient.conf /etc/ddclient
```

It&apos;s always a good idea to tidy up and remove things we don&apos;t need or that are toxic to us in life.

```bash
rm ddclient-3.9.0.tar.gz
rm -R ddclient-3.9.0
```

# Configure ddclient

Since we blazed through our setup phase, our configuration file is mostly empty and completely useless. We can take a peek at the wasteland ```cat /etc/ddclient/ddclient.conf```:

```
# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf

protocol=dyndns2

server=
login=
password=&apos;&apos;
```

Now we need to actually get ddclient to talk to Cloudflare. We have to edit **ddclient.conf** and put in some useful information. This is where we will deviate as your details are going to be different to mine. If you want more information around what the variables are, there&apos;s a good example file in the [3.9.0 download](https://sourceforge.net/projects/ddclient/files/ddclient/ddclient-3.9.0/) called **sample-etc_ddclient.conf**. Additionally, the [ddclient usage wiki](https://sourceforge.net/p/ddclient/wiki/usage/) has some good information around the ddclient options. Between the example configuration file and the ddclient usage wiki, you can find out what all the options actually are in the below ddclient configuration.

You&apos;ll want to update the values on each line that have **## Update Me** at the end. The commented out parts naturally don&apos;t actually change anything, but it&apos;s just to keep structure. Design your configuration file such that it brings you joy.

In the below configuration example, we&apos;ve signed up to Cloudflare using the email address &quot;red@act.ed&quot;. We&apos;ve then [copied our Cloudflare Global API Key](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-API-key-), which happens to be &quot;abcredactedxyz&quot;. Somehow, we own the domain &quot;domain.tld&quot; and we are updating the &quot;sub.domain.tld&quot; A Record with our dynamic IP address.

My redacted working ddclient.conf looks like ```cat /etc/ddclient/ddclient.conf```:

```
# Configuration file for ddclient

##
## Global Config
##
daemon=300
ssl=yes

##
## sub.domain.tld - Cloudflare ## Update Me
##
protocol=cloudflare
use=web
login=red@act.ed ## Update Me
password=abcredactedxyz ## Update Me
zone=domain.tld ## Update Me
sub.domain.tld ## Update Me
```

# Testing

## #1 - Public IP

Our first test is to see if ddclient is actually able to get our public IP address. First google what your IP is, then run:

```bash
sudo ddclient -query
```

You&apos;ll see your local IP address, your loopback address, and then your public IP  address (in this case it&apos;s shown as &quot;1.1.1.1&quot;) from a few different services. You might have some warnings from certain sites not resolving, that&apos;s fine.

```
use=if, if=ens160 address is 192.168.1.1
use=if, if=lo address is 127.0.0.1
WARNING:  cannot connect to ipdetect.dnspark.com:80 socket: IO::Socket::INET: Bad hostname &apos;ipdetect.dnspark.com&apos;
WARNING:  found neither ipv4 nor ipv6 address
use=web, web=dnspark address is NOT FOUND
use=web, web=dyndns address is 1.1.1.1
use=web, web=loopia address is 1.1.1.1
```

## #2 - Connection to Cloudflare

Our second test is to make sure we can successfully take our public IP and update our Cloudflare A record with it. A quick test which will use our configuration file and attempt to update our IP is basically just running ddclient now and asking for some verbosity. Fair warning, this will literally update your DNS records if it works, so, you know.

```bash
sudo ddclient -daemon=0 -verbose -noquiet
```

We should get something nice and easy to read like:

```
CONNECT:  checkip.dyndns.org
CONNECTED:  using HTTP
SENDING:  GET / HTTP/1.0
SENDING:   Host: checkip.dyndns.org
SENDING:   User-Agent: ddclient/3.9.0
SENDING:   Connection: close
SENDING:
SENDING:
RECEIVE:  HTTP/1.1 200 OK
RECEIVE:  Content-Type: text/html
RECEIVE:  Server: DynDNS-CheckIP/1.0.1
RECEIVE:  Connection: close
RECEIVE:  Cache-Control: no-cache
RECEIVE:  Pragma: no-cache
RECEIVE:  Content-Length: 107
RECEIVE:
RECEIVE:  &lt;html&gt;&lt;head&gt;&lt;title&gt;Current IP Check&lt;/title&gt;&lt;/head&gt;&lt;body&gt;Current IP Address: 1.1.1.1&lt;/body&gt;&lt;/html&gt;
SUCCESS:  sub.domain.tld: skipped: IP address was already set to 1.1.1.1.
```

The last line there tells us everything we need to know: **SUCCESS:  sub.domain.tld: skipped: IP address was already set to 1.1.1.1.** You might have another message like a failure, or a success in actually updating the IP. As long as it makes the good words.

If you do have problems, you can get a lot more information by running:

```bash
sudo ddclient -daemon=0 -debug -verbose -noquiet
```

## #3 - Running Daemon

Our third test is to make sure that the ddclient daemon is running and actually checking for a changed IP every five minutes. If we run ```htop``` then we should see one process running, and it&apos;ll probably be sleeping. The maximum amount of time it should sleep is 300 seconds, as that&apos;ll be 5 minutes (or whatever threshold you set). You might also see it updating. It also doesn&apos;t hurt to restart the guest to make sure the daemon resumes after a reboot.

```
PID		USER	...		CPU	MEM	TIME	Command
1622	root	...		0.0	1.9	0:10.69	ddclient - sleeping for 80 seconds
```

# Bugs and Errors

## PID City

I noticed a lot of PIDs running while inspecting in ```htop``` which was super odd. I&apos;m not sure what I did that caused this behavior, but this is a known bug that has already been resolved in the [Debian Bug #652298](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=652298), as well as the related and merged [Debian Bug #761505](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=761505). I killed all the process with ```killall -r ddclient```, however I couldn&apos;t intentionally reproduce the issue.

```
PID		USER	...		CPU	MEM	TIME	Command
15563	root	...		0.0	4.5	0:00.31	ddclient - sleeping for 300 seconds
15575	root	...		0.0	4.5	0:00.30	ddclient - sleeping for 150 seconds
15607	root	...		0.0	4.5	0:00.31	ddclient - sleeping for 230 seconds
15671	root	...		0.0	3.0	0:00.14	ddclient - sleeping for 280 seconds
15742	root	...		0.0	4.5	0:00.29	ddclient - sleeping for 160 seconds
15745	root	...		0.0	5.1	0:00.43	ddclient - sleeping for 190 seconds
15750	root	...		0.0	3.0	0:00.11	ddclient - sleeping for 230 seconds
16345	root	...		0.0	2.9	0:00.03	ddclient - sleeping for 200 seconds
```

## Literally Nothing

Swank, now you can run ddclient but... it just runs and then doesn&apos;t actually do anything. That&apos;s likely because it&apos;s actually hitting a 301 and not following the redirect. It&apos;s also likely that is happening because you&apos;re using **server=www.cloudflare.com**. That server location is the problem as per this link [Cloudflare Community - CF Returning 301](https://community.cloudflare.com/t/update-ddns-ipaddress-by-using-ddclient-but-301-received-always/29570). You can specify **server=api.cloudflare.com/client/v4** instead, however you can just remove the **server=** variable.

## Other Crimes

Good luck, here&apos;s some other crimes you can solve: [mcblum Github - Can&apos;t locate Data/Validate/IP.pm Error](https://github.com/mcblum/ddclient-cloudflare-ubuntu).

Also a quick hint, if it&apos;s not running as a daemon, the file **/etc/default/ddclient** will probably need to be adjusted and you might need to make a [Unit File](https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files). Check out [running ddclient as a service](https://askubuntu.com/questions/940283/run-ddclient-as-a-service-in-16-04).</content:encoded></item><item><title>Securing WordPress</title><link>https://jacobjangles.com/posts/securing-wordpress/</link><guid isPermaLink="true">https://jacobjangles.com/posts/securing-wordpress/</guid><description>WordPress seems to be infamous for getting hacked all the time, but you can fix that.</description><pubDate>Mon, 01 Apr 2019 00:00:00 GMT</pubDate><content:encoded>WordPress seems to be infamous for getting hacked all the time. The solution itself tends to get dismissed immediately in the eyes of an IT professional - it&apos;s not an &apos;enterprise&apos; solution, or even a &apos;professional&apos; one. Yet it&apos;s still exceptionally popular and it&apos;s so easy to get going, even if you don&apos;t understand the technical intricacies. It just works... until someone breaks in and ruins it. WordPress isn&apos;t really _that_ bad, there&apos;s just a few things people do (or don&apos;t do) which unfortunately allows it to become pretty insecure. Hopefully you can implement some of the ideas I&apos;ll present to help make your WordPress site more secure. This isn&apos;t a step by step guide, but it will give you the concepts and information to help you out in your journey to make your WordPress site more secure.

As an aside, if you _are_ an IT professional, you might be surprised at how many shadow IT instances of WordPress are currently operating under your business umbrella. It&apos;s worth taking a look.

It&apos;s also worth noting that there&apos;s no magical vest to make WordPress secure. This applies to every solution, not just WordPress. However, we do have ideas like applying secure options where we can, and automating as much security as we can. This will help bolster the security posture of WordPress, and that&apos;s all we&apos;re trying to achieve. Even if you follow everything here, there&apos;s no guarantee that your WordPress site will become hack proof, and anything saying otherwise is snake oil. In no particular order:

# Backups
Taking backups won&apos;t make you more secure, but it&apos;s the best thing you can do for when something inevitably goes wrong. Your hosting provider might offer a backup service which you should strongly consider using. It&apos;ll stop you having to install extra plugins (which is great!), if someone does get in they can&apos;t delete your backups, and you can effectively just bundle your services together which will make it easier for you to manage. If you do go with a hosting provider backup, make sure that you can recover your site totally should somebody have complete control over your WordPress site - if you&apos;re not sure then just ask your provider.

Outside of your hosting provider, there are plugins which will bundle up your site and send it somewhere (like Dropbox, for example). These are fine if you&apos;re not able to use backups from your hosting provider as it&apos;s better to have backups than no backups. Make sure that it doesn&apos;t just save the site backups to the WordPress hosting, as if it does then they can just be deleted by the attacker. Additionally, if they&apos;re going to Dropbox (or whatever cloud service), then try to make sure that the plugin doesn&apos;t have access to delete the backups it creates. It&apos;s also worth noting that the plugin should only be able to write to a specific directory, you shouldn&apos;t be able to browse your cloud service from the plugin.

If you can&apos;t do either of those, then you can look at doing a manual copy of the site via your hosting SFTP. This is more inconvenient but at the very least have _one_ backup, or just accept that you stand to lose everything you&apos;ve created.

# HTTPS
The popular Green Padlock in your browser (By the way, it&apos;s becoming [not green](https://www.socialreport.com/insights/article/360016143092-Tech-PSA-Say-Goodbye-to-The-Green-Secure-Lock-on-HTTPS-Sites-in-Chrome) in your browsers in an effort to be secure by default). You&apos;ll find a reasonable amount of information arguing _against_ HTTPS. There are some scenarios where you might not want HTTPS, but that&apos;s typically only valid when an enterprise is trying to gain visibility into traffic on their network. They&apos;ll probably have defence in depth in action, and implement this in a secure way. If you&apos;ve just got a WordPress site on the internet, _you want HTTPS_. Security aside, it will help [make your site faster!](https://www.troyhunt.com/i-wanna-go-fast-https-massive-speed-advantage/) Seriously, check [this out.](http://www.httpvshttps.com/)

HTTPS is pretty easy to achieve now. More often than not, your hosting provider can get you a certificate (the thing that gives you HTTPS) for free! They&apos;ll have easy to follow instructions where you&apos;ll say &quot;yes I want HTTPS&quot;, and then you set your site to say https:// instead of http://. It&apos;s pretty much that simple now. Of course, you can still pay for certificates, but there&apos;s no security difference (if they say there is, you&apos;re getting sold snake oil). There _are_ different types of certificates, some where they &apos;vet&apos; you more thoroughly, but from a technical perspective, they&apos;re the same difference - you don&apos;t need to pay for a certificate.

If your hosting provider can&apos;t get you a certificate, then you can look at [Cloudflare](https://www.cloudflare.com/), which also has it&apos;s own section later on for some other stuff. There&apos;s an easy guide by [Troy Hunt](https://httpsiseasy.com/) to help you through that. Cloudflare basically gives you &apos;flexible&apos; HTTPS, as in from your customer to Cloudflare is HTTPS, but from Cloudflare to your WordPress isn&apos;t. It&apos;s better than nothing.

# Passwords and MFA
A strong password in conjunction with [Multi Factor Authentication](https://www.acsc.gov.au/publications/protect/multi_factor_authentication.htm) (the link has a lot of information, the first few paragraphs are all you really need) will seriously help you out, a lot. MFA also goes by the name 2FA. MFA is basically something you know (like a password), something you have (like a mobile phone), and something you are (like your iris). You need two factors or more. Most likely this means you will have a password, and a mobile phone.

## Alternate Factors
There&apos;s a bunch of things that you have that you can use as a second factor. The most convenient is to use your mobile number as that factor, otherwise known as SMS MFA. You&apos;ll find people screaming that it&apos;s not secure, and it&apos;s true to a degree. It is the easiest other factor to steal (it&apos;s surprisingly easy to hijack a mobile number, now you&apos;ve stolen the tokens). However, if SMS is your only available other factor, it&apos;s better to use that than to not have MFA at all.

I&apos;d recommend using an application like the Google Authenticator, [Authy](https://authy.com/), or something similar. There&apos;s a whole mass of them out there. If you really want to go it, you can look at buying a hardware token, like a [Yubikey](https://www.yubico.com/). It&apos;s a physical item you&apos;ll pair to your service, and have to plug in when you need your second factor.

The elephant in the room is that WordPress doesn&apos;t natively support MFA. You&apos;ll need to install a plugin to get this to work, and then the plugins limit what factors you can use, but it&apos;s well worth it. I&apos;d suggest going with [WordFence](https://www.wordfence.com/), which has it&apos;s own section later on. Unfortunately, you need to pay for WordFence Premium to get MFA. WordFence have a page about [WordFence and MFA](https://www.wordfence.com/help/tools/two-factor-authentication/).

## Passwords
If you know what your password is, there&apos;s a big chance it&apos;s [not a &apos;good&apos; password](https://www.troyhunt.com/only-secure-password-is-one-you-cant/). Instead of debating what a &apos;good&apos; password is, go sign up for and use a password manager. You&apos;ll be good with either [1Password](https://1password.com/) or [LastPass](https://www.lastpass.com/). Now set a minimum password length of 64 characters and generate! You will definitely find people arguing that storing all your passwords in one place is even more risky! While it&apos;s true that it&apos;d be really bad if someone got into your password manager, it&apos;s even more risky to use the same (or a similar) password you remember for all your services. As it turns out, humans all think they&apos;re being smart by making their password look fancy with numbers and special characters. The problem is that the people trying to break in are, you know, also humans, and they&apos;ve got [stupid big and plentiful](https://haveibeenpwned.com/) data sets to work with. They _know_ what you&apos;ll do, they _know_ what words, characters, numbers, and special characters you&apos;ll probably use. Besides, they&apos;ll usually just break in doing something called [Credential Stuffing](https://www.owasp.org/index.php/Credential_stuffing) anyway. Password Managers easily defeat this.

To quickly address your master password issue for your password manager, for starters you&apos;re using MFA like we discussed before. And then, you&apos;re not using a _password_, you&apos;re using a _passphrase_. Have you ever heard anyone say [correct horse battery staple](https://xkcd.com/936/)?

It&apos;s also worth just changing your administrator account username to a few random characters. People can try and break in, and they&apos;ll basically always try &apos;admin&apos;, &apos;root&apos;, and then a password. Someone that cares more might even try and guess your name (your site probably has your name on it) as the username. It&apos;ll never work if your username is &apos;nqpyxb&apos;.

# Avoid Installing Themes and Plugins
This is the hardest one because everyone wants their site to look unique, and all that power the plugins give you is just so handy. It&apos;s WordPress, it&apos;s built around the concept of using plugins to expand functionality, and themes to customise the look. That in and of itself isn&apos;t a problem, but you can&apos;t guarantee that the extra code you&apos;re plugging in is safe, or will be maintained safely. Compromised themes and plugins are a big reason WordPress gets hacked. The advice here is simple, but can be very difficult to follow. Don&apos;t install plugins. Don&apos;t install themes. It&apos;ll also help prevent your site from being slowed down. It&apos;s not practical to leave it at never installing anything, so install as few as you can possibly get away with.

There&apos;s also some really sketchy things that they can do, like [slowing your site down](https://twitter.com/nickstadb/status/1112479746972151808) if you use a competitors hosting platform. It doesn&apos;t matter so much whether or not the accused in the link are actually doing that ([here is their response](https://www.pipdig.co/blog/sad-times/)), the concept is definitely there and I wouldn&apos;t put it past a company to do something along these lines.

## Themes
If you can live with it, use the default WordPress theme. It&apos;s supported by the maintainers of the WordPress Core - it&apos;s about as good as you&apos;re going to get from a security perspective. If you really need a custom theme, it&apos;s almost impossible to reliably figure out which ones are good and which ones aren&apos;t. Some things that might help are to try and find a theme that shows a history of recent updates, this is a big one. If it looks active in development then it&apos;s being maintained and isn&apos;t abandoned. You can try and find a popular theme, although popularity doesn&apos;t mean it&apos;s secure. The less &apos;features&apos; you get is usually better (as there&apos;s less code performing functionality that you might not actually want). You could argue that paying for a theme is better as they&apos;re more likely to maintain the theme, although that&apos;s really not definitive. Unfortunately the best advice on themes is to use the default.

## Plugins
Not installing plugins isn&apos;t really practical, heck, WordPress even comes with some plugins installed by default. But you&apos;re trying to install as little extra stuff as possible, and generally removing anything that isn&apos;t used. Like the themes, there&apos;s basically no way reliable to figure out which ones are good and which ones aren&apos;t. There&apos;s some stuff that you should just generally avoid, for example you probably don&apos;t really need a plugin to browse your site directory as you can hopefully do that via your hosting provider. If you really need it, then install it and uninstall it when you&apos;re done. There&apos;s not much to elaborate on, don&apos;t install it, consider if you _really_ need it, if you have to, remove it when you&apos;re done, otherwise it&apos;s just something that needs to be installed.

# Install Updates
This is a super easy thing to do which is really really good! As soon as an update for WordPress Core, any plugins, or any themes become available, update! As vulnerabilities are discovered, there are exploits that get developed. Eventually they get turned into effectively point and shoot exploits which are easy for anyone to purchase (or download for free) and start breaking into outdated software. You side step all this by keeping everything up to date. Of course, updates have a chance to go wrong - although you&apos;re better off running the risk of a bad update than not updating. There&apos;s a good reason why Windows 10 forces updates down your throat. Always take a backup before you update, _just in case_. 

Just reiterating, update all your software. In fact, set WordPress to automatically update. Now the comparison to Windows 10 updates isn&apos;t so bad because you don&apos;t actually have to do anything - and that includes waiting.

# WordFence
If you can spare the gold, purchase and install [WordFence Premium](https://www.wordfence.com/). It&apos;ll help keep your site protected by proactively blocking the bad from touching your site. It does a bunch of other stuff too. Again, installing this doesn&apos;t make you hack proof, but it&apos;s a solid option to help keep things secure. It&apos;s got a [Web Application Firewall](https://www.wordfence.com/help/firewall/) which will seriously help you out - not to mention that you get to plug in to their network of &apos;badness against WordPress&apos;, so you gain the visibility that all other WordPress sites have. Besides, you already got WordFence for [MFA](https://www.wordfence.com/help/tools/two-factor-authentication/), right?.

# Cloudflare
[Cloudflare](https://www.cloudflare.com/) is a bit unique here, for free you can leverage some of their services (like the free certificates for HTTPS mentioned before, and free [DDoS](https://www.cloudflare.com/en-au/learning/ddos/what-is-a-ddos-attack/) protection), but their paid option unlocks their [WAF](https://www.cloudflare.com/en-au/waf/). This effectively does the same thing as WordFence. If you can, use Cloudflare _and_ WordFence - WordFence Premium and free Cloudflare is likely the most realistic option. In order to use Cloudflare  you&apos;ll need to move your [DNS](https://www.cloudflare.com/learning/dns/what-is-dns/) over to them.</content:encoded></item><item><title>Employee Morale</title><link>https://jacobjangles.com/posts/employee-morale/</link><guid isPermaLink="true">https://jacobjangles.com/posts/employee-morale/</guid><description>The impact to a business of low employee morale can be monumental.</description><pubDate>Thu, 26 Jul 2018 00:00:00 GMT</pubDate><content:encoded>The impact to a business of low employee morale can be monumental. The way in which these impacts manifest are plentiful with varying degrees of severity. Not only is it bad from a business perspective, but it&apos;s also not fun to be an employee in a workplace with low morale or worse yet, be an employee with low morale. I&apos;d wager most places don&apos;t strive for low morale and so through a series of unfortunate events, misconstrued comments and actions, missed opportunities, and the passage of time; morale fades away. Employees start talking amongst themselves sharing negative stories, mocking those perceived as problems, and justifying why everything is bad. They feed each other negativity and start self sabotaging. What&apos;s the point of trying to do anything anyway - nobody cares. Low morale feeds into employee burn out, and burn out feeds into low morale. It&apos;s a dangerous positive feed back loop that impacts business and staff alike.

Thinking rationally becomes difficult as emotions overwhelmingly seize control and you start to assign unproportionate blame to little things. Regular activities start dragging heavily, fun activities cease to exist, and getting out of bed to go to work becomes challenging.

# Boons, Conditions, and Various Things
An employer and employee duo enjoy boons applied by high morale like a lower number of leave days taken, a safer workplace, an increase of job satisfaction, an increase in the perceived quality of the workplace, as well as improved productivity, performance, and creativity.

However, the duo suffers conditions inflicted by low morale such as an increase in absenteeism, low motivation and interest, loss of money via productivity loss and decreased efficiency, and even outright refusals to perform certain tasks.

Low employee morale is caused by a whole stack of various things. Notably, management plays a very large role in this - other things like out of office affairs and work conditions factor into this equation too.

A short list of things which can negatively impact morale are:
- Poor Leadership
- Distrust of management
- Poor interpersonal relations between staff and management
- Inflexible working conditions
- Unclear expectations
- Lack of opportunity for personal growth

# Regaining
Identifying low morale is the first step to regaining it. It can be difficult to come to the conclusion that morale is low, even though it&apos;s so blatant that things aren&apos;t what they were. Low morale is sneaky like that.

As an employee, there&apos;s not a lot of things you can do to resolve low morale yourself. The problems tend to be rooted in individuals behaviours and perceptions, yet it takes the entire team to start shifting perspectives and attitudes to regain morale. Identifying and discussing root causes reveals where the patches need to be applied. Applying them can be tricky and depends on a lot of factors - a quick conversation might resolve one problem. A coffee with someone might start to smooth over another problem. Should a particular manager be a large source of grief, it might be more difficult to approach appropriately. If the company culture itself is toxic, then resolving that is a daunting task which simply can&apos;t be completed by one person and requires the company at large to shift. A great starting point for an employee is focus on themselves and make sure they&apos;re not feeding despair to themselves and others.

As a manager, effective morale boosting behaviours are noted as talking less and listening more, providing clear expectations, increasing informal interactions, assigning tasks based on skill instead of politics, advocating for and providing more rights to staff, providing opportunities for staff to make decisions for tasks, and to respect people with greater expertise even though they fall &apos;under&apos; you within the management tree. Gauging morale can be hard, and if you&apos;re perceived as a problem then it&apos;s likely staff will actively hide that from you. Culture or &apos;climate&apos; surveys are an option that can be run every nine to eighteen months to monitor morale so it can be tracked over time.

# You
As an individual, remove yourself from the situation if you can. Take some leave, get out and away. Turn off. Allow the emotional disconnect to flush your buffers and discharge your capacitors. Come back to measured thought and reasonable reactions. This will help you to identify causes and solutions to low morale within your unique situation. Additionally, it will help you mitigate and manage your own personal burn out. The disconnect will grant you the ability to genuinely reflect.

Once you&apos;re able to genuinely reflect, do so. Sit and look out the window, stand looking out over the water, or otherwise remove yourself from distraction and just exist in a place for a bit. Think about you for a few minutes. Breathe. Reflect on what places stress on you, why? Of those things, how can you shift them such they don&apos;t place stress on you. Reflect on why you reacted the way you did. Reflect on how you could better handle the situation. Breathe. Generally reflect. What have you done recently that you liked, why? What was a fun interaction you had with someone, why? Complete the circle and reflect on the fact that you&apos;re reflecting, why?

If it helps, spend some time to simply exist. Don&apos;t actively think about anything, just be. Having a thought is ok, just grant it passage and let it move by. Sit and be comfortable within your skin. Just exist within this world that you share with so many others. Sometimes doing and thinking nothing can help, and that&apos;s ok.

# References
https://lb.webservices.illinois.edu/files/2012/06/01/39974.pdf</content:encoded></item><item><title>Installing and Migrating TeamSpeak3</title><link>https://jacobjangles.com/posts/installing-and-migrating-teamspeak3/</link><guid isPermaLink="true">https://jacobjangles.com/posts/installing-and-migrating-teamspeak3/</guid><description>Rebuilding the community TeamSpeak3 server.</description><pubDate>Mon, 07 May 2018 00:00:00 GMT</pubDate><content:encoded>The time finally arrived to annihilate the old [Crystal Docks](https://crystaldocks.com/) TeamSpeak3 server, and rebuild it to make it harder better faster stronger. Below is all the bits and pieces related to rebuilding TeamSpeak3 on the new server.

_This was created on a Ubuntu 18.04 LTS server, so if you&apos;re using a different distribution then you might run into problems executing the commands below._

# Backup
It&apos;s always a good idea to backup your stuff, especially when you&apos;re going to be detonating servers. It&apos;s also a smart move to test your backups, which you should totally do because I definitely did. When you back up TeamSpeak3, you should stop the service for the backup. I use the below [rsync](https://rsync.samba.org/) command. Adjust as you need.

```bash
rsync -azvAXH -e ssh teamspeak:/home/teamspeak/ /home/user/backup/
```

## Migration Files
If you need to migrate your TeamSpeak3 instance over to a new instance, there&apos;s a few files which you&apos;ll want to move over from your backup. Their [knowledge base suggests](https://support.teamspeakusa.com/index.php?/Knowledgebase/Article/View/315/0/i-want-to-move-my-server-to-another-machine-which-files-should-i-copy) migrating the below files.

* licensekey.dat
* query_ip_whitelist.txt
* query_ip_blacklist.txt
* files/*
* ts3server.sqlitedb
* \*.ini
* tsdns/tsdns_settings.ini

# Installation and Configuration
We&apos;ll create a specific and unprivileged user account to run TeamSpeak3. You can call it what you like, but `teamspeak` seems pretty reasonable. Make sure to note the password, even though we won&apos;t need to log in often. Worst case you can just reset it with your privileged account.

```bash
 sudo adduser teamspeak
```

Now we can switch over to the `teamspeak` user, create a `teamspeak3-server-current` directory (which will act as the operating TS3 directory), download, then extract TeamSpeak3. If you just copy and paste the below commands, you&apos;ll get version 3.1.3 - It&apos;s a good idea to get the latest stable version. Go to the TeamSpeak3 [downloads page](https://www.teamspeak.com/en/downloads#server) and check what the latest Linux Server 64-bit release is. You&apos;ll need to copy the link as you&apos;ll be downloading it with wget.

```bash
su teamspeak
cd ~
wget http://dl.4players.de/ts/releases/3.1.3/teamspeak3-server_linux_amd64-3.1.3.tar.bz2
mkdir teamspeak3-server-current
tar xf teamspeak3-server_linux_amd64-3.1.3.tar.bz2 -C ./teamspeak3-server-current --strip-components 1
rm teamspeak3-server_linux_amd64-3.1.3.tar.bz2
```
Now you&apos;ll want to move your backed up files over, if you have any. Make sure the owner and group of the migrated files are set to `teamspeak`, which would be `sudo chown teamspeak:teamspeak /path/to/file`. Remember to change files within directories too.

## TS3 License
You&apos;ll probably need to accept the TeamSpeak3 license - if you&apos;ve tried to run TeamSpeak3 without accepting the license, it won&apos;t start up.

If you&apos;ve migrated an existing server, you bypass accepting the license during the usual setup. We can notify TeamSpeak that we accept the license by creating a dot file and putting a variable in there.

```bash
vi /home/teamspeak/teamspeak3-server-current/.ts3server_license_accepted
```
With `license_accepted=1` in it.

Now we&apos;ll want to type and execute `exit` to exit out of our `teamspeak` user, and land back in our sudo enabled account.

# Firewall
We need to open some ports on our firewall to allow connections through. I&apos;ll be using [Uncomplicated Firewall](https://wiki.ubuntu.com/UncomplicatedFirewall), and we&apos;ll need root permissions. If you&apos;re not using UFW, then you&apos;ll need to find your commands to allow port/protocol `9987/udp`, `30033/tcp`, `10011/tcp`.

_If you use TSDNS, you will also need to allow 41144/tcp._

```bash
sudo ufw allow 9987/udp
sudo ufw allow 30033/tcp
sudo ufw allow 10011/tcp
```

# TeamSpeak3 Service - Start on Boot
There&apos;s two popular initialisation systems - the older systemV, and the newer systemd. These will allow you to create and manage the TeamSpeak3 service more easily, and also allow TeamSpeak3 to automatically start if your server reboots.

## systemd
Systemd calls it&apos;s configuration files &quot;Unit Files&quot;, and we&apos;ll be creating our Unit File in the `/etc/systemd/system/` directory.
```bash
sudo vi /etc/systemd/system/teamspeak.service
```
Thanks to [Schroeffu](https://schroeffu.ch/), who has a nice example and breakdown on a [systemd Unit File](https://schroeffu.ch/2017/05/einfache-systemd-start-stopscripts-am-beispiel-teamspeak-3/) for TeamSpeak3 which I&apos;ve pilfered and modified ever so slightly.
```bash
[Unit]
Description=TeamSpeak3 Server Daemon
After=network.service

[Service]
User=teamspeak
Group=teamspeak
Type=forking
WorkingDirectory=/home/teamspeak/teamspeak3-server-current/
ExecStart=/home/teamspeak/teamspeak3-server-current/ts3server_startscript.sh start
ExecStop=/home/teamspeak/teamspeak3-server-current/ts3server_startscript.sh stop
PIDFile=/home/teamspeak/teamspeak3-server-current/ts3server.pid
RestartSec=15
Restart=always

[Install]
WantedBy=multi-user.target
```
Make sure our permissions are set correctly on our new file.
```bash
sudo chmod 644 /etc/systemd/system/teamspeak.service
```
Then we&apos;ll tell systemd that our shiny new Unit File is a thing and it should totally check it out because it&apos;s pretty sweet, and then we&apos;ll start the teamspeak service.
```bash
 sudo systemctl daemon-reload
 sudo systemctl enable teamspeak.service
 sudo systemctl start teamspeak.service
 ```
Now we can control the teamspeak service with the below commands.
```bash
sudo systemctl start teamspeak.service
sudo systemctl stop teamspeak.service
sudo systemctl reload teamspeak.service
sudo systemctl status teamspeak.service
```

## systemV
SystemV calls it&apos;s configuration files &quot;Init Files&quot;, and we&apos;ll be creating our Init File in the `/etc/init.d/` directory.
```bash
sudo vi /etc/init.d/teamspeak
```
Our Init File will have the below contents.
```bash
#!/bin/sh
### BEGIN INIT INFO
# Provides: teamspeak
# Required-Start: $local_fs $network
# Required-Stop: $local_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: Start/Stop/Restart/Status for the TeamSpeak3 Server Daemon
### END INIT INFO

USER=&quot;teamspeak&quot;
DIR=&quot;/home/teamspeak/teamspeak3-server-current&quot;
###### Teamspeak 3 server start/stop script ######
case &quot;$1&quot; in
start)
su $USER -c $DIR&quot;/ts3server_startscript.sh start&quot;
;;
stop)
su $USER -c $DIR&quot;/ts3server_startscript.sh stop&quot;
;;
restart)
su $USER -c $DIR&quot;/ts3server_startscript.sh restart&quot;
;;
status)
su $USER -c $DIR&quot;/ts3server_startscript.sh status&quot;
;;
*)
echo &quot;Usage: &quot; &gt;&amp;2
exit 1
;;
esac
exit 0
```
Make sure our permissions are set correctly on our new file.
```bash
sudo chmod 755 /etc/init.d/teamspeak
```
Now we&apos;ll need to let the system know we&apos;ve got a new service.
```bash
 sudo update-rc.d teamspeak defaults
 ```
 Now we can control the teamspeak service with the below commands.
 ```bash
 sudo service teamspeak start
 sudo service teamspeak stop
 sudo service teamspeak restart
 sudo service teamspeak status
 ```</content:encoded></item><item><title>Technical Debt</title><link>https://jacobjangles.com/posts/technical-debt/</link><guid isPermaLink="true">https://jacobjangles.com/posts/technical-debt/</guid><description>The result of poor and uninformed decisions, typically within a project.</description><pubDate>Sun, 04 Mar 2018 00:00:00 GMT</pubDate><content:encoded>**Technical Debt**  
/ˈtɛknɪk(ə)l dɛt/  
_noun_

The result of poor and uninformed decisions, typically within a project.

&gt; but we need to go live two days ago!

_synonyms: Design Debt, Code Debt_  
_antonyms: Happy, Pleased, Content_

---

# Normal Debt
Like any kind of debt, you assume it for a particular reason. Maybe you can&apos;t afford to buy that car outright, but you&apos;ll be able to afford it over a period of time; so you take on debt, buy the car, and you pay it back over a period of a few months.

Like any kind of debt, it can come in various forms. Maybe you left some work until the last minute, so you&apos;ll have to stay up all night and pump out the document; so you pay it back in the form of suffering and misery for two days.

Like any kind of debt, it can become a boon or a condition. Maybe you took on debt and you bought your car; you successfully paid it off over a few months so now you have a car you otherwise wouldn&apos;t have been able to get, and you no longer have debt. Maybe you were up all night and produced a &apos;kwality&apos; document which your manager doesn&apos;t like so you have to do it again. Now your document is late anyway, and you have to do it again. Also you&apos;re really tired because you stayed up all night. Now you&apos;re sick because you threw your body clock out of whack. Now the document is even later because you&apos;re taking time off. Now you fail a KPI of delivering that particular document, so you lose your bonus. Owies.

There&apos;s perks to be gained from taking on debt, but if you&apos;re not prepared then you might inadvertently be risking a lot.

All debt functions in similar ways, except Technical Debt.

# Technical Debt
Technical debt is just straight garbage from the start. There&apos;s no potential boon, you just end up with problems later on. The project limps along delicately before collapsing, people who had no say in the matter get blamed, and nobody is happy anywhere. Nobody seems to know how to take on technical debt and then pay it back responsibly. This happens because whoever calls the shots doesn&apos;t seem to know what technical debt is from the get go, but they&apos;re happy to take it on.

With enhanced accuracy and mitigated emotion; there is a lack of knowledge and understanding around the impacts of taking short cuts when things are being built. This lack of knowledge applies to the whole human resource stack - from owners, C-Levels, to managers, and technical staff. That&apos;s not to say nobody is doing it right, but there&apos;s a distinct lack of understanding around how to take on, and repay, technical debt.

## Manifestation
My view is predominately from infrastructure, however technical debt is typically applied to software - the project design is sound, but problems occur during implementation. Be it technical, or even if the timeline gets pulled closer due to &apos;business reasons&apos;, this is a problem. Decisions get made to make adjustments and certain things simply become unachievable and dropped. This happens, we know it will happen. We can cater for it by taking on technical debt.

This is where badness starts to manifest because the adjustments are not catered for correctly. The adjustment is made and everything keeps pushing on, just keep building. You start to hit other things that need to be adjusted due to the previous adjustment. This continues. Once all the tasks are completed, you have a thing. It&apos;s not what it was designed to be and sure, it works (kinda), but it&apos;s not what was agreed upon. Any documentation hasn&apos;t been updated, it&apos;s kind of correct but not really. The thing is stable and runs, we just need to restart this part every few weeks for some reason. Then everyone moves on, adjusting the thing only when it stumbles every now and then.

## Abomination
Over time, people have been poking and prodding to make sure the thing keeps running. Sometimes more features are delicately placed on top. The documentation gets inaccurate at in incredible rate. Only a select few people know how to keep the thing going. However, it&apos;s no longer a thing. It has been festering and gurgling away; it has become an abomination.

Sometimes the abomination explodes, and everyone gets covered in a toxic goo. There&apos;s precise documentation on how to dissolve the goo, but the documentation wasn&apos;t accurate so now you just have a different kind of goo burning you. Everyone is flailing and screaming because there&apos;s this goo hurting you, but your instructions to fix it just made it a different kind of hurty goo. After a little bit of this, someone figures out how to patch the hole in the abomination, and properly get rid of the remaining goo. Everything is terrible. This is when everybody realises the solution is garbage, and something needs to be done about it. Whether or not anything happens is another tale.

This is the payment technical debt seizes from you when you have not been keeping up on your repayments.

# Responsible Technical Debt
To be able to successfully leverage technical debt, you need to know it&apos;s being taken on. You also need to know how you&apos;ve taken it on, and how you&apos;ll need to pay it back. By discussing, documenting, and actioning the technical debt, we can mitigate the risk associated with the debt, and hopefully reap the benefits of it. The debt needs to be discussed so it&apos;s understood how it will affect other aspects of the project, the consequences of the adjustments, and how the debt will be repaid. Once it has been discussed, it needs to be documented and tracked.

Upholding the repayments is often one of the more difficult parts as &quot;the thing is running fine, why change it&quot;. However, the changes have already been slated and resources estimated when we documented our debt, so it&apos;s just tasks to make sure everything continues to run properly. This way, you never end up with an abomination. In fact, you didn&apos;t even end up with a &apos;thing&apos;, it&apos;s actually the system that you set out to build in the first place.

In order to achieve this, everyone needs to know and understand what technical debt is, and how it can be used. A lot of the people involved in the project don&apos;t need to know the intricacies of technical debt, just a basic awareness of what it is and how it works at a high level - like what has just been outlined. Other people within the project need to understand it more thoroughly so they can work with it, make informed decisions on how to proceed, and how to manage and repay the debt that is taken on. When the project team have knowledge and understanding around technical debt, systems can be built properly. The end result is a system that is harder, better, faster, stronger.</content:encoded></item><item><title>Cancon 2018</title><link>https://jacobjangles.com/posts/cancon-2018/</link><guid isPermaLink="true">https://jacobjangles.com/posts/cancon-2018/</guid><description>Cancon 2018 - The Chronicles.</description><pubDate>Sun, 11 Feb 2018 00:00:00 GMT</pubDate><content:encoded>&gt; Cancon is coming - good time to start planning. Who&apos;s in, who&apos;s not, and where are we staying?

This year it turns out the Canberra Carotel Motel destroyed our beloved living quarters for some reason. This was super disappointing as where we usually went was absolutely perfect for [Cancon](http://www.cgs.asn.au/cancon/). It was the right distance from the venue, it was isolated enough so we could be loud into the early morning without disrupting others, it had a massive fridge to hold everything, and it had two giant tables which could comfortably fit two or three large games. In every way, it was perfect.

Instead, we ended up staying at the Pinnacle. It definitely was the pinnacle of garbage. For starters, the fridge. Not only was it not cold enough, but every time you opened the door, all of a sudden a truck is backing up - it would just beep at you. You don&apos;t realise how frequently people use a fridge until it screams every time it&apos;s opened. The floor upstairs was _unreasonably_ creaky, I&apos;m talking other apartments would probably think their ceiling is collapsing. The beds were way too short, basically everyone had their feet hanging off the edge. The walls were apparently made out of paper so we also got a noise complaint the first night we were there (sorry whoever that was, but to be fair, it wasn&apos;t even midnight yet). There were several other things that were just... subpar. But we could still visit the Con, and we could still play games.

# Day One
Once we arrived at our destination we went to check in. Within the tiny reception area there was a mother who was ruthlessly informing her child that they were to no longer receive pocket money, as they were being too loud. Game over for that kid, but we checked in fine. After witnessing the casualty that was that kid, we entered our apartment to claim our rooms and discuss the _other_ casualty which appears to have taken place in our apartment before we got there. How the mysterious blood splatter ended up all over the ceiling and walls, we don&apos;t know. We&apos;re pretty sure people have been killed in that place before. However, if we were going to die there, we were going to die playing games.

First up was Word. Slam. [Word Slam](https://boardgamegeek.com/boardgame/203411/word-slam) is the tale of two story tellers aggressively sifting through cards to get their team to guess a word. The story teller can&apos;t talk and must place down cards as clues for their team. It turns out being blunt works out really well, one story teller put down the cards &quot;Job&quot;, &quot;Do&quot;, &quot;Movie&quot; with the team correctly guessing &quot;Actor&quot; within minutes of the round starting.

Up next we drafted for land and resources in a game of [Bunny Kingdom](https://boardgamegeek.com/boardgame/184921/bunny-kingdom). The key to victory was hoarding Treasure and Parchment cards, with a little bit of sabotage mixed in. Joining your opponents fiefs can be a great way to strip them of points. Who knew making your enemy more powerful would be their demise.

Last up was the game that led us into the night and caused us the noise complaint, which makes sense considering it was a bar brawl at [The Dragon and Flagon](https://boardgamegeek.com/boardgame/193840/dragon-flagon)! Bellowing atop tables, swinging from chandeliers, throwing chairs, and generally assaulting anyone who comes near you are all fair game - just don&apos;t stand on the rug. Seriously, I was doing well up until someone pulled the rug out from underneath me. Then we got told to be quiet so we finished our brawl in relative silence and then went to bed.

# Day Two
As is tradition, we woke up and had to regain some AP by visiting our beloved Pancake Parlour. Pancakes, bacon, eggs, and caffeine - the breakfast of Cancon. Naturally, we hit up the con and browsed for many hours. I picked up a nice leather dice rolling tray from the [Level Up Dice](http://levelupdice.net/) stall which doubles as a holder for my wallet and keys. We were rifling through the dollar bin and I wanted to ditch some of my shrapnel, so I bought [The Hunger Games: JabberJay Card Game](https://boardgamegeek.com/boardgame/118340/hunger-games-jabberjay-card-game) for $1. I bet it&apos;s garbage. I also picked up eight Boyz for my Warhammer 40K Ork horde.

Back at the murder house, we did some arts and crafts by assembling and painting some Warhammer 40k minis. Once some more of us came back from the con, we started talking about what games we were going to play. First up for the day was [Evolution: Climate](https://boardgamegeek.com/boardgame/182134/evolution-climate), where the carnivores had a hard time finding some delicious prey. The climate didn&apos;t get out of hand, although we flirted with the possibility of a wildfire which would have been unforgiving on a few players. At the end of the day, friendship between species prevailed as sharing food won the game for me.

Those friendships were short lived as we entered a few games of [Secret Hitler](https://boardgamegeek.com/boardgame/188834/secret-hitler). Apparently I&apos;m inherently evil as out of the three games we played, I was Hitler twice in a row. I pushed through our third and final game as a liberal with blind faith that the two people next to me were also liberals. After a few elections, I recruited another member into the dream team. Being accused of being a fascist because I was pushing the game along was a fair call, but it all worked out in the end when everyone I believed in was a liberal, and we won the game in record time.

Being Hitler was hard, so we settled down to defend our corporate overlords and destroy the natives in a game of [Xenoshyft Onslaught](https://boardgamegeek.com/boardgame/159109/xenoshyft-onslaught). We just need their natural resources, it&apos;s nothing personal. The start was very touch and go as our base sustained a lot of damage early on, but as we pushed on and got organised, we started to easily destroy the onslaught. The bosses appeared and were swiftly dealt with, and their minions lay where they appeared. A successful operation called for a good nights sleep.


# Day Three
A nice if not too lengthy sleep in was had, and so there was no time for breakfast. Onwards, to the con! I picked up [Photosynthesis](https://boardgamegeek.com/boardgame/218603/photosynthesis) and [Mountains of Madness](https://boardgamegeek.com/boardgame/214293/mountains-madness), and got [Warmachine: High Command](https://boardgamegeek.com/boardgame/138104/warmachine-high-command) for free.

Back at our quarters, we did some more Warhammer 40k mini building before we started with the games again. The popular drink of the night was a variant of an Old Fashioned. We had no measuring utensils so roughly 60ml of Hochstadter&apos;s Slow &amp; Low Rock and Rye whiskey, 15-30ml of maple syrup, 2-3 dashes of Angostura bitters, and an orange rind... or slice... getting a rind is hard with a blunt knife. Good drink.

Starting us off, we were stranded and waiting for a rescue ship when we noticed we were [Not Alone](https://boardgamegeek.com/boardgame/194879/not-alone). In our struggles to hold out, the rescue ship almost made it to us before the Creature was able to assimilate us to the planet. We will forever live our lives on this strange planet.

Turns out we got assimilated on Mars, so we got to [Terraforming Mars](https://boardgamegeek.com/boardgame/167791/terraforming-mars). Mars transformed into a beautiful place, lush with forests and rivers, and bustling cities. In the battle for corporate supremacy, there was no clear leader, however one player did have an intimidating engine, pumping out all sorts of resources. At the end the Milestones and Awards were the key to being the most successful corporation. I was in last place once we scored everything - forgetting the milestones and awards. Then we noticed and scored them too. That&apos;s the tale of how I won.

Then the worst case scenario happened. We played [The Worst Case Scenario Survival Game](https://boardgamegeek.com/boardgame/5050/worst-case-scenario-survival-game). The only reason it&apos;s good is because the questions and answers are so terrible, it was funny. Someone picked it up from the second hand store because it was so cheap. How would you determine if you were a high risk candidate for being kidnapped? Well, be American and work for a fortune 500 company, apparently. Let&apos;s not forget this one... **How to stun an attacker without permanent injury.** Do you,

* Swiftly hit the back of the attacker&apos;s neck with the side of your hand.
* Poke the attacker&apos;s eyes as hard as possible while saying, &quot;Nyah, nyah, nyah, nyah&quot;.
* Sharply jab the area just below the attacker&apos;s ear lobe, above the bend of the jaw.

I&apos;m not even making that up, that&apos;s literally the card. Although my personal favourite is how to keep your computer secure. The best way is to install two layers of encryption software on your computer: one state-of-the-art and one that is outdated.

# Day Four
Our final day demanded breakfast at Pancake Parlour again. One of the waiters had this accidental attitude where everything was unamusing and not funny to her - she was the unamused waiter lady. Yeah, ok, sure, of course you can have a coffee. Yeah, alright, no problem, of course I can get you that. Don&apos;t even worry about it.

At the con, I bought [SUPERHOT the Card Game](https://boardgamegeek.com/boardgame/206156/superhot-card-game) to raise the cost of a friends purchase to get [Hordes: High Command](https://boardgamegeek.com/boardgame/138105/hordes-high-command) for free, which was generously given to me. We loitered around the con for a while and watched the bidding take place, along with a disgruntled war gamer complaining about the heat to some volunteer workers. Then it was time to hit the road home.

---

Cancon 2018 was excellent, as anticipated. Despite losing the perfect place for us to stay, we could still attend the con and play games. We&apos;ve noticed it&apos;s becoming less about the con and more about the games and company, so we&apos;re in discussions of having our own Pancon - over a long weekend we go somewhere for a few days and play games. Lots of games. Preferably getting pancakes for breakfast each day.

The games to checkout from this year are definitely [Bunny Kingdom](https://boardgamegeek.com/boardgame/184921/bunny-kingdom) and [Secret Hitler](https://boardgamegeek.com/boardgame/188834/secret-hitler). Cancon is so great, we&apos;re definitely going next year and hopefully starting something up to slip into the middle.</content:encoded></item><item><title>Post Outage Review</title><link>https://jacobjangles.com/posts/post-outage-review/</link><guid isPermaLink="true">https://jacobjangles.com/posts/post-outage-review/</guid><description>No matter how well you plan for something, you will inevitably have something go wrong that simply couldn&apos;t be catered for.</description><pubDate>Fri, 27 Oct 2017 00:00:00 GMT</pubDate><content:encoded>No matter how well you plan for something, you will inevitably have something go wrong that simply couldn&apos;t be catered for. One day you will find yourself dealing with an unplanned outage. Drawing from your bag of holding, you resolve the problem but now you have to provide answers to The Business - and it&apos;s not happy. Not many people enjoy dealing with The Business, so it&apos;s best to promptly provide answers and prevent the situation from occurring ever again. After all, The Business is a fickle beast with multiple heads that appears to do it&apos;s own nonsensical thing most of the time.

The beast tends to be out for blood, which lamb caused this outage? The many eyes dart around looking for the most weak and guilt ridden lamb to ravage and eviscerate from this world. You confront this twisted beast which contorts and shrieks before you, armed with three items.

* The Glinting Sword of Fact.
* The Defiant Shield of Time.
* The Righteous Scroll of Prevention.

These items are forged through the fires of The Outage and the ritual of the Post Outage Review. They exist to provide answers, and prevent or mitigate further unplanned outages. There are many techniques and methods in which to perform the ritual, but our overarching goal is to identify flaws within processes. Once the flaws have been identified, steps can be taken to mitigate or prevent them from occurring again.

Incidentally, I just got back from my journey to acquire coffee in which one of the staff tripped the circuit breaker, cutting the power. From what I could observe, water hit a power outlet while someone was cleaning. The initial response from the barista was on par with &quot;Which one of you idiots did something&quot;. It&apos;s telling that the knee jerk reaction was who did it, before shifting to what happened in order to resolve the current outage.

# Sword and Shield
During the outage you might make several calls, update the project lead on the problem status, make changes to systems, and wait for propagation. As you do these things, the action you took and a time stamp should be recorded. This is the data-set that the post incident review will be based on. Once the outage is resolved, the data can then be compiled (or just enter it such that it&apos;s already compiled), forming a definitive list of everything that was executed, and when.

The primary person dealing with the outage should then flesh out the incident, so that others reading can have a grasp on what was going on. This involves taking the actions and forming proper sentences with context instead of just &quot;10:06am - ran script&quot;.

Once the data flourishes into information, it can be passed over to whoever is appropriate to interrogate the information. This could be the IT team, it might be the CIO and lead engineers, it mostly depends on the company.

_With The Glinting Sword of Fact and The Defiant Shield of Time, you can now defend yourself against the fiery breath of the beast, and strike back when an opening presents itself. But these items alone cannot slay the beast._

# The Scroll
The review can now begin as all of the information required lays before you. This is the real meat that will actually provide actionable items to make your processes more robust. Ultimately you end up with a list of flaws in your processes, and ideas can be formulated to counter them.

One technique to expose flaws is the [Five Whys](https://en.wikipedia.org/wiki/5_Whys). Iteratively asking why will lead you to discover the problems within your process.

Once a list of each process flaw has been discovered, you can then begin brainstorming ways to either prevent or mitigate the flaw. A process might require to be completely reworked, or slightly adjusted. It helps to have some kind of management involved as changes should actually be implemented, not just listed.

_The Righteous Scroll of Prevention should be invoked when the beast begins to run out of steam, it will be the final blow required to best the beast. Although, there&apos;s always the pacifist option - for this we&apos;ll still need The Righteous Scroll of Prevention, but another item is required._

# The Secret Potion of Explanation
Now that everything has been identified and explored, you can present this in a business friendly way. A good example of what you&apos;re trying to achieve would be when Amazon had their [S3 Outage](https://aws.amazon.com/message/41926/). This includes what happened, where the problems were, and the actions they plan on taking to prevent this from happening again. It&apos;s got a good blend of &quot;here&apos;s what happened&quot;, &quot;we&apos;re sorry&quot;, and &quot;here&apos;s how we&apos;re going to do better&quot;.

_Instead of invoking The Righteous Scroll of Prevention, you can take the essence of The Righteous Scroll of Prevention and condense it into a vial, sprinkle some &apos;business words&apos; in there, add a caring twist, and shake gently. This produces The Secret Potion of Explanation. Now, provide this to the beast - note that the beast should simmer in rage before cooling off completely._
_**Your results may vary, if symptoms persist, run away.**_</content:encoded></item><item><title>Colonising The Death Planet</title><link>https://jacobjangles.com/posts/colonising-the-death-planet/</link><guid isPermaLink="true">https://jacobjangles.com/posts/colonising-the-death-planet/</guid><description>The trials and tribulations of the The Death Planet crew.</description><pubDate>Sat, 27 May 2017 00:00:00 GMT</pubDate><content:encoded>Red, Hideki, and Spike quickly realised there wasn&apos;t much hope. Thriving within the subterranean planet was almost certainly futile. Water, warmth, food, and oxygen were practically non-existent. The initial survey determined they were inside _The Death Planet_. How and why they were there was anyone&apos;s guess. The first Duplicant would be printed in a few cycles. Strike the earth!

The crew set out to clear some space and gather some resources to prepare for the first night, which was rapidly approaching. The earth was mined and materials were gathered, enough to hastily assemble three cots and an outhouse. As the night settled in, so too did the Duplicants. When morning broke, it was time to build a power source and a microbe musher. Red set to producing some mush bars to keep the crew going. Hideki and Spike surveyed the land and started to assemble the start of what would become home.

A few cycles in and things are looking good. There&apos;s enough mush bars to go around, the outhouse is being kept clean, and the algae deoxydizer is busy converting algae into life giving oxygen. Some caverns the crew had broken into had contaminated some of the oxygen, but they were able to keep the contamination under control with the help of some air deoderizers. One of these caverns was home to something monumentally useful. A body of clean water. It sat patiently, glimmering gently before them. Proper amenities were close.

Suddenly, the Duplicator burst into life, and Bubbles stepped forth. She took a moment to get her bearings and greeted the crew as she passed by to begin construction on the sewerage system - Duplicants don&apos;t care much for pleasantries. Disposing of waste is not a glorious task, but a robust and resilient sewerage system would allow for the expansion of the colony. It was no menial task either with extensive planning, massive excavations, unending days of labour, and the ever looming threat of death.

Despite the treacherous conditions, it was still an unfathomable shock as Bubbles discovered the body of Spike, lifeless, and still clutching his tools. He suffocated a stone&apos;s throw away from the air lock. The oxygen wasn&apos;t rich down in the facilities line, but it was enough for short stints of work. Spike must have stayed back to finish off his work. A burial site was made, but the work must continue otherwise the entire colony might meet the same fate as Spike. The Death Planet had claimed it&apos;s first victim.

----

The colony had grown to around ten Duplicants, and construction on the sewerage system had recently completed. The bathroom facilities were up and running, but something wasn&apos;t right. Unbeknown to the colony, the pipes were laid incorrectly and only one toilet was functioning. When the crew realised, it was too late to begin the huge effort to resolve the situation. The tireless workers had to go where they stood and continued to perform their duties. Conditions started to deteriorate rapidly - and then the crew ran out of algae. One by one, the algae deoxydizer&apos;s stopped working. In the span of one cycle, conditions became unsustainable - and they were closing in on death.

The surveyors had determined there was a deposit of algae residing within the earth not far west of the colony. It could keep the algae deoxydizer&apos;s going for a bit longer. They could dig further out and hope to find oxygenated caverns, food, water, anything. With few options left, the colony began mining what became known as The West Tunnel. It was a race against time, and the harder they worked, the faster the clock ticked. The oxygen in the tunnel was sparse and toxic in places. The length of the tunnel meant that only small portions of work could be completed in one journey. To be put to work within The West Tunnel was a harsh and gruelling assignment.

As work on The West Tunnel dragged on, Duplicants started to suffocate in the tunnel. Duplicants starved at home. The algae deposit was reached, but most of the crew lay motionless where they fell. Bubbles and Eugene were the only Duplicants left standing. There was enough rations to keep them going, and the algae would be plenty for just the two of them. The West Tunnel sat deathly still as it leeched toxic air into the carcass of the crew&apos;s home. The West Tunnel was stripped of its algae supply and no further attempts were made to dig further. Toxic air, strange creatures, and corpses were all that were left within the tunnel, and it was sealed off - never to see the artificial light of technology again.

Bubbles and Eugene migrated essentials to the level above their home into a pocket of breathable oxygen. All three original founders had now passed. Bubbles was the first Duplicant to step into their world, and she saw it her mission to rebuild and truly colonise The Death Planet. As they worked to stay alive, the machines built below slowly died as the power drained and the dust settled. The only machine that stood tall and lit the cavern was the Duplicator.

----

Progress was being made, the crew had bolstered to five Duplicants. The West Tunnel was never breached again, but the old home saw expeditions to retrieve water and salvage dead machinery. The algae supply was almost empty, but an electrolyzer was constructed to replenish the oxygen. A new piping system was built and ran straight through the old home to bring water up to the electrolyzer. The crew was able to maintain a steady supply of oxygen and food with new facilities.

Now the threat was exhausting the water supply. Construction began on expanding the contaminated water supply storage area, and constructing an additional storage zone for clean water. A water purifier was slated to be built, allowing a continuous source of clean water.

While constructing the new storage zone, Bubbles fell off the ledge into an unreachable location filled with toxic air. The clock started ticking as the rescue crew scrambled to build a ladder to bring Bubbles back from limbo. The crew wasn&apos;t going to make it in time, though Bubbles wasn&apos;t about to accept her fate as there was much work to do. Bubbles tunneled through the earth into a pocket of oxygen to buy the crew more time. Just as Bubbles inhaled the last of the oxygen, the rescue crew made contact and Bubbles was successfully extracted preventing The Death Planet from claiming yet another victim.

----

Cycles passed and the colony began to prosper. New Duplicants joined the ranks and the recycled water system was functioning which allowed oxygen production and food creation. The colony was at ease again and lived happily just above the rotting carcass which served as a perpetual reminder of the toils endured to get to where they were.

Bubbles reminisced about Red, Hideki, and Spike wishing they were able to see how far they had come. None of this would have existed without them.

Suddenly, a new patch was released which meant older saves could no longer be played but it had new content like more buildings and items and stuff.</content:encoded></item><item><title>Cancon 2017</title><link>https://jacobjangles.com/posts/cancon-2017/</link><guid isPermaLink="true">https://jacobjangles.com/posts/cancon-2017/</guid><description>Cancon 2017 - The Chronicles.</description><pubDate>Sun, 26 Feb 2017 00:00:00 GMT</pubDate><content:encoded>**Cancon? Yes.**

Not long after that, we descended upon Canberra for Round Two of the epic event known simply as [Cancon](http://www.cgs.asn.au/cancon/). This time around was a little bit different than [last time](https://jacobjangles.com/posts/cancon-2016/), but it was just as excellent. We had a slightly different group of people, and I didn&apos;t participate in any tournaments. On the plus side, this allowed more time for browsing for some good deals, and playing more games.  
If you like miniature games, board games, and card games, then I highly suggest you get a group of friends together and try and make Cancon the next time it comes around!

# Day One - Thursday
Once we arrived at our lodging, I accelerated towards the most optimal room to dump my belongings - about five rooms down, on the left. Five rooms down is the closest to the fun and utilities, but also the furthest from the noise. The left... because I was there last time.

The first game this time was _Entropy_, where you try to piece back your shattered reality. After a few rounds we moved over to something we could really sink our teeth into. A struggle for resources, accomplishments, popularity, and power - _Scythe_. I got to play one of the new factions, Albion. Then we tried _Suddenly Drunk_ with _Thief&apos;s Market_ which made for some very interesting moments. There was substantially more dice stealing, but because it was funny, not because it was a good play. There was relentless laughing and the root cause analysis determined it was Shayne&apos;s new name, Milkyballs, in conjunction with the ridiculous voice he chose to use. Milkyballs met an untimely end as I became widowed, twice - they were unrelated, I swear.

# Day Two - Friday
At last, Cancon was open. But first, breakfast. After an extremely lengthy discussion as to why we should definitely go to Pancake Parlour for breakfast, we skipped breakfast and went to the Con instead. After a few hours of browsing, buying, and trying to hunt down some _Warhammer 40K Conquest_ cards, it was time for lunch. It was totally Pancake Parlour this time. Pancakes, bacon, egg, banana, and pineapple sounds like a weird combination but it was delicious.

Now for _Alchemists_, to prove that I am the best Alchemist and that I&apos;m not afear&apos;d to chug some of my own concoctions. Turns out that I&apos;m not actually the best Alchemist, but at least I didn&apos;t poison myself. The same can&apos;t be said for the other alchemists. After all the deducing, we settled in for a nice and easy game of _Red7_, the birth child of _Uno_ and _Fluxx_.  

Dinner time jumped out of nowhere and it was time for a classic BBQ. After filling our hunger bars it was time for some _Codenames_, which if you haven&apos;t already played it, go play it. Don&apos;t randomly pick a card in your bonus guess, that&apos;s how you pick the assassin. It was worth it, though. To wrap up the night, it was time to sketch and guess in _Telestrations_. Even this family friendly game isn&apos;t immune from someone deciding to draw something a little.. unsavoury. Our house rules are to ignore points, and introduce yourself each time you go through your sketch book. Somehow it&apos;s already 1am, time for bed.

# Day Three - Saturday
Rise and shine, but there&apos;s only time for a banana and coffee before we hit up the Con. A vigorous shakedown of the stalls yielded some cheap _Warhammer 40K Conquest_ battle-packs this time around, as well as several items for sleeving and storing my cards. There was only limited time for lunch though, for we had more important matters to attend to...  

_Battlestar Galactica_ must be sabotaged. As a loyal Cylon, it was my duty to see the Battlestar and its crew be obliterated. Unfortunately, I didn&apos;t get the memo on how to effectively execute that. In conjunction with extreme luck, the Battlestar and crew made it out just fine. Since the crew made it out, we enjoyed another BBQ dinner. In an alternate Battlestar Galactica reality, the Cylons have planted several bombs on our ship and we need to defuse them. We only have ten minutes, and the _Fuze_ is burning quickly. How anyone pulls off the harder levels in _Fuze_ is beyond me, seriously. Wrapping up the night, was a peaceful and pleasant ocean life tour. We sauntered through the sea in our submarine in a game of _Oceanos_. My ocean was lacking in coral reef, but my submarine was really cool so I almost snagged the coolest ocean based on my submarine being awesome.

# Day Four - Sunday
Another banana and coffee, another morning. Before we left the lodging for the last time, we had a quick game of _Magic_. Not drawing into Land is such a pain. After losing quickly, an even quicker game of _Star Wars: Destiny_ took place. The game ended prematurely as check out time was upon us. We arrived at Cancon, one last time, trying to find any valuable remains from the event. A few more card storage pieces were acquired, and it was time to head home.

----

Cancon 2017 proved itself to be another huge success. One game I didn&apos;t play but noticed, probably because it went for about five hours and was the root of a lot of arguing, was _New Angeles_. I can only imagine the city was saved (If that was even the goal) because each decision seemed to take twenty terse minutes to decide upon.  
My recommendations this time around are _Red7_ for a game that&apos;s easy to get into and not too complicated, and _Scythe_ for a more complicated and strategic game.

That was my experience of Cancon 2017, and I&apos;d definitely do it again.</content:encoded></item><item><title>Optimise Ghost and nginx</title><link>https://jacobjangles.com/posts/optimise-ghost-and-nginx/</link><guid isPermaLink="true">https://jacobjangles.com/posts/optimise-ghost-and-nginx/</guid><description>A step by step guide on how to optimise Ghost with nginx - optimise.</description><pubDate>Wed, 04 Jan 2017 00:00:00 GMT</pubDate><content:encoded>---

**With the release of [Ghost 1.0](https://blog.ghost.org/1-0/), this guide is now deprecated. Please check out the [official documentation](https://docs.ghost.org/v1/docs/install) on how to install the latest version.**

---

This is a step by step guide on how to optimise [Ghost](https://ghost.org/) and [nginx](https://nginx.org/). This guide is part of the Deploy Ghost series and is split into three parts - [Deploying](https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/), [Securing](https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/), and **Optimising**. This is part three and once we&apos;re done, we will have nginx and our Ghost blog optimised to serve content quickly and reduce the server load in the process.

Before you start typing into your console, please take the time to research and understand what it is that you&apos;re actually executing on your server. This process worked for me, and might change as the applications have new patches and versions rolled out.

# Series Links
* [Part One - Deploy.](https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/)
* [Part Two - Secure.](https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/)
* [Part Three - Optimise.](https://jacobjangles.com/posts/optimise-ghost-and-nginx/)

****

# Update

**04/01/2017:** The nginx configuration for the `location / {}` block has been updated to include all of the headers. The original configuration was sending the root document without the correct security headers. The issue was caused by an added header preventing the `location / {}` block from inheriting the `server {}` headers.
****

Our Ghost blog is well and truly up and running. We could put the keyboard away now if we wanted to, but we really should look at fine tuning everything. This should help visitors get served faster, and reduce our server load.

You can get pretty crazy with optimising, so this post definitely doesn&apos;t cover everything. You might consider trying to optimise your fonts, HTML, CSS, even your favicon. Users don&apos;t tend to like waiting, speeding up your sites responsiveness even slightly could be the difference between a purchase or a negative review.

## Utilise nginx

So it turns out that nginx is actually really good at serving static content and caching. When a request comes in, we can get nginx to respond instead of sending the request over to Ghost. This is great because we remove the burden on Ghost to generate everything each time a request comes in. This will free up server resources and also reduce the amount of time a client waits before getting a response.

### Serve Static Content

Getting nginx to serve static content is as simple as telling it what to serve. To let nginx know what to serve, we want to edit our ghost.conf.

```bash
cd /etc/nginx/sites-available
nano ghost.conf
```

We&apos;ll ask nginx to serve up our casper theme, and our images. If you ever change your theme, you will need to update nginx.

```nginx
location ^~ /assets/ {
    root /var/www/ghost/content/themes/casper;
}
location ^~ /content/images/ {
    root /var/www/ghost;
}
```

### Caching Content

Getting nginx to cache content is a little more in depth as we need to create the area to hold the cache, configure the area to hold the cache, and then we can tell nginx what we want to be cached. First we need to create an area to hold the cache, we&apos;ll call it `ghostcache`.

```bash
cd /var/cache/nginx
sudo mkdir ghostcache
```

Then we want to edit `nginx.conf` to configure our cache.

```bash
cd /etc/nginx
sudo nano nginx.conf
```

We&apos;ll want to add in the below.

```nginx
proxy_cache_path /var/cache/nginx/ghostcache levels=1:2 keys_zone=ghostcache:60m max_size=500m inactive=24h;
proxy_cache_key &quot;$scheme$request_method$host$request_uri&quot;;
```

Now that we&apos;ve made our cache and configured it, we can tell nginx what to cache. We&apos;ll do this in `ghost.conf`.

```bash
cd /etc/nginx/sites-available
nano ghost.conf
```

This is what our root location will look like with our new cache directives added in. There&apos;s some extra stuff in there too such as specifying the cache validity, ignoring some headers that Ghost uses, configuring nginx to serve from cache if there&apos;s an issue, and adding in a header that shows if the cache was used. We&apos;ll need to add in our other headers again, as `add_header X-Cache-Status $upstream_cache_status;` will prevent the `location / {}` block from inheriting the `server {}` headers. This could be done more cleanly by adding the headers into their own configuration file and including them where they&apos;re needed, but this is easier to show.

```nginx
location / {
    proxy_cache ghostcache;
    proxy_cache_valid 60m;
    proxy_cache_valid 404 1m;
    proxy_ignore_headers Set-Cookie Cache-Control;
    proxy_hide_header Set-Cookie;
    proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
    add_header X-Cache-Status $upstream_cache_status;
    add_header Strict-Transport-Security max-age=15768000; includeSubDomains;
    add_header X-Frame-Options &quot;SAMEORIGIN&quot; always;
    add_header X-Content-Type-Options &quot;nosniff&quot; always;
    add_header X-Xss-Protection &quot;1&quot;;
    add_header Content-Security-Policy &quot;default-src &apos;self&apos;; script-src &apos;self&apos; *.google-analytics.com&quot;;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://127.0.0.1:2368;
}
```

We definitely don&apos;t want to cache our Ghost administrator panel, so we will need to add in an exception for that. If the administrator panel is hit, just pass through to Ghost.

```nginx
location ^~ /ghost/ {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:2368;
}
```

## Final Configuration

At last, this is what our complete `ghost.conf` file looks like.

```nginx
server {
    listen 80 default_server;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}
​
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    server_tokens off;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.2;
    ssl_ciphers &apos;ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256&apos;;
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security max-age=15768000; includeSubDomains;
    add_header X-Frame-Options &quot;SAMEORIGIN&quot; always;
    add_header X-Content-Type-Options &quot;nosniff&quot; always;
    add_header X-Xss-Protection &quot;1&quot;;
    add_header Content-Security-Policy &quot;default-src &apos;self&apos;; script-src &apos;self&apos; *.google-analytics.com&quot;;

    location &apos;/.well-known/acme-challenge&apos; {
        root /var/www/ghost/;
    }
​
    location ^~ /assets/ {
        root /var/www/ghost/content/themes/casper;
    }
​
    location ^~ /content/images/ {
        root /var/www/ghost;
    }
​
    location ^~ /ghost/ {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:2368;
    }

    location / {
        proxy_cache ghostcache;
        proxy_cache_valid 60m;
        proxy_cache_valid 404 1m;
        proxy_ignore_headers Set-Cookie Cache-Control;
        proxy_hide_header Set-Cookie;
        proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
        add_header X-Cache-Status $upstream_cache_status;
        add_header Strict-Transport-Security max-age=15768000; includeSubDomains;
        add_header X-Frame-Options &quot;SAMEORIGIN&quot; always;
        add_header X-Content-Type-Options &quot;nosniff&quot; always;
        add_header X-Xss-Protection &quot;1&quot;;
        add_header Content-Security-Policy &quot;default-src &apos;self&apos;; script-src &apos;self&apos; *.google-analytics.com&quot;;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:2368;
    }
}
```

## Ghost is Optimised

We&apos;ve now got a functioning, secure, optimised blog. There&apos;s always going to be more we can do to keep getting enhanced security and further optimisation, but this is a very solid start.

# Series Links
* [Part One - Deploy.](https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/)
* [Part Two - Secure.](https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/)
* [Part Three - Optimise.](https://jacobjangles.com/posts/optimise-ghost-and-nginx/)

# References
* [Patrick Nommensen](http://pnommensen.com/2014/09/07/high-performance-ghost-configuration-with-nginx/)
* [Scott Helme](https://scotthelme.co.uk/caching-ghost-with-nginx/)
* [nginx HTTP Proxy Module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html)</content:encoded></item><item><title>Ghost Emails Using Email Hosting</title><link>https://jacobjangles.com/posts/ghost-emails-using-email-hosting/</link><guid isPermaLink="true">https://jacobjangles.com/posts/ghost-emails-using-email-hosting/</guid><description>Getting Ghost to fling emails.</description><pubDate>Fri, 09 Dec 2016 00:00:00 GMT</pubDate><content:encoded>Ghost uses [Nodemailer](https://github.com/nodemailer/nodemailer) to fling emails around, which means you can use an email hosting solution with Ghost in order to send emails out. The [Ghost team](https://ghost.org/) have covered [self hosted mail configuration](https://support.ghost.org/mail/), though it might not be obvious what to do if you use a lesser known email hosting solution, or even operate your own. [Sergey Cheparev](http://cheparev.com/) covered this exact problem in their [Custom SMTP post](http://cheparev.com/ghost-use-custom-smtp-server/). That post should be all you need, but I thought I&apos;d expand on it a bit, and help spread the content.

A Ghost email configuration looks like the below. It&apos;s configured to use encrypted email.

```javascript
mail: {
    transport: &apos;SMTP&apos;,
    options: {
        host: &apos;yourdomain.com&apos;,
        port: 587,
        secureConnection: true,
        auth: {
            user: &apos;ghost@yourdomain.com&apos;,
            pass: &apos;securePassword1&apos;
        }
    }
}
```

## Breakdown
* `transport:` - The way we&apos;re sending emails.
* `host:` - The hostname or IP address to connect to.
* `port:` - The port to connect to.
* `secureConnection:` - Determines if TLS is enforced.
  * If `true`, exclusively use TLS.
  * If `false`, optionally use TLS.
* `user:` - The email address you want to send from.
* `pass:` - The password for the email address listed in `user:`.

If you can&apos;t use TLS, you&apos;ll need to set `secureConnection` to `false`, and set `port` to the insecure port provided by your email host.

## What are my details
If you&apos;re unsure what details to put in, your email provider usually has the details listed for you. It might be under a heading like **Mail Client Configuration** or **Manual Settings**. They usually look something like this:

### Secure SSL/TLS Settings  
**Username:** email@domain.com  
**Password:** The email account password.   
**Incoming Server:** secure.emailhosting.com  
**IMAP Port:** 993  
**POP3 Port:** 995  
**Outgoing Server:** secure.emailhosting.com  
**SMTP Port:** 587

### Non-SSL Settings
**Username:** email@domain.com  
**Password:** The email account password.  
**Incoming Server:** insecure.emailhosting.com  
**IMAP Port:** 143  
**POP3 Port:** 110  
**Outgoing Server:** insecure.emailhosting.com  
**SMTP Port:** 25

## Example
Let&apos;s say your email provider has given you the following details:
**Username:** ghost@example.com  
**Password:** a%fsd5)0v.  
**Outgoing Server:** secure.fastemailhosting.com  
**SMTP Port:** 587

Then your configuration should look like this:
```javascript
mail: {
    transport: &apos;SMTP&apos;,
    options: {
        host: &apos;secure.fastemailhosting.com &apos;,
        port: 587,
        secureConnection: true,
        auth: {
            user: &apos;ghost@example.com&apos;,
            pass: &apos;a%fsd5)0v.&apos;
        }
    }
}
```</content:encoded></item><item><title>Secure Ghost with nginx and Let&apos;s Encrypt</title><link>https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/</link><guid isPermaLink="true">https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/</guid><description>A step by step guide on how to secure Ghost with nginx and Let&apos;s Encrypt - secure.</description><pubDate>Sat, 12 Nov 2016 00:00:00 GMT</pubDate><content:encoded>---

**With the release of [Ghost 1.0](https://blog.ghost.org/1-0/), this guide is now deprecated. Please check out the [official documentation](https://docs.ghost.org/v1/docs/install) on how to install the latest version.**

---

This is a step by step guide on how to secure [Ghost](https://ghost.org/) with [nginx](https://nginx.org/) and [Let&apos;s Encrypt](https://letsencrypt.org/). This guide is part of the Deploy Ghost series and is split into three parts - [Deploying](https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/), **Securing**, and [Optimising](https://jacobjangles.com/posts/optimise-ghost-and-nginx/). This is part two and once we&apos;re done, we will have our Ghost blog running over HTTPS, with a good TLS and security header setup.

Before you start typing into your console, please take the time to research and understand what it is that you&apos;re actually executing on your server. This process worked for me, and might change as the applications have new patches and versions rolled out.

# Series Links
* [Part One - Deploy.](https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/)
* [Part Two - Secure.](https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/)
* [Part Three - Optimise.](https://jacobjangles.com/posts/optimise-ghost-and-nginx/)

***

Now that we&apos;ve got our server up and running, we need to make the whole thing a **lot** more secure. Case in point, when you access the Ghost administrator panel and log in, you&apos;re sending those credentials through HTTP, not HTTPS. That&apos;s bad.

## Get Utilities

In order to get Let&apos;s Encrypt, we need to use git. Update you repositories and grab it now.

```bash
sudo apt-get update
sudo apt-get install git
```

## Let&apos;s Encrypt

Before we get and execute Let&apos;s Encrypt, we need to set the stage up a bit. In order for Let&apos;s Encrypt to verify that we own the domain we&apos;ll claim we do, we need to open up a directory for it to do it&apos;s verification. We need to edit our ghost configuration file.

```bash
sudo nano /etc/nginx/sites-available/ghost.conf
```

Then we need to put this in:

```nginx
 location &apos;/.well-known/acme-challenge&apos; {
        root        /var/www/ghost;
 }
```

So we end up with our configuration file looking like so:

```nginx
server {
    listen 80 default_server;
    server_name example.com www.example.com;
    location &apos;/.well-known/acme-challenge&apos; {
        root        /var/www/ghost;
    }
    location / {
        proxy_set_header   Host      $http_host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_pass         http://127.0.0.1:2368;
    }
}
```

To apply the changes, we need to make sure to parse and reload nginx.

```bash
sudo nginx -t &amp;&amp; sudo nginx -s reload
```

To get Let&apos;s Encrypt, we clone the repository and we will put it in `/opt/letsencrypt`.

```bash
sudo git clone https://github.com/certbot/certbot /opt/letsencrypt
```

Then we need to execute the certbot. Follow any prompts and read the output to see if it worked correctly. Set `example.com` to your own domain, and make sure to add in `www.example.com` as another entry.

```bash
sudo /opt/letsencrypt/certbot-auto certonly --agree-tos --webroot -w /var/www/ghost -d example.com -d www.example.com
```

Now we&apos;ve got our certificates lined up, we&apos;ll want to make sure they are automatically renewed. We can do this by using [crontab](https://debian-administration.org/article/56/Command_scheduling_with_cron).

```bash
sudo crontab -e
```

We&apos;ll want to run the certbot to renew our certificates once a week, and reload nginx if it does perform the renewal. The below should be one line in our crontab.

```bash
43 5 * * 1 /opt/letsencrypt/certbot-auto renew --quiet --post-hook &quot;/usr/sbin/service nginx reload&quot; &gt;&gt; /var/log/le-renew.log
```

## Configure nginx

To utilise HTTPS we need to change our nginx configuration. We need to add in a permanent redirect to HTTPS, add in our Let&apos;s Encrypt certificates as well as make some adjustments for talking to Ghost. We&apos;ll have a section listening for insecure requests on port 80 and execute a permanent redirect to HTTPS with a 301. Then our section listening for secure requests on port 443 can talk to Ghost, with some slight configuration changes to do so.

So we want to edit our ghost configuration file.

```bash
sudo nano /etc/nginx/sites-available/ghost.conf
```

Our configuration file should look like the below.

```nginx
server {
    listen 80 default_server;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    location &apos;/.well-known/acme-challenge&apos; {
        root /var/www/ghost/;
    }
    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:2368;
    }
}
```

As with all things nginx, we then want to parse the file and reload nginx for our changes to take effect.

```bash
sudo nginx -t &amp;&amp; sudo nginx -s reload
```

## Ghost is Secure

Not only do we have a functioning blog, but it&apos;s also covered by HTTPS. Now we can securely log into the Ghost admin panel, and visitors can be safe knowing their connection is encrypted.

Our job isn&apos;t done just yet as we should strengthen our configuration. There are some things in here that might break your website - so be careful here. Pay attention to what you&apos;re doing and don&apos;t just copy and paste stuff in.

## Strengthening Security

The only thing we want to do before we configure nginx is to generate a stronger [Diffie-Hellman](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange) parameter by using at least 2048 bits. We&apos;ll need this for one of our [nginx directives](https://nginx.org/en/docs/dirindex.html).

```bash
sudo openssl dhparam 2048 -out /etc/ssl/certs/dhparam.pem
```

Here&apos;s what our complete configuration looks like:

```nginx
server {
    listen 80 default_server;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    server_tokens off;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.2;
    ssl_ciphers &apos;ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256&apos;;
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security max-age=15768000; includeSubDomains;
    add_header X-Frame-Options &quot;SAMEORIGIN&quot; always;
    add_header X-Content-Type-Options &quot;nosniff&quot; always;
    add_header X-Xss-Protection &quot;1&quot;;
    add_header Content-Security-Policy &quot;default-src &apos;self&apos;; script-src &apos;self&apos; *.google-analytics.com&quot;;

    location &apos;/.well-known/acme-challenge&apos; {
        root /var/www/ghost/;
    }

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:2368;
    }
}
```

Then parse and reload nginx.

```bash
sudo nginx -t &amp;&amp; sudo nginx -s reload
```

You can then use [Qualys SSL Labs](https://www.ssllabs.com/ssltest/) and [Security Headers](https://securityheaders.io/) to test out your new security configuration.

## Configuration Details

This section will very briefly touch on what each directive does. Please go and research what each one does in depth, especially the headers `Strict-Transport-Security` and `Content-Security-Policy` as they can break things badly.

### General

By preventing nginx from divulging its version number, we can deny a malicious attacker some useful information. A particular version might be vulnerable to a certain attack.

```nginx
server_tokens off;
```

### SSL/TLS

All we&apos;re doing here is telling nginx where our certificates are.

```nginx
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
```

This is responsible for enabling [OCSP Stapling](https://en.wikipedia.org/wiki/OCSP_stapling) and provides a DNS server.

```nginx
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4;
```

Optimising SSL/TLS.

```nginx
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
```

Here we are disabling SSL in favour of TLS. We&apos;ll only accept TLSv1.2, then we provide some ciphers and favour those.

```nginx
ssl_protocols TLSv1.2;
ssl_ciphers &apos;ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256&apos;;
ssl_prefer_server_ciphers on;
```

### Headers
Instructs client browsers to force HTTPS for 6 months, this includes subdomains. **If you don&apos;t serve something via HTTPS, it will not load for the client - for six months.**

```nginx
add_header Strict-Transport-Security max-age=15768000; includeSubDomains;  
```

Prevents a browser from loading your site in an iframe - this safeguards against [clickjacking](https://www.owasp.org/index.php/Clickjacking). An iframe is only allowed when it&apos;s being loaded from your domain.

```nginx
add_header X-Frame-Options &quot;SAMEORIGIN&quot; always;
```

Prevents a browser from [MIME-type sniffing](https://en.wikipedia.org/wiki/Content_sniffing) and stops the browser from interpreting files as something that they&apos;re not.

```nginx
add_header X-Content-Type-Options &quot;nosniff&quot; always;
```

Prevents [Cross-Site Scripting](https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)) by sanitising the page if such an attack is detected.

```nginx
add_header X-Xss-Protection &quot;1&quot;;
```

Provides a white-list of approved sources of content that a browser is allowed to load. **You can prevent your website from loading with this header**. A reporting mode exists, use `Content-Security-Policy-Report-Only` instead of `Content-Security-Policy`.

```nginx
add_header Content-Security-Policy &quot;default-src &apos;self&apos;; script-src &apos;self&apos; *.google-analytics.com&quot;;
```

## Ghost is Secure

We&apos;ve now got a functioning, secure blog. It will keep running and serving content securely, but we can make it serve content a little better. [Part three, Optimising Ghost](https://jacobjangles.com/posts/optimise-ghost-and-nginx/) will speed up the blog while reducing server load.

# Series Links
* [Part One - Deploy.](https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/)
* [Part Two - Secure.](https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/)
* [Part Three - Optimise.](https://jacobjangles.com/posts/optimise-ghost-and-nginx/)

# References
* [Bjorn Johansen](https://bjornjohansen.no/letsencrypt-nginx)
* [Let&apos;s Secure Me](https://letsecure.me/secure-web-deployment-with-lets-encrypt-and-nginx/)
* [Mozilla SSL Configuration Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/)
* [Qualys SSL Labs](https://www.ssllabs.com/ssltest/)
* [Security Headers](https://securityheaders.io/)</content:encoded></item><item><title>Deploy Ghost on Debian with nginx and PM2</title><link>https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/</link><guid isPermaLink="true">https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/</guid><description>A step by step guide on how to deploy Ghost on Debian with nginx and PM2 - deploy.</description><pubDate>Mon, 07 Nov 2016 00:00:00 GMT</pubDate><content:encoded>---

**With the release of [Ghost 1.0](https://blog.ghost.org/1-0/), this guide is now deprecated. Please check out the [official documentation](https://docs.ghost.org/v1/docs/install) on how to install the latest version.**

---

This is a step by step guide on how to deploy [Ghost](https://ghost.org/) on [Debian (Jessie)](https://www.debian.org/releases/jessie/) using [nginx](https://nginx.org/) as a reverse-proxy, and [PM2](http://pm2.keymetrics.io/) as our Node.js process manager. This guide is part of the Deploy Ghost series and is split into three parts - **Deploying**, [Securing](https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/), and [Optimising](https://jacobjangles.com/posts/optimise-ghost-and-nginx/). This is part one and once we&apos;re done, we will have a fully functional Ghost blog (running over HTTP).

Before you start typing into your console, please take the time to research and understand what it is that you&apos;re actually executing on your server. This process worked for me, and might change as the applications have new patches and versions rolled out.

# Series Links
* [Part One - Deploy.](https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/)
* [Part Two - Secure.](https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/)
* [Part Three - Optimise.](https://jacobjangles.com/posts/optimise-ghost-and-nginx/)

****

If you want to have your own Ghost server, but don&apos;t want to manage it, the [Ghost Foundation](https://ghost.org/about/) offer [a hosted solution.](https://ghost.org/pricing/) If you want to get your hands dirty and [help out](https://github.com/TryGhost/Ghost/graphs/contributors), they&apos;re happy for anyone to [contribute](https://ghost.org/about/contribute/).

## Get Utilities

I&apos;m assuming we&apos;re running as root for the time being. Once we create another user, that&apos;s when we&apos;ll start executing commands as them. We&apos;ll start by getting some utility software that we can use later on.

```bash
apt-get update
apt-get install curl
apt-get install zip
```

## Install and Prepare nginx

The Debian nginx repository tends to lag behind in release versions. At the time of writing, it has version 1.6.2, which is missing some security features we want later on. To get the [latest version](https://nginx.org/en/linux_packages.html), we need to add in a new source for nginx and get their GPG key.

So log in as root and let&apos;s edit our sources list.

```bash
nano /etc/apt/sources.list
```

We want to add the below to the bottom of the sources.list file.

```nginx
# nginx
deb http://nginx.org/packages/mainline/debian/ jessie nginx
deb-src http://nginx.org/packages/mainline/debian/ jessie nginx
```

Now we need to grab the GPG key and add it. Once it&apos;s been added, we can delete the key.

```bash
curl -O https://nginx.org/keys/nginx_signing.key &amp;&amp; apt-key add ./nginx_signing.key
rm nginx_signing.key
```

Update our repositories so we can download and install nginx.

```bash
apt-get update
apt-get install nginx
```

We need to setup nginx so that we can easily administer it later. To do that, we&apos;ll make `/var/www/` our webroot. We will also use `/etc/nginx/sites-available` to store our individual configuration files, and create a symlink to `/etc/nginx/sites-enabled` when we&apos;re reading for them to go live.

```bash
cd /var
mkdir www
cd /etc/nginx
mkdir sites-available
mkdir sites-enabled
```

In order for nginx to know about our `sites-enabled` configuration files, we have to tell it to include any configuration files it finds in there. We make this change in the `nginx.conf` file.

```bash
nano nginx.conf
```

Add the below string in to the end just after `include /etc/nginx/conf.d/*.conf;` and save the file.

```nginx
include /etc/nginx/sites-enabled/*;
```

Now we can remove the default configuration file.

```bash
cd conf.d
rm default.conf
```

## Add a user

We want a dedicated user to run Ghost and the associated processes, so let&apos;s go ahead and create a user called ```ghost``` and give it ```sudo``` access.

```bash
adduser ghost
adduser ghost sudo
```

Log out of `root` and log into `ghost`. Going forward we&apos;ll be working from the user `ghost`.

## Install Node.js

We want to install Node.js with [NVM](https://github.com/creationix/nvm) so we don&apos;t need to execute things as root (or with sudo), it also makes managing Node.js that much easier. We&apos;ll download and install it.
_This command will get NVM version 0.32.1, there might be a new one, so check [over here.](https://github.com/creationix/nvm)_

```bash
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.32.1/install.sh | bash
```

**In order for NVM to take effect, we need to log off and log in again. If you don&apos;t, you will get an error.**

As of writing, [Ghost supports Node V4](http://support.ghost.org/supported-node-versions/) so we can use the Argon LTS to get the latest in V4. We then want to make it our default Node version.

```bash
nvm install --lts=argon
nvm alias default node
```

Once it&apos;s installed, we can verify the Node and NPM versions with ``node -v`` and ``npm -v``.

## Install Ghost

To install Ghost we want to get it and extract it to it&apos;s own directory.

```bash
curl -L https://ghost.org/zip/ghost-latest.zip -o ghost.zip
mkdir ghost
unzip ghost.zip -d /home/ghost/ghost
cd ghost
```

Now we need to execute Ghost so it can perform the first run setup.

```bash
npm install --production
npm start --production
```

When we execute `npm start --production`, we will need to press `ctrl` + `c` to get our console control back. This will shutdown our blog, which is good for now.

Now we need to move our Ghost directory somewhere that nginx can deal with. Since we&apos;re using `/var/www/` as our web root, we&apos;ll move it there. We&apos;re done with `ghost.zip` so we can delete it too.

```bash
cd ../
sudo mv /home/ghost/ghost/ /var/www/
rm ghost.zip
```

## Configure Ghost

A simple change to update our URL and Mail settings inside Ghost is needed. We&apos;ll need to change directory to our Ghost install and alter the `url` and `mail` settings inside `config.js`.

```bash
cd /var/www/ghost
nano config.js
```

We want to change `url: &apos;http://my-ghost-blog.com&apos;,` to whatever your URL is. I will use example.com. The mail settings should also be changed - there&apos;s a few ways to do it so read the [Ghost mail configuration page.](https://support.ghost.org/mail)

Below is a small extract of what `config.js` will look like once we&apos;re done.

```javascript
config = {
  // ### Production
  // When running Ghost in the wild, use the production environment.
  // Configure your URL and mail settings here
  production: {
    url: &apos;http://example.com&apos;,
    mail: {
      transport: `SMTP`,
      options: {
        service: `Gmail`,
        auth: {
          user: &apos;youremail@gmail.com&apos;,
          pass: &apos;yourpassword&apos;
        }
      }
    },
    database: {
      // More content.
    },
  },
  // More content.
}
```

## Configure &apos;ghost&apos; ngnix File

We can create our Ghost configuration file in `sites-available`, this will be used to let nginx know about Ghost. We will make any nginx changes that relate to Ghost in this file. We&apos;ll call the configuration file `ghost.conf`.

```bash
cd /etc/nginx/
sudo touch /etc/nginx/sites-available/ghost.conf
sudo nano /etc/nginx/sites-available/ghost.conf
```

Inside our freshly created configuration file, we want to add in the below content and save it.

```nginx
server {
    listen 80 default_server;
    root /var/www/ghost/;
    server_name example.com www.example.com;
    location / {
        proxy_set_header   Host      $http_host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_pass         http://127.0.0.1:2368;
    }
}
```

Now we want to create a symlink to enable Ghost inside nginx.

```bash
sudo ln -s /etc/nginx/sites-available/ghost.conf /etc/nginx/sites-enabled/ghost.conf
```

In order for our changes to take effect, we need to parse our file to check for errors, and if it&apos;s good then we want to reload nginx.

```bash
sudo nginx -t &amp;&amp; sudo nginx -s reload
```

If you try and hit your blog now, you&apos;ll be provided with a 502 Bad Gateway error. Our Ghost blog isn&apos;t actually running just yet, so there&apos;s nothing there.

## Keep Ghost running with PM2

We&apos;ll use PM2 to make sure Ghost starts up again if the server restarts. PM2 also makes it easier to manage the Ghost process and let&apos;s us easily manage other Node processes.

```bash
cd /var/www/ghost
npm install pm2 -g
NODE_ENV=production pm2 start index.js --name=&quot;GhostBlog&quot;
pm2 save
pm2 startup debian
```

After we execute `pm2 startup debian`, we&apos;ll get an output saying you need to execute another command. You&apos;ll need to type it in exactly as it says and execute it **otherwise Ghost won&apos;t start when the server restarts.**

## Ghost is Deployed

We&apos;ve now got a functioning blog, but it&apos;s seriously insecure. If you log in to the Ghost admin panel, those credentials are sent in plain text. That&apos;s really bad. [Part two, Securing Ghost](https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/) will cover that particular issue.

# Series Links
* [Part One - Deploy.](https://jacobjangles.com/posts/deploy-ghost-on-debian-with-nginx-and-pm2/)
* [Part Two - Secure.](https://jacobjangles.com/posts/secure-ghost-with-nginx-and-lets-encrypt/)
* [Part Three - Optimise.](https://jacobjangles.com/posts/optimise-ghost-and-nginx/)

# References
* [Ghost Documentation](https://help.ghost.org/)
* [Hamza Shezad - DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-create-a-blog-with-ghost-and-nginx-on-ubuntu-14-04)</content:encoded></item><item><title>Learning iptables Basics</title><link>https://jacobjangles.com/posts/learning-iptables-basics/</link><guid isPermaLink="true">https://jacobjangles.com/posts/learning-iptables-basics/</guid><description>Learning the basics of iptables.</description><pubDate>Fri, 01 Apr 2016 00:00:00 GMT</pubDate><content:encoded>When a new server is stood up, one of the first things that it undergoes is the formidable process of *‘becoming secure’*. During this process, the [firewall](https://en.wikipedia.org/wiki/Firewall_%28computing%29) often becomes the centre of attention for a while – and understandably so. The firewall dictates what traffic is allowed in and out of the server, as well as where the traffic can come in and out from. Kind of like a police officer (the firewall) only allowing a specific type of car (the protocol) to enter the city (the server) on the M2 highway (the port), but denies entry if it’s a different car or the wrong highway. That’s a very simple way of describing it, as you can configure a firewall to do a bunch of stuff. There’s multiple ways to administer a firewall, and I decided to learn some basics using [iptables](https://en.wikipedia.org/wiki/Iptables).

*Important Note: iptables itself isn’t a firewall. It’s a front end for the Netfilter framework, which is used as a firewall. Put simply, iptables is used to administer Netfilter.*

Just quickly, ‘iptables’ is made up of a few tables, the tables are made up of a few chains, and the chains are made up of one or more rules. You can add tables, chains, and rules. As a result, you can get very specific with the configuration – but that’s outside of our scope.
We’re dealing with the default table called the **Filter Table**, which has the default chains **INPUT**(Inbound packets), **OUTPUT** (Outbound packets), and **FORWARD** (Passing through packets). I’ll show you some simple rules that we can use to populate the necessary chains.

A standard default table without any comments or formatting (_**Please** always comment and format your stuff_) looks like this:

```bash
*filter
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -s 127.0.0.0/8 -j REJECT
-A INPUT -p icmp -m state --state NEW --icmp-type 8 -j ACCEPT
-A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m limit --limit 5/min -j LOG --log-prefix &quot;iptables_INPUT_denied: &quot; --log-level 7
-A INPUT -j REJECT
-A FORWARD -j REJECT
COMMIT
```

First up is `*filter`. This indicates we’re dealing with the Filter table (Which is our default table). 

Down the very bottom is `COMMIT`. This applies everything above it to the table we named at the top. It’s worth noting that these firewall rules get lost on reboot – we just reapply them on start up.

**Now for the rules.** Each rule follows this system: The **parameter**, and then the _parameters value_. This can be repeated as many times as needed. They look like `-p value`, or `--parameter value`.  
\*Note: It’s not a standard, but generally if the parameter name is a single character, it gets a single dash. If it’s at least two characters, it gets two dashes.*

First we need to say which chain we’re appending the rule to. Our parameter is `-A` (A for Append), and our value is the chain. Lines 2 through 8 are `-A INPUT` because we’re appending the rule to the`INPUT` chain. Just like line 9 is `-A FORWARD` because we’re appending it to the `FORWARD` chain.

This is where things don’t really look the same, but it’s still just a parameter and then the value. The parameter will be in (round brackets), and the parameter value will be in [square brackets].  
\*Note: ! means ‘not’. So if it’s **not** the interface named, for example.*

```bash
! (-i) [lo] (-s) [127.0.0.0/8] (-j) [REJECT]
(-p) [icmp] (-m) [state] (--state) [NEW] (--icmp-type) [8] (-j) [ACCEPT]
(-m) [limit] (--limit) [5/min] (-j) [LOG] (--log-prefix) [&quot;iptables_INPUT_denied: &quot;] (--log-level) [7]
```

Now that you can read it, the next part is figuring out what each piece does. There’s not much point in me rehashing what each parameter accepts and does, as it’s already documented thoroughly. If you are interested, two good resources are the [iptables manual page](http://ipset.netfilter.org/iptables.man.html) and the [netfilter iptables page](http://www.netfilter.org/documentation/HOWTO/packet-filtering-HOWTO-7.html).

Here’s a quick description of some common parameters: 
- `-i` specifies the name of an **i**nterface in which a packet was received.
- `-j` specifies what happens if a packet matches. *(**j**ump – this is always at the end of the rule)*
- `-s` specifies a **s**ource address.
- `-p` specifies a **p**rotocol.
- `-m` specifies a **m**atch to use.

To bring everything together, below is what a useful, tidy, and strict Filter table looks like. Feel free to use it. It’s modified from the [Debian iptables](https://wiki.debian.org/iptables) page. As with all things, make sure you know what it does before you use it.

```bash
*filter

# Allow all loopback (lo0) traffic and reject traffic
# to localhost that does not originate from lo0.
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -s 127.0.0.0/8 -j REJECT

# Allow ping.
-A INPUT -p icmp -m state --state NEW --icmp-type 8 -j ACCEPT

# Allow SSH connections.
-A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT

# Allow inbound traffic from established connections.
# This includes ICMP error returns.
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Log what was incoming but denied (optional but useful).
-A INPUT -m limit --limit 5/min -j LOG --log-prefix &quot;iptables_INPUT_denied: &quot; --log-level 7

# Reject all other inbound.
-A INPUT -j REJECT

# Reject all traffic forwarding.
-A FORWARD -j REJECT

COMMIT
```</content:encoded></item><item><title>Cancon 2016</title><link>https://jacobjangles.com/posts/cancon-2016/</link><guid isPermaLink="true">https://jacobjangles.com/posts/cancon-2016/</guid><description>Cancon 2016 - The Chronicles.</description><pubDate>Sun, 28 Feb 2016 00:00:00 GMT</pubDate><content:encoded>I was invited to attend [Cancon](http://www.cgs.asn.au/cancon/) this year, a convention focusing on miniature games, card games, and board games. Games are awesome, so a long weekend of playing games was locked in. I had so much fun it was ridiculous, anyone that likes games should go check it out (even if you’re not sure about liking games just yet) – make sure you can stay somewhere with a bunch of people. A lot of the fun for me came from playing new games with the people I was staying with over the four days. We’d visit the convention each day, then return to the building we stayed in which accommodated ~12 people and continue to play games. There were a lot of people that popped up during the evening and left before everyone went to sleep so there was always a group of people available to play. It was absolutely excellent.

If you weren’t playing or looking at games, you were either eating or sleeping.

# Day One – Friday
After arriving at the lodging and unpacking into my room, the evening was spent getting introduced to some of the people I’d be living with for the next few days (The rest would arrive on Saturday with others dropping in and out throughout). After chatting and drinking for a while, we busted out some _Maximum Throwdown_. It’s a simple game based on luck and some skill that is very easy to play, and can get some rivalries going. Pick your faction and then literally throw your cards onto the table – score points, steal cards, waste other player’s cards, and trash talk. After a couple of games, it was time to call it and head off to bed.

# Day Two – Saturday
Early on Saturday morning, Cancon opened. Before we could visit, we needed nourishment. A trip to Pancake Parlour for breakfast was organised. After a short drive down the very straight Canberra roads and parking in the weirdly shaped parking lot, we sat down to talk some more games and eat some delicious pancakes. After eating, the first order of business was to determine the fate of the galaxy in the _Star Wars_ tournament. The Imperial fleet is tracking the Rebel Alliance’s hidden base with intentions to eviscerate it with one shot from the Death Star. The tournament was friendly and laid back, despite what was at stake.

Once the fate of the galaxy had been determined, it was time to see what was around and check out all the stores. I spent a while perusing the stalls looking for some games that caught my eye and ended up buying _Codenames_ and _Epic_. Lunch was acquired and we headed back to the lodging for some drinks and games. We played a few rounds of _Coup: Rebellion G54_, a game about bluffing and psyching out your opponents. Then, we moved onto the swashbuckling MOBA styled _Rum &amp; Bones_ miniatures game where you try and take out your opponents heroes and ship while remaining vigilant of the looming threat of the Kraken. After, we all sat down (and then stood up) for _The Last Banquet_ – a wonderful gesture from the King. Unfortunately, the nobles attending plan on relieving the King from his duties by knife or poison. Finally, our village was under threat by none other than the _Werewolf_. Would the villagers lynch the werewolves successfully, or would the werewolves feast? Spoiler alert, the villagers won and so they retired to their beds for the night.

# Day Three – Sunday
The start of another day filled with more games! A healthy selection of bacon, eggs, and toast was had for breakfast before marching off to conquer in _A Game of Thrones (Second Edition)_. It’s a power struggle between two forces, with each side claiming power through military prowess, shady intrigue, and literally taking your opponents power. The tournament was fierce with some excellent plays and a few turns being played without deciding who won in the Domination Phase (Whoops…). Once the tournament wrapped up, I played a couple of games of _Epic_, a game sort of like _Magic The Gathering_. _Epic_ pits two Elder Gods against each other in a literal battle of the gods, creating champions to do battle and casting devastating spells to determine who the _real_ Elder God is.
Then, we scoured the stalls for any remaining deals before heading off to get a late lunch. Upon returning to the lodging, a game of _Star Wars Imperial Assault_ was being set up – the perfect time to jump into any game. _Imperial Assault_ is a hybrid miniatures and RPG game set in the Star Wars universe that places the mastermind of the Galactic Empire against a squad of the Rebel elites. Once our campaign was completed, we settled around the table for a brain wrenching game of _Codenames_. Only the two spy masters know their agents codenames, and they need to dish out hints for their team so they can make contact with all of their agents before the opposing team. After we’d found our agents, I had to learn how to play _Conquest_ for the tournament in the morning. Once my training was complete, I retired once again to bed.

# Day Four – Monday
On Monday afternoon, Cancon closed up shop – but not before the _Warhammer 40K Conquest_ tournament. The remaining scraps of bacon and eggs were turned into fuel for the imminent war over planets. Your Warlord joins the fray as you and your armies travel to distant planets to enforce your claim of the Traxis Sector, a sector worth dying for. The tournament was packed with very close matches, most coming down to a final stand where one mistake could determine the winner. I was also very happy to pull off a Warp Storm that took out ~6 Ork units in one go. If you’re interested in a deck building card game or an LCG (Living Card Game), I highly recommend checking out _Conquest_ – it’s got neat mechanics and is set in an awesome universe.
Once the tournament wrapped up, all the stores were well into the process of packing up. With that, it was time to bid farewell to the few left and start the journey home.
___
​Cancon was a huge pile of fun. New friends were made, new games were played, and new memories were established. Out of all the games that were listed, these were only the ones that I participated in – there was at least two or three games being played at any one time. Each game I played was great, but two games that you should really try out are _Codenames_ and _Star Wars Imperial Assault_. The former is a very fun party game that is easy to get running (especially for people new to games), whereas the latter is a much more in depth and complicated game that is immensely fun.

That was my experience of Cancon 2016, and I’d definitely do it again.</content:encoded></item><item><title>Power Cycling</title><link>https://jacobjangles.com/posts/power-cycling/</link><guid isPermaLink="true">https://jacobjangles.com/posts/power-cycling/</guid><description>Have you tried turning it off and on again, rebooting, restarting, kicking, or bouncing it?</description><pubDate>Fri, 22 Jan 2016 00:00:00 GMT</pubDate><content:encoded>Have you tried turning it off and on again, rebooting, restarting, kicking, or bouncing it?  

These are some of the ways I hear people suggesting to [power cycle](https://en.wikipedia.org/wiki/Power_cycling) something that’s not working as it should. That is, turning something off and turning it back on again. Once the device comes back from the dead, it’s usually working at full capacity and there’s no sign of the error that was plaguing the device just moments before.

It’s straight up **magic**.

You don’t even *do* anything. It just… starts working after you wait for a minute. Power cycling is the go to resolution. To make it even better, power cycling applies to so many things. Phone isn’t getting emails? Power cycle. Laptop feels sluggish? Power cycle. I don’t have any internet! Power cycle. My ridiculously specific mobile application keeps crashing! Power cycle.

Despite how magical and easy this operation is, I still have people that get it wrong or just lie to me. No, your PC didn’t reboot in five seconds – you just locked Windows and logged in again. No, closing the laptop lid isn’t what we want. No, locking and unlocking your phone doesn’t qualify. I fear the day that I might see someone turn their monitor off and on again.

Unfortunately, power cycling can’t resolve every single issue. However, we can kick it up a notch by adding some technique to the power cycle. Shut it down again. Now go grab a coffee, when you’re back you can turn it on again. This is expert level power cycling.

Power cycling works because it allows the device to cleanse itself. When the device loses power, all of the volatile memory is cleared. If you wait while it’s powered off, it allows time for the capacitors to discharge. When the power comes back, it’s like starting a brand new version of the device where every little bit is happy and friendly.

Please, if you encounter a problem. **Turn it off and on again**. There’s a reason this is the first thing suggested when devices aren’t working properly.</content:encoded></item><item><title>Icon/Logo Design</title><link>https://jacobjangles.com/posts/icon-logo-design/</link><guid isPermaLink="true">https://jacobjangles.com/posts/icon-logo-design/</guid><description>Design is hard, so removing your choices makes life easier.</description><pubDate>Tue, 22 Dec 2015 00:00:00 GMT</pubDate><content:encoded>I wanted to create a design for my game community project [Crystal Docks](https://crystaldocks.com/). After thinking about it for a while, the realisation came to me that creating a good design is hard. Really hard. There’s so many factors to consider. So to make it easier I decided to leverage the [Material Design](https://www.google.com/design/spec/style/icons.html) specifications. Adhering to a design spec *removes* options, and this makes things easier. It provides a framework you need to build from and abide by instead of having unlimited options to work with.

Up first was to put down rough concept designs and ideas while keeping the whole Material concept in mind. Just rough ideas, nothing fleshed out. Doing this gets ideas out of the brain and puts the design into a form you can work with. This is what I ended up with after playing with MS Paint:

![Hastily made design concepts created in MS Paint](@/assets/blog-images/2015-12-22-icon-logo-design/conceptCYDKLogos.png)

Secondly, throw together some more polished designs. Open up a more substantial program like [GIMP](https://www.gimp.org/) and flesh out some of the designs you like. Never mind the tiny details, those come later. I liked the crystal shape and it worked well with the Material shadow, so I built upon that. Here are two designs I ended up with:

![CYDK-Material-Horizontal](@/assets/blog-images/2015-12-22-icon-logo-design/CYDK-Material-Horizontal.png)
![CYDK-Material-Vertical](@/assets/blog-images/2015-12-22-icon-logo-design/CYDK-Material-Vertical.png)

Finally, after many discussions and ideas being thrown back and forth, I ended up with one design in two flavours. Now to add in all the little details the Material Design spec includes.

![CYDK-Line-Diamond-Fold-Thirds-V2](@/assets/blog-images/2015-12-22-icon-logo-design/CYDK-Line-Diamond-Fold-Thirds-V2.png)
![CYDK-Line-Diamond-Fold-3D](@/assets/blog-images/2015-12-22-icon-logo-design/CYDK-Line-Diamond-Fold-3D.png)

Creating something that looks good is hard and can take a while. I’m not a designer by any stretch, but I like the end result that was achieved. Bonus points because it wasn’t bought and I got to learn some things along the way.</content:encoded></item><item><title>Hello World!</title><link>https://jacobjangles.com/posts/hello-world/</link><guid isPermaLink="true">https://jacobjangles.com/posts/hello-world/</guid><description>The start of another eternal project.</description><pubDate>Fri, 20 Nov 2015 00:00:00 GMT</pubDate><content:encoded>The start of another project, though in theory this one just never ends. Make a blog of some kind so I can write about the other projects I’m doing. It also acts as a place I can write about anything else, so that’s neat.

Any good project starts with some form of [Hello World](https://en.wikipedia.org/wiki/%22Hello,_World!%22_program), so:

&gt; Hello, World!</content:encoded></item></channel></rss>