mirror of
https://github.com/containers/podman.git
synced 2025-08-02 17:22:30 +08:00
quadlet: Rework uid/gid remapping
Quadlet was doing some custom handling of uid/gid remapping, originating from pre --userns=auto support, including its own user for getting subuids which kinda conflicts with the "container" user used for that. This drops all the old support for id remapping in favour of a new set of keys that more directly map to the podman run options. We have essentially 3 modes now: ``` RemapUsers=manual RemapUid=0:10000:10 RemapUid=10:20000:10 RemapGid=0:10000:10 RemapGid=10:20000:10 ``` This maps to --uidmap and --gidmap options. ``` RemapUsers=auto ``` This maps to --userns=auto. But you can additionally specify RemapUid, RemapGid and RemapUidSize which gets applied as options to the --userns podman option. ``` RemapUsers=keep-id ``` This maps to --userns=keep-id and only works for user units. Signed-off-by: Alexander Larsson <alexl@redhat.com>
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)
|
||||||
|
} else {
|
||||||
|
podman.addf("%d", uid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var remapUsers bool
|
uidMaps := container.LookupAllStrv(ContainerGroup, KeyRemapUID)
|
||||||
if isUser {
|
gidMaps := container.LookupAllStrv(ContainerGroup, KeyRemapGID)
|
||||||
remapUsers = false
|
|
||||||
} else {
|
|
||||||
remapUsers = container.LookupBoolean(ContainerGroup, KeyRemapUsers, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !remapUsers {
|
remapUsers, ok := container.LookupLast(ContainerGroup, KeyRemapUsers)
|
||||||
// No remapping of users, although we still need maps if the
|
if ok && remapUsers != "" {
|
||||||
// main user/group is remapped, even if most ids map one-to-one.
|
switch remapUsers {
|
||||||
if uid != hostUID {
|
case "":
|
||||||
addIDMaps(podman, "--uidmap", uid, hostUID, math.MaxUint32, nil)
|
if len(uidMaps) > 0 {
|
||||||
}
|
return nil, fmt.Errorf("UidMap set without RemapUsers")
|
||||||
if gid != hostGID {
|
}
|
||||||
addIDMaps(podman, "--gidmap", gid, hostGID, math.MaxUint32, nil)
|
if len(gidMaps) > 0 {
|
||||||
}
|
return nil, fmt.Errorf("GidMap set without RemapUsers")
|
||||||
} else {
|
}
|
||||||
uidRemapIDs := lookupRanges(container, ContainerGroup, KeyRemapUIDRanges, lookupHostSubuid, getDefaultRemapUids())
|
case "manual":
|
||||||
gidRemapIDs := lookupRanges(container, ContainerGroup, KeyRemapGIDRanges, lookupHostSubgid, getDefaultRemapGids())
|
for _, uidMap := range uidMaps {
|
||||||
remapUIDStart := container.LookupUint32(ContainerGroup, KeyRemapUIDStart, 1)
|
podman.addf("--uidmap=%s", uidMap)
|
||||||
remapGIDStart := container.LookupUint32(ContainerGroup, KeyRemapGIDStart, 1)
|
}
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
addIDMaps(podman, "--uidmap", uid, hostUID, remapUIDStart, uidRemapIDs)
|
podman.addf("--userns=" + usernsOpts("auto", autoOpts))
|
||||||
addIDMaps(podman, "--gidmap", gid, hostGID, remapGIDStart, gidRemapIDs)
|
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