Luther Monson, Staff Software Engineer at SUSE

Luther Monson

Go by way of PHP · Containers · VMs · Windows · Kubernetes

Your Agent's Shell Allowlist Is Bypassable

A permission gate for AI agents' shell commands judged spellings instead of commands. I found several classes of bypass in shipmates' own gate, and one idea fixes most of them.

9-Minute Read

A shell command decomposed into the nested command lines the permission gate has to judge

Every shipmates crew member runs shell commands. Before any of them runs, it goes through a permission gate. The gate uses the same rule syntax as Claude Code settings: Bash(git status), Bash(rm *), Write(**/.claude/settings.json). The rules are merged from several layers:

Bind Mounts Across Mount Namespaces: Four PRs and a Container Escape

Making GitHub Actions container: jobs work through ephemerd's Docker API shim took four tries, then a TOCTOU race turned the working version into an escape.

8-Minute Read

Sequence diagram of the bind mount TOCTOU race and the openat2 plus staging fix

GitHub Actions has a container: key that runs a job’s steps inside an image of your choosing. It’s popular because it gives you a reproducible toolchain without building a custom runner image. On a self-hosted runner it works by having the runner drive Docker. On ephemerd, “Docker” is a shim that speaks the Docker API and creates containers in containerd. Getting bind mounts right through that shim took four PRs in a week. A couple of months later the version that worked…

Every Way a Tenant Escaped: A Cross-Tenant Breakout Field Guide

Eight ways one site broke out of its sandbox on a multi-tenant PHP server, all found by attacking it on purpose, all fixed.

10-Minute Read

Table of tenant isolation boundaries in ePHPm, how each was crossed, and the PR that fixed it

ePHPm can serve a directory full of sites out of one process. Point sites_dir at a folder and every subdirectory is a virtual host. The Host header decides which one you get. That’s how the PR preview environment runs. Every pull request gets a subdomain, and the code behind it is whatever somebody pushed. One process holding many tenants means ePHPm enforces every per-tenant boundary itself, in-process. There’s no container wall between site A and site B unless you add one.

Security and Performance Hardening of a Rust Database Proxy

What a week of hardening litewire turned up. A dependency had a memory bug, one site could open its neighbor's database, a security rule lived only in a comment and every query paid for a needless 1 MB clear.

11-Minute Read

Bar chart of point SELECT throughput over the litewire MySQL frontend at three revisions: before the command filter, with the filter, and with the poll_read fix

litewire is the piece of ePHPm that lets PHP apps talk MySQL to a SQLite file. I wrote about how it works and about the MySQL quirks WordPress depends on. This one is the less visible half of August:

My iptables Rule Matched 0 Packets in 9.8 GB

Container-to-host traffic goes through INPUT, not FORWARD, so ephemerd's carefully written allow-list never matched a single packet.

8-Minute Read

Netfilter flow showing container traffic to the host taking INPUT instead of FORWARD

After a week of Windows firewall rules that listed fine and enforced nothing, I went back to the Linux side of ephemerd feeling pretty good about it. iptables is the boring, well-understood one. Then I looked at the packet counters on a live node. I found a rule that had never matched a packet in its life. That node had pushed 9.8 GB through the same chain.

Five Ways to Not Firewall a Windows Container (and the One That Works)

Five different Windows firewall approaches failed to stop a container from reaching my home network. The sixth, a different network mode for the container, finally worked.

10-Minute Read

Packet path of a Windows container on an HNS NAT network versus an L2Bridge network

ephemerd runs CI jobs from pull requests. One of the rules is that a job can reach the internet but not my LAN. On Linux that’s a few iptables rules in the FORWARD path, and I didn’t think about it much. On Windows I spent about a week finding out how many ways there are to write a firewall rule that Windows accepts, lists and never applies.

Recent Posts

Categories

About

Luther is a 20+ year software veteran specializing in backend APIs and architecting products. Employed at SUSE as the Team Lead for the Containers on Windows Team working on Rancher + Kubernetes