1
0
mirror of https://github.com/containers/buildah.git synced 2026-02-05 09:45:38 +01:00

Add support for containers.conf

This is a rework of Qi Wang's patches.
Import package pkg/config from containers/common to read containers.conf
This patch allows users to specify default values stored in containers.conf
that will modify the behaviour of buildah tool.

Signed-off-by: Qi Wang <qiwan@redhat.com>
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #2011
Approved by: TomSweeneyRedHat
This commit is contained in:
Daniel J Walsh
2020-01-14 07:12:56 -05:00
committed by Atomic Bot
parent bf14e6ca2d
commit 09d1c24e3d
29 changed files with 2294 additions and 99 deletions

View File

@@ -8,10 +8,10 @@ import (
"io/ioutil"
"os"
"path/filepath"
"sort"
"time"
"github.com/containers/buildah/docker"
"github.com/containers/buildah/util"
"github.com/containers/image/v5/types"
"github.com/containers/storage"
"github.com/containers/storage/pkg/ioutils"
@@ -180,13 +180,8 @@ type Builder struct {
CNIConfigDir string
// ID mapping options to use when running processes in the container with non-host user namespaces.
IDMappingOptions IDMappingOptions
// AddCapabilities is a list of capabilities to add to the default set when running
// commands in the container.
AddCapabilities []string
// DropCapabilities is a list of capabilities to remove from the default set,
// after processing the AddCapabilities set, when running commands in the container.
// If a capability appears in both lists, it will be dropped.
DropCapabilities []string
// Capabilities is a list of capabilities to use when running commands in the container.
Capabilities []string
// PrependedEmptyLayers are history entries that we'll add to a
// committed image, after any history items that we inherit from a base
// image, but before the history item for the layer that we're
@@ -229,13 +224,11 @@ type BuilderInfo struct {
DefaultMountsFilePath string
Isolation string
NamespaceOptions NamespaceOptions
Capabilities []string
ConfigureNetwork string
CNIPluginPath string
CNIConfigDir string
IDMappingOptions IDMappingOptions
DefaultCapabilities []string
AddCapabilities []string
DropCapabilities []string
History []v1.History
Devices []configs.Device
}
@@ -255,6 +248,7 @@ func GetBuildInfo(b *Builder) BuilderInfo {
EmptyLayer: false,
})
history = append(history, copyHistory(b.AppendedEmptyLayers)...)
sort.Strings(b.Capabilities)
return BuilderInfo{
Type: b.Type,
FromImage: b.FromImage,
@@ -278,9 +272,7 @@ func GetBuildInfo(b *Builder) BuilderInfo {
CNIPluginPath: b.CNIPluginPath,
CNIConfigDir: b.CNIConfigDir,
IDMappingOptions: b.IDMappingOptions,
DefaultCapabilities: append([]string{}, util.DefaultCapabilities...),
AddCapabilities: append([]string{}, b.AddCapabilities...),
DropCapabilities: append([]string{}, b.DropCapabilities...),
Capabilities: b.Capabilities,
History: history,
Devices: b.Devices,
}
@@ -406,19 +398,16 @@ type BuilderOptions struct {
CNIConfigDir string
// ID mapping options to use if we're setting up our own user namespace.
IDMappingOptions *IDMappingOptions
// AddCapabilities is a list of capabilities to add to the default set when
// Capabilities is a list of capabilities to use when
// running commands in the container.
AddCapabilities []string
// DropCapabilities is a list of capabilities to remove from the default set,
// after processing the AddCapabilities set, when running commands in the
// container. If a capability appears in both lists, it will be dropped.
DropCapabilities []string
Capabilities []string
CommonBuildOpts *CommonBuildOptions
// Format for the container image
Format string
// Devices are the additional devices to add to the containers
Devices []configs.Device
//DefaultEnv for containers
DefaultEnv []string
}
// ImportOptions are used to initialize a Builder from an existing container

View File

@@ -69,7 +69,7 @@ func init() {
budFlags.StringVar(&budFlagResults.Runtime, "runtime", util.Runtime(), "`path` to an alternate runtime. Use BUILDAH_RUNTIME environment variable to override.")
layerFlags := buildahcli.GetLayerFlags(&layerFlagsResults)
fromAndBudFlags := buildahcli.GetFromAndBudFlags(&fromAndBudResults, &userNSResults, &namespaceResults)
fromAndBudFlags := buildahcli.GetFromAndBudFlags(&fromAndBudResults, &userNSResults, &namespaceResults, defaultContainerConfig)
flags.AddFlagSet(&budFlags)
flags.AddFlagSet(&layerFlags)
@@ -224,7 +224,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budResults) error {
runtimeFlags = append(runtimeFlags, "--"+arg)
}
commonOpts, err := parse.CommonBuildOptions(c)
commonOpts, err := parse.CommonBuildOptions(c, defaultContainerConfig)
if err != nil {
return err
}
@@ -281,7 +281,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budResults) error {
defaultsMountFile, _ := c.PersistentFlags().GetString("defaults-mount-file")
transientMounts := []imagebuildah.Mount{}
for _, volume := range iopts.Volumes {
for _, volume := range append(defaultContainerConfig.Containers.AdditionalVolumes, iopts.Volumes...) {
mount, err := parse.Volume(volume)
if err != nil {
return err
@@ -291,7 +291,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budResults) error {
}
devices := []configs.Device{}
for _, device := range iopts.Devices {
for _, device := range append(defaultContainerConfig.Containers.AdditionalDevices, iopts.Devices...) {
dev, err := parse.DeviceFromPath(device)
if err != nil {
return err
@@ -299,6 +299,8 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budResults) error {
devices = append(devices, dev...)
}
capabilities := defaultContainerConfig.Capabilities("", iopts.CapAdd, iopts.CapDrop)
options := imagebuildah.BuildOptions{
ContextDirectory: contextDir,
PullPolicy: pullPolicy,
@@ -322,8 +324,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budResults) error {
CNIPluginPath: iopts.CNIPlugInPath,
CNIConfigDir: iopts.CNIConfigDir,
IDMappingOptions: idmappingOptions,
AddCapabilities: iopts.CapAdd,
DropCapabilities: iopts.CapDrop,
Capabilities: capabilities,
CommonBuildOpts: commonOpts,
DefaultMountsFilePath: defaultsMountFile,
IIDFile: iopts.Iidfile,
@@ -338,6 +339,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budResults) error {
Target: iopts.Target,
TransientMounts: transientMounts,
Devices: devices,
DefaultEnv: defaultContainerConfig.GetDefaultEnv(),
}
if iopts.Quiet {

View File

@@ -8,6 +8,7 @@ import (
"github.com/containers/buildah"
"github.com/containers/buildah/pkg/umask"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/unshare"
"github.com/containers/image/v5/image"
is "github.com/containers/image/v5/storage"
@@ -20,7 +21,12 @@ import (
"github.com/spf13/pflag"
)
var needToShutdownStore = false
var (
// defaultContainerConfig structure defines the default container
// configuration, including customizations made in containers.conf
defaultContainerConfig = getDefaultConfig()
needToShutdownStore = false
)
func getStore(c *cobra.Command) (storage.Store, error) {
options, err := storage.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID())
@@ -424,3 +430,13 @@ Flags:
{{end}}
`
}
func getDefaultConfig() *config.Config {
var err error
conf, err := config.NewConfig("")
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading container config %v\n", err)
os.Exit(1)
}
return conf
}

View File

@@ -176,33 +176,32 @@ func updateConfig(builder *buildah.Builder, c *cobra.Command, iopts configResult
}
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) EXPOSE %s", strings.Join(iopts.ports, " "))
}
if c.Flag("env").Changed {
for _, envSpec := range iopts.env {
env := strings.SplitN(envSpec, "=", 2)
if len(env) > 1 {
var unexpanded []string
getenv := func(name string) string {
for _, envvar := range builder.Env() {
val := strings.SplitN(envvar, "=", 2)
if len(val) == 2 && val[0] == name {
return val[1]
}
for _, envSpec := range iopts.env {
env := strings.SplitN(envSpec, "=", 2)
if len(env) > 1 {
var unexpanded []string
getenv := func(name string) string {
for _, envvar := range builder.Env() {
val := strings.SplitN(envvar, "=", 2)
if len(val) == 2 && val[0] == name {
return val[1]
}
logrus.Errorf("error expanding variable %q: no value set in configuration", name)
unexpanded = append(unexpanded, name)
return name
}
env[1] = os.Expand(env[1], getenv)
builder.SetEnv(env[0], env[1])
} else if strings.HasSuffix(env[0], "-") {
env[0] = strings.TrimSuffix(env[0], "-")
builder.UnsetEnv(env[0])
} else {
logrus.Errorf("error setting variable %q: no value given.", env[0])
logrus.Errorf("error expanding variable %q: no value set in configuration", name)
unexpanded = append(unexpanded, name)
return name
}
env[1] = os.Expand(env[1], getenv)
builder.SetEnv(env[0], env[1])
} else if strings.HasSuffix(env[0], "-") {
env[0] = strings.TrimSuffix(env[0], "-")
builder.UnsetEnv(env[0])
} else {
logrus.Errorf("error setting variable %q: no value given.", env[0])
}
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) ENV %s", strings.Join(iopts.env, " "))
}
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) ENV %s", strings.Join(iopts.env, " "))
if c.Flag("entrypoint").Changed {
updateEntrypoint(builder, iopts)
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) ENTRYPOINT %s", iopts.entrypoint)

View File

@@ -78,7 +78,7 @@ func init() {
flags.BoolVar(&opts.tlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
// Add in the common flags
fromAndBudFlags := buildahcli.GetFromAndBudFlags(&fromAndBudResults, &userNSResults, &namespaceResults)
fromAndBudFlags := buildahcli.GetFromAndBudFlags(&fromAndBudResults, &userNSResults, &namespaceResults, defaultContainerConfig)
flags.AddFlagSet(&fromAndBudFlags)
rootCmd.AddCommand(fromCommand)
@@ -179,7 +179,6 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error {
if err := buildahcli.CheckAuthFile(iopts.authfile); err != nil {
return err
}
systemContext, err := parse.SystemContextFromOptions(c)
if err != nil {
return errors.Wrapf(err, "error building system context")
@@ -218,7 +217,7 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error {
return err
}
commonOpts, err := parse.CommonBuildOptions(c)
commonOpts, err := parse.CommonBuildOptions(c, defaultContainerConfig)
if err != nil {
return err
}
@@ -244,7 +243,7 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error {
}
devices := []configs.Device{}
for _, device := range iopts.Devices {
for _, device := range append(defaultContainerConfig.Containers.AdditionalDevices, iopts.Devices...) {
dev, err := parse.DeviceFromPath(device)
if err != nil {
return err
@@ -252,6 +251,7 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error {
devices = append(devices, dev...)
}
capabilities := defaultContainerConfig.Capabilities("", iopts.CapAdd, iopts.CapDrop)
options := buildah.BuilderOptions{
FromImage: args[0],
Container: iopts.name,
@@ -265,12 +265,12 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error {
CNIPluginPath: iopts.CNIPlugInPath,
CNIConfigDir: iopts.CNIConfigDir,
IDMappingOptions: idmappingOptions,
AddCapabilities: iopts.CapAdd,
DropCapabilities: iopts.CapDrop,
Capabilities: capabilities,
CommonBuildOpts: commonOpts,
Format: format,
BlobDirectory: iopts.BlobCache,
Devices: devices,
DefaultEnv: defaultContainerConfig.GetDefaultEnv(),
}
if !iopts.quiet {

View File

@@ -126,6 +126,7 @@ func runCmd(c *cobra.Command, args []string, iopts runInputOptions) error {
}
options := buildah.RunOptions{
Env: defaultContainerConfig.Containers.Env,
Hostname: iopts.hostname,
Runtime: iopts.runtime,
Args: runtimeFlags,

3
go.mod
View File

@@ -5,11 +5,12 @@ go 1.12
require (
github.com/blang/semver v3.5.0+incompatible // indirect
github.com/containernetworking/cni v0.7.1
github.com/containers/common v0.0.7
github.com/containers/common v0.0.8-0.20200108114752-d87ce6ce296b
github.com/containers/image/v5 v5.1.0
github.com/containers/storage v1.15.5
github.com/cyphar/filepath-securejoin v0.2.2
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.4.0
github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316

4
go.sum
View File

@@ -62,6 +62,10 @@ github.com/containers/common v0.0.5 h1:Hi4+eyUZx8hXB4reLNPbdT6XT8MGMAzdlbg8V+Wif
github.com/containers/common v0.0.5/go.mod h1:lhWV3MLhO1+KGE2x6v9+K38MxpjXGso+edmpkFnCOqI=
github.com/containers/common v0.0.7 h1:eKYZLKfJ2d/RNDgecLDFv45cHb4imYzIcrQHx1Y029M=
github.com/containers/common v0.0.7/go.mod h1:lhWV3MLhO1+KGE2x6v9+K38MxpjXGso+edmpkFnCOqI=
github.com/containers/common v0.0.8-0.20200106141003-a79791495fd1 h1:udiDqxQSdunVXNjBW4icHrnFLNOiTpvH6GRG+ywA4f4=
github.com/containers/common v0.0.8-0.20200106141003-a79791495fd1/go.mod h1:lhWV3MLhO1+KGE2x6v9+K38MxpjXGso+edmpkFnCOqI=
github.com/containers/common v0.0.8-0.20200108114752-d87ce6ce296b h1:G+DKyzrku0fC5Qa3paArNBERTwRleTg45ypY0qjo7YM=
github.com/containers/common v0.0.8-0.20200108114752-d87ce6ce296b/go.mod h1:ss8uGpUsaDE4DPmaVFOjzKrlgf5eUnSAWL+d/PYGaoM=
github.com/containers/image/v4 v4.0.1 h1:idNGHChj0Pyv3vLrxul2oSVMZLeFqpoq3CjLeVgapSQ=
github.com/containers/image/v4 v4.0.1/go.mod h1:0ASJH1YgJiX/eqFZObqepgsvIA4XjCgpyfwn9pDGafA=
github.com/containers/image/v5 v5.0.0 h1:arnXgbt1ucsC/ndtSpiQY87rA0UjhF+/xQnPzqdBDn4=

View File

@@ -122,14 +122,10 @@ type BuildOptions struct {
// ID mapping options to use if we're setting up our own user namespace
// when handling RUN instructions.
IDMappingOptions *buildah.IDMappingOptions
// AddCapabilities is a list of capabilities to add to the default set when
// Capabilities is a list of capabilities to use when
// handling RUN instructions.
AddCapabilities []string
// DropCapabilities is a list of capabilities to remove from the default set
// when handling RUN instructions. If a capability appears in both lists, it
// will be dropped.
DropCapabilities []string
CommonBuildOpts *buildah.CommonBuildOptions
Capabilities []string
CommonBuildOpts *buildah.CommonBuildOptions
// DefaultMountsFilePath is the file path holding the mounts to be mounted in "host-path:container-path" format
DefaultMountsFilePath string
// IIDFile tells the builder to write the image ID to the specified file
@@ -160,6 +156,8 @@ type BuildOptions struct {
Target string
// Devices are the additional devices to add to the containers
Devices []configs.Device
//DefaultEnv for containers
DefaultEnv []string
}
// BuildDockerfiles parses a set of one or more Dockerfiles (which may be
@@ -250,6 +248,7 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt
return "", nil, errors.Wrapf(err, "error creating build executor")
}
b := imagebuilder.NewBuilder(options.Args)
b.Env = append(options.DefaultEnv, b.Env...)
stages, err := imagebuilder.NewStages(mainNode, b)
if err != nil {
return "", nil, errors.Wrap(err, "error reading multiple stages")

View File

@@ -91,8 +91,7 @@ type Executor struct {
excludes []string
unusedArgs map[string]struct{}
buildArgs map[string]string
addCapabilities []string
dropCapabilities []string
capabilities []string
devices []configs.Device
}
@@ -148,8 +147,7 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod
blobDirectory: options.BlobDirectory,
unusedArgs: make(map[string]struct{}),
buildArgs: options.Args,
addCapabilities: options.AddCapabilities,
dropCapabilities: options.DropCapabilities,
capabilities: options.Capabilities,
devices: options.Devices,
}
if exec.err == nil {

View File

@@ -605,8 +605,7 @@ func (s *StageExecutor) prepare(ctx context.Context, stage imagebuilder.Stage, f
CommonBuildOpts: s.executor.commonBuildOptions,
DefaultMountsFilePath: s.executor.defaultMountsFilePath,
Format: s.executor.outputFormat,
AddCapabilities: s.executor.addCapabilities,
DropCapabilities: s.executor.dropCapabilities,
Capabilities: s.executor.capabilities,
Devices: s.executor.devices,
}

15
new.go
View File

@@ -398,14 +398,13 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions
UIDMap: uidmap,
GIDMap: gidmap,
},
AddCapabilities: copyStringSlice(options.AddCapabilities),
DropCapabilities: copyStringSlice(options.DropCapabilities),
CommonBuildOpts: options.CommonBuildOpts,
TopLayer: topLayer,
Args: options.Args,
Format: options.Format,
TempVolumes: map[string]bool{},
Devices: options.Devices,
Capabilities: copyStringSlice(options.Capabilities),
CommonBuildOpts: options.CommonBuildOpts,
TopLayer: topLayer,
Args: options.Args,
Format: options.Format,
TempVolumes: map[string]bool{},
Devices: options.Devices,
}
if options.Mount {

View File

@@ -12,6 +12,7 @@ import (
"github.com/containers/buildah"
"github.com/containers/buildah/util"
"github.com/containers/common/pkg/config"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/spf13/pflag"
@@ -175,7 +176,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
return fs
}
func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults, namespaceResults *NameSpaceResults) pflag.FlagSet {
func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults, namespaceResults *NameSpaceResults, defaultConfig *config.Config) pflag.FlagSet {
fs := pflag.FlagSet{}
fs.StringSliceVar(&flags.AddHost, "add-host", []string{}, "add a custom host-to-IP mapping (`host:ip`) (default [])")
fs.StringVar(&flags.BlobCache, "blob-cache", "", "assume image blobs in the specified directory will be available for pushing")
@@ -190,10 +191,10 @@ func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults,
fs.Uint64VarP(&flags.CPUShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
fs.StringVar(&flags.CPUSetCPUs, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
fs.StringVar(&flags.CPUSetMems, "cpuset-mems", "", "memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.")
fs.StringArrayVar(&flags.Devices, "device", []string{}, "Additional devices to be used within containers (default [])")
fs.StringSliceVar(&flags.DNSSearch, "dns-search", []string{}, "Set custom DNS search domains")
fs.StringSliceVar(&flags.DNSServers, "dns", []string{}, "Set custom DNS servers or disable it completely by setting it to 'none', which prevents the automatic creation of `/etc/resolv.conf`.")
fs.StringSliceVar(&flags.DNSOptions, "dns-option", []string{}, "Set custom DNS options")
fs.StringArrayVar(&flags.Devices, "device", defaultConfig.Containers.AdditionalDevices, "Additional devices to be used within containers (default [])")
fs.StringSliceVar(&flags.DNSSearch, "dns-search", defaultConfig.Containers.DNSSearches, "Set custom DNS search domains")
fs.StringSliceVar(&flags.DNSServers, "dns", defaultConfig.Containers.DNSServers, "Set custom DNS servers or disable it completely by setting it to 'none', which prevents the automatic creation of `/etc/resolv.conf`.")
fs.StringSliceVar(&flags.DNSOptions, "dns-option", defaultConfig.Containers.DNSOptions, "Set custom DNS options")
fs.BoolVar(&flags.HTTPProxy, "http-proxy", true, "pass through HTTP Proxy environment variables")
fs.StringVar(&flags.Isolation, "isolation", DefaultIsolation(), "`type` of process isolation to use. Use BUILDAH_ISOLATION environment variable to override.")
fs.StringVarP(&flags.Memory, "memory", "m", "", "memory limit (format: <number>[<unit>], where unit = b, k, m or g)")
@@ -207,9 +208,9 @@ func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults,
panic(fmt.Sprintf("error marking override-arch as hidden: %v", err))
}
fs.StringArrayVar(&flags.SecurityOpt, "security-opt", []string{}, "security options (default [])")
fs.StringVar(&flags.ShmSize, "shm-size", "65536k", "size of '/dev/shm'. The format is `<number><unit>`.")
fs.StringSliceVar(&flags.Ulimit, "ulimit", []string{}, "ulimit options (default [])")
fs.StringArrayVarP(&flags.Volumes, "volume", "v", []string{}, "bind mount a volume into the container (default [])")
fs.StringVar(&flags.ShmSize, "shm-size", defaultConfig.Containers.ShmSize, "size of '/dev/shm'. The format is `<number><unit>`.")
fs.StringSliceVar(&flags.Ulimit, "ulimit", defaultConfig.Containers.DefaultUlimits, "ulimit options")
fs.StringArrayVarP(&flags.Volumes, "volume", "v", defaultConfig.Containers.AdditionalVolumes, "bind mount a volume into the container")
// Add in the usernamespace and namespaceflags
usernsFlags := GetUserNSFlags(usernsResults)

View File

@@ -14,6 +14,7 @@ import (
"unicode"
"github.com/containers/buildah"
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/types"
"github.com/containers/storage/pkg/idtools"
units "github.com/docker/go-units"
@@ -43,7 +44,7 @@ var (
)
// CommonBuildOptions parses the build options from the bud cli
func CommonBuildOptions(c *cobra.Command) (*buildah.CommonBuildOptions, error) {
func CommonBuildOptions(c *cobra.Command, defaultConfig *config.Config) (*buildah.CommonBuildOptions, error) {
var (
memoryLimit int64
memorySwap int64
@@ -80,6 +81,7 @@ func CommonBuildOptions(c *cobra.Command) (*buildah.CommonBuildOptions, error) {
noDNS = false
dnsServers, _ := c.Flags().GetStringSlice("dns")
dnsServers = append(defaultConfig.Containers.DNSServers, dnsServers...)
for _, server := range dnsServers {
if strings.ToLower(server) == "none" {
noDNS = true
@@ -90,11 +92,13 @@ func CommonBuildOptions(c *cobra.Command) (*buildah.CommonBuildOptions, error) {
}
dnsSearch, _ := c.Flags().GetStringSlice("dns-search")
dnsSearch = append(defaultConfig.Containers.DNSSearches, dnsSearch...)
if noDNS && len(dnsSearch) > 0 {
return nil, errors.Errorf("invalid --dns-search, --dns-search may not be used with --dns=none")
}
dnsOptions, _ := c.Flags().GetStringSlice("dns-option")
dnsOptions = append(defaultConfig.Containers.DNSOptions, dnsOptions...)
if noDNS && len(dnsOptions) > 0 {
return nil, errors.Errorf("invalid --dns-option, --dns-option may not be used with --dns=none")
}
@@ -111,6 +115,8 @@ func CommonBuildOptions(c *cobra.Command) (*buildah.CommonBuildOptions, error) {
cpuShares, _ := c.Flags().GetUint64("cpu-shares")
httpProxy, _ := c.Flags().GetBool("http-proxy")
ulimit, _ := c.Flags().GetStringSlice("ulimit")
ulimit = append(defaultConfig.Containers.DefaultUlimits, ulimit...)
commonOpts := &buildah.CommonBuildOptions{
AddHost: addHost,
CgroupParent: c.Flag("cgroup-parent").Value.String(),

View File

@@ -31,6 +31,7 @@ import (
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/reexec"
"github.com/containers/storage/pkg/stringid"
"github.com/docker/docker/oci/caps"
"github.com/docker/go-units"
"github.com/docker/libnetwork/resolvconf"
"github.com/docker/libnetwork/types"
@@ -1799,21 +1800,27 @@ func setupCapDrop(g *generate.Generator, caps ...string) error {
return nil
}
func setupCapabilities(g *generate.Generator, firstAdds, firstDrops, secondAdds, secondDrops []string) error {
func setupCapabilities(g *generate.Generator, defaultCapabilities, adds, drops []string) error {
g.ClearProcessCapabilities()
if err := setupCapAdd(g, util.DefaultCapabilities...); err != nil {
if err := setupCapAdd(g, defaultCapabilities...); err != nil {
return err
}
if err := setupCapAdd(g, firstAdds...); err != nil {
for _, c := range adds {
if strings.ToLower(c) == "all" {
adds = caps.GetAllCapabilities()
break
}
}
for _, c := range drops {
if strings.ToLower(c) == "all" {
g.ClearProcessCapabilities()
return nil
}
}
if err := setupCapAdd(g, adds...); err != nil {
return err
}
if err := setupCapDrop(g, firstDrops...); err != nil {
return err
}
if err := setupCapAdd(g, secondAdds...); err != nil {
return err
}
return setupCapDrop(g, secondDrops...)
return setupCapDrop(g, drops...)
}
// Search for a command that isn't given as an absolute path using the $PATH
@@ -1880,7 +1887,7 @@ func (b *Builder) configureUIDGID(g *generate.Generator, mountPoint string, opti
if err != nil {
return "", err
}
if err := setupCapabilities(g, b.AddCapabilities, b.DropCapabilities, options.AddCapabilities, options.DropCapabilities); err != nil {
if err := setupCapabilities(g, b.Capabilities, options.AddCapabilities, options.DropCapabilities); err != nil {
return "", err
}
g.SetProcessUID(user.UID)
@@ -1901,6 +1908,7 @@ func (b *Builder) configureUIDGID(g *generate.Generator, mountPoint string, opti
func (b *Builder) configureEnvironment(g *generate.Generator, options RunOptions) {
g.ClearProcessEnv()
if b.CommonBuildOpts.HTTPProxy {
for _, envSpec := range []string{
"http_proxy",

60
tests/containers.conf Normal file
View File

@@ -0,0 +1,60 @@
[containers]
# A list of ulimits to be set in containers by default, specified as
# "<ulimit name>=<soft limit>:<hard limit>", for example:
# "nofile=1024:2048"
# See setrlimit(2) for a list of resource names.
# Any limit not specified here will be inherited from the process launching the
# container engine.
# Ulimits has limits for non privileged container engines.
#
default_ulimits = [
"nofile=500:500",
]
# Environment variable list for the conmon process; used for passing necessary
# environment variables to conmon or the runtime.
#
env = [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"foo=bar",
]
# container engines use container separation using MAC(SELinux) labeling.
# Flag is ignored on label disabled systems.
#
label = true
# Size of /dev/shm. Specified as <number><unit>.
# Unit is optional, values:
# b (bytes), k (kilobytes), m (megabytes), or g (gigabytes).
# If the unit is omitted, the system uses bytes.
#
shm_size = "200k"
# List of additional devices. Specified as
# "<device-on-host>:<device-on-container>:<permissions>", for example:
# "/dev/sdc:/dev/xvdc:rwm".
# If it is empty or commented out, only the default devices will be used
#
additional_devices = [
]
# List of default capabilities for containers. If it is empty or commented out,
# the default capabilities defined in the container engine will be added.
#
default_capabilities = [
"AUDIT_WRITE",
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"FSETID",
"KILL",
"MKNOD",
"NET_BIND_SERVICE",
"NET_RAW",
"SETGID",
"SETPCAP",
"SETUID",
"SYS_CHROOT",
]

View File

@@ -0,0 +1,93 @@
#!/usr/bin/env bats
load helpers
@test "containers.conf env test" {
export CONTAINERS_CONF=${TESTSDIR}/containers.conf
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah --log-level=error run $cid sh -c 'env | grep "foo=bar"'
}
@test "containers.conf selinux test" {
if ! which selinuxenabled > /dev/null 2> /dev/null ; then
skip "No selinuxenabled executable"
elif ! selinuxenabled ; then
skip "selinux is disabled"
fi
export CONTAINERS_CONF=${TESTSDIR}/containers.conf
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah --log-level=error run $cid sh -c "cat /proc/self/attr/current | grep container_t"
buildah rm $cid
export CONTAINERS_CONF=${TESTSDIR}/containers1.conf
sed "s/^label = true/label = false/g" ${TESTSDIR}/containers.conf > ${TESTSDIR}/containers1.conf
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah 1 --log-level=error run $cid sh -c "cat /proc/self/attr/current | grep container_t"
rm ${TESTSDIR}/containers1.conf
}
@test "containers.conf ulimit test" {
if test "$BUILDAH_ISOLATION" = "chroot" -o "$BUILDAH_ISOLATION" = "rootless" ; then
skip "BUILDAH_ISOLATION = $BUILDAH_ISOLATION"
fi
export CONTAINERS_CONF=${TESTSDIR}/containers.conf
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah --log-level=error run $cid awk '/open files/{print $4}' /proc/self/limits
expect_output "500" "limits: open files (w/file limit)"
cid=$(buildah from --pull --ulimit nofile=300:400 --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah --log-level=error run $cid awk '/open files/{print $4}' /proc/self/limits
expect_output "300" "limits: open files (w/file limit)"
}
@test "containers.conf additional devices test" {
if test "$BUILDAH_ISOLATION" = "chroot" -o "$BUILDAH_ISOLATION" = "rootless" ; then
skip "BUILDAH_ISOLATION = $BUILDAH_ISOLATION"
fi
export CONTAINERS_CONF=${TESTSDIR}/containers.conf
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah 1 --log-level=error run $cid ls /dev/foo1
buildah rm $cid
sed '/additional_devices.*/a "/dev/foo:\/dev\/foo1:rmw",' ${TESTSDIR}/containers.conf > ${TESTSDIR}/containers1.conf
rm -f /dev/foo; mknod /dev/foo c 1 1
export CONTAINERS_CONF=${TESTSDIR}/containers1.conf
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah --log-level=error run $cid ls /dev/foo1
rm -f /dev/foo
rm ${TESTSDIR}/containers1.conf
}
@test "containers.conf capabilities test" {
export CONTAINERS_CONF=${TESTSDIR}/containers.conf
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah --log-level=error run $cid sh -c 'grep CapEff /proc/self/status | cut -f2'
CapEff=$output
expect_output "00000000280425fb"
buildah rm $cid
sed "/AUDIT_WRITE/d" ${TESTSDIR}/containers.conf > ${TESTSDIR}/containers1.conf
export CONTAINERS_CONF=${TESTSDIR}/containers1.conf
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah --log-level=error run $cid sh -c 'grep CapEff /proc/self/status | cut -f2'
buildah rm $cid
test "$output" != "$CapEff"
rm ${TESTSDIR}/containers1.conf
}
@test "containers.conf /dev/shm test" {
if test "$BUILDAH_ISOLATION" = "chroot" -o "$BUILDAH_ISOLATION" = "rootless" ; then
skip "BUILDAH_ISOLATION = $BUILDAH_ISOLATION"
fi
export CONTAINERS_CONF=${TESTSDIR}/containers.conf
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json docker.io/alpine)
run_buildah --log-level=error run $cid sh -c 'df /dev/shm | awk '\''/shm/{print $4}'\'''
expect_output "200"
}

173
vendor/github.com/containers/common/pkg/caps/caps.go generated vendored Normal file
View File

@@ -0,0 +1,173 @@
package caps // originally imported "github.com/docker/docker/oci/caps"
import (
"strings"
"github.com/pkg/errors"
"github.com/syndtr/gocapability/capability"
)
var (
capabilityList Capabilities
// InvalidCapability indicates the caller specified invalid capability
InvalidCapability = errors.New("invalid capability")
)
func init() {
last := capability.CAP_LAST_CAP
// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
if last == capability.Cap(63) {
last = capability.CAP_BLOCK_SUSPEND
}
for _, cap := range capability.List() {
if cap > last {
continue
}
capabilityList = append(capabilityList,
&CapabilityMapping{
Key: "CAP_" + strings.ToUpper(cap.String()),
Value: cap,
},
)
}
}
type (
// CapabilityMapping maps linux capability name to its value of capability.Cap type
// Capabilities is one of the security systems in Linux Security Module (LSM)
// framework provided by the kernel.
// For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html
CapabilityMapping struct {
Key string `json:"key,omitempty"`
Value capability.Cap `json:"value,omitempty"`
}
// Capabilities contains all CapabilityMapping
Capabilities []*CapabilityMapping
)
// String returns <key> of CapabilityMapping
func (c *CapabilityMapping) String() string {
return c.Key
}
// GetCapability returns CapabilityMapping which contains specific key
func GetCapability(key string) *CapabilityMapping {
for _, capp := range capabilityList {
if capp.Key == key {
cpy := *capp
return &cpy
}
}
return nil
}
// GetAllCapabilities returns all of the capabilities
func GetAllCapabilities() []string {
output := make([]string, len(capabilityList))
for i, capability := range capabilityList {
output[i] = capability.String()
}
return output
}
// inSlice tests whether a string is contained in a slice of strings or not.
func inSlice(slice []string, s string) bool {
for _, ss := range slice {
if s == ss {
return true
}
}
return false
}
const allCapabilities = "ALL"
// NormalizeLegacyCapabilities normalizes, and validates CapAdd/CapDrop capabilities
// by upper-casing them, and adding a CAP_ prefix (if not yet present).
//
// This function also accepts the "ALL" magic-value, that's used by CapAdd/CapDrop.
func NormalizeLegacyCapabilities(caps []string) ([]string, error) {
var normalized []string
valids := GetAllCapabilities()
for _, c := range caps {
c = strings.ToUpper(c)
if c == allCapabilities {
normalized = append(normalized, c)
continue
}
if !strings.HasPrefix(c, "CAP_") {
c = "CAP_" + c
}
if !inSlice(valids, c) {
return nil, errors.Wrap(InvalidCapability, c)
}
normalized = append(normalized, c)
}
return normalized, nil
}
// ValidateCapabilities validates if caps only contains valid capabilities
func ValidateCapabilities(caps []string) error {
valids := GetAllCapabilities()
for _, c := range caps {
if !inSlice(valids, c) {
return errors.Wrap(InvalidCapability, c)
}
}
return nil
}
// TweakCapabilities tweaks capabilities by adding, dropping, or overriding
// capabilities in the basics capabilities list.
func TweakCapabilities(basics, adds, drops, capabilities []string, privileged bool) ([]string, error) {
switch {
case privileged:
// Privileged containers get all capabilities
return GetAllCapabilities(), nil
case capabilities != nil:
// Use custom set of capabilities
if err := ValidateCapabilities(capabilities); err != nil {
return nil, err
}
return capabilities, nil
case len(adds) == 0 && len(drops) == 0:
// Nothing to tweak; we're done
return basics, nil
}
capDrop, err := NormalizeLegacyCapabilities(drops)
if err != nil {
return nil, err
}
capAdd, err := NormalizeLegacyCapabilities(adds)
if err != nil {
return nil, err
}
var caps []string
switch {
case inSlice(capAdd, allCapabilities):
// Add all capabilities except ones on capDrop
for _, c := range GetAllCapabilities() {
if !inSlice(capDrop, c) {
caps = append(caps, c)
}
}
case inSlice(capDrop, allCapabilities):
// "Drop" all capabilities; use what's in capAdd instead
caps = capAdd
default:
// First drop some capabilities
for _, c := range basics {
if !inSlice(capDrop, c) {
caps = append(caps, c)
}
}
// Then add the list of capabilities from capAdd
caps = append(caps, capAdd...)
}
return caps, nil
}

View File

@@ -0,0 +1,847 @@
package config
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"github.com/BurntSushi/toml"
"github.com/containers/common/pkg/caps"
"github.com/containers/common/pkg/unshare"
"github.com/containers/storage"
units "github.com/docker/go-units"
selinux "github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const (
// DefaultContainersConfig holds the default containers config path
DefaultContainersConfig = "/usr/share/containers/containers.conf"
// OverrideContainersConfig holds the default config paths overridden by the root user
OverrideContainersConfig = "/etc/containers/containers.conf"
// UserOverrideContainersConfig holds the containers config path overridden by the rootless user
UserOverrideContainersConfig = ".config/containers/containers.conf"
)
// RuntimeStateStore is a constant indicating which state store implementation
// should be used by libpod
type RuntimeStateStore int
const (
// InvalidStateStore is an invalid state store
InvalidStateStore RuntimeStateStore = iota
// InMemoryStateStore is an in-memory state that will not persist data
// on containers and pods between libpod instances or after system
// reboot
InMemoryStateStore RuntimeStateStore = iota
// SQLiteStateStore is a state backed by a SQLite database
// It is presently disabled
SQLiteStateStore RuntimeStateStore = iota
// BoltDBStateStore is a state backed by a BoltDB database
BoltDBStateStore RuntimeStateStore = iota
)
// Config contains configuration options for container tools
type Config struct {
// Containers specify settings that configure how containers will run ont the system
Containers ContainersConfig `toml:"containers"`
// Libpod specifies how the container engine based on Libpod will run
Libpod LibpodConfig `toml:"libpod"`
// Network section defines the configuration of CNI Plugins
Network NetworkConfig `toml:"network"`
}
// ContainersConfig represents the "containers" TOML config table
// containers global options for containers tools
type ContainersConfig struct {
// Devices to add to all containers
AdditionalDevices []string `toml:"additional_devices"`
// Volumes to add to all containers
AdditionalVolumes []string `toml:"additional_volumes"`
// ApparmorProfile is the apparmor profile name which is used as the
// default for the runtime.
ApparmorProfile string `toml:"apparmor_profile"`
// CGroupManager is the CGroup Manager to use Valid values are "cgroupfs"
// and "systemd".
CgroupManager string `toml:"cgroup_manager"`
// Default way to create a cgroup namespace for the container
CgroupNS string `toml:"cgroupns"`
// Capabilities to add to all containers.
DefaultCapabilities []string `toml:"default_capabilities"`
// Sysctls to add to all containers.
DefaultSysctls []string `toml:"default_sysctls"`
// DefaultUlimits specifies the default ulimits to apply to containers
DefaultUlimits []string `toml:"default_ulimits"`
// DefaultMountsFile is the path to the default mounts file for testing
DefaultMountsFile string `toml:"-"`
// DNSServers set default DNS servers.
DNSServers []string `toml:"dns_servers"`
// DNSOptions set default DNS options.
DNSOptions []string `toml:"dns_options"`
// DNSSearches set default DNS search domains.
DNSSearches []string `toml:"dns_searches"`
// EnableLabeling tells the container engines whether to use MAC
// Labeling to separate containers (SELinux)
EnableLabeling bool `toml:"label"`
// Env is the environment variable list for container process.
Env []string `toml:"env"`
// EnvHost Pass all host environment variables into the container.
EnvHost bool `toml:"env_host"`
// HTTPProxy is the proxy environment variable list to apply to container process
HTTPProxy bool `toml:"http_proxy"`
// Init tells container runtimes whether to run init inside the
// container that forwards signals and reaps processes.
Init bool `toml:"init"`
// InitPath is the path for init to run if the Init bool is enabled
InitPath string `toml:"init_path"`
// IPCNS way to to create a ipc namespace for the container
IPCNS string `toml:"ipcns"`
// LogDriver for the container. For example: k8s-file and journald
LogDriver string `toml:"log_driver"`
// LogSizeMax is the maximum number of bytes after which the log file
// will be truncated. It can be expressed as a human-friendly string
// that is parsed to bytes.
// Negative values indicate that the log file won't be truncated.
LogSizeMax int64 `toml:"log_size_max"`
// NetNS indicates how to create a network namespace for the container
NetNS string `toml:"netns"`
// NoHosts tells container engine whether to create its own /etc/hosts
NoHosts bool `toml:"no_hosts"`
// PidsLimit is the number of processes each container is restricted to
// by the cgroup process number controller.
PidsLimit int64 `toml:"pids_limit"`
// PidNS indicates how to create a pid namespace for the container
PidNS string `toml:"pidns"`
// SeccompProfile is the seccomp.json profile path which is used as the
// default for the runtime.
SeccompProfile string `toml:"seccomp_profile"`
// ShmSize holds the size of /dev/shm.
ShmSize string `toml:"shm_size"`
// SignaturePolicyPath is the path to a signature policy to use for
// validating images. If left empty, the containers/image default signature
// policy will be used.
SignaturePolicyPath string `toml:"_"`
// UTSNS indicates how to create a UTS namespace for the container
UTSNS string `toml:"utsns"`
// UserNS indicates how to create a User namespace for the container
UserNS string `toml:"userns"`
// UserNSSize how many UIDs to allocate for automatically created UserNS
UserNSSize int `toml:"userns_size"`
}
// LibpodConfig contains configuration options used to set up a libpod runtime
type LibpodConfig struct {
// NOTE: when changing this struct, make sure to update (*Config).Merge().
// ConmonEnvVars are environment variables to pass to the Conmon binary
// when it is launched.
ConmonEnvVars []string `toml:"conmon_env_vars"`
// ConmonPath is the path to the Conmon binary used for managing containers.
// The first path pointing to a valid file will be used.
ConmonPath []string `toml:"conmon_path"`
//DetachKeys is the sequence of keys used to detach a container.
DetachKeys string `toml:"detach_keys"`
// EnablePortReservation determines whether libpod will reserve ports on the
// host when they are forwarded to containers. When enabled, when ports are
// forwarded to containers, they are held open by conmon as long as the
// container is running, ensuring that they cannot be reused by other
// programs on the host. However, this can cause significant memory usage if
// a container has many ports forwarded to it. Disabling this can save
// memory.
EnablePortReservation bool `toml:"enable_port_reservation"`
// EventsLogFilePath is where the events log is stored.
EventsLogFilePath string `toml:"events_logfile_path"`
// EventsLogger determines where events should be logged.
EventsLogger string `toml:"events_logger"`
// configuration files. When the same filename is present in in
// multiple directories, the file in the directory listed last in
// this slice takes precedence.
HooksDir []string `toml:"hooks_dir"`
// ImageDefaultTransport is the default transport method used to fetch
// images.
ImageDefaultTransport string `toml:"image_default_transport"`
// InfraCommand is the command run to start up a pod infra container.
InfraCommand string `toml:"infra_command"`
// InfraImage is the image a pod infra container will use to manage
// namespaces.
InfraImage string `toml:"infra_image"`
// InitPath is the path to the container-init binary.
InitPath string `toml:"init_path"`
// LockType is the type of locking to use.
LockType string `toml:"lock_type,omitempty"`
// Namespace is the libpod namespace to use. Namespaces are used to create
// scopes to separate containers and pods in the state. When namespace is
// set, libpod will only view containers and pods in the same namespace. All
// containers and pods created will default to the namespace set here. A
// namespace of "", the empty string, is equivalent to no namespace, and all
// containers and pods will be visible. The default namespace is "".
Namespace string `toml:"namespace,omitempty"`
// NetworkCmdPath is the path to the slirp4netns binary.
NetworkCmdPath string `toml:"network_cmd_path"`
// NoPivotRoot sets whether to set no-pivot-root in the OCI runtime.
NoPivotRoot bool `toml:"no_pivot_root"`
// NumLocks is the number of locks to make available for containers and
// pods.
NumLocks uint32 `toml:"num_locks,omitempty"`
// OCIRuntime is the OCI runtime to use.
OCIRuntime string `toml:"runtime"`
// OCIRuntimes are the set of configured OCI runtimes (default is runc).
OCIRuntimes map[string][]string `toml:"runtimes"`
// RuntimeSupportsJSON is the list of the OCI runtimes that support
// --format=json.
RuntimeSupportsJSON []string `toml:"runtime_supports_json"`
// RuntimeSupportsNoCgroups is a list of OCI runtimes that support
// running containers without CGroups.
RuntimeSupportsNoCgroups []string `toml:"runtime_supports_nocgroups"`
// SetOptions contains a subset of config options. It's used to indicate if
// a given option has either been set by the user or by a parsed libpod
// configuration file. If not, the corresponding option might be
// overwritten by values from the database. This behavior guarantess
// backwards compat with older version of libpod and Podman.
SetOptions
// SDNotify tells container engine to allow containers to notify the host systemd of
// readiness using the SD_NOTIFY mechanism.
SDNotify bool
// StateType is the type of the backing state store. Avoid using multiple
// values for this with the same containers/storage configuration on the
// same system. Different state types do not interact, and each will see a
// separate set of containers, which may cause conflicts in
// containers/storage. As such this is not exposed via the config file.
StateType RuntimeStateStore `toml:"-"`
// StaticDir is the path to a persistent directory to store container
// files.
StaticDir string `toml:"static_dir"`
// StorageConfig is the configuration used by containers/storage Not
// included in the on-disk config, use the dedicated containers/storage
// configuration file instead.
StorageConfig storage.StoreOptions `toml:"-"`
// TmpDir is the path to a temporary directory to store per-boot container
// files. Must be stored in a tmpfs.
TmpDir string `toml:"tmp_dir"`
// VolumePath is the default location that named volumes will be created
// under. This convention is followed by the default volume driver, but
// may not be by other drivers.
VolumePath string `toml:"volume_path"`
}
// SetOptions contains a subset of options in a Config. It's used to indicate if
// a given option has either been set by the user or by a parsed libpod
// configuration file. If not, the corresponding option might be overwritten by
// values from the database. This behavior guarantess backwards compat with
// older version of libpod and Podman.
type SetOptions struct {
// StorageConfigRunRootSet indicates if the RunRoot has been explicitly set
// by the config or by the user. It's required to guarantee backwards
// compatibility with older versions of libpod for which we must query the
// database configuration. Not included in the on-disk config.
StorageConfigRunRootSet bool `toml:"-"`
// StorageConfigGraphRootSet indicates if the RunRoot has been explicitly
// set by the config or by the user. It's required to guarantee backwards
// compatibility with older versions of libpod for which we must query the
// database configuration. Not included in the on-disk config.
StorageConfigGraphRootSet bool `toml:"-"`
// StorageConfigGraphDriverNameSet indicates if the GraphDriverName has been
// explicitly set by the config or by the user. It's required to guarantee
// backwards compatibility with older versions of libpod for which we must
// query the database configuration. Not included in the on-disk config.
StorageConfigGraphDriverNameSet bool `toml:"-"`
// StaticDirSet indicates if the StaticDir has been explicitly set by the
// config or by the user. It's required to guarantee backwards compatibility
// with older versions of libpod for which we must query the database
// configuration. Not included in the on-disk config.
StaticDirSet bool `toml:"-"`
// VolumePathSet indicates if the VolumePath has been explicitly set by the
// config or by the user. It's required to guarantee backwards compatibility
// with older versions of libpod for which we must query the database
// configuration. Not included in the on-disk config.
VolumePathSet bool `toml:"-"`
// TmpDirSet indicates if the TmpDir has been explicitly set by the config
// or by the user. It's required to guarantee backwards compatibility with
// older versions of libpod for which we must query the database
// configuration. Not included in the on-disk config.
TmpDirSet bool `toml:"-"`
}
// NetworkConfig represents the "network" TOML config table
type NetworkConfig struct {
// CNIPluginDirs is where CNI plugin binaries are stored.
CNIPluginDirs []string `toml:"cni_plugin_dirs"`
// DefaultNetwork is the network name of the default CNI network
// to attach pods to.
DefaultNetwork string `toml:"default_network,omitempty"`
// NetworkConfigDir is where CNI network configuration files are stored.
NetworkConfigDir string `toml:"network_config_dir"`
}
// NewConfig creates a new Config. It starts with an empty config and, if
// specified, merges the config at `userConfigPath` path. Depending if we're
// running as root or rootless, we then merge the system configuration followed
// by merging the default config (hard-coded default in memory).
// Note that the OCI runtime is hard-set to `crun` if we're running on a system
// with cgroupsv2. Other OCI runtimes are not yet supporting cgroupsv2. This
// might change in the future.
func NewConfig(userConfigPath string) (*Config, error) {
// Genereate the default config for the system
config, err := DefaultConfig()
if err != nil {
return nil, err
}
// If the caller specified a config path to use, then we read this
// rather then using the system defaults.
if userConfigPath != "" {
var err error
// ReadConfigFromFile reads in container config in the specified
// file and then merge changes with the current defauls.
config, err = ReadConfigFromFile(userConfigPath, config)
if err != nil {
return nil, errors.Wrapf(err, "error reading user config %q", userConfigPath)
}
}
// Now, gather the system configs and merge them as needed.
configs, err := systemConfigs()
if err != nil {
return nil, errors.Wrapf(err, "error finding config on system")
}
for _, path := range configs {
// Merge changes in later configs with the previous configs.
// Each config file that specified fields, will override the
// previous fields.
config, err := ReadConfigFromFile(path, config)
if err != nil {
return nil, errors.Wrapf(err, "error reading system config %q", path)
}
logrus.Debugf("Merged system config %q: %v", path, config)
}
config.checkCgroupsAndAdjustConfig()
config.addCAPPrefix()
if err := config.Validate(true); err != nil {
return nil, err
}
return config, nil
}
// ReadConfigFromFile reads the specified config file at `path` and attempts to
// unmarshal its content into a Config. The config param specifies the previos
// default config. If the path, only specifies a few fields in the Toml file
// the defaults from the config paramater will be used for all other fields.
func ReadConfigFromFile(path string, config *Config) (*Config, error) {
logrus.Debugf("Reading configuration file %q", path)
_, err := toml.DecodeFile(path, config)
if err != nil {
return nil, fmt.Errorf("unable to decode configuration %v: %v", path, err)
}
if config.Libpod.VolumePath != "" {
config.Libpod.VolumePathSet = true
}
if config.Libpod.StaticDir != "" {
config.Libpod.StaticDirSet = true
}
if config.Libpod.TmpDir != "" {
config.Libpod.TmpDirSet = true
}
return config, err
}
// Returns the list of configuration files, if they exist in order of hierarchy.
// The files are read in order and each new file can/will override previous
// file settings.
func systemConfigs() ([]string, error) {
configs := []string{}
path := os.Getenv("CONTAINERS_CONF")
if path != "" {
if _, err := os.Stat(path); err != nil {
return nil, errors.Wrap(err, "failed to stat of %s from CONTAINERS_CONF environment variable")
}
return append(configs, path), nil
}
if _, err := os.Stat(DefaultContainersConfig); err == nil {
configs = append(configs, DefaultContainersConfig)
}
if _, err := os.Stat(OverrideContainersConfig); err == nil {
configs = append(configs, OverrideContainersConfig)
}
if unshare.IsRootless() {
path, err := rootlessConfigPath()
if err != nil {
return nil, err
}
if _, err := os.Stat(path); err == nil {
configs = append(configs, path)
}
}
return configs, nil
}
// checkCgroupsAndAdjustConfig checks if we're running rootless with the systemd
// cgroup manager. In case the user session isn't available, we're switching the
// cgroup manager to cgroupfs. Note, this only applies to rootless.
func (c *Config) checkCgroupsAndAdjustConfig() {
if !unshare.IsRootless() || c.Containers.CgroupManager != SystemdCgroupsManager {
return
}
session := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
hasSession := session != ""
if hasSession && strings.HasPrefix(session, "unix:path=") {
_, err := os.Stat(strings.TrimPrefix(session, "unix:path="))
hasSession = err == nil
}
if !hasSession {
logrus.Warningf("The cgroups manager is set to systemd but there is no systemd user session available")
logrus.Warningf("For using systemd, you may need to login using an user session")
logrus.Warningf("Alternatively, you can enable lingering with: `loginctl enable-linger %d` (possibly as root)", unshare.GetRootlessUID())
logrus.Warningf("Falling back to --cgroup-manager=cgroupfs")
c.Containers.CgroupManager = CgroupfsCgroupsManager
}
}
func (c *Config) addCAPPrefix() {
toCAPPrefixed := func(cap string) string {
if !strings.HasPrefix(strings.ToLower(cap), "cap_") {
return "CAP_" + strings.ToUpper(cap)
}
return cap
}
for i, cap := range c.Containers.DefaultCapabilities {
c.Containers.DefaultCapabilities[i] = toCAPPrefixed(cap)
}
}
// Validate is the main entry point for library configuration validation.
// The parameter `onExecution` specifies if the validation should include
// execution checks. It returns an `error` on validation failure, otherwise
// `nil`.
func (c *Config) Validate(onExecution bool) error {
if err := c.Containers.Validate(); err != nil {
return errors.Wrapf(err, "containers config")
}
if !unshare.IsRootless() {
if err := c.Network.Validate(onExecution); err != nil {
return errors.Wrapf(err, "network config")
}
}
if !c.Containers.EnableLabeling {
selinux.SetDisabled()
}
return nil
}
// Validate is the main entry point for Libpod configuration validation
// It returns an `error` on validation failure, otherwise
// `nil`.
func (c *LibpodConfig) Validate() error {
// Relative paths can cause nasty bugs, because core paths we use could
// shift between runs (or even parts of the program - the OCI runtime
// uses a different working directory than we do, for example.
if !filepath.IsAbs(c.StaticDir) {
return fmt.Errorf("static directory must be an absolute path - instead got %q", c.StaticDir)
}
if !filepath.IsAbs(c.TmpDir) {
return fmt.Errorf("temporary directory must be an absolute path - instead got %q", c.TmpDir)
}
if !filepath.IsAbs(c.VolumePath) {
return fmt.Errorf("volume path must be an absolute path - instead got %q", c.VolumePath)
}
return nil
}
// Validate is the main entry point for containers configuration validation
// It returns an `error` on validation failure, otherwise
// `nil`.
func (c *ContainersConfig) Validate() error {
for _, u := range c.DefaultUlimits {
ul, err := units.ParseUlimit(u)
if err != nil {
return fmt.Errorf("unrecognized ulimit %s: %v", u, err)
}
_, err = ul.GetRlimit()
if err != nil {
return err
}
}
for _, d := range c.AdditionalDevices {
_, _, _, err := Device(d)
if err != nil {
return err
}
}
if c.LogSizeMax >= 0 && c.LogSizeMax < OCIBufSize {
return fmt.Errorf("log size max should be negative or >= %d", OCIBufSize)
}
if _, err := units.FromHumanSize(c.ShmSize); err != nil {
return fmt.Errorf("invalid --shm-size %s, %q", c.ShmSize, err)
}
return nil
}
// Validate is the main entry point for network configuration validation.
// The parameter `onExecution` specifies if the validation should include
// execution checks. It returns an `error` on validation failure, otherwise
// `nil`.
func (c *NetworkConfig) Validate(onExecution bool) error {
if onExecution {
err := IsDirectory(c.NetworkConfigDir)
if err != nil {
if os.IsNotExist(err) {
if err = os.MkdirAll(c.NetworkConfigDir, 0755); err != nil {
return errors.Wrapf(err, "Cannot create network_config_dir: %s", c.NetworkConfigDir)
}
} else {
return errors.Wrapf(err, "invalid network_config_dir: %s", c.NetworkConfigDir)
}
}
for _, pluginDir := range c.CNIPluginDirs {
if err := os.MkdirAll(pluginDir, 0755); err != nil {
return errors.Wrapf(err, "invalid cni_plugin_dirs entry")
}
}
}
return nil
}
// DBConfig is a set of Libpod runtime configuration settings that are saved in
// a State when it is first created, and can subsequently be retrieved.
type DBConfig struct {
LibpodRoot string
LibpodTmp string
StorageRoot string
StorageTmp string
GraphDriver string
VolumePath string
}
// MergeDBConfig merges the configuration from the database.
func (c *Config) MergeDBConfig(dbConfig *DBConfig) error {
if !c.Libpod.StorageConfigRunRootSet && dbConfig.StorageTmp != "" {
if c.Libpod.StorageConfig.RunRoot != dbConfig.StorageTmp &&
c.Libpod.StorageConfig.RunRoot != "" {
logrus.Debugf("Overriding run root %q with %q from database",
c.Libpod.StorageConfig.RunRoot, dbConfig.StorageTmp)
}
c.Libpod.StorageConfig.RunRoot = dbConfig.StorageTmp
}
if !c.Libpod.StorageConfigGraphRootSet && dbConfig.StorageRoot != "" {
if c.Libpod.StorageConfig.GraphRoot != dbConfig.StorageRoot &&
c.Libpod.StorageConfig.GraphRoot != "" {
logrus.Debugf("Overriding graph root %q with %q from database",
c.Libpod.StorageConfig.GraphRoot, dbConfig.StorageRoot)
}
c.Libpod.StorageConfig.GraphRoot = dbConfig.StorageRoot
}
if !c.Libpod.StorageConfigGraphDriverNameSet && dbConfig.GraphDriver != "" {
if c.Libpod.StorageConfig.GraphDriverName != dbConfig.GraphDriver &&
c.Libpod.StorageConfig.GraphDriverName != "" {
logrus.Errorf("User-selected graph driver %q overwritten by graph driver %q from database - delete libpod local files to resolve",
c.Libpod.StorageConfig.GraphDriverName, dbConfig.GraphDriver)
}
c.Libpod.StorageConfig.GraphDriverName = dbConfig.GraphDriver
}
if !c.Libpod.StaticDirSet && dbConfig.LibpodRoot != "" {
if c.Libpod.StaticDir != dbConfig.LibpodRoot && c.Libpod.StaticDir != "" {
logrus.Debugf("Overriding static dir %q with %q from database", c.Libpod.StaticDir, dbConfig.LibpodRoot)
}
c.Libpod.StaticDir = dbConfig.LibpodRoot
}
if !c.Libpod.TmpDirSet && dbConfig.LibpodTmp != "" {
if c.Libpod.TmpDir != dbConfig.LibpodTmp && c.Libpod.TmpDir != "" {
logrus.Debugf("Overriding tmp dir %q with %q from database", c.Libpod.TmpDir, dbConfig.LibpodTmp)
}
c.Libpod.TmpDir = dbConfig.LibpodTmp
c.Libpod.EventsLogFilePath = filepath.Join(dbConfig.LibpodTmp, "events", "events.log")
}
if !c.Libpod.VolumePathSet && dbConfig.VolumePath != "" {
if c.Libpod.VolumePath != dbConfig.VolumePath && c.Libpod.VolumePath != "" {
logrus.Debugf("Overriding volume path %q with %q from database", c.Libpod.VolumePath, dbConfig.VolumePath)
}
c.Libpod.VolumePath = dbConfig.VolumePath
}
return nil
}
// FindConmon iterates over (*Config).ConmonPath and returns the path to first
// (version) matching conmon binary. If non is found, we try to do a path lookup
// of "conmon".
func (c *Config) FindConmon() (string, error) {
foundOutdatedConmon := false
for _, path := range c.Libpod.ConmonPath {
stat, err := os.Stat(path)
if err != nil {
continue
}
if stat.IsDir() {
continue
}
if err := probeConmon(path); err != nil {
logrus.Warnf("Conmon at %s invalid: %v", path, err)
foundOutdatedConmon = true
continue
}
logrus.Debugf("Using conmon: %q", path)
return path, nil
}
// Search the $PATH as last fallback
if path, err := exec.LookPath("conmon"); err == nil {
if err := probeConmon(path); err != nil {
logrus.Warnf("Conmon at %s is invalid: %v", path, err)
foundOutdatedConmon = true
} else {
logrus.Debugf("Using conmon from $PATH: %q", path)
return path, nil
}
}
if foundOutdatedConmon {
return "", errors.Wrapf(ErrConmonOutdated,
"please update to v%d.%d.%d or later",
_conmonMinMajorVersion, _conmonMinMinorVersion, _conmonMinPatchVersion)
}
return "", errors.Wrapf(ErrInvalidArg,
"could not find a working conmon binary (configured options: %v)",
c.Libpod.ConmonPath)
}
// GetDefaultEnv returns the environment variables for the container.
// It will checn the HTTPProxy and HostEnv booleans and add the appropriate
// environment variables to the container.
func (c *Config) GetDefaultEnv() []string {
var env []string
if c.Containers.EnvHost {
env = append(env, os.Environ()...)
} else if c.Containers.HTTPProxy {
proxy := []string{"http_proxy", "https_proxy", "ftp_proxy", "no_proxy", "HTTP_PROXY", "HTTPS_PROXY", "FTP_PROXY", "NO_PROXY"}
for _, p := range proxy {
if val, ok := os.LookupEnv(p); ok {
env = append(env, fmt.Sprintf("%s=%s", p, val))
}
}
}
return append(env, c.Containers.Env...)
}
// Capabilities returns the capabilities parses the Add and Drop capability
// list from the default capabiltiies for the container
func (c *Config) Capabilities(user string, addCapabilities, dropCapabilities []string) []string {
userNotRoot := func(user string) bool {
if user == "" || user == "root" || user == "0" {
return false
}
return true
}
var capabilities []string
defaultCapabilities := c.Containers.DefaultCapabilities
if userNotRoot(user) {
defaultCapabilities = []string{}
}
mapCap := make(map[string]bool, len(defaultCapabilities))
for _, c := range addCapabilities {
if strings.ToLower(c) == "all" {
defaultCapabilities = caps.GetAllCapabilities()
addCapabilities = nil
break
}
}
for _, c := range append(defaultCapabilities, addCapabilities...) {
mapCap[c] = true
}
for _, c := range dropCapabilities {
if "all" == strings.ToLower(c) {
return capabilities
}
mapCap[c] = false
}
for cap, add := range mapCap {
if add {
capabilities = append(capabilities, cap)
}
}
return capabilities
}
// Device parses device mapping string to a src, dest & permissions string
// Valid values for device looklike:
// '/dev/sdc"
// '/dev/sdc:/dev/xvdc"
// '/dev/sdc:/dev/xvdc:rwm"
// '/dev/sdc:rm"
func Device(device string) (string, string, string, error) {
src := ""
dst := ""
permissions := "rwm"
split := strings.Split(device, ":")
switch len(split) {
case 3:
if !IsValidDeviceMode(split[2]) {
return "", "", "", fmt.Errorf("invalid device mode: %s", split[2])
}
permissions = split[2]
fallthrough
case 2:
if IsValidDeviceMode(split[1]) {
permissions = split[1]
} else {
if len(split[1]) == 0 || split[1][0] != '/' {
return "", "", "", fmt.Errorf("invalid device mode: %s", split[1])
}
dst = split[1]
}
fallthrough
case 1:
if !strings.HasPrefix(split[0], "/dev/") {
return "", "", "", fmt.Errorf("invalid device mode: %s", split[0])
}
src = split[0]
default:
return "", "", "", fmt.Errorf("invalid device specification: %s", device)
}
if dst == "" {
dst = src
}
return src, dst, permissions, nil
}
// IsValidDeviceMode checks if the mode for device is valid or not.
// IsValid mode is a composition of r (read), w (write), and m (mknod).
func IsValidDeviceMode(mode string) bool {
var legalDeviceMode = map[rune]bool{
'r': true,
'w': true,
'm': true,
}
if mode == "" {
return false
}
for _, c := range mode {
if !legalDeviceMode[c] {
return false
}
legalDeviceMode[c] = false
}
return true
}
// IsDirectory tests whether the given path exists and is a directory. It
// follows symlinks.
func IsDirectory(path string) error {
info, err := os.Stat(path)
if err != nil {
return err
}
if !info.Mode().IsDir() {
// Return a PathError to be consistent with os.Stat().
return &os.PathError{
Op: "stat",
Path: path,
Err: syscall.ENOTDIR,
}
}
return nil
}
func rootlessConfigPath() (string, error) {
home, err := unshare.HomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, UserOverrideContainersConfig), nil
}

View File

@@ -0,0 +1,7 @@
package config
import selinux "github.com/opencontainers/selinux/go-selinux"
func selinuxEnabled() bool {
return selinux.GetEnabled()
}

View File

@@ -0,0 +1,15 @@
// +build !windows
package config
// Defaults for linux/unix if none are specified
const (
cniConfigDir = "/etc/cni/net.d/"
)
var cniBinDir = []string{
"/usr/libexec/cni",
"/usr/lib/cni",
"/usr/local/lib/cni",
"/opt/cni/bin",
}

View File

@@ -0,0 +1,7 @@
// +build !linux
package config
func selinuxEnabled() bool {
return false
}

View File

@@ -0,0 +1,10 @@
// +build windows
package config
// Defaults for linux/unix if none are specified
const (
cniConfigDir = "C:\\cni\\etc\\net.d\\"
)
var cniBinDir = []string{"C:\\cni\\bin\\"}

View File

@@ -0,0 +1,366 @@
# The containers configuration file specifies all of the available configuration
# command-line options/flags for container runtime tools like Podman & Buildah,
# but in a TOML format that can be easily modified and versioned.
# Please refer to containers.conf(5) for details of all configuration options.
# Not all container tools implement all of the options.
# All of the options have hard coded defaults and these options will override
# the built in defaults. Users can then override these options via the command
# line. Container engines will read containers.conf files in up to three
# locations in the following order:
# 1. /usr/share/containers/containers.conf
# 2. /etc/containers/containers.conf
# 3. $HOME/.config/containers/containers.conf (Rootless containers ONLY)
# Items specified in the latter containers.conf, if they exist, override the
# previous containers.conf settings, or the default settings.
[containers]
# List of additional devices. Specified as
# "<device-on-host>:<device-on-container>:<permissions>", for example:
# "/dev/sdc:/dev/xvdc:rwm".
# If it is empty or commented out, only the default devices will be used
#
# additional_devices = []
# List of additional volumes. Specified as
# "<directory-on-host>:<directory-in-container>:<options>", for example:
# "/db:/var/lib/db:ro".
# If it is empty or commented out, no volumes will be added
#
# additional_volumes = []
# Used to change the name of the default AppArmor profile of container engines.
#
# apparmor_profile = "container-default"
# Default way to to create a cgroup namespace for the container
# Options are:
# `private` Create private Cgroup Namespace for the container.
# `host` Share host Cgroup Namespace with the container.
#
# cgroupns = "private"
# Cgroup management implementation used for the runtime.
# Valid options “systemd” or “cgroupfs”
#
# cgroup_manager = "systemd"
# List of default capabilities for containers. If it is empty or commented out,
# the default capabilities defined in the container engine will be added.
#
# default_capabilities = [
# "AUDIT_WRITE",
# "CHOWN",
# "DAC_OVERRIDE",
# "FOWNER",
# "FSETID",
# "KILL",
# "MKNOD",
# "NET_BIND_SERVICE",
# "NET_RAW",
# "SETGID",
# "SETPCAP",
# "SETUID",
# "SYS_CHROOT",
# ]
# A list of sysctls to be set in containers by default,
# specified as "name=value",
# for example:"net.ipv4.ping_group_range = 0 1000".
#
# default_sysctls = [
# "net.ipv4.ping_group_range=0 1000",
# ]
# A list of ulimits to be set in containers by default, specified as
# "<ulimit name>=<soft limit>:<hard limit>", for example:
# "nofile=1024:2048"
# See setrlimit(2) for a list of resource names.
# Any limit not specified here will be inherited from the process launching the
# container engine.
# Ulimits has limits for non privileged container engines.
#
# default_ulimits = [
# “nofile”=”1280:2560”,
# ]
# List of default DNS options to be added to /etc/resolv.conf inside of the container.
#
# dns_options = []
# List of default DNS search domains to be added to /etc/resolv.conf inside of the container.
#
# dns_searches = []
# Set default DNS servers.
# This option can be used to override the DNS configuration passed to the
# container. The special value “none” can be specified to disable creation of
# /etc/resolv.conf in the container.
# The /etc/resolv.conf file in the image will be used without changes.
#
# dns_servers = []
# Environment variable list for the conmon process; used for passing necessary
# environment variables to conmon or the runtime.
#
# env = [
# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
# ]
# Pass all host environment variables into the container.
#
# env_host = false
# Path to OCI hooks directories for automatically executed hooks.
#
# hooks_dir = [
# “/usr/share/containers/oci/hooks.d”,
# ]
# Default proxy environment variables passed into the container.
# The environment variables passed in include:
# http_proxy, https_proxy, ftp_proxy, no_proxy, and the upper case versions of
# these. This option is needed when host system uses a proxy but container
# should not use proxy. Proxy environment variables specified for the container
# in any other way will override the values passed from the host.
#
# http_proxy = true
# Run an init inside the container that forwards signals and reaps processes.
#
# init = false
# Container init binary, if init=true, this is the init binary to be used for containers.
#
# init_path = "/usr/libexec/podman/catatonit"
# Default way to to create an IPC namespace (POSIX SysV IPC) for the container
# Options are:
# `private` Create private IPC Namespace for the container.
# `host` Share host IPC Namespace with the container.
#
# ipcns = "private"
# container engines use container separation using MAC(SELinux) labeling.
# Flag is ignored on label disabled systems.
#
# label = true
# Logging driver for the container. Available options: k8s-file and journald.
#
# log_driver = "k8s-file"
# Maximum size allowed for the container log file. Negative numbers indicate
# that no size limit is imposed. If positive, it must be >= 8192 to match or
# exceed conmon's read buffer. The file is truncated and re-opened so the
# limit is never exceeded.
#
# log_size_max = -1
# Default way to to create a Network namespace for the container
# Options are:
# `private` Create private Network Namespace for the container.
# `host` Share host Network Namespace with the container.
# `none` Containers do not use the network
#
# netns = "private"
# Create /etc/hosts for the container. By default, container engines manage
# /etc/hosts, automatically adding the container's own IP address.
#
# no_hosts = false
# Maximum number of processes allowed in a container.
#
# pids_limit = 2048
# Default way to to create a PID namespace for the container
# Options are:
# `private` Create private PID Namespace for the container.
# `host` Share host PID Namespace with the container.
#
# pidns = "private"
# Path to the seccomp.json profile which is used as the default seccomp profile
# for the runtime.
#
# seccomp_profile = "/usr/share/containers/seccomp.json"
# Size of /dev/shm. Specified as <number><unit>.
# Unit is optional, values:
# b (bytes), k (kilobytes), m (megabytes), or g (gigabytes).
# If the unit is omitted, the system uses bytes.
#
# shm_size = "65536k"
# Default way to to create a UTS namespace for the container
# Options are:
# `private` Create private UTS Namespace for the container.
# `host` Share host UTS Namespace with the container.
#
# utsns = "private"
# Default way to to create a User namespace for the container
# Options are:
# `auto` Create unique User Namespace for the container.
# `host` Share host User Namespace with the container.
#
# userns = "host"
# Number of UIDs to allocate for the automatic container creation.
# UIDs are allocated from the “container” UIDs listed in
# /etc/subuid & /etc/subgid
#
# userns_size=65536
# The network table contains settings pertaining to the management of
# CNI plugins.
[network]
# Path to directory where CNI plugin binaries are located.
#
# cni_plugin_dirs = ["/usr/libexec/cni"]
# Path to the directory where CNI configuration files are located.
#
# network_config_dir = "/etc/cni/net.d/"
[libpod]
# Environment variables to pass into conmon
#
# conmon_env_vars = [
# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# ]
# Paths to look for the conmon container manager binary
#
# conmon_path = [
# "/usr/libexec/podman/conmon",
# "/usr/local/libexec/podman/conmon",
# "/usr/local/lib/podman/conmon",
# "/usr/bin/conmon",
# "/usr/sbin/conmon",
# "/usr/local/bin/conmon",
# "/usr/local/sbin/conmon"
# ]
# Specify the keys sequence used to detach a container.
# Format is a single character [a-Z] or a comma separated sequence of
# `ctrl-<value>`, where `<value>` is one of:
# `a-z`, `@`, `^`, `[`, `\`, `]`, `^` or `_`
#
# detach_keys = "ctrl-p,ctrl-q"
# Determines whether libpod will reserve ports on the host when they are
# forwarded to containers. When enabled, when ports are forwarded to containers,
# ports are held open by as long as the container is running, ensuring that
# they cannot be reused by other programs on the host. However, this can cause
# significant memory usage if a container has many ports forwarded to it.
# Disabling this can save memory.
#
# enable_port_reservation = true
# Selects which logging mechanism to use for container engine events.
# Valid values are `journald`, `file` and `none`.
#
# events_logger = "journald"
# Default transport method for pulling and pushing for images
#
# image_default_transport = "docker://"
# Default command to run the infra container
#
# infra_command = "/pause"
# Infra (pause) container image name for pod infra containers. When running a
# pod, we start a `pause` process in a container to hold open the namespaces
# associated with the pod. This container does nothing other then sleep,
# reserving the pods resources for the lifetime of the pod.
#
# infra_image = "k8s.gcr.io/pause:3.1"
# Specify the locking mechanism to use; valid values are "shm" and "file".
# Change the default only if you are sure of what you are doing, in general
# "file" is useful only on platforms where cgo is not available for using the
# faster "shm" lock type. You may need to run "podman system renumber" after
# you change the lock type.
#
# lock_type** = "shm"
# Default libpod namespace
# If libpod is joined to a namespace, it will see only containers and pods
# that were created in the same namespace, and will create new containers and
# pods in that namespace.
# The default namespace is "", which corresponds to no namespace. When no
# namespace is set, all containers and pods are visible.
#
# namespace = ""
# Whether to use chroot instead of pivot_root in the runtime
#
# no_pivot_root = false
# Number of locks available for containers and pods.
# If this is changed, a lock renumber must be performed (e.g. with the
# 'podman system renumber' command).
#
# num_locks = 2048
# Directory for persistent libpod files (database, etc)
# By default, this will be configured relative to where the containers/storage
# stores containers
# Uncomment to change location from this default
#
# static_dir = "/var/lib/containers/storage/libpod"
# Directory for temporary files. Must be tmpfs (wiped after reboot)
#
# tmp_dir = "/var/run/libpod"
# Directory for libpod named volumes.
# By default, this will be configured relative to where containers/storage
# stores containers.
# Uncomment to change location from this default.
#
# volume_path = "/var/lib/containers/storage/volumes"
# Default OCI runtime
#
# runtime = "runc"
# List of the OCI runtimes that support --format=json. When json is supported
# libpod will use it for reporting nicer errors.
#
# runtime_supports_json = ["crun", "runc"]
# Paths to look for a valid OCI runtime (runc, runv, etc)
[libpod.runtimes]
# runc = [
# "/usr/bin/runc",
# "/usr/sbin/runc",
# "/usr/local/bin/runc",
# "/usr/local/sbin/runc",
# "/sbin/runc",
# "/bin/runc",
# "/usr/lib/cri-o-runc/sbin/runc",
# ]
# crun = [
# "/usr/bin/crun",
# "/usr/sbin/crun",
# "/usr/local/bin/crun",
# "/usr/local/sbin/crun",
# "/sbin/crun",
# "/bin/crun",
# "/run/current-system/sw/bin/crun",
# ]
# The [libpod.runtimes] table MUST be the last entry in this file.
# (Unless another table is added)
# TOML does not provide a way to end a table other than a further table being
# defined, so every key hereafter will be part of [runtimes] and not the main
# config.

View File

@@ -0,0 +1,342 @@
package config
import (
"bytes"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"github.com/containers/common/pkg/cgroups"
"github.com/containers/common/pkg/unshare"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const (
// _conmonMinMajorVersion is the major version required for conmon.
_conmonMinMajorVersion = 2
// _conmonMinMinorVersion is the minor version required for conmon.
_conmonMinMinorVersion = 0
// _conmonMinPatchVersion is the sub-minor version required for conmon.
_conmonMinPatchVersion = 1
// _conmonVersionFormatErr is used when the expected versio-format of conmon
// has changed.
_conmonVersionFormatErr = "conmon version changed format"
// _defaultGraphRoot points to the default path of the graph root.
_defaultGraphRoot = "/var/lib/containers/storage"
// _defaultTransport is a prefix that we apply to an image name to check
// docker hub first for the image.
_defaultTransport = "docker://"
)
var (
// DefaultInitPath is the default path to the container-init binary
DefaultInitPath = "/usr/libexec/podman/catatonit"
// DefaultInfraImage to use for infra container
DefaultInfraImage = "k8s.gcr.io/pause:3.1"
// DefaultInfraCommand to be run in an infra container
DefaultInfraCommand = "/pause"
// DefaultRootlessSHMLockPath is the default path for rootless SHM locks
DefaultRootlessSHMLockPath = "/libpod_rootless_lock"
// DefaultDetachKeys is the default keys sequence for detaching a
// container
DefaultDetachKeys = "ctrl-p,ctrl-q"
)
var (
// ErrConmonOutdated indicates the version of conmon found (whether via the configuration or $PATH)
// is out of date for the current podman version
ErrConmonOutdated = errors.New("outdated conmon version")
// ErrInvalidArg indicates that an invalid argument was passed
ErrInvalidArg = errors.New("invalid argument")
// DefaultHooksDirs defines the default hooks directory
DefaultHooksDirs = []string{"/usr/share/containers/oci/hooks.d"}
// DefaultCapabilities for the default_capabilities option in the containers.conf file
DefaultCapabilities = []string{
"CAP_AUDIT_WRITE",
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FOWNER",
"CAP_FSETID",
"CAP_KILL",
"CAP_MKNOD",
"CAP_NET_BIND_SERVICE",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETPCAP",
"CAP_SETUID",
"CAP_SYS_CHROOT",
}
)
const (
// EtcDir is the sysconfdir where podman should look for system config files.
// It can be overridden at build time.
_etcDir = "/etc"
// InstallPrefix is the prefix where podman will be installed.
// It can be overridden at build time.
_installPrefix = "/usr"
// CgroupfsCgroupsManager represents cgroupfs native cgroup manager
CgroupfsCgroupsManager = "cgroupfs"
// DefaultApparmorProfile specifies the default apparmor profile for the container.
DefaultApparmorProfile = "container-default"
// SystemdCgroupsManager represents systemd native cgroup manager
SystemdCgroupsManager = "systemd"
// DefaultLogDriver is the default type of log files
DefaultLogDriver = "k8s-file"
// DefaultLogSizeMax is the default value for the maximum log size
// allowed for a container. Negative values mean that no limit is imposed.
DefaultLogSizeMax = -1
// DefaultPidsLimit is the default value for maximum number of processes
// allowed inside a container
DefaultPidsLimit = 2048
// rootless policy.json file.
DefaultRootlessSignaturePolicyPath = ".config/containers/policy.json"
// DefaultShmSize default value
DefaultShmSize = "65536k"
// DefaultUserNSSize default value
DefaultUserNSSize = 65536
// OCIBufSize limits maximum LogSizeMax
OCIBufSize = 8192
// SeccompOverridePath if this exists it overrides the default seccomp path.
SeccompOverridePath = _etcDir + "/crio/seccomp.json"
// SeccompDefaultPath defines the default seccomp path.
SeccompDefaultPath = _installPrefix + "/share/containers/seccomp.json"
)
// DefaultConfig defines the default values from containers.conf
func DefaultConfig() (*Config, error) {
defaultLibpodConfig, err := defaultConfigFromMemory()
if err != nil {
return nil, err
}
var signaturePolicyPath string
if unshare.IsRootless() {
home, err := unshare.HomeDir()
if err != nil {
return nil, err
}
sigPath := filepath.Join(home, DefaultRootlessSignaturePolicyPath)
if _, err := os.Stat(sigPath); err == nil {
signaturePolicyPath = sigPath
}
}
return &Config{
Containers: ContainersConfig{
AdditionalDevices: []string{},
AdditionalVolumes: []string{},
ApparmorProfile: DefaultApparmorProfile,
CgroupManager: SystemdCgroupsManager,
CgroupNS: "private",
DefaultCapabilities: DefaultCapabilities,
DefaultSysctls: []string{},
DefaultUlimits: []string{},
DNSServers: []string{},
DNSOptions: []string{},
DNSSearches: []string{},
EnableLabeling: selinuxEnabled(),
Env: []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
},
EnvHost: false,
HTTPProxy: false,
Init: false,
InitPath: "",
IPCNS: "private",
LogDriver: DefaultLogDriver,
LogSizeMax: DefaultLogSizeMax,
NetNS: "private",
NoHosts: false,
PidsLimit: DefaultPidsLimit,
PidNS: "private",
SeccompProfile: SeccompDefaultPath,
ShmSize: DefaultShmSize,
SignaturePolicyPath: signaturePolicyPath,
UTSNS: "private",
UserNS: "private",
UserNSSize: DefaultUserNSSize,
},
Network: NetworkConfig{
DefaultNetwork: "podman",
NetworkConfigDir: cniConfigDir,
CNIPluginDirs: cniBinDir,
},
Libpod: *defaultLibpodConfig,
}, nil
}
// defaultConfigFromMemory returns a default libpod configuration. Note that the
// config is different for root and rootless. It also parses the storage.conf.
func defaultConfigFromMemory() (*LibpodConfig, error) {
c := new(LibpodConfig)
tmp, err := defaultTmpDir()
if err != nil {
return nil, err
}
c.TmpDir = tmp
c.EventsLogFilePath = filepath.Join(c.TmpDir, "events", "events.log")
storeOpts, err := storage.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID())
if err != nil {
return nil, err
}
if storeOpts.GraphRoot == "" {
logrus.Warnf("Storage configuration is unset - using hardcoded default graph root %q", _defaultGraphRoot)
storeOpts.GraphRoot = _defaultGraphRoot
}
c.StaticDir = filepath.Join(storeOpts.GraphRoot, "libpod")
c.VolumePath = filepath.Join(storeOpts.GraphRoot, "volumes")
c.StorageConfig = storeOpts
c.HooksDir = DefaultHooksDirs
c.ImageDefaultTransport = _defaultTransport
c.StateType = BoltDBStateStore
c.OCIRuntime = "runc"
// If we're running on cgroups v2, default to using crun.
if onCgroupsv2, _ := cgroups.IsCgroup2UnifiedMode(); onCgroupsv2 {
c.OCIRuntime = "crun"
}
c.OCIRuntimes = map[string][]string{
"runc": {
"/usr/bin/runc",
"/usr/sbin/runc",
"/usr/local/bin/runc",
"/usr/local/sbin/runc",
"/sbin/runc",
"/bin/runc",
"/usr/lib/cri-o-runc/sbin/runc",
"/run/current-system/sw/bin/runc",
},
"crun": {
"/usr/bin/crun",
"/usr/sbin/crun",
"/usr/local/bin/crun",
"/usr/local/sbin/crun",
"/sbin/crun",
"/bin/crun",
"/run/current-system/sw/bin/crun",
},
}
c.ConmonEnvVars = []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
}
c.ConmonPath = []string{
"/usr/libexec/podman/conmon",
"/usr/local/libexec/podman/conmon",
"/usr/local/lib/podman/conmon",
"/usr/bin/conmon",
"/usr/sbin/conmon",
"/usr/local/bin/conmon",
"/usr/local/sbin/conmon",
"/run/current-system/sw/bin/conmon",
}
c.RuntimeSupportsJSON = []string{
"crun",
"runc",
}
c.RuntimeSupportsNoCgroups = []string{"crun"}
c.InitPath = DefaultInitPath
c.NoPivotRoot = false
c.InfraCommand = DefaultInfraCommand
c.InfraImage = DefaultInfraImage
c.EnablePortReservation = true
c.NumLocks = 2048
c.EventsLogger = "journald"
c.DetachKeys = DefaultDetachKeys
c.SDNotify = false
// TODO - ideally we should expose a `type LockType string` along with
// constants.
c.LockType = "shm"
return c, nil
}
func defaultTmpDir() (string, error) {
if !unshare.IsRootless() {
return "/var/run/libpod", nil
}
runtimeDir, err := getRuntimeDir()
if err != nil {
return "", err
}
libpodRuntimeDir := filepath.Join(runtimeDir, "libpod")
if err := os.Mkdir(libpodRuntimeDir, 0700|os.ModeSticky); err != nil {
if !os.IsExist(err) {
return "", errors.Wrapf(err, "cannot mkdir %s", libpodRuntimeDir)
} else if err := os.Chmod(libpodRuntimeDir, 0700|os.ModeSticky); err != nil {
// The directory already exist, just set the sticky bit
return "", errors.Wrapf(err, "could not set sticky bit on %s", libpodRuntimeDir)
}
}
return filepath.Join(libpodRuntimeDir, "tmp"), nil
}
// probeConmon calls conmon --version and verifies it is a new enough version for
// the runtime expectations podman currently has.
func probeConmon(conmonBinary string) error {
cmd := exec.Command(conmonBinary, "--version")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return err
}
r := regexp.MustCompile(`^conmon version (?P<Major>\d+).(?P<Minor>\d+).(?P<Patch>\d+)`)
matches := r.FindStringSubmatch(out.String())
if len(matches) != 4 {
return errors.Wrap(err, _conmonVersionFormatErr)
}
major, err := strconv.Atoi(matches[1])
if err != nil {
return errors.Wrap(err, _conmonVersionFormatErr)
}
if major < _conmonMinMajorVersion {
return ErrConmonOutdated
}
if major > _conmonMinMajorVersion {
return nil
}
minor, err := strconv.Atoi(matches[2])
if err != nil {
return errors.Wrap(err, _conmonVersionFormatErr)
}
if minor < _conmonMinMinorVersion {
return ErrConmonOutdated
}
if minor > _conmonMinMinorVersion {
return nil
}
patch, err := strconv.Atoi(matches[3])
if err != nil {
return errors.Wrap(err, _conmonVersionFormatErr)
}
if patch < _conmonMinPatchVersion {
return ErrConmonOutdated
}
if patch > _conmonMinPatchVersion {
return nil
}
return nil
}

View File

@@ -0,0 +1,69 @@
// +build linux darwin
package config
import (
"fmt"
"os"
"path/filepath"
"sync"
"syscall"
"github.com/containers/common/pkg/unshare"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var (
rootlessRuntimeDirOnce sync.Once
rootlessRuntimeDir string
)
// getRuntimeDir returns the runtime directory
func getRuntimeDir() (string, error) {
var rootlessRuntimeDirError error
rootlessRuntimeDirOnce.Do(func() {
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
uid := fmt.Sprintf("%d", unshare.GetRootlessUID())
if runtimeDir == "" {
tmpDir := filepath.Join("/run", "user", uid)
if err := os.MkdirAll(tmpDir, 0700); err != nil {
logrus.Debugf("unable to make temp dir %s", tmpDir)
}
st, err := os.Stat(tmpDir)
if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && st.Mode().Perm() == 0700 {
runtimeDir = tmpDir
}
}
if runtimeDir == "" {
tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("run-%s", uid))
if err := os.MkdirAll(tmpDir, 0700); err != nil {
logrus.Debugf("unable to make temp dir %s", tmpDir)
}
st, err := os.Stat(tmpDir)
if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && st.Mode().Perm() == 0700 {
runtimeDir = tmpDir
}
}
if runtimeDir == "" {
home := os.Getenv("HOME")
if home == "" {
rootlessRuntimeDirError = fmt.Errorf("neither XDG_RUNTIME_DIR nor HOME was set non-empty")
return
}
resolvedHome, err := filepath.EvalSymlinks(home)
if err != nil {
rootlessRuntimeDirError = errors.Wrapf(err, "cannot resolve %s", home)
return
}
runtimeDir = filepath.Join(resolvedHome, "rundir")
}
rootlessRuntimeDir = runtimeDir
})
if rootlessRuntimeDirError != nil {
return "", rootlessRuntimeDirError
}
return rootlessRuntimeDir, nil
}

View File

@@ -0,0 +1,12 @@
// +build windows
package config
import (
"github.com/pkg/errors"
)
// getRuntimeDir returns the runtime directory
func getRuntimeDir() (string, error) {
return "", errors.New("this function is not implemented for windows")
}

169
vendor/github.com/docker/docker/oci/caps/utils.go generated vendored Normal file
View File

@@ -0,0 +1,169 @@
package caps // import "github.com/docker/docker/oci/caps"
import (
"fmt"
"strings"
"github.com/docker/docker/errdefs"
"github.com/syndtr/gocapability/capability"
)
var capabilityList Capabilities
func init() {
last := capability.CAP_LAST_CAP
// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
if last == capability.Cap(63) {
last = capability.CAP_BLOCK_SUSPEND
}
for _, cap := range capability.List() {
if cap > last {
continue
}
capabilityList = append(capabilityList,
&CapabilityMapping{
Key: "CAP_" + strings.ToUpper(cap.String()),
Value: cap,
},
)
}
}
type (
// CapabilityMapping maps linux capability name to its value of capability.Cap type
// Capabilities is one of the security systems in Linux Security Module (LSM)
// framework provided by the kernel.
// For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html
CapabilityMapping struct {
Key string `json:"key,omitempty"`
Value capability.Cap `json:"value,omitempty"`
}
// Capabilities contains all CapabilityMapping
Capabilities []*CapabilityMapping
)
// String returns <key> of CapabilityMapping
func (c *CapabilityMapping) String() string {
return c.Key
}
// GetCapability returns CapabilityMapping which contains specific key
func GetCapability(key string) *CapabilityMapping {
for _, capp := range capabilityList {
if capp.Key == key {
cpy := *capp
return &cpy
}
}
return nil
}
// GetAllCapabilities returns all of the capabilities
func GetAllCapabilities() []string {
output := make([]string, len(capabilityList))
for i, capability := range capabilityList {
output[i] = capability.String()
}
return output
}
// inSlice tests whether a string is contained in a slice of strings or not.
func inSlice(slice []string, s string) bool {
for _, ss := range slice {
if s == ss {
return true
}
}
return false
}
const allCapabilities = "ALL"
// NormalizeLegacyCapabilities normalizes, and validates CapAdd/CapDrop capabilities
// by upper-casing them, and adding a CAP_ prefix (if not yet present).
//
// This function also accepts the "ALL" magic-value, that's used by CapAdd/CapDrop.
func NormalizeLegacyCapabilities(caps []string) ([]string, error) {
var normalized []string
valids := GetAllCapabilities()
for _, c := range caps {
c = strings.ToUpper(c)
if c == allCapabilities {
normalized = append(normalized, c)
continue
}
if !strings.HasPrefix(c, "CAP_") {
c = "CAP_" + c
}
if !inSlice(valids, c) {
return nil, errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
}
normalized = append(normalized, c)
}
return normalized, nil
}
// ValidateCapabilities validates if caps only contains valid capabilities
func ValidateCapabilities(caps []string) error {
valids := GetAllCapabilities()
for _, c := range caps {
if !inSlice(valids, c) {
return errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
}
}
return nil
}
// TweakCapabilities tweaks capabilities by adding, dropping, or overriding
// capabilities in the basics capabilities list.
func TweakCapabilities(basics, adds, drops, capabilities []string, privileged bool) ([]string, error) {
switch {
case privileged:
// Privileged containers get all capabilities
return GetAllCapabilities(), nil
case capabilities != nil:
// Use custom set of capabilities
if err := ValidateCapabilities(capabilities); err != nil {
return nil, err
}
return capabilities, nil
case len(adds) == 0 && len(drops) == 0:
// Nothing to tweak; we're done
return basics, nil
}
capDrop, err := NormalizeLegacyCapabilities(drops)
if err != nil {
return nil, err
}
capAdd, err := NormalizeLegacyCapabilities(adds)
if err != nil {
return nil, err
}
var caps []string
switch {
case inSlice(capAdd, allCapabilities):
// Add all capabilities except ones on capDrop
for _, c := range GetAllCapabilities() {
if !inSlice(capDrop, c) {
caps = append(caps, c)
}
}
case inSlice(capDrop, allCapabilities):
// "Drop" all capabilities; use what's in capAdd instead
caps = capAdd
default:
// First drop some capabilities
for _, c := range basics {
if !inSlice(capDrop, c) {
caps = append(caps, c)
}
}
// Then add the list of capabilities from capAdd
caps = append(caps, capAdd...)
}
return caps, nil
}

5
vendor/modules.txt vendored
View File

@@ -48,8 +48,10 @@ github.com/containernetworking/cni/pkg/types
github.com/containernetworking/cni/pkg/types/020
github.com/containernetworking/cni/pkg/types/current
github.com/containernetworking/cni/pkg/version
# github.com/containers/common v0.0.7
# github.com/containers/common v0.0.8-0.20200108114752-d87ce6ce296b
github.com/containers/common/pkg/caps
github.com/containers/common/pkg/cgroups
github.com/containers/common/pkg/config
github.com/containers/common/pkg/unshare
# github.com/containers/image/v5 v5.1.0
github.com/containers/image/v5/copy
@@ -178,6 +180,7 @@ github.com/docker/docker/api/types/versions
github.com/docker/docker/api/types/volume
github.com/docker/docker/client
github.com/docker/docker/errdefs
github.com/docker/docker/oci/caps
github.com/docker/docker/pkg/archive
github.com/docker/docker/pkg/fileutils
github.com/docker/docker/pkg/homedir