Caius Theory

Now with even more cowbell…

Raspberry Pi 3 as an emergency router

Given a dead router, how do you get back online whilst you wait for the replacement part to arrive? Grab a Raspberry Pi 3 off the shelf, along with a USB to Ethernet adapter and hey presto the internet works again.

This is with a fibre modem (FTTC), using PPPoE to connect out. Plug the modem (WAN) into the RPi's ethernet port, and plug the LAN switch into the USB adapter.

First thing is to get the WAN link working, get it talking PPPoE to the ISP. Usually this will be configured in /etc/ppp/pppoe.conf (depends on your linux distro). (That'll require your username/password for your ISP usually too.)

Get it up & connected, and make sure you can ping the internet from the RPi. Then it's time to get the LAN working. Give it a static IP in the range you want shared out.

# /etc/network/interfaces
iface eth0 inet static
  address 192.168.1.1
  netmask 255.255.255.0
  gateway 192.168.1.1

auto eth1
iface eth1 inet dhcp

Get a dhcp server running on the LAN connection,

# /etc/dhcpcd.conf
interface eth0
static ip_address=192.168.1.1
static routers=192.168.1.1
static domain_name_servers=8.8.8.8,8.8.4.4

And then it's time to handle WAN -> LAN traffic and the reverse. Make sure you have packet forwarding enabled, and then setup the firewall to handle NAT and also keep out undesirable traffic.

sysctl net.ipv4.ip_forward=1

iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -i eth0 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type any -j ACCEPT
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
iptables -A INPUT -f -j DROP
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
iptables -A INPUT -j DROP

Hey presto, you have a working emergency router. In testing I found my fibre connection (80/20Mb) was slower than the traffic the RPi could push, so didn't notice any difference vs my normal router. (Although I did disable a bunch of automated stuff, so there was less contention on the WAN link.)

Finding cheap Microserver G8 memory

I've been wanting to drop more memory in my HP Microserver G8, but hoping to find a cheaper alternative to buying new sticks from Crucial. I needed one or two 4GB sticks, but they had to be ECC of course.

At the time of writing (May 2017), Crucial's offering for the G8 shows a 4GB stick to be £43.19, and an 8GB stick to be £81.59. This was a little more than I wanted to pay, but I was struggling to find anything on eBay or Amazon UK that I could be sure was ECC, and also cheaper.

Eventually I wondered what else had compatible memory, after all this isn't a bespoke machine. It should share the same memory specifications as plenty of other machines. The spec I was looking for was:

After a little while of searching, I happened to find the previous Mac Pro (ie. the tower, not the trashcan) also uses that specification of memory. One quick search on eBay and up turned someone selling off his 4GB sticks where he'd upgrade his Mac Pro to 8GB sticks across the board. £29 for 2x 4GB sticks is better than I was hoping for, and once fitted in the Microserver they work flawlessly.

(The onboard management software warns me that some processor features are disabled because I'm not using HP Approved memory, but it also logged that warning when I was using HP Approved memory previously and the machine worked perfectly then. No doubt it's to make IT Managers who don't like warnings spend more money with HP.)

Bash script setup

Recently I've been writing a bunch of bash scripts for various things. As some up-front safety checks I've taken to opening every script with the following:

#!/usr/bin/env bash

[[ "$TRACE" ]] && set -o xtrace
set -o errexit
set -o nounset
set -o pipefail
set -o noclobber

Other things I'm also trying to be good about doing:

And some useful reading I ran across in my quest to level up bash-scripts:

BMW 328i crankshaft sensor issue

My 1996 e36 BMW 328i Convertible was having trouble starting, and it's always broken up slightly at idle since I took ownership of the car. On a couple of hot days last summer it also stalled during idle, then started doing it again recently (even though the ambient temperature was much colder.)

The main sympton at the point I took notice was it struggling to start, then hunting at idle until warm, then promptly stalling when warm and idling. It would also cut out when coming to a stop at junctions.

Reading the codes showed one related to the crankshaft sensor:

53 Crankshaft Sensor

(Codes can also show up in hex, which would be "83 Crankshaft Sensor".)

I duly replaced both the Crankshaft Sensor (Part # 12141703277) and the Camshaft Sensor (Part # 12141703221), but it was still throwing the crankshaft sensor code.

Upon closer inspection, the sensor wire in the plug for the crankshaft sensor wasn't pushed fully into the socket under the intake manifold. This lead to the ECU not being able to get a signal from the sensor, so it quite correctly threw a code and didn't run correctly.

Made sure all the pins were seated in the plug correctly, and reconnected the plug under the intake manifold and she started up perfectly. Drives much better, and the engine pulls more smoothly all the way up the rev range.

Stop HealthKit causing SIGABRT

You have some crazy idea for an iOS app that uses HealthKit so you fire up Xcode, create a new project & add the HealthKit entitlement. Follow the tutorial to request authorization from the HKHealthKitStore. Hit run to make sure the app compiles and find that it instantly crashes with a SIGABRT in AppDelegate.

Puzzled by this you go over the minimal amount of code you've added and pare it right down to just the HKHealthKitStore.requestAuthorization call which is still causing the SIGABRT as soon as the app tries to boot.

The missing piece of the puzzle is Info.plist needs a key adding to it for the HealthKit authorisation screen. The documentation helpfully forgets to mention this however. Here's some quick simple steps to fix it:

  1. Open Info.plist in Xcode
  2. Click the (+) at the top to add a new key/value to the file
  3. Enter "Privacy - Health Share Usage Description" for the key
  4. Enter a useful message to the user explaining why they should allow access to their healthkit data for your app for the value
  5. Run your app and see the HealthKit authorisation sheet appear

NB: if you want to update/write any data to healthkit, you'll need to add the "Privacy - Health Update Usage Description" key with a description as well.

Remove OS X Disk Password

I recently reinstalled a laptop and in doing so setup full disk encryption in a slightly strange fashion. The basic flow I followed was:

  1. Boot into Recovery mode (hold ⌘-R at boot)
  2. Erase the internal HD as HFS+ (Journaled, Encrypted) and set a disk password
  3. Install OS X onto the internal disk
  4. During setup, use Migration Assistant to copy clone containing previous install data from backup disk

This worked great in the end, once I'd recompiling various utilities I had installed. (Downside of moving from one CPU arch to another - can't just copy all compiled binaries over.)

However, I failed at step 2 above and entered "password" as my disk password since it was only intended to be temporary. Usually OS X's full disk encryption (FileVault 2) allows the machine users to unlock the disk, and not a standalone password. Due to the slightly odd way I setup the machine, I had the option of either using the disk password or my user account's password.

Having hunted around trying to find how to change or remove this disk password and leave only my users password, I finally stumbled across the magic incantations in an apple discussion thread asking How to disable "Disk Password" on boot?.

The magic incantations are as follows:

  1. List all the passwords that can currently unlock the drive

    Make sure there is a second password listed or removing the disk password will lock you out of the disk.

     $ sudo fdesetup list -extended
     ESCROW  UUID                                                    TYPE USER
             28376DDE-B6E1-48BE-A06F-4212067581D6    Disk Passphrase User
             4DBF8CEF-40F7-4F00-902F-A47AA643C656                 OS User caius
    
  2. Note the UUID of the "Disk Passphrase" entry, and remove that from the list

     sudo fdesetup remove -uuid 28376DDE-B6E1-48BE-A06F-4212067581D6
    
  3. List the passwords again to make sure the Disk Passphrase entry was removed

     $ sudo fdesetup list -extended
     ESCROW  UUID                                                    TYPE USER
             4DBF8CEF-40F7-4F00-902F-A47AA643C656                 OS User caius
    

Hey presto, only your user is left being able to unlock the disk.

Find dependencies blocking rails upgrades

The initial pain point when upgrading a rails app is figuring out which of your dependencies are blocking you upgrading the actual rails gem (& immediate dependencies, actionpack, etc.). One way to start this is to update the rails dependency in your Gemfile and run bundle update rails. Then check the error output (it never works first time…) to see which gems are blocking the upgrade. Repeat, rinse until it works.

I figured I'd cheat a little and eyeball the Gemfile.lock to see which gems had an explicit dependency pinning rails (or actionpack, activejob, etc) to a version lower than I want to upgrade to, so I could get an idea of what needs to be upgraded without having to do them all one-by-one.

Then instead of eyeballing Gemfile.lock, I wrote an awk script to pull out the interesting dependencies (ie, anything that depends on rails gems) so I just have to check which versions they depend on by hand.

# Reads a Gemfile.lock and outputs all dependencies that depend on rails

BEGIN {
  parent = 0
  parent_printed = 0
  rails_gems = "^(rail(s|ties)|action(mailer|pack|view)|active(job|model|record|support))$"
}

# We only want the specs from the GEM section
NR == 1, $1 ~ /GEM/ { next }
$1 == "" { exit }

# Skip parent gems we don't care about (rails itself…)
$0 ~ /^ {4}[^ ]/ &&
$1 ~ rails_gems {
  parent = 0
  parent_printed = 0
  next
}

# Parent gems that aren't part of rails core
# Store the name to be printed if we match below
$0 ~ /^ {4}[^ ]/ {
  parent = $0
  parent_printed = 0
  next
}

# If the nested gem (6 space prefix) matches rails-names and we have a parent value
# set then we print them out - making sure to only print the parent once
$0 ~ /^ {6}[^ ]/ &&
$1 ~ rails_gems &&
parent != 0 {
  if (parent_printed == 0) {
    parent_printed = 1
    print parent
  }

  print $0
}

Run it against your Gemfile.lock for the app you're upgrading:

awk -f rails5.awk Gemfile.lock

And you'll get output like this, to run through and see if any of the dependencies are pinning to lower versions than you need.

    coffee-rails (4.0.1)
      railties (>= 4.0.0, < 5.0)
    factory_girl (4.4.0)
      activesupport (>= 3.0.0)
    factory_girl_rails (4.4.1)
      railties (>= 3.0.0)
    globalid (0.3.7)
      activesupport (>= 4.1.0)
    google-api-client (0.8.6)
      activesupport (>= 3.2)
    jquery-rails (3.1.4)
      railties (>= 3.0, < 5.0)
    jquery-ui-rails (5.0.5)
      railties (>= 3.2.16)
    rails-deprecated_sanitizer (1.0.3)
      activesupport (>= 4.2.0.alpha)
    rails-dom-testing (1.0.7)
      activesupport (>= 4.2.0.beta, < 5.0)
    rspec-rails (3.4.2)
      actionpack (>= 3.0, < 4.3)
      activesupport (>= 3.0, < 4.3)
      railties (>= 3.0, < 4.3)
    sass-rails (4.0.5)
      railties (>= 4.0.0, < 5.0)
    sprockets-rails (2.3.3)
      actionpack (>= 3.0)
      activesupport (>= 3.0)

In this case, I'm trying to take this app to rails 5.0, so all the ones specifying < 5 and < 4.3 need upgrading beforehand.

SmartOS Recovery mount /usbkey

Recently I managed to hose a box in a perfectly self-inflicted storm of idiocy. Imagine a SmartOS server with the following issues:

Needless to say, this caused a tiny issue in the server doing what it's supposed to. Luckily I had access to a KVM remote console for the box and the following worked.

I brought the machine up, choosing the second option for recovery at the grub menu. Waited for a login prompt, then logged in with root/root.

Realised quite quickly that /usbkey must be persisted on the zones zfs pool otherwise the configuration would be lost after shutdown, so imported the correct pool, created a directory to mount into and then mounted the zfs share.

zpool import zones
mkdir /usbkey
mount -F zfs zones/usbkey /usbkey

Much Wenlock Triathlon 2016

500m Pool Swim: 00:11:34

Felt good setting off, within a couple of lengths felt like I had no energy. Didn’t have enough to eat/drink during the morning before starting, really makes a huge difference once you set off. Must remember to replace my goggles at some point too, the strap comes loose without warning.

Transition 1: 00:03:06

Rough time for transition. Legs felt good as soon as I was out the pool. Got my kit on without too much trouble, although taking a t-shirt style cycling top instead of a full zip jacket was a mistake—forgot I’d be putting it on wet. Had a gel in transition in the hope it would give me some energy.

19km Cycle: 00:48:03

Lovely start to the cycle route, massive downhill on recently resurfaced roads for the most part. Best part of the cycle ride, with the rest of the route consisting of climbing back up to the start. Couple of nasty steep hills I had to walk sections of, for all I fitted lower gears they still weren't low enough given my lack of bike fitness currently.

Transition 2: 00:06:57

Bike racked easily, shoes swapped, gel necked and a couple of Lucozade bottles clutched and off to run. Took on another gel.

7km Run: 01:04:37

Needed all the fluid I carried, didn't take anything else during the run and felt no less energetic than the other disciplines. Legs didn't really hurt, just felt like lead and had no power. Route was basically a mini trail run then up a tarmac road to the turning point then head back via trail run to finish. Adopted a walk/run approach.

Total: 02:14:17

Certainly the hardest triathlon I've done, and I'm probably at my least fittest compared to any of the others I've done to boot. The location was lovely, the weather was pretty decent (sunny but not too hot). Don't feel like I was beaten by it, but definitely haven't given it my best. One to redo next year and train towards.

Setup DHCP interface in FreeBSD

Given a FreeBSD instance without a configured network interface that you'd like to configure, first check what the name of the interface you want to configure is with ifconfig. (Mine is em0 in this instance.)

Then we need to add the configuration telling services that we want to use DHCP for this interface, and setting up our default router (use your IP, not mine!) too:

cat >> rc.conf <<CONF
ifconfig_em0="DHCP"
default_router="192.168.1.1"
CONF

And then we need to start dhclient on the given interface:

service dhclient start em0

Hey presto, you should see dhclient finding a DHCP server and being handed an IP address for em0.