From 4cc35c3f261fffffff6ff3be0210084fbc97f81e Mon Sep 17 00:00:00 2001 From: poscat Date: Fri, 3 Feb 2023 11:53:26 +0800 Subject: [PATCH 01/13] Private Derivations --- rfcs/0141-private-derivations.md | 58 ++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 rfcs/0141-private-derivations.md diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md new file mode 100644 index 000000000..22a2c3a61 --- /dev/null +++ b/rfcs/0141-private-derivations.md @@ -0,0 +1,58 @@ +--- +feature: private-derivations +start-date: 2023-02-03 +author: poscat +co-authors: (find a buddy later to help out with the RFC) +shepherd-team: (names, to be nominated and accepted by RFC steering committee) +shepherd-leader: (name to be appointed by RFC steering committee) +related-issues: (will contain links to implementation PRs) +--- + +# Summary +[summary]: #summary + +This RFC proposes to add a special type of derivation called private derivation, which, upon being built, will have their file permissions set to 000 instead of the usual 444. + +# Motivation +[motivation]: #motivation + +In short: This RFC mainly concerns with how to safely store credentials on NixOS. + +The world readability of nix store means that, to safely store credentials, they must be first somehow be encrypted before written into the store. They also need to be decrypted before the services are started. + +This is less than ideal because one needs to setup a key (which is stored as plaintext on disk) on every machine just to prevent unauthorized users from seeting the credentials. + +Furthermore, if encryption is done before the evaluation of the system configuration (as is the case with [agenix](https://github.com/ryantm/agenix) and [sops-nix](https://github.com/Mic92/sops-nix)), then the nixos module system cannot be utilized to generate configs that contain credentials and one must write them manually. + +All of this can be prevented if we added the ability to make derivation outputs as not readable by anyone other than root, by setting the file mode to 111 (directories) or 000 (files). We can then use a trustworthy credential manager, for example systemd with its `LoadCredential=`, to distribute these derivations to the consumers safely. + +# Detailed design +[design]: #detailed-design + +We propose adding a `noReadAccess` option to `builtins.derivation`, which, when set to true, makes this derivation a private derivation. Relevant changes should also be made in nix-instantiate and nix-daemon to understand this attribute. + +# Examples and Interactions +[examples-and-interactions]: #examples-and-interactions + +TBA + +# Drawbacks +[drawbacks]: #drawbacks + +- Adding private derivations further complicates the nix store model. + +# Alternatives +[alternatives]: #alternatives + +An alternative would be to support more complicated ACLs as described in [this](https://github.com/NixOS/nix/issues/8) Nix issue. + +# Unresolved questions +[unresolved]: #unresolved-questions + +It is not yet known how this might interact with content addressed paths. + +# Future work +[future]: #future-work + +What future work, if any, would be implied or impacted by this feature +without being directly part of the work? From f2468921030962cbe51004ebdca050f28a72c724 Mon Sep 17 00:00:00 2001 From: poscat Date: Fri, 3 Feb 2023 15:00:54 +0800 Subject: [PATCH 02/13] expand on unresolved questions --- rfcs/0141-private-derivations.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index 22a2c3a61..81ca47f28 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -49,6 +49,14 @@ An alternative would be to support more complicated ACLs as described in [this]( # Unresolved questions [unresolved]: #unresolved-questions +## Binary caches and copying +How do we prevent the attacker from using `nix copy` to simply copy out the +private derivation to another machine? + +What changes are needed in binary cache providers such as `nix-serve` to handle +private derivations? + +## Content-Addressed paths It is not yet known how this might interact with content addressed paths. # Future work From ee44986e6900de6dcc6358499fdabe73f4e81172 Mon Sep 17 00:00:00 2001 From: poscat Date: Fri, 3 Feb 2023 15:22:53 +0800 Subject: [PATCH 03/13] mention executable files --- rfcs/0141-private-derivations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index 81ca47f28..dabb3b452 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -11,7 +11,7 @@ related-issues: (will contain links to implementation PRs) # Summary [summary]: #summary -This RFC proposes to add a special type of derivation called private derivation, which, upon being built, will have their file permissions set to 000 instead of the usual 444. +This RFC proposes to add a special type of derivation called private derivation, which, upon being built, will have their file permissions set to 000/111 instead of the usual 444/555. # Motivation [motivation]: #motivation @@ -24,7 +24,7 @@ This is less than ideal because one needs to setup a key (which is stored as pla Furthermore, if encryption is done before the evaluation of the system configuration (as is the case with [agenix](https://github.com/ryantm/agenix) and [sops-nix](https://github.com/Mic92/sops-nix)), then the nixos module system cannot be utilized to generate configs that contain credentials and one must write them manually. -All of this can be prevented if we added the ability to make derivation outputs as not readable by anyone other than root, by setting the file mode to 111 (directories) or 000 (files). We can then use a trustworthy credential manager, for example systemd with its `LoadCredential=`, to distribute these derivations to the consumers safely. +All of this can be prevented if we added the ability to make derivation outputs as not readable by anyone other than root, by setting the file mode to 111 (directories and executables) or 000 (regular files). We can then use a trustworthy credential manager, for example systemd with its `LoadCredential=`, to distribute these derivations to the consumers safely. # Detailed design [design]: #detailed-design From 73c4e53f84203277aefd6353538a66b15f81a73f Mon Sep 17 00:00:00 2001 From: poscat Date: Fri, 3 Feb 2023 15:44:37 +0800 Subject: [PATCH 04/13] add intended usage section --- rfcs/0141-private-derivations.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index dabb3b452..fef89b673 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -34,7 +34,15 @@ We propose adding a `noReadAccess` option to `builtins.derivation`, which, when # Examples and Interactions [examples-and-interactions]: #examples-and-interactions -TBA +## Intended usage in NixOS modules +On NixOS there are many modules where configurations that might contain sensitive +information get written into nix store in plaintext (for example the wpa_supplicant module). Private derivations can solve this issue by + +1. Writing a helper function `writeTextPrivate` that functions similar to +`writeText`, but instead outputs a private derivation +2. Replace the `writeText` function with `writeTextPrivate` inside the module +3. Use `LoadCredential=` to load the private derivation +4. Replace the derivation output path with `%d/` (see systemd.exec) # Drawbacks [drawbacks]: #drawbacks From 6e6ad6fad22fe322b3442dd45ce9d393ecf9eaa5 Mon Sep 17 00:00:00 2001 From: poscat Date: Fri, 3 Feb 2023 16:03:18 +0800 Subject: [PATCH 05/13] grammar --- rfcs/0141-private-derivations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index fef89b673..b40688e09 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -61,7 +61,7 @@ An alternative would be to support more complicated ACLs as described in [this]( How do we prevent the attacker from using `nix copy` to simply copy out the private derivation to another machine? -What changes are needed in binary cache providers such as `nix-serve` to handle +What changes are needed for binary cache providers such as `nix-serve` to handle private derivations? ## Content-Addressed paths From a49426fef86eff8637d631f4cd4e3f83bfd0c306 Mon Sep 17 00:00:00 2001 From: poscat Date: Fri, 3 Feb 2023 16:06:20 +0800 Subject: [PATCH 06/13] mention leaking metadata --- rfcs/0141-private-derivations.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index b40688e09..833f1d8fd 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -64,6 +64,10 @@ private derivation to another machine? What changes are needed for binary cache providers such as `nix-serve` to handle private derivations? +## Leaking metadata +The hash is still exposed to the attacker, which opens up some possible attacks. +How does this impact the security? + ## Content-Addressed paths It is not yet known how this might interact with content addressed paths. From 2246c7337c7da14adf9f4ece8c52c08c58c9dbb7 Mon Sep 17 00:00:00 2001 From: poscat Date: Fri, 3 Feb 2023 16:21:27 +0800 Subject: [PATCH 07/13] .drv files should also be affected --- rfcs/0141-private-derivations.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index 833f1d8fd..3cb9f166d 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -29,7 +29,11 @@ All of this can be prevented if we added the ability to make derivation outputs # Detailed design [design]: #detailed-design -We propose adding a `noReadAccess` option to `builtins.derivation`, which, when set to true, makes this derivation a private derivation. Relevant changes should also be made in nix-instantiate and nix-daemon to understand this attribute. +We propose adding a `noReadAccess` option to `builtins.derivation`, which, when set to true, makes this derivation a private derivation. + +The only difference between a private derivation and a normal derivation, apart +from the hash, is that upon instantiation and after building, the read bit of +the `.drv` file and the output path will be removed. # Examples and Interactions [examples-and-interactions]: #examples-and-interactions From 742196c37583a70912619f98fab36502d6ed92cd Mon Sep 17 00:00:00 2001 From: poscat Date: Fri, 3 Feb 2023 16:28:32 +0800 Subject: [PATCH 08/13] more alternative designs --- rfcs/0141-private-derivations.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index 3cb9f166d..e1a081ad5 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -56,7 +56,10 @@ information get written into nix store in plaintext (for example the wpa_supplic # Alternatives [alternatives]: #alternatives -An alternative would be to support more complicated ACLs as described in [this](https://github.com/NixOS/nix/issues/8) Nix issue. +- Supporting more complicated ACLs as described in [this](https://github.com/NixOS/nix/issues/8) Nix issue. +- Storing private derivations in a separate store path, for example + `/nix/private-store` that have its executable bit removed so that the hashes + are not visible to non-root users. # Unresolved questions [unresolved]: #unresolved-questions From a51bfd89d5b9445f680e0282b25dda495aa19fa5 Mon Sep 17 00:00:00 2001 From: poscat Date: Fri, 3 Feb 2023 20:03:03 +0800 Subject: [PATCH 09/13] grammar --- rfcs/0141-private-derivations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index e1a081ad5..104b1fec7 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -18,7 +18,7 @@ This RFC proposes to add a special type of derivation called private derivation, In short: This RFC mainly concerns with how to safely store credentials on NixOS. -The world readability of nix store means that, to safely store credentials, they must be first somehow be encrypted before written into the store. They also need to be decrypted before the services are started. +The world readability of nix store means that, to safely store credentials, they must first somehow be encrypted before written into the store. They also need to be decrypted before the services are started. This is less than ideal because one needs to setup a key (which is stored as plaintext on disk) on every machine just to prevent unauthorized users from seeting the credentials. From a62c605db9ec4e303f08bdf1a67571ffecb65850 Mon Sep 17 00:00:00 2001 From: poscat Date: Mon, 6 Feb 2023 14:19:46 +0800 Subject: [PATCH 10/13] clarify motivation --- rfcs/0141-private-derivations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index 104b1fec7..b5fb67d34 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -16,7 +16,7 @@ This RFC proposes to add a special type of derivation called private derivation, # Motivation [motivation]: #motivation -In short: This RFC mainly concerns with how to safely store credentials on NixOS. +In short: This RFC mainly concerns with how to safely manage credentials using nix (as opposed to using impure methods like manually copying them over) on NixOS. The world readability of nix store means that, to safely store credentials, they must first somehow be encrypted before written into the store. They also need to be decrypted before the services are started. From 2f389aaa7b3ac49b896451af0455ed02c83e9f9d Mon Sep 17 00:00:00 2001 From: poscat Date: Thu, 9 Feb 2023 10:14:53 +0800 Subject: [PATCH 11/13] clarify --- rfcs/0141-private-derivations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index b5fb67d34..bf00a7987 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -33,7 +33,7 @@ We propose adding a `noReadAccess` option to `builtins.derivation`, which, when The only difference between a private derivation and a normal derivation, apart from the hash, is that upon instantiation and after building, the read bit of -the `.drv` file and the output path will be removed. +the `.drv` file and the output path will be removed (recursively). # Examples and Interactions [examples-and-interactions]: #examples-and-interactions From f94d9724b7313b609f9c6d17186fbac97fbbc6d7 Mon Sep 17 00:00:00 2001 From: poscat Date: Thu, 16 May 2024 15:17:35 +0800 Subject: [PATCH 12/13] mention credential managers --- rfcs/0141-private-derivations.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index bf00a7987..a32a004ba 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -35,6 +35,12 @@ The only difference between a private derivation and a normal derivation, apart from the hash, is that upon instantiation and after building, the read bit of the `.drv` file and the output path will be removed (recursively). + +## Credential Managers + +In our design, the responsibility of access control is to be delegated to a separate process called the credential manager, which is a process with +the `CAP_DAC_OVERRIDE` capability. This frees us from further bloating the store model. + # Examples and Interactions [examples-and-interactions]: #examples-and-interactions From 328362aa7cec6a60abf00ad6e9529c240864c8cb Mon Sep 17 00:00:00 2001 From: poscat Date: Thu, 16 May 2024 15:26:46 +0800 Subject: [PATCH 13/13] mention potential attacks --- rfcs/0141-private-derivations.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rfcs/0141-private-derivations.md b/rfcs/0141-private-derivations.md index a32a004ba..f5aea3443 100644 --- a/rfcs/0141-private-derivations.md +++ b/rfcs/0141-private-derivations.md @@ -58,6 +58,10 @@ information get written into nix store in plaintext (for example the wpa_supplic [drawbacks]: #drawbacks - Adding private derivations further complicates the nix store model. +- In the intended use case, the credential remains plaintext at all time, and while the resulting derivation is unreadable, the inputs aren't. + This means the necessary materials that can be used to reconstruct the credential are exposed during build until the inputs are + garbage collected. A dedicated attacker can in theory monitor for file system changes, save the inputs before they are being GC'd + and later reconstruct the final derivation. # Alternatives [alternatives]: #alternatives