PageSourceSearch

https://namespace.so/_next/static/chunks/pages/blog/protecting-you…s-with-egress-filtering-d798ebf340c8ef24.js

js namespace.so collected 2026-09-25 20:14:54 UTC 14,268 bytes, 1 lines download raw bytes

1!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},t=(new e.Error).stack;t&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[t]="fd5666d7-0df5-4963-8ccf-5ed93020dd83",e._sentryDebugIdIdentifier="sentry-dbid-fd5666d7-0df5-4963-8ccf-5ed93020dd83")}catch(e){}}(),(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2639],{3479:(e,t,s)=>{"use strict";s.r(t),s.d(t,{default:()=>u});var n=s(23798),i=s(24943),r=s(99089),a=s(39760),o=s(53037),l=s(29093),d=s(65269);let c=e=>(0,n.jsx)(r.T,Object.assign({name:"protecting-your-workloads-with-egress-filtering",authorColor:"black"},e));function h(e){let t=Object.assign({p:"p",a:"a",h2:"h2",span:"span",h3:"h3",code:"code",em:"em"},(0,i.RP)(),e.components);return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.p,{children:"As coding agents get better and better at doing work for us, they also get better at doing work for\nmalicious actors. Just a few months ago, an attacker hijacked an Axios maintainer's npm account and\npublished two compromised versions of the library. They added a hidden dependency that ran on install\nand would download and execute platform-specific second-stage payloads from a malicious URL."}),"\n",(0,n.jsxs)(t.p,{children:["This attack made us realize we needed to provide more control and visibility around egress to our\ncustomers. We already had egress filtering at the instance level, but our customers need to be able to\nconfigure and observe it directly in the ",(0,n.jsx)(t.a,{href:"/docs/solutions/github-actions",children:"GitHub Runners"})," and\n",(0,n.jsx)(t.a,{href:"/docs/devbox",children:"Devboxes"}),". Egress filtering gives you a way to make sure that even when something gets\nthrough, it has nowhere to go: the Axios attack, like a lot of supply chain attacks, only worked\nbecause the compromised code could call out."]}),"\n",(0,n.jsx)(o.O1,{label:(0,n.jsx)(n.Fragment,{children:"When the incident was disclosed, we used our job-level telemetry to identify which customers had potentially been impacted and notified them directly."}),infoCircleColor:"text-black",labelTextColor:"text-black",linkTextColor:"text-black"}),"\n",(0,n.jsxs)(t.h2,{id:"what-egress-filtering-does",children:["What egress filtering does",(0,n.jsx)(t.a,{href:"#what-egress-filtering-does",className:"autolink","aria-hidden":"true",tabIndex:"-1",children:(0,n.jsx)(t.span,{className:"subheading-anchor"})})]}),"\n",(0,n.jsx)(t.p,{children:"Egress filtering restricts a workload's outbound network access to a list of allowed domains. A CI\nrunner or Devbox usually needs only its source host, a package registry or two, and whatever APIs\nthe job calls, so in the majority of cases the allow-list is short and stable."}),"\n",(0,n.jsx)(t.p,{children:"With it in place, a compromised dependency can still execute, but it has nowhere to send what it\ncollects: the beacon fails, the callback fails, the exfiltration attempt fails. This is why network\nallowlisting has been commonly viewed as a popular mitigation for incidents like these."}),"\n",(0,n.jsxs)(t.p,{children:["Namespace gives you egress filtering for both ",(0,n.jsx)(t.a,{href:"/docs/solutions/github-actions",children:"GitHub Actions runners"}),"\nand ",(0,n.jsx)(t.a,{href:"/docs/devbox",children:"Devboxes"}),", along with a dashboard that shows what outbound requests your workloads\nare making."]}),"\n",(0,n.jsxs)(t.h2,{id:"setting-it-up",children:["Setting it up",(0,n.jsx)(t.a,{href:"#setting-it-up",className:"autolink","aria-hidden":"true",tabIndex:"-1",children:(0,n.jsx)(t.span,{className:"subheading-anchor"})})]}),"\n",(0,n.jsx)(t.p,{children:"Egress filtering is available on Linux for both GitHub Actions Runners and Devboxes, using the same\nallow-list model."}),"\n",(0,n.jsxs)(t.h3,{id:"github-actions",children:["Github Actions",(0,n.jsx)(t.a,{href:"#github-actions",className:"autolink","aria-hidden":"true",tabIndex:"-1",children:(0,n.jsx)(t.span,{className:"subheading-anchor"})})]}),"\n",(0,n.jsxs)(t.p,{children:["For GitHub Actions runners, configure it on a ",(0,n.jsx)(t.a,{href:"/docs/solutions/github-actions#configure-your-runners",children:"runner profile"}),".\nOpen the profile in the ",(0,n.jsx)(t.a,{href:"/workspace/actions/profiles/",children:"web UI"}),", enable it in the network policy,\nand list the allowed domains. Prefix a domain with ",(0,n.jsx)(t.code,{children:"*."})," to include its subdomains. The minimum set\nrequired to talk to GitHub is included by default."]}),"\n",(0,n.jsx)(o.G1,{src:l.A,alt:"Github Actions Egress Filtering"}),"\n",(0,n.jsx)(t.p,{children:"You can also configure it from the CLI:"}),"\n",(0,n.jsx)(a.F,{command:'nsc github profile create --tag "secure-runners" --egress_policy DOMAIN_ALLOW_LIST --egress_domain_allow_list "*.github.com,*.npmjs.org"'}),"\n",(0,n.jsxs)(t.h3,{id:"devboxes",children:["Devboxes",(0,n.jsx)(t.a,{href:"#devboxes",className:"autolink","aria-hidden":"true",tabIndex:"-1",children:(0,n.jsx)(t.span,{className:"subheading-anchor"})})]}),"\n",(0,n.jsxs)(t.p,{children:["Devboxes use the same model, configured through the ",(0,n.jsx)(t.a,{href:"/blog/define-your-devbox-environment-with-a-spec-file",children:"Devbox spec file"}),".\nSet ",(0,n.jsx)(t.code,{children:"network_policy.egress_domains"})," to restrict outbound access:"]}),"\n",(0,n.jsx)(a.bV,{lang:"yaml",code:'network_policy:\n  egress_domains:\n    - "github.com"\n    - "*.githubusercontent.com"\n    - "registry.npmjs.org"'}),"\n",(0,n.jsxs)(t.h2,{id:"seeing-what-your-workloads-actually-reach",children:["Seeing what your workloads actually reach",(0,n.jsx)(t.a,{href:"#seeing-what-your-workloads-actually-reach",className:"autolink","aria-hidden":"true",tabIndex:"-1",children:(0,n.jsx)(t.span,{className:"subheading-anchor"})})]}),"\n",(0,n.jsxs)(t.p,{children:["Once you have created an egress policy, we surface every outbound request in the\n",(0,n.jsx)(t.a,{href:"/workspace/egress",children:"Egress Traffic Dashboard"}),". Requests are grouped by base domain and split into\n",(0,n.jsx)(t.em,{children:"Allowed"})," and ",(0,n.jsx)(t.em,{children:"Denied"}),". This allows you to confirm that the domains a task needs are reachable\nand spot anything that was blocked. Each instance also has 
1its own Egress tab, useful when a\nspecific build fails or you need to check what a suspicious job tried to reach."]}),"\n",(0,n.jsx)(o.G1,{src:d.A,alt:"Egress Dashboard"}),"\n",(0,n.jsx)(t.p,{children:"The dashboard is helpful during incident response. When a dependency is disclosed as compromised,\na denied-domain record and per-instance egress history show you which jobs ran it and what they\ntouched, instead of piecing it together from scattered logs."}),"\n",(0,n.jsxs)(t.h2,{id:"summary",children:["Summary",(0,n.jsx)(t.a,{href:"#summary",className:"autolink","aria-hidden":"true",tabIndex:"-1",children:(0,n.jsx)(t.span,{className:"subheading-anchor"})})]}),"\n",(0,n.jsx)(t.p,{children:"Egress filtering blocks malicious outbound requests during the build itself. It works by restricting a\nworkload's outbound network access to an allow-list of domains, so even if a compromised dependency runs,\nit has nowhere to send what it collects."}),"\n",(0,n.jsx)(t.p,{children:"Namespace supports egress filtering across all products, including GitHub Actions runners, and Devboxes.\nThe dashboard helps you understand what requests your workloads are making. Use observation mode to\nbuild the allow-list from real traffic, then switch to block mode once the list is stable, starting\nwith your highest-value pipelines."}),"\n",(0,n.jsxs)(t.p,{children:["None of these controls cover everything on their own, which is why you want ",(0,n.jsx)(t.a,{href:"/blog/defense-in-depth-how-we-think-about-security",children:"more than one"}),".\nEgress filtering earns its place because it catches a different failure mode than the others: a\ncompromised dependency, an install hook running code you didn't write, an agent tricked into\nreconnaissance. In each case, the attacker still needs to get data out. Egress filtering is what\nstops that call from going through."]}),"\n",(0,n.jsxs)(t.p,{children:["Egress filtering is generally available now. See the\n",(0,n.jsx)(t.a,{href:"/docs/security/egress-policy",children:"egress filtering documentation"})," to get started."]})]})}let u=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return(0,n.jsx)(c,Object.assign({},e,{children:(0,n.jsx)(h,e)}))}},10907:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n=(0,s(94195).A)("file-code",[["path",{d:"M10 12.5 8 15l2 2.5",key:"1tg20x"}],["path",{d:"m14 12.5 2 2.5-2 2.5",key:"yinavb"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z",key:"1mlx9k"}]])},13408:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n=(0,s(94195).A)("zap",[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]])},15205:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n=(0,s(94195).A)("square-terminal",[["path",{d:"m7 11 2-2-2-2",key:"1lz0vl"}],["path",{d:"M11 13h4",key:"1p7l4v"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2",key:"1m3agn"}]])},17358:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n=(0,s(94195).A)("move-up",[["path",{d:"M8 6L12 2L16 6",key:"1yvkyx"}],["path",{d:"M12 2V22",key:"r89rzk"}]])},24943:(e,t,s)=>{"use strict";s.d(t,{RP:()=>r});var n=s(21462);let i=n.createContext({});function r(e){let t=n.useContext(i);return n.useMemo(()=>"function"==typeof e?e(t):{...t,...e},[t,e])}},28270:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n=(0,s(94195).A)("bot",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]])},29093:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n={src:"/_next/static/media/devbox-egress-filtering.2a6e8867.png",height:588,width:1654,blurDataURL:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAADCAMAAACZFr56AAAABlBMVEX8/f3z9PUJGtWYAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAE0lEQVQImWNgZIACRkYoE8QAMQEAfgAJyuP7NgAAAABJRU5ErkJggg==",blurWidth:8,blurHeight:3}},39343:(e,t,s)=>{"use strict";s.d(t,{A:()=>i});var n=s(21462);let i=n.forwardRef(function(e,t){let{title:s,titleId:i,...r}=e;return n.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"
1currentColor","aria-hidden":"true","data-slot":"icon",ref:t,"aria-labelledby":i},r),s?n.createElement("title",{id:i},s):null,n.createElement("path",{fillRule:"evenodd",d:"M12.53 16.28a.75.75 0 0 1-1.06 0l-7.5-7.5a.75.75 0 0 1 1.06-1.06L12 14.69l6.97-6.97a.75.75 0 1 1 1.06 1.06l-7.5 7.5Z",clipRule:"evenodd"}))})},44498:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n=(0,s(94195).A)("link-2",[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2",key:"8i5ue5"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2",key:"1b9ql8"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12",key:"1jonct"}]])},54650:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n=(0,s(94195).A)("git-compare-arrows",[["circle",{cx:"5",cy:"6",r:"3",key:"1qnov2"}],["path",{d:"M12 6h5a2 2 0 0 1 2 2v7",key:"1yj91y"}],["path",{d:"m15 9-3-3 3-3",key:"1lwv8l"}],["circle",{cx:"19",cy:"18",r:"3",key:"1qljk2"}],["path",{d:"M12 18H7a2 2 0 0 1-2-2V9",key:"16sdep"}],["path",{d:"m9 15 3 3-3 3",key:"1m3kbl"}]])},65269:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n={src:"/_next/static/media/egress-traffic-ui.4a79bd70.png",height:1140,width:1600,blurDataURL:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAGCAMAAADJ2y/JAAAABlBMVEX9/f7z9fcn5vm+AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAGElEQVQImWNgZAADRgYEA8qCicAZcDUMAAFsAAsG2T+cAAAAAElFTkSuQmCC",blurWidth:8,blurHeight:6}},76018:(e,t,s)=>{"use strict";s.d(t,{Wx:()=>d});var n=s(21462),i=Object.defineProperty,r=new Map,a=new WeakMap,o=0,l=void 0;function d({threshold:e,delay:t,trackVisibility:s,rootMargin:i,root:c,triggerOnce:h,skip:u,initialInView:g,fallbackInView:b,onChange:f}={}){var p;let[y,m]=n.useState(null),A=n.useRef(f),[w,x]=n.useState({inView:!!g,entry:void 0});A.current=f,n.useEffect(()=>{let n;if(!u&&y)return n=function(e,t,s={},n=l){if(void 0===window.IntersectionObserver&&void 0!==n){let i=e.getBoundingClientRect();return t(n,{isIntersecting:n,target:e,intersectionRatio:"number"==typeof s.threshold?s.threshold:0,time:0,boundingClientRect:i,intersectionRect:i,rootBounds:i}),()=>{}}let{id:i,observer:d,elements:c}=function(e){let t=Object.keys(e).sort().filter(t=>void 0!==e[t]).map(t=>{var s;return`${t}_${"root"===t?!(s=e.root)?"0":(a.has(s)||(o+=1,a.set(s,o.toString())),a.get(s)):e[t]}`}).toString(),s=r.get(t);if(!s){let n,i=new Map,a=new IntersectionObserver(t=>{t.forEach(t=>{var s;let r=t.isIntersecting&&n.some(e=>t.intersectionRatio>=e);e.trackVisibility&&void 0===t.isVisible&&(t.isVisible=r),null==(s=i.get(t.target))||s.forEach(e=>{e(r,t)})})},e);n=a.thresholds||(Array.isArray(e.threshold)?e.threshold:[e.threshold||0]),s={id:t,observer:a,elements:i},r.set(t,s)}return s}(s),h=c.get(e)||[];return c.has(e)||c.set(e,h),h.push(t),d.observe(e),function(){h.splice(h.indexOf(t),1),0===h.length&&(c.delete(e),d.unobserve(e)),0===c.size&&(d.disconnect(),r.delete(i))}}(y,(e,t)=>{x({inView:e,entry:t}),A.current&&A.current(e,t),t.isIntersecting&&h&&n&&(n(),n=void 0)},{root:c,rootMargin:i,threshold:e,trackVisibility:s,delay:t},b),()=>{n&&n()}},[Array.isArray(e)?e.toString():e,y,c,i,h,u,s,b,t]);let k=null==(p=w.entry)?void 0:p.target,v=n.useRef(void 0);y||!k||h||u||v.current===k||(v.current=k,x({inView:!!g,entry:void 0}));let j=[m,w.inView,w.entry];return j.ref=j[0],j.inView=j[1],j.entry=j[2],j}n.Component},78260:(e,t,s)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/blog/protecting-your-workloads-with-egress-filtering",function(){return s(3479)}])},81557:(e,t,s)=>{"use strict";s.d(t,{A:()=>n});let n=(0,s(94195).A)("terminal",[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]])}},e=>{e.O(0,[2091,2662,1945,8715,8690,7451,5793,3831,9297,3037,9579,9926,9089,636,6593,8792],()=>e(e.s=78260)),_N_E=e.O()}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.