<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Pipeline Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/tag/pipeline/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/tag/pipeline/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Mon, 27 Jul 2026 11:57:31 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.4</generator>

<image>
	<url>https://linuxcent.com/wp-content/uploads/2026/04/favicon-512x512-1-150x150.png</url>
	<title>Pipeline Archives - Linuxcent</title>
	<link>https://linuxcent.com/tag/pipeline/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>The Pipeline Gate — Hardened Images as a CI/CD Build Constraint</title>
		<link>https://linuxcent.com/hardened-image-cicd-pipeline-gate/</link>
					<comments>https://linuxcent.com/hardened-image-cicd-pipeline-gate/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Sat, 23 May 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[OS Image Builder]]></category>
		<category><![CDATA[BakeX]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[DevSecOps]]></category>
		<category><![CDATA[GitOps]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Pipeline]]></category>
		<category><![CDATA[Security]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1831</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span>Make hardened OS images a CI/CD build constraint: POST /api/pipeline/scan fails the build if grade < threshold. Unhardened images never reach production.
</p>
<p>The post <a href="https://linuxcent.com/hardened-image-cicd-pipeline-gate/">The Pipeline Gate — Hardened Images as a CI/CD Build Constraint</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>OS Hardening as Code, Episode 5</em><br />
<em><a href="https://linuxcent.com/cloud-ami-security-risks-custom-os-images/">Cloud AMI Security Risks</a> · <a href="/linux-hardening-as-code/">Linux Hardening as Code</a> · <a href="/linux-hardening-multi-cloud/">Multi-Cloud OS Hardening</a> · <a href="/automated-compliance-scanning-openscap/">Automated OpenSCAP Compliance</a> · </em><em>CI/CD Compliance Gate</em>**</p>
<blockquote>
<p><strong>Note:</strong> the tool in this series was released as <strong>Stratum</strong> and renamed to <strong>BakeX</strong> at<br />
v0.6.0 — same project, same license, same team. Commands below use the current <code class="" data-line="">bakex</code><br />
CLI. If you arrived here looking for <code class="" data-line="">stratum</code> or <code class="" data-line="">pip install stratumoss</code>, you&#8217;re in the<br />
right place: <a href="https://github.com/invicton/bakex">github.com/invicton/bakex</a>.</p>
</blockquote>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>A CI/CD compliance gate turns an OS hardening grade from a report into a build constraint — unhardened images fail the pipeline before they can be deployed</li>
<li><code class="" data-line="">POST /api/pipeline/scan</code> scores an image against a <code class="" data-line="">pass_threshold</code> and a <code class="" data-line="">severity_threshold</code>, and returns a <code class="" data-line="">passed</code> boolean</li>
<li><strong>The endpoint returns HTTP 200 even when the gate fails.</strong> <code class="" data-line="">curl -sf</code> will not catch it — you must parse <code class="" data-line="">.passed</code>. This is the single most important detail on this page</li>
<li>The gate is two-dimensional: a score floor <em>and</em> a severity ceiling, so one critical finding blocks a release that scores 94</li>
<li>GitHub Actions, GitLab CI, Jenkins, and Tekton integrations are one curl plus one <code class="" data-line="">jq</code></li>
<li>The structural guarantee: an image that doesn&#8217;t pass the gate doesn&#8217;t reach the deploy job</li>
</ul>
<hr />
<h2 id="the-problem-a-grade-no-one-checks-is-decoration">The Problem: A Grade No One Checks Is Decoration</h2>
<pre><code class="" data-line="">Pipeline without compliance gate:
  Build → Test → Security scan (results to dashboard) → Deploy

What actually happens:
  Build → Test → Security scan → &quot;C grade, but we need to ship&quot; → Deploy anyway
                                           │
                                           └─ Dashboard shows C grade
                                              Nobody is paged
                                              Deployment succeeds
</code></pre>
<p>A CI/CD compliance gate means the pipeline can&#8217;t continue if the grade is below threshold.</p>
<p>EP04 showed that automated OpenSCAP compliance gives every image a verified, reproducible grade before deployment. What it assumed is that someone checks the grade before deploying. They don&#8217;t — not under deadline pressure, not when the image has been &#8220;working fine for months,&#8221; not at 2am.</p>
<p>The same problem that made hardening runbooks skippable applies to compliance grades: if checking the grade is a discretionary step, it will be skipped.</p>
<hr />
<p>A new microservice was deployed from an unhardened base image. The team had built it quickly during a sprint, used a community AMI as the base, and planned to harden it &#8220;in the next sprint.&#8221;</p>
<p>Three weeks later, a penetration test found it. SSH password authentication enabled. Three unnecessary services running — one of them with a known CVE. The finding: the instance had full inbound access from the VPC and was reachable from a compromised adjacent instance.</p>
<p>The deployment had gone through the normal CI/CD pipeline. Unit tests passed. Integration tests passed. A vulnerability scan ran. The scan produced a report that went to a dashboard. Nobody had a gate set up to fail the build if the image was unhardened.</p>
<p>The hardening work from the &#8220;next sprint&#8221; plan would have taken four hours. The pentest remediation took a week, plus the time to investigate what had been exposed during the three weeks the instance was running.</p>
<p>The CI/CD pipeline had every check except the one that would have caught the base image problem before the first deployment.</p>
<hr />
<h2 id="the-pipeline-api">The Pipeline API</h2>
<p>The Pipeline API is a single HTTP endpoint that takes an image ID, scans it, and returns a verdict:</p>
<pre><code class="" data-line="">curl -s -X POST https://bakex.yourdomain.com/api/pipeline/scan \
  -H &quot;X-API-Key: ${BAKEX_TOKEN}&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d &#039;{
    &quot;image_id&quot;: &quot;ami-0a7f3c9e82d1b4c05&quot;,
    &quot;provider&quot;: &quot;aws&quot;,
    &quot;region&quot;: &quot;us-east-1&quot;,
    &quot;pass_threshold&quot;: 75.0,
    &quot;severity_threshold&quot;: &quot;high&quot;,
    &quot;wait&quot;: true
  }&#039;
</code></pre>
<p>Authentication takes either <code class="" data-line="">X-API-Key</code> or <code class="" data-line="">Authorization: Bearer</code>; keys are created at<br />
<code class="" data-line="">/settings/api-keys</code>. With <code class="" data-line="">wait: true</code> the request blocks until the scan completes — which is what<br />
you want in CI, where a job that returns before the answer exists is worse than a slow one. There&#8217;s<br />
a <code class="" data-line="">timeout_seconds</code> (default 900) for when it doesn&#8217;t.</p>
<p>The response is the same shape whether you passed or failed:</p>
<pre><code class="" data-line="">{
  &quot;job_id&quot;: &quot;7f3c9e82-4d1b-4c05-a7f3-c9e82d1b4c05&quot;,
  &quot;status&quot;: &quot;complete&quot;,
  &quot;passed&quot;: false,
  &quot;grade&quot;: &quot;C&quot;,
  &quot;score_pct&quot;: 72.0,
  &quot;severity_counts&quot;: { &quot;critical&quot;: 0, &quot;high&quot;: 2, &quot;medium&quot;: 5, &quot;low&quot;: 11 },
  &quot;threshold_violations&quot;: [&quot;high&quot;],
  &quot;pass_threshold&quot;: 75.0,
  &quot;severity_threshold&quot;: &quot;high&quot;,
  &quot;image_id&quot;: &quot;ami-0c9d5e3f81a2b6e07&quot;,
  &quot;sarif_url&quot;: &quot;.../api/auditor/scan-image/7f3c9e82.../report?fmt=sarif&quot;,
  &quot;html_report_url&quot;: &quot;.../api/auditor/scan-image/7f3c9e82.../report&quot;
}
</code></pre>
<h3 id="the-detail-that-will-silently-break-your-gate">The detail that will silently break your gate</h3>
<p><strong>A failed gate still returns HTTP 200.</strong> There is no 4xx on failure — the verdict is in the<br />
<code class="" data-line="">passed</code> field, not the status code.</p>
<p>That means the pattern everyone reaches for first is wrong:</p>
<pre><code class="" data-line=""># WRONG — this never fails. -f only reacts to HTTP &gt;= 400,
# and a failed gate returns 200.
curl -sf -X POST .../api/pipeline/scan -d &#039;...&#039; || exit 1
</code></pre>
<p>You have to read the body:</p>
<pre><code class="" data-line=""># RIGHT
RESULT=$(curl -s -X POST &quot;${BAKEX_URL}/api/pipeline/scan&quot; \
  -H &quot;X-API-Key: ${BAKEX_TOKEN}&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d &quot;{\&quot;image_id\&quot;: \&quot;${AMI_ID}\&quot;, \&quot;pass_threshold\&quot;: 75.0, \&quot;severity_threshold\&quot;: \&quot;high\&quot;}&quot;)

echo &quot;$RESULT&quot; | jq -r &#039;&quot;grade=\(.grade) score=\(.score_pct) passed=\(.passed)&quot;&#039;

if [ &quot;$(echo &quot;$RESULT&quot; | jq -r &#039;.passed&#039;)&quot; != &quot;true&quot; ]; then
  echo &quot;Compliance gate failed — violations: $(echo &quot;$RESULT&quot; | jq -c &#039;.threshold_violations&#039;)&quot;
  echo &quot;Report: $(echo &quot;$RESULT&quot; | jq -r &#039;.html_report_url&#039;)&quot;
  exit 1
fi
</code></pre>
<p>A gate that reports failure and exits 0 is worse than no gate, because it produces a green<br />
pipeline and the belief that something was checked.</p>
<h3 id="two-thresholds-not-one">Two thresholds, not one</h3>
<p><code class="" data-line="">passed</code> is the AND of two independent conditions:</p>
<pre><code class="" data-line="">passed = (score_pct &gt;= pass_threshold) AND (no findings at or above severity_threshold)
</code></pre>
<p><code class="" data-line="">severity_threshold: &quot;high&quot;</code> means any <code class="" data-line="">critical</code> or <code class="" data-line="">high</code> finding fails the build regardless of<br />
score. An image can score 94 — a comfortable A — and still fail on a single critical finding. That<br />
is the right default: scores average away the thing that gets you breached.</p>
<hr />
<h2 id="github-actions-integration">GitHub Actions Integration</h2>
<pre><code class="" data-line=""># .github/workflows/deploy.yml

jobs:
  build-image:
    runs-on: ubuntu-latest
    outputs:
      ami_id: ${{ steps.build.outputs.ami_id }}
    steps:
      - name: Build hardened AMI
        id: build
        run: |
          AMI_ID=$(bakex build blueprints/ubuntu/22.04/cis-l1-aws.yaml --json \
            | jq -r &#039;.artifact_id&#039;)
          echo &quot;ami_id=${AMI_ID}&quot; &gt;&gt; $GITHUB_OUTPUT

  compliance-gate:
    runs-on: ubuntu-latest
    needs: build-image
    steps:
      - name: BakeX compliance gate
        run: |
          RESULT=$(curl -s -X POST ${{ vars.BAKEX_URL }}/api/pipeline/scan \
            -H &quot;X-API-Key: ${{ secrets.BAKEX_TOKEN }}&quot; \
            -H &quot;Content-Type: application/json&quot; \
            -d &quot;{\&quot;image_id\&quot;: \&quot;${{ needs.build-image.outputs.ami_id }}\&quot;,
                 \&quot;pass_threshold\&quot;: 75.0, \&quot;severity_threshold\&quot;: \&quot;high\&quot;}&quot;)

          echo &quot;$RESULT&quot; | jq -r &#039;&quot;grade=\(.grade) score=\(.score_pct)&quot;&#039;

          # Must check .passed — the endpoint returns 200 on failure
          if [ &quot;$(echo &quot;$RESULT&quot; | jq -r &#039;.passed&#039;)&quot; != &quot;true&quot; ]; then
            echo &quot;::error::Compliance gate failed: $(echo &quot;$RESULT&quot; | jq -c &#039;.threshold_violations&#039;)&quot;
            exit 1
          fi

      - name: Upload SARIF to code scanning
        if: always()
        run: |
          curl -s -o bakex.sarif &quot;$(echo &quot;$RESULT&quot; | jq -r &#039;.sarif_url&#039;)&quot;
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: bakex.sarif

  deploy:
    runs-on: ubuntu-latest
    needs: [build-image, compliance-gate]
    steps:
      - name: Deploy to staging
        run: |
          aws autoscaling update-auto-scaling-group \
            --auto-scaling-group-name my-asg \
            --launch-template &quot;ImageId=${{ needs.build-image.outputs.ami_id }}&quot;
</code></pre>
<p>The <code class="" data-line="">deploy</code> job only runs if <code class="" data-line="">compliance-gate</code> passes. The AMI doesn&#8217;t reach the autoscaling group if it doesn&#8217;t meet the grade threshold.</p>
<hr />
<h2 id="gitlab-ci-integration">GitLab CI Integration</h2>
<pre><code class="" data-line=""># .gitlab-ci.yml

stages:
  - build
  - compliance
  - deploy

build-image:
  stage: build
  script:
    - |
      AMI_ID=$(bakex build blueprints/ubuntu/22.04/cis-l1-aws.yaml --json \
        | jq -r &#039;.artifact_id&#039;)
      echo &quot;AMI_ID=${AMI_ID}&quot; &gt;&gt; build.env
  artifacts:
    reports:
      dotenv: build.env

compliance-gate:
  stage: compliance
  needs: [build-image]
  script:
    - |
      RESULT=$(curl -s -X POST ${BAKEX_URL}/api/pipeline/scan \
        -H &quot;X-API-Key: ${BAKEX_TOKEN}&quot; \
        -H &quot;Content-Type: application/json&quot; \
        -d &quot;{\&quot;image_id\&quot;: \&quot;${AMI_ID}\&quot;, \&quot;pass_threshold\&quot;: 75.0,
             \&quot;severity_threshold\&quot;: \&quot;high\&quot;}&quot;)
      echo &quot;$RESULT&quot; | jq -r &#039;&quot;grade=\(.grade) score=\(.score_pct) passed=\(.passed)&quot;&#039;
      test &quot;$(echo &quot;$RESULT&quot; | jq -r &#039;.passed&#039;)&quot; = &quot;true&quot;

deploy:
  stage: deploy
  needs: [build-image, compliance-gate]
  script:
    - ./deploy.sh ${AMI_ID}
</code></pre>
<hr />
<h2 id="what-the-failed-gate-tells-you">What the Failed Gate Tells You</h2>
<p>The value of the CI/CD compliance gate is not just that it blocks bad images — it&#8217;s that the failure output tells engineers what to fix.</p>
<p>The response carries three things an engineer can act on immediately:</p>
<pre><code class="" data-line="">$ echo &quot;$RESULT&quot; | jq &#039;{grade, score_pct, threshold_violations, severity_counts}&#039;
{
  &quot;grade&quot;: &quot;C&quot;,
  &quot;score_pct&quot;: 72.0,
  &quot;threshold_violations&quot;: [&quot;high&quot;],
  &quot;severity_counts&quot;: { &quot;critical&quot;: 0, &quot;high&quot;: 2, &quot;medium&quot;: 5, &quot;low&quot;: 11 }
}
</code></pre>
<p><code class="" data-line="">threshold_violations</code> names the severities that broke the gate — here, two <code class="" data-line="">high</code> findings, not the<br />
score. That distinction matters: an engineer who reads &#8220;grade C&#8221; starts a broad hardening project,<br />
while one who reads &#8220;two high findings&#8221; goes and fixes two things.</p>
<p>For the rule-level detail, follow <code class="" data-line="">sarif_url</code>. Pushing that SARIF into GitHub code scanning (as in<br />
the workflow above) puts each finding on the pull request diff, which is where someone will actually<br />
read it — a link to a dashboard in a CI log is a link nobody clicks.</p>
<hr />
<h2 id="thresholds-by-environment">Thresholds by Environment</h2>
<p>Not all environments need the same bar, and both dimensions are per-request — so the environment<br />
distinction lives in your pipeline, not in BakeX config:</p>
<pre><code class="" data-line=""># Production — high score floor, nothing high or above
PASS=90.0 ; SEV=high

# Staging — lower floor, still no criticals
PASS=75.0 ; SEV=critical

# Development — score only, severity effectively off
PASS=60.0 ; SEV=low

curl -s -X POST &quot;${BAKEX_URL}/api/pipeline/scan&quot; \
  -H &quot;X-API-Key: ${BAKEX_TOKEN}&quot; -H &quot;Content-Type: application/json&quot; \
  -d &quot;{\&quot;image_id\&quot;: \&quot;${AMI_ID}\&quot;, \&quot;pass_threshold\&quot;: ${PASS}, \&quot;severity_threshold\&quot;: \&quot;${SEV}\&quot;}&quot;
</code></pre>
<p>Note that <code class="" data-line="">severity_threshold</code> gets <em>stricter</em> as it goes down the list: <code class="" data-line="">low</code> fails on any finding<br />
at all, <code class="" data-line="">critical</code> fails only on criticals. It reads backwards the first time. Development wanting a<br />
permissive gate wants <code class="" data-line="">critical</code>, not <code class="" data-line="">low</code>.</p>
<hr />
<h2 id="production-gotchas">Production Gotchas</h2>
<p><strong>The 200-on-failure behaviour is the whole ballgame.</strong> Repeating it because it is the one thing that<br />
turns this page from useful to harmful if missed: check <code class="" data-line="">.passed</code>. Never rely on <code class="" data-line="">curl -f</code>, and never<br />
rely on the HTTP status.</p>
<p><strong>Scans take minutes, and <code class="" data-line="">wait: true</code> blocks.</strong> The endpoint provisions an instance from the image<br />
and scans it. With <code class="" data-line="">wait: true</code> your CI job blocks for the duration; <code class="" data-line="">timeout_seconds</code> defaults to<br />
900. Set your CI step timeout above that, or use <code class="" data-line="">wait: false</code> and poll <code class="" data-line="">GET /api/pipeline/scan/{job_id}</code>.</p>
<p><strong>Token rotation.</strong> The API key should rotate on the same schedule as other service credentials, and<br />
environments should use different keys — a leaked staging key must not be able to satisfy a<br />
production gate.</p>
<p><strong>The gate needs a reachable BakeX server.</strong> This is an HTTP API, not a self-contained action: the<br />
runner must reach the BakeX instance, and that instance needs cloud credentials for the provider<br />
whose image it is scanning.</p>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>A CI/CD compliance gate turns a compliance grade from a dashboard metric into a pipeline constraint — the image doesn&#8217;t deploy if it doesn&#8217;t pass</li>
<li><code class="" data-line="">POST /api/pipeline/scan</code> is a single HTTP call that any CI/CD system can make — no agent, no plugin, no SDK required</li>
<li><strong>The endpoint returns 200 even when the gate fails.</strong> Parse <code class="" data-line="">.passed</code>; <code class="" data-line="">curl -sf || exit 1</code> produces a green pipeline and a false sense of security</li>
<li>The verdict is two-dimensional — a score floor AND a severity ceiling — so a single critical finding blocks an image that scores 94</li>
<li><code class="" data-line="">threshold_violations</code> tells an engineer <em>why</em> it failed, which is the difference between &#8220;fix two high findings&#8221; and &#8220;start a hardening project&#8221;</li>
<li>Push the <code class="" data-line="">sarif_url</code> into GitHub code scanning so findings land on the pull request, not in a CI log</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>The CI/CD compliance gate closes the final gap: even if an unhardened image gets built, it can&#8217;t deploy. EP05 is the bookmark episode — this is the point where OS hardening becomes structurally enforced rather than procedurally expected.</p>
<p>EP06 is the series closer. For five episodes, you&#8217;ve been using BakeX as a user. What does it look like to run it yourself — extend it with a custom provider, deploy it in your own infrastructure, or contribute a blueprint back?</p>
<p>BakeX is Apache 2.0. EP06 is the architecture reveal, the deployment guide, and the extension points for everything the series taught.</p>
<p><em>Next: <a href="/stratum-os-hardening-platform/">BakeX — open-source OS hardening platform for multi-cloud infrastructure</a></em></p>
<p>Get EP06 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fhardened-image-cicd-pipeline-gate%2F&amp;linkname=The%20Pipeline%20Gate%20%E2%80%94%20Hardened%20Images%20as%20a%20CI%2FCD%20Build%20Constraint" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fhardened-image-cicd-pipeline-gate%2F&amp;linkname=The%20Pipeline%20Gate%20%E2%80%94%20Hardened%20Images%20as%20a%20CI%2FCD%20Build%20Constraint" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fhardened-image-cicd-pipeline-gate%2F&amp;linkname=The%20Pipeline%20Gate%20%E2%80%94%20Hardened%20Images%20as%20a%20CI%2FCD%20Build%20Constraint" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fhardened-image-cicd-pipeline-gate%2F&amp;linkname=The%20Pipeline%20Gate%20%E2%80%94%20Hardened%20Images%20as%20a%20CI%2FCD%20Build%20Constraint" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fhardened-image-cicd-pipeline-gate%2F&amp;linkname=The%20Pipeline%20Gate%20%E2%80%94%20Hardened%20Images%20as%20a%20CI%2FCD%20Build%20Constraint" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fhardened-image-cicd-pipeline-gate%2F&amp;linkname=The%20Pipeline%20Gate%20%E2%80%94%20Hardened%20Images%20as%20a%20CI%2FCD%20Build%20Constraint" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fhardened-image-cicd-pipeline-gate%2F&amp;linkname=The%20Pipeline%20Gate%20%E2%80%94%20Hardened%20Images%20as%20a%20CI%2FCD%20Build%20Constraint" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fhardened-image-cicd-pipeline-gate%2F&#038;title=The%20Pipeline%20Gate%20%E2%80%94%20Hardened%20Images%20as%20a%20CI%2FCD%20Build%20Constraint" data-a2a-url="https://linuxcent.com/hardened-image-cicd-pipeline-gate/" data-a2a-title="The Pipeline Gate — Hardened Images as a CI/CD Build Constraint"></a></p><p>The post <a href="https://linuxcent.com/hardened-image-cicd-pipeline-gate/">The Pipeline Gate — Hardened Images as a CI/CD Build Constraint</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/hardened-image-cicd-pipeline-gate/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1831</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: linuxcent.com @ 2026-08-15 01:54:12 by W3 Total Cache
-->