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
Conversation
b0bb50b
to
4c69d37
Compare
Remodeled SELinux to use static instance.
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. |
@etbe Why? |
On Friday, 22 February 2019 8:01:17 PM AEDT Alexander Kahl wrote:
> I don't think that policy for /nix/store/* is generally a good idea.
@etbe Why?
https://lore.kernel.org/selinux/7853167.K65cXu0y11@neuromancer/T/#u
I think that it's best to do something like this, get it accepted upstream,
and then have most things just work without any policy changes of note.
…--
My Main Blog http://etbe.coker.com.au/
My Documents Blog http://doc.coker.com.au/
|
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, |
On Friday, 22 February 2019 11:03:06 PM AEDT Alexander Kahl wrote:
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). 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. 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.
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.
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?
…--
My Main Blog http://etbe.coker.com.au/
My Documents Blog http://doc.coker.com.au/
|
This is not always the case.
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.
Who is "we"? And why the added complexity, when a solution with per-derivation file context support eliminates this problem altogether?
Again, why the added complexity, when my proposed solution eliminates this problem, as well?
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? |
On Saturday, 23 February 2019 5:04:29 AM AEDT Alexander Kahl wrote:
>> 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?
> 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.
> 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.
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.
>> 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 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?
Policy for daemons that have been around for a while doesn't change fast.
Apache now does pretty much the same thing that it did 10 years ago. Policy
changes that you are likely to need would be based on configuration changes.
Of SE Linux policy changes in the last few years by far the vast majority are
related to systemd, both it's own policy and how it changes interactions with
various daemons (including things like NSS for dynamic users). There is a
small amount of change related to kernel features, but that's mostly new
protocols that don't get much use anyway. Then the next area of changes is
related to decisions of SE Linux developers, for example separating "map" from
the other file access controls which involved kernel and policy changes.
When kernel changes result in daemon changes that require new policy it's
usually not things you want to bother disabling for older versions. If an
older daemon that doesn't look for the vm_overcommit status is given
permission to do so it doesn't do any harm.
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?
Everything will be more complex if you can't follow upstream policy
development.
…--
My Main Blog http://etbe.coker.com.au/
My Documents Blog http://doc.coker.com.au/
|
Alright, you got me there. I thought I remembered a case, but I'm too lazy to
That's a good point. So you are essentially saying that by being able to map
Haha, I should've known to begin with. This puts all of this in a different light.
Well, you'll also have to convert (nearly?) all
I was thinking of a completely different approach which feels more intuitive I know this is completely different from how SELinux normally works, but then What do you think about this approach? [snip]
Yes, that is true - your suggested approach removes a lot of burden from Nix and Aside all of that, I would appreciate if you could help review or assess the |
On Sunday, 24 February 2019 1:07:45 AM AEDT Alexander Kahl wrote:
> 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?
Absolutely.
/bin /usr/bin
/lib /usr/lib
/lib32 /usr/lib
/lib64 /usr/lib
/libx32 /usr/lib
/sbin /usr/sbin
I've been idly considering mapping sbin and bin directories to the same policy
anyway for unrelated reasons. 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). This not only solved
the usrmerge issue (which some people would have advocated "solving" by just
not doing it) but also the case of files moving between /usr and other
directories (a regular annoyance).
https://wiki.debian.org/UsrMerge
>> 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.
The simple solution for NixOS only is to use sed to replace sbin with bin and
put in a subst rule to make sbin and bin equal.
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.
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.
storage = base
sysadm = base
application = base
authlogin = base
fstools = base
init = base
libraries = base
locallogin = base
logging = base
miscfiles = base
modutils = base
mount = base
selinuxutil = base
sysnetwork = base
udev = base
userdomain = base
There is a base module (usually base.pp) which has the policy for init and
labels for the base files. The base module contains the above for Debian and a
fairly similar list for other distributions. While you could theoretically
remove some of them from base and version them separately that doesn't get
much testing and will probably require some work. But separating them
provides little benefit.
Building multiple versions of file_contexts is easy enough if you really need
to do it. With the restriction that it has to match the type names in the
policy, but that also isn't a big deal as you have typealias rules and types
don't change often anyway.
Changing modules takes quite a bit of CPU time and disk access and isn't
something you want to do often.
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).
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?
You haven't made your suggestion clear enough for me to comment.
> 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?
Yes.
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.
Sure. But I am just learning Nix so I can't comment much on Nix specific
parts.
Also it might be easier if we correspond directly about some of these things
instead of going through github. Feel free to email me directly or respond to
my linkedin invitation. ;)
…--
My Main Blog http://etbe.coker.com.au/
My Documents Blog http://doc.coker.com.au/
|
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 |
@etbe
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
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
(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 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 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. |
On Monday, 25 February 2019 9:19:27 PM AEDT Alexander Kahl wrote:
@etbe
I'll limit my answer to what should or can be discussed in public, this
time.
I'm mystified as to why this sort of design discussion shouldn't be discussed
fully in public. But you know how to contact me for a private discussion if
that's what you wish.
> 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.
http://oss.tresys.com/pipermail/refpolicy/2017-January/008976.html
Above is my patch for the usrmerge in SE Linux policy. You will note that
there are many deletions of /bin and /sbin entries without matching additions
of /usr/bin or /usr/sbin, this is probably due to Fedora policy going upstream
(I didn't bother tracking the history).
> 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.
That's a possibility.
> 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.
But .fc file changes are much less common than .te and .if file changes.
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.
From a quick scan of the policy, the Apache policy uses interfaces for clamav,
cobbler, cron, cvs, dbus, gitosis, gpg, init, kerberos, logging, mailman,
memcached, mta, mysql, nagios, nsco, openca, pcsd, postgresql, puppet, rpc/
nfs, shibboleth, smokeping, snmp, spamassassin, systemd, udev, userdomains,
yam, and zabbix. If any of those interfaces change then the Apache policy
will change. Basically any update of policy is going to change something that
Apache interfaces with.
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
You also need to add attributes named something like httpd_t_attribute and
then change the Apache interfaces to grant access to them instead of httpd_t.
Every Apache interface needs to grant access to an attribute instead of a
type.
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.
It might be a better if we could get the 2.8 versions of the SE Linux
utilities into NixOS and get semanage working first.
Getting SE Linux working reasonably well in a new distribution requires lots
of changes to a variety of packages and years of debate with people who
maintain those packages. Getting an early start on the process of filing bug
reports everywhere would be a good plan.
…--
My Main Blog http://etbe.coker.com.au/
My Documents Blog http://doc.coker.com.au/
|
Hi, a small contribution to the discussion about the merging of 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 :) |
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 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. |
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 |
@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 |
@e-user @etbe @fishilico any update on this issue? It would be really handy to have. |
@mmahut see this RFC: NixOS/rfcs#41 (comment) |
I marked this as stale due to inactivity. → More info |
still important to me |
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… |
From a new user perspective I really like NixOS's declarative and reproducible style, the fact that Forgive me for going off-topic. |
I marked this as stale due to inactivity. → More info |
Still an issue |
Discussed in Nix team meeting 2022-12-09:
decision: close the pull request, but not the underlying issue #2670 |
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 |
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.