惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
A
Arctic Wolf
Security Latest
Security Latest
Simon Willison's Weblog
Simon Willison's Weblog
I
Intezer
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Troy Hunt's Blog
Latest news
Latest news
Help Net Security
Help Net Security
S
Security Affairs
Webroot Blog
Webroot Blog
The Hacker News
The Hacker News
AI
AI
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Tor Project blog
Forbes - Security
Forbes - Security
Google DeepMind News
Google DeepMind News
AWS News Blog
AWS News Blog
Attack and Defense Labs
Attack and Defense Labs
P
Proofpoint News Feed
www.infosecurity-magazine.com
www.infosecurity-magazine.com
H
Help Net Security
L
Lohrmann on Cybersecurity
S
SegmentFault 最新的问题
Google Online Security Blog
Google Online Security Blog
MongoDB | Blog
MongoDB | Blog
Cyberwarzone
Cyberwarzone
The Last Watchdog
The Last Watchdog
S
Securelist
N
News and Events Feed by Topic
S
Secure Thoughts
F
Fortinet All Blogs
博客园_首页
C
Cybersecurity and Infrastructure Security Agency CISA
量子位
M
MIT News - Artificial intelligence
F
Full Disclosure
T
The Blog of Author Tim Ferriss
T
Tailwind CSS Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Microsoft Security Blog
Microsoft Security Blog
I
InfoQ
P
Privacy International News Feed
L
LangChain Blog
Know Your Adversary
Know Your Adversary
C
CERT Recently Published Vulnerability Notes

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Stop Pulling Unsafe Docker Images
Sven Kroll · 2026-04-29 · via DEV Community

"AI ships the bugs. AI ships the exploits. The number of projects sitting on critical CVEs is climbing fast, and protecting yourself is no longer optional."

A due diligence process lands, or an enterprise customer asks their first hard security question. Hopefully, this happens before data actually walks out the door. Yet, there it sits: a third-party Docker Hub image running in production. The same story plays out on most local dev machines.

Most teams respond in one of two ways. They either pay for Chainguard or Docker Hardened Images on a team plan, which usually sits outside the budget of a 30-person shop. Alternatively, they shrug and promise to look at it later, claiming it isn't critical.

You have another option. Build and host the container yourself on your own terms. It requires real work but delivers the exact artifacts an auditor will sign off on.

Why Does This Pipeline Exist? transfer.sh, Abandoned

We needed a small tool to move files from A to B without exposing them to the public internet. A self-hosted, secure replacement for WeTransfer. Our AI advisor suggested dutchcoders/transfer.sh. It is a popular self-hosted file-sharing tool in the Go ecosystem and seemed like the perfect fit. It offered simple uploads and downloads without a UI or complex authentication schemes. A minimal feature set usually means a minimal attack surface. It was exactly the shape of tool we were after.

That is also the trap. Many teams blindly follow this advice: "transfer.sh is popular, here's the docker-compose, ship it." That would be fatal.

First quick manual scan: 4 CRITICAL and 16 HIGH in the Go dependencies. The CVEs were months old. The upstream repo had seen no commits since 2023. The project had quietly died, yet Docker Hub was happily serving the corpse.

We looked for a maintained fork but found nothing active enough for production. We switched to Forceu/Gokapi, an actively maintained Firefox-Send-style alternative in Go. Gokapi is more tool than we strictly need. However, rolling our own minimal replacement isn't on the cards. Building one with AI takes a weekend; maintaining it takes years. Those are two different jobs.

This failure mode makes a casual docker pull dangerous in production. A widely-used tool drifts from actively maintained to effectively abandoned with no announcement. Docker Hub keeps serving it. Your cluster keeps pulling it. CVEs accumulate invisibly.

The point isn't how many CVEs transfer.sh accumulated. The point is that we look every single time. The pipeline below makes that sustainable.

How Do You Stop Pulling Unsafe Docker Images?

Build a secure supply chain without expensive enterprise tools, you must implement three non-negotiable controls::

  1. Build it yourself. No Docker Hub pulls in production. You must own the registry and the build logs.
  2. Scan and gate. Every build must ship an SBOM along with a scanner report and a human-readable diff against upstream. Any critical CVEs block the push.
  3. Harden the base. Stop relying on Alpine or Debian-slim with their monthly CVE churn. Rebase onto a minimal base, distroless by default, so your runtime attack surface shrinks to roughly what your binary actually uses.

These steps form a supply-chain control story that survives due diligence. Skip the base hardening, and Trivy reports become a monthly waiver-filing ritual against upstream Alpine CVEs. That is how scanning every image degrades into scanning and ignoring every image.

How Do You Set This Up?

The 5-Minute Setup: Fork, Configure, Ship

Here is how you stand up your own mirror in four steps.

1. Fork the template. Go to https://github.com/THEKROLL-LTD/oss-mirror-build and click "Use this template". You now own a copy.

2. Set two env vars. Open .github/workflows/build.yml and edit the env block at the top:

env:
  UPSTREAM_REPO: "Forceu/Gokapi"
  IMAGE_NAME: "ghcr.io/your-org/gokapi"

Enter fullscreen mode Exit fullscreen mode

3. Drop in your hardened Dockerfile. Save it at dockerfiles/Dockerfile.override. The Gokapi-on-distroless example sits in the next section for you to copy and adjust for your upstream.

4. Commit and push. The nightly cron handles the rest.

From here on, the workflow runs every night:

  • Check for a new upstream tag. If nothing changed, it exits.
  • Clone upstream at the new tag. This requires zero fork maintenance since the source is unchanged.
  • Apply your Dockerfile override. If dockerfiles/Dockerfile.override exists, it replaces the upstream version, allowing you to rebase onto distroless.
  • Emit a diff-review artifact. This includes the commit log and full patch between the last built tag and the new one with 90-day retention.
  • Build the image once and load it locally.
  • Scan with Trivy. It generates a SARIF for the Security tab and a CycloneDX SBOM. CRITICAL or HIGH findings with an available fix block the push.
  • Pass or block. If blocked, it auto-files an issue. If passed, it pushes to GHCR and opens a PR against main with the new digest pin. After you review and merge, Flux or ArgoCD sees the updated image-pin.yml and rolls out the exact image CI scanned.

Can Claude Just Automate This For You?

The model side is easy. Claude Code with Opus 4.7 or Codex with GPT-5.5 will generate a working override in a few minutes. Pointing the agent at a repo is only half the battle. Knowing what you actually want and recognizing when the model is confidently wrong is the hard part. On a complex app where you don't know how the code is structured, the agent will gladly hand you a beautifully scanned, fully reproducible disaster. For most overrides, however, it is genuinely easy.

You must read what the agent shipped, understand it, and sign off on it. Skimming is not enough. The agent doesn't know if a CVE is exploitable from your topology, nor does it know your waiver policy. It will not push back on a 400-line diff on your behalf. That part stays human.

Save the scaffolding time and spend it on review. You get a five-minute setup and nightly automation, but judgment remains in human hands.

What Does a Morning Look Like With This Running?

10 AM. Second coffee. The pipeline finished while we were asleep. A notification is waiting in the inbox:

(A) "Gokapi v2.2.4 → v2.2.5. Trivy clean, SBOM updated. PR ready." You skim the diff, approve, and merge. ArgoCD picks up the new digest.

(B) "Gokapi v2.2.5 build BLOCKED. HIGH CVE in a dependency. Image NOT pushed. Issue opened." You open the issue, read the Trivy output, and make a decision. You can wait for an upstream fix or add a documented waiver to .trivyignore.

Either way, nothing unreviewed lands in your cluster. The build either waits clean or blocks with documentation. It costs roughly zero extra minutes a day once it's running.

Does This Protect You From Everything? What It Catches, What It Doesn't.

It catches registry-side image tampering since you pull from your own GHCR, along with missing CVE visibility at deploy time. It also handles the base-image CVE backlog and insulates you from Docker Hub outages. Crucially, we learned it catches abandoned upstreams that quietly stopped shipping patches. It provides the exact artifacts an auditor wants, including SBOMs and scan reports per upstream bump.

It catches CVEs published after release. The main build job scans at build time. A nightly rescan job then hits the last-built image against the current Trivy database without rebuilding. If a CVE lands tomorrow against a library inside yesterday's deployed image, the rescan surfaces it and auto-files an issue. You decide manually whether to wait for upstream or roll a dependency pin into your override. The pipeline never silently ships new digests. This covers the gap release-driven pipelines have by default.

It doesn't catch a sophisticated source-code backdoor from a compromised upstream maintainer like the xz scenario. No scanner catches that cleanly, and anyone selling you otherwise is overclaiming. What the pipeline does give you for that threat class is a documented diff-review artifact at every upstream bump. A human reviews it before deployment. This is the same control a well-run enterprise applies. It is defensible in an audit, which is more than most SMBs have today.

The whole problem stays invisible until the first due diligence questionnaire drags it into the open.

The Result, in Practice: Gokapi v2.2.4

v2.2.4 Ships With 8 HIGH on the Stock Image

Run Trivy against the upstream Gokapi v2.2.4 image using trivy image forceu/gokapi:v2.2.4. You get 8 HIGH findings sitting in the binary you were about to deploy. Most teams never scan and never know.

Rebuild with our override using a distroless/static:nonroot runtime and a Go 1.26.2 build stage, then rescan.

The result: Only 1 HIGH left.

Seven of the eight findings are gone. We used the same upstream code and release tag without Gokapi-side patches. We simply applied a different base layer and a fresher toolchain. The vulnerabilities that disappeared were never about Gokapi; they were about what Gokapi was being shipped on top of.

The single HIGH finding that remained sat in a code path our deployment doesn't exercise. We assessed it, filed a documented waiver, and shipped. The pipeline surfaced the finding so a human could make the call. The rationale is on record. This is exactly how the gate is supposed to work when blocking forever isn't an option.

GitHub Issue automatically created by a GitHub Actions bot showing a blocked CI/CD pipeline due to a vulnerability finding

The actual issue the bot filed for that one HIGH: CVE-2026–34986 in go-jose/v4. It's a Go JOSE DoS via crafted input with a fix in v4.1.4, while we were at v4.1.3 transitively. The build blocked, the image was not pushed, and the tracker received structured findings with cve, blocked, and security labels. Trivy scans the built binary rather than go.mod, so the report covers what actually ships after the linker has trimmed unused paths. From here, a human decides to wait, fix, or waive. We waived.

Now look at why.

The Override: 7 of 8, Gone

Gokapi's upstream Dockerfile uses a clean Go-Alpine build stage followed by a runtime layer of FROM alpine:3.19. It includes su-exec, tini, ca-certificates, curl, tzdata, and a shell entrypoint script. Our override swaps the runtime stage for gcr.io/distroless/static:nonroot:

FROM golang:1.26.2-alpine AS build
WORKDIR /src
RUN apk add --no-cache git
COPY . .
RUN go generate ./... && \
    CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" \
    -o /out/gokapi github.com/forceu/gokapi/cmd/gokapi

FROM gcr.io/distroless/static:nonroot
COPY --from=build /out/gokapi /gokapi
USER nonroot:nonroot
EXPOSE 53842
ENTRYPOINT ["/gokapi"]

Enter fullscreen mode Exit fullscreen mode

Several elements vanish on purpose. su-exec and tini act as workarounds for raw docker run. Kubernetes handles signal forwarding and privilege-dropping natively, and Go's standard library handles SIGTERM cleanly on its own. The upstream entrypoint script mostly switches UIDs at container start. Since distroless's :nonroot variant runs as UID 65532 out of the box, the script's job is already done. curl exists for raw-Docker health checks, whereas Kubernetes probes hit HTTP endpoints directly.

Distroless is Google's minimal-base-images project, licensed under Apache-2.0 since 2017. The static:nonroot variant is around 2 MB. It includes CA certificates, /etc/passwd, and tzdata, but omits a shell, package manager, wget, and sh.

Mechanics, in detail

Where the seven went. Six split cleanly by mechanism. Two were in Alpine-layer packages (musl, musl-utils) and vanished with the base swap. Four were Go stdlib CVEs, gone because the stdlib is linked into the binary and the toolchain bump rebuilt them. The seventh was in the application layer and cleared upstream between our pre- and post-override scans. The single HIGH that remained also sat in the application layer within a code path our deployment doesn't use. We waived it, documented the call, and shipped. The override clears the base-image backlog and the toolchain backlog in one pass. The application-layer backlog stays a manual judgment.

Those two Alpine findings represent a lower bound. The v2.2.4 release Docker Hub currently serves pins FROM alpine:3.19, which has been EOL since late 2025. Trivy flags this mid-scan, noting that the OS version is no longer supported and vulnerability detection may be insufficient. Backported fixes don't exist for that layer anymore. Upstream master is already on alpine:3.23 without a release yet. The gap between a CVE landing, an EOL kicking in, and a release catching up leaves users blind. The override cuts the dependency so we pick our own base on our own cadence.

A subtlety on the build stage. You might assume the CVEs in golang:1.26.2-alpine don't matter since it gets discarded after COPY --from=build. That is mostly true, with one exception: the Go toolchain links the standard library into the resulting binary. That is how the four Go stdlib CVEs vanished. We simply rebuilt on a newer Go. The builder Go release acts as a runtime-security lever rather than just builder hygiene. Renovate tracks new Go releases automatically, turning every stdlib patch into a reviewable bump PR.

Pin both FROM lines to digests. The example uses tags for readability, but production overrides should use strict SHA256 digests. Tags are mutable. If Google repoints distroless/static:nonroot overnight for a CA-certificate refresh, a tag-based build picks it up silently. A digest-pinned build stays put until you review the change. Renovate with pinDigests: true handles this by pinning on the first PR it opens and filing a new reviewable PR every time a digest changes.

Why distroless and not Docker Hardened Images? DHI is a solid product, but it sits inside an evolving vendor program with shifting free-tier terms. Distroless is Apache-2.0, Google-maintained, and stable. If you want to avoid vendor lock-in, distroless is the consistent choice. Chainguard represents the commercial tier above both and makes sense when your compliance regime explicitly demands vendor support.

The catch: distroless has no shell. kubectl exec -it pod -- sh doesn't work. For Kubernetes debugging, you need to use kubectl debug with an ephemeral container or maintain a :debug variant for non-production. This is a cultural shift, though not a difficult one.

The override is a one-time job per image, though the effort varies depending on what upstream ships. Gokapi sits at the higher end because its shell entrypoint, su-exec, and tini need translating into orchestrator-level concerns. Budget 30 to 60 minutes for something like that. Upstreams that ship a clean USER nonroot directive and a direct binary entrypoint are closer to a 10-minute swap.

What Does the Auditor Actually Open?

For any upstream bump in the last 90 days, you get:

  • Diff-review artifact: Commits landed, files changed, reviewer name from the merge commit, and timestamp.
  • Trivy scanner report: SARIF in the Security tab, timestamped on every build.
  • CycloneDX SBOM: Every package and version exactly as shipped. This is what DORA, SOC 2, and ISO 27001 ask for by name.
  • Digest-pin PR history: Every production image rollout goes through a reviewed and merged PR with linked scan evidence.
  • Documented base-image choice: dockerfile_source: override on every pinned build, reviewable in a PR.
  • Blocked builds with auto-filed issues: Idempotent tracking when a CVE hits a version before it reaches production.

This gives you a documented supply-chain control with artifacts that speak for themselves. It isn't as polished as Chainguard, but it is polished enough to pass an audit.

This pipeline does not replace Dependabot on your own code. Dependabot lives upstream and scans the repos you write. This pipeline provides the missing piece for the third-party OSS container layer where you don't own the source.

When Not to Bother

There are scenarios where a different approach wins. Large, well-audited upstreams like official Postgres, Nginx, or Redis offer no marginal value from building yourself. Pin the digest, proxy through a cache, and move on. Highly regulated workloads that require minimal-by-construction SBOMs and vendor support should pay for Chainguard or DHI. Finally, upstreams that release more than weekly break the human-in-the-loop review model.

For everything else—like a handful of OSS services you self-host for data sovereignty—the template works.

A quick note on licenses. Mirror images inherit upstream's license. Gokapi is AGPL-3.0, so what we publish to GHCR is AGPL-3.0. This is separate from this template's Apache-2.0 license. For self-hosted operators, AGPL §13 requires that network users can reach the source, which is trivially the upstream repo. Read upstream's LICENSE before publishing mirrored images to a public registry.


Template (fork for your own pipeline): https://github.com/THEKROLL-LTD/oss-mirror-build

Live example (our Gokapi mirror, nightly builds, audit bundles retained 90 days): https://github.com/THEKROLL-LTD/mirror-gokapi

PRs with Dockerfile overrides for other upstreams are welcome. The live mirror has no SLA. If you need production-grade assurance, fork the template.