Post by David Parsley
It's a fairly common practice to configure services and provide secrets
with environment variables.
Passing secrets through env vars is marginally better than passing
them in via cmdline params, but still bad, bad, bad
practice. Seriously, don't do that.
Env vars suck for this. They are generally not considered secret, and
thus they leak everywhere, as programs generally don't assume them to
be. As one example: "systemctl show -p Environment …" will show to
unpriv users the env vars you pass to any service of your choice.
Moreover, env vars are by default inherited down the process tree, and
this means code that really shouldn't see them might end up seeing
them. The lifecycle of secrets needs to be tightly controlled, and as
limited as possible. Env vars are the opposite of that. They are
propagated agressively by default, even across security boundaries
(setuid/setgid!) and everything.
Seriously, don't do it that way. If you do it like that, then you are
building an insecure system.
There are other options available. The optimal way would be to stick
them in the kernel keyring. In the kernel keyring you get some
guarantees about lifecycle, access control, even paging that you
otherwise generally don't get. If the keyring doesn't work, then stick
them in a regular file in your $RUNTIME_DIRECTORY and make sure that
files has 0600 access mode or so. You can even set an env var to the
path of such a file if you really want to use the env vars for
something. But please, never stick passwords directly there.
Another option is to allocate a pipe(), write the password in there,
then pass over the reading side of the pipe to the process you
fork. It's not ideal since the fd would theoretically be propagated
further down the tree, but it does have nice properties in that you
can read the pw off the pipe only once, and then it's gone. It also
has the benefit over the file solution suggested above that it won't
leave artifacts in the fs by design.
Post by David Parsley
For instance, both Hubot (made by Github) and
Gopherbot (made by me) can get their Slack token from an environment
variable. In my case, github.com/lnxjedi/ansible-role-gopherbot stores the
Slack bot token with "Environtment=GOPHER_SLACK_TOKEN=xxx" in the systemd
unit file. I had hoped to keep this info to the robot user by marking the
unit file world-inaccessible. I was dismayed to see the log warning about
values being accessible via the API, though super glad that my unprivileged
user couldn't fetch it with a simple systemctl cat gopherbot. I know very
little about DBUS or any APIs for systemd, so wanted to ask - is there some
means by which a non-privileged user can access the values provided with
"Environment=..." lines? Can I disable this by disabling dbus-daemon on
Yes, the bus API is generally open for any clients. One way to access
it is "systemctl show -p Environment …", another is directly with busctl.
And no, you cannot reasonably disable this. It's the wrong place. It's
a swiss cheese.
Seriously, the right approach is not to pass these secrets in the env
vars in the first place, not then to try to play hide and seek to make
them hopefully not leak.
Lennart Poettering, Red Hat