-
-
Notifications
You must be signed in to change notification settings - Fork 160
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
[RFC 0059]: Systemd Service Secrets #59
Conversation
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 find this a great idea in total, and one which would bring NixOS a step closer to a pain-free devops' choice.
Side note: This approach does not prevent one service from reading the secrets of another, e.g. through privilege escalation or remote code execution. But I think that issue is beyond the scope of this RFC, and should better be handled with something like containers.
rfcs/0058-secrets-for-services.md
Outdated
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
I can't really think of a serious drawback right now, but hopefully the |
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 does the secret store deal with system/main config upgrades? When I uninstall an old service and install a new one that asks for a secret of the same name, does the new service get access to the old secret?
When I uninstall a lot of services, are old secrets garbage collected? Do downgrades always work seamlessly?
Can you outline the possible pitfalls regarding that topic in an extra section?
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.
In the current shape, no such management would happen. The secrets would just remain in the secrets store and if a config author decides to re-use them, then so be it. I can include that. I was initially thinking of the drawbacks of "sth that would be worse when adding this", but I guess it depends on the reference point. Definitely good points to add, even if they would remain unhandled.
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.
Well, it's maybe a strawman, but if you put a secret in /nix/store
, it's going to vanish upon garbage collection when I uninstall the package. If I don't care whether it's readable by root or by all users (e.g. inside a container), this is a drawback now because no such garbage collection would take place.
As in "something that would be worse when adding this", you could also say that people possibly expect that NixOS doesn't do any state management by default and that downgrading will bring their machine back into the exact state, but that's not the case anymore.
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.
you could also say that people possibly expect that NixOS doesn't do any state management by default
The difference being that before this RFC, I'd manually handle the secret state and remember to handle it. After all, I'm deleting the line saying service.foo.secretsFile = "/foo/bar";
, and when I delete the line, I'll delete the file. This is now not the case anymore. Instead, I need to remember how this semiautomatic secret handling works.
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.
if you put a secret in
/nix/store
It is exactly the purpose of this RFC not to have secrets in the nix store. This is possible without any problems and if they are referenced in the nixos config they would not be garbage collected as currently is the case. And this can still be achieved with this proposal as before.
If I don't care whether it's readable by root or by all users (e.g. inside a container)
Actually currently the nix store of (nixos) containers is shared with the host and all other containers, so that might be an issue nonetheless.
With regard to secret state handling, a lot of services have the possibility do use a passwordFile
, secretFile
, etc. but there is no abstraction for handing out permissions to secrets correctly, which is something like the proposal in this RFC is needed. Currently you have to not only drop the files on the machine yourself in the correct destination (same with this RFC, as long as you are using a fetcher that requires that), but also set the correct permissions for the respective service, which might be a chicken-and-egg problem of adding users for service by enabling it and the service requiring the secret.
rfcs/0058-secrets-for-services.md
Outdated
* When using a scope with multiple services, ideally only the secrets | ||
referenced in the services definition should be made available to each | ||
service. Right now all the secrets of the scope are blindly copied. | ||
* Transition of most critical services to use proposed approach |
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 it would be better to transition some non-critical services first, to gain some experience without breaking anything. If this works for 10+ regularly used non-critical e.g. webapps, then transition everything to this approach.
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 will fix the wording on that one. Parts of this section are maybe still a bit too "note to self"-like. What I intended was not for "inclusion into upstream" but more to hypothetically verify this is covering all the most important aspects needed for the most critical services. Your version is definitely better for an actual slow inclusion into upstream step.
Co-Authored-By: Manuel Bärenz <programming@manuelbaerenz.de>
Co-Authored-By: Manuel Bärenz <programming@manuelbaerenz.de>
The detailed design is quite long. I think it's better to clean up the Nix secrets story before moving downstream, and I suspect trying to do everything at once will leave us stuck. |
@Ericson2314 I think this proposal is orthogonal to the secrets-in-nix-store issue and that this is quite a nice way to move forward without getting in the way of that, as this RFC abstracts over where the secrets are fetched from. In the case of nix store secrets being implement one could easily add a fetcher to use that, but also keep the abstraction in place for remote secret management systems if for some reason you are required to use that. But obviously I'd be very happy if #5 or similar would be continued! |
@turion I'm not sure how you mean this? This as far as I can see is possible with this RFC as you can also use non-FS fetcher functions. And obviously always depends on how far you want to go with isolation. |
It was really just a comment that all secrets are still root-readable, and that this RFC is orthogonal to more isolation/virtualization. |
From reading this over it seems once a module uses this proposal there is no opt out for the user, correct? If say the drawback is that this adds more complexity to the system. When I was new to nix I always enjoyed being able to read a modules source and usually understand what is happening... translate things into what a regular distro does. This is an entirely new abstraction that has no equivalent in a regular distro and after reading through it twice I still feel very uncomfortable thinking about how I can explain/justify this mechanism to my co-workers who manage nixos servers with me, but have not dived into nixos enough to understand all the nuances yet. I foresee having to answer many questions to puzzled looks from people wondering why we can't continue to keep doing what we're doing, and wondering why, as non "nix people", they no longer understand module source code. I also wonder if a product similar to vault should be put in the alternatives section. I'm under the impression that while this may not be an entirely solved problem, there are existing solutions out there that can fit. |
I think that this rather allows us to consistently use something like vault or some otherwise created secrets folder etc. and not having to depend on every module implementing support for it. It does create a further abstraction but by that allows all modules to have a consistent way of passing secrets and not a different implementation for each of them. |
@globin I meant a single solution like vault could be chosen by the sysadmin for every secret on a machine and then module code could continue as is, unmodified. This provides greater flexibility, though currently at the cost of upfront work for the sysadmin. That being said I concede that the open issues regarding secrets in the nix store have been open for years without resolution, and no one has provided a nice to use module for something like vault that integrates nicely into nixos yet. I guess some sort of system/module that keeps this logic out of the module system would have been my desired outcome. I hope my opinion has provided at least minimal value as a differing point of view. |
The thing is that is not possibly currently. For all modules that only allow passing secrets by string (secret content) you'd have to modify it to pass in the secret differently as it would otherwise be added to the nix store nonetheless. This change should happen anyway even without this proposal and a lot of modules now at least allow passing by file, where one can choose to use a non-nix-store-file with more specific permissions or just a |
@globin I entirely agree that all modules need to replace any |
Do you mean for a module author or a user? If a module is switched to this, then no, without "more involved" fiddling about a user wouldn't be able to change that unless author has created multiple avenues. We could of-course add a "secret store" that is just the nix store as some sort of fallback for scenarios where people don't care and they can at least go and add their secrets to the nix store as before.
This is of course different perceptions, but for me NixOS is already quite an opinionated distribution that does a lot of things quite differently and brings a few abstractions that no other distro has. I wonder, why does this particular code/interface seem more complex than for instance all the module evaluation and merging logic in https://github.com/NixOS/nixpkgs/blob/master/lib/modules.nix? It is of course possible to simplify and document to help people understand it more easily, once we understand what points of confusion exist.
Well, yes. I thought of this as a given, that people can still go and use vault directly if they want/their software supports it. If all things we are dealing with would support sth like vault, then it would be great. But as @globin said, we'd still want the ability to plug in another service to manage secrets if need be, so some abstraction would likely emerge.
Could you please explain a little bit what exactly you mean? Something like this, but being just some library living separately from NixOS in general, or is this more about how such an approach would be integrated with the module system? For instance, instead of the modules using this interface directly, the user of a module calling some functions on it? Thanks, I will keep the points in mind when I do the next pass. |
rfcs/0058-secrets-for-services.md
Outdated
DynamicUser = true; | ||
}; | ||
}; | ||
}; |
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 seems unnecessarily verbose. I would do something like this:
systemd.services.foo = {
needsSecrets = [ "secret1" ];
...
};
and then our systemd module can generate whatever helper units are necessary.
It also avoids imperative-sounding function names like mkSecretsScope
which suggest that they allocate a unique new scope, but that's not the case, e.g. in
secretsScope1 = mkSecretsScope {
loadSecrets = [ "secret1" "secret2" ];
type = "folder";
};
secretsScope2 = mkSecretsScope {
loadSecrets = [ "secret1" "secret2" ];
type = "folder";
};
secretsScope1
and secretsScope2
are actually the same scope.
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 seems unnecessarily verbose. I would do something like this: [...]
I agree, it's more compact. Why I initially decided against it and for something that modifies the resulting structure separately was to reduce changes to the existing systemd modules. At the same time I thought it would be nice to be able to get the secrets as arguments, which I thought made it nicer to deal with in code. In the needsSecrets
case, if I understand it correctly, the user would be requesting a secret by its name, like in the scope creation, but then would have to possibly deal with paths (as strings) to pass the secret as an argument to the service, or load it into an env-var, because there would not be an automatic mapping mechanism anymore. Unless we pack it up into a shell variable or so.
It also avoids imperative-sounding function names like mkSecretsScope which suggest that they allocate a unique new scope, but that's not the case, e.g. ...
I do not necessarily perceive mk*
as an imperative terminology, but that depends on the reader. We have a lot of pure mk*
functions that construct some structure. But I'm not at all attached to the naming here, so we can change it to whatever seems more suitable if they're still around further down the road.
Edit: In fact, I'm not attached to most of the terms in the RFC, such as "sidecart" and similar. I merely picked them from what I thought would make it easily enough understood. So if there are suggestions to rename things, I'm up for it.
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.
@edolstra: Did this sufficiently address your remarks? I'm not super familiar with the inner workings of the process, so for now I just left those as discussion comments here, but I'm willing to incorporate some of the things pointed out into "alternatives" or the core section, if there is some consensus around that.
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.
So, getting back to this suggestion - if somebody could point me to the simplest way of implementing it with such an additional field on any systemd service without needing to change too many things in the guts of core modules I'm willing to try and adjust the poc to see how that works. One thing I do kinda like though is the ability to have some at least very basic checking of secrets used, which wouldn't work the same way with just strings.
rfcs/0058-secrets-for-services.md
Outdated
with secrets | ||
* "Side-car" service: A privileged systemd service running the fetcher | ||
function to retrieve the secret, and initially create the service | ||
namespace |
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's not clear to me why the helper unit is needed. Can't the keys be fetched using an ExecStartPre=+...
command?
More generally, instead of creating our own mechanism for passing keys to services, maybe the kernel keyring mechanism can be used for this? Units would call keyctl request/search/read/...
to fetch keys. These keys would either be preloaded into the keyring or produced on demand using the request-key
program.
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 briefly tried ExecStartPre
, but unfortunately it does not seem to run within the mount namespace, so it doesn't have a access to the PrivateTmp
we want. I am not sure if this is intentional or not.
Regarding kernel keyring mechanism - I agree, it might be a good default backend. The directory based thing was just the dumbest proof-of-concept case I came up with (given that similar approaches are used in nixops and krops/stockholm). Part of the intention is to have a somewhat agnostic interface.
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.
Kernel keyring mechanism sounds overkill to me. Its usecase is not to communicate files between userland processes, but between userland and kernel drivers. Files are a perfectly sufficient abstraction for passing secrets around in userland.
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 have mixed feelings about this.
Using files usually implies having to worry about ACLs and who's allowed to access them. We can cheat by mounting them in a private namespace, but it's still a bit cumbersome.
Sometimes you want to have "use once" properties and provide a new key on every read / issue tokens on access etc. We could cheat again by providing these key files by a fuse filesystem, but then it just gets more complicated.
The kernel keyring might be a good abstraction over all this, it's just not widely adapted currently and lacking real-world usage. Reading keyrings (7)
looks promising. In addition to thread/process/session, there's also an upcall
feature, bouncing back to userspace to request secrets which could be a request to whatever credentials provider is used.
I'd love to experiment with that a bit, or see some real-world examples. Anybody aware of these?
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.
kernel keyring is very much intended for userspace keys too. See the kerberos stuff that has been ported to use it for that, or systemd's cryptsetup.
I think the kernel keyring has deficiencies (upcalls, yuck! also no namespacing for containers, …), but it probably is the right approach in the long run.
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 briefly tried ExecStartPre, but unfortunately it does not seem to run within the mount namespace, so it doesn't have a access to the PrivateTmp we want. I am not sure if this is intentional or not.
I think that's what the !
prefix is for:
"!" - Similar to the "+" character discussed above this permits invoking command lines with elevated privileges. However, unlike "+" the "!" character exclusively alters the effect of User=, Group= and SupplementaryGroups=, i.e. only the stanzas that affect user and group credentials. Note that this setting may be combined with DynamicUser=, in which case a dynamic user/group pair is allocated before the command is invoked, but credential changing is left to the executed process itself.
@dhess did you post a summary of the meetup somewhere else? I can't find one |
@Lassulus Sorry, I haven't posted the meeting notes yet. I'll get to it soon. |
Sorry for the delay in posting the meeting minute notes, but here they are. Notes from 2020-05-06 callAttending: @d-goldin reported the following:
@dhess & @flokli agreed to try out the implementation before the next meeting.
Given @poettering's intention to work on related things in systemd, a vote was taken on this RFC, whether to proceed with the work, pause it for a bit until systemd work is better fleshed-out, or close the proposal. The votes were as follows (@d-goldin did not vote):
The vote was 4 : 1 : 0 (continue : pause : close), so we'll proceed with the RFC in parallel with the systemd work. Any further work should be done with the intention of integrating with systemd's primitives (if any) when the time comes. |
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.
Noting a couple typos.
@dhess What are next steps for this RFC. Another meeting? |
Thought I would link this here for future reference. Possibly of interest to people subscribed to this thread: https://cedwards.xyz/protecting-systemd-service-secrets/ |
We have now a pass based secret implementation and one based on sops. |
As things stand, I don't think this will progress much, so I propose to close this. |
Thanks for everyone's feedback, ideas and other contributions. I hope they'll be useful at the very least as a reference for other efforts. |
Systemd actually has this feature now: systemd/systemd#16568 |
Hi,
This is a first draft for an RFC about a possible approach to secrets management for systemd services.
Curious to hear your thoughts.
Rendered