<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Bruno Schaatsbergen</title><link>https://bschaatsbergen.com/</link><description>Recent content on Bruno Schaatsbergen</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 21 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://bschaatsbergen.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Signing TLS handshakes inside a TPM</title><link>https://bschaatsbergen.com/posts/go-tpm-tls/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate><guid>https://bschaatsbergen.com/posts/go-tpm-tls/</guid><description>&lt;p&gt;I&amp;rsquo;ve been doing &lt;a href="https://www.rfc-editor.org/rfc/rfc9334.html"&gt;remote attestation&lt;/a&gt;&#10;work on confidential VMs. This post is one piece of that: whatever the&#10;attestation ends up looking like, the machine still needs an identity it can&#10;hold and use afterwards, and in my case that means authenticating to other&#10;services with mutual TLS.&lt;/p&gt;&#10;&lt;p&gt;An application that does mutual TLS authenticates with a client certificate,&#10;and on disk that is almost always two files. &lt;code&gt;client.crt&lt;/code&gt; is the one you show:&#10;it carries a public key and a CA&amp;rsquo;s signature over that key, and it isn&amp;rsquo;t a&#10;secret. &lt;code&gt;client.key&lt;/code&gt; is the one you sign the handshake with, and the server&#10;checks that signature against the public key in the certificate you just showed&#10;it.&lt;/p&gt;</description></item><item><title>Migrating out of GDC air-gapped in an emergency</title><link>https://bschaatsbergen.com/posts/gdc-air-gapped-emergency/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0200</pubDate><guid>https://bschaatsbergen.com/posts/gdc-air-gapped-emergency/</guid><description>&lt;blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;Disclaimer.&lt;/strong&gt; This is a personal thought exercise, written from public&#10;documentation on my own time. It is not affiliated with, endorsed by, or done&#10;on behalf of my employer, and nothing here represents my employer&amp;rsquo;s work,&#10;views, or internal systems.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;&lt;a href="https://docs.cloud.google.com/distributed-cloud/hosted/docs/latest/gdch/overview"&gt;Google Distributed Cloud (GDC)&#10;air-gapped&lt;/a&gt;&#10;is Google Cloud you run yourself, on your own premises, with no connection to&#10;Google or the internet. It is meant for workloads that cannot use a public&#10;network: classified systems, defense, or data that a regulator says must stay in&#10;the building. You get the Google Cloud APIs, running on hardware you control.&lt;/p&gt;</description></item><item><title>Blocking VoidLink with Tetragon</title><link>https://bschaatsbergen.com/posts/voidlink/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0000</pubDate><guid>https://bschaatsbergen.com/posts/voidlink/</guid><description>&lt;p&gt;&lt;a href="https://research.checkpoint.com/2026/voidlink-the-cloud-native-malware-framework/"&gt;VoidLink&lt;/a&gt;&#10;is Linux malware written in Zig. Check Point Research found it in December 2025.&#10;It is modular and cloud-native: it goes after containers in a Kubernetes cluster&#10;and the VMs those clusters run on, and it is built to persist there quietly, for&#10;a long time.&lt;/p&gt;&#10;&lt;p&gt;I take no credit for finding this. I dug into it and figured it was worth writing&#10;up. Shoutout to Isovalent and Check Point Research for sharing their VoidLink&#10;research.&lt;/p&gt;</description></item><item><title>A TPM has a clock, but it does not have the time</title><link>https://bschaatsbergen.com/posts/tpm-clock/</link><pubDate>Sun, 26 Jul 2026 00:00:00 +0000</pubDate><guid>https://bschaatsbergen.com/posts/tpm-clock/</guid><description>&lt;p&gt;I have a confidential VM running for some attestation work. It has a virtual&#10;TPM, which reports its manufacturer as &lt;code&gt;GOOG&lt;/code&gt;. I was poking at it and ran&#10;&lt;code&gt;tpm2_readclock&lt;/code&gt;, expecting a timestamp somewhere in there.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;$ sudo tpm2_readclock&#10;time: 964062646&#10;clock_info:&#10; clock: 964065681&#10; reset_count: 15&#10; restart_count: 0&#10; safe: yes&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;No date. I went looking for why, assuming I&amp;rsquo;d missed a command.&lt;/p&gt;&#10;&lt;h2 id="what-a-tpm-is"&gt;what a TPM is&lt;/h2&gt;&#10;&lt;p&gt;A &lt;a href="https://trustedcomputinggroup.org/resource/tpm-library-specification/"&gt;TPM&lt;/a&gt;&#10;is a small, slow processor that holds keys and won&amp;rsquo;t give them back. You can&#10;ask it to sign something. You can&amp;rsquo;t ask it for the key.&lt;/p&gt;</description></item><item><title>cek (Container Exploration Kit)</title><link>https://bschaatsbergen.com/posts/cek/</link><pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate><guid>https://bschaatsbergen.com/posts/cek/</guid><description>&lt;p&gt;&lt;a href="https://github.com/bschaatsbergen/cek"&gt;cek&lt;/a&gt; is a small CLI I built to read and explore OCI container images without running them, and without needing a container daemon. It talks directly to any registry that implements the &lt;a href="https://github.com/opencontainers/distribution-spec"&gt;OCI Distribution Spec&lt;/a&gt;, pulls the manifest and layer blobs that make up an image, and walks them in memory.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ cek cat nginx:latest /etc/nginx/nginx.conf&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Under the tooling, an &lt;a href="https://github.com/opencontainers/image-spec"&gt;OCI image&lt;/a&gt; is a JSON manifest pointing at a stack of gzipped tar layers. What a running container sees is those layers merged top-down. &lt;code&gt;cek&lt;/code&gt; reads from that same stack, just without the container.&lt;/p&gt;</description></item><item><title>Terraform, Google Cloud, and Secrets</title><link>https://bschaatsbergen.com/posts/terraform-google-cloud-secrets/</link><pubDate>Sat, 26 Jul 2025 00:00:00 +0000</pubDate><guid>https://bschaatsbergen.com/posts/terraform-google-cloud-secrets/</guid><description>&lt;p&gt;In Google Cloud, Secret Manager is great for storing sensitive data—but keeping secrets out of Terraform state, logs, and version control is a challenge. Terraform 1.11 introduced ephemeral resources and write-only arguments to fix that. Here&amp;rsquo;s how.&lt;/p&gt;&#10;&lt;h2 id="ephemerality"&gt;Ephemerality&lt;/h2&gt;&#10;&lt;p&gt;Ephemerality in computing refers to the ability to create something that is short-lived or temporary — a piece of data that exists only for a brief period and is discarded once its purpose is fulfilled. In Terraform, this concept was introduced to manage sensitive data or open a connection in a way that ensures it doesn&amp;rsquo;t persist beyond its immediate use. In Terraform 1.11, HashiCorp implemented new language constructs that track values only at runtime, making them transient and, therefore, ephemeral by design.&lt;/p&gt;</description></item><item><title>S3-native state locking</title><link>https://bschaatsbergen.com/posts/s3-native-state-locking/</link><pubDate>Sun, 22 Dec 2024 00:00:00 +0000</pubDate><guid>https://bschaatsbergen.com/posts/s3-native-state-locking/</guid><description>&lt;p&gt;State locking is an important feature in Terraform that protects your Terraform state. Terraform produces &amp;ldquo;state&amp;rdquo;, a snapshot of your deployed configuration, stored in a file that tracks resource attributes, their relationships and more. State locking ensures only one operation can modify this file at a time, preventing conflicts and potential data corruption.&lt;/p&gt;&#10;&lt;p&gt;Terraform uses (remote state&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;) backends to store the state file on a storage system. Each backend represents a specific storage system and manages its own approach to reading, writing, and locking the state file. For a backend to support state locking, it must adhere to &lt;a href="https://github.com/hashicorp/terraform/blob/main/internal/states/statemgr/locker.go#L48"&gt;a simple contract&lt;/a&gt;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;: it needs to be able to &lt;code&gt;Lock&lt;/code&gt; and &lt;code&gt;Unlock&lt;/code&gt; a specific state. When Terraform starts an operation, it first acquires the lock (&lt;code&gt;Lock&lt;/code&gt;), and once the operation is complete, it releases the lock (&lt;code&gt;Unlock&lt;/code&gt;).&lt;/p&gt;</description></item><item><title>Why there isn't a generic HCL formatter</title><link>https://bschaatsbergen.com/posts/hcl-formatter/</link><pubDate>Wed, 18 Dec 2024 00:00:00 +0000</pubDate><guid>https://bschaatsbergen.com/posts/hcl-formatter/</guid><description>&lt;p&gt;Sometimes, people ask why there isn&amp;rsquo;t a generic formatter for HCL (HashiCorp Configuration Language). The short answer is that HCL was designed as a framework for building languages, not as a standalone language, so it&amp;rsquo;s up to each application to define how formatting should work. Tools like Terraform and Packer include their own formatters, which extend basic HCL conventions with domain-specific rules. This ensures formatting aligns not only with general HCL syntax but also with the specific idiomatic patterns and best practices of the application.&lt;/p&gt;</description></item><item><title>Behind the Scenes Lambda</title><link>https://bschaatsbergen.com/posts/behind-the-scenes-lambda/</link><pubDate>Fri, 12 Jun 2020 00:00:00 +0000</pubDate><guid>https://bschaatsbergen.com/posts/behind-the-scenes-lambda/</guid><description>&lt;p&gt;Lambda is split into a control plane and data plane. Each plane is responsible for a specific set of activities in the service. The Control Plane provides management APIs and manages integrations with all AWS services. Whilst the Data Plane is Lambda&amp;rsquo;s Invoke API that triggers Lambda function invocations, this explanation is still very abstract but things will become clearer over time.&lt;/p&gt;&#10;&lt;h2 id="deployment-interoperability"&gt;Deployment Interoperability&lt;/h2&gt;&#10;&lt;p&gt;When deploying your Lambda function, you can either define a container image which is stored in Amazon ECR registry or deploy the code through a .zip file. You can specificy the location of an object in Amazon S3 by defining this in a CloudFormation template or through the CLI. If you&amp;rsquo;re uploading the .zip through the console, it will be stored in an inaccessible S3 bucket.&lt;/p&gt;</description></item><item><title>about</title><link>https://bschaatsbergen.com/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://bschaatsbergen.com/about/</guid><description>&lt;p&gt;Engineer from the Netherlands. Currently building systems for a national&#10;defence organisation, mostly Kubernetes and Go, with a focus on security.&#10;Open source maintainer across the Terraform and Kubernetes ecosystems, and&#10;Tech Lead in CNCF TAG Infrastructure.&lt;/p&gt;</description></item></channel></rss>