<?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 apple-google-aligned-incentives, artisanal-docker-images, debugging-beyond-the-debugger but not docker-automatic-nginx-proxy, migrating-from-github-to-forgejo, why-you-should-be-using-https</title>
    <subtitle>Personal homepage of Chris Smith</subtitle>
    <link href="https://chameth.com/feeds/posts/like/apple-google-aligned-incentives,artisanal-docker-images,debugging-beyond-the-debugger/unlike/docker-automatic-nginx-proxy,migrating-from-github-to-forgejo,why-you-should-be-using-https/" rel="self"/>
    <link href="https://chameth.com/"/>
    <icon>https://chameth.com/favicon.png</icon>
    <updated>2025-07-16T00:00:00Z</updated>
    <id>https://chameth.com/</id>
    <author>
        <name>Chris Smith</name>
    </author>
    <entry>
        <title>How tech companies failed to build the Star Trek computer</title>
        <link href="https://chameth.com/how-tech-companies-failed-to-build-the-star-trek-computer/"/>
        <updated>2025-07-16T00:00:00Z</updated>
        <id>https://chameth.com/how-tech-companies-failed-to-build-the-star-trek-computer/</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/how-tech-companies-failed-to-build-the-star-trek-computer/enterprise-computer-room.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/how-tech-companies-failed-to-build-the-star-trek-computer/enterprise-computer-room.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/how-tech-companies-failed-to-build-the-star-trek-computer/enterprise-computer-room.jpg&#34; alt=&#34;Still from an episode of Star Trek: The Next Generation, with various characters stood around in a computer core room&#34; loading=&#34;lazy&#34; width=&#34;500&#34; height=&#34;376&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;A computer core room on the Enterprise-D&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In most Star Trek series, the ship or station computer is ever-present in the
background, waiting to be called on by the main characters&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;. It nearly
always does exactly the right thing, and there’s little limit to the functions
it can perform. Take this mundane example from DS9:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;KIRA: Computer, establish link with the Bajoran Medical Index for the Northwestern District. &lt;br/&gt;
COMPUTER: Link established. &lt;br/&gt;
KIRA: Access all information on Doctor Surmak Ren. &lt;br/&gt;
COMPUTER: There are no records matching that name. &lt;br/&gt;
KIRA: Try the Northeastern District, same search. &lt;br/&gt;
COMPUTER: Doctor Surmak Ren, currently serving as Chief Administrator of the Ilvian Medical Complex. &lt;br/&gt;
KIRA: Computer, open a channel to the Ilvian Medical Complex. Administrator’s office.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The computer is doing some kind of networking to a database only identified by
name. It does a search and summarises the lack of results. It then repeats the
process with another database, and succinctly announces the results. Finally,
it opens a communication channel to a specific room in a facility, based only
on its name.&lt;/p&gt;
&lt;p&gt;This whole interaction is remarkably boring&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;. Kira doesn’t have to know
any URLs or API endpoints, or what protocol she wants to use. She doesn’t have
to open a specific app and then login and then try the query again. She just
says what she wants and the computer does it.&lt;/p&gt;
&lt;p&gt;It seems like this should be one of the most easily obtainable bits of sci-fi
wizardry with our current technology. We have multiple massive companies
throwing lots of money at digital assistants, LLMs that are improving at an
insane rate, but we’re somehow not even close to the usability or usefulness of
the Trek computers. What gives?&lt;/p&gt;
&lt;h3 id=&#34;boring-is-well-boring&#34;&gt;Boring is, well, boring.&lt;/h3&gt;
&lt;p&gt;Larry Page once said something that might help explain it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Star Trek computer doesn’t seem that interesting. They ask it random
questions, it thinks for a while. I think we can do better than that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the same Larry Page that founded Google, whose mission statement is
“to organize the world’s information and make it universally accessible and
useful”. Of all people, surely he should find an omnipresent computer that can
answer ‘random questions’ interesting?! It seems like it should be the epitome
of Google’s mission!&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Google’s “better than that” seems to have been to stuff LLMs into every product
they can, even when you don’t want them there. Even when they’re worse than the
normal content they displace. These things look &lt;em&gt;exciting&lt;/em&gt; when they’re part of
a scripted demo at Google I/O, but they fall flat and just get in the way when
they’re exposed to the reality of day-to-day use.&lt;/p&gt;
&lt;p&gt;The Star Trek computer is the opposite: it isn’t snazzy, but it is genuinely
useful. That means it’s not an attractive target for the company execs who want
marketing opportunities, and it’s not appealing for engineers who need to
demonstrate “impact”. But even if Google did try to make the Trek computer,
there are other problems…&lt;/p&gt;
&lt;h3 id=&#34;assistants-need-to-be-free&#34;&gt;Assistants need to be free&lt;/h3&gt;
&lt;p&gt;A significant amount of tech companies’ business models currently revolves
around trapping users in walled gardens. They want you using &lt;em&gt;their&lt;/em&gt; ecosystem;
that way they get more data from you, and you’re more likely to spend more money
on their other offerings that work together. There’s barely any incentive to
allow any kind of interoperability with other platforms outside carefully
contracted integrations.&lt;/p&gt;
&lt;p&gt;I remember trying to help a family member move their photos from iCloud to
Google Photos. At one point they turned around and said, exasperated, “why is
this so hard? Aren’t they both in the cloud?!”. It’s easy to dismiss that as
someone who hasn’t quite grasped the fundamental idea that “the cloud” is just
someone else’s computers, but that’s not the whole story. There’s no reason why
there shouldn’t be a quick and easy transfer: both services already allow
uploading and downloading, there’s just no incentive for the companies involved
to make it so&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;.&lt;/p&gt;
&lt;p&gt;These kinds of misaligned incentives and walled garden business models cause
even more problems when it comes to digital assistants. Siri is basically never
going to be able to interact with, say, your Google Drive; &lt;del&gt;Bard&lt;/del&gt; Gemini
is never going to be able to send a message via iMessage. Even when there are
appropriately blessed interactions, they’re so clunky. Can you imagine Captain
Picard saying “Computer, ask the turbolift skill to take me to deck 5”?&lt;/p&gt;
&lt;h3 id=&#34;someone-elses-computer&#34;&gt;Someone else’s computer&lt;/h3&gt;
&lt;p&gt;Software issues aside, there’s still a key difference between the Star Trek
computers and our current batch of digital assistants: where they run. The
Trek computers are all housed within the ship or station they serve; they can
connect elsewhere to gather information, but they run entirely independently.
If they go wrong, a local engineer can go in and fix things. While some of our
assistants may have physical hardware in your home, they don’t work without
a vast cloud apparatus behind them. If your Internet connection fails, they
become paperweights. If the company running them decide to remove some
functionality you depend on, you have no recourse.&lt;/p&gt;
&lt;p&gt;That kind of helplessness isn’t limited to assistants, either. There’s a rapidly
growing trend of being unable to modify or repair hardware you fully own and
control. Part of this is just that they’re becoming more complex: it’s a lot
harder to replace a microchip than a gear, but companies are also going out
of their way to make it more difficult for users through draconian DRM
regimes&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; and aggressive intellectual property enforcement. If the US Navy
can’t repair their own equipment because a corporation says so, what hope do
consumers have?&lt;/p&gt;
&lt;p&gt;We’re approaching a point where you don’t actually own anything. Software
is cloud and subscription based, hardware is unrepairable. Even cars can
be remotely updated and have features added or removed. The Federation wouldn’t
allow a third party control over their ships&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;, so why are we so happy to
put up with it in everything we consume?&lt;/p&gt;
&lt;h3 id=&#34;a-small-ray-of-hope&#34;&gt;A small ray of hope?&lt;/h3&gt;
&lt;p&gt;The most promising way of tackling all of these problems is through legislation.
The EU’s &lt;a href=&#34;https://digital-markets-act.ec.europa.eu/index_en&#34;&gt;Digital Market Act&lt;/a&gt;
is an attempt to force ‘gatekeepers’ like Google, Apple and Meta, to allow
third-party access to their services. It seems like a pretty reasonable
approach, but the tech companies are unsurprisingly resisting it. Apple in
particular have gone out of their way to refuse to comply, and when forced to
do so have limited the functionality to people in Europe.
Still, the DMA is a promising start, and if similar legislation is introduced
(and robustly enforced) elsewhere it might start forcing companies to behave a
bit better.&lt;/p&gt;
&lt;p&gt;There are also smaller companies that actually do the right thing.
&lt;a href=&#34;https://frame.work/gb/en&#34;&gt;Framework&lt;/a&gt; make laptops that are user-serviceable;
&lt;a href=&#34;https://www.fairphone.com/&#34;&gt;Fairphone&lt;/a&gt; do the same for mobile phones. Smaller
software companies provide useful, open APIs. The average person on the street
will probably have never heard of these, unfortunately, but they do still
exist. Maybe as the bigger tech companies tighten the screws more, people will
turn to alternatives like this? Or maybe we’ll just keep accepting that our
computers work for everyone but us?&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;Unless, of course, the computer is playing the role of the episode’s
MacGuffin and has contracted space-computer-COVID or something, then it’s a lot
less in-the-background. &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;It’s almost like it only exists to move the plot along. &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;You can generally export your data, thanks to a combination of legislation
and efforts like Google’s “Data Liberation Front”, but I’ve never seen an export
format that could then just be imported into an equivalent commercial product. &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;Oh, you’ve changed the screen on your iPhone? Better hope it can do the
secret handshake with the Apple hardware. &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;I think there might actually have been an episode where that did in fact
happen. We’ll just ignore that as a plot contrivance. &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>Reproducible Builds and Docker Images</title>
        <link href="https://chameth.com/reproducible-builds-docker-images/"/>
        <updated>2022-02-18T00:00:00Z</updated>
        <id>https://chameth.com/reproducible-builds-docker-images/</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/reproducible-builds-docker-images/dependency.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/reproducible-builds-docker-images/dependency.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/reproducible-builds-docker-images/dependency.png&#34; alt=&#34;Comic showing all modern digital infrastructure is built upon one project by a random person in Nebraska&#34; loading=&#34;lazy&#34; width=&#34;385&#34; height=&#34;489&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;XKCD 2347: Dependency&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&#34;https://reproducible-builds.org/&#34;&gt;Reproducible builds&lt;/a&gt; are builds which you are able to reproduce byte-for-byte,
given the same source input. Your initial reaction to that statement might be “Aren’t nearly all builds
‘reproducible builds’, then? If I give my compiler a source file it will always give me the same binary, won’t it?”
It &lt;em&gt;sounds&lt;/em&gt; simple, like it’s something that should just be fundamentally true unless we go out of our way to break it,
but in reality it’s actually quite a challenge. A group of Debian developers have been working on reproducible packages
for the best part of a decade and while they’ve made fantastic progress,
&lt;a href=&#34;https://isdebianreproducibleyet.com/&#34;&gt;Debian still isn’t reproducible&lt;/a&gt;. Before we talk about why it’s a hard problem,
let’s take a minute to ponder why it’s worth that much effort.&lt;/p&gt;
&lt;h3 id=&#34;on-supply-chain-attacks&#34;&gt;On supply chain attacks&lt;/h3&gt;
&lt;p&gt;Suppose you want to run some open-source software. One of the many benefits of open-source software is that anyone
can look at the source and, in theory, spot bugs or malicious code. Some projects even have sponsored audits or
penetration tests to affirm that the software is safe. But how do you actually deploy that software? You’re probably
not building from source - more likely you’re using a package manager to install a pre-built version, or downloading
a binary archive, or running a docker image. How do you know whoever prepared those binary artifacts did so from
an un-doctored copy of the source? How do you know a
&lt;a href=&#34;https://en.wikipedia.org/wiki/SourceForge#Controversies&#34;&gt;middle-man hasn’t decided to add malware to the binaries to make money&lt;/a&gt;?&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Even worse: if the software you’re trying to use includes any dependencies, you have the same issue of trust
with them. Maybe &lt;em&gt;your&lt;/em&gt; supplier isn’t compromising the software, but that doesn’t mean &lt;em&gt;their&lt;/em&gt; supplier isn’t. The
beauty-cum-horror of a supply chain attack is that it can target the weakest link anywhere along the supply chain.
Even if there aren’t any binary files involved, dependencies can still be attacked: what if &lt;code&gt;npmjs.com&lt;/code&gt; or
&lt;code&gt;proxy.golang.org&lt;/code&gt; or &lt;code&gt;github.com&lt;/code&gt; return a different version of a dependency-of-a-dependency when the request
comes from your IP address? It doesn’t even need to be a modified dependency, it could be a perfectly un-tampered,
properly signed copy of the source, just from an older version with a known vulnerability.&lt;/p&gt;
&lt;p&gt;Enter stage left: reproducible builds, here to save the day! If the build process is reproducible then you - or anyone
else on the internet - can perform the same build on the same source and validate the output has the same checksum or
hash. If Debian publish a binary package and an independent re-builder comes up with the exact same build artifact,
there’s a reasonably good chance that the build is good. An attacker would have to compromise both the build machine
and the re-build machine to do anything nefarious. The more re-builders there are, the less feasible a supply chain
attack is.&lt;/p&gt;
&lt;h3 id=&#34;so-why-isnt-software-just-reproducible&#34;&gt;So why isn’t software just reproducible?&lt;/h3&gt;
&lt;h4 id=&#34;compilers&#34;&gt;Compilers&lt;/h4&gt;
&lt;p&gt;As a bit of an experiment, I asked some friends to run the following for me and report the answer:&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-nb&#34;&gt;echo&lt;/span&gt; -e &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;#include &amp;lt;stdio.h&amp;gt;\nint main() { printf(\&amp;#34;Hello\&amp;#34;); return 0; }&amp;#34;&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;chroma-se&#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;  gcc -x c -o hello.out - &lt;span class=&#34;chroma-o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;chroma-se&#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;  sha256sum hello.out
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This compiles a super-simple hello world program and then prints the SHA-256 hash of the resulting binary. Here are
the results:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hash&lt;/th&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;GCC&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1f62feab5a06861dc575201d807781926d1ae49fb113da018fde8b670a1346f7&lt;/td&gt;
&lt;td&gt;Arch&lt;/td&gt;
&lt;td&gt;11.2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;b8e6f2c7082be69f65ffa5e7a3d749eb47866a1b2e1ec19efb63cc59a8b160cd&lt;/td&gt;
&lt;td&gt;Debian&lt;/td&gt;
&lt;td&gt;8.3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cbad2e47a22c234b5e7fa55e029a8db4d64ac7a962e2176bd2e1373d78954088&lt;/td&gt;
&lt;td&gt;Debian&lt;/td&gt;
&lt;td&gt;8.3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;e0f6bbc13b29fea8cfa2a975ba4661e781323298aec166c8311d342e6f93c4a6&lt;/td&gt;
&lt;td&gt;Alpine&lt;/td&gt;
&lt;td&gt;10.3.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;e379156895e06c7a0bf18ac4d648860edcb2655576b0ab9fab172bd6c8b92075&lt;/td&gt;
&lt;td&gt;Debian&lt;/td&gt;
&lt;td&gt;10.2.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7ffdaee4eb64e016b89dc5e54d2c8eebab3cebafe2c7aa97de627b5972ecea46&lt;/td&gt;
&lt;td&gt;Debian&lt;/td&gt;
&lt;td&gt;11.2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8ae52cc166743b6ae1eb3e14179ef33de5061a04237f8f97088c896c41a2f698&lt;/td&gt;
&lt;td&gt;Arch&lt;/td&gt;
&lt;td&gt;11.1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8ae52cc166743b6ae1eb3e14179ef33de5061a04237f8f97088c896c41a2f698&lt;/td&gt;
&lt;td&gt;Arch&lt;/td&gt;
&lt;td&gt;11.1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As you can see, there are barely any duplicates. Even the same version of GCC on the same OS sometimes produces
different results. And this is the most basic program I could write! Differences arise from the compiler version,
the build flags, the libraries installed, and a whole host of other factors. If you compile a Go application instead of
a C one, then by default the compiler will include debug information in the binary. This includes the full path to the
source file on disk, so building a project in &lt;code&gt;/home/chris/&lt;/code&gt; will produce a different binary to building the same
source in &lt;code&gt;/tmp&lt;/code&gt;. Future versions of Go are also going to stamp in other meta-data such as VCS info, so building inside
and outside a Git repository will produce different binaries.&lt;/p&gt;
&lt;h4 id=&#34;archives&#34;&gt;Archives&lt;/h4&gt;
&lt;p&gt;Compilers are only half the problem. Build processes are usually multistep, involving compiling, moving, compressing,
and so on. Consider creating an archive of a file:&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;repeat &lt;span class=&#34;chroma-m&#34;&gt;4&lt;/span&gt; touch hello &lt;span class=&#34;chroma-o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; tar zcf hello.tgz hello &lt;span class=&#34;chroma-o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sha256sum hello.tgz &lt;span class=&#34;chroma-o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sleep 0.5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;f3d5c56f6b8089de95d62d060e6ffcbbad26875807ae7bc253f07cd097ea61be  hello.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;ab67f2e865b5afa87d9b2434d92b0c271b3cf730fa85988f84852551749ba6ed  hello.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;ab67f2e865b5afa87d9b2434d92b0c271b3cf730fa85988f84852551749ba6ed  hello.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;738678c9650b10fd83636997dd1aba4016bbf0ec5ebf3dfd4ef75d770b56e23b  hello.tgz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Any file added to a tar takes with it a timestamp, so the build is only reproducible if it happens at the exact same
time! We can make this reproducible by forcing &lt;code&gt;tar&lt;/code&gt; (and the same goes for &lt;code&gt;zip&lt;/code&gt; and most other archive formats) to
set a certain timestamp on the files:&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;repeat &lt;span class=&#34;chroma-m&#34;&gt;4&lt;/span&gt; touch hello &lt;span class=&#34;chroma-o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; tar --mtime 2022-02-18T01:00 -zcf hello.tgz hello &lt;span class=&#34;chroma-o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sha256sum hello.tgz &lt;span class=&#34;chroma-o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sleep 0.5 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;081060a900beff2a6aad9957a8cbb8792f8db7904f86b318dbf26b682a2d3f0a  hello.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;081060a900beff2a6aad9957a8cbb8792f8db7904f86b318dbf26b682a2d3f0a  hello.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;081060a900beff2a6aad9957a8cbb8792f8db7904f86b318dbf26b682a2d3f0a  hello.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;081060a900beff2a6aad9957a8cbb8792f8db7904f86b318dbf26b682a2d3f0a  hello.tgz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In a real build there are basically two approaches here: you can set it to a pre-defined value (like the unix epoch),
or you can set it to match the modification time of the source files. The former is easiest, but the latter is more
cosmetically and semantically appealing.&lt;/p&gt;
&lt;h4 id=&#34;iteration-order&#34;&gt;Iteration order&lt;/h4&gt;
&lt;p&gt;So we’ve pinned our build environment, we’re manipulating timestamps when adding files to archives, now what? Imagine
part of the build process involves looping through all the files in a directory and doing &lt;em&gt;something&lt;/em&gt;. What order do
these files get iterated in? Well, that very much depends on the filesystem and perhaps when the files themselves
were created. To ensure this is reproducible we need to explicitly sort any such operation so that it’s always
consistent. This iteration could be happening in a tool that’s called by another tool that’s called by a build script,
so the fix isn’t necessarily straight-forward.&lt;/p&gt;
&lt;aside class=&#34;sidenote raised-box&#34;&gt;
  &lt;h5 class=&#34;plain-header&#34;&gt;Side note: a bug war story&lt;/h5&gt;
  &lt;p&gt;I’ve personally been victim to this kind of non-determinism. I was working on an Android app, and committed a new
test that worked fine on my machine, and worked fine on the CI server. But it failed consistently for a colleague.&lt;/p&gt;
&lt;p&gt;We both did fresh checkouts of the source, and ran the tests. Mine passed, his failed. He sent me an archive of
his checkout in case there was something weird going on there, and the tests passed on my machine. We compared
hashes of our checkouts, and they were the same. It was obviously environmental somehow, but everything else worked
fine, and the build system went to great pains to ensure things were the same.&lt;/p&gt;
&lt;p&gt;After a &lt;em&gt;lot&lt;/em&gt; of debugging, I worked out that his test was running with a different version of a library to me,
despite the libraries being defined in the build files and the build files being identical. After &lt;em&gt;even more&lt;/em&gt;
debugging it turned out there were two versions of the library on the classpath, and the ordering of them was
different between my machine and his.&lt;/p&gt;
&lt;p&gt;The actual issue turned out to be that the build tool generated the classpath by iterating over the library
files, and that iteration was done in order of file creation time. The two libraries were added at different points
in the project history, so the creation time in your local cache depended on which versions of the app you’d built
in the past. With no cache everything worked as expected but there was a slim range of commits where only one
library was in use, and if you had run the tests during that period your cache was effectively poisoned.&lt;/p&gt;
&lt;p&gt;We fixed the issue by excluding the older version of the library (which was being pulled in as a transient dependency),
and filed a bug against the build tool to make the classpath properly deterministic. I think that stands as the most
difficult to diagnose bug I’ve ever dealt with.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;Interestingly, if you iterate over a map in Go, the iteration is &lt;em&gt;deliberately&lt;/em&gt; non-deterministic. That’s an attempt
to defeat &lt;a href=&#34;https://www.hyrumslaw.com/&#34;&gt;Hyrum’s Law&lt;/a&gt; and prevent developers from relying on whatever the current
behaviour happens to be. This actually makes it easier to make things reproducible as the problem is loud and
in-your-face, rather than subtle and hard to spot.&lt;/p&gt;
&lt;h4 id=&#34;other-sources&#34;&gt;Other sources&lt;/h4&gt;
&lt;p&gt;There’s an awful lot of other places that non-determinism can come from. If the app pulls in dependencies, their
versions have to be pinned, otherwise your build changes depending on the latest release of that dependency. If
the build process pulls any information from a website, it’s liable to change. Hopefully the website is under your
control so that you can version the resource and pin that version. Obviously, anything to do with dates or the
current user will probably cause problems. Timezones and locales can cause subtle differences.&lt;/p&gt;
&lt;h3 id=&#34;what-about-docker&#34;&gt;What about Docker?&lt;/h3&gt;
&lt;p&gt;Docker comes with some good and some bad points for reproducibility. The biggest advantage is that it inherently
completely describes the build environment; it should work exactly the same from one system to another, even across
different OS families. The biggest drawback is it sprays timestamps around like no-one’s business. Each layer in
a container image is a &lt;code&gt;.tar.gz&lt;/code&gt; file, meaning each file within it is timestamped as discussed above. Making an image
involves a lot of copying of files around, so these timestamps invariably end up causing reproducibility issues.&lt;/p&gt;
&lt;p&gt;Even worse than timestamps in the filesystem, the image format also contains some meta-data that includes the
timestamp at which each layer was built. That means even if you go out of your way to set the timestamp of every
single file in your image, the image itself will be different every time you rebuild it. There is no way to deal
with this in Docker, which is a very sad state of affairs. Fortunately, &lt;a href=&#34;https://buildah.io/&#34;&gt;Buildah&lt;/a&gt; provides
a &lt;code&gt;--timestamp&lt;/code&gt; flag for &lt;em&gt;its&lt;/em&gt; build commands; this not only sets the layer timestamp but also the creation
timestamp of any file within the layer.&lt;/p&gt;
&lt;p&gt;The other major issue that affects Docker images is the pinning of packages pulled in by package managers. An awful
lot of images are based on Alpine or Debian derivatives, and use &lt;code&gt;apk&lt;/code&gt; or &lt;code&gt;apt&lt;/code&gt; to install dependencies. These need
to have a version specified as otherwise the package manager will just pull in the latest at the time of the build.
But this isn’t quite enough: you also need to pin the version of any packages that they depend on, recursively.
This means flattening the entire package hierarchy and installing all the packages explicitly and with pinned
versions.&lt;/p&gt;
&lt;p&gt;One more wrinkle in the package management space is that Alpine don’t keep old packages in their main repositories.
If you have a Docker image with pinned alpine packages in, it will stop building if the package is updated. This
isn’t necessarily fatal to making a reproducible build — as long as it’s reproducible for its useful lifetime,
I don’t really see an issue.&lt;/p&gt;
&lt;p&gt;Honestly, though, the biggest issue with making Docker images reproducible is getting people to care. Dockerfiles
are a relatively new way of packaging software, and there’s no centralised organisation like you find with Linux
distributions. There are enough challenges that most casual packagers aren’t going to bother, and no real
incentive for them to. That won’t stop me trying, though!&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Artisanal Docker images</title>
        <link href="https://chameth.com/artisanal-docker-images/"/>
        <updated>2022-02-05T00:00:00Z</updated>
        <id>https://chameth.com/artisanal-docker-images/</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/artisanal-docker-images/artisanal-containers.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/artisanal-docker-images/artisanal-containers.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/artisanal-docker-images/artisanal-containers.jpg&#34; alt=&#34;Shelf showing a variety of artisanal containers&#34; loading=&#34;lazy&#34; width=&#34;300&#34; height=&#34;432&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;Artisanal containers…&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I run a fair number of services as docker containers. Recently, I’ve been moving away from pre-built images
pulled from Docker Hub in favour of those I’ve hand-crafted myself. If you’re thinking “that sounds like a
lot of effort”, you’re right. It also comes with a number of advantages, though, and has been a fairly fun
journey.&lt;/p&gt;
&lt;h3 id=&#34;the-problems-with-docker-hub-and-its-images&#34;&gt;The problems with Docker Hub and its images&lt;/h3&gt;
&lt;h4 id=&#34;rate-limits&#34;&gt;Rate limits&lt;/h4&gt;
&lt;p&gt;For the last few years, I’ve been getting increasingly unhappy with Docker Hub itself. Docker-the-technology
is wonderful, but Docker-the-company has been making some rather large missteps. The biggest and most impactful
of these has been introducing “pull rate” limits. At the time of writing, if you want to just pull a public image
without logging in then you are limited to 100 pulls every 6 hours. If you log in then you’re limited to 200 pulls
per 6 hours, but it’s account wide. This might seem like a big enough number, but I repeatedly hit it and there
is no way to actually audit what is causing it. I have various containers that may all pull images at arbitrary
times (e.g. continuous integration build agents), and the only information you get back from Docker Hub is the
number of pulls remaining.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Obviously, I could start paying Docker Hub for a “Pro” plan. That gets you 5,000 pulls per day for $7/month.
The downside is that every docker client would have to be authenticated, which presents a fair annoyance in
terms of credential management. I also don’t really like how they positioned the service as a public utility
with special treatment in the docker software, and then start tightening the ratchet to make money.&lt;/p&gt;
&lt;h4 id=&#34;bad-images&#34;&gt;“Bad” images&lt;/h4&gt;
&lt;p&gt;I’m fairly opinionated about what a container image should look like: most importantly it should run just a
single process, and only include the bare minimum dependencies required for that. Other people think differently,
and it’s very hard to tell at a glance whether an image on Docker Hub contains just the application you want,
or whether it also bundles MySQL, Redis, Elasticsearch, and a partridge in a pear tree. Some people want that
kind of thing, but I really don’t. It’s also very hard to tell whether an image is officially endorsed by the
upstream project, and where the source Dockerfile is. This used to be better because most projects used Docker Hub’s
automatic builds, but they’re now a “pro” feature.&lt;/p&gt;
&lt;p&gt;I quite often found that I’d be looking for an image for X, and there would be 5-10 images from different users.
None of them looked official, some of them were out-of-date, some bundled the kitchen sink. Even when one looked
good, it’s a bit of a gamble whether the author is going to keep it updated or not.&lt;/p&gt;
&lt;h4 id=&#34;doijanky&#34;&gt;Doijanky&lt;/h4&gt;
&lt;p&gt;The rate limits and other problems were annoying, but they weren’t really annoying enough to force me to do
anything about it. The straw that broke the camel’s back came later: I was looking at the
&lt;a href=&#34;https://hub.docker.com/_/golang&#34;&gt;official golang images&lt;/a&gt;, and noticed that all the tags were pushed by a
random user account called “doijanky”:&lt;/p&gt;
&lt;figure class=&#34;image center&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/artisanal-docker-images/doijanky.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/artisanal-docker-images/doijanky.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/artisanal-docker-images/doijanky.png&#34; alt=&#34;An &amp;#39;official&amp;#39; Docker Hub image pushed by user &amp;#39;doijanky&amp;#39;&#34; loading=&#34;lazy&#34; width=&#34;786&#34; height=&#34;249&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;An ‘official’ Docker Hub image pushed by user ‘doijanky’&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I, perhaps naively, assumed that official images were built on Docker Hub’s own infrastructure. Why would
all the Golang images be attributed to this user? Checking out their profile, they’re simply identified as
a “Community User” like everyone else, with no repositories of their own. The only thing in the profile is
their homepage, which is a link to a Jenkins dashboard: &lt;a href=&#34;https://doi-janky.infosiftr.net/&#34;&gt;https://doi-janky.infosiftr.net/&lt;/a&gt;. It appears
legitimate: “Infosiftr” are a container consultancy and the dashboard is linked to from the README in the
official images git repository, but I find it baffling that they’re using third-party infrastructure and
a normal user account (with a dubious name) to push these images. There doesn’t seem to be a good way to
verify what you pull corresponds to the Dockerfile it came from; if infosiftr wanted to inject something
into the build they could happily do so, and who knows how good their infosec posture is? If someone got
access to the “doijanky” account, how long could they upload malicious images before someone noticed?&lt;/p&gt;
&lt;p&gt;This little roller-coaster ride from “are all the official images compromised?!” to “oh, no, they’re not,
it’s all just awful” finally convinced me to look at building my own images from scratch.&lt;/p&gt;
&lt;h3 id=&#34;the-implementation-templating-with-contempt&#34;&gt;The implementation: templating with contempt&lt;/h3&gt;
&lt;p&gt;One of the big issues I needed to tackle was how to deal with updates. I didn’t want to have to go and
edit a file every time some minor release was made of some software, or every time there was a security
vulnerability in a common library. The official images use a shell-scripting based system to check for
updates and generate Dockerfiles, I decided to do something similar but with Go templates. The result is
a tool called &lt;a href=&#34;https://github.com/csmith/contempt&#34;&gt;contempt&lt;/a&gt;. It takes a template like:&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-k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-s&#34;&gt;{{image&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;golang&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;}}&lt;/span&gt; AS build&lt;span class=&#34;chroma-err&#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-err&#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-k&#34;&gt;ARG&lt;/span&gt; &lt;span class=&#34;chroma-nv&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;{{github_tag &amp;#34;&lt;/span&gt;example/project&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;}}&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-err&#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-err&#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-k&#34;&gt;RUN&lt;/span&gt; apk add --no-cache &lt;span class=&#34;chroma-se&#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-o&#34;&gt;{{&lt;/span&gt;range &lt;span class=&#34;chroma-nv&#34;&gt;$key&lt;/span&gt;, &lt;span class=&#34;chroma-nv&#34;&gt;$value&lt;/span&gt; :&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt; alpine_packages &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;git&amp;#34;&lt;/span&gt; -&lt;span class=&#34;chroma-o&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;chroma-err&#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-o&#34;&gt;{{&lt;/span&gt;&lt;span class=&#34;chroma-nv&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;}}={{&lt;/span&gt;&lt;span class=&#34;chroma-nv&#34;&gt;$value&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;chroma-se&#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-o&#34;&gt;{{&lt;/span&gt;end&lt;span class=&#34;chroma-o&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;chroma-se&#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;# ...&lt;/span&gt;&lt;span class=&#34;chroma-err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Contempt has support for getting information from a variety of sources. In this case, it’s getting
the latest digest of another Docker image, the latest tag from a Git repository, and the latest version
of an alpine package and all its dependencies. The resulting Dockerfile looks something like this:&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-c&#34;&gt;# Generated from https://github.com/csmith/dockerfiles/blob/master/miniflux/Dockerfile.gotpl&lt;/span&gt;&lt;span class=&#34;chroma-err&#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-c&#34;&gt;# BOM: {&amp;#34;apk:brotli-libs&amp;#34;:&amp;#34;1.0.9-r5&amp;#34;,&amp;#34;apk:busybox&amp;#34;:&amp;#34;1.34.1-r4&amp;#34;, &amp;lt;snip&amp;gt; }&lt;/span&gt;&lt;span class=&#34;chroma-err&#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-err&#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-k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-s&#34;&gt;reg.c5h.io/golang@sha256:ac8fa5f4078b0a697796b5d741&lt;/span&gt;... AS build&lt;span class=&#34;chroma-err&#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-err&#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-k&#34;&gt;ARG&lt;/span&gt; &lt;span class=&#34;chroma-nv&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;2.0.35&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-err&#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-err&#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-k&#34;&gt;RUN&lt;/span&gt; apk add --no-cache &lt;span class=&#34;chroma-se&#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;        brotli-libs&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;1.0.9-r5&lt;span class=&#34;chroma-se&#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-nv&#34;&gt;busybox&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;1.34.1-r4&lt;span class=&#34;chroma-se&#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;# &amp;lt;snip&amp;gt;&lt;/span&gt;&lt;span class=&#34;chroma-err&#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-nv&#34;&gt;pcre2&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;10.39-r0&lt;span class=&#34;chroma-se&#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-nv&#34;&gt;zlib&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;1.2.11-r3&lt;span class=&#34;chroma-se&#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 class=&#34;chroma-se&#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;# ...&lt;/span&gt;&lt;span class=&#34;chroma-err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I’ve cut out the longer parts for readability. You can see that it pins all versions of the alpine packages in use,
as well as the base image. This ensures that if you build from the same Dockerfile at a later time it will build the
same image (or will fail entirely, as Alpine doesn’t keep their old packages around indefinitely). It also produces
a “bill of materials” as a really long JSON-encoded comment. If you let contempt commit the Dockerfile it uses the
BOM to generate useful commit messages like: &lt;code&gt;[project] apk:busybox: 1.34.1-r3-&amp;gt;1.34.1-r4&lt;/code&gt;, so you can see exactly
what changed.&lt;/p&gt;
&lt;p&gt;Contempt also has support for building and pushing images whenever it changes the Dockerfile. I use it in a
GitHub action that runs daily to check all my images are up-to-date and push those that aren’t. It understands
the dependencies between images (by pre-analysing the templates) so it will always check and build base images
before ones that require them. This means an update to, say, the “alpine” base image will cause anything that
depends on it to get updated at the same time, ensuring security updates are rolled out promptly.&lt;/p&gt;
&lt;h3 id=&#34;the-result&#34;&gt;The result&lt;/h3&gt;
&lt;p&gt;You can see my collection of lovingly hand-crafted Dockerfiles in my &lt;a href=&#34;https://github.com/csmith/dockerfiles&#34;&gt;dockerfiles&lt;/a&gt;
repository.&lt;/p&gt;
&lt;p&gt;There are a number of advantages to handwriting all the images I use. The obvious one is that they’re all built
how I want: there are no extraneous dependencies, they’re all based on the same small set of base images (rather
than pulling around 10 different versions of debian), nothing tries to also run a DBMS in its container, etc.&lt;/p&gt;
&lt;p&gt;This level of customisation goes further, though. Because I’m packaging the software myself, I can tweak how it’s
built to fit my needs. A couple of things I run need their own TLS certificates separate from my normal HTTPS
setup, so I bake my &lt;a href=&#34;https://github.com/csmith/certwrapper/&#34;&gt;certwrapper&lt;/a&gt; tool in to manage those; I can even set the
build flags on certwrapper to only enable the particular DNS provider I personally need (thus avoiding dragging in
clients for AWS, GCP, etc). Some software like Hashicorp Vault has an optional web interface that I don’t need,
so I simply don’t enable it in the build. These changes save build time, reduce image sizes, in some cases improve
runtime performance, and generally reduce the attack surface of what’s running in the container.&lt;/p&gt;
&lt;p&gt;It’s also been a great way to learn more about how software is distributed. Writing a Dockerfile is not that distant
from writing a PKGBUILD file for an Arch Linux package, or the equivalent for other distributions. In a couple of
instances I’ve googled how to solve a particular issue, and found an Arch or Void linux maintainer asking the upstream
project about the exact same issue.&lt;/p&gt;
&lt;p&gt;Finally, all the images I build I push to my own registry so there are obviously no rate limiting issues.
Standing up a service (assuming the Dockerfile has been written!) is amazingly quick because the base layers are all
shared and cached, and the registry is a lot physically closer than Docker Hub. Bootstrapping this whole thing becomes
an interesting problem because the image for the registry is stored on the registry, but I’ll leave that discussion for
another post…&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Apple, Google and aligned incentives</title>
        <link href="https://chameth.com/apple-google-aligned-incentives/"/>
        <updated>2020-10-17T00:00:00Z</updated>
        <id>https://chameth.com/apple-google-aligned-incentives/</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/apple-google-aligned-incentives/htc-dream.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/apple-google-aligned-incentives/htc-dream.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/apple-google-aligned-incentives/htc-dream.jpg&#34; alt=&#34;White HTC Dream mobile phone&#34; loading=&#34;lazy&#34; width=&#34;300&#34; height=&#34;225&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The HTC Dream, the first phone released running Android.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For the past decade I’ve exclusively used Android phones. I got the HTC Dream (aka the T-Mobile G1)
shortly after it came out, and dutifully upgraded every 1-2 years. In that timespan I used Android
as the basis for my Master’s Thesis, took a job on the Android team at Google, and eventually
became a contractor specialising in Android app development. So when I switched to using an iPhone
earlier this year a few people were surprised&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;h3 id=&#34;the-good-old-days&#34;&gt;The good old days&lt;/h3&gt;
&lt;p&gt;When Android was announced in 2007 – alongside the formation of the Open Handset Alliance – it
was positioned as a bastion of openness: it would be built on open standards and the operating
system would be open source. At the time iPhones were strongly coupled to iTunes and Apple was
exercising strict control over what app developers could do.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;When the HTC Dream was released it lived up to expectations. You could write apps for it without
shelling out for a Mac! You could get root access, and it was running Linux under the hood! A
whole ecosystem of custom firmwares and bootloaders started to appear, thanks to the open source
nature of the OS. It shipped with some Google apps, but they were just normal apps that served
as examples of what could be done.&lt;/p&gt;
&lt;p&gt;After the Dream came a line of Nexus devices. These were Android’s flagship devices, designed to
show off what a good Android phone should look like. Both the hardware and software releases
tended to feature interesting, useful upgrades. Some of the original open source apps were
replaced with closed source, Google proprietary ones, but that was OK - the open source
versions lived on in the open source project as examples of what you &lt;em&gt;could&lt;/em&gt; do. The devices
allowed flashing custom firmware, and the OS source was always released… eventually.&lt;/p&gt;
&lt;h3 id=&#34;the-downfall&#34;&gt;The downfall&lt;/h3&gt;
&lt;figure class=&#34;image left&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/apple-google-aligned-incentives/pixels.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/apple-google-aligned-incentives/pixels.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/apple-google-aligned-incentives/pixels.jpg&#34; alt=&#34;Pixel and Pixel XL phones&#34; loading=&#34;lazy&#34; width=&#34;400&#34; height=&#34;363&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The Pixel and Pixel XL&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Over time, Android has got less and less free. The Nexus line of phones gave way to the Pixel
range, which got rid of the clearly demarcated border where Android ended and Google began.
&lt;a href=&#34;https://www.android.com/android-11/&#34;&gt;The Android 11 highlights&lt;/a&gt; list is dotted with sections
that say “On Pixel devices…”, and every single one is a software feature that could be
implemented on any device, but Google have decided to keep it proprietary instead of releasing
it as part of the open source platform.&lt;/p&gt;
&lt;p&gt;At the same time more and more functionality has been added to Google Play Services. This was
originally a shared location for Google specific services - in 2012 it merely handled some
Google+ functionality and dealing with OAuth for Google accounts. These days it contains a
huge swathe of Google services, as well as platform functionality such as push notifications,
barcode scanning, geolocation, and so on. Google Play Services isn’t part of the Android
Open Source Project, and is only available under license from Google. You can’t really have
a device without these functions, so as a manufacturer you have the choice between agreeing
to whatever terms Google requires&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; or spending an awful lot of development time creating an
alternative.&lt;/p&gt;
&lt;p&gt;While these are fairly abstract arguments about what a free platform should look like,
during the same period there has been a marked restriction in how you can actually use
Android devices – both as an end-user and as a developer. iOS has always had very stark
restrictions on what apps can do in the background; Android started its life allowing
pretty much anything, like any good general purpose computing device. This inevitably
lead to lots of apps doing lots of things that ranged from stupid to mildly suboptimal,
creating a &lt;a href=&#34;https://en.wikipedia.org/wiki/Tragedy_of_the_commons&#34;&gt;tragedy of the commons&lt;/a&gt;
amongst apps. The victim was the phone’s battery life, and Google’s solution was a
progressive series of restrictions on what apps can do and when. This includes limits
on when push notifications are delivered to a device, how frequently apps can wake up,
and so forth. Some of these can be bypassed by the end-user, but not all, and the
process is fairly cumbersome.&lt;/p&gt;
&lt;h3 id=&#34;aligned-incentives&#34;&gt;Aligned incentives&lt;/h3&gt;
&lt;p&gt;From my point of view, Android and iOS are now pretty much in a similar place.
They’re not general purpose computers, but
&lt;a href=&#34;https://daringfireball.net/linked/2020/08/14/orland-epic-game-consoles&#34;&gt;app consoles&lt;/a&gt;:
much like games consoles they consist of hardware and software that the user lacks
control over but accepts in order to access the library of apps/games.&lt;/p&gt;
&lt;p&gt;Given there’s no clear winner between them in terms of hardware and software, my
decision came down to a more holistic question: how aligned are their
incentives to my own? Apple is a product company: they make their money by
producing shiny things that people want to purchase; Google is an advertising
company: they make their money by using my personal information to show me
targeted adverts.&lt;/p&gt;
&lt;p&gt;For Google, Android was originally a strategic move to ensure that
Apple couldn’t dominate the mobile web, and by extension the revenue from ads.
As a company, there’s nothing to really push them forward in any particular
direction other than one that facilitates advertising. Of course, Google employs
tonnes of good people who want to do good things which counterbalances this, but
I’d still prefer to deal with a company that has an intrinsic motivation to
do things I want them to do, rather than one forced to by regulation, custom,
or the good intent of their employees.&lt;/p&gt;
&lt;p&gt;So the decision became obvious: if I have no strong opinions about the software
and hardware, Apple is the clear winner because their incentives are a lot
better aligned to mine.&lt;/p&gt;
&lt;hr/&gt;
&lt;h3 id=&#34;image-credits&#34;&gt;Image credits&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Photo&lt;/th&gt;
&lt;th&gt;Creator&lt;/th&gt;
&lt;th&gt;Licence&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTC Dream&lt;/td&gt;
&lt;td&gt;Akela NDE&lt;/td&gt;
&lt;td&gt;CC BY-SA 3.0&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;https://commons.wikimedia.org/w/index.php?curid=6680413&#34;&gt;Wikimedia&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel and Pixel XL&lt;/td&gt;
&lt;td&gt;Maurizio Pesce from Milan, Italia&lt;/td&gt;
&lt;td&gt;CC BY 2.0&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;https://commons.wikimedia.org/w/index.php?curid=52110138&#34;&gt;Wikimedia&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;Or, at least, politely feigned surprise. &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;And Google’s terms were particularly onerous: as well as requiring Chrome and Google
Search to be preinstalled, they prevented manufacturers from selling any devices
powered by alternative versions of Android - e.g., Samsung wouldn’t be allowed to
sell a refrigerator that ran Amazon’s FireOS. The European Union handed Google a
$5,000,000,000 fine for this anti-competitive behaviour, which Google are still in
the process of contesting. &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>Debugging beyond the debugger</title>
        <link href="https://chameth.com/debugging-beyond-the-debugger/"/>
        <updated>2019-05-08T00:00:00Z</updated>
        <id>https://chameth.com/debugging-beyond-the-debugger/</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/debugging-beyond-the-debugger/tools.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/debugging-beyond-the-debugger/tools.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/debugging-beyond-the-debugger/tools.jpg&#34; alt=&#34;Collection of tools hanging on a wall&#34; loading=&#34;lazy&#34; width=&#34;300&#34; height=&#34;396&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;Real-life debugging tools&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Most programming — and sysadmin — problems can be debugged in a
fairly straight forward manner using logs, print statements,
educated guesses, or an actual debugger. Sometimes, though, the
problem is more elusive. There’s a wider box of tricks that can
be employed in these cases but I’ve not managed to find a nice
overview of them, so here’s mine. I’m mainly focusing on Linux
and similar systems, but there tend to be alternatives available
for other Operating Systems or VMs if you seek them out.&lt;/p&gt;
&lt;h3 id=&#34;networking&#34;&gt;Networking&lt;/h3&gt;
&lt;h4 id=&#34;tcpdump&#34;&gt;tcpdump&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;tcpdump&lt;/code&gt; prints out descriptions of packets on a network interface. You can
apply filters to limit which packets are displayed, chose to dump the entire
content of the packet, and so forth.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Typical usage might look something like:&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;# tcpdump -nSi eth0 port 80
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;16:03:35.577781 IP6 2001:db8::1.54742 &amp;gt; 2001:db8::2.80: Flags [S], seq 2815779044, win 64800, options [mss 1440,sackOK,TS val 2378811665 ecr 0,nop,wscale 7], length 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;16:03:35.586853 IP6 2001:db8::2.80 &amp;gt; 2001:db8::1.54742: Flags [S.], seq 1522609102, ack 2815779045, win 28560, options [mss 1440,sackOK,TS val 3063610173 ecr 2378811665,nop,wscale 7], length 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;16:03:35.586877 IP6 2001:db8::1.54742 &amp;gt; 2001:db8::2.80: Flags [.], ack 1522609103, win 507, options [nop,nop,TS val 2378811674 ecr 3063610173], length 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;16:03:35.620678 IP6 2001:db8::1.54742 &amp;gt; 2001:db8::2.80: Flags [P.], seq 2815779045:2815779399, ack 1522609103, win 507, options [nop,nop,TS val 2378811708 ecr 3063610173], length 354: HTTP: GET / HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here you can see the start of a plaintext HTTP request: the three-way
handshake as the TCP connection is established followed by a GET request.
Even if the data is encrypted as it will be in most cases, it’s often useful
to see the “shape” of the transmissions: did the client start sending data
when it connected, did the server ever respond, etc.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://danielmiessler.com/study/tcpdump/&#34;&gt;Daniel Miessler has a good tutorial on tcpdump&lt;/a&gt;
if you’re not familiar with it and don’t want to jump straight into the man
page.&lt;/p&gt;
&lt;h5 id=&#34;-with-docker&#34;&gt;… with Docker&lt;/h5&gt;
&lt;p&gt;Docker sets up separate network namespaces for each container. To see the
traffic across the interfaces of a single container you can &lt;code&gt;nsenter&lt;/code&gt; the
container’s network namespace:&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;# nsenter -t $(docker inspect --format &amp;#39;{{.State.Pid}}&amp;#39; my_container) -n tcpdump -nS port 80
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This retrieves the PID for the container, and tells &lt;code&gt;nsenter&lt;/code&gt; to enter the
network (&lt;code&gt;-n&lt;/code&gt;) namespace from the given target (&lt;code&gt;-t&lt;/code&gt;) PID, and then run the
given command (in this case &lt;code&gt;tcpdump ...&lt;/code&gt;).&lt;/p&gt;
&lt;h4 id=&#34;openssl-s-client--s-server&#34;&gt;openssl s_client / s_server&lt;/h4&gt;
&lt;p&gt;When a connection is using TLS it’s often useful to try connecting to the
server and see what certificate it presents, algorithms it negotiates, and
so forth. OpenSSL offers two useful subcommands which can help with this:
&lt;code&gt;s_client&lt;/code&gt; for connecting as a client, and &lt;code&gt;s_server&lt;/code&gt; for listening to
connections.&lt;/p&gt;
&lt;p&gt;For example, using &lt;code&gt;s_client&lt;/code&gt; to connect to &lt;code&gt;google.com&lt;/code&gt; on the standard
HTTPS port shows us details about the server cert and its verification
status:&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;$ openssl s_client -connect google.com:443
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;CONNECTED(00000003)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;verify return:1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;depth=1 C = US, O = Google Trust Services, CN = Google Internet Authority G3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;verify return:1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = *.google.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;verify return:1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Certificate chain
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; 0 s:C = US, ST = California, L = Mountain View, O = Google LLC, CN = *.google.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;   i:C = US, O = Google Trust Services, CN = Google Internet Authority G3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; 1 s:C = US, O = Google Trust Services, CN = Google Internet Authority G3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;   i:OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Whereas connecting to my webserver and providing an unknown host in the SNI
field results in an SSL alert 112 (“The server name sent was not recognized”)
and no server certificate is sent:&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;$ openssl s_client -connect chameth.com:443 -servername example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;CONNECTED(00000003)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;140384831313024:error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name:../ssl/record/rec_layer_s3.c:1536:SSL alert number 112
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;no peer certificate available
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Often if you hit this kind of alert in an application the exact error will be
lost somewhere in the many layers between the SSL library and the logs, so
being able to directly connect and test can help diagnose a lot of issues.&lt;/p&gt;
&lt;p&gt;Once a connection is established you can read and write plain text and it
will be encrypted and decrypted automatically.&lt;/p&gt;
&lt;h4 id=&#34;java-apps&#34;&gt;Java apps&lt;/h4&gt;
&lt;p&gt;If a Java app is involved in the connection, you can enable a lot of built-in
debugging with a simple JVM property: &lt;code&gt;javax.net.debug&lt;/code&gt;. You can tweak
what exactly gets logged, but the easiest thing to do is just set the property
to &lt;code&gt;all&lt;/code&gt; and you’ll see information about certificate chains, verification,
and packet dumps:&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;$ java -Djavax.net.debug=all -jar ....
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;found key for : duke
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;chain [0] = [
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  Version: V1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  Subject: CN=Duke, OU=Java Software, O=&amp;#34;Sun Microsystems, Inc.&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  L=Cupertino, ST=CA, C=US
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;More information about Java’s debugging options is available on
&lt;a href=&#34;https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ReadDebug.html&#34;&gt;docs.oracle.com&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;thread-and-core-dumps&#34;&gt;Thread and core dumps&lt;/h3&gt;
&lt;p&gt;Higher-level languages frequently provide an interactive way to dump the
current execution state of all of their threads (a “thread dump”). This
is useful to spot deadlocks, some types of race conditions, and as a
quick and dirty method of investigating hangs or excessive CPU usage.&lt;/p&gt;
&lt;p&gt;With both Java and Go applications you can send a QUIT signal to have a
thread dump printed out; Go applications will quit after doing so, Java
ones will carry on running. At most terminals you can hit &lt;code&gt;Ctrl&lt;/code&gt; and &lt;code&gt;\&lt;/code&gt; to
send a QUIT signal.&lt;/p&gt;
&lt;p&gt;For Java you can also use the &lt;code&gt;jstack&lt;/code&gt; tool from the JDK to dump threads
by PID; this can be useful if the application is running in the background
or has redirected sysout:&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;$ jstack 8321
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Attaching to process ID 8321, please wait...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Debugger attached successfully.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Client compiler detected.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Thread t@5: (state = BLOCKED)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.Object.wait(long) @bci=-1107318896 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.ref.ReferenceQueue.remove(long) @bci=44, line=116 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.ref.ReferenceQueue.remove() @bci=2, line=132 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.ref.Finalizer$FinalizerThread.run() @bci=3, line=159 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A core dump provides more complete information about the state of a process,
but is often more complex to interpret. The &lt;code&gt;gcore&lt;/code&gt; utility from GDB will
create a core dump of a process with a given PID. You can then generally
load the core file using your normal debugger, depending on the language
in question.&lt;/p&gt;
&lt;h3 id=&#34;system-calls&#34;&gt;System calls&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;strace&lt;/code&gt; is the swiss army knife for seeing what a process is doing. It
details each system call made by a program (you can filter them down, of
course). For example:&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;$ strace -e read curl https://google.com/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0 \236\0\0\0\0\0\0&amp;#34;..., 832) = 832
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0P!\0\0\0\0\0\0&amp;#34;..., 832) = 832
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0\200l\2\0\0\0\0\0&amp;#34;..., 832) = 832
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0\20Q\0\0\0\0\0\0&amp;#34;..., 832) = 832
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\0\0\0\0\0\0\0\4\25\345\366\302\273sE6\365wI\225\321|\3435Z\362\216\372\215\251aO&amp;#34;..., 253) = 253
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;HTML&amp;gt;&amp;lt;HEAD&amp;gt;&amp;lt;meta http-equiv=&amp;#34;content-type&amp;#34; content=&amp;#34;text/html;charset=utf-8&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;TITLE&amp;gt;301 Moved&amp;lt;/TITLE&amp;gt;&amp;lt;/HEAD&amp;gt;&amp;lt;BODY&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;H1&amp;gt;301 Moved&amp;lt;/H1&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;The document has moved
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;A HREF=&amp;#34;https://www.google.com/&amp;#34;&amp;gt;here&amp;lt;/A&amp;gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;/BODY&amp;gt;&amp;lt;/HTML&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\27\3\3\0!&amp;#34;, 5)                = 5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;http://www.brendangregg.com/blog/2014-05-12/strace-wow-much-syscall.html&#34;&gt;Brendan Gregg&lt;/a&gt;
has a nice guide on &lt;code&gt;strace&lt;/code&gt; and alternatives.&lt;/p&gt;
&lt;h4 id=&#34;-with-docker-1&#34;&gt;… with docker&lt;/h4&gt;
&lt;p&gt;When the application is running in docker you can usually just &lt;code&gt;strace&lt;/code&gt; it
from the host with the correct PID
(from e.g. &lt;code&gt;docker inspect --format &amp;#39;{{.State.Pid}}&amp;#39; my_container&lt;/code&gt;).
Sometimes you may need to trace the startup of an application though, which is
a bit trickier. Instead you can run a new container using the same PID
namespace as your target, and the permissions needed to &lt;code&gt;strace&lt;/code&gt;:&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;$ docker run --rm -it --pid=container:my_container \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  --net=container:my_container \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  --cap-add sys_admin \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  --cap-add sys_ptrace \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  alpine
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From within the new container you can install strace, and trace any running
program within the target container using &lt;code&gt;strace -p&lt;/code&gt; as normal. To start a
new program you need access to the target container’s filesystem, which you
can get to via &lt;code&gt;/proc/1/root&lt;/code&gt; (PID &lt;code&gt;1&lt;/code&gt; being the main process that docker
started in the target container).&lt;/p&gt;
&lt;h3 id=&#34;files&#34;&gt;Files&lt;/h3&gt;
&lt;p&gt;Sometimes the problem might relate to file access. There are a couple of
straight forward — but nonetheless useful — tools which might help here.
&lt;code&gt;inotifywait&lt;/code&gt; uses the Linux &lt;code&gt;inotify&lt;/code&gt; subsystem to watch files or directories
for operations. For example:&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;$ inotifywait -mr site/content
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Setting up watches.  Beware: since -r was given, this may take a while!
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Watches established.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ MODIFY 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ OPEN 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ MODIFY 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ MODIFY 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ CLOSE_WRITE,CLOSE 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here the &lt;code&gt;-m&lt;/code&gt; switch makes &lt;code&gt;inotifywait&lt;/code&gt; monitor the files forever (instead
of exiting on the first modification, which is the normal behaviour) and &lt;code&gt;r&lt;/code&gt;
makes it recurse into the directory and monitor each file and subdirectory in
there.&lt;/p&gt;
&lt;p&gt;If you want to see what processes currently have a file open, &lt;code&gt;fuser&lt;/code&gt; is the
go-to tool. For example:&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;$ fuser -v /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     USER PID ACCESS COMMAND
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;/:                   root     kernel mount /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     chris      2961 .rc.. systemd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     chris      2986 .r... gdm-x-session
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     chris      2994 .r... dbus-daemon
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     chris      3001 .r... gnome-session-b
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;honourable-mentions&#34;&gt;Honourable mentions&lt;/h3&gt;
&lt;p&gt;These aren’t really debugging tools, but I feel it’s worth mentioning as
they often feature somewhere along the debugging-of-weird-problems journey.&lt;/p&gt;
&lt;p&gt;I’ve seen some weird and wonderful problems happen
because a disk is full, so a quick &lt;code&gt;df&lt;/code&gt; early on in the debugging process
never hurts. Some apps may hang, some may corrupt their config, some may
fall over and die; sometimes the manner in which they fail doesn’t obviously
point to a disk space issue.&lt;/p&gt;
&lt;p&gt;Another issue that comes up now and then — especially inside VMs or
other environment that don’t have a decent amount of “noise” happening —
is entropy exhaustion. A quick look at &lt;code&gt;/proc/sys/kernel/random/entropy_avail&lt;/code&gt;
should be enough to confirm that everything is ticking along nicely. If it’s
exceedingly low then you may find that anything involving random number
generation stalls (TLS connections for example).&lt;/p&gt;
</content>
    </entry>
</feed>
