1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-12-09 02:21:02 +01:00

pasta: wip

TODO: add original authors as commit authors
This commit is contained in:
Jörg Thalheim 2025-07-24 13:17:58 +02:00
parent 82315c3807
commit b2c35b45d2
16 changed files with 653 additions and 3 deletions

View file

@ -10,6 +10,7 @@ coreutils=@coreutils@
dot=@dot@
busybox="@sandbox_shell@"
pasta_path=@pasta_path@
version=@PACKAGE_VERSION@
system=@system@

View file

@ -21,6 +21,7 @@ busybox = find_program('busybox', native : true, required : false)
# guaranteed to exist either.
coreutils = find_program('ls', native : true)
dot = find_program('dot', native : true, required : false)
pasta_path = find_program('pasta', native : false, required : false)
nix_bin_dir = fs.parent(nix.full_path())
@ -35,6 +36,15 @@ test_confdata = {
'PACKAGE_VERSION' : meson.project_version(),
'system' : nix_system_cpu + '-' + host_machine.system(),
}
if pasta_path.found()
test_confdata += {
'pasta_path': pasta_path.full_path(),
}
else
test_confdata += {
'pasta_path': '',
}
endif
# Just configures `common/vars-and-functions.sh.in`.
# Done as a subdir() so Meson places it under `common` in the build directory as well.
@ -127,6 +137,7 @@ suites = [
'misc.sh',
'dump-db.sh',
'linux-sandbox.sh',
'pasta-network.sh',
'supplementary-groups.sh',
'build-dry.sh',
'structured-attrs.sh',

View file

@ -12,6 +12,7 @@
mercurial,
util-linux,
unixtools,
passt,
nix-store,
nix-expr,
@ -56,7 +57,8 @@ mkMesonDerivation (
git
mercurial
unixtools.script
]
passt
]
++ lib.optionals stdenv.hostPlatform.isLinux [
# For various sandboxing tests that needs a statically-linked shell,
# etc.

133
tests/functional/pasta-network.sh Executable file
View file

@ -0,0 +1,133 @@
#!/usr/bin/env bash
source common.sh
# This test requires Linux sandbox support and pasta
needLocalStore "the sandbox only runs on the builder side"
requireSandboxSupport
requiresUnprivilegedUserNamespaces
# Skip test if pasta is not configured or available
PASTA_PATH="${pasta_path:-}"
if [[ -z "$PASTA_PATH" ]] || [[ "$PASTA_PATH" == "pasta" ]] || [[ ! -x "$PASTA_PATH" ]]; then
skipTest "pasta is not available (pasta_path=$PASTA_PATH)"
fi
# Ensure pasta is in a standard location that Nix can access
# If pasta is in a non-standard location, we need to add it to sandbox-paths
PASTA_DIR=$(dirname "$PASTA_PATH")
export NIX_SANDBOX_PATHS="$PASTA_DIR=$PASTA_DIR"
# Skip test if /dev/net/tun is not available (required for pasta)
if [[ ! -e /dev/net/tun ]]; then
skipTest "/dev/net/tun not available"
fi
clearStore
# Test that fixed-output derivations can access the network when pasta is enabled
echo 'testing fixed-output derivation with network access...'
# Create a test derivation that tries to access the network
cat > pasta-test.nix <<'EOF'
with import ./config.nix;
{
# Test basic network functionality with a fixed-output derivation
testNetworkAccess = mkDerivation {
name = "test-network-access";
builder = builtins.toFile "builder.sh" ''
${bash}/bin/bash -c '
# Test basic network connectivity
# Try to resolve a hostname
if getent hosts localhost >/dev/null 2>&1; then
echo "DNS resolution works"
else
echo "DNS resolution failed"
exit 1
fi
# Test if we can see network interfaces
if ${coreutils}/bin/test -e /sys/class/net/eth0; then
echo "Network interface eth0 exists"
else
echo "Network interface eth0 missing"
exit 1
fi
# Create output
echo "Network test passed" > $out
'
'';
outputHashMode = "flat";
outputHashAlgo = "sha256";
outputHash = "sha256-YCa7ssqLHbdFkPJEG4REJJbsZF9g3w1i+Eg21nUYCCk=";
};
# Test that non-fixed-output derivations cannot access the network
testNoNetworkAccess = mkDerivation {
name = "test-no-network-access";
builder = builtins.toFile "builder.sh" ''
${bash}/bin/bash -c '
# This should fail because non-fixed-output derivations
# should not have network access
if getent hosts localhost >/dev/null 2>&1; then
echo "ERROR: DNS resolution works but should not!"
exit 1
fi
# There should be no network interfaces
if ${coreutils}/bin/test -e /sys/class/net/eth0; then
echo "ERROR: Network interface exists but should not!"
exit 1
fi
echo "Network properly isolated" > $out
'
'';
};
}
EOF
# Test with pasta enabled
echo "Setting pasta-path for network isolation..."
NIX_CONFIG="pasta-path = $PASTA_PATH
sandbox-paths = $NIX_SANDBOX_PATHS" \
nix-build pasta-test.nix -A testNetworkAccess --no-out-link
# Test that non-fixed-output derivations are still isolated
echo "Testing non-fixed-output derivation isolation..."
nix-build pasta-test.nix -A testNoNetworkAccess --no-out-link
# Test that pasta process is properly cleaned up
echo "Testing pasta process cleanup..."
cat > pasta-cleanup-test.nix <<'EOF'
with import ./config.nix;
mkDerivation {
name = "pasta-cleanup-test";
builder = builtins.toFile "builder.sh" ''
${bash}/bin/bash -c '
# Just create output
echo "test" > $out
'
'';
outputHashMode = "flat";
outputHashAlgo = "sha256";
outputHash = "sha256-n4xS51kG4lw0bKJl5VUkJptBS0EbV8LPHZkFV3RJQBU=";
}
EOF
# Build with pasta and check that no pasta processes remain
PASTA_COUNT_BEFORE=$(pgrep -c pasta || echo 0)
NIX_CONFIG="pasta-path = $PASTA_PATH
sandbox-paths = $NIX_SANDBOX_PATHS" \
nix-build pasta-cleanup-test.nix --no-out-link
PASTA_COUNT_AFTER=$(pgrep -c pasta || echo 0)
if [[ $PASTA_COUNT_AFTER -gt $PASTA_COUNT_BEFORE ]]; then
echo "ERROR: pasta process was not cleaned up properly"
exit 1
fi
echo "pasta network isolation tests passed!"

View file

@ -0,0 +1,57 @@
with import ./config.nix;
{
# Test basic network functionality with a fixed-output derivation
testNetworkAccess = mkDerivation {
name = "test-network-access";
builder = builtins.toFile "builder.sh" ''
${bash}/bin/bash -c '
# Test basic network connectivity
# Try to resolve a hostname
if getent hosts localhost >/dev/null 2>&1; then
echo "DNS resolution works"
else
echo "DNS resolution failed"
exit 1
fi
# Test if we can see network interfaces
if ${coreutils}/bin/test -e /sys/class/net/eth0; then
echo "Network interface eth0 exists"
else
echo "Network interface eth0 missing"
exit 1
fi
# Create output
echo "Network test passed" > $out
'
'';
outputHashMode = "flat";
outputHashAlgo = "sha256";
outputHash = "sha256-YCa7ssqLHbdFkPJEG4REJJbsZF9g3w1i+Eg21nUYCCk=";
};
# Test that non-fixed-output derivations cannot access the network
testNoNetworkAccess = mkDerivation {
name = "test-no-network-access";
builder = builtins.toFile "builder.sh" ''
${bash}/bin/bash -c '
# This should fail because non-fixed-output derivations
# should not have network access
if getent hosts localhost >/dev/null 2>&1; then
echo "ERROR: DNS resolution works but should not!"
exit 1
fi
# There should be no network interfaces
if ${coreutils}/bin/test -e /sys/class/net/eth0; then
echo "ERROR: Network interface exists but should not!"
exit 1
fi
echo "Network properly isolated" > $out
'
'';
};
}

View file

@ -0,0 +1,93 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "nix/store/globals.hh"
#include "nix/util/processes.hh"
#include "nix/util/file-system.hh"
#ifdef __linux__
#include "nix/unix/build/pasta.hh"
namespace nix {
TEST(PastaTest, ConstantsAreDefined) {
// Test that all pasta constants are properly defined
EXPECT_STREQ(pasta::PASTA_NS_IFNAME, "eth0");
EXPECT_STREQ(pasta::PASTA_HOST_IPV4, "169.254.1.1");
EXPECT_STREQ(pasta::PASTA_CHILD_IPV4, "169.254.1.2");
EXPECT_STREQ(pasta::PASTA_PREFIX_IPV4, "30");
}
TEST(PastaTest, RewriteResolvConfBasic) {
// Test basic resolv.conf rewriting
std::string original = R"(
# Generated by NetworkManager
nameserver 8.8.8.8
nameserver 8.8.4.4
search example.com
)";
std::string expected = R"(
# Generated by NetworkManager
nameserver 169.254.1.1
nameserver 169.254.1.1
search example.com
)";
std::string result = pasta::rewriteResolvConf(original);
EXPECT_EQ(result, expected);
}
TEST(PastaTest, RewriteResolvConfEmpty) {
// Test empty resolv.conf
std::string original = "";
std::string result = pasta::rewriteResolvConf(original);
EXPECT_EQ(result, "");
}
TEST(PastaTest, RewriteResolvConfComments) {
// Test resolv.conf with only comments
std::string original = R"(# This is a comment
# Another comment
)";
std::string result = pasta::rewriteResolvConf(original);
EXPECT_EQ(result, original);
}
TEST(PastaTest, RewriteResolvConfMixed) {
// Test resolv.conf with various directives
std::string original = R"(
nameserver 192.168.1.1
domain local.lan
search local.lan corp.lan
nameserver 192.168.1.2
options ndots:1
)";
std::string expected = R"(
nameserver 169.254.1.1
domain local.lan
search local.lan corp.lan
nameserver 169.254.1.1
options ndots:1
)";
std::string result = pasta::rewriteResolvConf(original);
EXPECT_EQ(result, expected);
}
TEST(PastaTest, SettingIsConfigurable) {
// Test that pasta path setting can be configured
Settings settings;
// Default should be empty
EXPECT_EQ(settings.pastaPath.get(), "");
// Should be settable
settings.pastaPath = "/usr/bin/pasta";
EXPECT_EQ(settings.pastaPath.get(), "/usr/bin/pasta");
}
} // namespace nix
#endif // __linux__