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

Provide SELinux support for Nix #2670

Closed
wants to merge 8 commits into from

Conversation

outergod
Copy link

@outergod outergod commented Feb 8, 2019

Fixes #2374.

This changeset enables Nix to write SELinux file contexts on systems where SELinux is available. The resulting tarball also provides a modified installer, which attempts to install the aforementioned policy during installation and also advises the user on how to uninstall it.

The benefit of the change is that it re-enables Nix in multi-user (daemon) mode to work again on SELinux-enabled systems, such as the Fedora family of distributions, including RHEL and CentOS.
Additionally, it can serve as a foundation to enable SELinux hardening for NixOS.

Edit: After a bit of pondering, I decided it would be best to move the Nix policy for SELinux into Nix itself. The latest commit reflects that.

@outergod outergod force-pushed the selinux-support branch 2 times, most recently from b0bb50b to 4c69d37 Compare February 12, 2019 11:31
@etbe
Copy link

etbe commented Feb 22, 2019

I don't think that policy for /nix/store/* is generally a good idea. I've written a little patch for libselinux that adds wildcards to file_contexts.subs. Then you can run a command like semanage fcontext -a -e / "/nix/store/*" to make /nix/store/directory/ be equivalent to / when making labelling decisions. If we get that (or something similar) upstream then /nix/store on a Fedora, Debian or other SE Linux system should get the right labels without any extra effort. Also for a full NixOS SE Linux installation the only real labelling difference between NixOS and Fedora or Debian will be fixed.

@outergod
Copy link
Author

I don't think that policy for /nix/store/* is generally a good idea.

@etbe Why?

@etbe
Copy link

etbe commented Feb 22, 2019 via email

@outergod
Copy link
Author

Your implementation has one major drawback: Per-derivation file contexts. By essentially removing the store path from the labeling context, you are breaking with Nix philosophy completely. How do you want to support changed file contexts for updated versions of programs?

Another problem is that certain rules simply don't match. For instance,
/usr/sbin/httpd(\.worker)? -- system_u:object_r:httpd_exec_t:s0 (from Fedora)
doesn't match Nix' /bin/httpd. Just like with my implementation, it simply gets labeled as bin_t, which means it can be executed at all. However from there, policies can be added incrementally to derivations to harden them. In this instance, nixpkgs.apacheHttpd_2_4 would be enhanced to ship its own policy which gets built to list /nix/store/...-apache-httpd-2.4.37/bin/httpd.

@etbe
Copy link

etbe commented Feb 22, 2019 via email

@outergod
Copy link
Author

Your implementation has one major drawback: Per-derivation file contexts. By essentially removing the store path from the labeling context, you are breaking with Nix philosophy completely. How do you want to support changed file contexts for updated versions of programs?

Changes to file contexts are only specific to changes to program versions when the program binaries are renamed (like /bin/program2 to /bin/program3 when a new version comes out).

This is not always the case.

Other than that file context changes related to new programs are associated with new policy. Now it's theoretically possible to have modular policy and change versions of the modules to match the versions of the apps and daemons. But no-one has done this.

And no one had implemented a Linux distribution with per-build paths before, but along came NixOS. And Nix makes it particularly easy, even a native kind of feature to implement this. Which is why I think the SELinux solution for Nix is best kept in line with how Nix does things, in general.

What we generally do is to either upgrade the policy for everything or backport one section of policy if newer policy is needed to support a newer app or daemon.

Who is "we"?

And why the added complexity, when a solution with per-derivation file context support eliminates this problem altogether?

Another problem is that certain rules simply don't match. For instance, /usr/sbin/httpd(\.worker)? -- system_u:object_r:httpd_exec_t:s0 (from Fedora) doesn't match Nix' /bin/httpd. Just like with my implementation,

Every distribution has some minor variations in file names. We just include all the options in the policy, sometimes with ifdef() blocks around them, sometimes with all options available for everyone.

Again, why the added complexity, when my proposed solution eliminates this problem, as well?

it simply gets labeled as bin_t, which means it can be executed at all. However from there, policies can be added incrementally to derivations to harden them. In this instance, nixpkgs.apacheHttpd_2_4 would be enhanced to ship its own policy which gets built to list /nix/store/...-apache-httpd-2.4.37/bin/httpd.

What are you planning to do if there are multiple versions of Apache installed?

Every version of Apache httpd can ship with its own policy with full-path file contexts, why would that be a problem? I would like to ask you the same, how do you want to support multiple versions of httpd installed once an updated policy with different requirements for a certain version exists? Invent your own version suffix for the binary every single time?

I would really like to understand your motivation. What are the use cases which are not covered by this implementation vs. your proposed implementation? Or which of your use cases become more complex?

@etbe
Copy link

etbe commented Feb 23, 2019 via email

@outergod
Copy link
Author

Your implementation has one major drawback: Per-derivation file contexts.
By essentially removing the store path from the labeling context, you
are breaking with Nix philosophy completely. How do you want to support
changed file contexts for updated versions of programs?

Changes to file contexts are only specific to changes to program versions
when the program binaries are renamed (like /bin/program2 to
/bin/program3 when a new version comes out).

This is not always the case.

I can't think of an exception off the top of my head. Do you know of an example?

Alright, you got me there. I thought I remembered a case, but I'm too lazy to
dig it up, so it must be a rare exception anyway.

Other than that file context changes related to new programs are
associated with new policy. Now it's theoretically possible to have
modular policy and change versions of the modules to match the versions
of the apps and daemons. But no-one has done this.

And no one had implemented a Linux distribution with per-build paths before,
but along came NixOS. And Nix makes it particularly easy, even a native kind
of feature to implement this. Which is why I think the SELinux solution for
Nix is best kept in line with how Nix does things, in general.

Writing SE Linux policy either requires a lot of time and effort, working
closely with upstream to take advantage of work by other people, or being very
limited in scope. If you do things in a very different way you limit your
ability to work with upstream and will end up supporting tens of thousands of
lines of patches. That works for Fedora, but they have more resources to apply
to the problem.

That's a good point. So you are essentially saying that by being able to map
arbitrary roots which correspond to Nix store paths, Nix and NixOS both benefit
from all the existing policies, and small adjustments, like for instance
bin/httpd instead of sbin/httpd can be made upstream to support Nix?

What we generally do is to either upgrade the policy for everything or
backport one section of policy if newer policy is needed to support a
newer app or daemon.

Who is "we"?

The SE Linux developers.

Haha, I should've known to begin with. This puts all of this in a different light.

And why the added complexity, when a solution with per-derivation file context
support eliminates this problem altogether?

A dozen lines of code in a library patch isn't a lot of added complexity.

Well, you'll also have to convert (nearly?) all sbin/ rules to s?bin/, but
please correct me if I'm wrong.

it simply gets labeled as bin_t, which means it can be executed at
all
. However from there, policies can be added incrementally to
derivations to harden them. In this instance, nixpkgs.apacheHttpd_2_4
would be enhanced to ship its own policy which gets built to list
/nix/store/...-apache-httpd-2.4.37/bin/httpd.

What are you planning to do if there are multiple versions of Apache > installed?

Every version of Apache httpd can ship with its own policy with full-path
file contexts, why would that be a problem?

Every version of Apache could have it's own set of file-contexts which
wouldn't be that difficult.

If every version of Apache was to have it's own policy (IE the allow rules) and
they were to all be loaded at once then you would need to have a different type
name (like httpd_$1_t where $1 is the version number) for each one. Then when
you have interfaces that allow access to the different types you would need to
change them to access via attributes that refer to all of the types in question
(like have httpd_domain be an attribute referring to httpd_$1_t, httpd_$2_t,
etc). That would be big, ugly, and difficult to maintain.

If you were to have different versions of the Apache policy that aren't loaded
at the same time then you need to change the version of the policy when you
change the version. This would preclude running multiple versions of Apache at
the same time.

But this is a theoretical issue.

I was thinking of a completely different approach which feels more intuitive
and in line with how Nix does things: Instead of building modules per derivation
and installing them destructively (i.e. state-changing) using semodule, build
file_contexts based on the whole store for NixOS, or a single unversioned
installable module for non-NixOS, respectively.

I know this is completely different from how SELinux normally works, but then
again, this is Nix(OS).

What do you think about this approach?

[snip]

Everything will be more complex if you can't follow upstream policy
development.

Yes, that is true - your suggested approach removes a lot of burden from Nix and
NixOS. Just to make sure I understand correctly, semanage fcontext -a -e / "/nix/store/*" after your patch would suffice to be run once and never again,
as it applies to any possible derivation?

Aside all of that, I would appreciate if you could help review or assess the
direct code changes to Nix which relate to interfacing with libselinux and file
context changes, so the reviewer for Nix has an easier time, if that's not too
much to ask.

@etbe
Copy link

etbe commented Feb 24, 2019 via email

@mstone
Copy link

mstone commented Feb 24, 2019

Dear @e-user and @etbe, thank you both for sharing this PR and for the fine discussion emerging above — as an interested onlooker who has run selinux in enforcing mode voluntarily for several years on other systems, I am excited to see this line of development.

Next, regarding the more purely Nix-ish aspects of the problem: one long-standing challenge that the nix approach to daemon configuration has faced is where to put and how to control access to important cryptographic secrets — see #8 and it’s many followups for details.

At any rate: it has long seemed to me that selinux might be one of the more viable tools with which to address this problem.

Hence two questions:

@etbe — have upstream policy authors already established a set of conventions, practices, or idioms about how distro packages should label, name, or otherwise designate files containing secrets for which access should be tightly restricted to specific roles and types?

@e-user — have you had a chance to give thought to the selinux integration you’re developing might be used to help address this need / to how selinux policy authoring and loading might work at the level of nixexprs, derivations, builders, and storepaths?

Thanks!

Michael

@outergod
Copy link
Author

@etbe
I'll limit my answer to what should or can be discussed in public, this time.

About a year ago we put in the above subst entries which cover the Debian usrmerge package (see
below URL) and similar functionality in other distributions (I don't know of any other distribution than
Ubuntu which supports SE Linux and has usemerge).

The first distribution to merge usr was Fedora 17 in 2012, which is also referenced by Debian on the page you mentioned. Because of that, RHEL and CentOS also have /bin, /sbin, /lib and /lib64 symlinked to their respective counterparts in /usr. All of these distributions are SELinux-enforcing by default.

That still leaves you with a problem when you run Nix on Fedora or Debian, have something like
Apache in a bin directory when the base OS uses sbin. But that will be a less common combination of
corner cases.

Being able to run Nix-based daemons on an SELinux-enforcing system is my core motivation to fix this whole issue, so I wouldn't consider it a combination of corner cases. If we don't fix this, the whole endeavor becomes moot to me, unfortunately.

So either the upstream base policy changes to support bin/ for common services, or we have to find a different solution.

Also one thing to note is that the size of the kernel map of policy (in unpagable kernel memory) is
O(N^2) of the number of types. That's something to keep in mind when considering ideas that involve
significant increases in the number of types (EG httpd_$1_t for each version of Apache that is
installed).

You haven't made your suggestion clear enough for me to comment.

(also @mstone)

Having one type per version of Apache httpd was never the intention, but instead one set of file context mappings per version of httpd installed. Any version of httpd would depend on a versioned httpd_t type which it would reference in its mappings. If httpd_t never changes, there won't be more than that one version of httpd_t flying around - which shall be httpd_$SHA256SUM_t derived from its src, to keep things consistent. This is also in line with the rest of Nix, e.g. shared objects.

My current idea is this; there's a first httpd policy version which gets built from a regular old Nix derivation. It supplies a type httpd_$SHA256SUM_t and a set of relative paths.
Now, the httpd derivation is adapted to accept this new policy derivation as a build input, which results in the expansion of the relative paths and the custom type into the actual context mappings, e.g.
/nix/store/y7swsddnb5dlf8yzsci2phaw3icj7mcd-apache-httpd-2.4.37/bin/httpd system_u:object_r:httpd_config_1l9rnzfxa7h9y5v5n50q5qrgz8jx8q96_t:s0. All versions of httpd would share httpd_config_1l9rnzfxa7h9y5v5n50q5qrgz8jx8q96_t. However a changed version of the policy could be used by an updated version of the httpd derivation without activating any of the others.

This proposed solution could be combined with your patch to SELinux to cover all the special cases like involving sbin which are not covered by the patch.

@mstone I hope the above clarifies my proposed approach. Following the philosophy that working code is the best documentation, I could follow up with a prototype for httpd as outlined above.

@etbe
Copy link

etbe commented Feb 25, 2019 via email

@fishilico
Copy link

Hi, a small contribution to the discussion about the merging of /bin, /sbin, /usr/bin, etc. On Arch Linux, everything is merged into /usr/bin, which is why I contributed to patches to the SELinux reference policy to support this (for example TresysTechnology/refpolicy-contrib@05e32a5 and SELinuxProject/refpolicy@80fb19a).

By the way, I belong to the team of people who review/merge patches for SELinux libraries and userspace tools (https://github.com/SELinuxProject/selinux). It would be great if it was possible to run NixOS with SELinux, so if you need help in submitting patches upstream, please ask :)

@outergod
Copy link
Author

outergod commented Mar 6, 2019

Hey @fishilico and @etbe, sorry I had a few other things on my plate but can spend some time on this again. Also thanks @fishilico for offering help!

Related PRs which bump all SELinux related libs and tools to 2.8 NixOS/nixpkgs#56960, NixOS/nixpkgs#56961, NixOS/nixpkgs#56962, NixOS/nixpkgs#56963, NixOS/nixpkgs#56964 and NixOS/nixpkgs#56965 are up for review, now.

Could you guys please help by building them and verify all binaries work correctly? I don't know all the tools, so ideally, there would be some feedback to help the reviewer(s) understand everything works.
I had to adapt some flags passed to make which seemed to have changed, but the files seem to be in the right places, again.

@Mic92
Copy link
Member

Mic92 commented Mar 7, 2019

I think for a feature like this it would be good to write an RFC describing the design. This way you would also get the attention for the Nix developers. We also have now a process in place that ensures the RFC gets feedback.

@outergod
Copy link
Author

outergod commented Apr 6, 2019

After spending some time pondering NixOS/rfcs#41 (comment), I have come up with a new solution on how to implement RFC 0041 without breaking immutability: We simply query and apply file contexts during build times, and also write them to NAR archives and restore them from there, respectively.

We might have to make sure that filesystem relabeling skips all of /nix on non-NixOS systems so the store doesn't break, but then again, fixing labels during store recovery can also take care of the issue.

@outergod
Copy link
Author

outergod commented Apr 7, 2019

@etbe and @fishilico, are you still interested in supporting this endeavor?

At the current point of my research on how to integrate SELinux in Nix without breaking immutability, I came to realize we need a small but significant change in libselinux to resolve a circular dependency issue that is specific to Nix and SELinux: It must be possible to override SELINUXDIR and SELINUXCONFIG during runtime, e.g. via environment variables. At least that's the only way out of the conundrum I can think of right now. Would upstream accept such a change?

@mmahut
Copy link
Member

mmahut commented Jul 1, 2020

@e-user @etbe @fishilico any update on this issue? It would be really handy to have.

@Mic92
Copy link
Member

Mic92 commented Jul 1, 2020

@mmahut see this RFC: NixOS/rfcs#41 (comment)

@stale
Copy link

stale bot commented Feb 12, 2021

I marked this as stale due to inactivity. → More info

@stale stale bot added the stale label Feb 12, 2021
@hjones2199
Copy link
Member

still important to me

@stale stale bot removed the stale label Nov 17, 2021
@mstone
Copy link

mstone commented Nov 17, 2021

Weirdly still important to me too, and I even have some time to work on it, although I am daunted by some of the design challenges involved in making something that fits in nicely in the nix/nixpkgs ecosystem…

@dngray
Copy link

dngray commented Dec 30, 2021

From a new user perspective I really like NixOS's declarative and reproducible style, the fact that /etc configs are able to be declaratively defined makes it a more attractive option than Silverblue for me. However I also wanted get some experience writing SELinux policies. I really hope this is still added. I get the feeling that Silverblue might have more priority on sandboxing though it does seem like that's also desired on NixOS too 1, 2. 3. I haven't made up my mind yet, which option I will go with. I feel that a lot of the declarative features might be possible through docker-compose.yml and Dockerfile, something similar to this.

Forgive me for going off-topic.

@stale
Copy link

stale bot commented Jul 10, 2022

I marked this as stale due to inactivity. → More info

@stale stale bot added the stale label Jul 10, 2022
@half-duplex
Copy link

Still an issue

@fricklerhandwerk
Copy link
Contributor

Discussed in Nix team meeting 2022-12-09:

decision: close the pull request, but not the underlying issue #2670

@nixos-discourse
Copy link

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

https://discourse.nixos.org/t/2022-12-09-nix-team-meeting-minutes-15/23951/1

@infinisil infinisil mentioned this pull request Feb 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Nix multi-user installer fails on Fedora Rawhide due to SELinux denial