-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Pluggable authentication #9857
base: master
Are you sure you want to change the base?
Pluggable authentication #9857
Conversation
|
||
template<typename T> | ||
std::optional<T> readOptional(const StoreDirConfig & store, WorkerProto::ReadConn conn) | ||
{ | ||
auto tag = readNum<uint8_t>(conn.from); | ||
switch (tag) { | ||
case 0: | ||
return std::nullopt; | ||
case 1: | ||
return WorkerProto::Serialise<T>::read(store, conn); | ||
default: | ||
throw Error("Invalid optional tag from remote"); | ||
} | ||
} | ||
|
||
|
||
template<typename T> | ||
void writeOptional(const StoreDirConfig & store, WorkerProto::WriteConn conn, const std::optional<T> & x) | ||
{ | ||
if (!x.has_value()) { | ||
conn.to << uint8_t{0}; | ||
} else { | ||
conn.to << uint8_t{1}; | ||
WorkerProto::Serialise<T>::write(store, conn, *x); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you have the same code in serialize.hh
?
template<typename T> | ||
DECLARE_WORKER_SERIALISER(std::vector<T>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can make a partial template specialization like this for std::optional
, and then you won't need the std::optional<auth::AuthData>
instance. (Can also turn std::optional<TrustedFlag>
into one for just TrustedFlag
too.)
I used to have that but I removed it because I only had the special-cases instances at the time, but now we do have a use case for it.
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 #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 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 |
3487e19
to
703871a
Compare
601d98e
to
e1143f1
Compare
Nix can now read authentication data from ~/.local/share/nix/auth/*. The files in that directory have the format: protocol=https host=my-cache.example.org username=alice password=blabla Also, the Nix daemon can now call back to the client to get authentication data from the client (if it's trusted). This makes it possible to have the daemon substitute from an authenticated binary cache, with the authentication coming from the client. Issue NixOS#8635.
This is similar to 'git credential fill', i.e. it tries to fill an authentication request using the configured authentication sources. Mostly useful for testing.
Sorry I need to clarify on my previous comment:
but setting an s3 bucket as a substituter e.g.:
doesn't work as the nix daemon doesn't have access to my local environment variables or my 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 |
Forwarding can now be disabled entirely, enabled for trusted users, or enabled for all users.
Some status updates:
|
Some thoughts:
|
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 I comment motivated by the exchange in #10567. cc @roberth |
I'm sympathetic to that.
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. |
/* Set up authentication tunneling for builtin:fetchurl. FIXME: | ||
maybe we want to support this for arbitrary fixed-output | ||
derivations. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/* Set up authentication tunneling for builtin:fetchurl. FIXME: | |
maybe we want to support this for arbitrary fixed-output | |
derivations. */ | |
/* Set up authentication tunneling for builtin:fetchurl. | |
We generally do not want to pass credentials to arbitrary | |
fixed output derivations, because that makes it possible | |
for any dependency (however deep down) to steal them. */ |
We'd need to come up with a scheme to safely pass authorization (with letter z) to specific derivations in such a way that the user controls where their credentials go; perhaps through some sort of unforgeable token that's passed around explicitly in expressions? Something for another day.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OT: notably we can't store the unforgeable token in the drv itself, because that would expose it to be read with readFile foo.drvPath
.
This seems to be another instance where we should be tracking transient derivation metadata, just like we should have been doing with meta.timeout
or perhaps the licenses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed the docs only
[the same protocol as Git's credential | ||
helpers](https://git-scm.com/docs/gitcredentials#_custom_helpers), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[the same protocol as Git's credential | |
helpers](https://git-scm.com/docs/gitcredentials#_custom_helpers), | |
a protocol compatible with that of [Git's credential | |
helper programs](https://git-scm.com/docs/gitcredentials#_custom_helpers), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are these strings resolved?
Any other differences? This deserves its own page in the protocols sections of the manual.
a username and password for `cache.example.org`: | ||
|
||
``` | ||
# cat <<EOF > ~/.local/share/nix/auth/my-cache |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the relevance of my-cache
?
username=alice | ||
password=foobar | ||
EOF | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have a page in the config files section of the manual.
* `false`: Forwarding is disabled. | ||
* `trusted-users`: Forwarding is only supported for [trusted users](#conf-trusted-users). | ||
* `all-users`: Forwarding is supported for all users. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens to the daemon's own config? Is it ignored completely or merged? If so, how?
Thought:
It's unfortunate that we can't seem to just enable this for all users by default. (Or am I wrong to think I shouldn't make myself trusted user? Cachix has certainly normalized it...)
Receiving auth data seems pretty harmless on the face of it, but I suppose attack vectors may involve:
- Implementation errors in our protocol clients
- Bugs in remotes that cause them to misbehave while we trust them
- Remotes that behave differently depending on user name
I wonder if there's any mitigations we could apply? Perhaps a scenario like this:
- a cache
https://foo
exists https://foo/nix-cache-info
has a header that declares that its happy-path behavior is not auth-dependent (e.g. doesn't select a cache based on username)nix daemon
hastrusted-substituters = https://foo
- trusted substituter + header allow credential forwarding
Also the retrieval of content addressed paths seems pretty safe, because misbehaving remotes are not much of an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Receiving auth data seems pretty harmless on the face of it
Yeah, this is not harmless because we do indeed have substituters that return different data depending on the auth data, e.g. cache.flakehub.com (which is based on Attic) does that.
https://foo/nix-cache-info has a header that declares that its happy-path behavior is not auth-dependent (e.g. doesn't select a cache based on username)
Yeah that sounds like a good idea. Probably for a future PR though. There's a bit of a chicken/egg problem with that since to fetch nix-cache-info
, we may already need to know the auth data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens to the daemon's own config?
The daemon will try its own authentication sources and those forwarded by the client.
Whether to support pluggable authentication via external credential helpers. | ||
)", | ||
.trackingUrl = "https://github.com/NixOS/nix/pull/9857", | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be helpful to refer to the options here.
Currently, the worker protocol has a version number that we increment whenever we change something in the protocol. However, this can cause a collision between Nix PRs / forks that make protocol changes (e.g. PR NixOS#9857 increments the version, which could collide with another PR). So instead, the client and daemon now exchange a set of protocol features (such as `auth-forwarding`). They will use the intersection of the sets of features, i.e. the features they both support. Note that protocol features are completely distinct from `ExperimentalFeature`s.
Currently, the worker protocol has a version number that we increment whenever we change something in the protocol. However, this can cause a collision between Nix PRs / forks that make protocol changes (e.g. PR NixOS#9857 increments the version, which could collide with another PR). So instead, the client and daemon now exchange a set of protocol features (such as `auth-forwarding`). They will use the intersection of the sets of features, i.e. the features they both support. Note that protocol features are completely distinct from `ExperimentalFeature`s.
Currently, the worker protocol has a version number that we increment whenever we change something in the protocol. However, this can cause a collision between Nix PRs / forks that make protocol changes (e.g. PR NixOS#9857 increments the version, which could collide with another PR). So instead, the client and daemon now exchange a set of protocol features (such as `auth-forwarding`). They will use the intersection of the sets of features, i.e. the features they both support. Note that protocol features are completely distinct from `ExperimentalFeature`s.
Motivation
This PR makes several improvements to HTTP authentication in Nix:
auth-sources
setting. For instance, addingextra-auth-sources = git-credential-libsecret
allows Nix to obtain usernames/passwords from the KDE/Gnome keyrings.builtin:nix
toauth-sources
), which reads usernames/passwords from files in~/.local/share/nix/auth
. The advantage over thenetrc
authentication source (now known asbuiltin:netrc
) is that it has a file per secret, which makes it easier for scripts/installers to add authentication data.$SSH_ASKPASS
) and store this in the configuredauth-sources
for future use.--substituters http://my-cache
, the daemon will trigger the client to ask the user for the username/password for that cache.builtin:fetchurl
requests authentication from the parent (outside of the sandbox), so e.g.Context
Priorities and Process
Add 👍 to pull requests you find important.
The Nix maintainer team uses a GitHub project board to schedule and track reviews.