Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lowdown: add flag to disable the Darwin sandbox #346945

Merged
merged 2 commits into from
Oct 8, 2024

Conversation

emilazy
Copy link
Member

@emilazy emilazy commented Oct 6, 2024

This is a program written in a memory‐unsafe language that processes potentially‐untrusted user input. We shouldn’t disable upstream’s sandboxing mechanisms for all downstream consumers without good reason.

Although the sandbox API is officially marked as deprecated, it is used as the basis for the supported App Sandbox and it is extremely unlikely to ever be removed as it is used extensively throughout the OS for service hardening and by third parties like the Chrome sandbox. Nix itself uses it to sandbox builds, and its lack of support for nesting is why this caused problems in the first place. Instead, introduce a lowdown-unsandboxed package that can be used in the nativeBuildInputs of Nix builds, while keeping the sandboxed version of the program for general use. The name might not be ideal, as it remains identical to lowdown on non‐Darwin platforms, but I couldn’t think of a better one.

See: #125004
Closes: #346933

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.11 Release Notes (or backporting 23.11 and 24.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

@ofborg ofborg bot added the 6.topic: darwin Running or building packages on Darwin label Oct 6, 2024
@emilazy
Copy link
Member Author

emilazy commented Oct 6, 2024

nixpkgs-vet failure is spurious.

@kirillrdy
Copy link
Member

nixpkgs-vet failure is spurious.

that's correct, new entries to all-packages.nix that use callPackage are not allowed

instead you can do something like

 lowdown-unsandboxed = lowdown.override {
    enableDarwinSandbox = false;
  };

@emilazy
Copy link
Member Author

emilazy commented Oct 6, 2024

That’s not true; it doesn’t complain about callPackage ../by-name/ab/abcd { … }, which is still perfectly valid, and has different semantics in the presence of overlays. The tool just isn’t smart enough to tell that the path to the package expression is an existing one in this case.

It might be reasonable to use .override here anyway, but it’s not a genuine violation of the by-name ratchet checks.

@emilazy
Copy link
Member Author

emilazy commented Oct 6, 2024

Result of nixpkgs-review pr 346945 run on aarch64-darwin 1

1 package marked as broken and skipped:
  • clightning
13 packages failed to build:
  • libnixxml
  • nix-du
  • nix-plugin-pijul
  • nix-serve-ng
  • nixtract
  • sbomnix
  • sbomnix.dist
  • update-python-libraries
  • vulnix
  • vulnix.dist
  • vulnix.doc
  • vulnix.man
  • zon2nix
152 packages built:
  • appvm
  • attic-client
  • attic-server
  • bower2nix
  • bundix
  • cabal2nix
  • cached-nix-shell
  • cachix (cachix.bin ,cachix.doc)
  • certspotter
  • colmena
  • common-updater-scripts
  • crate2nix
  • crystal2nix
  • devenv
  • dub-to-nix
  • dydisnix
  • harmonia
  • hci
  • hci.doc
  • hercules-ci-agent
  • home-manager
  • lix
  • lix.dev
  • lix.devdoc
  • lix.doc
  • lix.man
  • lixVersions.lix_2_90
  • lixVersions.lix_2_90.dev
  • lixVersions.lix_2_90.devdoc
  • lixVersions.lix_2_90.doc
  • lixVersions.lix_2_90.man
  • lowdown
  • lowdown-unsandboxed
  • lowdown-unsandboxed.dev
  • lowdown-unsandboxed.lib
  • lowdown-unsandboxed.man
  • lowdown.dev
  • lowdown.lib
  • lowdown.man
  • lua51Packages.luarocks-nix
  • lua52Packages.luarocks-nix
  • lua53Packages.luarocks-nix
  • lua54Packages.luarocks-nix
  • luajitPackages.luarocks-nix
  • luarocks-packages-updater
  • luarocks-packages-updater.dist
  • nil
  • nim_lk
  • niv (niv.bin ,niv.data)
  • nix (nixVersions.nix_2_18)
  • nix-bundle
  • nix-direnv
  • nix-eval-jobs
  • nix-fast-build
  • nix-fast-build.dist
  • nix-index
  • nix-init
  • nix-inspect
  • nix-pin
  • nix-plugins
  • nix-prefetch
  • nix-prefetch-bzr
  • nix-prefetch-cvs
  • nix-prefetch-docker
  • nix-prefetch-git
  • nix-prefetch-hg
  • nix-prefetch-scripts
  • nix-prefetch-svn
  • nix-required-mounts
  • nix-required-mounts.dist
  • nix-serve
  • nix-template
  • nix-unit
  • nix-update
  • nix-update-source
  • nix-update-source.dist
  • nix-update.dist
  • nix-visualize
  • nix-visualize.dist
  • nix-web
  • nix.dev (nixVersions.nix_2_18.dev)
  • nix.doc (nixVersions.nix_2_18.doc)
  • nix.man (nixVersions.nix_2_18.man)
  • nixVersions.git
  • nixVersions.git.dev
  • nixVersions.git.doc
  • nixVersions.git.man
  • nixVersions.latest
  • nixVersions.latest.dev
  • nixVersions.latest.doc
  • nixVersions.latest.man
  • nixVersions.minimum
  • nixVersions.minimum.dev
  • nixVersions.minimum.doc
  • nixVersions.minimum.man
  • nixVersions.nix_2_19
  • nixVersions.nix_2_19.dev
  • nixVersions.nix_2_19.doc
  • nixVersions.nix_2_19.man
  • nixVersions.nix_2_20
  • nixVersions.nix_2_20.dev
  • nixVersions.nix_2_20.doc
  • nixVersions.nix_2_20.man
  • nixVersions.nix_2_21
  • nixVersions.nix_2_21.dev
  • nixVersions.nix_2_21.doc
  • nixVersions.nix_2_21.man
  • nixVersions.nix_2_22
  • nixVersions.nix_2_22.dev
  • nixVersions.nix_2_22.doc
  • nixVersions.nix_2_22.man
  • nixVersions.nix_2_23
  • nixVersions.nix_2_23.dev
  • nixVersions.nix_2_23.doc
  • nixVersions.nix_2_23.man
  • nixci
  • nixd
  • nixos-anywhere
  • nixos-generators
  • nixos-option
  • nixos-rebuild
  • nixos-shell
  • nixpkgs-hammering
  • nixpkgs-manual
  • nixpkgs-review
  • nixpkgs-review.dist
  • nixt
  • nixt.dev
  • node2nix
  • npins
  • nuget-to-nix
  • nurl
  • nvfetcher
  • prefetch-yarn-deps
  • python311Packages.nix-kernel
  • python311Packages.nix-kernel.dist
  • python311Packages.nixpkgs
  • python311Packages.nixpkgs.dist
  • python311Packages.pythonix
  • python312Packages.nix-kernel
  • python312Packages.nix-kernel.dist
  • python312Packages.nixpkgs
  • python312Packages.nixpkgs.dist
  • python312Packages.pythonix
  • sonarr
  • swiftPackages.swiftpm2nix
  • terranix
  • typescript-language-server
  • update-nix-fetchgit
  • vimPluginsUpdater
  • wp4nix
  • yarn2nix

I believe the failures are pre‐existing.

@emilazy
Copy link
Member Author

emilazy commented Oct 6, 2024

I switched to an override so that users overlaying lowdown will have their customizations applied to lowdown-unsandboxed, which is probably what most people would want.

This is a program written in a memory‐unsafe language that processes
potentially‐untrusted user input. We shouldn’t disable upstream’s
sandboxing mechanisms for all downstream consumers without good
reason.

Although the sandbox API is officially marked as deprecated, it is
used as the basis for the supported App Sandbox and it is extremely
unlikely to ever be removed as it is used extensively throughout
the OS for service hardening and by third parties like the Chrome
sandbox. Nix itself uses it to sandbox builds, and its lack of support
for nesting is why this caused problems in the first place. Instead,
introduce a `lowdown-unsandboxed` package that can be used in the
`nativeBuildInputs` of Nix builds, while keeping the sandboxed
version of the program for general use. The name might not be ideal,
as it remains identical to `lowdown` on non‐Darwin platforms,
but I couldn’t think of a better one.

See: NixOS#125004
Closes: NixOS#346933
Copy link
Contributor

@fricklerhandwerk fricklerhandwerk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me to make it explicit

@kirillrdy
Copy link
Member

@emilazy yes, you are correct, it should be allowed but i interpreted requirement as implementation, so it made sense to me that you can't add callPackage for non by-name imports

@reckenrode reckenrode merged commit 56b9fe7 into NixOS:master Oct 8, 2024
27 checks passed
@emilazy emilazy deleted the push-wpmkxnzkkzpr branch October 8, 2024 00:44
@lf-
Copy link
Member

lf- commented Oct 14, 2024

I think the actually reasonable thing that should have been done here is to make sandbox setup failure not fatal in lowdown. Disabling it completely isn't great, and nor is the current situation, where things like Lix which iirc use lowdown at runtime and at build time had their builds broken and also cannot use the sandbox (see: https://git.lix.systems/lix-project/lix/issues/547)

@emilazy
Copy link
Member Author

emilazy commented Oct 14, 2024

@lf- Sorry, I should have sent a change to the Lix flake.nix. However, it’s easy to fix, as you can see from the diff to the Lix and Nix expressions in this PR; lowdown already has to be listed separately for build‐ and runtime due to cross‐compilation, so the solution is to simply use lowdown-unsandboxed in nativeBuildInputs and lowdown in buildInputs. If you do that, there should be no issues, given that the Nixpkgs channel has advanced fine after this PR. I don’t think patching the upstream code to silently be less secure when sandbox fails to be created is a good idea for maintainability or security.

@emilazy
Copy link
Member Author

emilazy commented Oct 14, 2024

I think the Nix flake will also probably need fixing. I don’t think I’ll have time for that today, but maybe tomorrow if nobody beats me to it.

@lf-
Copy link
Member

lf- commented Oct 14, 2024

We can fix Lix but the problem is that it will break NixOS 24.05 assuming we don't put in funny feature detection code because -unsandboxed exists only on unstable. That's the real issue with this sort of change for stuff like Lix which needs to be compatible with both branches.

@emilazy
Copy link
Member Author

emilazy commented Oct 14, 2024

Does e.g.

{
  lowdown,
  lowdown-unsandboxed ? lowdown,
}:

…

work for you?

@lf-
Copy link
Member

lf- commented Oct 14, 2024

I am about to try that, but I think this is still fundamentally busted without any override, since we quite plausibly might test lowdown-using functionality in installCheckPhase. That being said, it does seem like the offending sandbox should only be applied to the CLI so maybe it's okay? It still gives me a bad feeling that tools have the potential to arbitrarily not work in the nix sandbox if someone puts the path to the CLI in a wrapper script or whatever.

And as for the sandbox setup failing being fatal, it should be changed to print a warning IMO. The sandbox isn't fundamental to it working properly, it's not that weird for it to be unavailable (just like Linux's user namespaces!), and we have very bad CI testing of the Nix sandbox on macOS in general, so it seems like it's likely to be a liability in the future. Or if it is going to be fatal at least there needs to be an env-var to override it or so and we will have another __darwinAllowLocalNetworking equivalent thing I guess.

@emilazy
Copy link
Member Author

emilazy commented Oct 14, 2024

It only applies to the CLI, yeah. I’m not really sure what override we could provide for install checks other than the option this already introduces to disable the sandboxing. The macOS sandbox just doesn’t support nesting AFAIK, so either you have code that never invokes the sandbox, test code other than what you install, or it can’t run in the sandbox. Lots of stuff doesn’t really work well in the macOS sandbox, ultimately, and we don’t have clear ways to fix that for many of them. But at least downstream derivations can make any of those possible choices after this PR.

I’m not sure what you mean about the sandbox setup being fatal and environment variables. It’s lowdown that fails to start up, not Nix.

@emilazy
Copy link
Member Author

emilazy commented Oct 14, 2024

We could make it so that lowdown-unsandboxed still tries to initialize the sandbox and fails gracefully, to improve the situation for unsandboxed Nix builds. I just didn’t bother doing it here, as this never worsens the security properties compared to before this PR, and it needed fixing for the Darwin rework anyway, but I’d be happy to review. It’d still be necessary to use lowdown-unsandboxed in nativebuildInputs.

Edit: Actually, this is a waste of time because I forgot that even “unsandboxed” builds use sandbox-exec.

@lf-
Copy link
Member

lf- commented Oct 14, 2024

I’m not sure what you mean about the sandbox setup being fatal and environment variables. It’s lowdown that fails to start up, not Nix.

Yes, so lowdown should not completely fail in a way that requires all package maintainers paying attention to macOS by having to have two separate versions of it in a way that cannot be correct for any dependents-of-dependents. This PR adds extra configuration in a way that only affects macOS and breaks builds in ways that may only happen in sandboxed Nix builds. Furthermore, it might only affect a second order consumer of lowdown: perhaps there is a lowdown wrapper script in nixpkgs; there is no way to correctly write that package, except by always using the unsandboxed variant, if that package can be used by any other packages at build time.

This whole configuration mess is solved by not having sandbox setup failure be fatal, either by requiring an override environment variable to allow that condition (which then just can be set by any second-order dependents), or by issuing a warning and proceeding anyhow.

The solution of having two packages adds more work to everyone on a platform that already has packages randomly break too much due to needing macOS-only changes. This is done in order to "improve security", which would also be achieved by just trying to enable the sandbox every time and just not dying if it doesn't work. I don't see there being any realistic case where even failing sandbox setup silently (let alone with a warning) would be a security issue; executing lowdown from a sandbox is basically only ever going to happen in a nix build, in which case you should not be processing malicious markdown docs. I don't see any conceivable case where the macOS sandbox state and markdown input would be controlled by an attacker without them simply having code execution already.

@emilazy
Copy link
Member Author

emilazy commented Oct 14, 2024

that may only happen in sandboxed Nix builds

They happen in all builds on macOS because the sandbox is unconditionally initialized; unsandboxed builds just use a minimal profile. It’s not possible to hold wrong here unless you don’t test on macOS at all. If an executable that wraps lowdown should itself be used in downstream Nix derivations, then lowdown-unsandboxed can be used in that one. I agree that in that case, lowdown-unsandboxed still trying to initialize the sandbox and allowing failure (perhaps with a warning) would be a security improvement from the state before and after this PR, and I’d be happy to review that change if it’s not too invasive a patch.

Previously, such packages would always be using the unsandboxed version on aarch64-darwin, so using lowdown-unsandboxed there would not be a regression from the state before this PR, which I kept minimal because some change was necessary as part of the extensive follow‐up work to the Darwin platform improvements in #346043 and I thought being explicit about intent was better than the approach of extending the previous bad stripping of the sandboxing to more platforms in #346933.

I really think that upstream should be making the decisions about how lowdown sandboxes itself, though, rather than us weakening it in ways that can potentially help an exploit chain. It’s probably a marginal case, but I think a pretty strong norm against doing patches to weaken upstream sandboxing is a good thing. If upstream is okay making it fail silently, then I’m okay with it too, but if not then I don’t think we should be making that decision.

@emilazy
Copy link
Member Author

emilazy commented Oct 14, 2024

The solution of having two packages adds more work to everyone on a platform that already has packages randomly break too much due to needing macOS-only changes.

FWIW, all this work is directly aimed towards making far more packages build out‐of‐the‐box on macOS and significantly reducing the maintenance burden of macOS support both in Nixpkgs and for downstream derivations, which it is already starting to pay dividends for even before making it out of staging. But I admit that it is sometimes hard to stay motivated in contributing to that work given the hostility.

@lf-
Copy link
Member

lf- commented Oct 14, 2024

Apologies for my frustration. I realize that I am currently a bit emotionally destabilized and burned out and I was very unreasonable in this thread. I will do my best to fix the conditions that led to this (chiefly trying to push back more on being the backstop maintainer) and do better in the future.

Thank you for your work in making it more maintainable, and I hope after we get through the growing pains, it will be easier to deal with.

I will file an issue upstream to get their input on possibly changing the sandbox setup design; it looks like a possible oversight.

@emilazy
Copy link
Member Author

emilazy commented Oct 14, 2024

Thank you; I sincerely do appreciate that. I know upstream backwards‐incompatibilities are frustrating, whether motivated by security or not, and I had made a mental note to check if I needed to mirror the changes in the Lix and Nix flakes that I neglected to follow up on. I also know that macOS support has been a persistent pain point for the ecosystem, and that both Apple decisions and Nixpkgs neglect have made it difficult to improve for a long time. I’m hoping that 24.11 and 25.05 will be vast improvements in the state of support, but I understand that a lot of people already feel fed up with the platform and that it’ll be on us to prove its newfound health.

FWIW, I agree that it seems relatively unlikely that a fail‐open approach to sandboxing could be a critical security failure, but when an upstream has explicitly written code to sandbox itself and fail if it can’t be initialized, I’m very hesitant to go against that more than is absolutely necessary. It’s hard to predict what states people will be able to direct weird machines into, and I don’t want a Nixpkgs decision to one day end up as one of the nodes in a chain like this. How exactly to handle sandboxing in programs that process untrusted user input isn’t our area of expertise, so I feel it is best to make any divergence upstream as localized as possible when we absolutely must do it because of technical limitations.

Sadly, in this case, that interacts with the limitations of Apple’s APIs in such a way as to force consumers to make explicit decisions about the trade‐off. I think in the absence of the upstream decision it’s better for us to expose the trade‐off so downstreams can decide what’s best for them, but it’s certainly an awkward situation. Sandboxing APIs should be able to nest and it sucks that this one can’t.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants