The ACL model behind shared/.env, why a box-wide
/etc/environment was rejected, and why there's no dotenv-rails
dependency anywhere in the stack.
Most "add secrets to the server" setups pick one of two shapes: a file every deploy account can
read (convenient, but now every developer/deployer on the box has standing read access to
DATABASE_URL and SECRET_KEY_BASE), or a box-wide
/etc/environment (worse — that file loads into every login shell and PAM
session on the host, including admins', so any sudoers rule broad enough to let a developer edit
it is a realistic path to tampering with root's own environment on next login). Bosun does
neither.
/var/www/<app>/shared/.env is created root:root, mode
600, plus exactly one thing on top: a named-user ACL read grant for
webapp-svc — the same unprivileged, no-login system account Puma and Sidekiq already
run as. Not the webapps group every developer/deployer/admin account belongs to.
That's deliberate: a developer or deployer account being compromised (or just making a mistake)
has zero standing read access to this file, even though they can freely clone,
bundle install, and symlink everything else in the app's tree — those operations
genuinely don't need the secrets, so they don't get them.
EnvironmentFile=-shared/.env, read by root before the process drops privileges to
webapp-svc, so the file's tight permissions never block the unit itself.rails console, rake,
migrations) — bosun run elevates to webapp-svc via a narrowly-scoped
sudoers grant (sudo -u webapp-svc, exact path only) instead of ever letting the
calling developer/admin account read the file as themselves.ENV var some
app's production.rb reads eagerly) also runs elevated to webapp-svc,
for that step only — the deploying account (deployer/admin) still never gets standing access.dotenv-rails?
A plain .env file does nothing on its own without something to read it — the usual
answer is a Gemfile dependency like dotenv-rails. Bosun deliberately doesn't require
one: that's a gem every single app on the box would have to carry just to support this host's
deploy tooling. Instead, loading is entirely the host's job — systemd's
EnvironmentFile= directive and a handful of small wrapper scripts
(shared/bin/{puma,sidekiq,run}) that source the file before
exec'ing the real command. An app's Gemfile stays exactly as it would be deployed
anywhere else.
| Scenario | Group-readable .env | Box-wide /etc/environment | Bosun's ACL model |
|---|---|---|---|
| Developer account compromised | Secrets exposed | Secrets exposed | No standing access |
| Deployer account compromised | Secrets exposed | Secrets exposed | No standing access |
| Risk to root's own shell environment | None | Realistic (shared file, every login) | None |
| Extra Gemfile dependency required | Usually (dotenv-rails) | No | No |