<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Security on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/tags/security/</link><description>Recent content in Security on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 08 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/tags/security/index.xml" rel="self" type="application/rss+xml"/><item><title>IaC Scanning</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/iac-scanning/</link><pubDate>Mon, 08 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/iac-scanning/</guid><description>&lt;p&gt;Static analysis for infrastructure code. Scan Terraform, Helm, Kubernetes manifests, Dockerfiles, and CloudFormation before they are applied. The goal is catching misconfigurations — open security groups, missing encryption, public S3 buckets — in the same pipeline that runs your application tests.&lt;/p&gt;
&lt;p&gt;These tools do not require running infrastructure. They read the source and flag violations against a built-in rule library, with optional custom rule support.&lt;/p&gt;
&lt;h2 id="checkov"&gt;Checkov
&lt;/h2&gt;&lt;p&gt;Checkov is a static analysis tool from Bridgecrew (now Prisma Cloud). Large built-in rule library for Terraform, Helm, Kubernetes, Dockerfiles, GitHub Actions, and more. Custom rules in Python or YAML.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install checkov
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install checkov
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Scan a Terraform directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;checkov -d ./infra/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Scan a specific file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;checkov -f main.tf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Scan Kubernetes manifests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;checkov -d ./k8s/ --framework kubernetes
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Scan a Helm chart&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;checkov -d ./charts/myapp/ --framework helm
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Output as JSON&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;checkov -d ./infra/ -o json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Skip specific checks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;checkov -d ./infra/ --skip-check CKV_AWS_20,CKV_AWS_57
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Output shows passed/failed checks per resource with the check ID, description, and file:line reference.&lt;/p&gt;
&lt;h3 id="custom-policies-yaml"&gt;Custom policies (YAML)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Ensure S3 bucket has MFA delete enabled&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;id&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;CKV2_AWS_CUSTOM_1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;category&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;ENCRYPTION&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;scope&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;provider&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;terraform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;definition&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cond_type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;attribute&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resource_types&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;aws_s3_bucket&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;attribute&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mfa_delete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;operator&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;equals&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;value&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Enabled&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="trivy-iac-mode"&gt;Trivy (IaC mode)
&lt;/h2&gt;&lt;p&gt;Trivy is primarily a container image vulnerability scanner, but its &lt;code&gt;--scanners misconfig&lt;/code&gt; mode covers IaC. One tool, multiple surfaces — useful when you are already using Trivy for image scanning and want consistent output.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install trivy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Scan Terraform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;trivy config ./infra/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Scan Kubernetes manifests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;trivy config ./k8s/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Scan a Helm chart&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;trivy config ./charts/myapp/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Include severity filter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;trivy config --severity HIGH,CRITICAL ./infra/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# JSON output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;trivy config -f json ./infra/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Trivy&amp;rsquo;s IaC rules are sourced from &lt;a class="link" href="https://github.com/aquasecurity/trivy-checks" target="_blank" rel="noopener"
 &gt;Trivy&amp;rsquo;s built-in checks&lt;/a&gt; — the same checks engine used by &lt;code&gt;tfsec&lt;/code&gt; (Trivy absorbed &lt;code&gt;tfsec&lt;/code&gt; in 2023).&lt;/p&gt;
&lt;h2 id="tfsec"&gt;tfsec
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;tfsec&lt;/code&gt; was a standalone Terraform-focused scanner, now maintained as part of Trivy. The standalone CLI still works and is simpler if you only scan Terraform.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install tfsec
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tfsec ./infra/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tfsec ./infra/ --severity HIGH
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tfsec ./infra/ --format json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Most teams migrating from &lt;code&gt;tfsec&lt;/code&gt; to Trivy get equivalent coverage with &lt;code&gt;trivy config&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="comparison"&gt;Comparison
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Checkov&lt;/th&gt;
 &lt;th&gt;Trivy config&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Terraform&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Kubernetes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Helm&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Dockerfile&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GitHub Actions&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Custom rules&lt;/td&gt;
 &lt;td&gt;Python / YAML&lt;/td&gt;
 &lt;td&gt;Rego&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Also scans&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;Container images, Git secrets, SBOMs&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Maintained by&lt;/td&gt;
 &lt;td&gt;Prisma Cloud&lt;/td&gt;
 &lt;td&gt;Aqua Security&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a team already using Trivy for image scanning, extending to IaC with &lt;code&gt;trivy config&lt;/code&gt; keeps the toolchain simple. Checkov&amp;rsquo;s advantage is the broader coverage (GitHub Actions, Bitbucket Pipelines) and more mature custom rule support.&lt;/p&gt;
&lt;h2 id="in-ci"&gt;In CI
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# GitHub Actions — Trivy IaC scan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Trivy IaC scan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;aquasecurity/trivy-action@master&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;scan-type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;scan-ref&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;./infra/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;severity&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;HIGH,CRITICAL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;exit-code&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# GitHub Actions — Checkov&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Checkov scan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bridgecrewio/checkov-action@master&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;directory&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;infra/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;framework&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;terraform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.checkov.io/1.Welcome/What%20is%20Checkov.html" target="_blank" rel="noopener"
 &gt;Checkov documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://aquasecurity.github.io/trivy/latest/docs/scanner/misconfiguration/" target="_blank" rel="noopener"
 &gt;Trivy misconfig scanning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/aquasecurity/trivy-checks" target="_blank" rel="noopener"
 &gt;Trivy-checks (rule library)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/bridgecrewio/checkov" target="_blank" rel="noopener"
 &gt;Checkov GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>SLSA — Supply-chain Levels for Software Artifacts</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/slsa/</link><pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/slsa/</guid><description>&lt;p&gt;SLSA (pronounced &amp;ldquo;salsa&amp;rdquo;) is a framework for securing the software supply chain. Developed by Google, now under the OpenSSF. The core question it answers: how do you know the artifact you are deploying is actually what was built from the source you think it was?&lt;/p&gt;
&lt;p&gt;The answer is &lt;strong&gt;provenance&lt;/strong&gt;: cryptographically signed metadata that records what built an artifact, from what source, when, and under what conditions. SLSA defines levels of increasing rigour around how that provenance is generated and verified.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="why-it-matters"&gt;Why it matters
&lt;/h2&gt;&lt;p&gt;The software supply chain is an attack surface. SolarWinds, XZ Utils, Log4Shell — different attack vectors but the same underlying problem: something got into the build or the dependency that should not have been there, and nobody noticed until it was too late.&lt;/p&gt;
&lt;p&gt;SLSA does not prevent all supply chain attacks. It makes certain classes of attack verifiably harder and gives consumers of an artifact a way to check that it was built the way it claims to have been.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-levels"&gt;The levels
&lt;/h2&gt;&lt;p&gt;SLSA defines three build levels (reorganised from the original four in SLSA v1.0):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L1 — Provenance exists&lt;/strong&gt;
The build produces signed provenance documenting what produced the artifact. Any build system, any format. Protects against accidental tampering and gives a starting point for verification.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L2 — Hosted build, signed provenance&lt;/strong&gt;
Build runs on a hosted CI platform (GitHub Actions, Cloud Build, etc.). Provenance is generated and signed by the build platform, not the developer&amp;rsquo;s machine. Harder to falsify — an attacker needs to compromise the build platform, not just a developer laptop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L3 — Hardened build&lt;/strong&gt;
Ephemeral, isolated build environment. Non-falsifiable provenance — the build platform itself signs the provenance in a way that even a compromised build script cannot forge. Parameterless builds where the inputs are fully declared.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="provenance-in-practice"&gt;Provenance in practice
&lt;/h2&gt;&lt;p&gt;Provenance is a JSON document (SLSA uses the &lt;a class="link" href="https://github.com/in-toto/attestation" target="_blank" rel="noopener"
 &gt;in-toto Attestation Framework&lt;/a&gt;) that records:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The source repository and commit&lt;/li&gt;
&lt;li&gt;The build platform and workflow&lt;/li&gt;
&lt;li&gt;The artifact digest(s)&lt;/li&gt;
&lt;li&gt;The builder identity&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is signed using &lt;a class="link" href="https://www.sigstore.dev/" target="_blank" rel="noopener"
 &gt;Sigstore&lt;/a&gt; / cosign, which provides keyless signing backed by OIDC identity. No key management required for the common case.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tooling"&gt;Tooling
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;SLSA GitHub Generator&lt;/strong&gt;: reusable GitHub Actions workflows that generate SLSA L3 provenance for common artifact types (Go binaries, container images, Maven/Gradle packages). The easiest path to L3 on GitHub Actions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;cosign&lt;/strong&gt;: signs and verifies container images and provenance attestations. Part of the Sigstore project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;slsa-verifier&lt;/strong&gt;: CLI tool to verify SLSA provenance against an artifact. Used by consumers to check that an artifact meets a required SLSA level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dependency Track / Grype / Trivy&lt;/strong&gt;: SBOM and vulnerability scanning tools that complement SLSA (SLSA is about build integrity, SBOM is about knowing what is in the artifact — related but distinct).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="slsa-and-container-images"&gt;SLSA and container images
&lt;/h2&gt;&lt;p&gt;Container images are a natural fit. The image digest is the artifact, provenance attests to the build that produced it, cosign attaches the attestation to the registry.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# verify an image has SLSA provenance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;slsa-verifier verify-image &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ghcr.io/myorg/myimage@sha256:abc123 &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --source-uri github.com/myorg/myrepo &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --source-tag v1.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/thinking/shared-tooling-images/" &gt;tooling images&lt;/a&gt; pattern — images published to a registry that teams depend on — SLSA provenance is worth adding. It gives consumers a verifiable chain from source to image.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="practical-adoption"&gt;Practical adoption
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;L1&lt;/strong&gt; is low friction. Generate provenance as part of the build, attach it to the release. Most CI platforms make this straightforward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L2&lt;/strong&gt; requires a hosted build platform (most teams already use one) and using the platform&amp;rsquo;s signing rather than rolling your own.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L3&lt;/strong&gt; requires workflow changes — ephemeral environments, parameterless builds, using the SLSA GitHub Generator or equivalent. More investment but achievable for a standard GitHub Actions project.&lt;/p&gt;
&lt;p&gt;Start at L1. Provenance exists, the chain is documented, the habit is established. L2 and L3 follow naturally as the pipeline matures — you are not making a big architectural decision, you are incrementally tightening what you already have.&lt;/p&gt;
&lt;p&gt;In practice: never seen L1, L2, or L3 required explicitly by a client or platform. It is still &amp;ldquo;good idea in theory&amp;rdquo; territory for most teams. That said, the underlying practices — signed builds, traceable artifacts, reproducible pipelines — show up as requirements all the time, just not always under the SLSA name.&lt;/p&gt;
&lt;p&gt;Which points to what SLSA actually is: a name and a framework for practices that are already good ideas. Automate the build, sign the output, record the provenance. Quality follows from automation — it frees up time to iterate, makes review easier, and ensures consistency without manual discipline. SLSA formalises that and gives you a vocabulary to talk about it with others. The levels are a ladder, not a checklist to complete before you can ship.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/thinking/shared-tooling-images/" &gt;Shared Tooling Images&lt;/a&gt;: SLSA provenance applies directly to published tooling images&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/github/" &gt;GitHub Actions&lt;/a&gt;: where SLSA GitHub Generator workflows run&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Bastion / jump server</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bastion/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bastion/</guid><description>&lt;p&gt;A bastion host (jump server) is a single, hardened machine exposed to the outside that acts as the entry point into a private network. You SSH into the bastion, then hop from there to internal hosts — or configure your SSH client to do it transparently in one step.&lt;/p&gt;
&lt;p&gt;The pattern is simple: minimise the network attack surface to one well-monitored machine, harden that machine specifically, and keep everything else off the public internet.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="basic-jump-manual-two-hop"&gt;Basic jump: manual two-hop
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Step 1: SSH into the bastion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh user@bastion.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Step 2: from bastion, SSH into internal host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh user@192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Works, but requires your private key to be on the bastion — which you want to avoid.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="proxyjump-recommended"&gt;ProxyJump (recommended)
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;ProxyJump&lt;/code&gt; tells SSH to tunnel through the bastion transparently. Your key never leaves your local machine.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# One-liner&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh -J user@bastion.example.com user@192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Or in ~/.ssh/config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host internal
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HostName 192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ProxyJump bastion
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host bastion
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HostName bastion.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; IdentityFile ~/.ssh/id_ed25519
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After this, &lt;code&gt;ssh internal&lt;/code&gt; works as a single command with no key on the bastion.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="agent-forwarding-vs-proxyjump"&gt;Agent forwarding vs ProxyJump
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Agent forwarding&lt;/strong&gt; (&lt;code&gt;-A&lt;/code&gt;, &lt;code&gt;ForwardAgent yes&lt;/code&gt;) forwards your SSH agent socket through the bastion so you can authenticate onward with your local key. Older approach — it works but the agent socket is briefly accessible on the bastion host, which is a lateral movement risk if the bastion is compromised.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ProxyJump&lt;/strong&gt; is strictly better for the common case: the connection to the internal host is established from your machine through an SSH tunnel, not from the bastion itself. No agent socket on the bastion.&lt;/p&gt;
&lt;p&gt;Use ProxyJump unless you specifically need agent forwarding for something else.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hardening-basics"&gt;Hardening basics
&lt;/h2&gt;&lt;p&gt;A bastion is only useful if it&amp;rsquo;s actually hardened. Minimum:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /etc/ssh/sshd_config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PasswordAuthentication no
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PermitRootLogin no
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AuthorizedKeysFile .ssh/authorized_keys
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AllowUsers jumpuser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Key-only authentication — no passwords&lt;/li&gt;
&lt;li&gt;Dedicated user with no shell access to the bastion itself (optional: &lt;code&gt;ForceCommand&lt;/code&gt; to restrict what they can do)&lt;/li&gt;
&lt;li&gt;Non-standard port reduces log noise, not actual security&lt;/li&gt;
&lt;li&gt;fail2ban or equivalent for rate-limiting auth attempts&lt;/li&gt;
&lt;li&gt;Minimal installed software — the bastion should do one thing&lt;/li&gt;
&lt;li&gt;Regular log review (&lt;code&gt;/var/log/auth.log&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="restricting-to-jump-only"&gt;Restricting to jump-only
&lt;/h2&gt;&lt;p&gt;If you want users to be able to jump through the bastion but not get a shell on it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# In authorized_keys, prefix the key with:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;restrict,port-forwarding ssh-ed25519 AAAA...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or use &lt;code&gt;AllowTcpForwarding yes&lt;/code&gt; with &lt;code&gt;ForceCommand /usr/sbin/nologin&lt;/code&gt; — though the interaction between these options is subtle; test carefully.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;For making the bastion reachable from outside without a public IP → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunnels/" &gt;Tunnels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;For exposing internal web services via public URLs → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunneled-reverse-proxy/" &gt;Tunneled reverse proxy platforms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Kyverno</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kyverno/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kyverno/</guid><description>&lt;p&gt;Kyverno is a policy engine for Kubernetes. It runs as an admission controller and intercepts every resource creation or update, applying rules that validate, mutate, or generate resources. Policies are written as Kubernetes CRDs in YAML — no Rego, no separate language to learn. If you can write a Kubernetes manifest, you can write a Kyverno policy.&lt;/p&gt;
&lt;h2 id="three-rule-types"&gt;Three rule types
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Validate&lt;/strong&gt; — reject resources that don&amp;rsquo;t meet requirements:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;kyverno.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ClusterPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;require-labels&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;check-team-label&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;match&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;any&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kinds&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;Deployment]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;message&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Deployments must have a &amp;#39;team&amp;#39; label.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;pattern&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;labels&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;team&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;?*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Mutate&lt;/strong&gt; — automatically add or modify fields on admission:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;add-default-resources&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;match&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;any&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kinds&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;Pod]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mutate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;patchStrategicMerge&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;containers&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;(name)&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requests&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;+(memory)&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;64Mi&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;+(cpu)&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;250m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Generate&lt;/strong&gt; — create related resources automatically. A common use: generate a &lt;code&gt;NetworkPolicy&lt;/code&gt; every time a new namespace is created.&lt;/p&gt;
&lt;h2 id="enforcement-vs-audit"&gt;Enforcement vs audit
&lt;/h2&gt;&lt;p&gt;Policies run in &lt;code&gt;enforce&lt;/code&gt; mode (block non-compliant resources) or &lt;code&gt;audit&lt;/code&gt; mode (allow but report violations). Audit mode is the right starting point — understand your existing state before enforcing.&lt;/p&gt;
&lt;h2 id="common-policies"&gt;Common policies
&lt;/h2&gt;&lt;p&gt;The &lt;a class="link" href="https://kyverno.io/policies/" target="_blank" rel="noopener"
 &gt;Kyverno policy library&lt;/a&gt; has ready-made policies for common requirements: disallow privileged containers, require image tags to not be &lt;code&gt;latest&lt;/code&gt;, enforce resource limits, restrict hostPath mounts. Most teams start from the library and customise.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://kyverno.io/docs/" target="_blank" rel="noopener"
 &gt;Kyverno documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kyverno.io/policies/" target="_blank" rel="noopener"
 &gt;Kyverno policy library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Linux Identity Management — FreeIPA and SSSD</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/linux-identity/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/linux-identity/</guid><description>&lt;p&gt;Managing user accounts across many Linux machines by hand — creating the same user on every host, syncing passwords, maintaining sudo rules — breaks down fast. FreeIPA provides centralised identity management: one place to define users, groups, sudo rules, host policies, and SSH keys. SSSD is the daemon that runs on each Linux machine and connects it to FreeIPA (or any LDAP/Kerberos provider), making those central definitions available locally.&lt;/p&gt;
&lt;h2 id="freeipa"&gt;FreeIPA
&lt;/h2&gt;&lt;p&gt;An integrated identity management solution from Red Hat, combining LDAP (389 Directory Server), Kerberos, DNS, a certificate authority, and a web UI into a single deployable stack. Users, groups, sudo rules, HBAC (host-based access control) rules, and SSH public keys are all managed centrally and enforced on enrolled hosts. FreeIPA is the open source upstream of Red Hat Identity Management (IdM).&lt;/p&gt;
&lt;p&gt;Install on RHEL/Rocky/AlmaLinux:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dnf install freeipa-server
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ipa-server-install --domain&lt;span style="color:#f92672"&gt;=&lt;/span&gt;example.com --realm&lt;span style="color:#f92672"&gt;=&lt;/span&gt;EXAMPLE.COM
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the server is running, enroll a client host:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dnf install freeipa-client
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ipa-client-install --server&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ipa.example.com --domain&lt;span style="color:#f92672"&gt;=&lt;/span&gt;example.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After enrollment, users defined in FreeIPA can log into the host with Kerberos SSO — no separate account needed on the machine.&lt;/p&gt;
&lt;h2 id="sssd"&gt;SSSD
&lt;/h2&gt;&lt;p&gt;The System Security Services Daemon. SSSD runs on each Linux host and mediates all identity lookups — NSS (name service switch) queries for users and groups, PAM authentication, sudo rule lookups. It caches responses locally so logins still work when the identity server is temporarily unreachable, and it handles the Kerberos ticket lifecycle transparently.&lt;/p&gt;
&lt;p&gt;SSSD is not FreeIPA-specific. It supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FreeIPA (the natural pairing)&lt;/li&gt;
&lt;li&gt;Active Directory (via the &lt;code&gt;ad&lt;/code&gt; provider — direct AD integration without Samba)&lt;/li&gt;
&lt;li&gt;Generic LDAP and Kerberos&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;ipa-client-install&lt;/code&gt; command configures SSSD automatically when enrolling a FreeIPA client. For AD integration, the configuration is similar but uses the &lt;code&gt;ad&lt;/code&gt; provider:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[domain/example.com]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;id_provider&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;ad&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;auth_provider&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;ad&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;access_provider&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;ad&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ad_domain&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;example.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;krb5_realm&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;EXAMPLE.COM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="the-pairing"&gt;The pairing
&lt;/h2&gt;&lt;p&gt;FreeIPA and SSSD are complementary halves of the same solution. FreeIPA is the authoritative store — where you create and manage identities. SSSD is the enforcer on each host — it translates FreeIPA&amp;rsquo;s policies into local authentication decisions, caches them for resilience, and keeps Kerberos tickets current. Neither replaces the other; together they give you centralised identity management with no single point of failure for login availability.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.freeipa.org/page/Documentation" target="_blank" rel="noopener"
 &gt;FreeIPA documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://sssd.io/docs/" target="_blank" rel="noopener"
 &gt;SSSD documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/managing_idm_users_groups_hosts_and_access_control_rules/" target="_blank" rel="noopener"
 &gt;Red Hat IdM documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>LUKS — Linux Disk Encryption</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/luks/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/luks/</guid><description>&lt;p&gt;LUKS (Linux Unified Key Setup) is the standard for full-disk encryption on Linux. It uses dm-crypt in the kernel to encrypt block devices transparently — the filesystem sits on top of an encrypted layer, and the encryption happens below it. The LUKS header stores the encrypted key material and metadata, supporting up to eight independent key slots (passphrases or keyfiles) that all unlock the same volume.&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Encrypt a device (destroys existing data)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cryptsetup luksFormat /dev/sdb
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Open the encrypted device, exposing it as a plaintext mapper device&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cryptsetup luksOpen /dev/sdb data-encrypted
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Format and use the plaintext device normally&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkfs.ext4 /dev/mapper/data-encrypted
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount /dev/mapper/data-encrypted /mnt/data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Close when done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;umount /mnt/data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cryptsetup luksClose data-encrypted
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="key-slots"&gt;Key slots
&lt;/h2&gt;&lt;p&gt;LUKS supports multiple passphrases or keyfiles — useful for adding a recovery key alongside an operational passphrase:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Add a second key slot (e.g. a recovery keyfile)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cryptsetup luksAddKey /dev/sdb /path/to/recovery.key
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Remove a key slot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cryptsetup luksRemoveKey /dev/sdb
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# List key slot usage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cryptsetup luksDump /dev/sdb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="auto-unlock-at-boot"&gt;Auto-unlock at boot
&lt;/h2&gt;&lt;p&gt;For encrypted root or data partitions that should unlock automatically on boot, add the device to &lt;code&gt;/etc/crypttab&lt;/code&gt; with a keyfile path:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;data-encrypted /dev/sdb /etc/keys/data.key luks
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then add the plaintext device to &lt;code&gt;/etc/fstab&lt;/code&gt; as normal. On servers, the keyfile is stored on a separate volume or fetched from a secrets manager (Vault, Tang/Clevis for network-bound disk encryption).&lt;/p&gt;
&lt;h2 id="use-in-kubernetes"&gt;Use in Kubernetes
&lt;/h2&gt;&lt;p&gt;Node-level disk encryption with LUKS protects data at rest on Kubernetes worker nodes — persistent volume data stored on the node&amp;rsquo;s disks is encrypted before it leaves the kernel. Talos Linux enables LUKS encryption for its state and ephemeral partitions by default.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://gitlab.com/cryptsetup/cryptsetup/-/wikis/home" target="_blank" rel="noopener"
 &gt;cryptsetup documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://wiki.archlinux.org/title/dm-crypt" target="_blank" rel="noopener"
 &gt;dm-crypt reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Managing Secrets in Kubernetes</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/k8s-secrets/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/k8s-secrets/</guid><description>&lt;p&gt;Kubernetes has a built-in &lt;code&gt;Secret&lt;/code&gt; resource, but it is not a secrets management solution — it is base64-encoded storage with no encryption at rest by default and no access audit trail. How you actually manage secrets in a Kubernetes cluster depends on how far you need to go beyond the default.&lt;/p&gt;
&lt;h2 id="native-kubernetes-secrets"&gt;Native Kubernetes Secrets
&lt;/h2&gt;&lt;p&gt;The baseline. A &lt;code&gt;Secret&lt;/code&gt; is a key-value store mounted into pods as environment variables or files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Secret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;db-credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Opaque&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;data&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;username&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;YWRtaW4= &lt;/span&gt; &lt;span style="color:#75715e"&gt;# base64(&amp;#34;admin&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;password&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cGFzc3dvcmQ=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The problems: base64 is encoding, not encryption. Secrets are stored in etcd — enabling etcd encryption at rest is a cluster configuration step that is easy to skip. Secrets are visible to anyone with &lt;code&gt;kubectl get secret&lt;/code&gt; in that namespace. For anything beyond a local dev cluster or a low-sensitivity workload, you need something more.&lt;/p&gt;
&lt;h2 id="sealed-secrets"&gt;Sealed Secrets
&lt;/h2&gt;&lt;p&gt;A Kubernetes controller from Bitnami. &lt;code&gt;SealedSecret&lt;/code&gt; resources contain secrets encrypted with the cluster&amp;rsquo;s public key — only the controller running in that cluster can decrypt them. The encrypted form is safe to commit to Git, which makes GitOps workflows possible without a separate secrets store. Simple to operate, no external dependency. The tradeoff: secrets are tied to a specific cluster&amp;rsquo;s key, cross-cluster sharing requires re-encryption, and there is no centralised audit trail.&lt;/p&gt;
&lt;h2 id="external-secrets-operator"&gt;External Secrets Operator
&lt;/h2&gt;&lt;p&gt;ESO reads secrets from an external store (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, Azure Key Vault, 1Password) and syncs them into native Kubernetes Secrets. Your source of truth stays in the external system; the K8s Secret is a read-only projection of it, refreshed on a configurable interval:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;external-secrets.io/v1beta1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ExternalSecret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;db-credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;refreshInterval&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;secretStoreRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;aws-secrets-manager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ClusterSecretStore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;target&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;db-credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;data&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;secretKey&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;remoteRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;prod/db/password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ESO is the right choice when you already have a secrets store and want Kubernetes workloads to consume from it without changing how secrets are managed elsewhere.&lt;/p&gt;
&lt;h2 id="secrets-store-csi-driver"&gt;Secrets Store CSI Driver
&lt;/h2&gt;&lt;p&gt;An alternative to ESO for the same problem: mount secrets from an external store directly as files in a pod, without creating a Kubernetes Secret at all. The secret materialises only in the pod&amp;rsquo;s filesystem, is not stored in etcd, and disappears when the pod terminates. Supported by AWS, Azure, GCP, and Vault providers. Used in combination with a &lt;code&gt;SecretProviderClass&lt;/code&gt; to define what to fetch and where to mount it.&lt;/p&gt;
&lt;h2 id="hashicorp-vault"&gt;HashiCorp Vault
&lt;/h2&gt;&lt;p&gt;A dedicated secrets management platform. Vault stores arbitrary secrets, issues dynamic credentials (database passwords that expire, AWS IAM credentials valid for an hour), manages PKI, and provides a full audit log of every read and write. Kubernetes workloads authenticate to Vault via the Kubernetes auth method (using the pod&amp;rsquo;s service account token) and receive a Vault token scoped to the secrets their service account is allowed to read. More to operate than the other options, but the right answer for organisations that need dynamic credentials, fine-grained access control, and audit logs.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Approach&lt;/th&gt;
 &lt;th&gt;Good for&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Native Secrets&lt;/td&gt;
 &lt;td&gt;Local dev, low-sensitivity workloads&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Sealed Secrets&lt;/td&gt;
 &lt;td&gt;GitOps, single-cluster, no external dependency&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;External Secrets Operator&lt;/td&gt;
 &lt;td&gt;Syncing from existing external stores&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Secrets Store CSI&lt;/td&gt;
 &lt;td&gt;Avoiding etcd entirely, file-based secret injection&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;HashiCorp Vault&lt;/td&gt;
 &lt;td&gt;Dynamic credentials, audit logs, enterprise requirements&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/bitnami-labs/sealed-secrets" target="_blank" rel="noopener"
 &gt;Sealed Secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://external-secrets.io/" target="_blank" rel="noopener"
 &gt;External Secrets Operator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://secrets-store-csi-driver.sigs.k8s.io/" target="_blank" rel="noopener"
 &gt;Secrets Store CSI Driver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.hashicorp.com/vault/docs/platform/k8s" target="_blank" rel="noopener"
 &gt;HashiCorp Vault on Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Security Scanning &amp; Monitoring</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/security-scanning/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/security-scanning/</guid><description>&lt;p&gt;Security tooling broadly splits into three concerns: what vulnerabilities exist in your software before it runs (image scanning), what is actually happening on your systems while they run (endpoint monitoring), and what is moving across your network (intrusion detection). Clair, osquery, and SNORT each cover one of these.&lt;/p&gt;
&lt;h2 id="clair"&gt;Clair
&lt;/h2&gt;&lt;p&gt;A static analysis tool for container image vulnerability scanning, from the Quay project (Red Hat). Clair maintains a database of CVEs from multiple sources (NVD, Red Hat, Debian, Alpine, Ubuntu) and matches them against the packages installed in a container image layer by layer. Integrated into a container registry, it scans every image on push and blocks or flags images with known vulnerabilities above a configurable severity threshold. The result is a vulnerability report tied to the image digest — not the running container, but the image itself before it&amp;rsquo;s ever deployed.&lt;/p&gt;
&lt;h2 id="osquery"&gt;osquery
&lt;/h2&gt;&lt;p&gt;Facebook&amp;rsquo;s open source endpoint monitoring tool. osquery exposes the operating system as a relational database — processes, users, network connections, installed packages, kernel modules, scheduled tasks, browser extensions — all queryable with SQL. This makes ad-hoc security investigation fast (a single query answers &amp;ldquo;which processes are listening on unexpected ports?&amp;rdquo;) and continuous monitoring straightforward (schedule queries, collect results centrally, alert on anomalies). osquery runs on Linux, macOS, and Windows. Used standalone it is a powerful investigation tool; integrated with a SIEM or fleet management layer (Fleet, Kolide) it becomes a continuous compliance and detection platform.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;-- Processes with open network connections
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; p.name, p.pid, l.address, l.port
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; processes p
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;JOIN&lt;/span&gt; listening_ports l &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; p.pid &lt;span style="color:#f92672"&gt;=&lt;/span&gt; l.pid
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; l.port &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;IN&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;22&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;443&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="snort"&gt;SNORT
&lt;/h2&gt;&lt;p&gt;A network intrusion detection and prevention system (IDS/IPS). SNORT inspects network traffic in real time against a ruleset — signatures for known attack patterns, protocol anomalies, port scans, exploit attempts. In IDS mode it logs and alerts; in IPS mode it can drop matching packets inline. SNORT rules are expressive and the community ruleset (Snort Community Rules, Emerging Threats) covers a wide range of threats. Placed at a network chokepoint — in front of a server, at the edge of a network segment — it gives visibility into what traffic is actually reaching your systems and can detect lateral movement, exfiltration attempts, and known exploits in transit.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://quay.github.io/clair/" target="_blank" rel="noopener"
 &gt;Clair documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://osquery.readthedocs.io/" target="_blank" rel="noopener"
 &gt;osquery documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.snort.org/documents" target="_blank" rel="noopener"
 &gt;SNORT documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://rules.emergingthreats.net/" target="_blank" rel="noopener"
 &gt;Emerging Threats ruleset&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>SSH</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/</guid><description>&lt;p&gt;SSH (Secure Shell) is the standard protocol for encrypted remote access to Linux and Unix systems. It replaced telnet and rsh by wrapping the session in a cryptographic tunnel — authentication, commands, and data transfer all protected against interception and tampering.&lt;/p&gt;
&lt;h2 id="public-key-authentication"&gt;Public key authentication
&lt;/h2&gt;&lt;p&gt;The default auth mechanism for any serious setup. You generate a key pair: a private key that never leaves your machine, and a public key that goes on the remote host.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-keygen -t ed25519 -C &lt;span style="color:#e6db74"&gt;&amp;#34;your@email.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-copy-id user@host &lt;span style="color:#75715e"&gt;# appends public key to ~/.ssh/authorized_keys on remote&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Prefer &lt;code&gt;ed25519&lt;/code&gt; over the older &lt;code&gt;rsa&lt;/code&gt; — smaller keys, faster, stronger. Keep your private key protected with a passphrase; use &lt;code&gt;ssh-agent&lt;/code&gt; to avoid typing it repeatedly.&lt;/p&gt;
&lt;h2 id="key-deployment-at-scale"&gt;Key deployment at scale
&lt;/h2&gt;&lt;p&gt;When you have many hosts, distributing public keys manually doesn&amp;rsquo;t scale. A typical pattern: submit your public key to a central approval system, which then deploys it to the relevant hosts via automation (Ansible, Puppet, etc.).&lt;/p&gt;
&lt;p&gt;&lt;img alt="SSH key deployment flow" class="gallery-image" data-flex-basis="480px" data-flex-grow="200" height="1280" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm.png" srcset="https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm_hu_7067fdd6ddfbd76d.png 800w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm_hu_a732934bde95612.png 1600w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm_hu_4339d3dc0eb426e1.png 2400w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm.png 2560w" width="2560"&gt;&lt;/p&gt;
&lt;p&gt;This keeps a clear audit trail — keys are approved, recorded, and can be revoked centrally without touching individual &lt;code&gt;authorized_keys&lt;/code&gt; files.&lt;/p&gt;
&lt;h2 id="certificate-authentication"&gt;Certificate authentication
&lt;/h2&gt;&lt;p&gt;At larger scale, even centralised key distribution gets unwieldy. SSH certificates solve this properly: instead of deploying individual public keys, you run an SSH Certificate Authority (CA). Hosts and users trust the CA, not each other&amp;rsquo;s individual keys.&lt;/p&gt;
&lt;p&gt;&lt;img alt="SSH certificate authentication architecture" class="gallery-image" data-flex-basis="528px" data-flex-grow="220" height="1018" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-certificate-authentication.png" srcset="https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-certificate-authentication_hu_3c89cbd02831450b.png 800w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-certificate-authentication_hu_98e8f600ea8421b2.png 1600w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-certificate-authentication.png 2242w" width="2242"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generate a key pair locally&lt;/li&gt;
&lt;li&gt;Submit the public key to the SSH CA (backed by your identity provider — FreeIPA, Vault, etc.)&lt;/li&gt;
&lt;li&gt;The CA issues a signed certificate with a short TTL&lt;/li&gt;
&lt;li&gt;SSH to any host — the host trusts the CA, so the certificate is accepted without any pre-deployed keys&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Short-lived certificates (hours, not days) dramatically reduce the blast radius of a compromised credential. No revocation lists to maintain.&lt;/p&gt;
&lt;h2 id="ssh-config"&gt;SSH config
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;~/.ssh/config&lt;/code&gt; avoids repetitive flags on every connection:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Host bastion
 HostName bastion.example.com
 User deploy
 IdentityFile ~/.ssh/id_ed25519
 ForwardAgent yes

Host internal-*
 ProxyJump bastion
 User deploy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;ProxyJump&lt;/code&gt; (formerly &lt;code&gt;-J&lt;/code&gt;) lets you reach hosts that aren&amp;rsquo;t directly accessible — you SSH through the bastion transparently.&lt;/p&gt;
&lt;h2 id="port-forwarding"&gt;Port forwarding
&lt;/h2&gt;&lt;p&gt;SSH can tunnel TCP traffic, useful for reaching services on private networks:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Local forward: reach remote Postgres via localhost:5432&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh -L 5432:db.internal:5432 bastion
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Dynamic forward: SOCKS proxy through the bastion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh -D &lt;span style="color:#ae81ff"&gt;1080&lt;/span&gt; bastion
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="hardening-basics"&gt;Hardening basics
&lt;/h2&gt;&lt;p&gt;Key settings in &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers deploy ansible
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Disable password auth entirely once keys are in place. Restrict which users can log in. Run &lt;code&gt;sshd -t&lt;/code&gt; to validate config before reloading.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.openssh.com/manual.html" target="_blank" rel="noopener"
 &gt;OpenSSH manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.hashicorp.com/vault/docs/secrets/ssh/signed-ssh-certificates" target="_blank" rel="noopener"
 &gt;SSH certificate authentication (HashiCorp Vault)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://infosec.mozilla.org/guidelines/openssh" target="_blank" rel="noopener"
 &gt;Mozilla SSH Guidelines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>TLS Certificates — Let's Encrypt, Certbot, cert-manager</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/tls-certificates/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/tls-certificates/</guid><description>&lt;p&gt;TLS certificates prove that a server is who it claims to be and encrypt traffic in transit. Getting and renewing them used to mean manual requests to a CA, waiting days, and calendar reminders to renew before expiry. Let&amp;rsquo;s Encrypt automated the entire process in 2015 and made it free. Today there is no reason to run a public service without TLS.&lt;/p&gt;
&lt;h2 id="lets-encrypt"&gt;Let&amp;rsquo;s Encrypt
&lt;/h2&gt;&lt;p&gt;A free, automated, open certificate authority run by the Internet Security Research Group. Let&amp;rsquo;s Encrypt issues Domain Validation (DV) certificates valid for 90 days, automatically, via the ACME protocol. The short lifetime is intentional — it forces automation and limits the window if a certificate is compromised. Let&amp;rsquo;s Encrypt is now the largest CA in the world by certificates issued. You never interact with Let&amp;rsquo;s Encrypt directly; you use an ACME client that speaks the protocol on your behalf.&lt;/p&gt;
&lt;h2 id="certbot"&gt;Certbot
&lt;/h2&gt;&lt;p&gt;The EFF&amp;rsquo;s ACME client — the most widely used way to obtain and renew Let&amp;rsquo;s Encrypt certificates on a Linux server. Certbot handles the ACME challenge (proving you control the domain), fetches the certificate, installs it into your web server config (nginx, Apache), and sets up a cron job or systemd timer for automatic renewal. For a straightforward public-facing server, Certbot is the default choice:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;certbot --nginx -d example.com -d www.example.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Certbot supports HTTP-01 challenges (serve a file over port 80) and DNS-01 challenges (add a TXT record — required for wildcard certificates and useful when port 80 isn&amp;rsquo;t accessible).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Wildcard cert via DNS challenge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;certbot certonly --manual --preferred-challenges dns -d &lt;span style="color:#e6db74"&gt;&amp;#34;*.example.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Renewals run automatically. Check the timer is active:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;systemctl status certbot.timer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="cert-manager"&gt;cert-manager
&lt;/h2&gt;&lt;p&gt;The Kubernetes-native way to manage certificates. cert-manager runs as a controller in the cluster and automates the full certificate lifecycle — requesting, renewing, and storing certificates as Kubernetes &lt;code&gt;Secret&lt;/code&gt; resources. It supports Let&amp;rsquo;s Encrypt via ACME (HTTP-01 and DNS-01 challenges) as well as other issuers (Vault, Venafi, self-signed).&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;ClusterIssuer&lt;/code&gt; configures the CA once for the whole cluster:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ClusterIssuer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;letsencrypt-prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;acme&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;email&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;admin@example.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;privateKeySecretRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;letsencrypt-prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;solvers&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;http01&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ingress&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;class&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;alb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A &lt;code&gt;Certificate&lt;/code&gt; resource then requests a cert for a specific domain — or an Ingress annotation triggers cert-manager automatically. The certificate is stored as a Secret and mounted into pods or referenced by the Ingress. Renewal happens automatically before expiry.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://letsencrypt.org/docs/" target="_blank" rel="noopener"
 &gt;Let&amp;rsquo;s Encrypt documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://certbot.eff.org/docs/" target="_blank" rel="noopener"
 &gt;Certbot documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://cert-manager.io/docs/" target="_blank" rel="noopener"
 &gt;cert-manager documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>