Test Driven Development with Tailscale

September 10, 2023

Test Driven Development with Tailscale

I have seen the light

Test Driven Development is a topic I have been petrified of for quite some time. It is something that I knew was praised as a development strategy, rightly so as many would argue. I struggled for a long time to even conceptually understand how TDD even worked. The slightly more naive version of brain simply could not cope with the idea of trying to test something before knowing something even existed. Put plainly, how could I assert 2 + 2 = 4 when I did not even have the code to add 2 and 2 together in the first place?

I helped solve this particular conundrum of mine primarily through reading. Modern Software Engineering by Dave Farley, was a great read and demonstrated both through the book and later his YouTube videos on just what TDD meant. Some amazing colleagues of mine, who live and breathe TDD, also helped show me the ropes just simply in how to sort of logically approach a test case. It’s something that also extended to what I will be talking about in this post, TDD with Tailscale ACLs. But the basic description of a test case for me now is x should be y.

Making my Tailnet more secure

If you spin up a new Tailscale account today, by default you get an Access Control List (ACL) that essentially allows all traffic to all devices. That’s well and good, I took a lot of solace from knowing that for someone to even be able to access my Tailnet, they would need to be authorised as my user. Eventually though, the thoughts start to creep in about how that’s not exactly a great idea. For starters, if a machine of mine gets breached, someone can just effectively hop to all the other machines over the Tailscale interface. Not ideal.

So I knew I had to implement an ACL properly. But in my mind this time around, properly meant writing test cases first. Does Tailscale even support such a thing in their ACLs? Why yes they do! If you browse their ACL documentation you can find there is support for a tests keyword. They also support GitOps (Some love it, some hate it. I am neutral) with a GitHub Action that is dead simple to implement, but importantly offers two commands to use in your workflow.

  • test = Only runs your tests
  • apply = Runs your tests and if they pass, updates the ACL

It ends up being quite a small workflow, I’m sure I could do more conditions to keep it even shorter but for now, its perfect.

# Note that I think Liquid relies on the same pair of curly braces that Actions uses. So just imagine in your mind the dollar sign and double pair of curly braces that signify a Secret value :D 
name: Deploy Tailscale Access Control List
on:
  workflow_call:
jobs:
  tailscale:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - if: github.ref != 'refs/heads/main'
      name: Test ACL
      id: test-acl
      uses: tailscale/gitops-acl-action@v1
      with:
        api-key: github-secret
        tailnet: github-secret
        action: test

    - if: github.ref == 'refs/heads/main'
      name: Deploy ACL
      id: deploy-acl
      uses: tailscale/gitops-acl-action@v1
      with:
        api-key: github-secret
        tailnet: github-secret
        action: apply

Approaching the first test

Once I had my Action in place, I needed to figure out how to go about writing this ACL. If there’s one thing I have learned with all this it’s perfectly fine to take as much time as you need to read and study the documentation of the tool to truly understand it, before jumping in to something like TDD that still demands respect. For example, I did not even know tags really existed before this and how they change the identity a machine uses in Tailscale.

For my tests, I took the x should be y approach when it came to how I wanted my network to look. So for example

  • My client machines should be able to talk to my server machines
  • Server machines should only be talking to any monitoring machine on the specified port
  • GitHub Actions workflows should only be talking to the Kubernetes cluster nodes on the specified port.

For an example with the first scenario, my test case looked something like the below. Forgive the formatting, the ACL is coded in JSON with Comments

"tests": [
{
  "src": "tag:client",
  "accept": ["my-lovely-server:22"]
}
]

The subsequent ACL rule looked like

"acls": [
{ "action": "accept", "src": ["tag:client"], "dst": ["my-lovely-server:22"] }
]

Everything else just flowed

Once that first test case was written, the rest just flowed. I did hit one snag with tests in an ACL where they currently do not support some edge cases, but in a way I think that helped me implement something better. The beautiful, delicious light bulb moment of “it all makes sense” came when one of my tests caught a copy paste error. In that moment I simply laughed and smiled, I knew TDD had me on the hook.

My next attempts of TDD will be with Java. Getting managed Github Actions runners connected to Tailscale is what spawned this ACL work. Now that I know it is working thanks to my tests, I should have a few less blockers from beginning the work of writing some Java apps again.

Thank you!

You could of consumed content on any website, but you went ahead and consumed my content, so I'm very grateful! If you liked this, then you might like this other piece of content I worked on.

Talking about Tailscale for the first time

Photographer

I've no real claim to fame when it comes to good photos, so it's why the header photo for this post was shot by Emile Perron . You can find some more photos from them on Unsplash. Unsplash is a great place to source photos for your website, presentation and more! But it wouldn't be anything without the photographers who put in the work.

Find Them On Unsplash

Support what I do

I write for the love and passion I have for technology. Just reading and sharing my articles is more than enough. But if you want to offer more direct support, then you can support the running costs of my website by donating via Stripe. Only do so if you feel I have truly delivered value, but as I said, your readership is more than enough already. Thank you :)

Support My Work

GitHub Profile

Visit My GitHub

LinkedIn

Connect With Me

Support my content

Support What I Do!

My CV / Resume

Download Here

Email

contact at evanday dot dev

Client Agreement

Read Here