mirror of
https://github.com/containers/podman.git
synced 2025-06-27 21:50:18 +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
|
||||
GIT_BASE_BRANCH ?= origin/main
|
||||
LIBPOD_INSTANCE := libpod_dev
|
||||
QUADLET_USER ?= quadlet
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= ${PREFIX}/bin
|
||||
LIBEXECDIR ?= ${PREFIX}/libexec
|
||||
@ -112,7 +111,6 @@ LDFLAGS_PODMAN ?= \
|
||||
-X $(LIBPOD)/config._installPrefix=$(PREFIX) \
|
||||
-X $(LIBPOD)/config._etcDir=$(ETCDIR) \
|
||||
-X github.com/containers/common/pkg/config.additionalHelperBinariesDir=$(HELPER_BINARIES_DIR)\
|
||||
-X $(PROJECT)/v4/pkg/quadlet.QuadletUserName=$(QUADLET_USER) \
|
||||
$(EXTRA_LDFLAGS)
|
||||
LDFLAGS_PODMAN_STATIC ?= \
|
||||
$(LDFLAGS_PODMAN) \
|
||||
|
@ -102,22 +102,12 @@ default entry point of the container image is used. The format is the same as fo
|
||||
#### `User=`
|
||||
|
||||
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.
|
||||
|
||||
#### `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`.
|
||||
which can be modified with `RemapUsers`, but if that is not specified, this uid is also used on the host.
|
||||
|
||||
#### `Group=`
|
||||
|
||||
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.
|
||||
|
||||
#### `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`.
|
||||
which can be modified with `RemapUsers`, but if that is not specified, this gid is also used on the host.
|
||||
|
||||
#### `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 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
|
||||
starting at `RemapUidStart` (and gids starting at `RemapGidStart`) in the container are chosen from the
|
||||
available host uids specified by `RemapUidRanges` (and `RemapGidRanges`).
|
||||
If this is set, then host user and group ids are remapped in the container. It currently
|
||||
supports values: `auto`, `manual` and `keep-id`.
|
||||
|
||||
#### `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
|
||||
to the equivalent host uid. This defaults to 1 so that the host root uid is in the container, because
|
||||
this means a lot less file ownership remapping in the container image.
|
||||
In `auto` mode mode, the subuids and subgids allocated to the `containers` user is used to allocate
|
||||
host uids/gids to use for the container. By default this will try to estimate a count of the ids
|
||||
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
|
||||
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.
|
||||
#### `RemapUid=`
|
||||
|
||||
#### `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
|
||||
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.
|
||||
#### `RemapGid=`
|
||||
|
||||
If not specified, the default ranges are chosen as the subuids of the `quadlet` user.
|
||||
|
||||
#### `RemapGidRanges=`
|
||||
|
||||
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.
|
||||
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
|
||||
at `container_gid`.
|
||||
|
||||
#### `Notify=` (defaults to `no`)
|
||||
|
||||
|
@ -42,13 +42,6 @@ func (c *PodmanCmdline) addAnnotations(annotations map[string]string) {
|
||||
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 {
|
||||
c := &PodmanCmdline{
|
||||
Args: make([]string, 0),
|
||||
|
@ -2,20 +2,12 @@ package quadlet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"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 (
|
||||
// Directory for global Quadlet files (sysadmin owned)
|
||||
UnitDirAdmin = "/etc/containers/systemd"
|
||||
@ -30,12 +22,6 @@ const (
|
||||
XContainerGroup = "X-Container"
|
||||
VolumeGroup = "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)?$`)
|
||||
@ -51,18 +37,14 @@ const (
|
||||
KeyAddCapability = "AddCapability"
|
||||
KeyReadOnly = "ReadOnly"
|
||||
KeyRemapUsers = "RemapUsers"
|
||||
KeyRemapUIDStart = "RemapUidStart"
|
||||
KeyRemapGIDStart = "RemapGidStart"
|
||||
KeyRemapUIDRanges = "RemapUidRanges"
|
||||
KeyRemapGIDRanges = "RemapGidRanges"
|
||||
KeyRemapUID = "RemapUid"
|
||||
KeyRemapGID = "RemapGid"
|
||||
KeyRemapUIDSize = "RemapUidSize"
|
||||
KeyNotify = "Notify"
|
||||
KeyExposeHostPort = "ExposeHostPort"
|
||||
KeyPublishPort = "PublishPort"
|
||||
KeyKeepID = "KeepId"
|
||||
KeyUser = "User"
|
||||
KeyGroup = "Group"
|
||||
KeyHostUser = "HostUser"
|
||||
KeyHostGroup = "HostGroup"
|
||||
KeyVolume = "Volume"
|
||||
KeyPodmanArgs = "PodmanArgs"
|
||||
KeyLabel = "Label"
|
||||
@ -86,18 +68,14 @@ var supportedContainerKeys = map[string]bool{
|
||||
KeyAddCapability: true,
|
||||
KeyReadOnly: true,
|
||||
KeyRemapUsers: true,
|
||||
KeyRemapUIDStart: true,
|
||||
KeyRemapGIDStart: true,
|
||||
KeyRemapUIDRanges: true,
|
||||
KeyRemapGIDRanges: true,
|
||||
KeyRemapUID: true,
|
||||
KeyRemapGID: true,
|
||||
KeyRemapUIDSize: true,
|
||||
KeyNotify: true,
|
||||
KeyExposeHostPort: true,
|
||||
KeyPublishPort: true,
|
||||
KeyKeepID: true,
|
||||
KeyUser: true,
|
||||
KeyGroup: true,
|
||||
KeyHostUser: true,
|
||||
KeyHostGroup: true,
|
||||
KeyVolume: true,
|
||||
KeyPodmanArgs: true,
|
||||
KeyLabel: true,
|
||||
@ -128,30 +106,6 @@ func replaceExtension(name string, extension string, extraPrefix string, extraSu
|
||||
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 {
|
||||
return validPortRange.MatchString(port)
|
||||
}
|
||||
@ -166,33 +120,6 @@ func checkForUnknownKeys(unit *parser.UnitFile, groupName string, supportedKeys
|
||||
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 {
|
||||
parts := make([]string, 0)
|
||||
|
||||
@ -222,59 +149,19 @@ func splitPorts(ports string) []string {
|
||||
return parts
|
||||
}
|
||||
|
||||
func addIDMaps(podman *PodmanCmdline, argPrefix string, containerID, hostID, remapStartID uint32, availableHostIDs *Ranges) {
|
||||
if availableHostIDs == nil {
|
||||
// Map everything by default
|
||||
availableHostIDs = NewRangesEmpty()
|
||||
func usernsOpts(kind string, opts []string) string {
|
||||
var res strings.Builder
|
||||
res.WriteString(kind)
|
||||
if len(opts) > 0 {
|
||||
res.WriteString(":")
|
||||
}
|
||||
|
||||
// Map the first ids up to remapStartID to the host equivalent
|
||||
unmappedIds := NewRanges(0, remapStartID)
|
||||
|
||||
// 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
|
||||
for i, opt := range opts {
|
||||
if i != 0 {
|
||||
res.WriteString(",")
|
||||
}
|
||||
res.WriteString(opt)
|
||||
}
|
||||
return res.String()
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
defaultContainerUID := uint32(0)
|
||||
defaultContainerGID := uint32(0)
|
||||
hasUser := container.HasKey(ContainerGroup, KeyUser)
|
||||
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")
|
||||
if gid == defaultContainerGID {
|
||||
podman.addf("%d", uid)
|
||||
} else {
|
||||
if hasGroup {
|
||||
podman.addf("%d:%d", uid, gid)
|
||||
} else {
|
||||
podman.addf("%d", uid)
|
||||
}
|
||||
}
|
||||
|
||||
var remapUsers bool
|
||||
if isUser {
|
||||
remapUsers = false
|
||||
} else {
|
||||
remapUsers = container.LookupBoolean(ContainerGroup, KeyRemapUsers, false)
|
||||
}
|
||||
uidMaps := container.LookupAllStrv(ContainerGroup, KeyRemapUID)
|
||||
gidMaps := container.LookupAllStrv(ContainerGroup, KeyRemapGID)
|
||||
|
||||
if !remapUsers {
|
||||
// No remapping of users, although we still need maps if the
|
||||
// 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)
|
||||
remapUsers, ok := container.LookupLast(ContainerGroup, KeyRemapUsers)
|
||||
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))
|
||||
}
|
||||
|
||||
addIDMaps(podman, "--uidmap", uid, hostUID, remapUIDStart, uidRemapIDs)
|
||||
addIDMaps(podman, "--gidmap", gid, hostGID, remapGIDStart, gidRemapIDs)
|
||||
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)
|
||||
|
@ -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}
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
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
|
||||
# are going to be installed into target system where the rpm is installed.
|
||||
%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("network.container", "network.container"),
|
||||
Entry("noimage.container", "noimage.container"),
|
||||
Entry("noremapuser2.container", "noremapuser2.container"),
|
||||
Entry("noremapuser.container", "noremapuser.container"),
|
||||
Entry("notify.container", "notify.container"),
|
||||
Entry("other-sections.container", "other-sections.container"),
|
||||
Entry("podmanargs.container", "podmanargs.container"),
|
||||
@ -294,9 +292,9 @@ var _ = Describe("quadlet system generator", func() {
|
||||
Entry("shortname.container", "shortname.container"),
|
||||
Entry("timezone.container", "timezone.container"),
|
||||
Entry("user.container", "user.container"),
|
||||
Entry("user-host.container", "user-host.container"),
|
||||
Entry("user-root1.container", "user-root1.container"),
|
||||
Entry("user-root2.container", "user-root2.container"),
|
||||
Entry("remap-manual.container", "remap-manual.container"),
|
||||
Entry("remap-auto.container", "remap-auto.container"),
|
||||
Entry("remap-auto2.container", "remap-auto2.container"),
|
||||
Entry("volume.container", "volume.container"),
|
||||
|
||||
Entry("basic.volume", "basic.volume"),
|
||||
|
Reference in New Issue
Block a user