mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add directfs support for Docker/k8s/Podman rootless containers.
These tools use rootless-containers/rootlesskit under the hood. rootlesskit
configures a new userns with uid/gid mappings such that 0 ID is mapped to
current user/group and IDs 1 onwards are mapped to host IDs configured in
/etc/subuid and /etc/subgid.
runsc is invoked by these tools in a new userns with UID=0 which is mapped
to the caller's UID in the parent userns.
When directfs tries modify the OCI spec by adding identity mappings, it should
not assume that we are running as root in the initial userns. As described
above, we could be running as root in a new userns. So the identity mappings
should be created by looking at /proc/self/{uid/gid}_map.
With this change, rootless containers work correctly with these tools.
PiperOrigin-RevId: 531796427
This commit is contained in:
@@ -401,7 +401,9 @@ steps:
|
||||
- <<: *common
|
||||
<<: *source_test
|
||||
label: ":podman: Podman"
|
||||
command: sudo ./test/podman/run.sh
|
||||
commands:
|
||||
- sudo ./test/podman/run.sh
|
||||
- sudo -E RUNTIME_ARGS=--directfs ./test/podman/run.sh
|
||||
agents:
|
||||
<<: *ubuntu_agents
|
||||
cgroup: "v2"
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -1648,28 +1650,64 @@ func modifySpecForDirectfs(conf *config.Config, spec *specs.Spec) error {
|
||||
return fmt.Errorf("spec defines UID/GID mappings without defining userns")
|
||||
}
|
||||
// Run the sandbox in a new user namespace with identity UID/GID mappings.
|
||||
log.Debugf("Configuring container with a new userns with identity user mappings into current userns")
|
||||
spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{Type: specs.UserNamespace})
|
||||
// The maximum range of UID/GID mapping should be the largest uint32 integer.
|
||||
// This is similar to what Linux does for identity mappings. Also note:
|
||||
// "This leaves 4294967295 (the 32-bit signed -1 value) unmapped. This is
|
||||
// deliberate: (uid_t) -1 is used in several interfaces (e.g., setreuid(2))
|
||||
// as a way to specify "no user ID". Leaving (uid_t) -1 unmapped and
|
||||
// unusable guarantees that there will be no confusion when using these
|
||||
// interfaces." -- user_namespaces(7).
|
||||
maxRange := ^uint32(0)
|
||||
spec.Linux.UIDMappings = []specs.LinuxIDMapping{
|
||||
{
|
||||
ContainerID: 0,
|
||||
HostID: 0,
|
||||
Size: maxRange,
|
||||
},
|
||||
uidMappings, err := getIdentityMapping("uid_map")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spec.Linux.GIDMappings = []specs.LinuxIDMapping{
|
||||
{
|
||||
ContainerID: 0,
|
||||
HostID: 0,
|
||||
Size: maxRange,
|
||||
},
|
||||
spec.Linux.UIDMappings = uidMappings
|
||||
logIDMappings(uidMappings, "UID")
|
||||
gidMappings, err := getIdentityMapping("gid_map")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spec.Linux.GIDMappings = gidMappings
|
||||
logIDMappings(gidMappings, "GID")
|
||||
return nil
|
||||
}
|
||||
|
||||
func getIdentityMapping(mapFileName string) ([]specs.LinuxIDMapping, error) {
|
||||
// See user_namespaces(7) to understand how /proc/self/{uid/gid}_map files
|
||||
// are organized.
|
||||
mapFile := path.Join("/proc/self", mapFileName)
|
||||
file, err := os.Open(mapFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open %s: %v", mapFile, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var mappings []specs.LinuxIDMapping
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
var myStart, parentStart, rangeLen uint32
|
||||
numParsed, err := fmt.Sscanf(line, "%d %d %d", &myStart, &parentStart, &rangeLen)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse line %q in file %s: %v", line, mapFile, err)
|
||||
}
|
||||
if numParsed != 3 {
|
||||
return nil, fmt.Errorf("failed to parse 3 integers from line %q in file %s", line, mapFile)
|
||||
}
|
||||
// Create an identity mapping with the current userns.
|
||||
mappings = append(mappings, specs.LinuxIDMapping{
|
||||
ContainerID: myStart,
|
||||
HostID: myStart,
|
||||
Size: rangeLen,
|
||||
})
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to scan file %s: %v", mapFile, err)
|
||||
}
|
||||
return mappings, nil
|
||||
}
|
||||
|
||||
func logIDMappings(mappings []specs.LinuxIDMapping, idType string) {
|
||||
if !log.IsLogging(log.Debug) {
|
||||
return
|
||||
}
|
||||
log.Debugf("%s Mappings:", idType)
|
||||
for _, m := range mappings {
|
||||
log.Debugf("\tContainer ID: %d, Host ID: %d, Range Length: %d", m.ContainerID, m.HostID, m.Size)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ make copy TARGETS=runsc DESTINATION="${test_dir}"
|
||||
cat > "${podman_runtime}" <<EOF
|
||||
#!/bin/bash
|
||||
|
||||
exec $test_dir/runsc --ignore-cgroups --debug --debug-log ${test_dir}/runsc.log "\$@"
|
||||
exec $test_dir/runsc --ignore-cgroups --debug --debug-log ${test_dir}/runsc.log ${RUNTIME_ARGS:-} "\$@"
|
||||
EOF
|
||||
chmod ugo+x "${podman_runtime}"
|
||||
chmod ugo+x "${test_dir}/runsc"
|
||||
|
||||
Reference in New Issue
Block a user