Kasper Grubbe

Read this first

Giving your servers fun pet names with Terraform

You are probably aware that that Terraform have a random number module that works like this:

resource "random_integer" "server-id" {
  min = 100
  max = 999
}

However, this isn’t very helpful as is, because it would regenerate new numbers after each Terraform run.

You can introduce a “keepers”-stanza that makes the random number dependent on the input, it works like this:

resource "random_integer" "server-id" {
  min = 100
  max = 999

  keepers = {
    image = data.digitalocean_images.server-images.images.0.image
  }
}

So now Terraform will only change the number when data.digitalocean_images.server-images.images.0.image changes, very handy!

So what about those pet names then?!

The random2-module provides us with a random_pet-resource that works like this:

resource "random_pet" "server-pet" {
  length = 2

  keepers = {
    image =
...

Continue reading →


Let your servers join your Tailscale tailnet with Terraform and cloud-init

First generate an oauth client credentials for use in Terraform by going to https://login.tailscale.com/admin/settings/oauth and clicking “Generate oauth client”:

Screenshot 2024-08-03 at 13.57.36.png

Note down your oauth_client_id and you oauth_client_secret.

Inside Terraform first add the Tailscale provider:

terraform {
  required_providers {
    tailscale = {
      source  = "tailscale/tailscale"
      version = "0.16.1"
    }
  }
}

After running terraform init, you can then create a file named tailscale.tf and insert this:

provider "tailscale" {
  oauth_client_id = ""
  oauth_client_secret = ""
}

resource "tailscale_tailnet_key" "hourlykey" {
  reusable      = true
  ephemeral     = true
  preauthorized = true
  expiry        = 3600
  description   = "Hourly key Terraform managed"
  tags          = ["tag:server"]
}

Now Terraform will automatically create tailnet keys that works just for an hour, and they will...

Continue reading →


Get attachments from .msg files using Ruby

A colleague had a lot of .msg and .msg.pst email files where they needed to extract the attachments from them, Ruby to the rescue!

I used the msg gem from https://github.com/aquasync/ruby-msg

I put all the files in a directory called emails/ and when the script is done running it will output the attachments to a directory named attachments/.

require 'bundler/inline'
gemfile do
  source 'https://rubygems.org'

   We're using a git reference here because https://github.com/aquasync/ruby-msg/issues/14 is 
   only fixed in master
  gem 'ruby-msg', github: 'aquasync/ruby-msg', ref: '34fb32e43e29b3d5fa535537faf7123a176146a8'
end

require 'mapi/msg'
require 'fileutils'

directory_path = 'attachments'
FileUtils.rm_rf(directory_path) if Dir.exist?('attachments')
FileUtils.mkdir_p(directory_path)

Dir.glob(File.join('emails', '*.{msg,msg.pst}')).each_with_index do |msg_file, mindex|
  puts
...

Continue reading →


Enforcing host parameter usage for URLs in e-mail templates with Hamlcop

At Billetto we have multiple domains for our ticketing website depending on the market, as an example our danish organisation lives at billetto.dk and our swedish counterpart lives at billetto.se.

All of our organisations shares the same infrastructure and the domains routes to the same Ruby on Rails application.

This means that when we send e-mails to our event organisers and ticket buyers, and our e-mails include links back to our websites, we need to make sure that we use the correct domain.

Ruby on Rails have a setting that sets the default domain for links used in e-mails called config.action_mailer.default_url_options:

config.action_mailer.default_url_options = {host: "billetto.dk"}

And this is an example of a haml-template used in our mailers:

%p
  = link_to("Link to order", order_url(id: @order.id))

This means that when host: isn’t set in the template, it defaults to...

Continue reading →


Logging on to WiFi automatically with Raspberry Pi OS (Debian Bookworm)

When I was looking at previous posts, they all seem be about older versions using wpa-supplicant, and it wouldn’t work for me.

I found out that newer distros seem to use NetworkManager to handle networking, and it requires a different setup.

It’s quite easy: Just plop a configuration file into /etc/NetworkManager/system-connections/kasperwifi.nmconnection, and update it matching your network:

[connection]
id=billettowifi
uid=A0A43787-0132-44AF-97DC-9ABC92CBA46B  generate your own using uuidgen
type=wifi
autoconnect-retries=0  0 means keep autoconnecting

[wifi]
mode=infrastructure
ssid=Kasper Guest

[wifi-security]
key-mgmt=wpa-psk
psk=iamapassword

[ipv4]
method=auto

[ipv6]
addr-gen-mode=default
method=auto

View →


Building Raspberry Pi images with Packer

I recently had to build a couple of Raspberry Pi images, I decided to try and set it up using Packer so the process was documented, version controlled and automatic.

I searched around for a bit, and found the project named packer-builder-arm by mkaczanowski.

Installing and running

The project is distributed through a Docker image, so it’s quite easy to get started:

$ docker run --rm --privileged -v /dev:/dev -v ${PWD}:/build mkaczanowski/packer-builder-arm:latest build install.json

The examples for the Packer code are a bit outdated inside the project, but for the newest version of Raspberry Pi OS (based on Debian Bookworm) this worked for me:

{
  "variables": {},
  "builders": [{
    "type": "arm",
    "file_urls" : ["https://downloads.raspberrypi.com/raspios_armhf/images/raspios_armhf-2023-10-10/2023-10-10-raspios-bookworm-armhf.img.xz"],
    "file_checksum_url" :
...

Continue reading →


Running and installing Ruby in parallel using Rbenv with flock and Buildkite

I am maintaining a few Docker-images that contains all supported versions of Ruby, and they also ship with Jemalloc.

Because I use Buildkite, it is very easy for me to test multiple versions at the same time, but they all run the same setup script:

echo "--- setup rbenv"
source ~/.bash_profile

echo "--- setup ruby"
rbenv local 2.7.7 || rbenv install 2.7.7 && rbenv local 2.7.7

echo "--- bundle setup"
gem query --silent --installed --exact bundler || gem install bundler

echo "--- tests"
ruby tools/test.rb 3.1

This fails for some reason when multiple agents of Buildkite run in parallel, I don’t know if Rbenv or Rubygems gives any guarantees for this sort of usage, but nevertheless it fails.

Instead of digging into the intricacies of Rbenv and Rubygems we could make sure that the code above are only being executed by one agent at a time, we can do this with flock:

exec
...

Continue reading →


Optimizing bicycling routes with OpenStreetMap and APIs

(There’s a video version of this article available here, where we also do a deep dive into the code behind it, remember to turn on subtitles):

I’ve started playing with data from OpenStreetMap (OSM). It started with me trying to fetch all the places where I could get free water when moving around Copenhagen, which turned out to be a daunting task, because OSM seems to have a lot of different ways to categorise available water. I’ve identified the following tags to look out for:

Tag Documentation amenity=drinking_water https://wiki.openstreetmap.org/wiki/Tag:amenity%3Ddrinking_water man_made=water_tap https://wiki.openstreetmap.org/wiki/Tag:man_made%3Dwater_tap amenity=water_point https://wiki.openstreetmap.org/wiki/Tag:amenity%3Dwater_point drinking_water=* https://wiki.openstreetmap.org/wiki/Key:drinking_water

Contributing back to OpenStreetMap

I soon became...

Continue reading →


How to use Haproxy redis-check with a password

It seems like option redis-check in Haproxy does not support Redis-databases that requires authentication as of 1.9.6 (newest version).

That means that if you want to check the health of password protected redis-servers, you would have to use a tcp-check, so you will have to modify your check like this:

listen redisserver
  bind *:6380
  mode tcp
  server redis_backend someredisserver:11020

  option redis-check

to:

listen redisserver
  bind *:6380
  mode tcp
  server redis_backend someredisserver:11020

  option tcp-check
  tcp-check send AUTH\ aveeeeeryseeeecretpassword\r\n
  tcp-check expect string +OK
  tcp-check send PING\r\n
  tcp-check expect string +PONG

View →


Rick-rolling people looking for free WiFi using an inexpensive ESP8266-chip

(If you are here because you were tricked, you can throw some words my way on rickroll@kaspergrubbe.com or by sending a tweet to @kaspergrubbe)

I found a project on Github named mobile-rr, by a user called idolpx. His project includes a little wifi-chip that includes a piezo buzzer, and when people connect they are promised free wifi, but instead of getting access to the internet, they are shown a video of Rick Astley singing “Never gonna give you up”.

This is a so called Rickrolling prank, and if you have been living under a rock the past decade, the prank involves bait-and-switch where a link on a webpage promises to bring the user to some unrelated material, and bam! they are now listening to the tunes of Rick Astley and they can consider themselves rickrolled.

The original project only beeped when a person was tricked, and if you wanted to know the score, you had to connect to...

Continue reading →