mirror of
https://github.com/containers/podman.git
synced 2025-06-28 14:29:04 +08:00
Merge pull request #16574 from alexlarsson/quadlet-usermap
quadlet: Rework uid/gid remapping
This commit is contained in:
2
Makefile
2
Makefile
@ -30,7 +30,6 @@ HEAD ?= HEAD
|
|||||||
PROJECT := github.com/containers/podman
|
PROJECT := github.com/containers/podman
|
||||||
GIT_BASE_BRANCH ?= origin/main
|
GIT_BASE_BRANCH ?= origin/main
|
||||||
LIBPOD_INSTANCE := libpod_dev
|
LIBPOD_INSTANCE := libpod_dev
|
||||||
QUADLET_USER ?= quadlet
|
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
BINDIR ?= ${PREFIX}/bin
|
BINDIR ?= ${PREFIX}/bin
|
||||||
LIBEXECDIR ?= ${PREFIX}/libexec
|
LIBEXECDIR ?= ${PREFIX}/libexec
|
||||||
@ -112,7 +111,6 @@ LDFLAGS_PODMAN ?= \
|
|||||||
-X $(LIBPOD)/config._installPrefix=$(PREFIX) \
|
-X $(LIBPOD)/config._installPrefix=$(PREFIX) \
|
||||||
-X $(LIBPOD)/config._etcDir=$(ETCDIR) \
|
-X $(LIBPOD)/config._etcDir=$(ETCDIR) \
|
||||||
-X github.com/containers/common/pkg/config.additionalHelperBinariesDir=$(HELPER_BINARIES_DIR)\
|
-X github.com/containers/common/pkg/config.additionalHelperBinariesDir=$(HELPER_BINARIES_DIR)\
|
||||||
-X $(PROJECT)/v4/pkg/quadlet.QuadletUserName=$(QUADLET_USER) \
|
|
||||||
$(EXTRA_LDFLAGS)
|
$(EXTRA_LDFLAGS)
|
||||||
LDFLAGS_PODMAN_STATIC ?= \
|
LDFLAGS_PODMAN_STATIC ?= \
|
||||||
$(LDFLAGS_PODMAN) \
|
$(LDFLAGS_PODMAN) \
|
||||||
|
@ -102,22 +102,12 @@ default entry point of the container image is used. The format is the same as fo
|
|||||||
#### `User=`
|
#### `User=`
|
||||||
|
|
||||||
The (numeric) uid to run as inside the container. This does not need to match the uid on the host,
|
The (numeric) uid to run as inside the container. This does not need to match the uid on the host,
|
||||||
which can be set with `HostUser`, but if that is not specified, this uid is also used on the host.
|
which can be modified with `RemapUsers`, but if that is not specified, this uid is also used on the host.
|
||||||
|
|
||||||
#### `HostUser=`
|
|
||||||
|
|
||||||
The host uid (numeric or a username) to run the container as. If this differs from the uid in `User`,
|
|
||||||
then user namespaces are used to map the ids. If unspecified, this defaults to what was specified in `User`.
|
|
||||||
|
|
||||||
#### `Group=`
|
#### `Group=`
|
||||||
|
|
||||||
The (numeric) gid to run as inside the container. This does not need to match the gid on the host,
|
The (numeric) gid to run as inside the container. This does not need to match the gid on the host,
|
||||||
which can be set with `HostGroup`, but if that is not specified, this gid is also used on the host.
|
which can be modified with `RemapUsers`, but if that is not specified, this gid is also used on the host.
|
||||||
|
|
||||||
#### `HostGroup=`
|
|
||||||
|
|
||||||
The host gid (numeric or group name) to run the container as. If this differs from the gid in `Group`,
|
|
||||||
then user namespaces are used to map the ids. If unspecified, this defaults to what was specified in `Group`.
|
|
||||||
|
|
||||||
#### `NoNewPrivileges=` (defaults to `yes`)
|
#### `NoNewPrivileges=` (defaults to `yes`)
|
||||||
|
|
||||||
@ -159,44 +149,33 @@ If enabled, makes image read-only, with /var/tmp, /tmp and /run a tmpfs (unless
|
|||||||
Set the seccomp profile to use in the container. If unset, the default podman profile is used.
|
Set the seccomp profile to use in the container. If unset, the default podman profile is used.
|
||||||
Set to either the pathname of a json file, or `unconfined` to disable the seccomp filters.
|
Set to either the pathname of a json file, or `unconfined` to disable the seccomp filters.
|
||||||
|
|
||||||
#### `RemapUsers=` (defaults to `no`)
|
#### `RemapUsers=`
|
||||||
|
|
||||||
If this is enabled, then host user and group ids are remapped in the container, such that all the uids
|
If this is set, then host user and group ids are remapped in the container. It currently
|
||||||
starting at `RemapUidStart` (and gids starting at `RemapGidStart`) in the container are chosen from the
|
supports values: `auto`, `manual` and `keep-id`.
|
||||||
available host uids specified by `RemapUidRanges` (and `RemapGidRanges`).
|
|
||||||
|
|
||||||
#### `RemapUidStart=` (defaults to `1`)
|
In `manual` mode, the `RemapUid` and `RemapGid` options can define an
|
||||||
|
exact mapping of uids from host to container. You must specify these.
|
||||||
|
|
||||||
If `RemapUsers` is enabled, this is the first uid that is remapped, and all lower uids are mapped
|
In `auto` mode mode, the subuids and subgids allocated to the `containers` user is used to allocate
|
||||||
to the equivalent host uid. This defaults to 1 so that the host root uid is in the container, because
|
host uids/gids to use for the container. By default this will try to estimate a count of the ids
|
||||||
this means a lot less file ownership remapping in the container image.
|
to remap, but you can set RemapUidSize to use an explicit size. You can also Use `RemapUid` and
|
||||||
|
`RemapGid` key to force a particular host uid to be mapped to the container.
|
||||||
|
|
||||||
#### `RemapGidStart=` (defaults to `1`)
|
In `keep-id` mode, the running user is mapped to the same id in the container. This is supported
|
||||||
|
only on user systemd units.
|
||||||
|
|
||||||
If `RemapUsers` is enabled, this is the first gid that is remapped, and all lower gids are mapped
|
#### `RemapUid=`
|
||||||
to the equivalent host gid. This defaults to 1 so that the host root gid is in the container, because
|
|
||||||
this means a lot less file ownership remapping in the container image.
|
|
||||||
|
|
||||||
#### `RemapUidRanges=`
|
If `RemapUsers` is enabled, this specifies a uid mapping of the form `container_uid:from_uid:amount`,
|
||||||
|
which will map `amount` number of uids on the host starting at `from_uid` into the container, starting
|
||||||
|
at `container_uid`.
|
||||||
|
|
||||||
This specifies a comma-separated list of ranges (like `10000-20000,40000-50000`) of available host
|
#### `RemapGid=`
|
||||||
uids to use to remap container uids in `RemapUsers`. Alternatively, it can be a username, which means
|
|
||||||
the available subuids of that user will be used.
|
|
||||||
|
|
||||||
If not specified, the default ranges are chosen as the subuids of the `quadlet` user.
|
If `RemapUsers` is enabled, this specifies a gid mapping of the form `container_gid:from_gid:amount`,
|
||||||
|
which will map `amount` number of gids on the host starting at `from_gid` into the container, starting
|
||||||
#### `RemapGidRanges=`
|
at `container_gid`.
|
||||||
|
|
||||||
This specifies a comma-separated list of ranges (like `10000-20000,40000-50000`) of available host
|
|
||||||
gids to use to remap container gids in `RemapUsers`. Alternatively, it can be a username, which means
|
|
||||||
the available subgids of that user will be used.
|
|
||||||
|
|
||||||
If not specified, the default ranges are chosen as the subgids of the `quadlet` user.
|
|
||||||
|
|
||||||
#### `KeepId=` (defaults to `no`, only works for user units)
|
|
||||||
|
|
||||||
If this is enabled, then the user uid will be mapped to itself in the container, otherwise it is
|
|
||||||
mapped to root. This is ignored for system units.
|
|
||||||
|
|
||||||
#### `Notify=` (defaults to `no`)
|
#### `Notify=` (defaults to `no`)
|
||||||
|
|
||||||
|
@ -42,13 +42,6 @@ func (c *PodmanCmdline) addAnnotations(annotations map[string]string) {
|
|||||||
c.addKeys("--annotation", annotations)
|
c.addKeys("--annotation", annotations)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PodmanCmdline) addIDMap(argPrefix string, containerIDStart, hostIDStart, numIDs uint32) {
|
|
||||||
if numIDs != 0 {
|
|
||||||
c.add(argPrefix)
|
|
||||||
c.addf("%d:%d:%d", containerIDStart, hostIDStart, numIDs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPodmanCmdline(args ...string) *PodmanCmdline {
|
func NewPodmanCmdline(args ...string) *PodmanCmdline {
|
||||||
c := &PodmanCmdline{
|
c := &PodmanCmdline{
|
||||||
Args: make([]string, 0),
|
Args: make([]string, 0),
|
||||||
|
@ -2,20 +2,12 @@ package quadlet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/containers/podman/v4/pkg/systemd/parser"
|
"github.com/containers/podman/v4/pkg/systemd/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Overwritten at build time:
|
|
||||||
var (
|
|
||||||
QuadletUserName = "quadlet" // Name of user used to look up subuid/subgid for remap uids
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Directory for global Quadlet files (sysadmin owned)
|
// Directory for global Quadlet files (sysadmin owned)
|
||||||
UnitDirAdmin = "/etc/containers/systemd"
|
UnitDirAdmin = "/etc/containers/systemd"
|
||||||
@ -30,12 +22,6 @@ const (
|
|||||||
XContainerGroup = "X-Container"
|
XContainerGroup = "X-Container"
|
||||||
VolumeGroup = "Volume"
|
VolumeGroup = "Volume"
|
||||||
XVolumeGroup = "X-Volume"
|
XVolumeGroup = "X-Volume"
|
||||||
|
|
||||||
// Fallbacks uid/gid ranges if the above username doesn't exist or has no subuids
|
|
||||||
FallbackUIDStart = 1879048192
|
|
||||||
FallbackUIDLength = 165536
|
|
||||||
FallbackGIDStart = 1879048192
|
|
||||||
FallbackGIDLength = 165536
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var validPortRange = regexp.MustCompile(`\d+(-\d+)?(/udp|/tcp)?$`)
|
var validPortRange = regexp.MustCompile(`\d+(-\d+)?(/udp|/tcp)?$`)
|
||||||
@ -51,18 +37,14 @@ const (
|
|||||||
KeyAddCapability = "AddCapability"
|
KeyAddCapability = "AddCapability"
|
||||||
KeyReadOnly = "ReadOnly"
|
KeyReadOnly = "ReadOnly"
|
||||||
KeyRemapUsers = "RemapUsers"
|
KeyRemapUsers = "RemapUsers"
|
||||||
KeyRemapUIDStart = "RemapUidStart"
|
KeyRemapUID = "RemapUid"
|
||||||
KeyRemapGIDStart = "RemapGidStart"
|
KeyRemapGID = "RemapGid"
|
||||||
KeyRemapUIDRanges = "RemapUidRanges"
|
KeyRemapUIDSize = "RemapUidSize"
|
||||||
KeyRemapGIDRanges = "RemapGidRanges"
|
|
||||||
KeyNotify = "Notify"
|
KeyNotify = "Notify"
|
||||||
KeyExposeHostPort = "ExposeHostPort"
|
KeyExposeHostPort = "ExposeHostPort"
|
||||||
KeyPublishPort = "PublishPort"
|
KeyPublishPort = "PublishPort"
|
||||||
KeyKeepID = "KeepId"
|
|
||||||
KeyUser = "User"
|
KeyUser = "User"
|
||||||
KeyGroup = "Group"
|
KeyGroup = "Group"
|
||||||
KeyHostUser = "HostUser"
|
|
||||||
KeyHostGroup = "HostGroup"
|
|
||||||
KeyVolume = "Volume"
|
KeyVolume = "Volume"
|
||||||
KeyPodmanArgs = "PodmanArgs"
|
KeyPodmanArgs = "PodmanArgs"
|
||||||
KeyLabel = "Label"
|
KeyLabel = "Label"
|
||||||
@ -86,18 +68,14 @@ var supportedContainerKeys = map[string]bool{
|
|||||||
KeyAddCapability: true,
|
KeyAddCapability: true,
|
||||||
KeyReadOnly: true,
|
KeyReadOnly: true,
|
||||||
KeyRemapUsers: true,
|
KeyRemapUsers: true,
|
||||||
KeyRemapUIDStart: true,
|
KeyRemapUID: true,
|
||||||
KeyRemapGIDStart: true,
|
KeyRemapGID: true,
|
||||||
KeyRemapUIDRanges: true,
|
KeyRemapUIDSize: true,
|
||||||
KeyRemapGIDRanges: true,
|
|
||||||
KeyNotify: true,
|
KeyNotify: true,
|
||||||
KeyExposeHostPort: true,
|
KeyExposeHostPort: true,
|
||||||
KeyPublishPort: true,
|
KeyPublishPort: true,
|
||||||
KeyKeepID: true,
|
|
||||||
KeyUser: true,
|
KeyUser: true,
|
||||||
KeyGroup: true,
|
KeyGroup: true,
|
||||||
KeyHostUser: true,
|
|
||||||
KeyHostGroup: true,
|
|
||||||
KeyVolume: true,
|
KeyVolume: true,
|
||||||
KeyPodmanArgs: true,
|
KeyPodmanArgs: true,
|
||||||
KeyLabel: true,
|
KeyLabel: true,
|
||||||
@ -128,30 +106,6 @@ func replaceExtension(name string, extension string, extraPrefix string, extraSu
|
|||||||
return extraPrefix + baseName + extraSuffix + extension
|
return extraPrefix + baseName + extraSuffix + extension
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultRemapUIDs, defaultRemapGIDs *Ranges
|
|
||||||
|
|
||||||
func getDefaultRemapUids() *Ranges {
|
|
||||||
if defaultRemapUIDs == nil {
|
|
||||||
defaultRemapUIDs = lookupHostSubuid(QuadletUserName)
|
|
||||||
if defaultRemapUIDs == nil {
|
|
||||||
defaultRemapUIDs =
|
|
||||||
NewRanges(FallbackUIDStart, FallbackUIDLength)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultRemapUIDs
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultRemapGids() *Ranges {
|
|
||||||
if defaultRemapGIDs == nil {
|
|
||||||
defaultRemapGIDs = lookupHostSubgid(QuadletUserName)
|
|
||||||
if defaultRemapGIDs == nil {
|
|
||||||
defaultRemapGIDs =
|
|
||||||
NewRanges(FallbackGIDStart, FallbackGIDLength)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultRemapGIDs
|
|
||||||
}
|
|
||||||
|
|
||||||
func isPortRange(port string) bool {
|
func isPortRange(port string) bool {
|
||||||
return validPortRange.MatchString(port)
|
return validPortRange.MatchString(port)
|
||||||
}
|
}
|
||||||
@ -166,33 +120,6 @@ func checkForUnknownKeys(unit *parser.UnitFile, groupName string, supportedKeys
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupRanges(unit *parser.UnitFile, groupName string, key string, nameLookup func(string) *Ranges, defaultValue *Ranges) *Ranges {
|
|
||||||
v, ok := unit.Lookup(groupName, key)
|
|
||||||
if !ok {
|
|
||||||
if defaultValue != nil {
|
|
||||||
return defaultValue.Copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewRangesEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v) == 0 {
|
|
||||||
return NewRangesEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !unicode.IsDigit(rune(v[0])) {
|
|
||||||
if nameLookup != nil {
|
|
||||||
r := nameLookup(v)
|
|
||||||
if r != nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NewRangesEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseRanges(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitPorts(ports string) []string {
|
func splitPorts(ports string) []string {
|
||||||
parts := make([]string, 0)
|
parts := make([]string, 0)
|
||||||
|
|
||||||
@ -222,59 +149,19 @@ func splitPorts(ports string) []string {
|
|||||||
return parts
|
return parts
|
||||||
}
|
}
|
||||||
|
|
||||||
func addIDMaps(podman *PodmanCmdline, argPrefix string, containerID, hostID, remapStartID uint32, availableHostIDs *Ranges) {
|
func usernsOpts(kind string, opts []string) string {
|
||||||
if availableHostIDs == nil {
|
var res strings.Builder
|
||||||
// Map everything by default
|
res.WriteString(kind)
|
||||||
availableHostIDs = NewRangesEmpty()
|
if len(opts) > 0 {
|
||||||
|
res.WriteString(":")
|
||||||
}
|
}
|
||||||
|
for i, opt := range opts {
|
||||||
// Map the first ids up to remapStartID to the host equivalent
|
if i != 0 {
|
||||||
unmappedIds := NewRanges(0, remapStartID)
|
res.WriteString(",")
|
||||||
|
|
||||||
// The rest we want to map to availableHostIDs. Note that this
|
|
||||||
// overlaps unmappedIds, because below we may remove ranges from
|
|
||||||
// unmapped ids and we want to backfill those.
|
|
||||||
mappedIds := NewRanges(0, math.MaxUint32)
|
|
||||||
|
|
||||||
// Always map specified uid to specified host_uid
|
|
||||||
podman.addIDMap(argPrefix, containerID, hostID, 1)
|
|
||||||
|
|
||||||
// We no longer want to map this container id as its already mapped
|
|
||||||
mappedIds.Remove(containerID, 1)
|
|
||||||
unmappedIds.Remove(containerID, 1)
|
|
||||||
|
|
||||||
// But also, we don't want to use the *host* id again, as we can only map it once
|
|
||||||
unmappedIds.Remove(hostID, 1)
|
|
||||||
availableHostIDs.Remove(hostID, 1)
|
|
||||||
|
|
||||||
// Map unmapped ids to equivalent host range, and remove from mappedIds to avoid double-mapping
|
|
||||||
for _, r := range unmappedIds.Ranges {
|
|
||||||
start := r.Start
|
|
||||||
length := r.Length
|
|
||||||
|
|
||||||
podman.addIDMap(argPrefix, start, start, length)
|
|
||||||
mappedIds.Remove(start, length)
|
|
||||||
availableHostIDs.Remove(start, length)
|
|
||||||
}
|
|
||||||
|
|
||||||
for cIdx := 0; cIdx < len(mappedIds.Ranges) && len(availableHostIDs.Ranges) > 0; cIdx++ {
|
|
||||||
cRange := &mappedIds.Ranges[cIdx]
|
|
||||||
cStart := cRange.Start
|
|
||||||
cLength := cRange.Length
|
|
||||||
|
|
||||||
for cLength > 0 && len(availableHostIDs.Ranges) > 0 {
|
|
||||||
hRange := &availableHostIDs.Ranges[0]
|
|
||||||
hStart := hRange.Start
|
|
||||||
hLength := hRange.Length
|
|
||||||
|
|
||||||
nextLength := minUint32(hLength, cLength)
|
|
||||||
|
|
||||||
podman.addIDMap(argPrefix, cStart, hStart, nextLength)
|
|
||||||
availableHostIDs.Remove(hStart, nextLength)
|
|
||||||
cStart += nextLength
|
|
||||||
cLength -= nextLength
|
|
||||||
}
|
}
|
||||||
|
res.WriteString(opt)
|
||||||
}
|
}
|
||||||
|
return res.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a quadlet container file (unit file with a Container group) to a systemd
|
// Convert a quadlet container file (unit file with a Container group) to a systemd
|
||||||
@ -451,66 +338,62 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
|||||||
podman.add("--read-only-tmpfs=false")
|
podman.add("--read-only-tmpfs=false")
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultContainerUID := uint32(0)
|
hasUser := container.HasKey(ContainerGroup, KeyUser)
|
||||||
defaultContainerGID := uint32(0)
|
hasGroup := container.HasKey(ContainerGroup, KeyGroup)
|
||||||
|
if hasUser || hasGroup {
|
||||||
|
uid := container.LookupUint32(ContainerGroup, KeyUser, 0)
|
||||||
|
gid := container.LookupUint32(ContainerGroup, KeyGroup, 0)
|
||||||
|
|
||||||
keepID := container.LookupBoolean(ContainerGroup, KeyKeepID, false)
|
|
||||||
if keepID {
|
|
||||||
if isUser {
|
|
||||||
defaultContainerUID = uint32(os.Getuid())
|
|
||||||
defaultContainerGID = uint32(os.Getgid())
|
|
||||||
podman.add("--userns", "keep-id")
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("key 'KeepId' in '%s' unsupported for system units", container.Path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uid := container.LookupUint32(ContainerGroup, KeyUser, defaultContainerUID)
|
|
||||||
gid := container.LookupUint32(ContainerGroup, KeyGroup, defaultContainerGID)
|
|
||||||
|
|
||||||
hostUID, err := container.LookupUID(ContainerGroup, KeyHostUser, uid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("key 'HostUser' invalid: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostGID, err := container.LookupGID(ContainerGroup, KeyHostGroup, gid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("key 'HostGroup' invalid: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if uid != defaultContainerUID || gid != defaultContainerGID {
|
|
||||||
podman.add("--user")
|
podman.add("--user")
|
||||||
if gid == defaultContainerGID {
|
if hasGroup {
|
||||||
podman.addf("%d", uid)
|
|
||||||
} else {
|
|
||||||
podman.addf("%d:%d", uid, gid)
|
podman.addf("%d:%d", uid, gid)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var remapUsers bool
|
|
||||||
if isUser {
|
|
||||||
remapUsers = false
|
|
||||||
} else {
|
} else {
|
||||||
remapUsers = container.LookupBoolean(ContainerGroup, KeyRemapUsers, false)
|
podman.addf("%d", uid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !remapUsers {
|
uidMaps := container.LookupAllStrv(ContainerGroup, KeyRemapUID)
|
||||||
// No remapping of users, although we still need maps if the
|
gidMaps := container.LookupAllStrv(ContainerGroup, KeyRemapGID)
|
||||||
// main user/group is remapped, even if most ids map one-to-one.
|
|
||||||
if uid != hostUID {
|
|
||||||
addIDMaps(podman, "--uidmap", uid, hostUID, math.MaxUint32, nil)
|
|
||||||
}
|
|
||||||
if gid != hostGID {
|
|
||||||
addIDMaps(podman, "--gidmap", gid, hostGID, math.MaxUint32, nil)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uidRemapIDs := lookupRanges(container, ContainerGroup, KeyRemapUIDRanges, lookupHostSubuid, getDefaultRemapUids())
|
|
||||||
gidRemapIDs := lookupRanges(container, ContainerGroup, KeyRemapGIDRanges, lookupHostSubgid, getDefaultRemapGids())
|
|
||||||
remapUIDStart := container.LookupUint32(ContainerGroup, KeyRemapUIDStart, 1)
|
|
||||||
remapGIDStart := container.LookupUint32(ContainerGroup, KeyRemapGIDStart, 1)
|
|
||||||
|
|
||||||
addIDMaps(podman, "--uidmap", uid, hostUID, remapUIDStart, uidRemapIDs)
|
remapUsers, ok := container.LookupLast(ContainerGroup, KeyRemapUsers)
|
||||||
addIDMaps(podman, "--gidmap", gid, hostGID, remapGIDStart, gidRemapIDs)
|
if ok && remapUsers != "" {
|
||||||
|
switch remapUsers {
|
||||||
|
case "":
|
||||||
|
if len(uidMaps) > 0 {
|
||||||
|
return nil, fmt.Errorf("UidMap set without RemapUsers")
|
||||||
|
}
|
||||||
|
if len(gidMaps) > 0 {
|
||||||
|
return nil, fmt.Errorf("GidMap set without RemapUsers")
|
||||||
|
}
|
||||||
|
case "manual":
|
||||||
|
for _, uidMap := range uidMaps {
|
||||||
|
podman.addf("--uidmap=%s", uidMap)
|
||||||
|
}
|
||||||
|
for _, gidMap := range gidMaps {
|
||||||
|
podman.addf("--gidmap=%s", gidMap)
|
||||||
|
}
|
||||||
|
case "auto":
|
||||||
|
autoOpts := make([]string, 0)
|
||||||
|
for _, uidMap := range uidMaps {
|
||||||
|
autoOpts = append(autoOpts, "uidmapping="+uidMap)
|
||||||
|
}
|
||||||
|
for _, gidMap := range gidMaps {
|
||||||
|
autoOpts = append(autoOpts, "gidmapping="+gidMap)
|
||||||
|
}
|
||||||
|
uidSize := container.LookupUint32(ContainerGroup, KeyRemapUIDSize, 0)
|
||||||
|
if uidSize > 0 {
|
||||||
|
autoOpts = append(autoOpts, fmt.Sprintf("size=%v", uidSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
podman.addf("--userns=" + usernsOpts("auto", autoOpts))
|
||||||
|
case "keep-id":
|
||||||
|
if !isUser {
|
||||||
|
return nil, fmt.Errorf("RemapUsers=keep-id is unsupported for system units")
|
||||||
|
}
|
||||||
|
podman.addf("--userns=keep-id")
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported RemapUsers option '%s'", remapUsers)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
volumes := container.LookupAll(ContainerGroup, KeyVolume)
|
volumes := container.LookupAll(ContainerGroup, KeyVolume)
|
||||||
|
@ -1,249 +0,0 @@
|
|||||||
package quadlet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The Ranges abstraction efficiently keeps track of a list of non-intersecting
|
|
||||||
// ranges of uint32. You can merge these and modify them (add/remove a range).
|
|
||||||
// The primary use of these is to manage Uid/Gid ranges for re-mapping
|
|
||||||
|
|
||||||
func minUint32(x, y uint32) uint32 {
|
|
||||||
if x < y {
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
|
|
||||||
func maxUint32(x, y uint32) uint32 {
|
|
||||||
if x > y {
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
|
|
||||||
type Range struct {
|
|
||||||
Start uint32
|
|
||||||
Length uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
type Ranges struct {
|
|
||||||
Ranges []Range
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Ranges) Add(start, length uint32) {
|
|
||||||
// The maximum value we can store is UINT32_MAX-1, because if start
|
|
||||||
// is 0 and length is UINT32_MAX, then the first non-range item is
|
|
||||||
// 0+UINT32_MAX. So, we limit the start and length here so all
|
|
||||||
// elements in the ranges are in this area.
|
|
||||||
if start == math.MaxUint32 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
length = minUint32(length, math.MaxUint32-start)
|
|
||||||
|
|
||||||
if length == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(r.Ranges); i++ {
|
|
||||||
current := &r.Ranges[i]
|
|
||||||
// Check if new range starts before current
|
|
||||||
if start < current.Start {
|
|
||||||
// Check if new range is completely before current
|
|
||||||
if start+length < current.Start {
|
|
||||||
// insert new range at i
|
|
||||||
newr := make([]Range, len(r.Ranges)+1)
|
|
||||||
copy(newr[0:i], r.Ranges[0:i])
|
|
||||||
newr[i] = Range{Start: start, Length: length}
|
|
||||||
copy(newr[i+1:], r.Ranges[i:])
|
|
||||||
r.Ranges = newr
|
|
||||||
|
|
||||||
return // All done
|
|
||||||
}
|
|
||||||
|
|
||||||
// ranges overlap, extend current backward to new start
|
|
||||||
toExtendLen := current.Start - start
|
|
||||||
current.Start -= toExtendLen
|
|
||||||
current.Length += toExtendLen
|
|
||||||
|
|
||||||
// And drop the extended part from new range
|
|
||||||
start += toExtendLen
|
|
||||||
length -= toExtendLen
|
|
||||||
|
|
||||||
if length == 0 {
|
|
||||||
return // That was all
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move on to next case
|
|
||||||
}
|
|
||||||
|
|
||||||
if start >= current.Start && start < current.Start+current.Length {
|
|
||||||
// New range overlaps current
|
|
||||||
if start+length <= current.Start+current.Length {
|
|
||||||
return // All overlapped, we're done
|
|
||||||
}
|
|
||||||
|
|
||||||
// New range extends past end of current
|
|
||||||
overlapLen := (current.Start + current.Length) - start
|
|
||||||
|
|
||||||
// And drop the overlapped part from current range
|
|
||||||
start += overlapLen
|
|
||||||
length -= overlapLen
|
|
||||||
|
|
||||||
// Move on to next case
|
|
||||||
}
|
|
||||||
|
|
||||||
if start == current.Start+current.Length {
|
|
||||||
// We're extending current
|
|
||||||
current.Length += length
|
|
||||||
|
|
||||||
// Might have to merge some old remaining ranges
|
|
||||||
for i+1 < len(r.Ranges) &&
|
|
||||||
r.Ranges[i+1].Start <= current.Start+current.Length {
|
|
||||||
next := &r.Ranges[i+1]
|
|
||||||
|
|
||||||
newEnd := maxUint32(current.Start+current.Length, next.Start+next.Length)
|
|
||||||
|
|
||||||
current.Length = newEnd - current.Start
|
|
||||||
|
|
||||||
copy(r.Ranges[i+1:], r.Ranges[i+2:])
|
|
||||||
r.Ranges = r.Ranges[:len(r.Ranges)-1]
|
|
||||||
current = &r.Ranges[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return // All done
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New range remaining after last old range, append
|
|
||||||
if length > 0 {
|
|
||||||
r.Ranges = append(r.Ranges, Range{Start: start, Length: length})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Ranges) Remove(start, length uint32) {
|
|
||||||
// Limit ranges, see comment in Add
|
|
||||||
if start == math.MaxUint32 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
length = minUint32(length, math.MaxUint32-start)
|
|
||||||
|
|
||||||
if length == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(r.Ranges); i++ {
|
|
||||||
current := &r.Ranges[i]
|
|
||||||
|
|
||||||
end := start + length
|
|
||||||
currentStart := current.Start
|
|
||||||
currentEnd := current.Start + current.Length
|
|
||||||
|
|
||||||
if end > currentStart && start < currentEnd {
|
|
||||||
remainingAtStart := uint32(0)
|
|
||||||
remainingAtEnd := uint32(0)
|
|
||||||
|
|
||||||
if start > currentStart {
|
|
||||||
remainingAtStart = start - currentStart
|
|
||||||
}
|
|
||||||
|
|
||||||
if end < currentEnd {
|
|
||||||
remainingAtEnd = currentEnd - end
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case remainingAtStart == 0 && remainingAtEnd == 0:
|
|
||||||
// Remove whole range
|
|
||||||
copy(r.Ranges[i:], r.Ranges[i+1:])
|
|
||||||
r.Ranges = r.Ranges[:len(r.Ranges)-1]
|
|
||||||
i-- // undo loop iter
|
|
||||||
case remainingAtStart != 0 && remainingAtEnd != 0:
|
|
||||||
// Range is split
|
|
||||||
|
|
||||||
newr := make([]Range, len(r.Ranges)+1)
|
|
||||||
copy(newr[0:i], r.Ranges[0:i])
|
|
||||||
copy(newr[i+1:], r.Ranges[i:])
|
|
||||||
newr[i].Start = currentStart
|
|
||||||
newr[i].Length = remainingAtStart
|
|
||||||
newr[i+1].Start = currentEnd - remainingAtEnd
|
|
||||||
newr[i+1].Length = remainingAtEnd
|
|
||||||
r.Ranges = newr
|
|
||||||
i++ /* double loop iter */
|
|
||||||
case remainingAtStart != 0:
|
|
||||||
r.Ranges[i].Start = currentStart
|
|
||||||
r.Ranges[i].Length = remainingAtStart
|
|
||||||
default: /* remainingAtEnd != 0 */
|
|
||||||
r.Ranges[i].Start = currentEnd - remainingAtEnd
|
|
||||||
r.Ranges[i].Length = remainingAtEnd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Ranges) Merge(other *Ranges) {
|
|
||||||
for _, o := range other.Ranges {
|
|
||||||
r.Add(o.Start, o.Length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Ranges) Copy() *Ranges {
|
|
||||||
rs := make([]Range, len(r.Ranges))
|
|
||||||
copy(rs, r.Ranges)
|
|
||||||
return &Ranges{Ranges: rs}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Ranges) Length() uint32 {
|
|
||||||
length := uint32(0)
|
|
||||||
for _, rr := range r.Ranges {
|
|
||||||
length += rr.Length
|
|
||||||
}
|
|
||||||
return length
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRangesEmpty() *Ranges {
|
|
||||||
return &Ranges{Ranges: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRanges(start, length uint32) *Ranges {
|
|
||||||
r := NewRangesEmpty()
|
|
||||||
r.Add(start, length)
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseEndpoint(str string, defaultVal uint32) uint32 {
|
|
||||||
str = strings.TrimSpace(str)
|
|
||||||
intVal, err := strconv.ParseInt(str, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return defaultVal
|
|
||||||
}
|
|
||||||
|
|
||||||
if intVal < 0 {
|
|
||||||
return uint32(0)
|
|
||||||
}
|
|
||||||
if intVal > math.MaxUint32 {
|
|
||||||
return uint32(math.MaxUint32)
|
|
||||||
}
|
|
||||||
return uint32(intVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ranges are specified inclusive. I.e. 1-3 is 1,2,3
|
|
||||||
func ParseRanges(str string) *Ranges {
|
|
||||||
r := NewRangesEmpty()
|
|
||||||
|
|
||||||
for _, part := range strings.Split(str, ",") {
|
|
||||||
start, end, isPair := strings.Cut(part, "-")
|
|
||||||
startV := parseEndpoint(start, 0)
|
|
||||||
endV := startV
|
|
||||||
if isPair {
|
|
||||||
endV = parseEndpoint(end, math.MaxUint32)
|
|
||||||
}
|
|
||||||
if endV >= startV {
|
|
||||||
r.Add(startV, endV-startV+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
@ -1,242 +0,0 @@
|
|||||||
package quadlet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRanges_Creation(t *testing.T) {
|
|
||||||
empty := NewRangesEmpty()
|
|
||||||
|
|
||||||
assert.Equal(t, empty.Length(), uint32(0))
|
|
||||||
|
|
||||||
one := NewRanges(17, 42)
|
|
||||||
assert.Equal(t, one.Ranges[0].Start, uint32(17))
|
|
||||||
assert.Equal(t, one.Ranges[0].Length, uint32(42))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRanges_Single(t *testing.T) {
|
|
||||||
/* Before */
|
|
||||||
r := NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(0, 9)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 2)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(0))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(9))
|
|
||||||
assert.Equal(t, r.Ranges[1].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[1].Length, uint32(10))
|
|
||||||
|
|
||||||
/* just before */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(0, 10)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(0))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(20))
|
|
||||||
|
|
||||||
/* before + inside */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(0, 19)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(0))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(20))
|
|
||||||
|
|
||||||
/* before + inside, whole */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(0, 20)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(0))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(20))
|
|
||||||
|
|
||||||
/* before + inside + after */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(0, 30)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(0))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(30))
|
|
||||||
|
|
||||||
/* just inside */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(10, 5)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
|
|
||||||
/* inside */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(12, 5)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
|
|
||||||
/* inside at end */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(15, 5)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
|
|
||||||
/* inside + after */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(15, 10)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(15))
|
|
||||||
|
|
||||||
/* just after */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(20, 10)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(20))
|
|
||||||
|
|
||||||
/* after */
|
|
||||||
r = NewRanges(10, 10)
|
|
||||||
|
|
||||||
r.Add(21, 10)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 2)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[1].Start, uint32(21))
|
|
||||||
assert.Equal(t, r.Ranges[1].Length, uint32(10))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRanges_Multi(t *testing.T) {
|
|
||||||
base := NewRanges(10, 10)
|
|
||||||
base.Add(50, 10)
|
|
||||||
base.Add(30, 10)
|
|
||||||
|
|
||||||
/* Test copy */
|
|
||||||
r := base.Copy()
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 3)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[1].Start, uint32(30))
|
|
||||||
assert.Equal(t, r.Ranges[1].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[2].Start, uint32(50))
|
|
||||||
assert.Equal(t, r.Ranges[2].Length, uint32(10))
|
|
||||||
|
|
||||||
/* overlap everything */
|
|
||||||
r = base.Copy()
|
|
||||||
|
|
||||||
r.Add(0, 100)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 1)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(0))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(100))
|
|
||||||
|
|
||||||
/* overlap middle */
|
|
||||||
r = base.Copy()
|
|
||||||
|
|
||||||
r.Add(25, 10)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 3)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[1].Start, uint32(25))
|
|
||||||
assert.Equal(t, r.Ranges[1].Length, uint32(15))
|
|
||||||
assert.Equal(t, r.Ranges[2].Start, uint32(50))
|
|
||||||
assert.Equal(t, r.Ranges[2].Length, uint32(10))
|
|
||||||
|
|
||||||
/* overlap last */
|
|
||||||
r = base.Copy()
|
|
||||||
|
|
||||||
r.Add(45, 10)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 3)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[1].Start, uint32(30))
|
|
||||||
assert.Equal(t, r.Ranges[1].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[2].Start, uint32(45))
|
|
||||||
assert.Equal(t, r.Ranges[2].Length, uint32(15))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRanges_Remove(t *testing.T) {
|
|
||||||
base := NewRanges(10, 10)
|
|
||||||
base.Add(50, 10)
|
|
||||||
base.Add(30, 10)
|
|
||||||
|
|
||||||
/* overlap all */
|
|
||||||
r := base.Copy()
|
|
||||||
|
|
||||||
r.Remove(0, 100)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 0)
|
|
||||||
|
|
||||||
/* overlap middle 1 */
|
|
||||||
|
|
||||||
r = base.Copy()
|
|
||||||
|
|
||||||
r.Remove(25, 20)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 2)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[1].Start, uint32(50))
|
|
||||||
assert.Equal(t, r.Ranges[1].Length, uint32(10))
|
|
||||||
|
|
||||||
/* overlap middle 2 */
|
|
||||||
|
|
||||||
r = base.Copy()
|
|
||||||
|
|
||||||
r.Remove(25, 10)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 3)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[1].Start, uint32(35))
|
|
||||||
assert.Equal(t, r.Ranges[1].Length, uint32(5))
|
|
||||||
assert.Equal(t, r.Ranges[2].Start, uint32(50))
|
|
||||||
assert.Equal(t, r.Ranges[2].Length, uint32(10))
|
|
||||||
|
|
||||||
/* overlap middle 3 */
|
|
||||||
r = base.Copy()
|
|
||||||
|
|
||||||
r.Remove(35, 10)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 3)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[1].Start, uint32(30))
|
|
||||||
assert.Equal(t, r.Ranges[1].Length, uint32(5))
|
|
||||||
assert.Equal(t, r.Ranges[2].Start, uint32(50))
|
|
||||||
assert.Equal(t, r.Ranges[2].Length, uint32(10))
|
|
||||||
|
|
||||||
/* overlap middle 4 */
|
|
||||||
|
|
||||||
r = base.Copy()
|
|
||||||
|
|
||||||
r.Remove(34, 2)
|
|
||||||
|
|
||||||
assert.Equal(t, len(r.Ranges), 4)
|
|
||||||
assert.Equal(t, r.Ranges[0].Start, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[0].Length, uint32(10))
|
|
||||||
assert.Equal(t, r.Ranges[1].Start, uint32(30))
|
|
||||||
assert.Equal(t, r.Ranges[1].Length, uint32(4))
|
|
||||||
assert.Equal(t, r.Ranges[2].Start, uint32(36))
|
|
||||||
assert.Equal(t, r.Ranges[2].Length, uint32(4))
|
|
||||||
assert.Equal(t, r.Ranges[3].Start, uint32(50))
|
|
||||||
assert.Equal(t, r.Ranges[3].Length, uint32(10))
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package quadlet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Code to look up subuid/subguid allocations for a user in /etc/subuid and /etc/subgid
|
|
||||||
|
|
||||||
func lookupHostSubid(name string, file string, cache *[]string) *Ranges {
|
|
||||||
ranges := NewRangesEmpty()
|
|
||||||
|
|
||||||
if len(*cache) == 0 {
|
|
||||||
data, e := os.ReadFile(file)
|
|
||||||
if e != nil {
|
|
||||||
*cache = make([]string, 0)
|
|
||||||
} else {
|
|
||||||
*cache = strings.Split(string(data), "\n")
|
|
||||||
}
|
|
||||||
for i := range *cache {
|
|
||||||
(*cache)[i] = strings.TrimSpace((*cache)[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// If file had no lines, add an empty line so the above cache created check works
|
|
||||||
if len(*cache) == 0 {
|
|
||||||
*cache = append(*cache, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, line := range *cache {
|
|
||||||
if strings.HasPrefix(line, name) &&
|
|
||||||
len(line) > len(name)+1 && line[len(name)] == ':' {
|
|
||||||
parts := strings.SplitN(line, ":", 3)
|
|
||||||
|
|
||||||
if len(parts) != 3 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
start, err := strconv.ParseUint(parts[1], 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
len, err := strconv.ParseUint(parts[1], 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len > 0 {
|
|
||||||
ranges.Add(uint32(start), uint32(len))
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ranges
|
|
||||||
}
|
|
||||||
|
|
||||||
var subuidCache, subgidCache []string
|
|
||||||
|
|
||||||
func lookupHostSubuid(userName string) *Ranges {
|
|
||||||
return lookupHostSubid(userName, "/etc/subuid", &subuidCache)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupHostSubgid(userName string) *Ranges {
|
|
||||||
return lookupHostSubid(userName, "/etc/subgid", &subgidCache)
|
|
||||||
}
|
|
@ -193,7 +193,7 @@ export BUILDTAGS="$BASEBUILDTAGS exclude_graphdriver_btrfs btrfs_noversion remot
|
|||||||
%gobuild -o bin/%{name}-remote ./cmd/%{name}
|
%gobuild -o bin/%{name}-remote ./cmd/%{name}
|
||||||
|
|
||||||
# build quadlet
|
# build quadlet
|
||||||
export BUILDTAGS="$BASEBUILDTAGS $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh) -X $(PROJECT)/v4/pkg/quadlet.QuadletUserName=quadlet"
|
export BUILDTAGS="$BASEBUILDTAGS $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh)"
|
||||||
%gobuild -o bin/quadlet ./cmd/quadlet
|
%gobuild -o bin/quadlet ./cmd/quadlet
|
||||||
|
|
||||||
make docs docker-docs
|
make docs docker-docs
|
||||||
@ -221,17 +221,6 @@ for file in `find %{buildroot}%{_mandir}/man[15] -type f | sed "s,%{buildroot},,
|
|||||||
echo "$file*" >> podman.file-list
|
echo "$file*" >> podman.file-list
|
||||||
done
|
done
|
||||||
|
|
||||||
%pre quadlet
|
|
||||||
# We create a quadlet user so that we can get subuids and subgids allocated.
|
|
||||||
# It really is a system user, but Unfortunately useradd doesn't create subuids
|
|
||||||
# for system users, so we manually make it system-like and start at a higher
|
|
||||||
# min uid to avoid conflicts with common uid nrs around 1000
|
|
||||||
getent passwd quadlet >/dev/null || \
|
|
||||||
useradd -M -U -K SUB_UID_COUNT=65536 -K UID_MIN=50000 \
|
|
||||||
-s /sbin/nologin -d /nonexisting \
|
|
||||||
-c "User for quadlet" quadlet
|
|
||||||
exit 0
|
|
||||||
|
|
||||||
# This lists all the files that are included in the rpm package and that
|
# This lists all the files that are included in the rpm package and that
|
||||||
# are going to be installed into target system where the rpm is installed.
|
# are going to be installed into target system where the rpm is installed.
|
||||||
%files -f %{name}.file-list
|
%files -f %{name}.file-list
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
## !assert-podman-args --uidmap
|
|
||||||
## !assert-podman-args --gidmap
|
|
||||||
|
|
||||||
[Container]
|
|
||||||
Image=localhost/imagename
|
|
||||||
RemapUsers=no
|
|
@ -1,28 +0,0 @@
|
|||||||
# This is an non-user-remapped container, but the user is mapped (uid
|
|
||||||
# 1000 in container is uid 90 on host). This means the result should
|
|
||||||
# map those particular ids to each other, but map all other container
|
|
||||||
# ids to the same as the host.
|
|
||||||
|
|
||||||
# There is some additional complexity, as the host uid (90) that the
|
|
||||||
# container uid is mapped to can't also be mapped to itself, as ids
|
|
||||||
# can only be mapped once, so it has to be unmapped.
|
|
||||||
|
|
||||||
## assert-podman-args --user 1000:1001
|
|
||||||
|
|
||||||
## assert-podman-args --uidmap 0:0:90
|
|
||||||
## assert-podman-args --uidmap 91:91:909
|
|
||||||
## assert-podman-args --uidmap 1000:90:1
|
|
||||||
## assert-podman-args --uidmap 1001:1001:4294966294
|
|
||||||
|
|
||||||
## assert-podman-args --gidmap 0:0:91
|
|
||||||
## assert-podman-args --gidmap 92:92:909
|
|
||||||
## assert-podman-args --gidmap 1001:91:1
|
|
||||||
## assert-podman-args --gidmap 1002:1002:4294966293
|
|
||||||
|
|
||||||
[Container]
|
|
||||||
Image=localhost/imagename
|
|
||||||
RemapUsers=no
|
|
||||||
User=1000
|
|
||||||
Group=1001
|
|
||||||
HostUser=90
|
|
||||||
HostGroup=91
|
|
5
test/e2e/quadlet/remap-auto.container
Normal file
5
test/e2e/quadlet/remap-auto.container
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
## assert-podman-args --userns=auto
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image=localhost/imagename
|
||||||
|
RemapUsers=auto
|
10
test/e2e/quadlet/remap-auto2.container
Normal file
10
test/e2e/quadlet/remap-auto2.container
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
## assert-podman-args "--userns=auto:uidmapping=0:10000:10,uidmapping=10:20000:10,gidmapping=0:10000:10,gidmapping=10:20000:10,size=20"
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image=localhost/imagename
|
||||||
|
RemapUsers=auto
|
||||||
|
RemapUid=0:10000:10
|
||||||
|
RemapUid=10:20000:10
|
||||||
|
RemapGid=0:10000:10
|
||||||
|
RemapGid=10:20000:10
|
||||||
|
RemapUidSize=20
|
12
test/e2e/quadlet/remap-manual.container
Normal file
12
test/e2e/quadlet/remap-manual.container
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
## assert-podman-args "--uidmap=0:10000:10"
|
||||||
|
## assert-podman-args "--uidmap=10:20000:10"
|
||||||
|
## assert-podman-args "--gidmap=0:10000:10"
|
||||||
|
## assert-podman-args "--gidmap=10:20000:10"
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image=localhost/imagename
|
||||||
|
RemapUsers=manual
|
||||||
|
RemapUid=0:10000:10
|
||||||
|
RemapUid=10:20000:10
|
||||||
|
RemapGid=0:10000:10
|
||||||
|
RemapGid=10:20000:10
|
@ -1,24 +0,0 @@
|
|||||||
## assert-podman-args --user 1000:1001
|
|
||||||
|
|
||||||
## assert-podman-args --uidmap 0:0:1
|
|
||||||
## assert-podman-args --uidmap 1:100000:999
|
|
||||||
## assert-podman-args --uidmap 1000:900:1
|
|
||||||
## assert-podman-args --uidmap 1001:100999:99001
|
|
||||||
|
|
||||||
## assert-podman-args --gidmap 0:0:1
|
|
||||||
## assert-podman-args --gidmap 1:100000:1000
|
|
||||||
## assert-podman-args --gidmap 1001:901:1
|
|
||||||
## assert-podman-args --gidmap 1002:101000:99000
|
|
||||||
|
|
||||||
[Container]
|
|
||||||
Image=localhost/imagename
|
|
||||||
User=1000
|
|
||||||
HostUser=900
|
|
||||||
Group=1001
|
|
||||||
HostGroup=901
|
|
||||||
|
|
||||||
RemapUsers=yes
|
|
||||||
|
|
||||||
# Set this to get well-known valuse for the checks
|
|
||||||
RemapUidRanges=100000-199999
|
|
||||||
RemapGidRanges=100000-199999
|
|
@ -1,26 +0,0 @@
|
|||||||
## assert-podman-args --user 1000:1001
|
|
||||||
|
|
||||||
## assert-podman-args --uidmap 0:100000:1000
|
|
||||||
## assert-podman-args --uidmap 1000:0:1
|
|
||||||
## assert-podman-args --uidmap 1001:101000:99000
|
|
||||||
## !assert-podman-args --uidmap 0:0:1
|
|
||||||
|
|
||||||
## assert-podman-args --gidmap 0:100000:1001
|
|
||||||
## assert-podman-args --gidmap 1001:0:1
|
|
||||||
## assert-podman-args --gidmap 1002:101001:98999
|
|
||||||
## !assert-podman-args --gidmap 0:0:1
|
|
||||||
|
|
||||||
# Map container uid 1000 to host root
|
|
||||||
# This means container root must map to something else
|
|
||||||
|
|
||||||
[Container]
|
|
||||||
Image=localhost/imagename
|
|
||||||
User=1000
|
|
||||||
# Also test name parsing
|
|
||||||
HostUser=root
|
|
||||||
Group=1001
|
|
||||||
HostGroup=0
|
|
||||||
RemapUsers=yes
|
|
||||||
# Set this to get well-known valuse for the checks
|
|
||||||
RemapUidRanges=100000-199999
|
|
||||||
RemapGidRanges=100000-199999
|
|
@ -1,22 +0,0 @@
|
|||||||
# No need for --user 0:0, it is the default
|
|
||||||
## !assert-podman-args --user
|
|
||||||
|
|
||||||
## assert-podman-args --uidmap 0:0:1
|
|
||||||
## assert-podman-args --gidmap 0:0:1
|
|
||||||
|
|
||||||
## assert-podman-args --uidmap 1:100000:100000
|
|
||||||
## assert-podman-args --gidmap 1:100000:100000
|
|
||||||
|
|
||||||
# Map container uid root to host root
|
|
||||||
|
|
||||||
[Container]
|
|
||||||
Image=localhost/imagename
|
|
||||||
User=0
|
|
||||||
# Also test name parsing
|
|
||||||
HostUser=root
|
|
||||||
Group=0
|
|
||||||
HostGroup=0
|
|
||||||
RemapUsers=yes
|
|
||||||
# Set this to get well-known valuse for the checks
|
|
||||||
RemapUidRanges=100000-199999
|
|
||||||
RemapGidRanges=100000-199999
|
|
@ -280,8 +280,6 @@ var _ = Describe("quadlet system generator", func() {
|
|||||||
Entry("name.container", "name.container"),
|
Entry("name.container", "name.container"),
|
||||||
Entry("network.container", "network.container"),
|
Entry("network.container", "network.container"),
|
||||||
Entry("noimage.container", "noimage.container"),
|
Entry("noimage.container", "noimage.container"),
|
||||||
Entry("noremapuser2.container", "noremapuser2.container"),
|
|
||||||
Entry("noremapuser.container", "noremapuser.container"),
|
|
||||||
Entry("notify.container", "notify.container"),
|
Entry("notify.container", "notify.container"),
|
||||||
Entry("other-sections.container", "other-sections.container"),
|
Entry("other-sections.container", "other-sections.container"),
|
||||||
Entry("podmanargs.container", "podmanargs.container"),
|
Entry("podmanargs.container", "podmanargs.container"),
|
||||||
@ -294,9 +292,9 @@ var _ = Describe("quadlet system generator", func() {
|
|||||||
Entry("shortname.container", "shortname.container"),
|
Entry("shortname.container", "shortname.container"),
|
||||||
Entry("timezone.container", "timezone.container"),
|
Entry("timezone.container", "timezone.container"),
|
||||||
Entry("user.container", "user.container"),
|
Entry("user.container", "user.container"),
|
||||||
Entry("user-host.container", "user-host.container"),
|
Entry("remap-manual.container", "remap-manual.container"),
|
||||||
Entry("user-root1.container", "user-root1.container"),
|
Entry("remap-auto.container", "remap-auto.container"),
|
||||||
Entry("user-root2.container", "user-root2.container"),
|
Entry("remap-auto2.container", "remap-auto2.container"),
|
||||||
Entry("volume.container", "volume.container"),
|
Entry("volume.container", "volume.container"),
|
||||||
|
|
||||||
Entry("basic.volume", "basic.volume"),
|
Entry("basic.volume", "basic.volume"),
|
||||||
|
Reference in New Issue
Block a user