Pluggable authentication
Motivation
This PR makes several improvements to HTTP authentication in Nix:
- It allows the use of Git credential helpers to obtain authentication data (issue #8635). This is configured by the new
auth-sourcessetting. For instance, addingextra-auth-sources = git-credential-libsecretallows Nix to obtain usernames/passwords from the KDE/Gnome keyrings. - It adds another builtin authentication source (enabled by adding
builtin:nixtoauth-sources), which reads usernames/passwords from files in~/.local/share/nix/auth. The advantage over thenetrcauthentication source (now known asbuiltin:netrc) is that it has a file per secret, which makes it easier for scripts/installers to add authentication data. - The client will now interactively ask the user for a username/password (via
$SSH_ASKPASS) and store this in the configuredauth-sourcesfor future use. - The Nix daemon can now request authentication data from the client (for trusted users only). E.g. if the user passes
--substituters http://my-cache, the daemon will trigger the client to ask the user for the username/password for that cache. - Similarly,
builtin:fetchurlrequests authentication from the parent (outside of the sandbox), so e.g.
now works.nix build --expr 'import <nix/fetchurl.nix> { url = https://httpbin.org/basic-auth/foo/bar; hash = "sha256-zRy0rqgpwiXQg6mBNsKUqDHHNkW/Dk+3/QHdUlrBURg="; }'
Context
Priorities and Process
Add :+1: to pull requests you find important.
The Nix maintainer team uses a GitHub project board to schedule and track reviews.
Separate from the low level protocol minutiae, I think I've considered solving the same problem in a different way, which is having a user-specific remote builder for fixed output derivations. See https://github.com/NixOS/nix/issues/9344
Client-side building would be great but is a much bigger project (and probably wouldn't work on platforms like macOS).
I don't think it's that big of a project. It's just a client side build + use daemon to add to store. (And the current registerValidPaths is awfully close to that.)
I think for fixed-output derivations without references it should work on macOS just fine. Since there are no references, there is no need to do fake roots / make $out appear in the store dir.
Triaged during the Nix maintainers meeting on 2024-02-16. To discuss further
- Needs more docs, probably also more tests
- Requires careful review
To discuss
This pull request has been mentioned on NixOS Discourse. There might be relevant details there:
https://discourse.nixos.org/t/2024-02-16-nix-team-meeting-minutes-124/39870/1
This looks great! I have one question:
The Nix daemon can now request authentication data from the client (for trusted users only). E.g. if the user passes --substituters http://my-cache, the daemon will trigger the client to ask the user for the username/password for that cache.
Could we also do this for the s3:// substituter? It is super unergonomic that when e.g. you want to push to s3 bucket in CI that the AWS credentials need to be available to the nix daemon instead of the caller. e.g. this doesn't work if you have a nix-daemon in CI:
steps:
- uses: DeterminateSystems/nix-installer-action@main
# this step sets some AWS_ env vars
- uses: aws-actions/configure-aws-credentials@main
with:
role-arn: my-role
# This doesn't work as nix-daemon cant access the env vars
- run: nix copy --to s3://my-bucket
Instead you need to massage the credentials into a /root/.aws/credentials file which is awkard
Team meeting notes:
- Needs more documentation.
- Maybe the extra Unix domain socket can be replaced with a new STDERR_* message to have the daemon request auth from the client. Then it would work over ssh-ng as well.
- Should be an experimental feature.
- Currently auth tunneling is only supported for trusted users, but it might be okay to support it for all users. (Though we have to be careful with e.g. Attic since we don't want the client to override the user name used to connect to Attic, since then the server will return a different (possibly untrused) slice of the binary cache.)
- Maybe the client should ask the user to approve sending credentials to the daemon, so that e.g. a remote SSH daemon cannot exfiltrate arbitrary secrets from the client.
Sorry I need to clarify on my previous comment:
nix copy --to s3:// works fine as it doesn't involve the nix daemon
but setting an s3 bucket as a substituter e.g.:
nix build --extra-substituters s3://my-bucket
doesn't work as the nix daemon doesn't have access to my local environment variables or my $HOME/.aws folder
So it would be cool if we can use the solution for the https substituter for the s3 substituter as well?
Edit:
I think it's not that easy actually. As all the credential logic is in the AWS SDK itself... So maybe this is not feasible
This pull request has been mentioned on NixOS Discourse. There might be relevant details there:
https://discourse.nixos.org/t/2024-02-19-nix-team-meeting-minutes-126/40308/1
Some status updates:
- There are now two experimental features:
auth-forwarding(whether to support forwarding auth from the client to the daemon) andpluggable-auth(whether to support credential helpers). - There is a setting
store-auththat controls whether to attempt to store interactively prompted auth data into one of the auth sources. - The
~/.local/share/nix/authauth source now supports storing auth data. - There is a setting
auth-forwardingthat determines what users are allowed to forward auth to the daemon:false(nobody),trusted-usersorall-users. - More tests.
Some thoughts:
- From what I can tell, this only does plain http auth, but most services these days authenticate through headers. GitHub (
Authorization), GitLab (Private-Token/Job-Token), etc. So ideally there was a way to configure the authentication method as well. But this can be added later on, just worth keeping this in mind. - I still think client-side fetching is the preferred long-term solution as it solves a lot of security concerns of passing around credentials. Especially when considering remote builds. If passing credentials to the daemon gets stabilized, it might end up having to support both methods. So worth being careful with how this is exposed. I know this is behind an experimental flag, but given how important this feature is, I expect this will get adopted by a lot of people very soon. Edit: I guess
auth-forwardingandpluggable-authare separate features so they can be stabilized separately.
IMO this goes in the wrong direction. Introduces lots of complexity to the Nix codebase. Instead, I suggest working on making fetchers as a whole pluggable. The complexity around builtins.fetchGit should be reduced, and it be used as a "bootstrap fetcher" to fetch fetchers. The fetched fetchers can then do more fancy things such as asking for credentials.
I comment motivated by the exchange in #10567.
cc @roberth
Introduces lots of complexity to the Nix codebase
I'm sympathetic to that.
"bootstrap fetcher" to fetch fetchers
This has security implications; any Nixpkgs package (or otherwise) could then steal credentials, by doing a builtin fetch with a malicious fetching plugin. The beauty of fixed-output derivations is that they sandbox the fetching logic, preventing this. We should take great care to only un-sandbox or pass credentials to fetchers that are trustworthy. That doesn't mean that opening a devshell on an unknown repo is safe (it's not); it's a defense in depth measure.