From 1b0515d9c9c55e29ebaa52ea344a24745ef84629 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 20 May 2024 11:55:48 +0200 Subject: [PATCH 1/6] fix: Create missing target directories before bind mounting Signed-off-by: Steffen Vogel --- pkg/cmd_test.go | 16 ++++++++++++++++ pkg/exec.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/pkg/cmd_test.go b/pkg/cmd_test.go index 5d64321..18aa5d1 100644 --- a/pkg/cmd_test.go +++ b/pkg/cmd_test.go @@ -13,6 +13,7 @@ import ( g "cunicu.li/gont/v2/pkg" co "cunicu.li/gont/v2/pkg/options/cmd" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -152,3 +153,18 @@ func TestCmdContext(t *testing.T) { require.True(t, ws.Signaled()) require.Equal(t, syscall.SIGKILL, ws.Signal()) } + +func TestIProute2Files(t *testing.T) { + n, err := g.NewNetwork("") + require.NoError(t, err, "Failed to create network") + defer n.Close() + + beep, err := n.AddHost("beep") + require.Nil(t, err) + + cmd := beep.Command("ip", "addr") + out, err := cmd.CombinedOutput() + assert.Nil(t, err) + + t.Logf("Output: %s", out) +} diff --git a/pkg/exec.go b/pkg/exec.go index 3dfdb6e..304320e 100644 --- a/pkg/exec.go +++ b/pkg/exec.go @@ -4,6 +4,7 @@ package gont import ( + "errors" "fmt" "os" "path/filepath" @@ -11,6 +12,7 @@ import ( "syscall" "cunicu.li/gont/v2/internal/execvpe" + "cunicu.li/gont/v2/internal/utils" "golang.org/x/sys/unix" ) @@ -120,6 +122,32 @@ func setupBindMounts(basePath string) error { src := filepath.Join(filesRootPath, path) tgt := filepath.Join("/", path) + srcInfo, err := os.Stat(src) + if err != nil { + return fmt.Errorf("failed to stat source: %s: %w", src, err) + } + + // Create non-existing targets + if _, err := os.Stat(tgt); errors.Is(err, os.ErrNotExist) { //nolint:nestif + var tgtDir string + if srcInfo.IsDir() { + tgtDir = tgt + } else { + tgtDir = filepath.Dir(tgt) + } + + // Create target if it does not exist yet + if srcInfo.IsDir() { + if err := os.MkdirAll(tgtDir, 0o755); err != nil { + return fmt.Errorf("failed to create directory: %s: %w", path, err) + } + } else { + if err := utils.Touch(tgt); err != nil { + return fmt.Errorf("failed to create empty file: %s: %w", tgt, err) + } + } + } + if err := syscall.Mount(src, tgt, "", syscall.MS_BIND, ""); err != nil { return fmt.Errorf("failed to mount: %w", err) } From 4a123f92f34fdc6b79c5272d57159cde27fc1250 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 20 May 2024 15:35:15 +0200 Subject: [PATCH 2/6] Allow simple pass-through of environment variables Signed-off-by: Steffen Vogel --- pkg/options/cmd/exec.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/options/cmd/exec.go b/pkg/options/cmd/exec.go index b0faa6a..35fa68a 100644 --- a/pkg/options/cmd/exec.go +++ b/pkg/options/cmd/exec.go @@ -75,11 +75,17 @@ func (e Env) ApplyExecCmd(c *exec.Cmd) { c.Env = append(c.Env, string(e)) } -// EnvVar appends a key-value paired environment variable +// EnvVar appends a key-value paired environment variable. func EnvVar(k, v string) Env { return Env(fmt.Sprintf("%s=%s", k, v)) } +// PassEnv forward environment variables from the main +// Gont process to the invoked sub-processes. +func PassEnv(k string) Env { + return EnvVar(k, os.Getenv(k)) +} + // Envs appends additional environment variables. type Envs []string From ffc317c171f50d21411617f0fb505312e3f3527f Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 20 May 2024 15:37:55 +0200 Subject: [PATCH 3/6] Minor cleanups Signed-off-by: Steffen Vogel --- pkg/base_node.go | 2 +- pkg/nat_http_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/base_node.go b/pkg/base_node.go index 93f923a..3e63cb6 100644 --- a/pkg/base_node.go +++ b/pkg/base_node.go @@ -73,7 +73,7 @@ func (n *Network) AddNode(name string, opts ...Option) (*BaseNode, error) { } } - // Create mount point dirs + // Create mount point directories for _, ed := range node.EmptyDirs { path := filepath.Join(basePath, "files", ed) diff --git a/pkg/nat_http_test.go b/pkg/nat_http_test.go index faf39d6..177ffba 100644 --- a/pkg/nat_http_test.go +++ b/pkg/nat_http_test.go @@ -62,7 +62,7 @@ func TestGetMyIP(t *testing.T) { require.NoError(t, err, "Failed to setup default route") outp := &bytes.Buffer{} - _, err = client.Run("curl", "-sk", "--connect-timeout", 1000, "https://server", + _, err = client.Run("curl", "--silent", "--insecure", "--connect-timeout", 1000, "https://server", co.Stdout(outp)) require.NoError(t, err, "Request failed") From 75c0848ea283030622443a9731cd7242ded7df26 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 20 May 2024 15:42:11 +0200 Subject: [PATCH 4/6] Fix interoperability with systemd-resolved Signed-off-by: Steffen Vogel --- internal/utils/utils.go | 5 ++ pkg/network_files.go | 103 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/internal/utils/utils.go b/internal/utils/utils.go index a1cc35a..c9fc702 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -6,9 +6,14 @@ package utils import ( "math/rand" "os" + "path/filepath" ) func Touch(path string) error { + if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { + return err + } + f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL, 0o444) if err != nil { return err diff --git a/pkg/network_files.go b/pkg/network_files.go index f80b6ce..d76c32c 100644 --- a/pkg/network_files.go +++ b/pkg/network_files.go @@ -4,12 +4,16 @@ package gont import ( + "bufio" "fmt" "io" "net" "os" "path/filepath" + "slices" "strings" + + "cunicu.li/gont/v2/internal/utils" ) // IPv4loopback is the IPv4 loopback address (127.0.0.1) @@ -90,10 +94,105 @@ func (n *Network) WriteHostsFile(f io.Writer) error { } func (n *Network) GenerateConfigFiles() error { - return n.GenerateIProute2Files() + if err := n.generateIProute2Files(); err != nil { + return err + } + + // We need to patch /etc/nsswitch.conf here + // to avoid using systemd-resolved here as its + // broken from within network namesapces + if err := n.patchNSSConfFile(); err != nil { + return err + } + + // We also need to hide the NSCD socket as glibc + // will otherwise use NSCD and hence again the resolve + // NSS module provided by systemd-resolved. + if err := n.hideNSCDSocket(); err != nil { + return err + } + + return nil +} + +func (n *Network) hideNSCDSocket() error { + fn := filepath.Join(n.VarPath, "files/var/run/nscd/socket") + + // We hide the NSCD socket here by bind mounting + // an empty file over its location. + return utils.Touch(fn) +} + +func readNSSwitchConfig(fn string) (map[string][]string, error) { + f, err := os.Open(fn) + if err != nil { + return nil, err + } + + m := map[string][]string{} + s := bufio.NewScanner(f) + + for s.Scan() { + line := s.Text() + + if strings.HasPrefix(line, "#") { + continue // Skip comments + } + + cols := strings.Split(line, ":") + + db := cols[0] + srcs := cols[1:] + + m[db] = srcs + } + + if err := s.Err(); err != nil { + return nil, err + } + + return nil, nil +} + +func writeNSSwitchConfig(fn string, config map[string][]string) error { + f, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) + if err != nil { + return err + } + defer f.Close() + + if _, err = fmt.Fprintln(f, "# Gont's patched nsswitch.conf"); err != nil { + return err + } + + for db, srcs := range config { + if _, err := fmt.Fprintf(f, "%s: %s\n", db, strings.Join(srcs, " ")); err != nil { + return err + } + } + + return err +} + +func (n *Network) patchNSSConfFile() error { + cfg, err := readNSSwitchConfig("/etc/nsswitch.conf") + if err != nil { + return fmt.Errorf("failed to read nsswitch.conf: %w", err) + } + + for db := range cfg { + if db == "hosts" { + cfg[db] = slices.DeleteFunc(cfg[db], func(src string) bool { + return !strings.HasPrefix(src, "resolve") && !strings.HasPrefix(src, "mymachines") && !strings.HasPrefix(src, "myhostname") + }) + } + } + + fn := filepath.Join(n.VarPath, "files/etc/nsswitch.conf") + return writeNSSwitchConfig(fn, cfg) } -func (n *Network) GenerateIProute2Files() error { +func (n *Network) generateIProute2Files() error { fn := filepath.Join(n.VarPath, "files/etc/iproute2/group") if err := os.MkdirAll(filepath.Dir(fn), 0o755); err != nil { return err From 31d0164bc935d3e43313b973457ecc8493d42761 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 20 May 2024 15:44:00 +0200 Subject: [PATCH 5/6] nix: Switch to nixfmt for formatting of Flake Signed-off-by: Steffen Vogel --- flake.nix | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/flake.nix b/flake.nix index 9d6a9fa..ace243d 100644 --- a/flake.nix +++ b/flake.nix @@ -8,23 +8,23 @@ nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; }; - outputs = { - self, - flake-utils, - nixpkgs, - }: - flake-utils.lib.eachDefaultSystem - ( - system: let + outputs = + { + self, + flake-utils, + nixpkgs, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let pkgs = nixpkgs.legacyPackages.${system}; - in rec { + in + rec { packages.gont = pkgs.buildGoModule { name = "gont"; src = ./.; vendorHash = "sha256-QOh1jBR7FL/fKFmJv7wGxuCghRLR3DV/0TzXd+bUFP0="; - buildInputs = with pkgs; [ - libpcap - ]; + buildInputs = with pkgs; [ libpcap ]; doCheck = false; }; @@ -38,12 +38,10 @@ packages.gont ]; - inputsFrom = [ - packages.gont - ]; + inputsFrom = [ packages.gont ]; }; - formatter = nixpkgs.alejandra; + formatter = nixpkgs.nixfmt-rfc-style; } ); } From 40b8c733ba9a9bee80daf79f596bfe62c814d235 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 20 May 2024 15:44:12 +0200 Subject: [PATCH 6/6] nix: Update Flake inputs Signed-off-by: Steffen Vogel --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 942e223..5e9fe1f 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1711163522, - "narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=", + "lastModified": 1716137900, + "narHash": "sha256-sowPU+tLQv8GlqtVtsXioTKeaQvlMz/pefcdwg8MvfM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4", + "rev": "6c0b7a92c30122196a761b440ac0d46d3d9954f1", "type": "github" }, "original": {