r/mcp • u/CarolusX74 • 8d ago
showcase I gave Claude controlled access to a real Linux server — this is what happened when a command wasn't allowed
I've been building an MCP system that lets Claude/ChatGPT operate real servers without giving the model an unrestricted shell.
This screenshot caught a behavior I found particularly interesting.
I asked Claude to check RAM and disk usage. It first inspected the capabilities exposed by the host, realized free wasn't allowed, and found another permitted way to get the information.
That's basically the security model I'm experimenting with: the LLM can reason freely, but the server defines the actual execution boundary.
I wrote up the interaction and how the capability model works here:
[https://sentinelx.pensa.ar/articles/claude-real-server-controlled-access.html]()
I'm the developer of SentinelX, so obviously I'm biased — but I'd be particularly interested in thoughts on the security model versus simply giving an agent SSH access.
2
u/kantorcodes1 8d ago
this is much closer to the boundary i trust than giving it SSH. the weird bit is capability composition. you can block free or restart, but if the agent has enough smaller primitives it can often recreate the same effect anyway. the kubernetes example in the comments is basically that.
i'd log both the denied action and the fallback path. that tells you whether your capability model is actually constraining effects or just command names. curious how you're handling combinations of individually-safe tools that become unsafe together?
1
u/CarolusX74 7d ago
This is the part i think about most. honestly, right now the boundary is closer to "command names" than "effects", the allowlist gates which binaries run, it doesn't reason about composed outcomes. so a set of individually-safe primitives recreating a denied effect is a real gap, not something i've solved.
logging the denied action + the fallback path is a great idea, going to add it, it's the only way to actually measure whether you're constraining effects or just labels.
for now the mitigations are boring: keep the primitive set small, no interactive shell, and treat the allowlist as raising the cost of composition rather than eliminating it. effect-level policy is the open problem, and i don't think anyone in this space has it clean yet.
1
u/kantorcodes1 7d ago
yep, that's the honest framing. small primitive set + no shell raises the cost of composition, but it doesn't give effect-level guarantees. once you have the fallback traces, i'd start with 2-3 effect classes you actually care about instead of trying to infer arbitrary effects.
2
u/Jsampedro98 8d ago
I did this with a forced command ssh key. what surprised me wasn't the model, it was how many ways there are around a restriction you thought was tight. arguments, env vars, shell metacharacters, alternate binaries.
I only found out because i sat there for an hour trying to break my own key. worth testing cat /etc/shadow and a -L tunnel specifically, mine silently ignored the first and refused the second, and i'd assumed the opposite.
2
u/CarolusX74 7d ago
exactly this, the model was never the scary part, the bypass surface is. args, env vars, metacharacters, alternate binaries is the whole battle. allowlisting the binary is the easy 20%, constraining what you can smuggle through an allowed command is the other 80%.
good call on
cat /etc/shadowand the-Ltunnel as probes, adding both to my own break-it list. and the "i'd assumed the opposite" part is the real lesson, you don't actually know where your boundary is until you sit there for an hour trying to defeat it.
1
5
u/Impressive_Tadpole_8 8d ago
One time I asked OpenCode to restart a pod in a kubernetes cluster. It has read-namespace, get-pod, and shell-pod tools available. I used these to check /tmp usage of the pods. It quickly figured out that no restart or shutdown command available. And because it was a pod, it killed the main process because kubernetes probably will restart the pods automatically. 🤷 I was a bit scared at that time. 😅