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

推荐订阅源

WordPress大学
WordPress大学
爱范儿
爱范儿
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CERT Recently Published Vulnerability Notes
P
Palo Alto Networks Blog
博客园 - 司徒正美
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
罗磊的独立博客
阮一峰的网络日志
阮一峰的网络日志
The Register - Security
The Register - Security
D
DataBreaches.Net
A
Arctic Wolf
C
Cyber Attacks, Cyber Crime and Cyber Security
P
Privacy & Cybersecurity Law Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
B
Blog
V
Vulnerabilities – Threatpost
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
G
Google Developers Blog
aimingoo的专栏
aimingoo的专栏
T
Tor Project blog
GbyAI
GbyAI
Recent Announcements
Recent Announcements
T
The Blog of Author Tim Ferriss
Simon Willison's Weblog
Simon Willison's Weblog
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
G
GRAHAM CLULEY
宝玉的分享
宝玉的分享
T
Threat Research - Cisco Blogs
C
Check Point Blog
W
WeLiveSecurity
F
Fortinet All Blogs
P
Proofpoint News Feed
Security Archives - TechRepublic
Security Archives - TechRepublic
月光博客
月光博客
Project Zero
Project Zero
Know Your Adversary
Know Your Adversary
V
Visual Studio Blog
H
Help Net Security
H
Hacker News: Front Page
Webroot Blog
Webroot Blog
S
Securelist
酷 壳 – CoolShell
酷 壳 – CoolShell
O
OpenAI News
The Cloudflare Blog
Attack and Defense Labs
Attack and Defense Labs

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
The Three-UID Containment Pattern for AI Agents on Linux
Josh Waldrep · 2026-05-10 · via DEV Community

Josh Waldrep

A correct AI agent containment model on a Linux workstation needs three Linux UIDs, not two. Two UIDs has a hole. The hole is structural, not a configuration mistake.

This post shows the three-UID model with a working nftables chain, the wrapper script that drops the agent process into the right identity, and the rollback path. The model came out of porting Kubernetes NetworkPolicy containment back to a single-machine setup, and the lesson it teaches is the same: the proxy needs internet because the proxy is the agent's exit. So the agent has to be a third identity.

Why two UIDs leaks

Naive containment says: run the proxy as one UID, run the agent as another. Add an nftables rule that drops anything from the agent UID except loopback. Done.

The problem surfaces the moment you ask which UID the agent runs as. If the agent runs as the proxy UID, the agent inherits direct internet because the proxy needs direct internet. The firewall cannot tell the agent's syscalls apart from the proxy's. They are the same UID.

If the agent runs as the operator UID, the agent has the operator's whole egress story, which is "anything I want." Same problem with extra steps.

The fix is to put the agent on a UID that is neither the operator nor the proxy. Three identities. The kernel firewall has a target to drop on. The proxy keeps its internet because it has its own UID. The operator keeps a normal desktop because the rules do not touch the operator UID. The agent process loses direct internet because it runs as a UID the firewall denies.

The model in one diagram and one chain

Three Linux UIDs:

  • operator: the human at the keyboard. Browser, terminal, git, kubectl. Normal egress.
  • pipelock-proxy: the proxy daemon. Runs the agent firewall. Has internet because that is its job.
  • cc-agent: every agent process. Coding CLI, AI assistant, browser driver, screenshot tool. Has loopback only.

The agent UID is denied direct internet by nftables. Loopback to the proxy is allowed. DNS to loopback is allowed because the operator's local resolver still serves names. Everything else from the agent UID drops.

The rule set lives in /etc/nftables.d/50-pipelock-containment.nft:

table inet pipelock_containment {
    chain output_filter {
        type filter hook output priority filter; policy accept;

        # Loopback always accepted. This is what the agent uses to reach the proxy.
        meta oif "lo" accept
        ip daddr 127.0.0.0/8 accept

        # Operator UID stays normal.
        meta skuid 1000 accept

        # Proxy UID has internet because the proxy IS the exit.
        meta skuid 988 accept

        # Agent UID: DNS to loopback resolver, then drop everything.
        meta skuid 987 udp dport 53 ip daddr 127.0.0.0/8 accept
        meta skuid 987 tcp dport 53 ip daddr 127.0.0.0/8 accept
        meta skuid 987 drop
    }
}

Enter fullscreen mode Exit fullscreen mode

That is the whole boundary. The proxy listens on 127.0.0.1:8888, the agent UID can reach loopback, the agent reaches the proxy through loopback, the proxy reaches the internet through its own UID's accepted rule. Everything else from the agent UID hits the drop and stays inside the kernel.

UIDs in the example are placeholders. The values vary by host. useradd --system picks them; capture them into your install state file once and reference by number.

The wrapper that drops the agent into the contained UID

Containment is structural, but a wrapper makes it usable day-to-day. Operators do not want to type sudo -u cc-agent -- every time they launch an agent.

Two pieces. First, a generic launcher at /usr/local/bin/cc-launch:

#!/bin/bash
set -euo pipefail
TOOL="$1"; shift
exec sudo -u cc-agent -- env \
    HOME=/home/cc-agent \
    HTTPS_PROXY=http://127.0.0.1:8888 \
    HTTP_PROXY=http://127.0.0.1:8888 \
    NO_PROXY=127.0.0.1,localhost \
    NODE_EXTRA_CA_CERTS=/etc/pipelock/ca.pem \
    SSL_CERT_FILE=/etc/pipelock/combined-ca.pem \
    REQUESTS_CA_BUNDLE=/etc/pipelock/combined-ca.pem \
    CURL_CA_BUNDLE=/etc/pipelock/combined-ca.pem \
    PATH=/home/cc-agent/.local/bin:/usr/local/bin:/usr/bin:/bin \
    "$TOOL" "$@"

Enter fullscreen mode Exit fullscreen mode

Second, per-tool wrappers like /usr/local/bin/cc-claude that just exec into the launcher:

#!/bin/bash
exec /usr/local/bin/cc-launch claude "$@"

Enter fullscreen mode Exit fullscreen mode

A scoped sudoers entry at /etc/sudoers.d/50-cc-agent allows the operator to drop into cc-agent without a password, but only via the launcher. The shape is:

operator ALL=(cc-agent) NOPASSWD: /usr/local/bin/cc-launch *

Enter fullscreen mode Exit fullscreen mode

This is not general-purpose sudo -u cc-agent access. The operator can run cc-launch to start agents, and that is all. The kernel firewall handles the network side. The sudoers handles the launch side. Together they keep the agent in its lane.

The CA bundle is load-bearing

If the proxy intercepts TLS, the agent UID needs the proxy's MITM CA in its trust store. The wrapper environment points every common library at the combined bundle:

  • NODE_EXTRA_CA_CERTS for Node.js and anything that uses tls.createSecureContext.
  • SSL_CERT_FILE for OpenSSL-linked clients.
  • REQUESTS_CA_BUNDLE for Python requests.
  • CURL_CA_BUNDLE for curl.

The bundle gets built once with pipelock tls export plus the system roots concatenated. Rebuild whenever the proxy CA rotates. Wrappers read the bundle by path, so a refresh of the file picks up automatically.

If you skip the CA bundle, the agent's HTTPS calls fail at TLS verification, and you spend an afternoon convinced the firewall is broken when the cert chain is the problem.

The verification probes

Containment is only real if you can prove it. Run these probes after install:

# 1. Operator still has internet.
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/

# 2. Proxy UID still has internet.
sudo -u pipelock-proxy curl -s -o /dev/null -w '%{http_code}\n' https://example.com/

# 3. Agent UID cannot dial direct.
sudo -u cc-agent curl -s -o /dev/null -w '%{http_code}\n' \
    --max-time 5 https://example.com/ 2>&1 \
    | grep -E '000|Connection refused|Network is unreachable'

# 4. Agent UID can reach the internet through the proxy.
sudo -u cc-agent curl -s -o /dev/null -w '%{http_code}\n' \
    -x http://127.0.0.1:8888 https://example.com/

# 5. The wrapper end-to-end.
cc-launch curl -s -o /dev/null -w '%{http_code}\n' https://example.com/

Enter fullscreen mode Exit fullscreen mode

Probes 1 and 2 prove the operator and proxy paths still work. Probe 3 proves the boundary holds. Probe 4 proves the proxy path is the legitimate exit. Probe 5 proves the wrapper sets up the agent's egress correctly.

If any of these fail, the boundary is not real. Roll back, fix the offending step, try again. Half-installed containment is worse than no containment because the dashboard says "secure" and the kernel disagrees.

Rollback

The boundary is reversible. The teardown:

  1. Disable the system pipelock unit, re-enable the user-mode unit.
  2. Delete the nftables table and remove the rule file.
  3. Remove the wrappers and the sudoers carve-out.
  4. Optionally remove the system users.

If the rollback procedure does not exist as a script, the install procedure is incomplete. Production systems get installed and uninstalled. Skipping rollback design is how operators end up afraid to touch the firewall later.

Why a CLI is the natural endpoint

The procedure described above is fifteen pages long when written out as a runbook. It collapses to five commands when written as a CLI: pipelock contain install / verify / rollback / add-tool / ca-refresh. The runbook proves the model. The CLI makes the model deployable to more than one workstation.

pipelock contain is being scoped for a future release. Until it lands, the runbook is the documented procedure. Either way, the load-bearing piece is the three-UID separation. The wrappers, sudoers entries, CA bundle, and probes are operational glue around that core idea.

What this post is and is not

This post is a description of a working pattern for one Linux workstation. It is the same shape as Kubernetes per-pod NetworkPolicy: the kernel below the agent is the boundary, and the agent's runtime choices do not reach the kernel.

This post is not a substitute for content scanning at the proxy. The boundary stops the agent from leaving without going through the proxy. The proxy is what catches credential leaks, prompt injection in responses, and tool-call abuse. Containment without scanning is a tunnel with no inspection. Scanning without containment is inspection that the agent can route around. Both layers exist for a reason.

If you are running agents on a Linux box and the only egress control is HTTPS_PROXY, this is the upgrade path. The kernel will agree with you for the first time.