From 83285981072ad76a05efbe4a1f7162a07161f3c9 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Sun, 4 Jul 2021 15:46:37 +0000 Subject: [PATCH 01/12] [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 rfcs/0097-no-read-store-dir.md diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md new file mode 100644 index 000000000..b6986abb6 --- /dev/null +++ b/rfcs/0097-no-read-store-dir.md @@ -0,0 +1,86 @@ +--- +feature: no-read-store-dir +start-date: 2021-07-04 +author: Las Safin +co-authors: +shepherd-team: +shepherd-leader: +related-issues: +--- + +# Summary +[summary]: #summary + +Set the permissions for /nix/store to 1771 instead of 1775, disabling reading the directory for other users. + +# Motivation +[motivation]: #motivation + +This means that you can not trivially see all the paths in the nix store, e.g. `ls` won't work +on /nix/store without sudo unless you're in the nixbld group. + +Almost everything in NixOS needs access to /nix/store, which means that all your systemd services, +your flatpak programs, your manually [bubblewrap](https://github.com/containers/bubblewrap)ed programs +will almost certainly have access to /nix/store and see all of its contents. +That means they will see your NixOS configuration, initrd secrets (unless your bootloader has support for it), +all the programs you've built, and possibly also your important secrets if you +(unfortunately) had to put them in the store! + +By simply removing the ability to read /nix/store (but not execute!), programs will only be able +to access the store paths *which they already know the hash and name of*. This is a huge boon to security +even if you don't have secrets in your store, as it will almost completely eliminate the above +problems. That isn't to say it's a complete solution to security, but it will *allow* much more +complete solutions to security, for example with bubblewrap, meaning that you can be sure that +your sandboxed game won't suddenly scrape your store and send it off to NSA. + +# Detailed design +[design]: #detailed-design + +Set the 1775 +in [nixpkgs/nixos/modules/system/boot/stage-2-init.sh](https://github.com/NixOS/nixpkgs/blob/8284fc30c84ea47e63209d1a892aca1dfcd6bdf3/nixos/modules/system/boot/stage-2-init.sh#L62), +in [nix/scripts/install-multi-user.sh](https://github.com/NixOS/nix/blob/cf1d4299a8fa8906f62271dcd878018cef84cc30/scripts/install-multi-user.sh#L577), +in [nix/src/libstore/globals.hh](https://github.com/NixOS/nix/blob/ba8b39c13003c8ddafb6bec308997e09b9851c46/src/libstore/globals.hh#L278), +in [nix/src/libstore/build/local-derivation-goal.cc](https://github.com/NixOS/nix/blob/6182ae689826554d915b4ed72e07f7978dc1d13c/src/libstore/build/local-derivation-goal.cc#L641), and +in [nix/src/libstore/local-store.cc](https://github.com/NixOS/nix/blob/0a535dd5ac93576f7152d786464e330ae3d46b50/src/libstore/local-store.cc#L181) +to 1771. + +# Examples and Interactions +[examples-and-interactions]: #examples-and-interactions + +Losing the read (r) bit means that you can't list the files inside the store. +The execute (x) bit allows us to `cd` to it and also access paths inside the store. + +E.g. `ls "$(readlink /nix/var/nix/profiles/system)"` will still work, since this is a directory +inside the store, and not the store itself, but you can't without sudo do `ls /nix/store` to find your system configuration. + +Note: A program could still have some idea of how the host machine is used by checking each store path that Hydra has ever built, +then it could estimate whether the host machine has e.g. Tor on it. + +# Drawbacks +[drawbacks]: #drawbacks + +It might be a slight annoyance since shell completion won't work in the /nix/store anymore, e.g. +if you have some hash 48914, you can't type `/nix/store/48914` to get the full path anymore. + +External tooling that does a traversal of the nix store (`find`, `du -s`, `ncdu`) would need `sudo` or explicitly given permissions. + +# Alternatives +[alternatives]: #alternatives + +I had [this script](https://github.com/L-as/nix-misc/blob/e844a03ebf4cad4fc8eca0e52306788b70c2a60d/claybox.rb) that is a wrapper around bubblewrap before, but doing this +is a lot cleaner, is system-wide, and is a lot faster, since bind mounting each individual store path with bubblewrap can be quite slow +if you have many (it's O(n^2) IIRC). + +Another alternative for users could be just doing `sudo unshare -m bash -c 'mount -o remount,rw /nix/store; chmod 1771 /nix/store'`, +but this doesn't work right now because all of the `nix` executables set it to 1775 again. + +# Unresolved questions +[unresolved]: #unresolved-questions + +There doesn't seem to be any. + +# Future work +[future]: #future-work + +There could be more work in the future toward properly sandboxing systemd services and such by default, +which could also make use of this vital change. From 2ecb1fcd08a319138addd5cb950d6ed60c0bbe28 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Sun, 4 Jul 2021 23:41:18 +0000 Subject: [PATCH 02/12] fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index b6986abb6..f65fbb0cf 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -44,6 +44,9 @@ in [nix/src/libstore/build/local-derivation-goal.cc](https://github.com/NixOS/ni in [nix/src/libstore/local-store.cc](https://github.com/NixOS/nix/blob/0a535dd5ac93576f7152d786464e330ae3d46b50/src/libstore/local-store.cc#L181) to 1771. +Nothing else has to be done likely, since setting the store permissions to `1771` manually doesn't break +anything other than what is mentioned in this document (though it is undone if you run a `nix` command). + # Examples and Interactions [examples-and-interactions]: #examples-and-interactions @@ -51,7 +54,7 @@ Losing the read (r) bit means that you can't list the files inside the store. The execute (x) bit allows us to `cd` to it and also access paths inside the store. E.g. `ls "$(readlink /nix/var/nix/profiles/system)"` will still work, since this is a directory -inside the store, and not the store itself, but you can't without sudo do `ls /nix/store` to find your system configuration. +inside the store, and not the store itself, but an unprivileged user can't `ls /nix/store` to find the system configuration. Note: A program could still have some idea of how the host machine is used by checking each store path that Hydra has ever built, then it could estimate whether the host machine has e.g. Tor on it. From 3333ded711e980c17f92953441c59d7ca2e871ad Mon Sep 17 00:00:00 2001 From: Las Safin Date: Mon, 5 Jul 2021 08:42:06 +0000 Subject: [PATCH 03/12] fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 70 ++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index f65fbb0cf..da1bc2fc4 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -11,7 +11,7 @@ related-issues: # Summary [summary]: #summary -Set the permissions for /nix/store to 1771 instead of 1775, disabling reading the directory for other users. +Set the permissions for /nix/store, /nix/store/.links and /nix/store/trash to 1771 instead of 1775, disabling reading the directory for other users. # Motivation [motivation]: #motivation @@ -19,19 +19,25 @@ Set the permissions for /nix/store to 1771 instead of 1775, disabling reading th This means that you can not trivially see all the paths in the nix store, e.g. `ls` won't work on /nix/store without sudo unless you're in the nixbld group. -Almost everything in NixOS needs access to /nix/store, which means that all your systemd services, -your flatpak programs, your manually [bubblewrap](https://github.com/containers/bubblewrap)ed programs -will almost certainly have access to /nix/store and see all of its contents. -That means they will see your NixOS configuration, initrd secrets (unless your bootloader has support for it), -all the programs you've built, and possibly also your important secrets if you -(unfortunately) had to put them in the store! +Almost everything in NixOS needs access to /nix/store, thus when sandboxing anything under NixOS, +if you want to have a secure sandbox that can't access your NixOS configuration, +initrd secrets (unless your bootloader has support for it), all the programs you've built, +and possibly also your important secrets if you (unfortunately) had to put them in the store, +you will need to restrict what store paths the sandbox can read. -By simply removing the ability to read /nix/store (but not execute!), programs will only be able -to access the store paths *which they already know the hash and name of*. This is a huge boon to security -even if you don't have secrets in your store, as it will almost completely eliminate the above -problems. That isn't to say it's a complete solution to security, but it will *allow* much more -complete solutions to security, for example with bubblewrap, meaning that you can be sure that -your sandboxed game won't suddenly scrape your store and send it off to NSA. +By simply removing the ability to read /nix/store (but not execute), programs will only be able +to access the store paths *which they already know the hash and name of*. +In this case, even if a sandbox has access to the entire store, they will not be able to access any path they do +not already know, and thus not be able to read any content they do not already know the hash of +(for non-content-addressed paths, it would be a function of the derivation however). +Essentially, knowing the hash will mean knowing the data. + +For non-sandboxed programs, much will not change if they can still read /run/current-system, /nix/var, +etc., thus this change is only important for sandboxes where you can remove such information leaks. +It isn't a big improvement by itself, but it is a small incremental hardening with few drawbacks that allows for better security in combination with sandboxes. + +NB: While this change is simple, it is not possible for end-users to do without changing Nix, since at the moment any +`nix` command will reset the permissions back to 1775. # Detailed design [design]: #detailed-design @@ -44,6 +50,11 @@ in [nix/src/libstore/build/local-derivation-goal.cc](https://github.com/NixOS/ni in [nix/src/libstore/local-store.cc](https://github.com/NixOS/nix/blob/0a535dd5ac93576f7152d786464e330ae3d46b50/src/libstore/local-store.cc#L181) to 1771. +/nix/store/trash and /nix/store/.links will also have to have their read bit removed, resulting in 0751. + +Currently, /proc/cmdline provides the path to the current system configuration, which is counter-productive in this case. +This would have to be fixed, by e.g. either embedding the path into the initrd or using the generation number instead of the full path. + Nothing else has to be done likely, since setting the store permissions to `1771` manually doesn't break anything other than what is mentioned in this document (though it is undone if you run a `nix` command). @@ -56,7 +67,7 @@ The execute (x) bit allows us to `cd` to it and also access paths inside the sto E.g. `ls "$(readlink /nix/var/nix/profiles/system)"` will still work, since this is a directory inside the store, and not the store itself, but an unprivileged user can't `ls /nix/store` to find the system configuration. -Note: A program could still have some idea of how the host machine is used by checking each store path that Hydra has ever built, +Note: A sandboxed program could still have some idea of how the host machine is used by checking each store path that Hydra has ever built, then it could estimate whether the host machine has e.g. Tor on it. # Drawbacks @@ -70,12 +81,30 @@ External tooling that does a traversal of the nix store (`find`, `du -s`, `ncdu` # Alternatives [alternatives]: #alternatives -I had [this script](https://github.com/L-as/nix-misc/blob/e844a03ebf4cad4fc8eca0e52306788b70c2a60d/claybox.rb) that is a wrapper around bubblewrap before, but doing this -is a lot cleaner, is system-wide, and is a lot faster, since bind mounting each individual store path with bubblewrap can be quite slow -if you have many (it's O(n^2) IIRC). +If Nix was made to not reset the permissions of /nix/store back to 1775, users who want this change could +do it themselves by simply putting this into their configuration.nix: +```nix +{ + system.activationScripts.chmod-store.text = '' + ${pkgs.util-linux}/bin/unshare -m ${pkgs.bash}/bin/sh -c '${pkgs.util-linux}/bin/mount -o remount,rw /nix/store ; ${pkgs.coreutils}/bin/chmod 1771 /nix/store' + ''; +} +``` -Another alternative for users could be just doing `sudo unshare -m bash -c 'mount -o remount,rw /nix/store; chmod 1771 /nix/store'`, -but this doesn't work right now because all of the `nix` executables set it to 1775 again. + +There is also currently the [systemd-confinement.nix](https://github.com/NixOS/nixpkgs/blob/93c9e5854d87b8e7eeafda2ead4b375d75500c80/nixos/modules/security/systemd-confinement.nix) module in NixOS, which makes use of systemd functionality +that is functionally equivalent to what bubblewrap does, to make sure only the necessary store paths are mounted. + +This is obviously limited to systemd services, for non-systemd-services I had [this script](https://github.com/L-as/nix-misc/blob/e844a03ebf4cad4fc8eca0e52306788b70c2a60d/claybox.rb) +that just wraps around bubblewrap, but 1) isn't convenient since you need to provide the derivation outputs you want to include and not the attributes for those derivations, +and 2) it can get [quite slow](https://github.com/containers/bubblewrap/issues/384) once you have sufficiently many store paths you want to include. This could be fixed +by fixing bubblewrap, but it does not seem to be [making progress](https://github.com/containers/bubblewrap/pull/385). + +Both of the above alternatives in addition have the problem that they can not be made aware of new store paths. +In the case of the design specified in this document, you could pass a store path to a running sandboxed program and they +would be able to access it normally. +In addition, you would also be able to e.g. execute a setuid program from inside the sandbox that then has access to paths +that the executer didn't, since the executed setuid program could read paths from a file with strict read permissions. # Unresolved questions [unresolved]: #unresolved-questions @@ -85,5 +114,4 @@ There doesn't seem to be any. # Future work [future]: #future-work -There could be more work in the future toward properly sandboxing systemd services and such by default, -which could also make use of this vital change. +There doesn't seem to be any. From f35e0c94413bad205481381770a9c0424ea568a8 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Mon, 5 Jul 2021 12:17:07 +0000 Subject: [PATCH 04/12] fixup! fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index da1bc2fc4..1fe6e859a 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -53,7 +53,9 @@ to 1771. /nix/store/trash and /nix/store/.links will also have to have their read bit removed, resulting in 0751. Currently, /proc/cmdline provides the path to the current system configuration, which is counter-productive in this case. -This would have to be fixed, by e.g. either embedding the path into the initrd or using the generation number instead of the full path. +This would be fixed by simply setting the permissions to 0440, since the permissions are universal to all pid namespaces. +Fixing the permissions for other files in /proc would likely also be a good idea, regardless of this RFC and regardless +of sandboxing, e.g. it doesn't make sense to expose /proc/config.gz to the entire world even if it doesn't expose any secrets. Nothing else has to be done likely, since setting the store permissions to `1771` manually doesn't break anything other than what is mentioned in this document (though it is undone if you run a `nix` command). From 78a5b82cace35a7345d82abe986331650708fcad Mon Sep 17 00:00:00 2001 From: Las Safin Date: Tue, 6 Jul 2021 21:09:37 +0000 Subject: [PATCH 05/12] fixup! fixup! fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 103 ++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 46 deletions(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index 1fe6e859a..551d8e2ee 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -11,30 +11,33 @@ related-issues: # Summary [summary]: #summary -Set the permissions for /nix/store, /nix/store/.links and /nix/store/trash to 1771 instead of 1775, disabling reading the directory for other users. +Set the permissions for `/nix/store`, `/nix/store/.links` and `/nix/store/trash` to 1771 instead of 1775, disabling reading the directory for other users. # Motivation [motivation]: #motivation -This means that you can not trivially see all the paths in the nix store, e.g. `ls` won't work -on /nix/store without sudo unless you're in the nixbld group. - -Almost everything in NixOS needs access to /nix/store, thus when sandboxing anything under NixOS, -if you want to have a secure sandbox that can't access your NixOS configuration, -initrd secrets (unless your bootloader has support for it), all the programs you've built, -and possibly also your important secrets if you (unfortunately) had to put them in the store, -you will need to restrict what store paths the sandbox can read. - -By simply removing the ability to read /nix/store (but not execute), programs will only be able -to access the store paths *which they already know the hash and name of*. -In this case, even if a sandbox has access to the entire store, they will not be able to access any path they do -not already know, and thus not be able to read any content they do not already know the hash of -(for non-content-addressed paths, it would be a function of the derivation however). -Essentially, knowing the hash will mean knowing the data. - -For non-sandboxed programs, much will not change if they can still read /run/current-system, /nix/var, -etc., thus this change is only important for sandboxes where you can remove such information leaks. -It isn't a big improvement by itself, but it is a small incremental hardening with few drawbacks that allows for better security in combination with sandboxes. +This is a simple change that acts as an extra layer of security by making +it harder to access store paths that programs don't need to access. + +For a directory, being able to execute without being able to read it means +that you can access paths inside the directory, if and only if you know the path beforehand, +since you can not list the directory entries. + +In our case, with this change, all users that are not root and are not part of nixbld, +will only be able to directly access paths inside `/nix/store` which they already know, +i.e. the hash and the name. +If you run some program that does not have access to the nix daemon under such +a user, it will not be able to search the store for built system configurations, +but will instead have to find the path to one through e.g. `/nix/var/nix/profiles/system` (if accessible). + +For tight sandboxes under NixOS, where only the store paths needed are mounted, +this is not very important, since they can already only access the ones they need, +however, this feature is useful for making sandboxes that have access to the entirety +of `/nix/store`, where you want to be sure that any store path encountered inside the sandbox +can be accessed. This will often happen when you want a quick sandbox with bubblewrap where +it's too much of a pain to manually find all store paths you need. +This type of sandbox can still be secure, granted that you are careful not to "leak" store paths +into the sandbox, which is not trivial but not impossible either. Specifics are discussed in [Drawbacks](#drawbacks). NB: While this change is simple, it is not possible for end-users to do without changing Nix, since at the moment any `nix` command will reset the permissions back to 1775. @@ -50,12 +53,18 @@ in [nix/src/libstore/build/local-derivation-goal.cc](https://github.com/NixOS/ni in [nix/src/libstore/local-store.cc](https://github.com/NixOS/nix/blob/0a535dd5ac93576f7152d786464e330ae3d46b50/src/libstore/local-store.cc#L181) to 1771. -/nix/store/trash and /nix/store/.links will also have to have their read bit removed, resulting in 0751. +`/nix/store/trash` and `/nix/store/.links` will also have to have their read bit removed, resulting in 0751. + +Various other parts of NixOS also ought have their permissions fixed, which is beneficial with and without this change. +Specifically, the permissions for these things ought to be looked at again: +- `/proc/cmdline` +- `/proc/sys` +- `/proc/config.gz` +- The other `/proc/*` stuff +- `dmesg` +- The nix daemon -Currently, /proc/cmdline provides the path to the current system configuration, which is counter-productive in this case. -This would be fixed by simply setting the permissions to 0440, since the permissions are universal to all pid namespaces. -Fixing the permissions for other files in /proc would likely also be a good idea, regardless of this RFC and regardless -of sandboxing, e.g. it doesn't make sense to expose /proc/config.gz to the entire world even if it doesn't expose any secrets. +Sandboxes should preferably not have access to the above in any case. Nothing else has to be done likely, since setting the store permissions to `1771` manually doesn't break anything other than what is mentioned in this document (though it is undone if you run a `nix` command). @@ -69,17 +78,30 @@ The execute (x) bit allows us to `cd` to it and also access paths inside the sto E.g. `ls "$(readlink /nix/var/nix/profiles/system)"` will still work, since this is a directory inside the store, and not the store itself, but an unprivileged user can't `ls /nix/store` to find the system configuration. -Note: A sandboxed program could still have some idea of how the host machine is used by checking each store path that Hydra has ever built, -then it could estimate whether the host machine has e.g. Tor on it. - # Drawbacks [drawbacks]: #drawbacks -It might be a slight annoyance since shell completion won't work in the /nix/store anymore, e.g. -if you have some hash 48914, you can't type `/nix/store/48914` to get the full path anymore. +It might be a slight annoyance since shell completion won't work in the `/nix/store` anymore, e.g. +if you have some hash 48914, you wouldn't be able to type `/nix/store/48914` to get the full path anymore. +This is likely the biggest drawback. External tooling that does a traversal of the nix store (`find`, `du -s`, `ncdu`) would need `sudo` or explicitly given permissions. +A sandboxed program could still have some idea of how the host machine is used by checking each store path that Hydra has ever built, +then it could estimate whether the host machine has e.g. Tor on it. + +Attempting to have security comparable to bind-mounting each necessary store path inside a sandbox is not easy, +since you need to be careful not to leak paths to the sandbox through global state, for example `/proc/cmdline`. +There is no easy way to know whether some path is readable from inside the sandbox, since the problem is +essentially proving that the sandbox doesn't have access to some secret that in this case is the store path itself. +Since Unix software has not traditionally treated paths as secrets, this is a bit problematic if you're +trying to make sure your sandbox can't access some path. We also can not know how future kernel interfaces and such +will be, and they will likely not have the same considerations for "paths as secrets" as us. +Essentially, you have to consider the store paths as encodings of their contents wrt. sandboxes. + +The above points about sandboxed programs are not negative effects of changing the permissions of /nix/store and such, +but rather considerations for making use of this change in the context of sandboxes. + # Alternatives [alternatives]: #alternatives @@ -88,25 +110,14 @@ do it themselves by simply putting this into their configuration.nix: ```nix { system.activationScripts.chmod-store.text = '' - ${pkgs.util-linux}/bin/unshare -m ${pkgs.bash}/bin/sh -c '${pkgs.util-linux}/bin/mount -o remount,rw /nix/store ; ${pkgs.coreutils}/bin/chmod 1771 /nix/store' + ${pkgs.util-linux}/bin/unshare -m ${pkgs.bash}/bin/sh -c '${pkgs.util-linux}/bin/mount -o remount,rw /nix/store ; ${pkgs.coreutils}/bin/chmod 1771 /nix/store ; ...' ''; } ``` - -There is also currently the [systemd-confinement.nix](https://github.com/NixOS/nixpkgs/blob/93c9e5854d87b8e7eeafda2ead4b375d75500c80/nixos/modules/security/systemd-confinement.nix) module in NixOS, which makes use of systemd functionality -that is functionally equivalent to what bubblewrap does, to make sure only the necessary store paths are mounted. - -This is obviously limited to systemd services, for non-systemd-services I had [this script](https://github.com/L-as/nix-misc/blob/e844a03ebf4cad4fc8eca0e52306788b70c2a60d/claybox.rb) -that just wraps around bubblewrap, but 1) isn't convenient since you need to provide the derivation outputs you want to include and not the attributes for those derivations, -and 2) it can get [quite slow](https://github.com/containers/bubblewrap/issues/384) once you have sufficiently many store paths you want to include. This could be fixed -by fixing bubblewrap, but it does not seem to be [making progress](https://github.com/containers/bubblewrap/pull/385). - -Both of the above alternatives in addition have the problem that they can not be made aware of new store paths. -In the case of the design specified in this document, you could pass a store path to a running sandboxed program and they -would be able to access it normally. -In addition, you would also be able to e.g. execute a setuid program from inside the sandbox that then has access to paths -that the executer didn't, since the executed setuid program could read paths from a file with strict read permissions. +Traditional sandboxing could be seen as an alternative to this, however, they are not mutually exclusive, +since this change is fundamentally just changing some permissions in NixOS. +Improving the permissions for /nix/store will not decrease security for traditional sandboxes. # Unresolved questions [unresolved]: #unresolved-questions @@ -116,4 +127,4 @@ There doesn't seem to be any. # Future work [future]: #future-work -There doesn't seem to be any. +Future work includes fixing the permissions of global state in NixOS, notably access to the nix daemon, much of the kernel API, such as `/proc`. From 82b553fd6622bdd2872270f1ff12058eca067aaa Mon Sep 17 00:00:00 2001 From: Las Safin Date: Thu, 8 Jul 2021 14:31:59 +0000 Subject: [PATCH 06/12] fixup! fixup! fixup! fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index 551d8e2ee..79603e630 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -55,7 +55,7 @@ to 1771. `/nix/store/trash` and `/nix/store/.links` will also have to have their read bit removed, resulting in 0751. -Various other parts of NixOS also ought have their permissions fixed, which is beneficial with and without this change. +Various parts of NixOS also ought have their permissions fixed, which is beneficial with and without this change. Specifically, the permissions for these things ought to be looked at again: - `/proc/cmdline` - `/proc/sys` @@ -116,7 +116,7 @@ do it themselves by simply putting this into their configuration.nix: ``` Traditional sandboxing could be seen as an alternative to this, however, they are not mutually exclusive, -since this change is fundamentally just changing some permissions in NixOS. +since this change is fundamentally just changing some permissions. Improving the permissions for /nix/store will not decrease security for traditional sandboxes. # Unresolved questions From 34285656ccfad3aa7455b52f1a34abfe65b37875 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Thu, 2 Sep 2021 13:52:40 +0000 Subject: [PATCH 07/12] fixup! fixup! fixup! fixup! fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index 79603e630..79216af93 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -3,8 +3,8 @@ feature: no-read-store-dir start-date: 2021-07-04 author: Las Safin co-authors: -shepherd-team: -shepherd-leader: +shepherd-team: @kevincox @7c6f434c @edolstra +shepherd-leader: @kevincox related-issues: --- From 1f78c9a6b2593a96d4ba68706e038f8bb7f6efbc Mon Sep 17 00:00:00 2001 From: Las Safin Date: Sat, 11 Sep 2021 20:13:11 +0000 Subject: [PATCH 08/12] fixup! fixup! fixup! fixup! fixup! fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index 79216af93..70e6a04ab 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -4,7 +4,7 @@ start-date: 2021-07-04 author: Las Safin co-authors: shepherd-team: @kevincox @7c6f434c @edolstra -shepherd-leader: @kevincox +shepherd-leader: @edolstra related-issues: --- From 5d26444aa107fc5492f6c23e2cab6e15a4bade33 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Fri, 15 Oct 2021 17:58:36 +0000 Subject: [PATCH 09/12] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 117 +++++++++------------------------ 1 file changed, 31 insertions(+), 86 deletions(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index 70e6a04ab..e640292e8 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -1,5 +1,5 @@ --- -feature: no-read-store-dir +feature: nix-store-perms start-date: 2021-07-04 author: Las Safin co-authors: @@ -11,113 +11,58 @@ related-issues: # Summary [summary]: #summary -Set the permissions for `/nix/store`, `/nix/store/.links` and `/nix/store/trash` to 1771 instead of 1775, disabling reading the directory for other users. +- NixOS should have a module for configuring the permissions set for `/nix/store` on boot. +- Nix should not enforce the permissions used for `/nix/store`. +- The default permissions if the store doesn't exist should be 1735 when the store is made by Nix or the NixOS installer. + This means that the nixbld group can't `ls` the directory. # Motivation [motivation]: #motivation -This is a simple change that acts as an extra layer of security by making -it harder to access store paths that programs don't need to access. +Right now you can't set the permissions for `/nix/store`, since they'll be overwritten +by Nix anytime you use `nix`. -For a directory, being able to execute without being able to read it means -that you can access paths inside the directory, if and only if you know the path beforehand, -since you can not list the directory entries. +We want to `chmod g-r /nix/store`, because the `nixbld` group doesn't actually +need to read the directory. It only needs to be able to write and "execute" it. +This, however, should be optional, since the user should be able to do what they want. -In our case, with this change, all users that are not root and are not part of nixbld, -will only be able to directly access paths inside `/nix/store` which they already know, -i.e. the hash and the name. -If you run some program that does not have access to the nix daemon under such -a user, it will not be able to search the store for built system configurations, -but will instead have to find the path to one through e.g. `/nix/var/nix/profiles/system` (if accessible). - -For tight sandboxes under NixOS, where only the store paths needed are mounted, -this is not very important, since they can already only access the ones they need, -however, this feature is useful for making sandboxes that have access to the entirety -of `/nix/store`, where you want to be sure that any store path encountered inside the sandbox -can be accessed. This will often happen when you want a quick sandbox with bubblewrap where -it's too much of a pain to manually find all store paths you need. -This type of sandbox can still be secure, granted that you are careful not to "leak" store paths -into the sandbox, which is not trivial but not impossible either. Specifics are discussed in [Drawbacks](#drawbacks). - -NB: While this change is simple, it is not possible for end-users to do without changing Nix, since at the moment any -`nix` command will reset the permissions back to 1775. +Some users might also want to do things like `chmod o-r /nix/store`, which +gives you the interesting property that you can not access paths you do not +already know of. +This, however, is also entirely optional and is not the default in any way. # Detailed design [design]: #detailed-design -Set the 1775 -in [nixpkgs/nixos/modules/system/boot/stage-2-init.sh](https://github.com/NixOS/nixpkgs/blob/8284fc30c84ea47e63209d1a892aca1dfcd6bdf3/nixos/modules/system/boot/stage-2-init.sh#L62), -in [nix/scripts/install-multi-user.sh](https://github.com/NixOS/nix/blob/cf1d4299a8fa8906f62271dcd878018cef84cc30/scripts/install-multi-user.sh#L577), -in [nix/src/libstore/globals.hh](https://github.com/NixOS/nix/blob/ba8b39c13003c8ddafb6bec308997e09b9851c46/src/libstore/globals.hh#L278), -in [nix/src/libstore/build/local-derivation-goal.cc](https://github.com/NixOS/nix/blob/6182ae689826554d915b4ed72e07f7978dc1d13c/src/libstore/build/local-derivation-goal.cc#L641), and -in [nix/src/libstore/local-store.cc](https://github.com/NixOS/nix/blob/0a535dd5ac93576f7152d786464e330ae3d46b50/src/libstore/local-store.cc#L181) -to 1771. - -`/nix/store/trash` and `/nix/store/.links` will also have to have their read bit removed, resulting in 0751. - -Various parts of NixOS also ought have their permissions fixed, which is beneficial with and without this change. -Specifically, the permissions for these things ought to be looked at again: -- `/proc/cmdline` -- `/proc/sys` -- `/proc/config.gz` -- The other `/proc/*` stuff -- `dmesg` -- The nix daemon +Where we previously would enforce the permissions, we now need to +only set them if there is no directory in the first place. +The same applies for `/nix/store/trash` and `/nix/store/.links`. -Sandboxes should preferably not have access to the above in any case. - -Nothing else has to be done likely, since setting the store permissions to `1771` manually doesn't break -anything other than what is mentioned in this document (though it is undone if you run a `nix` command). +Specifically, we need to modify the following places (not exhaustive): +- [nixpkgs/nixos/modules/system/boot/stage-2-init.sh](https://github.com/NixOS/nixpkgs/blob/8284fc30c84ea47e63209d1a892aca1dfcd6bdf3/nixos/modules/system/boot/stage-2-init.sh#L62) +- [nix/scripts/install-multi-user.sh](https://github.com/NixOS/nix/blob/cf1d4299a8fa8906f62271dcd878018cef84cc30/scripts/install-multi-user.sh#L577) +- [nix/src/libstore/globals.hh](https://github.com/NixOS/nix/blob/ba8b39c13003c8ddafb6bec308997e09b9851c46/src/libstore/globals.hh#L278) +- [nix/src/libstore/build/local-derivation-goal.cc](https://github.com/NixOS/nix/blob/6182ae689826554d915b4ed72e07f7978dc1d13c/src/libstore/build/local-derivation-goal.cc#L641) +- [nix/src/libstore/local-store.cc](https://github.com/NixOS/nix/blob/0a535dd5ac93576f7152d786464e330ae3d46b50/src/libstore/local-store.cc#L181) # Examples and Interactions [examples-and-interactions]: #examples-and-interactions -Losing the read (r) bit means that you can't list the files inside the store. -The execute (x) bit allows us to `cd` to it and also access paths inside the store. - -E.g. `ls "$(readlink /nix/var/nix/profiles/system)"` will still work, since this is a directory -inside the store, and not the store itself, but an unprivileged user can't `ls /nix/store` to find the system configuration. +You should be able to do something like the following: +```nix +nix.store-perms = "xxxx"; +``` # Drawbacks [drawbacks]: #drawbacks -It might be a slight annoyance since shell completion won't work in the `/nix/store` anymore, e.g. -if you have some hash 48914, you wouldn't be able to type `/nix/store/48914` to get the full path anymore. -This is likely the biggest drawback. - -External tooling that does a traversal of the nix store (`find`, `du -s`, `ncdu`) would need `sudo` or explicitly given permissions. - -A sandboxed program could still have some idea of how the host machine is used by checking each store path that Hydra has ever built, -then it could estimate whether the host machine has e.g. Tor on it. - -Attempting to have security comparable to bind-mounting each necessary store path inside a sandbox is not easy, -since you need to be careful not to leak paths to the sandbox through global state, for example `/proc/cmdline`. -There is no easy way to know whether some path is readable from inside the sandbox, since the problem is -essentially proving that the sandbox doesn't have access to some secret that in this case is the store path itself. -Since Unix software has not traditionally treated paths as secrets, this is a bit problematic if you're -trying to make sure your sandbox can't access some path. We also can not know how future kernel interfaces and such -will be, and they will likely not have the same considerations for "paths as secrets" as us. -Essentially, you have to consider the store paths as encodings of their contents wrt. sandboxes. - -The above points about sandboxed programs are not negative effects of changing the permissions of /nix/store and such, -but rather considerations for making use of this change in the context of sandboxes. +If a user on a non-NixOS platform mistakenly sets the permissions for `/nix/store` to +something else, it won't be reverted by Nix automatically. # Alternatives [alternatives]: #alternatives -If Nix was made to not reset the permissions of /nix/store back to 1775, users who want this change could -do it themselves by simply putting this into their configuration.nix: -```nix -{ - system.activationScripts.chmod-store.text = '' - ${pkgs.util-linux}/bin/unshare -m ${pkgs.bash}/bin/sh -c '${pkgs.util-linux}/bin/mount -o remount,rw /nix/store ; ${pkgs.coreutils}/bin/chmod 1771 /nix/store ; ...' - ''; -} -``` - -Traditional sandboxing could be seen as an alternative to this, however, they are not mutually exclusive, -since this change is fundamentally just changing some permissions. -Improving the permissions for /nix/store will not decrease security for traditional sandboxes. +You could not do this and keep it as it is. # Unresolved questions [unresolved]: #unresolved-questions @@ -127,4 +72,4 @@ There doesn't seem to be any. # Future work [future]: #future-work -Future work includes fixing the permissions of global state in NixOS, notably access to the nix daemon, much of the kernel API, such as `/proc`. +In the future we likely want to reduce the default permissions for `/nix/store` as much as possible. From fc5a0dac9213fbb5d0c807822c23358039590bde Mon Sep 17 00:00:00 2001 From: Las Safin Date: Tue, 2 Nov 2021 09:59:43 +0000 Subject: [PATCH 10/12] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index e640292e8..758994f76 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -29,6 +29,9 @@ This, however, should be optional, since the user should be able to do what they Some users might also want to do things like `chmod o-r /nix/store`, which gives you the interesting property that you can not access paths you do not already know of. +Do note that given that all processes can by default read `/proc/cmdline`, +they can still read your system's closure, which makes it an insufficient +solution for security in many cases. This, however, is also entirely optional and is not the default in any way. # Detailed design From 31ecff21d47cb57e6cf262beccabffc1d3b9b820 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Tue, 2 Nov 2021 22:31:05 +0000 Subject: [PATCH 11/12] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index 758994f76..c0fe6ab3b 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -30,8 +30,8 @@ Some users might also want to do things like `chmod o-r /nix/store`, which gives you the interesting property that you can not access paths you do not already know of. Do note that given that all processes can by default read `/proc/cmdline`, -they can still read your system's closure, which makes it an insufficient -solution for security in many cases. +`/run/current-system`, and many other places, they can still read your +system's closure, which makes it an insufficient solution for security in many cases. This, however, is also entirely optional and is not the default in any way. # Detailed design From a1a85dd895a650969c6424af2061ad54964282ab Mon Sep 17 00:00:00 2001 From: Las Safin Date: Sun, 21 Nov 2021 15:24:34 +0000 Subject: [PATCH 12/12] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! [RFC 0097] Unset read permission bit on /nix/store for other users --- rfcs/0097-no-read-store-dir.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rfcs/0097-no-read-store-dir.md b/rfcs/0097-no-read-store-dir.md index c0fe6ab3b..e099af675 100644 --- a/rfcs/0097-no-read-store-dir.md +++ b/rfcs/0097-no-read-store-dir.md @@ -22,17 +22,19 @@ related-issues: Right now you can't set the permissions for `/nix/store`, since they'll be overwritten by Nix anytime you use `nix`. -We want to `chmod g-r /nix/store`, because the `nixbld` group doesn't actually +`chmod g-r /nix/store` is beneficial because the `nixbld` group doesn't actually need to read the directory. It only needs to be able to write and "execute" it. -This, however, should be optional, since the user should be able to do what they want. +This, however, should be optional, since the user should be able to configure +the permissions however they want. Some users might also want to do things like `chmod o-r /nix/store`, which gives you the interesting property that you can not access paths you do not already know of. Do note that given that all processes can by default read `/proc/cmdline`, -`/run/current-system`, and many other places, they can still read your -system's closure, which makes it an insufficient solution for security in many cases. -This, however, is also entirely optional and is not the default in any way. +`/run/current-system`, and many other places which reveal your +system's closure, making this permission change an insufficient solution for +security in many cases. This, however, is also entirely optional and is not +the default in any way. # Detailed design [design]: #detailed-design @@ -60,7 +62,7 @@ nix.store-perms = "xxxx"; [drawbacks]: #drawbacks If a user on a non-NixOS platform mistakenly sets the permissions for `/nix/store` to -something else, it won't be reverted by Nix automatically. +something undesirable, it won't be reverted by Nix automatically. # Alternatives [alternatives]: #alternatives