summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/adapter/containers.go17
-rw-r--r--pkg/adapter/containers_remote.go15
-rw-r--r--pkg/adapter/pods.go12
-rw-r--r--pkg/capabilities/capabilities.go129
-rw-r--r--pkg/env/env.go126
-rw-r--r--pkg/inspect/inspect.go2
-rw-r--r--pkg/spec/createconfig.go1
-rw-r--r--pkg/spec/security.go45
-rw-r--r--pkg/spec/spec.go19
-rw-r--r--pkg/specgen/namespaces.go2
10 files changed, 209 insertions, 159 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index ab664e1c3..a5242270e 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -19,13 +19,13 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/shared/parse"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/libpod/logs"
"github.com/containers/libpod/pkg/adapter/shortcuts"
+ envLib "github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/systemd/generate"
"github.com/containers/storage"
"github.com/pkg/errors"
@@ -987,9 +987,20 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal
// Validate given environment variables
env := map[string]string{}
- if err := parse.ReadKVStrings(env, cli.EnvFile, cli.Env); err != nil {
- return ec, errors.Wrapf(err, "unable to process environment variables")
+ if len(cli.EnvFile) > 0 {
+ for _, f := range cli.EnvFile {
+ fileEnv, err := envLib.ParseFile(f)
+ if err != nil {
+ return ec, err
+ }
+ env = envLib.Join(env, fileEnv)
+ }
+ }
+ cliEnv, err := envLib.ParseSlice(cli.Env)
+ if err != nil {
+ return ec, errors.Wrap(err, "error parsing environment variables")
}
+ env = envLib.Join(env, cliEnv)
streams := new(libpod.AttachStreams)
streams.OutputStream = os.Stdout
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 60ee3cb2d..32a84b60d 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -15,11 +15,11 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/shared/parse"
iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
+ envLib "github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/varlinkapi/virtwriter"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/docker/pkg/term"
@@ -1025,16 +1025,11 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal
)
// default invalid command exit code
// Validate given environment variables
- env := map[string]string{}
- if err := parse.ReadKVStrings(env, []string{}, cli.Env); err != nil {
- return -1, errors.Wrapf(err, "Exec unable to process environment variables")
- }
-
- // Build env slice of key=value strings for Exec
- envs := []string{}
- for k, v := range env {
- envs = append(envs, fmt.Sprintf("%s=%s", k, v))
+ cliEnv, err := envLib.ParseSlice(cli.Env)
+ if err != nil {
+ return 0, errors.Wrap(err, "error parsing environment variables")
}
+ envs := envLib.Slice(cliEnv)
resize := make(chan remotecommand.TerminalSize, 5)
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index 0d9fa7210..dc856cc8d 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -22,6 +22,7 @@ import (
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/adapter/shortcuts"
ann "github.com/containers/libpod/pkg/annotations"
+ envLib "github.com/containers/libpod/pkg/env"
ns "github.com/containers/libpod/pkg/namespaces"
createconfig "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
@@ -916,9 +917,6 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
containerConfig.User = userConfig
containerConfig.Security = securityConfig
- // Set default environment variables and incorporate data from image, if necessary
- envs := shared.EnvVariablesFromData(imageData)
-
annotations := make(map[string]string)
if infraID != "" {
annotations[ann.SandboxID] = infraID
@@ -927,6 +925,14 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
containerConfig.Annotations = annotations
// Environment Variables
+ envs := map[string]string{}
+ if imageData != nil {
+ imageEnv, err := envLib.ParseSlice(imageData.Config.Env)
+ if err != nil {
+ return nil, errors.Wrap(err, "error parsing image environment variables")
+ }
+ envs = imageEnv
+ }
for _, e := range containerYAML.Env {
envs[e.Name] = e.Value
}
diff --git a/pkg/capabilities/capabilities.go b/pkg/capabilities/capabilities.go
deleted file mode 100644
index ea22498b8..000000000
--- a/pkg/capabilities/capabilities.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package capabilities
-
-// Copyright 2013-2018 Docker, Inc.
-
-// NOTE: this package has been copied from github.com/docker/docker but been
-// changed significantly to fit the needs of libpod.
-
-import (
- "strings"
-
- "github.com/containers/libpod/pkg/util"
- "github.com/pkg/errors"
- "github.com/syndtr/gocapability/capability"
-)
-
-var (
- // Used internally and populated during init().
- capabilityList []string
-
- // ErrUnknownCapability is thrown when an unknown capability is processed.
- ErrUnknownCapability = errors.New("unknown capability")
-)
-
-// All is a special value used to add/drop all known capababilities.
-// Useful on the CLI for `--cap-add=all` etc.
-const All = "ALL"
-
-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, "CAP_"+strings.ToUpper(cap.String()))
- }
-}
-
-// AllCapabilities returns all known capabilities.
-func AllCapabilities() []string {
- return capabilityList
-}
-
-// normalizeCapabilities normalizes caps by adding a "CAP_" prefix (if not yet
-// present).
-func normalizeCapabilities(caps []string) ([]string, error) {
- normalized := make([]string, len(caps))
- for i, c := range caps {
- c = strings.ToUpper(c)
- if c == All {
- normalized = append(normalized, c)
- continue
- }
- if !strings.HasPrefix(c, "CAP_") {
- c = "CAP_" + c
- }
- if !util.StringInSlice(c, capabilityList) {
- return nil, errors.Wrapf(ErrUnknownCapability, "%q", c)
- }
- normalized[i] = c
- }
- return normalized, nil
-}
-
-// ValidateCapabilities validates if caps only contains valid capabilities.
-func ValidateCapabilities(caps []string) error {
- for _, c := range caps {
- if !util.StringInSlice(c, capabilityList) {
- return errors.Wrapf(ErrUnknownCapability, "%q", c)
- }
- }
- return nil
-}
-
-// MergeCapabilities computes a set of capabilities by adding capapbitilities
-// to or dropping them from base.
-//
-// Note that "ALL" will cause all known capabilities to be added/dropped but
-// the ones specified to be dropped/added.
-func MergeCapabilities(base, adds, drops []string) ([]string, error) {
- if len(adds) == 0 && len(drops) == 0 {
- // Nothing to tweak; we're done
- return base, nil
- }
-
- capDrop, err := normalizeCapabilities(drops)
- if err != nil {
- return nil, err
- }
- capAdd, err := normalizeCapabilities(adds)
- if err != nil {
- return nil, err
- }
-
- // Make sure that capDrop and capAdd are distinct sets.
- for _, drop := range capDrop {
- if util.StringInSlice(drop, capAdd) {
- return nil, errors.Errorf("capability %q cannot be dropped and added", drop)
- }
- }
-
- var caps []string
-
- switch {
- case util.StringInSlice(All, capAdd):
- // Add all capabilities except ones on capDrop
- for _, c := range capabilityList {
- if !util.StringInSlice(c, capDrop) {
- caps = append(caps, c)
- }
- }
- case util.StringInSlice(All, capDrop):
- // "Drop" all capabilities; use what's in capAdd instead
- caps = capAdd
- default:
- // First drop some capabilities
- for _, c := range base {
- if !util.StringInSlice(c, capDrop) {
- caps = append(caps, c)
- }
- }
- // Then add the list of capabilities from capAdd
- caps = append(caps, capAdd...)
- }
- return caps, nil
-}
diff --git a/pkg/env/env.go b/pkg/env/env.go
new file mode 100644
index 000000000..31ffab03c
--- /dev/null
+++ b/pkg/env/env.go
@@ -0,0 +1,126 @@
+// Package for processing environment variables.
+package env
+
+// TODO: we need to add tests for this package.
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/pkg/errors"
+)
+
+// DefaultEnvVariables set $PATH, $TERM and $container.
+var DefaultEnvVariables = map[string]string{
+ "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "TERM": "xterm",
+ "container": "podman",
+}
+
+const whiteSpaces = " \t"
+
+// ParseSlice parses the specified slice and transforms it into an environment
+// map.
+func ParseSlice(s []string) (map[string]string, error) {
+ env := make(map[string]string, len(s))
+ for _, e := range s {
+ if err := parseEnv(env, e); err != nil {
+ return nil, err
+ }
+ }
+ return env, nil
+}
+
+// Slice transforms the specified map of environment variables into a
+// slice. If a value is non-empty, the key and value are joined with '='.
+func Slice(m map[string]string) []string {
+ env := make([]string, len(m))
+ for k, v := range m {
+ var s string
+ if len(v) > 0 {
+ s = fmt.Sprintf("%s=%s", k, v)
+ } else {
+ s = k
+ }
+ env = append(env, s)
+ }
+ return env
+}
+
+// Join joins the two environment maps with override overriding base.
+func Join(base map[string]string, override map[string]string) map[string]string {
+ if len(base) == 0 {
+ return override
+ }
+ for k, v := range override {
+ base[k] = v
+ }
+ return base
+}
+
+// ParseFile parses the specified path for environment variables and returns them
+// as a map.
+func ParseFile(path string) (env map[string]string, err error) {
+ defer func() {
+ if err != nil {
+ err = errors.Wrapf(err, "error parsing env file %q", path)
+ }
+ }()
+
+ fh, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer fh.Close()
+
+ scanner := bufio.NewScanner(fh)
+ for scanner.Scan() {
+ // trim the line from all leading whitespace first
+ line := strings.TrimLeft(scanner.Text(), whiteSpaces)
+ // line is not empty, and not starting with '#'
+ if len(line) > 0 && !strings.HasPrefix(line, "#") {
+ if err := parseEnv(env, line); err != nil {
+ return nil, err
+ }
+ }
+ }
+ return env, scanner.Err()
+}
+
+func parseEnv(env map[string]string, line string) error {
+ data := strings.SplitN(line, "=", 2)
+
+ // catch invalid variables such as "=" or "=A"
+ if data[0] == "" {
+ return errors.Errorf("invalid environment variable: %q", line)
+ }
+
+ // trim the front of a variable, but nothing else
+ name := strings.TrimLeft(data[0], whiteSpaces)
+ if strings.ContainsAny(name, whiteSpaces) {
+ return errors.Errorf("name %q has white spaces, poorly formatted name", name)
+ }
+
+ if len(data) > 1 {
+ env[name] = data[1]
+ } else {
+ if strings.HasSuffix(name, "*") {
+ name = strings.TrimSuffix(name, "*")
+ for _, e := range os.Environ() {
+ part := strings.SplitN(e, "=", 2)
+ if len(part) < 2 {
+ continue
+ }
+ if strings.HasPrefix(part[0], name) {
+ env[part[0]] = part[1]
+ }
+ }
+ } else if val, ok := os.LookupEnv(name); ok {
+ // if only a pass-through variable is given, clean it up.
+ env[name] = val
+ }
+ }
+ return nil
+}
diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go
index 569f208d9..b04ce71a5 100644
--- a/pkg/inspect/inspect.go
+++ b/pkg/inspect/inspect.go
@@ -6,7 +6,7 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/libpod/libpod/driver"
"github.com/opencontainers/go-digest"
- "github.com/opencontainers/image-spec/specs-go/v1"
+ v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
// ImageData holds the inspect information of an image
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index eb2cac8b8..9b2255d61 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -112,6 +112,7 @@ type NetworkConfig struct {
type SecurityConfig struct {
CapAdd []string // cap-add
CapDrop []string // cap-drop
+ CapRequired []string // cap-required
LabelOpts []string //SecurityOpts
NoNewPrivs bool //SecurityOpts
ApparmorProfile string //SecurityOpts
diff --git a/pkg/spec/security.go b/pkg/spec/security.go
index 3bad9f97a..ca025eb3e 100644
--- a/pkg/spec/security.go
+++ b/pkg/spec/security.go
@@ -4,11 +4,13 @@ import (
"fmt"
"strings"
+ "github.com/containers/common/pkg/capabilities"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/capabilities"
+ "github.com/containers/libpod/pkg/util"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// ToCreateOptions convert the SecurityConfig to a slice of container create
@@ -113,28 +115,49 @@ func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserCon
configSpec := g.Config
var err error
- var caplist []string
+ var defaultCaplist []string
bounding := configSpec.Process.Capabilities.Bounding
if useNotRoot(user.User) {
- configSpec.Process.Capabilities.Bounding = caplist
+ configSpec.Process.Capabilities.Bounding = defaultCaplist
}
- caplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop)
+ defaultCaplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop)
if err != nil {
return err
}
- configSpec.Process.Capabilities.Bounding = caplist
- configSpec.Process.Capabilities.Permitted = caplist
- configSpec.Process.Capabilities.Inheritable = caplist
- configSpec.Process.Capabilities.Effective = caplist
- configSpec.Process.Capabilities.Ambient = caplist
+ privCapRequired := []string{}
+
+ if !c.Privileged && len(c.CapRequired) > 0 {
+ // Pass CapRequired in CapAdd field to normalize capabilties names
+ capRequired, err := capabilities.MergeCapabilities(nil, c.CapRequired, nil)
+ if err != nil {
+ logrus.Errorf("capabilties requested by user or image are not valid: %q", strings.Join(c.CapRequired, ","))
+ } else {
+ // Verify all capRequiered are in the defaultCapList
+ for _, cap := range capRequired {
+ if !util.StringInSlice(cap, defaultCaplist) {
+ privCapRequired = append(privCapRequired, cap)
+ }
+ }
+ }
+ if len(privCapRequired) == 0 {
+ defaultCaplist = capRequired
+ } else {
+ logrus.Errorf("capabilties requested by user or image are not allowed by default: %q", strings.Join(privCapRequired, ","))
+ }
+ }
+ configSpec.Process.Capabilities.Bounding = defaultCaplist
+ configSpec.Process.Capabilities.Permitted = defaultCaplist
+ configSpec.Process.Capabilities.Inheritable = defaultCaplist
+ configSpec.Process.Capabilities.Effective = defaultCaplist
+ configSpec.Process.Capabilities.Ambient = defaultCaplist
if useNotRoot(user.User) {
- caplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop)
+ defaultCaplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop)
if err != nil {
return err
}
}
- configSpec.Process.Capabilities.Bounding = caplist
+ configSpec.Process.Capabilities.Bounding = defaultCaplist
// HANDLE SECCOMP
if c.SeccompProfilePath != "unconfined" {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index a4ae22efd..8f0630b85 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -3,12 +3,15 @@ package createconfig
import (
"strings"
+ "github.com/containers/common/pkg/capabilities"
"github.com/containers/libpod/libpod"
libpodconfig "github.com/containers/libpod/libpod/config"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/cgroups"
+ "github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/sysinfo"
+ "github.com/containers/libpod/pkg/util"
"github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -150,7 +153,6 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
for key, val := range config.Annotations {
g.AddAnnotation(key, val)
}
- g.AddProcessEnv("container", "podman")
addedResources := false
@@ -292,6 +294,9 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
}
+ // Make sure to always set the default variables unless overridden in the
+ // config.
+ config.Env = env.Join(env.DefaultEnvVariables, config.Env)
for name, val := range config.Env {
g.AddProcessEnv(name, val)
}
@@ -327,6 +332,18 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
configSpec := g.Config
+ // If the container image specifies an label with a
+ // capabilities.ContainerImageLabel then split the comma separated list
+ // of capabilities and record them. This list indicates the only
+ // capabilities, required to run the container.
+ var capRequired []string
+ for key, val := range config.Labels {
+ if util.StringInSlice(key, capabilities.ContainerImageLabels) {
+ capRequired = strings.Split(val, ",")
+ }
+ }
+ config.Security.CapRequired = capRequired
+
if err := config.Security.ConfigureGenerator(&g, &config.User); err != nil {
return nil, err
}
diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go
index 17b180cde..79a83819a 100644
--- a/pkg/specgen/namespaces.go
+++ b/pkg/specgen/namespaces.go
@@ -3,9 +3,9 @@ package specgen
import (
"os"
+ "github.com/containers/common/pkg/capabilities"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
- "github.com/containers/libpod/pkg/capabilities"
"github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"