Skip to content

Commit

Permalink
Write $ROOTLESSKIT_STATE_DIR/resolv.conf
Browse files Browse the repository at this point in the history
The file might be useful for detach-netns mode

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
  • Loading branch information
AkihiroSuda committed Jan 10, 2024
1 parent 2d8106d commit 3e782ae
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 86 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,9 @@ The following files will be created in the state directory, which can be specifi
* `lock`: lock file
* `child_pid`: decimal PID text that can be used for `nsenter(1)`.
* `api.sock`: REST API socket. See [`./docs/api.md`](./docs/api.md) and [`./docs/port.md`](./docs/port.md).
* `netns`: Detached NetNS. Created only with `--detach-netns`. Valid only in the child mount namespace.
* `netns` (since v2.0.0): Detached NetNS. Created only with `--detach-netns`. Valid only in the child mount namespace.
* `resolv.conf` (since v2.0.0): `resolv.conf` file. Bind-mounted to `/etc/resolv.conf` unles `--detach-netns` is specified.
* `hosts` (since v2.0.0): `hosts` file. Bind-mounted to `/etc/hosts` unless `--detach-netns` is specified.

If `--state-dir` is not specified, RootlessKit creates a temporary state directory on `/tmp` and removes it on exit.

Expand Down
39 changes: 27 additions & 12 deletions pkg/child/child.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,19 @@ func setupNet(stateDir string, msg *messages.ParentInitNetworkDriverCompleted, e
return nil
}

stateDirResolvConf := filepath.Join(stateDir, "resolv.conf")
if err := os.WriteFile(stateDirResolvConf, generateResolvConf(msg.DNS), 0644); err != nil {
return fmt.Errorf("writing %s: %w", stateDirResolvConf, err)
}
hostsContent, err := generateEtcHosts()
if err != nil {
return err
}
stateDirHosts := filepath.Join(stateDir, "hosts")
if err := os.WriteFile(stateDirHosts, hostsContent, 0644); err != nil {
return fmt.Errorf("writing %s: %w", stateDirHosts, err)
}

if detachedNetNSPath == "" {
// non-detached mode
if err := activateLoopback(); err != nil {
Expand All @@ -172,23 +185,26 @@ func setupNet(stateDir string, msg *messages.ParentInitNetworkDriverCompleted, e
return err
}
if etcWasCopied {
if err := writeResolvConf(msg.DNS); err != nil {
return err
}
if err := writeEtcHosts(); err != nil {
return err
// remove copied-up link
for _, f := range []string{"/etc/resolv.conf", "/etc/hosts"} {
if err := os.RemoveAll(f); err != nil {
return fmt.Errorf("failed to remove copied-up link %q: %w", f, err)
}
if err := os.WriteFile(f, []byte{}, 0644); err != nil {
return fmt.Errorf("writing %s: %w", f, err)
}
}
} else {
logrus.Warn("Mounting /etc/resolv.conf without copying-up /etc. " +
"Note that /etc/resolv.conf in the namespace will be unmounted when it is recreated on the host. " +
"Unless /etc/resolv.conf is statically configured, copying-up /etc is highly recommended. " +
"Please refer to RootlessKit documentation for further information.")
if err := mountResolvConf(stateDir, msg.DNS); err != nil {
return err
}
if err := mountEtcHosts(stateDir); err != nil {
return err
}
}
if err := unix.Mount(stateDirResolvConf, "/etc/resolv.conf", "", uintptr(unix.MS_BIND), ""); err != nil {
return fmt.Errorf("failed to create bind mount /etc/resolv.conf for %s: %w", stateDirResolvConf, err)
}
if err := unix.Mount(stateDirHosts, "/etc/hosts", "", uintptr(unix.MS_BIND), ""); err != nil {
return fmt.Errorf("failed to create bind mount /etc/hosts for %s: %w", stateDirHosts, err)
}
} else {
// detached mode
Expand All @@ -206,7 +222,6 @@ func setupNet(stateDir string, msg *messages.ParentInitNetworkDriverCompleted, e
}); err != nil {
return err
}
// TODO: write /etc/resolv.conf and /etc/hosts in a custom directory?
}
return nil
}
Expand Down
36 changes: 0 additions & 36 deletions pkg/child/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package child
import (
"fmt"
"os"
"path/filepath"

"golang.org/x/sys/unix"
)

// generateEtcHosts makes sure the current hostname is resolved into
Expand All @@ -26,36 +23,3 @@ func generateEtcHosts() ([]byte, error) {
string(etcHosts), hostname, hostname)
return []byte(s), nil
}

// writeEtcHosts is akin to writeResolvConf
// TODO: dedupe
func writeEtcHosts() error {
newEtcHosts, err := generateEtcHosts()
if err != nil {
return err
}
// remove copied-up link
_ = os.Remove("/etc/hosts")
if err := os.WriteFile("/etc/hosts", newEtcHosts, 0644); err != nil {
return fmt.Errorf("writing /etc/hosts: %w", err)
}
return nil
}

// mountEtcHosts is akin to mountResolvConf
// TODO: dedupe
func mountEtcHosts(tempDir string) error {
newEtcHosts, err := generateEtcHosts()
if err != nil {
return err
}
myEtcHosts := filepath.Join(tempDir, "hosts")
if err := os.WriteFile(myEtcHosts, newEtcHosts, 0644); err != nil {
return fmt.Errorf("writing %s: %w", myEtcHosts, err)
}

if err := unix.Mount(myEtcHosts, "/etc/hosts", "", uintptr(unix.MS_BIND), ""); err != nil {
return fmt.Errorf("failed to create bind mount /etc/hosts for %s: %w", myEtcHosts, err)
}
return nil
}
37 changes: 0 additions & 37 deletions pkg/child/resolvconf.go
Original file line number Diff line number Diff line change
@@ -1,42 +1,5 @@
package child

import (
"fmt"
"os"
"path/filepath"

"golang.org/x/sys/unix"
)

func generateResolvConf(dns string) []byte {
return []byte("nameserver " + dns + "\n")
}

func writeResolvConf(dns string) error {
// remove copied-up link
_ = os.Remove("/etc/resolv.conf")
if err := os.WriteFile("/etc/resolv.conf", generateResolvConf(dns), 0644); err != nil {
return fmt.Errorf("writing %s: %w", "/etc/resolv.conf", err)
}
return nil
}

// mountResolvConf does not work when /etc/resolv.conf is a managed by
// systemd or NetworkManager, because our bind-mounted /etc/resolv.conf (in our namespaces)
// is unexpectedly unmounted when /etc/resolv.conf is recreated in the initial initial namespace.
//
// If /etc/resolv.conf is a symlink, e.g. to ../run/systemd/resolve/stub-resolv.conf,
// our bind-mounted /etc/resolv.conf is still unmounted when /run/systemd/resolve/stub-resolv.conf is recreated.
//
// Use writeResolvConf with copying-up /etc for most cases.
func mountResolvConf(tempDir, dns string) error {
myResolvConf := filepath.Join(tempDir, "resolv.conf")
if err := os.WriteFile(myResolvConf, generateResolvConf(dns), 0644); err != nil {
return fmt.Errorf("writing %s: %w", myResolvConf, err)
}

if err := unix.Mount(myResolvConf, "/etc/resolv.conf", "", uintptr(unix.MS_BIND), ""); err != nil {
return fmt.Errorf("failed to create bind mount /etc/resolv.conf for %s: %w", myResolvConf, err)
}
return nil
}

0 comments on commit 3e782ae

Please sign in to comment.