<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feeds.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://chameth.com/">
    <title>Chameth.com - posts like blogging-and-the-imaginary-quality-bar, docker-automatic-nginx-proxy, travel-toolkit, why-you-should-be-using-https</title>
    <subtitle>Personal homepage of Chris Smith</subtitle>
    <link href="https://chameth.com/feeds/posts/like/blogging-and-the-imaginary-quality-bar,docker-automatic-nginx-proxy,travel-toolkit,why-you-should-be-using-https/" rel="self"/>
    <link href="https://chameth.com/"/>
    <icon>https://chameth.com/favicon.png</icon>
    <updated>2026-01-29T00:00:00Z</updated>
    <id>https://chameth.com/</id>
    <author>
        <name>Chris Smith</name>
    </author>
    <entry>
        <title>An interesting Tailscale + Docker gotcha</title>
        <link href="https://chameth.com/tailscale-docker-gotcha/"/>
        <updated>2026-01-29T00:00:00Z</updated>
        <id>https://chameth.com/tailscale-docker-gotcha/</id>
        <content xml:lang="en" type="html">&lt;p&gt;As I’ve &lt;a href=&#34;https://chameth.com/how-i-use-tailscale/&#34;&gt;written&lt;/a&gt; &lt;a href=&#34;https://chameth.com/avoiding-the-consequences-of-dumb-laws-with-tailscale/&#34;&gt;about&lt;/a&gt; &lt;a href=&#34;https://chameth.com/exposing-game-servers-over-tailscale/&#34;&gt;before&lt;/a&gt;, I use &lt;a href=&#34;https://tailscale.com/&#34;&gt;Tailscale&lt;/a&gt; for a lot of things. I thought I had it set up in a reasonably secure manner, but I recently noticed a problem.&lt;/p&gt;
&lt;p&gt;I use Tailscale’s ACLs to limit what each node can access, based on the tags I apply to it. So an &lt;code&gt;app&lt;/code&gt; node can’t access anything via Tailscale, while an &lt;code&gt;integration&lt;/code&gt; or &lt;code&gt;server&lt;/code&gt; node can access things tagged with either &lt;code&gt;app&lt;/code&gt; or &lt;code&gt;integration&lt;/code&gt;. This is expressed pretty simply in the Tailscale ACL JSON:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;	&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;tagOwners&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-c1&#34;&gt;// Servers that can be SSH&amp;#39;d into
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;tag:server&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-c1&#34;&gt;// Applications that are exposed on tailscale but never connect out
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;tag:app&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-c1&#34;&gt;// Things which talk to other services over tailscale (connecting to apps etc)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;tag:integration&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;	&lt;span class=&#34;chroma-p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;	&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;grants&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-c1&#34;&gt;// Users can access everything
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;autogroup:member&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;dst&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-c1&#34;&gt;// Servers and integrations can access integrations and apps
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tag:server&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tag:integration&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;dst&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tag:integration&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tag:app&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;	&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Over the past week I’ve been setting up a private &lt;a href=&#34;https://forgejo.org/&#34;&gt;Forgejo&lt;/a&gt; instance behind Tailscale, complete with an actions runner that runs things using a docker-in-docker container. I didn’t want the runner knowing anything about Tailscale, so I had it configured to speak to Forgejo direct over HTTP (&lt;code&gt;forgejo:3000&lt;/code&gt;) instead of using full Tailscale HTTPS URL that I use when accessing it (&lt;code&gt;http://git.example-net.ts.net/&lt;/code&gt;)&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Everything was going fine, until I forgot to do that translation… and it worked. My Forgejo action runners could access anything on my tailnet. I run several things on Tailscale that just have authentication turned off, on the basis that only authorised devices can access them. Things like the admin interface for this website. I definitely didn’t intend for any workflow I run on my git server to have access to edit my website!&lt;/p&gt;
&lt;p&gt;This wasn’t just limited to Forgejo, either. Any docker container I was running could access the tailnet. In hindsight it’s fairly obvious why: the host is running Tailscale, connected as a node tagged with my &lt;code&gt;server&lt;/code&gt; tag. That creates a &lt;code&gt;tailscale0&lt;/code&gt; interface, and automagically sets up iptables rules to route Tailscale traffic over the interface. Docker also automagically sets up iptables rules to bridge traffic, and apparently these two sets of rules interact in such a way that traffic from Docker containers is allowed to route via the &lt;code&gt;tailscale0&lt;/code&gt; interface.&lt;/p&gt;
&lt;p&gt;I say it’s fairly obvious in hindsight — there’s no reason why Docker would special case any particular host interface after all — but it still feels pretty surprising. Because both bits of software inject their own iptables rules, I never really had a good mental model for how they interact. The host Tailscale node was a completely separate building block to Docker. It would be a pain to use either of them if they didn’t do these rules, but it’s also one of the reasons I don’t really like “magical” things&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:2&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;There are lots of ways to fix this, but none of them feel particularly great. You can configure both Tailscale and Docker to not automatically fiddle with iptables and handle the rules yourself, but I really hate dealing with iptables&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:3&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;. As a stopgap I did hold my nose and add some iptables rules to drop traffic to the &lt;code&gt;tailscale0&lt;/code&gt; interface if it originated from the IP ranges that Docker was configured to use:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;iptables -I DOCKER-USER -s 192.168.0.0/16 -o tailscale0 -j DROP
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;iptables -I DOCKER-USER -s 172.17.0.0/12 -o tailscale0 -j DROP
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;DOCKER-USER&lt;/code&gt; chain is a nice little escape hatch; it comes before the main auto-generated &lt;code&gt;DOCKER&lt;/code&gt; chain, and Docker leaves the rules in it alone.&lt;/p&gt;
&lt;p&gt;So I added these rules, and felt pretty good about myself, and then… everything started breaking in weird ways. After some debugging I realised the problem was DNS&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:4&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;. The server’s DNS resolver is &lt;code&gt;100.100.100.100&lt;/code&gt;, a special Tailscale address. This is so that it can resolve tailnet hostnames via &lt;a href=&#34;https://tailscale.com/kb/1081/magicdns&#34;&gt;MagicDNS&lt;/a&gt; (and so I don’t have to configure my custom DNS servers manually on each device; Tailscale does it for me). My new iptables rules inadvertently dropped all the DNS packets coming from docker containers. D’oh.&lt;/p&gt;
&lt;p&gt;Obviously the solution here is to double down and add MORE iptables rules:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;iptables -I DOCKER-USER -s 192.168.0.0/16 -d 100.100.100.100 -o tailscale0 -j ACCEPT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;iptables -I DOCKER-USER -s 172.17.0.0/12 -d 100.100.100.100 -o tailscale0 -j ACCEPT
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This got DNS working again, but it all feels a bit gross. Not least because to actually make these persist you need to use &lt;code&gt;iptables-save&lt;/code&gt;, and then you also get all the rules that both Docker and Tailscale have inserted. I came across &lt;a href=&#34;https://github.com/giangi/iptables-docker-filter&#34;&gt;a script&lt;/a&gt; to filter out the Docker ones, but… yuck&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:5&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I realised a cleaner idea might just be to lock down what &lt;code&gt;server&lt;/code&gt; tagged nodes could access within Tailscale itself. That way I can avoid touching iptables at all. It’s handy to be able to &lt;code&gt;curl&lt;/code&gt; services from the host when debugging, but it’s not really necessary. So I removed that access, and… stuff broke again. I’m using my Forgejo instance as a registry for some of the docker images I run, so the docker daemon needs to be able to reach it. I ended up making a new tag for &lt;code&gt;infrastructure&lt;/code&gt;, which can be accessed from &lt;code&gt;server&lt;/code&gt; devices. This does still allow all the docker containers to reach Forgejo, but I already have it set up with appropriate access controls and public/private repository splits. Forgejo is a service designed to run publicly, so this seems a reasonable trade-off for convenience. I used the &lt;code&gt;tests&lt;/code&gt; feature of Tailscale’s ACL config to make sure I’d got the rules right:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;	&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;tests&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-c1&#34;&gt;// Servers can only access infrastructure
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt;   &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tag:server&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;proto&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;allow&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;				&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tag:infrastructure:8080&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;deny&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;				&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;100.84.16.43:8080&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;				&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;me@example.com:8080&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;				&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tag:server:8080&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;				&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tag:app:8080&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;				&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;tag:integration:8080&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;	&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The whole situation still feels a bit messy. If I ever get around to switching to nftables I might loop back and manually craft some rules for routing traffic, instead of leaving Tailscale and Docker to do their own thing.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I discovered later on that logging into the container registry at &lt;code&gt;forgejo:3000&lt;/code&gt; actually issued a redirect to &lt;code&gt;https://git.example-net.ts.net/&lt;/code&gt; so this was all basically for naught… &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;I didn’t use Tailscale’s MagicDNS for a long time just because the word “magic” put me off. Only when I eventually got around to learning how it worked, and seeing that it wasn’t really that magical under the hood, did I change my mind. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:2&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;nftables seems far better in lots of ways, but I can’t really be bothered migrating. Maybe next time I reimage the server for whatever reason… &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:3&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;It’s always DNS… &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:4&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:5&#34;&gt;
&lt;p&gt;Again, nftables would almost certainly help here. It actually has (gasp) configuration files. But again, I really didn’t want to spend the time migrating. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:5&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <title>Blogging and the Imaginary Quality Bar</title>
        <link href="https://chameth.com/blogging-and-the-imaginary-quality-bar/"/>
        <updated>2025-09-18T00:00:00Z</updated>
        <id>https://chameth.com/blogging-and-the-imaginary-quality-bar/</id>
        <content xml:lang="en" type="html">&lt;p&gt;Recently I realised that I’ve developed a self-imposed quality bar for blog
posts. They need to be a certain length, and have a certain &lt;em&gt;substance&lt;/em&gt; to them.
They need to be generally useful in some way I can’t quite define, to some
imagined future audience. They need to have images to break up the page, and
opengraph data for when they’re linked to on social media. But… maybe they
don’t? Those things all make sense for longer “article” type posts, but not so
much for a personal blog.&lt;/p&gt;
&lt;p&gt;I’ve done some fiddling so that I can make posts without all that extra stuff.
We’ll see how it goes. The posts won’t be distinguished on the site (yet?), but
I have set up separate RSS feeds for just &lt;a href=&#34;https://chameth.com/short.xml&#34;&gt;short form posts&lt;/a&gt; and
&lt;a href=&#34;https://chameth.com/long.xml&#34;&gt;long form posts&lt;/a&gt; in case subscribers are radically opposed to one
or the other. Now I can start blogging like it’s 2005 again. But hopefully
with a bit less cringe.&lt;/p&gt;
&lt;p&gt;I’ve been staring at this post in my editor for about five minutes. The urge
to make it longer, more thorough, more article-like is really strong. This
entire paragraph is only here as a compromise with myself so I can actually
save and commit the post.&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>The Curse of Knowledge and Blogging</title>
        <link href="https://chameth.com/curse-of-knowledge-blogging/"/>
        <updated>2025-05-16T00:00:00Z</updated>
        <id>https://chameth.com/curse-of-knowledge-blogging/</id>
        <content xml:lang="en" type="html">&lt;figure class=&#34;image right&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/curse-of-knowledge-blogging/ideas.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/curse-of-knowledge-blogging/ideas.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/curse-of-knowledge-blogging/ideas.jpg&#34; alt=&#34;Photo of a sign reading &amp;#34;YOUR IDEAS MATTER … Write them down :)&amp;#34;&#34; loading=&#34;lazy&#34; width=&#34;653&#34; height=&#34;463&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;Finding a picture to go with a blog post about blog post ideas is hard, OK?&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The single worst part about blogging for me is trying to come up with ideas
for what to write about. Not because they’re hard to come up with, but because
every idea seems too basic or not worth talking about. &lt;em&gt;Seems&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It’s not just me, either. My friends will occasionally describe some interesting
problem they’ve dealt with, and I’ll chime in with “you should blog about that”.
Almost invariably the response is something like “it’s not that interesting”.&lt;/p&gt;
&lt;p&gt;There’s a cognitive bias called &lt;a href=&#34;https://en.wikipedia.org/wiki/Curse_of_knowledge&#34;&gt;the curse of knowledge&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.
Basically, once you know something you can’t accurately reconstruct your previous
state of mind from when you didn’t know it. You can struggle to do something
for hours, piecing together scraps of information, using all kinds of specialist
knowledge, and as soon as you’re finished it all seems so &lt;em&gt;basic&lt;/em&gt; why would
anyone want to read about that?&lt;/p&gt;
&lt;!--more--&gt;
&lt;h3 id=&#34;other-mental-obstacles&#34;&gt;Other mental obstacles&lt;/h3&gt;
&lt;p&gt;Of course, nothing involving people is as simple as having one cause and an
obvious solution. Even when I get over the “it’s too basic” road bump, there
are other issues. Looking back over some of my posts, I get a horrible cringe-y
feeling and the question of “why would anyone be interested in reading that?!”
arrives unbidden. Even though I know that &lt;em&gt;I’m&lt;/em&gt; interested in reading similar
posts written by other people. I’m hesitant to attach labels to everything, but
&lt;a href=&#34;https://en.wikipedia.org/wiki/Impostor_syndrome&#34;&gt;imposter syndrome&lt;/a&gt; says hi.&lt;/p&gt;
&lt;p&gt;I think there’s also a cultural issue at play. British people tend to be
somewhat reserved and downplay their accomplishments, especially when compared
to people from the US&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:2&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. When I worked for a certain multinational
&lt;del&gt;ad conglomerate&lt;/del&gt; search engine company we actually had training on how to do
performance reviews and speak about ourselves more like our US counterparts.&lt;/p&gt;
&lt;p&gt;Put all these things into a cauldron, add a lizard’s leg and howlet’s wing,
mix it all up, and you get a brain that despises the idea of blogging about
anything. Great.&lt;/p&gt;
&lt;h3 id=&#34;dealing&#34;&gt;Dealing&lt;/h3&gt;
&lt;p&gt;So, now the problem is well-defined, what do we do about it? Clearly I am the
best person to be offering this advice, given it’s been four months since my
last blog post.&lt;/p&gt;
&lt;p&gt;The best counter to the Curse of Knowledge that I’ve found is keeping notes,
like a &lt;a href=&#34;https://en.wikipedia.org/wiki/Lab_notebook&#34;&gt;lab notebook&lt;/a&gt;. If you
finish something and have dozens of links to different resources that you had
to piece together, it’s probably something interesting and worth talking about
even if it doesn’t seem like it any more. Or maybe you didn’t need a lot of
references, but you had to write a lot down to make sense of something, or
needed to draw diagrams to solve a problem. Again, probably interesting.&lt;/p&gt;
&lt;p&gt;The other issues I’ve mentioned are not so easy to overcome. I’m also not going
to try and perform therapy in the form of a blog post! What I personally do is
keep a list of topics to write about (that have already passed the “probably
interesting even if my brain doesn’t agree” check), and occasionally when the
stars align I’ll feel like plucking one off the list and turning thoughts into
words.&lt;/p&gt;
&lt;h3 id=&#34;a-call-to-action&#34;&gt;A call to action&lt;/h3&gt;
&lt;p&gt;If you’ve got a blog and haven’t written anything recently because you “don’t
do anything interesting”, go write something! I promise that someone will be
interested. Hell, feel free to send me a link and &lt;em&gt;I’ll&lt;/em&gt; be interested for you.&lt;/p&gt;
&lt;p&gt;If you don’t have a blog then why not start one? There are lots of great articles
out there about why-you-should and how-you-can. And as a bonus, blog posts about
how you set up your blog are &lt;em&gt;super&lt;/em&gt; interesting. That’s one post sorted already!&lt;/p&gt;
&lt;p&gt;As the open internet is slowly subsumed by massive corporate interests, and
gummed up with spam and AI slop, the voice of real humans is becoming more and
more important. Maybe we can — one blog at a time — get back to the ’90s
internet that was full of individuality instead of today’s which is full of ads
and spam?&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://unsplash.com/@kommumikation&#34;&gt;Mika Baumeister&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/photos/a-sign-on-the-side-of-a-building-LaqL8nxiacc&#34;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;There’s also something called “Familiarity Blindness” that people like to
talk about. It’s a much better name. The only problem is that I can’t find any
actual published works that talk about it. No-one authoritative defines or
mentions it. It just popped into existence a few years ago. Maybe it’s an LLM
hallucination? &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;My favourite commentary on this is &lt;a href=&#34;https://www.youtube.com/watch?v=8k2AbqTBxao&#34;&gt;Stephen Fry talking about American vs British comedy&lt;/a&gt;. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:2&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <title>Building a travel toolkit</title>
        <link href="https://chameth.com/travel-toolkit/"/>
        <updated>2025-01-02T00:00:00Z</updated>
        <id>https://chameth.com/travel-toolkit/</id>
        <content xml:lang="en" type="html">&lt;figure class=&#34;image left&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/travel-toolkit/bag.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/travel-toolkit/bag.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/travel-toolkit/bag.png&#34; alt=&#34;UGREEN Travel Accessories Bag&#34; loading=&#34;lazy&#34; width=&#34;365&#34; height=&#34;578&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The container for my toolkit, a UGREEN “travel accessories” bag&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For a while now, whenever I’ve traveled anywhere I’ve dumped a selection of tools
into my backpack just in case I need them. Originally this was mostly focused
around being able to open and fix my laptop if anything went wrong, but
over time has slowly expanded to include other useful tools.&lt;/p&gt;
&lt;p&gt;Repeatedly packing these tools and then returning them to where they belonged
was tedious, though. I couldn’t just leave them in the backpack as I often
needed them for non-travelling purposes, and I didn’t want to just buy duplicate
tools that spent most of their life a few meters away from each other, so I
ignored the problem and just dealt with the tedium.&lt;/p&gt;
&lt;p&gt;Eventually I acquired enough tools that — while not quite duplicates — I
wouldn’t miss often if they were kept in the backpack. Building on these I’ve
created a little travel toolkit that contains everything I might want (and
more) while away.&lt;/p&gt;
&lt;h3 id=&#34;the-base&#34;&gt;The base&lt;/h3&gt;
&lt;p&gt;The biggest challenge I had before was finding a screwdriver set. I have an
&lt;a href=&#34;https://www.ifixit.com/products/mako-driver-kit-64-precision-bits&#34;&gt;iFixit Mako&lt;/a&gt;
set that did the in-and-out-of-the-bag routine for several years. Nothing else
covered the range of drivers I wanted for generic electronic tinkering and
more chunky work. I finally found one in the form of a
&lt;a href=&#34;https://toolrebels.de/en/products/wera-tool-rebel-tool-check-plus-tr-edition&#34;&gt;Wera Tool-Check PLUS TR-Edition&lt;/a&gt;.
“TR” stands for “Tool Rebel” and is Wera’s “fan club” brand. Its signature
features over the normal Tool-Check PLUS are a fancy fabric case and a 152mm
bit extender; but it also comes with a much different set of bits, which is
why I picked it up.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Here’s a comparison&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Standard version&lt;/th&gt;
&lt;th&gt;Tool Rebel edition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Phillips&lt;/td&gt;
&lt;td&gt;2x PH1&lt;br/&gt;3x PH2&lt;br/&gt;1x PH3&lt;/td&gt;
&lt;td&gt;1x PH00&lt;br/&gt;1x PH0&lt;br/&gt;1x PH1&lt;br/&gt;1x PH2&lt;br/&gt;1x PH3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pozidrive&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1x PZ1&lt;br/&gt;3x PZ2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slotted&lt;/td&gt;
&lt;td&gt;1x 5.5mm&lt;/td&gt;
&lt;td&gt;1x 4mm&lt;br/&gt;1x 4.5mm&lt;br/&gt;2x 5.5mm&lt;br/&gt;1x 6.5mm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Torx&lt;/td&gt;
&lt;td&gt;1x T10&lt;br/&gt;1x T15&lt;br/&gt;1x T20&lt;br/&gt;1x T25&lt;br/&gt;1x T30&lt;br/&gt;1x T40&lt;/td&gt;
&lt;td&gt;1x T10&lt;br/&gt;1x T15&lt;br/&gt;1x T20&lt;br/&gt;1x T25&lt;br/&gt;1x T30&lt;br/&gt;1x T40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security Torx&lt;/td&gt;
&lt;td&gt;1x T10&lt;br/&gt;1x T15&lt;br/&gt;1x T20&lt;br/&gt;1x T25&lt;br/&gt;1x T30&lt;/td&gt;
&lt;td&gt;1x T25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hex&lt;/td&gt;
&lt;td&gt;1x H3&lt;br/&gt;1x H4&lt;br/&gt;1x H5&lt;br/&gt;1x H6&lt;br/&gt;1x H8&lt;/td&gt;
&lt;td&gt;1x H1.5&lt;br/&gt;1x H2&lt;br/&gt;1x H2.5&lt;br/&gt;1x H3&lt;br/&gt;1x H4&lt;br/&gt;1x H6&lt;br/&gt;1x H8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There are only five bits present in the standard version that are missing in
the TR edition: 5mm hex (pretty uncommon) and four sizes of security torx (rare).
In exchange for those and the removal of a bunch of duplicates, you get two extra
phillips sizes (0 and 00, both very useful for electronics and spectacles),
some pozidrive bits (extremely common on random hardware/furniture/etc),
smaller hex bits (for electronics, 3D printers, and so on), and a few other
bits and pieces.&lt;/p&gt;
&lt;p&gt;Overall it’s a vastly superior selection for my needs. And while it doesn’t have
the full range of bits provided by the iFixit kit, it does cover almost
everything I’m ever actually likely to need while travelling, and it’s probably
around a third of the size.&lt;/p&gt;
&lt;p&gt;The other core part of the toolkit is the bag. I had a UGREEN “travel accessory”
bag hanging around unused, and noticed it was almost the exact right size for
the Tool-Check. Now, how many other tools could I stuff in there?&lt;/p&gt;
&lt;h3 id=&#34;all-the-tools&#34;&gt;All the tools&lt;/h3&gt;
&lt;figure class=&#34;image full&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/travel-toolkit/contents.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/travel-toolkit/contents.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/travel-toolkit/contents.jpg&#34; alt=&#34;The contents of my toolkit, spread out on a mat&#34; loading=&#34;lazy&#34; width=&#34;1024&#34; height=&#34;912&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The array of tools that make up the travel toolkit&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The kit can be broken down into a few sections:&lt;/p&gt;
&lt;h4 id=&#34;screwdriving&#34;&gt;Screwdriving&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The aforementioned Wera Tool-Check&lt;/li&gt;
&lt;li&gt;Its 152mm bit extender&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ve talked enough about this already, I think, so I’ll spare you any additional
commentary on screwdrivers and bits.&lt;/p&gt;
&lt;h4 id=&#34;splitting-gripping-and-cutting&#34;&gt;Splitting, gripping and cutting&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Engineer tweezers&lt;/li&gt;
&lt;li&gt;Engineer mini snips&lt;/li&gt;
&lt;li&gt;Engineer mini screw removal pliers&lt;/li&gt;
&lt;li&gt;Knipex mini waterpump pliers&lt;/li&gt;
&lt;li&gt;Two iFixit spudgers (one metal, one plastic)&lt;/li&gt;
&lt;li&gt;iFixit jimmy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have bigger or slightly different versions of these that stay in my normal
toolkit. The tweezers and snips are useful in a variety of situations,
and the spudgers are handy for getting inside laptops and other devices. The
pliers are less useful, but they’re one of those things where when you &lt;em&gt;do&lt;/em&gt;
need them there aren’t any good alternatives.&lt;/p&gt;
&lt;h4 id=&#34;sewing&#34;&gt;Sewing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Two needles&lt;/li&gt;
&lt;li&gt;Some black thread&lt;/li&gt;
&lt;li&gt;Set of mini clips&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You never know when you’re going to rip a seam in something. I find the clips a
lot less fiddly than using pins for keeping material in place when I’m trying to
sew.&lt;/p&gt;
&lt;h4 id=&#34;computering&#34;&gt;Computering&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Retractable ethernet cable&lt;/li&gt;
&lt;li&gt;RJ45 coupler&lt;/li&gt;
&lt;li&gt;USB-C to RJ45 adapter&lt;/li&gt;
&lt;li&gt;Dual USB-A and USB-C thumb drive, with an Arch ISO on it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes Wi-Fi just doesn’t cut it, or you need to fiddle with a router that
doesn’t expose its admin UI over Wi-Fi. The USB-C adapter is for use with my
phone, as it’s often easier to move that get the laptop where it needs to be.&lt;/p&gt;
&lt;p&gt;I can boot from the thumb drive into an Arch environment, and from there
reinstall my laptop from scratch if needed. There’s enough space left over so
it can be used to transfer files as well.&lt;/p&gt;
&lt;h4 id=&#34;miscellaneousing&#34;&gt;Miscellaneousing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Fabric tape measure&lt;/li&gt;
&lt;li&gt;Spirit level&lt;/li&gt;
&lt;li&gt;Sharpie&lt;/li&gt;
&lt;li&gt;Superglue&lt;/li&gt;
&lt;li&gt;Spare batteries: 2 CR2032s, 2 AA, 2 AAA&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m almost certain I won’t use the spirit level, but it’s tiny and I had a spare
so it went in. Everything else is just in the “generally handy to have”
category.&lt;/p&gt;
&lt;h3 id=&#34;putting-it-all-together&#34;&gt;Putting it all together&lt;/h3&gt;
&lt;figure class=&#34;image right&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/travel-toolkit/complete.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/travel-toolkit/complete.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/travel-toolkit/complete.png&#34; alt=&#34;The complete toolkit&#34; loading=&#34;lazy&#34; width=&#34;500&#34; height=&#34;506&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The nicely-organised side of the toolkit&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The Tool-Check PLUS fits almost perfectly into one side of the UGREEN bag,
leaving a little wiggle room along the side for the longer parts like spudgers.
The middle pockets can’t fit much in, but keep the batteries and sewing bits
out of the way. Everything else is in a big zip-up compartment in the other
side, which is slightly messy but still easy enough to dig through to find
whatever I need.&lt;/p&gt;
&lt;p&gt;The complete kit weighs in at around 1.2KG, so it’s not exactly lightweight,
but it goes in a bag with a heavy laptop and all of its peripherals so it’s
not as noticeable as it might otherwise be.&lt;/p&gt;
&lt;p&gt;It remains to be seen how much use I’ll actually get out of any of this. Maybe
I’ll put a bit of paper in so that I can track how often I use each tool…&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Docker reverse proxying, redux</title>
        <link href="https://chameth.com/docker-proxying-redux/"/>
        <updated>2022-12-06T00:00:00Z</updated>
        <id>https://chameth.com/docker-proxying-redux/</id>
        <content xml:lang="en" type="html">&lt;p&gt;Six years ago, &lt;a href=&#34;https://chameth.com/docker-automatic-nginx-proxy/&#34;&gt;I described&lt;/a&gt;
my system for configuring a reverse proxy for docker containers.
It involved six containers including a key-value store and a webserver.
Nothing in that system has persisted to this day. Don’t get me wrong – it
worked – but there were a lot of rough edges and areas for improvement.&lt;/p&gt;
&lt;h3 id=&#34;microservices-and-their-limitations&#34;&gt;Microservices and their limitations&lt;/h3&gt;
&lt;p&gt;My goal was to follow the UNIX philosophy of “do one thing and do it well”.
Unfortunately, that doesn’t really work when applied to network services that
have to interact with one and other. UNIX tools are built upon a common file
system and simple data passed over STDIN. Microservices don’t have that
shared foundation. You could make one:
companies that use microservices in anger often have a team that deals with
the “developer experience” of creating and using
microservices. But as a solo developer that’s not something I wanted to
spend my time doing.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;This became very apparent when trying to debug issues. In the UNIX world,
if your series of commands piped together does something strange you can simply
echo the data at various stages. Not so much when that data is flying around
on a network, going into and out of things like etcd. Trying to figure out why
a certificate hadn’t been acquired was a case of searching through logs from
four containers, none of which had particularly good logging.
There are many ways to get insight into what’s
happening with microservices but, again, that’s not something I wanted to do myself.&lt;/p&gt;
&lt;p&gt;Over time, and with experience in other projects, I came to realise that
microservices only really make sense if you’re unable to deploy a monolith.
For tech companies this naturally happens when different teams
contribute to the same service: splitting it out into smaller services that
are wholly owned by individual teams makes sense. For solo developers,
that never happens. You can still gain the other benefits
of microservices – such as code separation and having clearly defined APIs –
by sticking to certain coding standards.&lt;/p&gt;
&lt;h3 id=&#34;proxy-inconveniences&#34;&gt;Proxy inconveniences&lt;/h3&gt;
&lt;p&gt;As well as being unhappy with the microservice nature of the solution,
I wasn’t pleased with nginx. If you requested an unknown domain, nginx
would use the first server block in its config to serve a response, instead
of sending an “unrecognised name” alert as I wanted. It was a minor issue, but
it irked me.&lt;/p&gt;
&lt;p&gt;So from nginx I switched to haproxy. It has a &lt;code&gt;strict-sni&lt;/code&gt; option when configuring
TLS connections which makes it behave properly. It also performs a lot better for
this type of workload than nginx. All was well for a while, but then I started getting alerts
that requests were occasionally failing. I couldn’t reproduce the issue, but
my nightly jobs to build and push containers managed to hit it nearly every
night, causing them to fail.&lt;/p&gt;
&lt;p&gt;After some investigation, I found that the haproxy developers had refactored
the header parsing code, and
neglected to properly reset flags when multiple requests were sent over the same
connection. There was a patch, but it wasn’t released. No problem, I thought,
I’ll just cherry-pick it onto the last release… Except that haproxy use
Git in the most convoluted manner I’ve ever seen – they have one
repository per release. This makes it harder to patch, but it also made me question
whether I trusted them to ship stable software: there were no tests for
the header parsing code (which is both fundamental and finicky,
the perfect target for tests), the source code management was weird, and they didn’t
seem in any rush to patch this bug.&lt;/p&gt;
&lt;p&gt;Not long after that issue, &lt;a href=&#34;https://greg.holmes.name/&#34;&gt;Greg&lt;/a&gt; managed to
encounter another bug where haproxy returned a 500 error whenever the
upstream server replied with a particular, perfectly valid, header.
The die was cast – it was time to move to something else.&lt;/p&gt;
&lt;h3 id=&#34;not-invented-here-syndrome&#34;&gt;Not Invented Here syndrome&lt;/h3&gt;
&lt;p&gt;Looking for a new solution, there were many more options than
back in 2016. I’m still convinced, however, that anything
exposed to the Internet should not have access to run docker containers.
It’s the modern equivalent of running a CGI script as root. That
single requirement eliminates most off-the-shelf solutions.
What do you do when nothing quite meets
your specific requirements? You make something yourself! My new solution has two
components: &lt;a href=&#34;https://github.com/csmith/dotege&#34;&gt;Dotege&lt;/a&gt; and
&lt;a href=&#34;https://github.com/csmith/centauri&#34;&gt;Centauri&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dotege is a replacement for the
microservices that monitored containers and obtained certificates. It’s fundamentally
a templating engine - whenever the containers change, it evaluates a
template and saves the result to disk. The template has access to details about
the containers, their labels, ports, and so on. Dotege can also obtain certificates
from Let’s Encrypt, and raise a signal against another
process whenever the template or certificates change. I used this to generate
the configuration and certificates used by haproxy for a while, and more recently
changed the template so that it works for Centauri.&lt;/p&gt;
&lt;p&gt;Centauri is my own reverse proxy. It’s configured using a simple text file
and can also obtain certificates from an ACME provider. It doesn’t serve static
content, has no knowledge about docker, and avoids the other bells and
whistles that adorn most reverse proxies. It also has good test
coverage to ensure that I don’t, say, accidentally break header parsing.&lt;/p&gt;
&lt;p&gt;As a software engineer I enjoy writing software, but I also enjoy running
simple, easy to understand software. That’s what I’ve achieved here: it’s
very easy to identify where the problem is if anything goes wrong, both are small
Go programs rather than vast sprawling C
monstrosities, and their interaction is primarily through a file written to disk
that can be inspected or edited as needed.&lt;/p&gt;
</content>
    </entry>
</feed>
