How I Put Permission Boundaries Around Cross-Server Commands and File Serving
Cross-server commands without root keys, private files without chmod 777. Two config files did it. This is the story of how a Tacavar infrastructure problem became a lesson in least-privilege engineering — and how you can copy the pattern without reinventing your stack.
The Two Pain Points: Remote Commands and Private Files
Every operator eventually hits the same wall. You have two or more servers that need to talk to each other, but you refuse to give one of them the keys to the kingdom. At Tacavar, the immediate pain came from a simple workflow: a Bailian droplet needed to dispatch jobs to the primary droplet. Trigger a deploy. Post an Instagram reel. Write a video brief. Nothing crazy, but each of those actions required running commands on a machine that the Bailian server did not own.
Meanwhile, a separate problem was brewing around file serving. Caddy — running as a low-privilege user (uid 995) — needed to serve video files from /home/tacavar/Desktop/. That directory sat behind a 750 permission boundary. Caddy could not traverse the parent directories, and symlinks didn't help because Caddy walks the full path and hits the permissions wall before ever reaching the link target.
Two problems, one theme: how do you give a process exactly what it needs without opening up the entire system?
Why SSH Keys and chmod Are the Wrong First Move
The obvious answer for cross-server commands is to share SSH keys. Give Bailian a root key to the primary, and you can run anything. That's also precisely why it's wrong. A compromised Bailian droplet becomes a root shell on your primary server. Every command, every file, every secret is instantly exposed. You're not building automation; you're building a blast radius.
The obvious answer for file serving is to chmod 777 the directory or run Caddy as root. Both are equally terrible. Chmod 777 makes your private files world-readable. Running a web server as root means any exploit in Caddy or the PHP/FastCGI layer becomes a full host compromise. Some people copy files to a public directory — that works, but you're now managing sync jobs, duplicate storage, and stale content.
There's a better way. Both problems were solved by introducing explicit permission boundaries at the command level and the file-system level.
Building a Whitelist Dispatcher With an Audit Log
The primary droplet needed to accept commands from Bailian without giving away root. The pattern I landed on was a whitelist command dispatcher — a shell script called deploy_gate.sh that acts as a controlled entry point.
Bailian connects to the primary using a dedicated deploy key. That key does not grant a shell. It does not grant root. It is restricted to executing exactly one command: deploy_gate.sh. Inside that script, the allowed operations are enumerated by name — not by raw bash. Bailian sends a command name like instagram_post or domain_pending; the script validates that the name is in its whitelist, then runs the associated action under sudo. Anything not explicitly whitelisted is rejected.
That's the key: the dispatcher accepts command names, not arbitrary strings. There is no path to inject a ; rm -rf / because the input is never interpreted as bash. New commands are added explicitly to the whitelist over time — pip_install_* for specific package updates, trigger_deploy for release pushes, and so on. Every single call is written to an audit log with timestamp, calling host, and target command.
This is what a real cross-server SSH alternative looks like. Instead of handing out root keys, you hand out a restricted vocabulary. If Bailian is ever compromised, the attacker can only invoke those few whitelisted commands — and every invocation is already logged for the inevitable postmortem. The audit log becomes your devops record: you can see exactly which server triggered what, when, and how often.
The whitelist itself doubles as documentation. Anyone on the team can read the dispatcher and see what remote operations are allowed. No hidden permissions, no implicit trust.
Using a Bind Mount to Expose a Private Directory
Now the Caddy file-serving problem. The files live in /home/tacavar/Desktop/ — private, correctly permissioned at 750 for the tacavar user. Caddy runs as a separate, unprivileged user and cannot see inside. The solution wasn't a symlink, a chmod, or a copy job. It was a bind mount.
A 4-line entry in /etc/fstab makes /home/tacavar/Desktop appear at /var/www/videos. Caddy can read /var/www/videos because that path lives under the web root with appropriate permissions. Both paths point to the same inodes — no duplication, no moving files. When Hermes (or any other process) writes a new video to the Desktop directory, it is instantly available at hub.tacavar.com/videos/ with zero copy overhead.
Here's the magic: the permission check happens at the path level. Caddy only has access to the bind-mounted directory. It never gains access to /home/tacavar or any other sibling directory. The Linux permission boundaries remain intact — we've simply exported a narrow view of the filesystem for a specific process.
This is a caddy bind mount private directory pattern that solves the traversal problem elegantly. The parent directories stay sealed. No privileged container. No unsafe symlink tricks. Just an fstab entry that says: this private directory should also be visible at this public-safe path for this specific use case.
When to Use This Pattern and When to Avoid It
The whitelist dispatcher shines when you have a small, known set of remote operations that don't change frequently. Deploys, scheduled posts, domain checks — perfect candidates. The explicit whitelist keeps the attack surface small and the audit log meaningful.
Avoid it when you genuinely need arbitrary remote administration. If you need a full shell or the ability to run any command — debugging sessions, interactive package exploration — a whitelist dispatcher will just become a bottleneck. You'll be editing the script every five minutes, and the whitelist will grow until it's meaningless. Similarly, if your SSH key management is already mature (short-lived, restricted keys, central signing), a dispatcher may be redundant.
Bind mounts are almost always the right call for serving private directories through a web server — but they require you to think about the serving process's user separately from the file owner. If you try to bind-mount a home directory into a public root but leave the parent directories at 750, the bind mount still needs the web server user to have execute permission on every parent directory from the original path? No — bind mounts create a new path that bypasses the original parent traversal. That's exactly why they work. But they don't bypass ownership or mode on the mounted directory itself, so make sure the target user (Caddy) has read and execute on the mounted path.
Also avoid bind mounts if your storage backend is ephemeral or if you need copy-on-write semantics. Bind mounts are the same inodes, so a delete on the public side deletes the original file. Know the semantics before you put it in fstab.
The Permission-Boundary Mindset
The underlying lesson is about Linux permission boundaries. Too many operators treat permissions as a binary: either root or not, either 777 or not. That's lazy. The real work is identifying the narrowest path between two systems and building exactly that.
A whitelist command dispatcher creates a command-level boundary. An fstab bind mount creates a filesystem-level boundary. Both are tiny, documented, auditable changes that dramatically reduce blast radius. Both also scale: once you have the pattern, applying it to a new server takes minutes, not days.
At Tacavar, we stopped asking "how do I give this server access" and started asking "what is the minimum boundary that allows this specific action?". That shift changes everything. Your audit log becomes meaningful because every entry is a deliberate, approved action. Your file server stays private because you never chmod 777'd anything. Your cross-server automation runs without root keys because you built a dispatcher, not a backdoor.
The tools are trivial — a shell script, an fstab entry. The mindset is the real infrastructure. Start there.
Get a least-privilege automation review at tacavar.com/infrastructure-security.