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:
parent
82315c3807
commit
b2c35b45d2
16 changed files with 653 additions and 3 deletions
|
|
@ -10,6 +10,7 @@ coreutils=@coreutils@
|
|||
|
||||
dot=@dot@
|
||||
busybox="@sandbox_shell@"
|
||||
pasta_path=@pasta_path@
|
||||
|
||||
version=@PACKAGE_VERSION@
|
||||
system=@system@
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
133
tests/functional/pasta-network.sh
Executable 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!"
|
||||
57
tests/functional/pasta-test.nix
Normal file
57
tests/functional/pasta-test.nix
Normal 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
|
||||
'
|
||||
'';
|
||||
};
|
||||
}
|
||||
93
tests/unit/libstore/pasta-test.cc
Normal file
93
tests/unit/libstore/pasta-test.cc
Normal 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__
|
||||
Loading…
Add table
Add a link
Reference in a new issue