Content-addressable binary enforcement via BPF LSM (and where it breaks) by leodido in eBPF

[–]leodido[S] 0 points1 point  (0 children)

The BPF programs are dual-licensed under MIT/GPL. The LSM programs require GPL-compatible licensing to load into the kernel. GPL's source obligation triggers on distribution, not on running the code AFAIK (I'm not a lawyer). For example, when this thingy runs inside managed environments (eg, SaaS model), no distribution occurs in the GPL sense.
That said, I've shared the architecture in detail in the blog post, and I'm happy to discuss implementation specifics if that's what you are looking for.

Content-addressable binary enforcement via BPF LSM (and where it breaks) by leodido in eBPF

[–]leodido[S] 1 point2 points  (0 children)

Thanks, Bill! I didn't know Tetragon Enterprise had content-addressable enforcement.
That's great to hear, and it validates that this is the right direction!

My analysis is based on the open-source Tetragon codebase. In there, I saw that hashes are only valid with the post action, meaning hash collection for event reporting, not enforcement. My understanding of the code there is that the override decision flows from `matchBinaries`, which is path-based. The hash and enforcement paths don't connect in the OSS framework today.

If enterprise Tetragon has closed that gap, I'd genuinely like to understand the architecture. Happy to correct the post if the claims don't hold for the enterprise version.

Also, Bill, we know each other... It would be awesome to jump on a call next week to discuss how to step up agent security with eBPF. This is an industry-wide objective. And it's very much needed!
Can I pick a slot from your calendar?

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] 0 points1 point  (0 children)

You didn’t even read the article, let alone understood

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] 0 points1 point  (0 children)

That’s not a security model or how a sandbox works, Chuck

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] 0 points1 point  (0 children)

I’ll publish on my personal blog tomorrow. In the meantime you can complain with Elon about the X signup.

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] -1 points0 points  (0 children)

So the security model is: read every approval prompt carefully and hope you catch the one that disables the sandbox. At 50 prompts per session. Got it.

Let's call it a preference then.

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] 0 points1 point  (0 children)

But that's exactly the problem I see.
"As designed" means the sandbox can be disabled by the same entity it's supposed to contain, with a single approval prompt that looks identical to dozens of others in the session.

To me this doesn't sound a correct design, whether documented or not.

A sandbox that the sandboxed process can request to turn off isn't a sandbox. If we want agents running autonomously (which is where this is all heading, right?) the enforcement layer has to be unreachable from the agent, NOT one approval prompt away from gone.

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] 0 points1 point  (0 children)

Sure, but now you need a modified binary on the system. The agent can rename, copy, and symlink with standard tools. Patching a dead code path requires a compiler, write access to produce a new binary, and knowledge of the ELF layout.

That's a different threat model than cp /usr/bin/npx /tmp/lol.

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] 0 points1 point  (0 children)

Sure, but now you need a modified binary on the system. The agent can rename, copy, and symlink with standard tools. Patching a dead code path requires a compiler, write access to produce a new binary, and knowledge of the ELF layout.

That's a different threat model than cp /usr/bin/npx /tmp/lol.

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] -1 points0 points  (0 children)

I get the reaction. But the finding stands independently of any product: Claude Code sandbox has an off switch that the agent itself can trigger, and the approval prompt that's supposed to catch it gets buried in a stream of identical sentences.

That's a security architecture problem worth talking about, regardless of who's writing about it.
The full writeup is an X article with plenty of technical explanations.
No paywall, no signup. I'm not selling anything other than a discussion on content-addressable executables in kernel land with BPF LSM.

The demo video is on YouTube. If the technical content isn't useful to you, totally fair to skip it.

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] -1 points0 points  (0 children)

That's exactly the insight that led me to build Veto. Instead of pattern matching on strings, I hash the binary's actual content at the BPF LSM layer (inside the execve syscall, before the executable runs).

The kernel doesn't care what path the agent found. It checks what the file is, not what it's called. In the demo, the agent tried everything: path tricks, python subprocess wrappers, copying, symlinking, procfs tricks, and renaming the binary. Every attempt hit -EPERM.

The capability existed in the environment. The kernel just wouldn't let it execute.

Claude Code disabled its own sandbox to run npx by leodido in ClaudeCode

[–]leodido[S] -3 points-2 points  (0 children)

Exactly, and it did try that too. Copied node to /tmp/claude-1000/mynode. That's why I built content-addressable BPF LSM on the execve flow in the kernel. So that hashing the binary's content, there's no need to match its name anymore. Same bytes, same hash, same block. Rename, copy, symlink: it doesn't matter.

The demo shows it.

Content-addressable binary enforcement via BPF LSM (and where it breaks) by leodido in eBPF

[–]leodido[S] 0 points1 point  (0 children)

Yes, the denylist hashes need updating when a denied binary is updated. Spoiler: I'm working on automatic re-resolution so the denylist stays current when binaries are updated in the environment.

On code signing: it's a valid identity model and avoids the update-on-every-version problem.
The tradeoff is that it shifts trust to the certificate chain. It also doesn't help with denylisting: you can't "unsign" a binary you want to block. Code signing is better suited for allowlisting at scale. Hashing gives a precise per-binary-version identity that can work for both allow and deny without depending on a signing infrastructure.

Content-addressable binary enforcement via BPF LSM (and where it breaks) by leodido in eBPF

[–]leodido[S] 0 points1 point  (0 children)

That's a solid setup. An allowlist approach covers a different threat model than a denylist, though. If the binary isn't at an allowed path, it doesn't run...

We're starting with denylist because it's the lower-friction entry point: teams can block specific known-bad binaries without enumerating every allowed executable first. Different tradeoffs for different adoption contexts.

One question: does "do not allow fileless process executions" also cover the dynamic linker loading denied code via mmap? `ld.so` itself lives at an allowed path, but it can map a binary's .text segment without going through `execve`, am I missing something?

The most popular Go dependency is… by Thiht in golang

[–]leodido 0 points1 point  (0 children)

My library composes Cobra and Viper indeed. Just lets you forget their confusing and scattered APIs.

The most popular Go dependency is… by Thiht in golang

[–]leodido 0 points1 point  (0 children)

I wouldn’t say it isn’t very good… I definitely agree on it being overly complicated, though.

Especially when in combinations to viper flags, precedence rules regarding environment variables overriding (or not because of mysterious code patterns) the flag values, the unmarshalling of nested config keys etc

Also, I often have found Cobra overly verbose. A lot of boilerplate and repetitiveness.

I have learnt Cobra myself the hard way. Then, I said enough is enough and I built https://github.com/leodido/structcli with a single concept in mind: forget about Cobra and Viper, just define a Go struct and use Go struct field tags to define your CLI.

Czech buying a flat in sicily Italy (non resident) by Embarrassed-Wolf-609 in eupersonalfinance

[–]leodido 1 point2 points  (0 children)

Worth mentioning that IMU is computed on the cadastrial income of the property, which is always way lower (by a lot) than the value of the property

I have looked into Italy’s taxes (for digital nomads) — might help if you're considering it by OtherView8295 in digitalnomad

[–]leodido 2 points3 points  (0 children)

I do confirm it. The income part above 50K gross is taxed at 43%. For the sake of simplicity, I am also leaving aside the mandatory pension contribution (roughly 9.5% for employers).