Building puck... Why we chose Rust
When we set out to build puck.tools, the idea was clear: create a lightweight, reliable way to repeatedly test networks against the communications component of a C2 agent.
In security operations, network egress often remains one of the most overlooked parts of defense. Attackers know this, and many C2 (command-and-control) frameworks focus on reliably establishing outbound channels in hostile environments. We wanted a tool that could easily simulate these communication patterns to help security teams harden their defenses.
We chose Rust to build puck.tools. Here's why it made sense for us—and where we had to fight through the challenges.
Why Rust?
1. Fine-Grained Control Over Communications
At its core, puck.tools needed to:
- Speak over raw TCP, HTTP/S, DNS tunneling, and other common outbound protocols.
- Build packets carefully, sometimes mimicking real agent traffic byte-for-byte.
- Operate reliably even under constrained network conditions.
Rust gives you extremely fine-grained control over sockets, buffers, encryption, and protocol behavior—without sacrificing the stability and predictability. Rust makes it almost impossible to ignore the corner cases unlike working C or C++.
2. True Cross-Platform Support
We needed puck.tools agents to work on Linux and Windows with minimal code changes. Rust's ecosystem made this far easier:
- The standard library is designed with portability in mind.
- Tools like
cross
andcargo
simplify compiling for multiple platforms. - Conditional compilation (
#[cfg(windows)]
,#[cfg(unix)]
) lets you cleanly handle OS-specific code.
Rather than maintaining two separate codebases or fighting constant portability bugs, Rust let us share 95%+ of the code across targets.
3. Safety Without Sacrificing Performance
C2 communications often involve handling unpredictable, sometimes hostile environments. Memory safety errors (buffer overflows, use-after-free, etc.) could lead to instability, crashes, or security issues.
Rust's strict compiler guarantees helped us:
- Avoid subtle memory bugs.
- Eliminate data races in our asynchronous code (built with
tokio
). - Build fast, lightweight binaries without a garbage collector.
That peace of mind was invaluable, especially in a security-sensitive tool.
The Downsides of Rust (And How We Dealt With Them)
1. Steep Learning Curve
Rust demands a lot upfront:
- The borrow checker forces you to deeply understand memory ownership.
- Even experienced developers need time to "think in Rust."
We accepted that it would take longer to get the first versions working. But we also knew that once you internalize Rust's model, you move faster with fewer bugs.
Pro Tip: For small teams or startups, it’s worth budgeting extra ramp-up time if Rust is new to the team.
2. Relative Newness
Rust's ecosystem is growing fast, but it's not as mature as C/C++ or Python. Some challenges we hit:
- Sparse support for some niche network protocols.
- Fewer "battle-tested" libraries compared to older languages.
That said, crates like axum
, tokio
, hyper
, reqwest
, and serde
are incredibly solid. We rarely found ourselves truly blocked—just sometimes needing to build extra functionality ourselves.
3. Build Times
Rust’s build system (cargo
) is robust, but it can be slow, especially on large projects or when compiling across platforms.
Incremental builds are good, but full builds (especially targeting Windows from Linux) sometimes required patience. We invested in:
- Faster dev machines.
- Using
cargo check
during development for faster feedback loops. - Caching strategies in our CI/CD pipeline.
The trade-off was worth it for the correctness and reliability we gained.
Final Thoughts
Choosing Rust for puck.tools wasn't the "safe" choice. It was the right choice.
We got:
- The low-level control we needed to faithfully mimic real C2 traffic.
- A shared, clean codebase across Windows and Linux.
- Extra confidence in our stability and security story.
Rust made us slower at the start, but faster, safer, and more capable in the long run. If you're building something security-critical, performance-sensitive, or cross-platform—Rust is absolutely worth considering.
Stay tuned—we’ll be sharing more about puck.tools and the journey to simplify network egress testing for security teams.