<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: John Loper</title>
    <description>The latest articles on Forem by John Loper (@jloper3).</description>
    <link>https://forem.com/jloper3</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F91515%2Fb35d0f90-48d6-49b1-9133-7438cf968b7c.png</url>
      <title>Forem: John Loper</title>
      <link>https://forem.com/jloper3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jloper3"/>
    <language>en</language>
    <item>
      <title>EnvSecOps: The Obviously Painful Path Forward</title>
      <dc:creator>John Loper</dc:creator>
      <pubDate>Wed, 12 Nov 2025 23:17:39 +0000</pubDate>
      <link>https://forem.com/jloper3/envsecops-the-obviously-painful-path-forward-3gaa</link>
      <guid>https://forem.com/jloper3/envsecops-the-obviously-painful-path-forward-3gaa</guid>
      <description>&lt;h2&gt;
  
  
  Turning “Check the Bag” into a Working System
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The idea was simple: verify the bag, not the badge.&lt;br&gt;
The hard part is turning that into a system someone can actually build.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;actionable roadmap&lt;/strong&gt; — not a manifesto, not another “zero-trust” slide.&lt;br&gt;
It’s the pragmatic route to the world described in &lt;em&gt;Check the Bag&lt;/em&gt;: a unified model where credentials are issued only if the environment proves itself clean.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task 1 — Identity &amp;amp; Attestation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; unify workload and environment identity under one auditable standard.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Evaluate &lt;strong&gt;SPIFFE/SPIRE&lt;/strong&gt; as the &lt;strong&gt;Identity Control Plane&lt;/strong&gt; for all non-human workloads.&lt;/li&gt;
&lt;li&gt;SPIRE becomes the source of truth for what runs where, and under what evidence.&lt;/li&gt;
&lt;li&gt;Attestation plugins can integrate OS, hardware, and custom attributes (like the Nix or devcontainer signature from Task 2).&lt;/li&gt;
&lt;li&gt;Every service, job, or build agent receives its identity through SPIRE issuance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; a cryptographically provable binding between &lt;em&gt;a workload&lt;/em&gt; and &lt;em&gt;its attested environment&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task 2 — Reproducible Environments
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; make “the bag” reproducible and attestable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stand up a PoC comparing &lt;strong&gt;Nix&lt;/strong&gt; and &lt;strong&gt;DevContainers&lt;/strong&gt; for reproducible dev/runtime environments.&lt;/li&gt;
&lt;li&gt;The output must be deterministic: a Nix &lt;strong&gt;closure hash&lt;/strong&gt; or a signed &lt;strong&gt;devcontainer manifest&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Feed that hash into SPIRE’s attestation selector — so credentials can only be issued if the environment matches a known, reproducible build.&lt;/li&gt;
&lt;li&gt;Document developer workflow friction early; this is where usability sinks most pilots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; every environment can prove &lt;em&gt;exactly what it is&lt;/em&gt; — not just who’s using it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task 3 — Policy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; codify trust decisions in one place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adopt &lt;strong&gt;Open Policy Agent (OPA)&lt;/strong&gt; as the &lt;strong&gt;global Policy Decision Point (PDP)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;All attestation, identity, and credential issuance checks route through OPA.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OPA policies evaluate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether an environment is in policy,&lt;/li&gt;
&lt;li&gt;whether its attestation is valid,&lt;/li&gt;
&lt;li&gt;and what privileges the resulting identity should get.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Extend existing CI/CD gates with &lt;code&gt;conftest&lt;/code&gt; for developer feedback loops.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; the organization’s trust logic is versioned, testable, and enforced everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task 4 — Provenance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; link build outputs to their attested inputs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require &lt;strong&gt;Sigstore&lt;/strong&gt; signing for all build artifacts.&lt;/li&gt;
&lt;li&gt;Integrate Sigstore’s Rekor log and Fulcio CA for transparent verification.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;strong&gt;SLSA v1.0&lt;/strong&gt; as the minimum compliance level; pipelines must produce signed provenance linking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the SPIRE-attested environment (from Task 1–2),&lt;/li&gt;
&lt;li&gt;the OPA-approved policy decision (Task 3),&lt;/li&gt;
&lt;li&gt;and the final artifact digest.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Store provenance as evidence, not paperwork.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; an end-to-end chain of custody — from developer environment to running workload.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting It Together
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pillar&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Identity&lt;/td&gt;
&lt;td&gt;SPIFFE/SPIRE&lt;/td&gt;
&lt;td&gt;Verifiable workload identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;Nix / DevContainers&lt;/td&gt;
&lt;td&gt;Deterministic, attested environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy&lt;/td&gt;
&lt;td&gt;OPA / Conftest&lt;/td&gt;
&lt;td&gt;Centralized, auditable trust logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provenance&lt;/td&gt;
&lt;td&gt;Sigstore / SLSA&lt;/td&gt;
&lt;td&gt;Signed, traceable artifact lineage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This stack isn’t speculative — it’s all open, CNCF-anchored, and proven at scale.&lt;br&gt;
What’s missing isn’t tech; it’s discipline and integration glue.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Is the Path Forward
&lt;/h2&gt;

&lt;p&gt;Because the opposite has already failed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secrets rotate but drift remains.&lt;/li&gt;
&lt;li&gt;Tokens expire but trust persists unearned.&lt;/li&gt;
&lt;li&gt;“Zero-trust” dashboards glow while production runs on :latest.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This model replaces vibes with verification.&lt;br&gt;
It’s painful, yes — but every painful system you build now prevents a post-incident rebuild later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Stack, Why Now
&lt;/h2&gt;

&lt;p&gt;Five years ago, you couldn't build this. The pieces didn't exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SPIFFE/SPIRE was alpha code&lt;/li&gt;
&lt;li&gt;SLSA was a Google internal concept&lt;/li&gt;
&lt;li&gt;Sigstore didn't exist&lt;/li&gt;
&lt;li&gt;DevContainers were a VS Code experiment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today, it's all CNCF-graduated, production-proven, and free.&lt;/p&gt;

&lt;p&gt;The industry also just learned the hard cost of NOT doing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CircleCI (2023): Session token theft → full production access&lt;/li&gt;
&lt;li&gt;LastPass (2022): DevOps engineer's compromised laptop → vault breach
&lt;/li&gt;
&lt;li&gt;Okta (2024): Support engineer's personal device → customer data access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern is identical: &lt;strong&gt;trusted identity + untrusted environment = incident.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The tech to fix this finally exists. The question is whether your org will adopt it before or after your breach.&lt;/p&gt;




&lt;h3&gt;
  
  
  RC = 0
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;You don’t need another framework.&lt;br&gt;
You need fewer excuses.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Identity, Environment, Policy, Provenance — that’s the whole playbook.&lt;br&gt;
Pin them, wire them, and stop pretending automation is trust.&lt;/p&gt;

</description>
      <category>envsecops</category>
      <category>security</category>
      <category>devops</category>
      <category>openssf</category>
    </item>
    <item>
      <title>Pin It or Bin It</title>
      <dc:creator>John Loper</dc:creator>
      <pubDate>Wed, 12 Nov 2025 22:28:25 +0000</pubDate>
      <link>https://forem.com/jloper3/pin-it-or-bin-it-3b4l</link>
      <guid>https://forem.com/jloper3/pin-it-or-bin-it-3b4l</guid>
      <description>&lt;p&gt;&lt;em&gt;How to Stop Shipping Garbage Before It Ships You&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If your build still pulls &lt;code&gt;ubuntu:latest&lt;/code&gt;, you’re not “cloud-native.” You’re running a randomizer in production.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Pinning Matters
&lt;/h2&gt;

&lt;p&gt;Every tag that isn’t a digest is a liability.&lt;br&gt;
&lt;code&gt;:latest&lt;/code&gt; is not convenience; it’s negligence.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you write&lt;/th&gt;
&lt;th&gt;What happens next&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FROM node:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You rebuild on Tuesday and ship a new kernel vulnerability.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pip install flask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You pull in a supply-chain bomb before lunch.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You silently upgrade the AWS provider, then wonder why IAM exploded.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you don’t pin it, you can’t attest it.&lt;br&gt;
If you can’t attest it, you can’t trust it.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Tools of the Trade
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;OPA (Open Policy Agent)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A fast, embeddable policy engine.&lt;br&gt;
It doesn’t care what file you feed it — only that you speak JSON.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Conftest&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A CLI built on top of OPA.&lt;br&gt;
It handles the boring bits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;parses Dockerfiles, YAML, Terraform, JSON&lt;/li&gt;
&lt;li&gt;feeds them into OPA&lt;/li&gt;
&lt;li&gt;exits 1 on fail, 0 on pass
That’s all you need for CI or pre-commit hooks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS / Linux&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;conftest@0.56.0
&lt;span class="c"&gt;# or&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/open-policy-agent/conftest/releases/latest/download/conftest_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; /usr/local/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;conftest &lt;span class="nb"&gt;test &lt;/span&gt;Dockerfile &lt;span class="nt"&gt;--parser&lt;/span&gt; dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will parse your Dockerfile into JSON, run your Rego rules, and tell you exactly why your laziness failed policy.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Crane&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Part of Google’s &lt;code&gt;go-containerregistry&lt;/code&gt; toolkit.&lt;br&gt;
It’s the BOFH’s Swiss army knife for container registries.&lt;/p&gt;

&lt;p&gt;Use it to fetch digests, manifests, and metadata — fast, no daemon, no Docker socket.&lt;/p&gt;

&lt;p&gt;Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;go-containerregistry
&lt;span class="c"&gt;# or&lt;/span&gt;
go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/google/go-containerregistry/cmd/crane@0.20.02
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pin any image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python:3.12
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;crane digest &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python:3.12@sha256:deadbeefbadc0ffee123456789...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you know &lt;strong&gt;exactly&lt;/strong&gt; which bits are running in prod.&lt;br&gt;
No excuses, no surprises.&lt;/p&gt;


&lt;h2&gt;
  
  
  🪓 Policy as Code: The OPA Way
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;policies/dockerfile/pinning.rego&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rego"&gt;&lt;code&gt;&lt;span class="ow"&gt;package&lt;/span&gt; &lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dockerfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pinning&lt;/span&gt;

&lt;span class="n"&gt;deny&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ow"&gt;some&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
  &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"from"&lt;/span&gt;
  &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"@sha256:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"FROM %q missing digest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;deny&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"user"&lt;/span&gt;
  &lt;span class="n"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"root"&lt;/span&gt;
  &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s2"&gt;"Dockerfile must set non-root USER"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test it manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;conftest &lt;span class="nb"&gt;test &lt;/span&gt;Dockerfile &lt;span class="nt"&gt;--parser&lt;/span&gt; dockerfile &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FAIL - Dockerfile - FROM "ubuntu:latest" missing digest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧷 Make It Automatic: Pre-commit Hook
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.git/hooks/pre-commit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-eu&lt;/span&gt;
conftest pull ghcr.io/your-org/precommit:1 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--cached&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Ei&lt;/span&gt; &lt;span class="s1"&gt;'(Dockerfile|\.ya?ml)$'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
conftest &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--parser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dockerfile &lt;span class="nt"&gt;--output&lt;/span&gt; table &lt;span class="nv"&gt;$files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try to commit a floating Dockerfile and watch it fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FAIL - Dockerfile - FROM "nginx:latest" missing digest
✖ OPA pre-commit checks failed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success: laziness stopped at the gate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Philosophy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small tools.&lt;/strong&gt; &lt;code&gt;crane&lt;/code&gt; for digests. &lt;code&gt;conftest&lt;/code&gt; for checks. &lt;code&gt;jq&lt;/code&gt; and &lt;code&gt;yq&lt;/code&gt; for glue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero GUIs.&lt;/strong&gt; If it doesn’t fit in a pipeline, it doesn’t belong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast failure.&lt;/strong&gt; The only “UX” a BOFH cares about is &lt;code&gt;exit 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Determinism &amp;gt; decoration.&lt;/strong&gt; Pretty dashboards are for people who don’t read logs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pin everything&lt;/strong&gt; — &lt;code&gt;image@sha256:digest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write Rego policies&lt;/strong&gt; once; run everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Conftest&lt;/strong&gt; in pre-commit and CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Crane&lt;/strong&gt; to fetch digests and enforce reproducibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate the yelling&lt;/strong&gt; so you can focus on real problems.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>envsecops</category>
      <category>security</category>
      <category>devops</category>
      <category>devsecops</category>
    </item>
    <item>
      <title>Pin It or Bin It (for the brewsters)</title>
      <dc:creator>John Loper</dc:creator>
      <pubDate>Wed, 12 Nov 2025 14:05:29 +0000</pubDate>
      <link>https://forem.com/jloper3/pin-it-or-bin-it-for-the-brewsters-4aoe</link>
      <guid>https://forem.com/jloper3/pin-it-or-bin-it-for-the-brewsters-4aoe</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vw5pu7v7oc2lcgtnxta.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vw5pu7v7oc2lcgtnxta.gif" alt="EnvSecOps" width="245" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Opening jab
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;You can’t secure what you can’t reproduce.&lt;br&gt;
“Latest” isn’t a strategy — it’s a confession that you don’t control your own environment.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Real Problem
&lt;/h2&gt;

&lt;p&gt;Most “secure” pipelines fall apart before they start.&lt;br&gt;
Developers run different OPA versions.&lt;br&gt;
CI installs whatever Homebrew happens to serve that day.&lt;br&gt;
And your attestation chain begins with &lt;code&gt;curl https://sh.somestartup.io | bash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If your tooling isn’t pinned, your policies are theater.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Only Acceptable Rule
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Every tool in the critical path — compiler, linter, scanner, policy engine, image builder — &lt;strong&gt;must be pinned by version and checksum&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You don’t need a platform for this. You need &lt;strong&gt;discipline&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Homebrew, Done Right
&lt;/h2&gt;

&lt;p&gt;Homebrew is fine &lt;em&gt;if you cage it properly&lt;/em&gt;.&lt;br&gt;
Here’s how adults install their tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. install specific versions&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;open-policy-agent/tap/opa@1.1.1
brew &lt;span class="nb"&gt;install &lt;/span&gt;instrumenta/instrumenta/conftest@0.56.0
brew &lt;span class="nb"&gt;install &lt;/span&gt;go-containerregistry@0.20.2

&lt;span class="c"&gt;# 2. freeze them in place&lt;/span&gt;
brew pin opa
brew pin conftest
brew pin go-containerregistry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your version isn’t directly available, extract it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew extract &lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.56.0 conftest instrumenta/instrumenta
brew &lt;span class="nb"&gt;install &lt;/span&gt;instrumenta/instrumenta/conftest@0.56.0
brew pin conftest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can rebuild your environment on any box, any day, and get the same bits.&lt;/p&gt;

&lt;p&gt;No curl-to-bash. No “latest.”&lt;br&gt;
If it’s not signed, hashed, or attested, it doesn’t enter &lt;code&gt;$PATH&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Lock Your Provenance
&lt;/h2&gt;

&lt;p&gt;Your toolchain is part of your SBOM.&lt;br&gt;
Export it like any other dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew bundle dump &lt;span class="nt"&gt;--file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./Brewfile.lock.json
git add Brewfile.lock.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is now the &lt;strong&gt;attestable manifest&lt;/strong&gt; for your developer environment.&lt;br&gt;
Your CI and devcontainers can reproduce it 1:1.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Pinning is provenance.&lt;br&gt;
It’s the difference between “I built this” and “something built this once, maybe.”&lt;br&gt;
Without it, your SBOM is fiction and your attestations are decorative art.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;first layer&lt;/strong&gt; of EnvSecOps:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Trust begins with reproducibility.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;brew install …@version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;explicit version&lt;/td&gt;
&lt;td&gt;deterministic installs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;brew pin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;freeze it&lt;/td&gt;
&lt;td&gt;prevent drift&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;brew bundle dump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;export&lt;/td&gt;
&lt;td&gt;attest environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sha256sum -c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;verify&lt;/td&gt;
&lt;td&gt;don’t trust strangers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;jq&lt;/code&gt;, &lt;code&gt;yq&lt;/code&gt;, &lt;code&gt;crane&lt;/code&gt;, &lt;code&gt;conftest&lt;/code&gt;, &lt;code&gt;opa&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;small tools&lt;/td&gt;
&lt;td&gt;big security&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Exit Code
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If your pipeline depends on &lt;code&gt;brew upgrade&lt;/code&gt;, it’s not a pipeline — it’s a coin toss.&lt;br&gt;
Version-lock it, checksum it, and move on.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>sdlc</category>
      <category>security</category>
      <category>envsecops</category>
      <category>devops</category>
    </item>
    <item>
      <title>EnvSecOps - What It Actually Is (And Why DevSecOps Won't Cut It)</title>
      <dc:creator>John Loper</dc:creator>
      <pubDate>Sat, 08 Nov 2025 15:46:21 +0000</pubDate>
      <link>https://forem.com/jloper3/envsecops-what-it-actually-is-and-why-devsecops-wont-cut-it-43nh</link>
      <guid>https://forem.com/jloper3/envsecops-what-it-actually-is-and-why-devsecops-wont-cut-it-43nh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Meme recap:&lt;/strong&gt; Old security checks IDs. &lt;strong&gt;EnvSecOps&lt;/strong&gt; checks IDs &lt;strong&gt;and the bag&lt;/strong&gt;—the actual execution environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vw5pu7v7oc2lcgtnxta.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vw5pu7v7oc2lcgtnxta.gif" alt="EnvSecOps" width="245" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post pins down what EnvSecOps is, where it sits next to DevOps/DevSecOps, and why it closes the door those frameworks leave open.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR (30 seconds)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DevOps&lt;/strong&gt;: ship faster (automation, reliability).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DevSecOps&lt;/strong&gt;: ship safer code (shift-left testing, compliance).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EnvSecOps&lt;/strong&gt;: &lt;strong&gt;only issue credentials when the environment proves it’s policy-approved right now&lt;/strong&gt;—and keep re-verifying on renewal. If the bag mutates, the token dies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;EnvSecOps secures &lt;em&gt;where&lt;/em&gt; work is performed (dev, CI/CD, ops terminals) instead of assuming those machines are clean.&lt;/p&gt;




&lt;h2&gt;
  
  
  Assumptions (explicit)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform examples:&lt;/strong&gt; Kubernetes (pods, service accounts, namespaces) and cloud IAM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity:&lt;/strong&gt; OIDC for humans/CI; SPIFFE/SPIRE for workloads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evidence:&lt;/strong&gt; Signed attestations (image/tooling/config digests; provenance).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy:&lt;/strong&gt; OPA/Cedar to express “only approved tooling,” short TTLs, and runtime disqualifiers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime signals:&lt;/strong&gt; eBPF-driven tools (Falco/Tetragon) to catch both on-disk and in-memory tricks.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The pattern is product-agnostic. Swap components as needed; the &lt;strong&gt;gate&lt;/strong&gt; is the invariant.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What DevOps Solved (and didn’t)
&lt;/h2&gt;

&lt;p&gt;DevOps removed handoffs and manual toil—CD pipelines, IaC, fast recovery. Security lived mostly in &lt;em&gt;operations&lt;/em&gt;: hardening, monitoring, incident response. Useful, but &lt;strong&gt;post-issuance&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What DevSecOps Added (and where it stops)
&lt;/h2&gt;

&lt;p&gt;DevSecOps pushes security left—SAST/DAST, dependency and image scanning, policy checks in CI. It answers &lt;strong&gt;“Is this code/artifact safe?”&lt;/strong&gt; It &lt;strong&gt;assumes&lt;/strong&gt; the runner/terminal doing the work is trustworthy.&lt;/p&gt;

&lt;p&gt;That assumption is the hole: if the environment running the scanner/deployer is compromised, DevSecOps happily validates the attacker’s workflow too.&lt;/p&gt;




&lt;h2&gt;
  
  
  EnvSecOps, precisely
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt; A credential exists &lt;strong&gt;only&lt;/strong&gt; if the requesting environment can &lt;strong&gt;cryptographically prove&lt;/strong&gt; it contains &lt;strong&gt;only policy-approved tooling&lt;/strong&gt; and shows &lt;strong&gt;no recent runtime compromise&lt;/strong&gt;. Verification happens at issuance &lt;strong&gt;and&lt;/strong&gt; every renewal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Evidence → Policy → Token → Go&lt;/strong&gt; (repeat on a short heartbeat)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Evidence: signed statements about image/tooling/config/provenance + recent runtime signals&lt;/li&gt;
&lt;li&gt;Policy: allowlists + disqualifiers (writes under &lt;code&gt;/usr&lt;/code&gt;, memfd exec, RW mounts, caps gain, etc.)&lt;/li&gt;
&lt;li&gt;Token: short-lived, tagged with &lt;code&gt;env_hash&lt;/code&gt; and &lt;code&gt;policy_id&lt;/code&gt;; logs to append-only storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If either evidence or runtime fails → &lt;strong&gt;no token&lt;/strong&gt; (or renewal denied) with a human-readable reason.&lt;/p&gt;




&lt;h2&gt;
  
  
  The production-ops blind spot (the last mile)
&lt;/h2&gt;

&lt;p&gt;DevSecOps secures artifacts. &lt;strong&gt;EnvSecOps secures the consoles that touch prod.&lt;/strong&gt;&lt;br&gt;
SREs, DBAs, compliance analysts—anyone running privileged actions—get access &lt;strong&gt;only&lt;/strong&gt; from attested, policy-approved environments. Compromised laptops and ad-hoc jump boxes stop at the door.&lt;/p&gt;




&lt;h2&gt;
  
  
  Audit that finally proves &lt;em&gt;where&lt;/em&gt; actions came from
&lt;/h2&gt;

&lt;p&gt;Every issued token carries &lt;code&gt;env_hash&lt;/code&gt; and &lt;code&gt;policy_id&lt;/code&gt;. Cloud audit now answers:&lt;br&gt;
&lt;strong&gt;who&lt;/strong&gt; acted, &lt;strong&gt;what&lt;/strong&gt; they did, &lt;strong&gt;and&lt;/strong&gt; &lt;strong&gt;from which attested environment&lt;/strong&gt;—provably. That’s post-incident clarity and real compliance, not ceremony.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where they overlap (and don’t)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DevOps:&lt;/strong&gt; deploy velocity, reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DevSecOps:&lt;/strong&gt; artifact safety, shift-left assurance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EnvSecOps:&lt;/strong&gt; environment assurance and &lt;strong&gt;credential lifecycle&lt;/strong&gt; across dev, CI, and ops.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You need all three. EnvSecOps doesn’t replace DevSecOps; it &lt;strong&gt;binds&lt;/strong&gt; DevOps/DevSecOps activity to verified environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why “check the bag” matters more than “check the ID”
&lt;/h2&gt;

&lt;p&gt;Stolen or misused creds authenticate fine. &lt;strong&gt;ID passes.&lt;/strong&gt;&lt;br&gt;
Attacker’s environment can’t produce a valid attestation or passes runtime checks. &lt;strong&gt;Bag fails.&lt;/strong&gt;&lt;br&gt;
EnvSecOps makes that failure the gate: &lt;strong&gt;no proof, no token.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Works the same for pushing code, deploying, restarting services, or exporting audit logs.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you always enforce (model-agnostic hygiene)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short lifetimes&lt;/strong&gt; (5–15m): renew at TTL/2; deny on stale or drifted env.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime signals in the decision loop&lt;/strong&gt; (not just dashboards).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least privilege via issuance scope&lt;/strong&gt;, not just roles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-only roots, minimal caps, signed artifacts&lt;/strong&gt;—boring on purpose.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When you &lt;em&gt;need&lt;/em&gt; EnvSecOps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supply chain programs&lt;/strong&gt; (SLSA, provenance requirements): prove environments for dev, build, deploy, and ops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-breach realism&lt;/strong&gt;: stolen tokens and compromised laptops are a given; issuance must be gated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed teams/contractors&lt;/strong&gt;: network controls aren’t environment controls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform engineering&lt;/strong&gt;: self-service access that’s cryptographically enforced, not ticketed.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting started (one-door pilot)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pick one door&lt;/strong&gt; (a meaningful role with limited blast radius).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freeze the bag&lt;/strong&gt; (pinned image, approved tooling/config digests; sign + log).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate issuance&lt;/strong&gt; (require fresh proof + no disqualifying runtime events).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short TTLs&lt;/strong&gt;, renew at TTL/2; deny on mutation with clear reasons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag tokens&lt;/strong&gt; (&lt;code&gt;env_hash&lt;/code&gt;, &lt;code&gt;policy_id&lt;/code&gt;) and &lt;strong&gt;log&lt;/strong&gt; decisions append-only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publish&lt;/strong&gt;: the policy snippet and one denial screenshot.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DevOps&lt;/strong&gt; ships fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DevSecOps&lt;/strong&gt; ships safer code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EnvSecOps&lt;/strong&gt; ensures the work of shipping and operating happens &lt;strong&gt;only&lt;/strong&gt; from attested, policy-compliant environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;No attested, policy-approved bag → no token.&lt;/strong&gt;&lt;br&gt;
That’s how you stop the 2 AM “SSH from a mystery laptop” from being business as usual.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>envsecops</category>
      <category>devsecops</category>
    </item>
    <item>
      <title>EnvSecOps: Nix, Recognized: The Strongest Bag Wins</title>
      <dc:creator>John Loper</dc:creator>
      <pubDate>Sat, 08 Nov 2025 14:28:42 +0000</pubDate>
      <link>https://forem.com/jloper3/envsecop-nix-recognized-the-strongest-bag-wins-20nm</link>
      <guid>https://forem.com/jloper3/envsecop-nix-recognized-the-strongest-bag-wins-20nm</guid>
      <description>&lt;p&gt;Nix's functional purity makes deterministic artifacts a first class citizen. Shout out to the community for keeping it real. Its a primary building block we can build upon to keep our environments clean as a whistle.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Meme recap: Old security checks IDs. EnvSecOps checks IDs and the bag.&lt;br&gt;
This post: Why Nix is a first-class way to define, prove, and police the bag.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vw5pu7v7oc2lcgtnxta.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vw5pu7v7oc2lcgtnxta.gif" alt="EnvSecOps" width="245" height="132"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Nix fits EnvSecOps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reproducibility&lt;/strong&gt;: Inputs are pinned (flake.lock), outputs are content-addressed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Minimal drift surface&lt;/strong&gt;: The /nix/store is read-only; mutation stands out.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Smaller policy&lt;/strong&gt;: Instead of enumerating a thousand files, you allowlist a closure root (and optional successor roots).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;The bag stops being “a pile of files” and becomes “this exact closure hash and nothing else.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Policy: allow the closure, not vibes
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Renewal: prove the running bag AT EACH RENEWAL
&lt;/h2&gt;




&lt;h2&gt;
  
  
  Migration playbook (fast &amp;amp; boring)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Build from Nix (for the service you’ll gate first).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Emit labels/artifacts at build time&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CI signs an attestation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Publish a policy data bundle&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gate one role on that bundle&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Renew at TTL/2; deny on mismatch; measure and ratchet down TTLs as confidence grows.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Where Nix shines in this setup (reduces policy toil)
&lt;/h2&gt;

&lt;p&gt;From file allowlists to root allowlists. One item per release instead of a thousand.&lt;/p&gt;

&lt;p&gt;Successor maps tame digest churn. Hotfixes don’t flap renewals.&lt;/p&gt;

&lt;p&gt;Lockfile diffs become change requests.&lt;/p&gt;




&lt;h2&gt;
  
  
  Caveats (be honest)
&lt;/h2&gt;

&lt;p&gt;Purity isn’t magic: Ensure sandboxed builds; eliminate impure sources (time, network) or you’ll get “same intent, new hash” churn.&lt;/p&gt;

&lt;p&gt;Runtime still matters: Even with a read-only store, enforce RO mounts and minimal caps; detect in-memory tricks at renewal time.&lt;/p&gt;




&lt;p&gt;TL;DR&lt;/p&gt;

&lt;p&gt;Nix turns “check the bag” into check the closure.&lt;br&gt;
Attest a root, allowlist it, and refuse to mint tokens for anything else.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;No attested, policy-approved closure → no token.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>nix</category>
      <category>envsecops</category>
    </item>
    <item>
      <title>EnvSecOps: The Painfully Obvious Path Forward</title>
      <dc:creator>John Loper</dc:creator>
      <pubDate>Thu, 06 Nov 2025 22:19:49 +0000</pubDate>
      <link>https://forem.com/jloper3/check-the-bag-the-painfully-obvious-path-forward-po2</link>
      <guid>https://forem.com/jloper3/check-the-bag-the-painfully-obvious-path-forward-po2</guid>
      <description>&lt;p&gt;It's not radical. &lt;br&gt;
It's obvious.&lt;br&gt;
We're missing a big piece of security right at the entrance. We've been hanging out season passes (static creds) to kids with backpacks full of who knows what.&lt;br&gt;
We can check those bags. We have the technology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the Bag: The Meme You Cant Unsee
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vw5pu7v7oc2lcgtnxta.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vw5pu7v7oc2lcgtnxta.gif" alt="EnvSecOps" width="245" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Old security:&lt;/strong&gt; “Check IDs.”&lt;br&gt;
&lt;strong&gt;New security:&lt;/strong&gt; “Check IDs &lt;strong&gt;and the bag&lt;/strong&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The meme is the message. We don’t just ask &lt;em&gt;who&lt;/em&gt; is calling our systems—we open the bag and prove &lt;em&gt;what’s inside&lt;/em&gt;. And that bag must contain &lt;strong&gt;only policy-approved tooling&lt;/strong&gt;, cryptographically attested, no surprises, no freebies.&lt;/p&gt;

&lt;p&gt;Call it &lt;strong&gt;EnvSecOps&lt;/strong&gt; if you want a name. Practically, it’s the painfully obvious next step:&lt;br&gt;
&lt;strong&gt;No attested, policy-approved bag → no token.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What “the Bag” Actually Means
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The bag = the execution environment&lt;/strong&gt;: image, toolchain, configs, helper scripts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy-approved = explicit allowlist&lt;/strong&gt;: pinned digests, permitted binaries, known configs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attested = cryptographically proven&lt;/strong&gt;: signed statements that the environment &lt;em&gt;only&lt;/em&gt; contains those allowed artifacts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If anything unapproved is in the bag? Deny.&lt;br&gt;
If the bag can’t prove itself? Deny.&lt;br&gt;
No vibes. No &lt;code&gt;:latest&lt;/code&gt;. No “temporary” helper scripts.&lt;/p&gt;

&lt;p&gt;You still keep IAM, SCPs, runtime sensors. The shift is earlier:&lt;br&gt;
&lt;strong&gt;Tokens exist only after the bag proves it contains nothing except what policy allows.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Is Obvious (And Superior)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prevention beats monitoring.&lt;/strong&gt; If a tool isn’t approved, it never rides inside a token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic access.&lt;/strong&gt; Evidence → policy → token → go. No ticket tennis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit that speaks English.&lt;/strong&gt; Every token says: “Issued because &lt;em&gt;this&lt;/em&gt; bag (hash X) matched &lt;em&gt;that&lt;/em&gt; policy (ID Y) at &lt;em&gt;this&lt;/em&gt; time.”&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Here’s a sharper, more ruthless version — same facts, same events, same message, just delivered with more bite and clarity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters Right Now
&lt;/h2&gt;

&lt;p&gt;Look at the pattern in recent breaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CircleCI (2023): Malware on laptop → stolen session → production access&lt;/li&gt;
&lt;li&gt;LastPass (2022): Compromised home PC → vault extraction
&lt;/li&gt;
&lt;li&gt;Okta (2024): Personal device → customer tenant access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Same root cause every time:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We obsess over &lt;em&gt;who&lt;/em&gt; someone is while never verifying &lt;em&gt;what&lt;/em&gt; they’re running from.&lt;br&gt;
It’s the equivalent of checking IDs at the door while ignoring the backpack stuffed with explosives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real scenario (CircleCI, 2023):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Malware on an engineer’s laptop grabs a session cookie&lt;/li&gt;
&lt;li&gt;Cookie is valid — bypasses MFA, identity checks, everything&lt;/li&gt;
&lt;li&gt;Attacker walks straight into the CI system&lt;/li&gt;
&lt;li&gt;Extracts production secrets, compromises customer environments&lt;/li&gt;
&lt;li&gt;Postmortem: lots of shocked faces, very few surprises&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And now organizations are bolting LLM agents directly onto production systems, with tool-calling turned on, running from environments that could charitably be described as “unmonitored optimism.”&lt;/p&gt;

&lt;p&gt;Agents don’t magically become secure because you added a guardrail prompt.&lt;br&gt;
They inherit whatever chaos is in their runtime environment — usually &lt;strong&gt;hope, drift, and the ghosts of abandoned dependencies.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The irony?&lt;br&gt;
&lt;strong&gt;The tech to fix this has existed for years&lt;/strong&gt; — SPIFFE for workload identity, Sigstore for signing and verification, OPA for real-time policy enforcement.&lt;br&gt;
CNCF graduated, production-ready, already deployed at hyperscale.&lt;/p&gt;

&lt;p&gt;The only open question is whether you adopt this before your breach… or after you’re writing the apology blog post.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Absolute Minimum
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pin one tool&lt;/strong&gt; - Replace &lt;code&gt;FROM ubuntu:latest&lt;/code&gt; with &lt;code&gt;FROM ubuntu@sha256:abc123...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify it&lt;/strong&gt; - Add &lt;code&gt;cosign verify&lt;/code&gt; before using it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate on it&lt;/strong&gt; - Don't issue credentials unless verification passes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. That's the nucleus.&lt;/p&gt;

&lt;p&gt;Everything else (SPIRE, OPA, SLSA) is scaling that pattern.&lt;/p&gt;




&lt;h2&gt;
  
  
  Field Rules (Pin These)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minutes, not hours.&lt;/strong&gt; Long-lived = stolen-lived.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only approved tooling.&lt;/strong&gt; If it’s not on the list, it’s not in the bag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No drift.&lt;/strong&gt; Changing the bag means re-attesting or getting denied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Humans ≈ workloads.&lt;/strong&gt; MFA is a signal, not a hall pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy before platform.&lt;/strong&gt; Declare issuance conditions; automate enforcement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boring by design.&lt;/strong&gt; Deterministic gates beat heroics and dashboards.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Starter Kit
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For attestation:&lt;/strong&gt; Sigstore (Cosign + Rekor)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why: Free, open, CNCF-graduated, works today&lt;/li&gt;
&lt;li&gt;Alternative: in-toto if you need custom attestation predicates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For identity:&lt;/strong&gt; SPIFFE/SPIRE for workloads, OIDC for humans&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why: Industry standard, integrates with everything&lt;/li&gt;
&lt;li&gt;Alternative: Cloud IAM if you're single-cloud only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For policy:&lt;/strong&gt; OPA&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why: Flexible, testable, huge community&lt;/li&gt;
&lt;li&gt;Alternative: Cedar if you're deep in AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;pattern&lt;/strong&gt; is the product. Tools are replaceable; the gate is not.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Line to Use in Every Review
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Once prod creds are issued to an environment, that environment &lt;em&gt;is&lt;/em&gt; prod.”&lt;/strong&gt;&lt;br&gt;
So check the bag—and prove it only contains what policy allows—&lt;strong&gt;before&lt;/strong&gt; issuing the wristband.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Call to (Obvious) Action
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Replace one &lt;code&gt;:latest&lt;/code&gt; with a pinned, &lt;strong&gt;approved&lt;/strong&gt; base image and sign it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 1. Get the digest
docker pull ubuntu:22.04
docker inspect ubuntu:22.04 --format='{{.RepoDigests}}'
# Output: ubuntu@sha256:abc123...

# 2. Update your Dockerfile
FROM ubuntu@sha256:abc123...

# 3. Sign it
docker build -t myapp:v1 .
cosign sign --yes myapp:v1

# 4. Verify before deploy
cosign verify myapp:v1 --certificate-identity=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your security only checks IDs, you’re letting backpacks full of trouble stroll past the rope.&lt;br&gt;
&lt;strong&gt;Check the bag. Prove it only holds policy-approved tooling. Then, and only then, hand out the wristband.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>envsecops</category>
      <category>slsa</category>
    </item>
  </channel>
</rss>
