Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NixOS modules: Secrets provided in arguments are exposed to unprivileged users #156400

Open
2 of 5 tasks
TLATER opened this issue Jan 24, 2022 · 11 comments · Fixed by #181197
Open
2 of 5 tasks

NixOS modules: Secrets provided in arguments are exposed to unprivileged users #156400

TLATER opened this issue Jan 24, 2022 · 11 comments · Fixed by #181197

Comments

@TLATER
Copy link
Contributor

TLATER commented Jan 24, 2022

Describe the bug

When specifying secrets via arguments, they will be exposed via /proc/<pid>/cmdline - this is also possible if passed indirectly via an environment variable, since the shell resolves the value. This can be trivially demonstrated like so:

tlater ~ $ export SAMPLE_SECRET=20
tlater ~ $ sleep $SAMPLE_SECRET & ps -u | grep sleep
[1] 8181
tlater      8181  0.0  0.0 227256  1336 pts/2    SN   21:54   0:00 sleep 20

Some modules seem to use shell scripts to feed values provided via passwordFile or similar to their applications at runtime (with constructs like SECRETKEY="$(head -n1 ${secretKey})"). Often, these are then fed to sed to replace values in template files, or passed to --password arguments (which are often, but not always, arguably an upstream problem).

I'm not sure how widespread exactly this is, some way to search for occurrences of this would be nice. It's difficult because not all secrets are named passwordFile and there would be a lot of false positives if we simply pinged all modules that have such options. It's also hard to tell when this needs an upstream fix, but at least I'd argue passwordFile args should not be created for modules that cannot keep the secrets from being world-readable outside the nix store either.

I'd like to point out replace-secret, which essentially exists to do this without leaking secrets.

For now, I believe at least these modules to do such things in 21.11 - I did not check if upstream have a better way of supplying secrets where applicable:

There are also some modules that use bash heredocs and printf (notably, nixos/modules/config/ldap.nix), which I believe are safe, but could probably do with some comments to point this out.

I guess it will be fairly common though, since the requirement of passing secrets via files is somewhat more pronounced in NixOS, not always considered by upstreams, and leaking secrets via ps is an easy thing to overlook. Maybe we should consider some kind of support in lib for supplying secrets to applications to avoid things like this (and get more use out of systemd, which has some nice secret-passing functionality).

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/how-should-one-report-small-vulnerabilities-in-individual-modules/17279/5

@TLATER
Copy link
Contributor Author

TLATER commented Jan 24, 2022

Ah, sorry @jtojnar, I must have misread that, infinoted is clearly using a heredoc as well.

@atopuzov
Copy link
Contributor

Hi @TLATER i took a look at ttyd [1] and it does not provide a way of using a file for the password.

[1] https://github.com/tsl0922/ttyd

@nixinator
Copy link
Member

Yeah, it certainly not the greatest idea to hot wire processes with secrets injected into Environment Variables.

However, a work around would be to limit normal users from actually seeing other users processes at all.

https://www.cyberciti.biz/faq/linux-hide-processes-from-other-users/

Unfortunately You'll find many upstream projects think it's okay to pass sensitive stuff on the command line.

@RaitoBezarius
Copy link
Member

Another way to perform this operation securely is to use LoadCredential from systemd, see systemd/systemd#15778

@TLATER
Copy link
Contributor Author

TLATER commented Jan 25, 2022

@nixinator yes, those are good points, and partially why I hesitated raising this. Still, most users run their systems without any hardening (hard to know what might be a good idea), and a lot of projects are sympathetic to this problem.

I'm more concerned that NixOS modules are lulling users into a false sense of security through the existence of passwordFile options, which are explicitly claimed not to make passwords world-readable via the nix store; It's easy to miss that this doesn't necessarily apply to everything. We're also actively breaking these protections for projects that do have them, like in the gitea case.

The latter should clearly be fixed, and the former probably warrants further discussion. At least this problem should be more common knowledge in the community than it currently appears to be.

@Pacman99
Copy link
Contributor

Pacman99 commented Jan 31, 2022

Is this an issue when using envusbst too? Like would the following be insecure:

export SECRET=pass
envsubst -i /nix/store/... -o /var/lib/app/config.yaml

Does the act of just setting the secret variable reveal what it is? Or is only revealed when used in command line?

@TLATER
Copy link
Contributor Author

TLATER commented Feb 1, 2022

Does the act of just setting the secret variable reveal what it is?

No, since export doesn't launch a separate process - variables are only leaked if they are passed as arguments to an actual application executed by the kernel, not when bash modifies its own model of what the environment contains. This is also why printf and heredocs are usually fine - these are all built-in bash functionality that look a little suspect, but they do not actually launch separate processes, and therefore do not reveal their arguments via the kernel.

Note that this isn't true for all shells. busybox' ash for example implements printf as a separate binary. export specifically can probably not be implemented as an external binary though, so that one should always be safe.

This does also mean it's not a shell-exclusive issue though, secrets are also revealed when passed as variables to os.execv or subprocess.call in python, or similar library functions in other languages.

For reference, when exporting secrets in an interactive shell (i.e., by typing it manually into a terminal), those are logged to the user's bash history, which might also not be intended. This isn't an issue for the non-interactive shells this issue is about, though.

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/agenix-encrypted-plaintext-passwords-and-builtins-readfile/18425/3

rnhmjoj pushed a commit that referenced this issue Apr 15, 2022
SuperSandro2000 pushed a commit that referenced this issue Jul 12, 2022
...by using `replace-secret` instead of `sed` when injecting the
password into the ddclient config file. (Verified with `execsnoop`.)

Ref #156400.
github-actions bot pushed a commit that referenced this issue Jul 12, 2022
...by using `replace-secret` instead of `sed` when injecting the
password into the ddclient config file. (Verified with `execsnoop`.)

Ref #156400.

(cherry picked from commit e0f2f7f)
@bjornfor
Copy link
Contributor

Closed by accident, reopening now.

@bjornfor bjornfor reopened this Jul 12, 2022
bjornfor added a commit that referenced this issue Jul 12, 2022
...by using `replace-secret` instead of `sed` when injecting the
password into the ddclient config file. (Verified with `execsnoop`.)

Ref #156400.

(cherry picked from commit e0f2f7f)
bjornfor added a commit to bjornfor/nixpkgs that referenced this issue Jul 13, 2022
The current authentication code is broken against newer jenkins:

  jenkins-job-builder-start[1257]: Asking Jenkins to reload config
  jenkins-start[789]: 2022-07-12 14:34:31.148+0000 [id=17]        WARNING hudson.security.csrf.CrumbFilter#doFilter: Found invalid crumb 31e96e52938b51f099a61df9505a4427cb9dca7e35192216755659032a4151df. If you are calling this URL with a script, please use the API Token instead. More information: https://www.jenkins.io/redirect/crumb-cannot-be-used-for-script
  jenkins-start[789]: 2022-07-12 14:34:31.160+0000 [id=17]        WARNING hudson.security.csrf.CrumbFilter#doFilter: No valid crumb was included in request for /reload by admin. Returning 403.
  jenkins-job-builder-start[1357]: curl: (22) The requested URL returned error: 403

Fix it by using `jenkins-cli` instead of messing with `curl`.

This rewrite also prevents leaking the password in process listings. (We
could probably do it without `replace-secret`, assuming `printf` is a
shell built-in, but this implementation should be safe even with shells
not having a built-in `printf`.)

Ref NixOS#156400.
bjornfor added a commit that referenced this issue Jul 16, 2022
The current authentication code is broken against newer jenkins:

  jenkins-job-builder-start[1257]: Asking Jenkins to reload config
  jenkins-start[789]: 2022-07-12 14:34:31.148+0000 [id=17]        WARNING hudson.security.csrf.CrumbFilter#doFilter: Found invalid crumb 31e96e52938b51f099a61df9505a4427cb9dca7e35192216755659032a4151df. If you are calling this URL with a script, please use the API Token instead. More information: https://www.jenkins.io/redirect/crumb-cannot-be-used-for-script
  jenkins-start[789]: 2022-07-12 14:34:31.160+0000 [id=17]        WARNING hudson.security.csrf.CrumbFilter#doFilter: No valid crumb was included in request for /reload by admin. Returning 403.
  jenkins-job-builder-start[1357]: curl: (22) The requested URL returned error: 403

Fix it by using `jenkins-cli` instead of messing with `curl`.

This rewrite also prevents leaking the password in process listings. (We
could probably do it without `replace-secret`, assuming `printf` is a
shell built-in, but this implementation should be safe even with shells
not having a built-in `printf`.)

Ref #156400.
github-actions bot pushed a commit that referenced this issue Jul 16, 2022
The current authentication code is broken against newer jenkins:

  jenkins-job-builder-start[1257]: Asking Jenkins to reload config
  jenkins-start[789]: 2022-07-12 14:34:31.148+0000 [id=17]        WARNING hudson.security.csrf.CrumbFilter#doFilter: Found invalid crumb 31e96e52938b51f099a61df9505a4427cb9dca7e35192216755659032a4151df. If you are calling this URL with a script, please use the API Token instead. More information: https://www.jenkins.io/redirect/crumb-cannot-be-used-for-script
  jenkins-start[789]: 2022-07-12 14:34:31.160+0000 [id=17]        WARNING hudson.security.csrf.CrumbFilter#doFilter: No valid crumb was included in request for /reload by admin. Returning 403.
  jenkins-job-builder-start[1357]: curl: (22) The requested URL returned error: 403

Fix it by using `jenkins-cli` instead of messing with `curl`.

This rewrite also prevents leaking the password in process listings. (We
could probably do it without `replace-secret`, assuming `printf` is a
shell built-in, but this implementation should be safe even with shells
not having a built-in `printf`.)

Ref #156400.

(cherry picked from commit 7a01213)
bjornfor added a commit that referenced this issue Jul 16, 2022
The current authentication code is broken against newer jenkins:

  jenkins-job-builder-start[1257]: Asking Jenkins to reload config
  jenkins-start[789]: 2022-07-12 14:34:31.148+0000 [id=17]        WARNING hudson.security.csrf.CrumbFilter#doFilter: Found invalid crumb 31e96e52938b51f099a61df9505a4427cb9dca7e35192216755659032a4151df. If you are calling this URL with a script, please use the API Token instead. More information: https://www.jenkins.io/redirect/crumb-cannot-be-used-for-script
  jenkins-start[789]: 2022-07-12 14:34:31.160+0000 [id=17]        WARNING hudson.security.csrf.CrumbFilter#doFilter: No valid crumb was included in request for /reload by admin. Returning 403.
  jenkins-job-builder-start[1357]: curl: (22) The requested URL returned error: 403

Fix it by using `jenkins-cli` instead of messing with `curl`.

This rewrite also prevents leaking the password in process listings. (We
could probably do it without `replace-secret`, assuming `printf` is a
shell built-in, but this implementation should be safe even with shells
not having a built-in `printf`.)

Ref #156400.

(cherry picked from commit 7a01213)
@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/set-password-for-a-postgresql-user-from-a-file-agenix/41377/5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants