|
Packit Service |
509fd4 |
// +build integration
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
package boot
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
import (
|
|
Packit Service |
509fd4 |
"context"
|
|
Packit Service |
509fd4 |
"fmt"
|
|
Packit Service |
509fd4 |
"io/ioutil"
|
|
Packit Service |
509fd4 |
"log"
|
|
Packit Service |
509fd4 |
"os"
|
|
Packit Service |
509fd4 |
"os/exec"
|
|
Packit Service |
509fd4 |
"path"
|
|
Packit Service |
509fd4 |
"runtime"
|
|
Packit Service |
509fd4 |
"syscall"
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
"golang.org/x/sys/unix"
|
|
Packit Service |
509fd4 |
)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
const netnsDir = "/var/run/netns"
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Network namespace abstraction
|
|
Packit Service |
509fd4 |
type NetNS string
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// newNetworkNamespace returns a new network namespace with a random
|
|
Packit Service |
509fd4 |
// name. The calling goroutine remains in the same namespace
|
|
Packit Service |
509fd4 |
// as before the call.
|
|
Packit Service |
509fd4 |
func newNetworkNamespace() (NetNS, error) {
|
|
Packit Service |
509fd4 |
// This method needs to unshare the current thread. Go runtime can switch
|
|
Packit Service |
509fd4 |
// the goroutine to run on a different thread at any point, so we need
|
|
Packit Service |
509fd4 |
// to ensure that this method runs in the same thread for its whole
|
|
Packit Service |
509fd4 |
// lifetime.
|
|
Packit Service |
509fd4 |
runtime.LockOSThread()
|
|
Packit Service |
509fd4 |
defer runtime.UnlockOSThread()
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
_, err := os.Stat(netnsDir)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
if os.IsNotExist(err) {
|
|
Packit Service |
509fd4 |
err := os.Mkdir(netnsDir, 0755)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("cannot create %s: %v", netnsDir, err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("cannot stat %s: %v", netnsDir, err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
f, err := ioutil.TempFile(netnsDir, "osbuild-composer-namespace")
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("cannot create a tempfile: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// We want to remove the temporary file if the namespace initialization fails.
|
|
Packit Service |
509fd4 |
// The best method I could thought of is to have the following variable
|
|
Packit Service |
509fd4 |
// denoting if the initialization was successful. It is set to true right
|
|
Packit Service |
509fd4 |
// before the end of this function.
|
|
Packit Service |
509fd4 |
initOK := false
|
|
Packit Service |
509fd4 |
defer func() {
|
|
Packit Service |
509fd4 |
if !initOK {
|
|
Packit Service |
509fd4 |
err := os.Remove(f.Name())
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
log.Printf("cannot remove the temporary namespace: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}()
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
oldNS, err := os.Open("/proc/self/ns/net")
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("cannot open the current namespace: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
err = syscall.Unshare(syscall.CLONE_NEWNET)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("cannot unshare the network namespace")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
defer func() {
|
|
Packit Service |
509fd4 |
err = unix.Setns(int(oldNS.Fd()), syscall.CLONE_NEWNET)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
// We cannot return to the original namespace.
|
|
Packit Service |
509fd4 |
// As we don't know nothing about affected threads, let's just
|
|
Packit Service |
509fd4 |
// quit immediately.
|
|
Packit Service |
509fd4 |
log.Fatalf("returning to the original namespace failed, quitting: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}()
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
cmd := exec.Command("ip", "link", "set", "up", "dev", "lo")
|
|
Packit Service |
509fd4 |
cmd.Stderr = os.Stderr
|
|
Packit Service |
509fd4 |
cmd.Stdout = os.Stderr
|
|
Packit Service |
509fd4 |
err = cmd.Run()
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("cannot set up a loopback device in the new namespace: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
cmd = exec.Command("mount", "-o", "bind", "/proc/self/ns/net", f.Name())
|
|
Packit Service |
509fd4 |
cmd.Stderr = os.Stderr
|
|
Packit Service |
509fd4 |
cmd.Stdout = os.Stderr
|
|
Packit Service |
509fd4 |
err = cmd.Run()
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("cannot bind mount the new namespace: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
ns := NetNS(path.Base(f.Name()))
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Initialization OK, do not delete the namespace file.
|
|
Packit Service |
509fd4 |
initOK = true
|
|
Packit Service |
509fd4 |
return ns, nil
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// NamespaceCommand returns an *exec.Cmd struct with the difference
|
|
Packit Service |
509fd4 |
// that it's prepended by "ip netns exec NAMESPACE_NAME" command, which
|
|
Packit Service |
509fd4 |
// runs the command in a namespaced environment.
|
|
Packit Service |
509fd4 |
func (n NetNS) NamespacedCommand(name string, arg ...string) *exec.Cmd {
|
|
Packit Service |
509fd4 |
args := []string{"netns", "exec", string(n), name}
|
|
Packit Service |
509fd4 |
args = append(args, arg...)
|
|
Packit Service |
509fd4 |
return exec.Command("ip", args...)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// NamespaceCommand returns an *exec.Cmd struct with the difference
|
|
Packit Service |
509fd4 |
// that it's prepended by "ip netns exec NAMESPACE_NAME" command, which
|
|
Packit Service |
509fd4 |
// runs the command in a namespaced environment.
|
|
Packit Service |
509fd4 |
func (n NetNS) NamespacedCommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
|
|
Packit Service |
509fd4 |
args := []string{"netns", "exec", string(n), name}
|
|
Packit Service |
509fd4 |
args = append(args, arg...)
|
|
Packit Service |
509fd4 |
return exec.CommandContext(ctx, "ip", args...)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Path returns the path to the namespace file
|
|
Packit Service |
509fd4 |
func (n NetNS) Path() string {
|
|
Packit Service |
509fd4 |
return path.Join(netnsDir, string(n))
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Delete deletes the namespaces
|
|
Packit Service |
509fd4 |
func (n NetNS) Delete() error {
|
|
Packit Service |
509fd4 |
cmd := exec.Command("umount", n.Path())
|
|
Packit Service |
509fd4 |
cmd.Stderr = os.Stderr
|
|
Packit Service |
509fd4 |
cmd.Stdout = os.Stdout
|
|
Packit Service |
509fd4 |
err := cmd.Run()
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return fmt.Errorf("cannot unmount the network namespace: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
err = os.Remove(n.Path())
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return fmt.Errorf("cannot delete the network namespace file: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return nil
|
|
Packit Service |
509fd4 |
}
|