diff --git a/app/child.go b/app/child.go index 0835499..39a52de 100644 --- a/app/child.go +++ b/app/child.go @@ -1,19 +1,66 @@ package app import ( + "context" "fmt" "log" "log/slog" "os" "os/exec" + "syscall" + "time" + "github.com/cenkalti/backoff/v5" "github.com/coder/boundary/jail" + "golang.org/x/sys/unix" ) +// waitForInterface waits for a network interface to appear in the namespace. +// It retries checking for the interface with exponential backoff up to the specified timeout. +func waitForInterface(interfaceName string, timeout time.Duration) error { + b := backoff.NewExponentialBackOff() + b.InitialInterval = 50 * time.Millisecond + b.MaxInterval = 500 * time.Millisecond + b.Multiplier = 2.0 + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + operation := func() (bool, error) { + cmd := exec.Command("ip", "link", "show", interfaceName) + cmd.SysProcAttr = &syscall.SysProcAttr{ + AmbientCaps: []uintptr{uintptr(unix.CAP_NET_ADMIN)}, + } + + err := cmd.Run() + if err != nil { + return false, fmt.Errorf("interface %s not found: %w", interfaceName, err) + } + // Interface exists + return true, nil + } + + _, err := backoff.Retry(ctx, operation, backoff.WithBackOff(b)) + if err != nil { + return fmt.Errorf("interface %s did not appear within %v: %w", interfaceName, timeout, err) + } + + return nil +} + func RunChild(logger *slog.Logger, args []string) error { logger.Info("boundary CHILD process is started") vethNetJail := os.Getenv("VETH_JAIL_NAME") + if vethNetJail == "" { + return fmt.Errorf("VETH_JAIL_NAME environment variable is not set") + } + + // Wait for the veth interface to be moved into the namespace by the parent process + if err := waitForInterface(vethNetJail, 5*time.Second); err != nil { + return fmt.Errorf("failed to wait for interface %s: %w", vethNetJail, err) + } + err := jail.SetupChildNetworking(vethNetJail) if err != nil { return fmt.Errorf("failed to setup child networking: %v", err) diff --git a/go.mod b/go.mod index 5fbfc46..bcc62e7 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/coder/boundary go 1.24.0 require ( + github.com/cenkalti/backoff/v5 v5.0.3 github.com/coder/serpent v0.10.0 github.com/stretchr/testify v1.8.4 golang.org/x/sys v0.38.0 diff --git a/go.sum b/go.sum index 62cb6d2..2cc0b51 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tE cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU= github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs=