Read UID/GID mapping information from containers and images
Read UID/GID mapping information when creating or importing containers, and if there is mapping information, use it when building runtime configurations. Mounting sysfs in a user namespace requires that we also have our own network namespace, so default to creating one for that case. Switch permissions on files that we bind in so that they're writable from inside of the container. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com> Closes: #700 Approved by: rhatdan
This commit is contained in:
parent
40325d3e31
commit
1395e1805a
17
buildah.go
17
buildah.go
|
@ -116,6 +116,11 @@ type Builder struct {
|
|||
// DefaultMountsFilePath is the file path holding the mounts to be mounted in "host-path:container-path" format.
|
||||
DefaultMountsFilePath string `json:"defaultMountsFilePath,omitempty"`
|
||||
|
||||
// NamespaceOptions controls how we set up the namespaces for processes that we run in the container.
|
||||
NamespaceOptions NamespaceOptions
|
||||
// ID mapping options to use when running processes in the container with non-host user namespaces.
|
||||
IDMappingOptions IDMappingOptions
|
||||
|
||||
CommonBuildOpts *CommonBuildOptions
|
||||
}
|
||||
|
||||
|
@ -136,6 +141,8 @@ type BuilderInfo struct {
|
|||
OCIv1 v1.Image
|
||||
Docker docker.V2Image
|
||||
DefaultMountsFilePath string
|
||||
NamespaceOptions NamespaceOptions
|
||||
IDMappingOptions IDMappingOptions
|
||||
}
|
||||
|
||||
// GetBuildInfo gets a pointer to a Builder object and returns a BuilderInfo object from it.
|
||||
|
@ -156,6 +163,8 @@ func GetBuildInfo(b *Builder) BuilderInfo {
|
|||
OCIv1: b.OCIv1,
|
||||
Docker: b.Docker,
|
||||
DefaultMountsFilePath: b.DefaultMountsFilePath,
|
||||
NamespaceOptions: b.NamespaceOptions,
|
||||
IDMappingOptions: b.IDMappingOptions,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,7 +259,13 @@ type BuilderOptions struct {
|
|||
// DefaultMountsFilePath is the file path holding the mounts to be
|
||||
// mounted in "host-path:container-path" format
|
||||
DefaultMountsFilePath string
|
||||
CommonBuildOpts *CommonBuildOptions
|
||||
// NamespaceOptions controls how we set up namespaces for processes that
|
||||
// we might need to run using the container's root filesystem.
|
||||
NamespaceOptions NamespaceOptions
|
||||
// ID mapping options to use if we're setting up our own user namespace.
|
||||
IDMappingOptions *IDMappingOptions
|
||||
|
||||
CommonBuildOpts *CommonBuildOptions
|
||||
}
|
||||
|
||||
// ImportOptions are used to initialize a Builder from an existing container
|
||||
|
|
16
import.go
16
import.go
|
@ -16,6 +16,7 @@ func importBuilderDataFromImage(ctx context.Context, store storage.Store, system
|
|||
manifest := []byte{}
|
||||
config := []byte{}
|
||||
imageName := ""
|
||||
uidmap, gidmap := convertStorageIDMaps(storage.DefaultStoreOptions.UIDMap, storage.DefaultStoreOptions.GIDMap)
|
||||
|
||||
if imageID != "" {
|
||||
ref, err := is.Transport.ParseStoreReference(store, imageID)
|
||||
|
@ -39,6 +40,13 @@ func importBuilderDataFromImage(ctx context.Context, store storage.Store, system
|
|||
if len(img.Names) > 0 {
|
||||
imageName = img.Names[0]
|
||||
}
|
||||
if img.TopLayer != "" {
|
||||
layer, err4 := store.Layer(img.TopLayer)
|
||||
if err4 != nil {
|
||||
return nil, errors.Wrapf(err4, "error reading information about image's top layer")
|
||||
}
|
||||
uidmap, gidmap = convertStorageIDMaps(layer.UIDMap, layer.GIDMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +61,13 @@ func importBuilderDataFromImage(ctx context.Context, store storage.Store, system
|
|||
ContainerID: containerID,
|
||||
ImageAnnotations: map[string]string{},
|
||||
ImageCreatedBy: "",
|
||||
NamespaceOptions: DefaultNamespaceOptions(),
|
||||
IDMappingOptions: IDMappingOptions{
|
||||
HostUIDMapping: len(uidmap) == 0,
|
||||
HostGIDMapping: len(uidmap) == 0,
|
||||
UIDMap: uidmap,
|
||||
GIDMap: gidmap,
|
||||
},
|
||||
}
|
||||
|
||||
builder.initConfig()
|
||||
|
@ -87,6 +102,7 @@ func importBuilder(ctx context.Context, store storage.Store, options ImportOptio
|
|||
if builder.FromImage != "" {
|
||||
builder.Docker.ContainerConfig.Image = builder.FromImage
|
||||
}
|
||||
builder.IDMappingOptions.UIDMap, builder.IDMappingOptions.GIDMap = convertStorageIDMaps(c.UIDMap, c.GIDMap)
|
||||
|
||||
err = builder.Save()
|
||||
if err != nil {
|
||||
|
|
12
new.go
12
new.go
|
@ -278,6 +278,9 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uidmap, gidmap := convertStorageIDMaps(container.UIDMap, container.GIDMap)
|
||||
namespaceOptions := DefaultNamespaceOptions()
|
||||
namespaceOptions.AddOrReplace(options.NamespaceOptions...)
|
||||
|
||||
builder := &Builder{
|
||||
store: store,
|
||||
|
@ -293,7 +296,14 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions
|
|||
ProcessLabel: processLabel,
|
||||
MountLabel: mountLabel,
|
||||
DefaultMountsFilePath: options.DefaultMountsFilePath,
|
||||
CommonBuildOpts: options.CommonBuildOpts,
|
||||
NamespaceOptions: namespaceOptions,
|
||||
IDMappingOptions: IDMappingOptions{
|
||||
HostUIDMapping: len(uidmap) == 0,
|
||||
HostGIDMapping: len(uidmap) == 0,
|
||||
UIDMap: uidmap,
|
||||
GIDMap: gidmap,
|
||||
},
|
||||
CommonBuildOpts: options.CommonBuildOpts,
|
||||
}
|
||||
|
||||
if options.Mount {
|
||||
|
|
288
run.go
288
run.go
|
@ -69,11 +69,40 @@ func (t TerminalPolicy) String() string {
|
|||
return fmt.Sprintf("unrecognized terminal setting %d", t)
|
||||
}
|
||||
|
||||
// NamespaceOption controls how we set up a namespace when launching processes.
|
||||
type NamespaceOption struct {
|
||||
// Name specifies the type of namespace, typically matching one of the
|
||||
// ...Namespace constants defined in
|
||||
// github.com/opencontainers/runtime-spec/specs-go.
|
||||
Name string
|
||||
// Host is used to force our processes to use the host's namespace of
|
||||
// this type.
|
||||
Host bool
|
||||
// Path is the path of the namespace to attach our process to, if Host
|
||||
// is not set. If Host is not set and Path is also empty, a new
|
||||
// namespace will be created for the process that we're starting.
|
||||
Path string
|
||||
}
|
||||
|
||||
// NamespaceOptions provides some helper methods for a slice of NamespaceOption
|
||||
// structs.
|
||||
type NamespaceOptions []NamespaceOption
|
||||
|
||||
// IDMappingOptions controls how we set up UID/GID mapping when we set up a
|
||||
// user namespace.
|
||||
type IDMappingOptions struct {
|
||||
HostUIDMapping bool
|
||||
HostGIDMapping bool
|
||||
UIDMap []specs.LinuxIDMapping
|
||||
GIDMap []specs.LinuxIDMapping
|
||||
}
|
||||
|
||||
// RunOptions can be used to alter how a command is run in the container.
|
||||
type RunOptions struct {
|
||||
// Hostname is the hostname we set for the running container.
|
||||
Hostname string
|
||||
// Runtime is the name of the command to run. It should accept the same arguments that runc does.
|
||||
// Runtime is the name of the command to run. It should accept the same arguments
|
||||
// that runc does, and produce similar output.
|
||||
Runtime string
|
||||
// Args adds global arguments for the runtime.
|
||||
Args []string
|
||||
|
@ -91,8 +120,13 @@ type RunOptions struct {
|
|||
Cmd []string
|
||||
// Entrypoint is an override for the configured entry point.
|
||||
Entrypoint []string
|
||||
// NetworkDisabled puts the container into its own network namespace.
|
||||
// NetworkDisabled skips configuration of network interfaces and routing
|
||||
// when setting up a new network namespace (i.e., when not joining another's
|
||||
// namespace and not just using the host's namespace), effectively leaving
|
||||
// the process without a usable network.
|
||||
NetworkDisabled bool
|
||||
// NamespaceOptions controls how we set up the namespaces for the process.
|
||||
NamespaceOptions NamespaceOptions
|
||||
// Terminal provides a way to specify whether or not the command should
|
||||
// be run with a pseudoterminal. By default (DefaultTerminal), a
|
||||
// terminal is used if os.Stdout is connected to a terminal, but that
|
||||
|
@ -103,6 +137,59 @@ type RunOptions struct {
|
|||
Quiet bool
|
||||
}
|
||||
|
||||
// DefaultNamespaceOptions returns the default namespace settings from the
|
||||
// runtime-tools generator library.
|
||||
func DefaultNamespaceOptions() NamespaceOptions {
|
||||
options := NamespaceOptions{
|
||||
{Name: string(specs.CgroupNamespace), Host: true},
|
||||
{Name: string(specs.IPCNamespace), Host: true},
|
||||
{Name: string(specs.MountNamespace), Host: true},
|
||||
{Name: string(specs.NetworkNamespace), Host: true},
|
||||
{Name: string(specs.PIDNamespace), Host: true},
|
||||
{Name: string(specs.UserNamespace), Host: true},
|
||||
{Name: string(specs.UTSNamespace), Host: true},
|
||||
}
|
||||
g := generate.New()
|
||||
spec := g.Spec()
|
||||
if spec.Linux != nil {
|
||||
for _, ns := range spec.Linux.Namespaces {
|
||||
options.AddOrReplace(NamespaceOption{
|
||||
Name: string(ns.Type),
|
||||
Path: ns.Path,
|
||||
})
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// Find the configuration for the namespace of the given type. If there are
|
||||
// duplicates, find the _last_ one of the type, since we assume it was appended
|
||||
// more recently.
|
||||
func (n *NamespaceOptions) Find(namespace string) *NamespaceOption {
|
||||
for i := range *n {
|
||||
j := len(*n) - 1 - i
|
||||
if (*n)[j].Name == namespace {
|
||||
return &((*n)[j])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddOrReplace either adds or replaces the configuration for a given namespace.
|
||||
func (n *NamespaceOptions) AddOrReplace(options ...NamespaceOption) {
|
||||
nextOption:
|
||||
for _, option := range options {
|
||||
for i := range *n {
|
||||
j := len(*n) - 1 - i
|
||||
if (*n)[j].Name == option.Name {
|
||||
(*n)[j] = option
|
||||
continue nextOption
|
||||
}
|
||||
}
|
||||
*n = append(*n, option)
|
||||
}
|
||||
}
|
||||
|
||||
func addRlimits(ulimit []string, g *generate.Generator) error {
|
||||
var (
|
||||
ul *units.Ulimit
|
||||
|
@ -213,7 +300,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, optionMounts
|
|||
Source: src,
|
||||
Destination: dest,
|
||||
Type: "bind",
|
||||
Options: []string{"rbind", "ro"},
|
||||
Options: []string{"rbind"},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -222,10 +309,11 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, optionMounts
|
|||
return errors.Wrapf(err, "error determining work directory for container %q", b.ContainerID)
|
||||
}
|
||||
|
||||
// Add secrets mounts
|
||||
// Add secrets mounts, unless they conflict.
|
||||
secretMounts := secrets.SecretMounts(b.MountLabel, cdir, b.DefaultMountsFilePath)
|
||||
for _, mount := range secretMounts {
|
||||
if haveMount(mount.Destination) {
|
||||
// Already have something to mount there, so skip this one.
|
||||
continue
|
||||
}
|
||||
mounts = append(mounts, mount)
|
||||
|
@ -340,6 +428,115 @@ func (b *Builder) addNetworkConfig(rdir, hostPath string) (string, error) {
|
|||
return cfile, nil
|
||||
}
|
||||
|
||||
func setupMaskedPaths(g *generate.Generator) {
|
||||
for _, mp := range []string{
|
||||
"/proc/kcore",
|
||||
"/proc/latency_stats",
|
||||
"/proc/timer_list",
|
||||
"/proc/timer_stats",
|
||||
"/proc/sched_debug",
|
||||
"/proc/scsi",
|
||||
"/sys/firmware",
|
||||
} {
|
||||
g.AddLinuxMaskedPaths(mp)
|
||||
}
|
||||
}
|
||||
|
||||
func setupReadOnlyPaths(g *generate.Generator) {
|
||||
for _, rp := range []string{
|
||||
"/proc/asound",
|
||||
"/proc/bus",
|
||||
"/proc/fs",
|
||||
"/proc/irq",
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
} {
|
||||
g.AddLinuxReadonlyPaths(rp)
|
||||
}
|
||||
}
|
||||
|
||||
func setupSeccomp(spec *specs.Spec, seccompProfilePath string) error {
|
||||
if seccompProfilePath != "unconfined" {
|
||||
if seccompProfilePath != "" {
|
||||
seccompProfile, err := ioutil.ReadFile(seccompProfilePath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "opening seccomp profile (%s) failed", seccompProfilePath)
|
||||
}
|
||||
seccompConfig, err := seccomp.LoadProfile(string(seccompProfile), spec)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "loading seccomp profile (%s) failed", seccompProfilePath)
|
||||
}
|
||||
spec.Linux.Seccomp = seccompConfig
|
||||
} else {
|
||||
seccompConfig, err := seccomp.GetDefaultProfile(spec)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "loading seccomp profile (%s) failed", seccompProfilePath)
|
||||
}
|
||||
spec.Linux.Seccomp = seccompConfig
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupApparmor(spec *specs.Spec, apparmorProfile string) error {
|
||||
spec.Process.ApparmorProfile = apparmorProfile
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupTerminal(g *generate.Generator, terminalPolicy TerminalPolicy) {
|
||||
switch terminalPolicy {
|
||||
case DefaultTerminal:
|
||||
g.SetProcessTerminal(terminal.IsTerminal(int(os.Stdout.Fd())))
|
||||
case WithTerminal:
|
||||
g.SetProcessTerminal(true)
|
||||
case WithoutTerminal:
|
||||
g.SetProcessTerminal(false)
|
||||
}
|
||||
}
|
||||
|
||||
func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, idmapOptions IDMappingOptions, networkDisabled bool) error {
|
||||
// Set namespace options in the container configuration.
|
||||
for _, namespaceOption := range namespaceOptions {
|
||||
if namespaceOption.Host {
|
||||
if err := g.RemoveLinuxNamespace(namespaceOption.Name); err != nil {
|
||||
return errors.Wrapf(err, "error removing %q namespace for run", namespaceOption.Name)
|
||||
}
|
||||
} else if err := g.AddOrReplaceLinuxNamespace(namespaceOption.Name, namespaceOption.Path); err != nil {
|
||||
if namespaceOption.Path == "" {
|
||||
return errors.Wrapf(err, "error adding new %q namespace for run", namespaceOption.Name)
|
||||
}
|
||||
return errors.Wrapf(err, "error adding %q namespace %q for run", namespaceOption.Name, namespaceOption.Path)
|
||||
}
|
||||
}
|
||||
// If we've got mappings, we're going to have to create a user namespace.
|
||||
if len(idmapOptions.UIDMap) > 0 || len(idmapOptions.GIDMap) > 0 {
|
||||
if err := g.AddOrReplaceLinuxNamespace(specs.UserNamespace, ""); err != nil {
|
||||
return errors.Wrapf(err, "error adding new %q namespace for run", string(specs.UserNamespace))
|
||||
}
|
||||
for _, m := range idmapOptions.UIDMap {
|
||||
g.AddLinuxUIDMapping(m.HostID, m.ContainerID, m.Size)
|
||||
}
|
||||
for _, m := range idmapOptions.GIDMap {
|
||||
g.AddLinuxGIDMapping(m.HostID, m.ContainerID, m.Size)
|
||||
}
|
||||
if !networkDisabled {
|
||||
if err := g.AddOrReplaceLinuxNamespace(specs.NetworkNamespace, ""); err != nil {
|
||||
return errors.Wrapf(err, "error adding new %q namespace for run", string(specs.NetworkNamespace))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := g.RemoveLinuxNamespace(specs.UserNamespace); err != nil {
|
||||
return errors.Wrapf(err, "error removing %q namespace for run", string(specs.UserNamespace))
|
||||
}
|
||||
if !networkDisabled {
|
||||
if err := g.RemoveLinuxNamespace(specs.NetworkNamespace); err != nil {
|
||||
return errors.Wrapf(err, "error removing %q namespace for run", string(specs.NetworkNamespace))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run runs the specified command in the container's root filesystem.
|
||||
func (b *Builder) Run(command []string, options RunOptions) error {
|
||||
var user specs.User
|
||||
|
@ -353,7 +550,8 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
logrus.Errorf("error removing %q: %v", path, err2)
|
||||
}
|
||||
}()
|
||||
g := generate.New()
|
||||
gp := generate.New()
|
||||
g := &gp
|
||||
|
||||
for _, envSpec := range append(b.Env(), options.Env...) {
|
||||
env := strings.SplitN(envSpec, "=", 2)
|
||||
|
@ -366,7 +564,7 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
return errors.Errorf("Invalid format on container you must recreate the container")
|
||||
}
|
||||
|
||||
if err := addCommonOptsToSpec(b.CommonBuildOpts, &g); err != nil {
|
||||
if err := addCommonOptsToSpec(b.CommonBuildOpts, g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -396,49 +594,30 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
logrus.Errorf("error unmounting container: %v", err2)
|
||||
}
|
||||
}()
|
||||
for _, mp := range []string{
|
||||
"/proc/kcore",
|
||||
"/proc/latency_stats",
|
||||
"/proc/timer_list",
|
||||
"/proc/timer_stats",
|
||||
"/proc/sched_debug",
|
||||
"/proc/scsi",
|
||||
"/sys/firmware",
|
||||
} {
|
||||
g.AddLinuxMaskedPaths(mp)
|
||||
}
|
||||
|
||||
for _, rp := range []string{
|
||||
"/proc/asound",
|
||||
"/proc/bus",
|
||||
"/proc/fs",
|
||||
"/proc/irq",
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
} {
|
||||
g.AddLinuxReadonlyPaths(rp)
|
||||
}
|
||||
setupMaskedPaths(g)
|
||||
setupReadOnlyPaths(g)
|
||||
|
||||
g.SetRootPath(mountPoint)
|
||||
switch options.Terminal {
|
||||
case DefaultTerminal:
|
||||
g.SetProcessTerminal(terminal.IsTerminal(int(os.Stdout.Fd())))
|
||||
case WithTerminal:
|
||||
g.SetProcessTerminal(true)
|
||||
case WithoutTerminal:
|
||||
g.SetProcessTerminal(false)
|
||||
|
||||
setupTerminal(g, options.Terminal)
|
||||
|
||||
namespaceOptions := DefaultNamespaceOptions()
|
||||
namespaceOptions.AddOrReplace(b.NamespaceOptions...)
|
||||
namespaceOptions.AddOrReplace(options.NamespaceOptions...)
|
||||
if err = setupNamespaces(g, namespaceOptions, b.IDMappingOptions, options.NetworkDisabled); err != nil {
|
||||
return err
|
||||
}
|
||||
if !options.NetworkDisabled {
|
||||
if err = g.RemoveLinuxNamespace("network"); err != nil {
|
||||
return errors.Wrapf(err, "error removing network namespace for run")
|
||||
}
|
||||
}
|
||||
user, err = b.user(mountPoint, options.User)
|
||||
if err != nil {
|
||||
if user, err = b.user(mountPoint, options.User); err != nil {
|
||||
return err
|
||||
}
|
||||
g.SetProcessUID(user.UID)
|
||||
g.SetProcessGID(user.GID)
|
||||
|
||||
// Now grab the spec from the generator. Set the generator to nil so that future contributors
|
||||
// will quickly be able to tell that they're supposed to be modifying the spec directly from here.
|
||||
spec := g.Spec()
|
||||
g = nil
|
||||
if spec.Process.Cwd == "" {
|
||||
spec.Process.Cwd = DefaultWorkingDir
|
||||
}
|
||||
|
@ -447,27 +626,13 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
}
|
||||
|
||||
// Set the apparmor profile name.
|
||||
g.SetProcessApparmorProfile(b.CommonBuildOpts.ApparmorProfile)
|
||||
if err = setupApparmor(spec, b.CommonBuildOpts.ApparmorProfile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the seccomp configuration using the specified profile name.
|
||||
if b.CommonBuildOpts.SeccompProfilePath != "unconfined" {
|
||||
if b.CommonBuildOpts.SeccompProfilePath != "" {
|
||||
seccompProfile, err := ioutil.ReadFile(b.CommonBuildOpts.SeccompProfilePath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "opening seccomp profile (%s) failed", b.CommonBuildOpts.SeccompProfilePath)
|
||||
}
|
||||
seccompConfig, err := seccomp.LoadProfile(string(seccompProfile), spec)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "loading seccomp profile (%s) failed", b.CommonBuildOpts.SeccompProfilePath)
|
||||
}
|
||||
spec.Linux.Seccomp = seccompConfig
|
||||
} else {
|
||||
seccompConfig, err := seccomp.GetDefaultProfile(spec)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "loading seccomp profile (%s) failed", b.CommonBuildOpts.SeccompProfilePath)
|
||||
}
|
||||
spec.Linux.Seccomp = seccompConfig
|
||||
}
|
||||
if err = setupSeccomp(spec, b.CommonBuildOpts.SeccompProfilePath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cgroupMnt := specs.Mount{
|
||||
|
@ -476,7 +641,8 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
Source: "cgroup",
|
||||
Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
|
||||
}
|
||||
g.AddMount(cgroupMnt)
|
||||
spec.Mounts = append(spec.Mounts, cgroupMnt)
|
||||
|
||||
hostFile, err := b.addNetworkConfig(path, "/etc/hosts")
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
22
util.go
22
util.go
|
@ -2,7 +2,9 @@ package buildah
|
|||
|
||||
import (
|
||||
"github.com/containers/storage/pkg/chrootarchive"
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/reexec"
|
||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -32,3 +34,23 @@ func copyStringSlice(s []string) []string {
|
|||
copy(t, s)
|
||||
return t
|
||||
}
|
||||
|
||||
func convertStorageIDMaps(UIDMap, GIDMap []idtools.IDMap) ([]rspec.LinuxIDMapping, []rspec.LinuxIDMapping) {
|
||||
uidmap := make([]rspec.LinuxIDMapping, 0, len(UIDMap))
|
||||
gidmap := make([]rspec.LinuxIDMapping, 0, len(GIDMap))
|
||||
for _, m := range UIDMap {
|
||||
uidmap = append(uidmap, rspec.LinuxIDMapping{
|
||||
HostID: uint32(m.HostID),
|
||||
ContainerID: uint32(m.ContainerID),
|
||||
Size: uint32(m.Size),
|
||||
})
|
||||
}
|
||||
for _, m := range GIDMap {
|
||||
gidmap = append(gidmap, rspec.LinuxIDMapping{
|
||||
HostID: uint32(m.HostID),
|
||||
ContainerID: uint32(m.ContainerID),
|
||||
Size: uint32(m.Size),
|
||||
})
|
||||
}
|
||||
return uidmap, gidmap
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue