Most recon tooling treats network egress as an afterthought. You run a scanner, traffic leaves from whatever interface the host happens to have, and DNS resolves through whatever resolver the OS was configured with. For a serious engagement that is not good enough. Where your traffic originates is a scope question, a data-hygiene question, and a fidelity question all at once. Crossfyre builds egress control into the node itself rather than leaving it to you to wire up by hand.
This is about keeping authorized tests in-scope and modeling a realistic adversary, not about hiding unauthorized activity. Controlling source IP matters when a client tells you to test from an allow-listed address, when you are simulating an attacker in a specific region, or when you must prove your traffic stayed inside agreed boundaries. If you do not have written authorization to test a target, none of this applies to you. Use it responsibly or do not use it at all.
Why egress is an OPSEC primitive
Three concrete reasons the source of your traffic matters on a real engagement:
- Staying in-scope. Clients routinely require testing to originate from a specific IP or block they have allow-listed, so their team can distinguish your authorized traffic from a real attack. If your scan leaks out from the wrong interface, you have stepped outside the agreement.
- Realistic adversary modeling. A real attacker is not sitting on your office IP. Testing from egress that resembles the threat you are simulating (a given region, a cloud range, a residential exit) gives the defender a truer picture of what they would actually see.
- Not poisoning the test. If your own infrastructure IP gets flagged and blocked mid-engagement, your results are now about your IP's reputation, not the target's real posture. Controlled, separable egress keeps the test about the target.
Layer one: VPN-isolated network namespaces per node
The foundation is isolation. A Crossfyre node can run its scan engines inside a dedicated network namespace with its own VPN/WireGuard tunnel. Everything the engines do leaves through that tunnel and nothing else. The host's own network is never touched.
This is why crossfyre node init wants root: setting up isolated namespaces and tunnels is privileged work. Once it is in place, the practical guarantees are strong:
- Traffic originates from a chosen IP/network. Scans leave from the tunnel's exit, not from the box you happen to be sitting on. Run a node on a laptop and have its scans appear to come from a server in another country.
- The host network stays clean. Because the engines live in their own namespace, the machine's normal traffic and the scan traffic never mix. Your management plane and your attack plane are separate.
- No DNS leaks. DNS resolution happens inside the namespace, through the tunnel, not via the host's default resolver. This is the leak that catches people out: traffic goes through the VPN but name lookups quietly go around it, revealing your real resolver and location. Resolving inside the namespace closes that gap.
DNS-leak prevention is not a nice-to-have. A tunnel that carries your HTTP traffic but leaks DNS to the host resolver tells anyone watching exactly where you really are. Binding the resolver to the namespace is the difference between "egress control" and the appearance of it.
Layer two: proxy chains on top of the tunnel
The VPN namespace decides which network your traffic enters. Proxy chains decide the path it takes after that. A chain is an ordered set of proxies that your scan traffic hops through before it reaches the target, so the connection the target sees comes from the final hop, not from your node.
Layering matters because one hop is rarely the whole story on a real engagement. You might tunnel into a region, then route through a proxy that exits on the exact allow-listed address the client gave you. The chain is composed, each hop doing one job:
- The node's engines emit traffic inside the isolated namespace.
- That traffic enters the VPN tunnel, fixing which network it originates on.
- It then traverses the proxy chain, hop by hop, each forwarding to the next.
- The target sees a connection from the chain's final exit, which is the IP you deliberately chose to present.
Multi-hop egress control is what keeps a test inside a narrow, agreed boundary. When a client says "all traffic must come from this one address," a chain that terminates on that address is how you honor it precisely, across an entire distributed fleet, instead of hoping every node happened to be configured right.
Assigning proxies to nodes and workflows
Egress control is only useful if it is easy to point in the right direction. Crossfyre lets you attach proxies and chains where they make sense:
- Per node. Give a node a default chain so everything it runs leaves through a known exit. Useful when a node is dedicated to one client's engagement.
- Per workflow. Pin a specific workflow to a specific egress so a single scan honors a single client's allow-list, even if the node it runs on usually does other work.
Because the same workflow can run across many nodes, binding egress to the workflow means every node executing it presents the same agreed source, with no per-host drift. That is the property that makes "we tested only from the allow-listed address" a statement you can actually stand behind.
Proxy health testing
Proxies fail. They go offline, get rate-limited, get blocked, or quietly start returning garbage. A dead proxy in the middle of a chain does not just slow a scan, it can break the egress guarantee you were relying on, and the worst version is a chain that fails open and starts leaking from the wrong hop.
So proxies get health-tested: checked for reachability and for the exit they actually present, before and during use, so a chain you depend on is a chain that is actually up. A proxy that is not answering is one you find out about from a health check, not from a half-finished scan that left from the wrong place.
Treat your egress like part of your scope document. Before a major engagement, verify the chain exits where the agreement says it must, confirm there is no DNS leak out of the namespace, and re-check health once the run is underway. The discipline is cheap. Being out-of-scope because a proxy died is not.
How it fits together
The full picture is layered on purpose. The VPN-isolated namespace decides which network you are on and stops DNS from leaking around it. The proxy chain decides the precise path and exit. Assignment binds that egress to the node or workflow that needs it. Health testing keeps the whole thing honest while it runs.
None of this is about being sneaky for its own sake. It is about being able to prove, to a client and to yourself, that your authorized test left from exactly where you said it would and nowhere else. That is what OPSEC by design means here: the boring, verifiable kind that keeps engagements in-scope. For the full setup, see the nodes documentation.
Enrol a node, attach a proxy chain, and run your next authorized test from exactly the IP you choose.
Start free