summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/apparmor/apparmor.go7
-rw-r--r--pkg/apparmor/apparmor_linux.go66
-rw-r--r--pkg/apparmor/apparmor_unsupported.go16
-rw-r--r--pkg/hooks/exec/exec.go7
-rw-r--r--pkg/hooks/exec/exec_test.go6
-rw-r--r--pkg/hooks/exec/runtimeconfigfilter.go68
-rw-r--r--pkg/hooks/exec/runtimeconfigfilter_test.go266
-rw-r--r--pkg/spec/createconfig.go19
-rw-r--r--pkg/spec/spec.go1
-rw-r--r--pkg/sysinfo/sysinfo_linux.go~254
-rw-r--r--pkg/trust/trust.go129
-rw-r--r--pkg/util/utils.go23
-rw-r--r--pkg/varlinkapi/containers_create.go4
-rw-r--r--pkg/varlinkapi/images.go36
-rw-r--r--pkg/varlinkapi/system.go1
15 files changed, 587 insertions, 316 deletions
diff --git a/pkg/apparmor/apparmor.go b/pkg/apparmor/apparmor.go
index 8b9f99477..45c029c07 100644
--- a/pkg/apparmor/apparmor.go
+++ b/pkg/apparmor/apparmor.go
@@ -2,11 +2,16 @@ package apparmor
import (
"errors"
+ libpodVersion "github.com/containers/libpod/version"
)
var (
+ // DefaultLipodProfilePrefix is used for version-independent presence checks.
+ DefaultLipodProfilePrefix = "libpod-default" + "-"
// DefaultLibpodProfile is the name of default libpod AppArmor profile.
- DefaultLibpodProfile = "libpod-default"
+ DefaultLibpodProfile = DefaultLipodProfilePrefix + libpodVersion.Version
// ErrApparmorUnsupported indicates that AppArmor support is not supported.
ErrApparmorUnsupported = errors.New("AppArmor is not supported")
+ // ErrApparmorRootless indicates that AppArmor support is not supported in rootless mode.
+ ErrApparmorRootless = errors.New("AppArmor is not supported in rootless mode")
)
diff --git a/pkg/apparmor/apparmor_linux.go b/pkg/apparmor/apparmor_linux.go
index b0e3ca0fd..0787b3fa5 100644
--- a/pkg/apparmor/apparmor_linux.go
+++ b/pkg/apparmor/apparmor_linux.go
@@ -13,7 +13,10 @@ import (
"strings"
"text/template"
+ "github.com/containers/libpod/pkg/rootless"
runcaa "github.com/opencontainers/runc/libcontainer/apparmor"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// profileDirectory is the file store for apparmor profiles and macros.
@@ -21,6 +24,9 @@ var profileDirectory = "/etc/apparmor.d"
// IsEnabled returns true if AppArmor is enabled on the host.
func IsEnabled() bool {
+ if rootless.IsRootless() {
+ return false
+ }
return runcaa.IsEnabled()
}
@@ -71,6 +77,10 @@ func macroExists(m string) bool {
// InstallDefault generates a default profile and loads it into the kernel
// using 'apparmor_parser'.
func InstallDefault(name string) error {
+ if rootless.IsRootless() {
+ return ErrApparmorRootless
+ }
+
p := profileData{
Name: name,
}
@@ -97,6 +107,10 @@ func InstallDefault(name string) error {
// IsLoaded checks if a profile with the given name has been loaded into the
// kernel.
func IsLoaded(name string) (bool, error) {
+ if name != "" && rootless.IsRootless() {
+ return false, errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name)
+ }
+
file, err := os.Open("/sys/kernel/security/apparmor/profiles")
if err != nil {
if os.IsNotExist(err) {
@@ -188,3 +202,55 @@ func parseAAParserVersion(output string) (int, error) {
return numericVersion, nil
}
+
+// CheckProfileAndLoadDefault checks if the specified profile is loaded and
+// loads the DefaultLibpodProfile if the specified on is prefixed by
+// DefaultLipodProfilePrefix. This allows to always load and apply the latest
+// default AppArmor profile. Note that AppArmor requires root. If it's a
+// default profile, return DefaultLipodProfilePrefix, otherwise the specified
+// one.
+func CheckProfileAndLoadDefault(name string) (string, error) {
+ if name == "unconfined" {
+ return name, nil
+ }
+
+ if name != "" && rootless.IsRootless() {
+ return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name)
+ }
+
+ if name != "" && !runcaa.IsEnabled() {
+ return "", fmt.Errorf("profile %q specified but AppArmor is disabled on the host", name)
+ }
+
+ // If the specified name is not empty or is not a default libpod one,
+ // ignore it and return the name.
+ if name != "" && !strings.HasPrefix(name, DefaultLipodProfilePrefix) {
+ isLoaded, err := IsLoaded(name)
+ if err != nil {
+ return "", err
+ }
+ if !isLoaded {
+ return "", fmt.Errorf("AppArmor profile %q specified but not loaded")
+ }
+ return name, nil
+ }
+
+ name = DefaultLibpodProfile
+ // To avoid expensive redundant loads on each invocation, check
+ // if it's loaded before installing it.
+ isLoaded, err := IsLoaded(name)
+ if err != nil {
+ return "", err
+ }
+ if !isLoaded {
+ err = InstallDefault(name)
+ if err != nil {
+ return "", err
+ }
+ logrus.Infof("successfully loaded AppAmor profile %q", name)
+ } else {
+ logrus.Infof("AppAmor profile %q is already loaded", name)
+ }
+
+ return name, nil
+}
diff --git a/pkg/apparmor/apparmor_unsupported.go b/pkg/apparmor/apparmor_unsupported.go
index df1336b07..b2b4de5f5 100644
--- a/pkg/apparmor/apparmor_unsupported.go
+++ b/pkg/apparmor/apparmor_unsupported.go
@@ -2,19 +2,25 @@
package apparmor
-// IsEnabled returns true if AppArmor is enabled on the host.
+// IsEnabled dummy.
func IsEnabled() bool {
return false
}
-// InstallDefault generates a default profile in a temp directory determined by
-// os.TempDir(), then loads the profile into the kernel using 'apparmor_parser'.
+// InstallDefault dummy.
func InstallDefault(name string) error {
return ErrApparmorUnsupported
}
-// IsLoaded checks if a profile with the given name has been loaded into the
-// kernel.
+// IsLoaded dummy.
func IsLoaded(name string) (bool, error) {
return false, ErrApparmorUnsupported
}
+
+// CheckProfileAndLoadDefault dummy.
+func CheckProfileAndLoadDefault(name string) (string, error) {
+ if name == "" {
+ return "", nil
+ }
+ return "", ErrApparmorUnsupported
+}
diff --git a/pkg/hooks/exec/exec.go b/pkg/hooks/exec/exec.go
index 94469b1d2..0dd091561 100644
--- a/pkg/hooks/exec/exec.go
+++ b/pkg/hooks/exec/exec.go
@@ -10,6 +10,7 @@ import (
"time"
rspec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
)
// DefaultPostKillTimeout is the recommended default post-kill timeout.
@@ -42,7 +43,11 @@ func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer,
}
exit := make(chan error, 1)
go func() {
- exit <- cmd.Wait()
+ err := cmd.Wait()
+ if err != nil {
+ err = errors.Wrapf(err, "executing %v", cmd.Args)
+ }
+ exit <- err
}()
select {
diff --git a/pkg/hooks/exec/exec_test.go b/pkg/hooks/exec/exec_test.go
index 62e45ff3a..7aac315cb 100644
--- a/pkg/hooks/exec/exec_test.go
+++ b/pkg/hooks/exec/exec_test.go
@@ -163,14 +163,14 @@ func TestRunCancel(t *testing.T) {
name: "context timeout",
contextTimeout: time.Duration(1) * time.Second,
expectedStdout: "waiting\n",
- expectedHookError: "^signal: killed$",
+ expectedHookError: "^executing \\[sh -c echo waiting; sleep 2; echo done]: signal: killed$",
expectedRunError: context.DeadlineExceeded,
},
{
name: "hook timeout",
hookTimeout: &one,
expectedStdout: "waiting\n",
- expectedHookError: "^signal: killed$",
+ expectedHookError: "^executing \\[sh -c echo waiting; sleep 2; echo done]: signal: killed$",
expectedRunError: context.DeadlineExceeded,
},
} {
@@ -207,7 +207,7 @@ func TestRunKillTimeout(t *testing.T) {
}
hookErr, err := Run(ctx, hook, []byte("{}"), nil, nil, time.Duration(0))
assert.Equal(t, context.DeadlineExceeded, err)
- assert.Regexp(t, "^(failed to reap process within 0s of the kill signal|signal: killed)$", hookErr)
+ assert.Regexp(t, "^(failed to reap process within 0s of the kill signal|executing \\[sh -c sleep 1]: signal: killed)$", hookErr)
}
func init() {
diff --git a/pkg/hooks/exec/runtimeconfigfilter.go b/pkg/hooks/exec/runtimeconfigfilter.go
new file mode 100644
index 000000000..c6971f680
--- /dev/null
+++ b/pkg/hooks/exec/runtimeconfigfilter.go
@@ -0,0 +1,68 @@
+package exec
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "reflect"
+ "time"
+
+ "github.com/davecgh/go-spew/spew"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/pmezard/go-difflib/difflib"
+ "github.com/sirupsen/logrus"
+)
+
+var spewConfig = spew.ConfigState{
+ Indent: " ",
+ DisablePointerAddresses: true,
+ DisableCapacities: true,
+ SortKeys: true,
+}
+
+// RuntimeConfigFilter calls a series of hooks. But instead of
+// passing container state on their standard input,
+// RuntimeConfigFilter passes the proposed runtime configuration (and
+// reads back a possibly-altered form from their standard output).
+func RuntimeConfigFilter(ctx context.Context, hooks []spec.Hook, config *spec.Spec, postKillTimeout time.Duration) (hookErr, err error) {
+ data, err := json.Marshal(config)
+ for i, hook := range hooks {
+ var stdout bytes.Buffer
+ hookErr, err = Run(ctx, &hook, data, &stdout, nil, postKillTimeout)
+ if err != nil {
+ return hookErr, err
+ }
+
+ data = stdout.Bytes()
+ var newConfig spec.Spec
+ err = json.Unmarshal(data, &newConfig)
+ if err != nil {
+ logrus.Debugf("invalid JSON from config-filter hook %d:\n%s", i, string(data))
+ return nil, errors.Wrapf(err, "unmarshal output from config-filter hook %d", i)
+ }
+
+ if !reflect.DeepEqual(config, &newConfig) {
+ old := spewConfig.Sdump(config)
+ new := spewConfig.Sdump(&newConfig)
+ diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
+ A: difflib.SplitLines(old),
+ B: difflib.SplitLines(new),
+ FromFile: "Old",
+ FromDate: "",
+ ToFile: "New",
+ ToDate: "",
+ Context: 1,
+ })
+ if err == nil {
+ logrus.Debugf("precreate hook %d made configuration changes:\n%s", i, diff)
+ } else {
+ logrus.Warnf("precreate hook %d made configuration changes, but we could not compute a diff: %v", i, err)
+ }
+ }
+
+ *config = newConfig
+ }
+
+ return nil, nil
+}
diff --git a/pkg/hooks/exec/runtimeconfigfilter_test.go b/pkg/hooks/exec/runtimeconfigfilter_test.go
new file mode 100644
index 000000000..52d590d14
--- /dev/null
+++ b/pkg/hooks/exec/runtimeconfigfilter_test.go
@@ -0,0 +1,266 @@
+package exec
+
+import (
+ "context"
+ "encoding/json"
+ "os"
+ "testing"
+ "time"
+
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+)
+
+func pointerInt(value int) *int {
+ return &value
+}
+
+func pointerUInt32(value uint32) *uint32 {
+ return &value
+}
+
+func pointerFileMode(value os.FileMode) *os.FileMode {
+ return &value
+}
+
+func TestRuntimeConfigFilter(t *testing.T) {
+ unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil)
+
+ for _, test := range []struct {
+ name string
+ contextTimeout time.Duration
+ hooks []spec.Hook
+ input *spec.Spec
+ expected *spec.Spec
+ expectedHookError string
+ expectedRunError error
+ }{
+ {
+ name: "no-op",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", "cat"},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ },
+ {
+ name: "device injection",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ Linux: &spec.Linux{
+ Devices: []spec.LinuxDevice{
+ {
+ Path: "/dev/fuse",
+ Type: "c",
+ Major: 10,
+ Minor: 229,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ },
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ Linux: &spec.Linux{
+ Devices: []spec.LinuxDevice{
+ {
+ Path: "/dev/fuse",
+ Type: "c",
+ Major: 10,
+ Minor: 229,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ {
+ Path: "/dev/sda",
+ Type: "b",
+ Major: 8,
+ Minor: 0,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "chaining",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`},
+ },
+ {
+ Path: path,
+ Args: []string{"sh", "-c", `sed 's|/dev/sda|/dev/sdb|'`},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ Linux: &spec.Linux{
+ Devices: []spec.LinuxDevice{
+ {
+ Path: "/dev/fuse",
+ Type: "c",
+ Major: 10,
+ Minor: 229,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ },
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ Linux: &spec.Linux{
+ Devices: []spec.LinuxDevice{
+ {
+ Path: "/dev/fuse",
+ Type: "c",
+ Major: 10,
+ Minor: 229,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ {
+ Path: "/dev/sdb",
+ Type: "b",
+ Major: 8,
+ Minor: 0,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "context timeout",
+ contextTimeout: time.Duration(1) * time.Second,
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", "sleep 2"},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$",
+ expectedRunError: context.DeadlineExceeded,
+ },
+ {
+ name: "hook timeout",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", "sleep 2"},
+ Timeout: pointerInt(1),
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$",
+ expectedRunError: context.DeadlineExceeded,
+ },
+ {
+ name: "invalid JSON",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", "echo '{'"},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expectedRunError: unexpectedEndOfJSONInput,
+ },
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ ctx := context.Background()
+ if test.contextTimeout > 0 {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithTimeout(ctx, test.contextTimeout)
+ defer cancel()
+ }
+ hookErr, err := RuntimeConfigFilter(ctx, test.hooks, test.input, DefaultPostKillTimeout)
+ assert.Equal(t, test.expectedRunError, errors.Cause(err))
+ if test.expectedHookError == "" {
+ if hookErr != nil {
+ t.Fatal(hookErr)
+ }
+ } else {
+ assert.Regexp(t, test.expectedHookError, hookErr.Error())
+ }
+ assert.Equal(t, test.expected, test.input)
+ })
+ }
+}
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index ffc98e307..632d60b55 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -341,10 +341,9 @@ func (c *CreateConfig) createExitCommand() []string {
}
// GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions
-func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var portBindings []ocicni.PortMapping
- var pod *libpod.Pod
var err error
if c.Interactive {
@@ -358,12 +357,14 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib
logrus.Debugf("appending name %s", c.Name)
options = append(options, libpod.WithName(c.Name))
}
- if c.Pod != "" {
- logrus.Debugf("adding container to pod %s", c.Pod)
- pod, err = runtime.LookupPod(c.Pod)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to add container to pod %s", c.Pod)
+ if c.Pod != "" || pod != nil {
+ if pod == nil {
+ pod, err = runtime.LookupPod(c.Pod)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to add container to pod %s", c.Pod)
+ }
}
+ logrus.Debugf("adding container to pod %s", c.Pod)
options = append(options, runtime.WithPod(pod))
}
if len(c.PortBindings) > 0 {
@@ -518,7 +519,9 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib
if c.CgroupParent != "" {
options = append(options, libpod.WithCgroupParent(c.CgroupParent))
}
- if c.Detach {
+ // For a rootless container always cleanup the storage/network as they
+ // run in a different namespace thus not reusable when we restart.
+ if c.Detach || rootless.IsRootless() {
options = append(options, libpod.WithExitCommand(c.createExitCommand()))
}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index ffa999730..9ef0223f2 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -252,6 +252,7 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
}
// SECURITY OPTS
g.SetProcessNoNewPrivileges(config.NoNewPrivs)
+
g.SetProcessApparmorProfile(config.ApparmorProfile)
blockAccessToKernelFilesystems(config, &g)
diff --git a/pkg/sysinfo/sysinfo_linux.go~ b/pkg/sysinfo/sysinfo_linux.go~
deleted file mode 100644
index 471f786a7..000000000
--- a/pkg/sysinfo/sysinfo_linux.go~
+++ /dev/null
@@ -1,254 +0,0 @@
-package sysinfo
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path"
- "strings"
-
- "github.com/opencontainers/runc/libcontainer/cgroups"
- "github.com/sirupsen/logrus"
- "golang.org/x/sys/unix"
-)
-
-func findCgroupMountpoints() (map[string]string, error) {
- cgMounts, err := cgroups.GetCgroupMounts(false)
- if err != nil {
- return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
- }
- mps := make(map[string]string)
- for _, m := range cgMounts {
- for _, ss := range m.Subsystems {
- mps[ss] = m.Mountpoint
- }
- }
- return mps, nil
-}
-
-// New returns a new SysInfo, using the filesystem to detect which features
-// the kernel supports. If `quiet` is `false` warnings are printed in logs
-// whenever an error occurs or misconfigurations are present.
-func New(quiet bool) *SysInfo {
- sysInfo := &SysInfo{}
- cgMounts, err := findCgroupMountpoints()
- if err != nil {
- logrus.Warnf("Failed to parse cgroup information: %v", err)
- } else {
- sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
- sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
- sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
- sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
- sysInfo.cgroupPids = checkCgroupPids(quiet)
- }
-
- _, ok := cgMounts["devices"]
- sysInfo.CgroupDevicesEnabled = ok
-
- sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
- sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
- sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
-
- // Check if AppArmor is supported.
- if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
- sysInfo.AppArmor = true
- }
-
- // Check if Seccomp is supported, via CONFIG_SECCOMP.
- if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
- // Make sure the kernel has CONFIG_SECCOMP_FILTER.
- if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
- sysInfo.Seccomp = true
- }
- }
-
- return sysInfo
-}
-
-// checkCgroupMem reads the memory information from the memory cgroup mount point.
-func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
- mountPoint, ok := cgMounts["memory"]
- if !ok {
- if !quiet {
- logrus.Warn("Your kernel does not support cgroup memory limit")
- }
- return cgroupMemInfo{}
- }
-
- swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
- if !quiet && !swapLimit {
- logrus.Warn("Your kernel does not support swap memory limit")
- }
- memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
- if !quiet && !memoryReservation {
- logrus.Warn("Your kernel does not support memory reservation")
- }
- oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
- if !quiet && !oomKillDisable {
- logrus.Warn("Your kernel does not support oom control")
- }
- memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
- if !quiet && !memorySwappiness {
- logrus.Warn("Your kernel does not support memory swappiness")
- }
- kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
- if !quiet && !kernelMemory {
- logrus.Warn("Your kernel does not support kernel memory limit")
- }
-
- return cgroupMemInfo{
- MemoryLimit: true,
- SwapLimit: swapLimit,
- MemoryReservation: memoryReservation,
- OomKillDisable: oomKillDisable,
- MemorySwappiness: memorySwappiness,
- KernelMemory: kernelMemory,
- }
-}
-
-// checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
-func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
- mountPoint, ok := cgMounts["cpu"]
- if !ok {
- if !quiet {
- logrus.Warn("Unable to find cpu cgroup in mounts")
- }
- return cgroupCPUInfo{}
- }
-
- cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
- if !quiet && !cpuShares {
- logrus.Warn("Your kernel does not support cgroup cpu shares")
- }
-
- cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
- if !quiet && !cpuCfsPeriod {
- logrus.Warn("Your kernel does not support cgroup cfs period")
- }
-
- cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
- if !quiet && !cpuCfsQuota {
- logrus.Warn("Your kernel does not support cgroup cfs quotas")
- }
-
- cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
- if !quiet && !cpuRealtimePeriod {
- logrus.Warn("Your kernel does not support cgroup rt period")
- }
-
- cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
- if !quiet && !cpuRealtimeRuntime {
- logrus.Warn("Your kernel does not support cgroup rt runtime")
- }
-
- return cgroupCPUInfo{
- CPUShares: cpuShares,
- CPUCfsPeriod: cpuCfsPeriod,
- CPUCfsQuota: cpuCfsQuota,
- CPURealtimePeriod: cpuRealtimePeriod,
- CPURealtimeRuntime: cpuRealtimeRuntime,
- }
-}
-
-// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
-func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
- mountPoint, ok := cgMounts["blkio"]
- if !ok {
- if !quiet {
- logrus.Warn("Unable to find blkio cgroup in mounts")
- }
- return cgroupBlkioInfo{}
- }
-
- weight := cgroupEnabled(mountPoint, "blkio.weight")
- if !quiet && !weight {
- logrus.Warn("Your kernel does not support cgroup blkio weight")
- }
-
- weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
- if !quiet && !weightDevice {
- logrus.Warn("Your kernel does not support cgroup blkio weight_device")
- }
-
- readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
- if !quiet && !readBpsDevice {
- logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
- }
-
- writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
- if !quiet && !writeBpsDevice {
- logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
- }
- readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
- if !quiet && !readIOpsDevice {
- logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
- }
-
- writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
- if !quiet && !writeIOpsDevice {
- logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
- }
- return cgroupBlkioInfo{
- BlkioWeight: weight,
- BlkioWeightDevice: weightDevice,
- BlkioReadBpsDevice: readBpsDevice,
- BlkioWriteBpsDevice: writeBpsDevice,
- BlkioReadIOpsDevice: readIOpsDevice,
- BlkioWriteIOpsDevice: writeIOpsDevice,
- }
-}
-
-// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
-func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
- mountPoint, ok := cgMounts["cpuset"]
- if !ok {
- if !quiet {
- logrus.Warn("Unable to find cpuset cgroup in mounts")
- }
- return cgroupCpusetInfo{}
- }
-
- cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
- if err != nil {
- return cgroupCpusetInfo{}
- }
-
- mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
- if err != nil {
- return cgroupCpusetInfo{}
- }
-
- return cgroupCpusetInfo{
- Cpuset: true,
- Cpus: strings.TrimSpace(string(cpus)),
- Mems: strings.TrimSpace(string(mems)),
- }
-}
-
-// checkCgroupPids reads the pids information from the pids cgroup mount point.
-func checkCgroupPids(quiet bool) cgroupPids {
- _, err := cgroups.FindCgroupMountpoint("pids")
- if err != nil {
- if !quiet {
- logrus.Warn(err)
- }
- return cgroupPids{}
- }
-
- return cgroupPids{
- PidsLimit: true,
- }
-}
-
-func cgroupEnabled(mountPoint, name string) bool {
- _, err := os.Stat(path.Join(mountPoint, name))
- return err == nil
-}
-
-func readProcBool(path string) bool {
- val, err := ioutil.ReadFile(path)
- if err != nil {
- return false
- }
- return strings.TrimSpace(string(val)) == "1"
-}
diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go
index efc760364..31e41903e 100644
--- a/pkg/trust/trust.go
+++ b/pkg/trust/trust.go
@@ -2,6 +2,7 @@ package trust
import (
"bufio"
+ "bytes"
"encoding/base64"
"encoding/json"
"io/ioutil"
@@ -9,7 +10,6 @@ import (
"os/exec"
"path/filepath"
"strings"
- "unsafe"
"github.com/containers/image/types"
"github.com/pkg/errors"
@@ -52,6 +52,14 @@ type RegistryNamespace struct {
SigStoreStaging string `json:"sigstore-staging"` // For writing only.
}
+// ShowOutput keep the fields for image trust show command
+type ShowOutput struct {
+ Repo string
+ Trusttype string
+ GPGid string
+ Sigstore string
+}
+
// DefaultPolicyPath returns a path to the default policy of the system.
func DefaultPolicyPath(sys *types.SystemContext) string {
systemDefaultPolicyPath := "/etc/containers/policy.json"
@@ -167,84 +175,127 @@ func CreateTmpFile(dir, pattern string, content []byte) (string, error) {
return tmpfile.Name(), nil
}
-// GetGPGId return GPG identity, either bracketed <email> or ID string
-// comma separated if more than one key
-func GetGPGId(keys []string) string {
+func getGPGIdFromKeyPath(path []string) []string {
+ var uids []string
+ for _, k := range path {
+ cmd := exec.Command("gpg2", "--with-colons", k)
+ results, err := cmd.Output()
+ if err != nil {
+ logrus.Warnf("error get key identity: %s", err)
+ continue
+ }
+ uids = append(uids, parseUids(results)...)
+ }
+ return uids
+}
+
+func getGPGIdFromKeyData(keys []string) []string {
+ var uids []string
for _, k := range keys {
- if _, err := os.Stat(k); err != nil {
- decodeKey, err := base64.StdEncoding.DecodeString(k)
- if err != nil {
- logrus.Warnf("error decoding key data")
- continue
- }
- tmpfileName, err := CreateTmpFile("/run/", "", decodeKey)
- if err != nil {
- logrus.Warnf("error creating key date temp file %s", err)
- }
- defer os.Remove(tmpfileName)
- k = tmpfileName
+ decodeKey, err := base64.StdEncoding.DecodeString(k)
+ if err != nil {
+ logrus.Warnf("error decoding key data")
+ continue
}
+ tmpfileName, err := CreateTmpFile("", "", decodeKey)
+ if err != nil {
+ logrus.Warnf("error creating key date temp file %s", err)
+ }
+ defer os.Remove(tmpfileName)
+ k = tmpfileName
cmd := exec.Command("gpg2", "--with-colons", k)
results, err := cmd.Output()
if err != nil {
logrus.Warnf("error get key identity: %s", err)
continue
}
- resultsStr := *(*string)(unsafe.Pointer(&results))
- scanner := bufio.NewScanner(strings.NewReader(resultsStr))
- var parseduids []string
- for scanner.Scan() {
- line := scanner.Text()
- if strings.HasPrefix(line, "uid:") || strings.HasPrefix(line, "pub:") {
- uid := strings.Split(line, ":")[9]
- if uid == "" {
- continue
- }
- parseduid := uid
- if strings.Contains(uid, "<") && strings.Contains(uid, ">") {
- parseduid = strings.SplitN(strings.SplitAfterN(uid, "<", 2)[1], ">", 2)[0]
- }
- parseduids = append(parseduids, parseduid)
+ uids = append(uids, parseUids(results)...)
+ }
+ return uids
+}
+
+func parseUids(colonDelimitKeys []byte) []string {
+ var parseduids []string
+ scanner := bufio.NewScanner(bytes.NewReader(colonDelimitKeys))
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "uid:") || strings.HasPrefix(line, "pub:") {
+ uid := strings.Split(line, ":")[9]
+ if uid == "" {
+ continue
+ }
+ parseduid := uid
+ if strings.Contains(uid, "<") && strings.Contains(uid, ">") {
+ parseduid = strings.SplitN(strings.SplitAfterN(uid, "<", 2)[1], ">", 2)[0]
}
+ parseduids = append(parseduids, parseduid)
}
- return strings.Join(parseduids, ",")
}
- return ""
+ return parseduids
}
-// GetPolicyJSON return the struct to show policy.json in json format
-func GetPolicyJSON(policyContentStruct PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, error) {
+var typeDescription = map[string]string{"insecureAcceptAnything": "accept", "signedBy": "signed", "reject": "reject"}
+
+func trustTypeDescription(trustType string) string {
+ trustDescription, exist := typeDescription[trustType]
+ if !exist {
+ logrus.Warnf("invalid trust type %s", trustType)
+ }
+ return trustDescription
+}
+
+// GetPolicy return the struct to show policy.json in json format and a map (reponame, ShowOutput) pair for image trust show command
+func GetPolicy(policyContentStruct PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, map[string]ShowOutput, error) {
registryConfigs, err := LoadAndMergeConfig(systemRegistriesDirPath)
if err != nil {
- return nil, err
+ return nil, nil, err
}
+ trustShowOutputMap := make(map[string]ShowOutput)
policyJSON := make(map[string]map[string]interface{})
if len(policyContentStruct.Default) > 0 {
policyJSON["* (default)"] = make(map[string]interface{})
policyJSON["* (default)"]["type"] = policyContentStruct.Default[0].Type
+
+ var defaultPolicyStruct ShowOutput
+ defaultPolicyStruct.Repo = "default"
+ defaultPolicyStruct.Trusttype = trustTypeDescription(policyContentStruct.Default[0].Type)
+ trustShowOutputMap["* (default)"] = defaultPolicyStruct
}
for transname, transval := range policyContentStruct.Transports {
for repo, repoval := range transval {
+ tempTrustShowOutput := ShowOutput{
+ Repo: repo,
+ Trusttype: repoval[0].Type,
+ }
policyJSON[repo] = make(map[string]interface{})
policyJSON[repo]["type"] = repoval[0].Type
policyJSON[repo]["transport"] = transname
+ keyDataArr := []string{}
+ keyPathArr := []string{}
+ keyarr := []string{}
for _, repoele := range repoval {
- keyarr := []string{}
if len(repoele.KeyPath) > 0 {
keyarr = append(keyarr, repoele.KeyPath)
+ keyPathArr = append(keyPathArr, repoele.KeyPath)
}
if len(repoele.KeyData) > 0 {
keyarr = append(keyarr, string(repoele.KeyData))
+ keyDataArr = append(keyDataArr, string(repoele.KeyData))
}
- policyJSON[repo]["keys"] = keyarr
}
+ policyJSON[repo]["keys"] = keyarr
+ uids := append(getGPGIdFromKeyPath(keyPathArr), getGPGIdFromKeyData(keyDataArr)...)
+ tempTrustShowOutput.GPGid = strings.Join(uids, ",")
+
policyJSON[repo]["sigstore"] = ""
registryNamespace := HaveMatchRegistry(repo, registryConfigs)
if registryNamespace != nil {
policyJSON[repo]["sigstore"] = registryNamespace.SigStore
+ tempTrustShowOutput.Sigstore = registryNamespace.SigStore
}
+ trustShowOutputMap[repo] = tempTrustShowOutput
}
}
- return policyJSON, nil
+ return policyJSON, trustShowOutputMap, nil
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index a6f52cb3e..e0b94b011 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -314,9 +314,22 @@ func GetDefaultStoreOptions() (storage.StoreOptions, string, error) {
return storageOpts, volumePath, err
}
- storageConf := filepath.Join(os.Getenv("HOME"), ".config/containers/storage.conf")
+ storageConf := StorageConfigFile()
if _, err := os.Stat(storageConf); err == nil {
+ defaultRootlessRunRoot := storageOpts.RunRoot
+ defaultRootlessGraphRoot := storageOpts.GraphRoot
+ storageOpts = storage.StoreOptions{}
storage.ReloadConfigurationFile(storageConf, &storageOpts)
+
+ // If the file did not specify a graphroot or runroot,
+ // set sane defaults so we don't try and use root-owned
+ // directories
+ if storageOpts.RunRoot == "" {
+ storageOpts.RunRoot = defaultRootlessRunRoot
+ }
+ if storageOpts.GraphRoot == "" {
+ storageOpts.GraphRoot = defaultRootlessGraphRoot
+ }
} else if os.IsNotExist(err) {
os.MkdirAll(filepath.Dir(storageConf), 0755)
file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
@@ -334,3 +347,11 @@ func GetDefaultStoreOptions() (storage.StoreOptions, string, error) {
}
return storageOpts, volumePath, nil
}
+
+// StorageConfigFile returns the path to the storage config file used
+func StorageConfigFile() string {
+ if rootless.IsRootless() {
+ return filepath.Join(os.Getenv("HOME"), ".config/containers/storage.conf")
+ }
+ return storage.DefaultConfigFile
+}
diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go
index 63bc93686..d72eaeb18 100644
--- a/pkg/varlinkapi/containers_create.go
+++ b/pkg/varlinkapi/containers_create.go
@@ -41,7 +41,9 @@ func (i *LibpodAPI) CreateContainer(call iopodman.VarlinkCall, config iopodman.C
return call.ReplyErrorOccurred(err.Error())
}
- options, err := createConfig.GetContainerCreateOptions(i.Runtime)
+ // TODO fix when doing remote client and dealing with the ability to create a container
+ // within a non-existing pod (i.e. --pod new:foobar)
+ options, err := createConfig.GetContainerCreateOptions(i.Runtime, nil)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 5e4cb4ccb..744f031c0 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -41,18 +41,28 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error {
for _, image := range images {
labels, _ := image.Labels(getContext())
containers, _ := image.Containers()
+ repoDigests, err := image.RepoDigests()
+ if err != nil {
+ return err
+ }
+
size, _ := image.Size(getContext())
+ isParent, err := image.IsParent()
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
i := iopodman.ImageInList{
Id: image.ID(),
ParentId: image.Parent,
RepoTags: image.Names(),
- RepoDigests: image.RepoDigests(),
+ RepoDigests: repoDigests,
Created: image.Created().String(),
Size: int64(*size),
VirtualSize: image.VirtualSize,
Containers: int64(len(containers)),
Labels: labels,
+ IsParent: isParent,
}
imageList = append(imageList, i)
}
@@ -73,6 +83,10 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error {
if err != nil {
return err
}
+ repoDigests, err := newImage.RepoDigests()
+ if err != nil {
+ return err
+ }
size, err := newImage.Size(getContext())
if err != nil {
return err
@@ -82,7 +96,7 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error {
Id: newImage.ID(),
ParentId: newImage.Parent,
RepoTags: newImage.Names(),
- RepoDigests: newImage.RepoDigests(),
+ RepoDigests: repoDigests,
Created: newImage.Created().String(),
Size: int64(*size),
VirtualSize: newImage.VirtualSize,
@@ -611,3 +625,21 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman.
}
return call.ReplyContainerRunlabel()
}
+
+// ImagesPrune ....
+func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall) error {
+ var (
+ pruned []string
+ )
+ pruneImages, err := i.Runtime.ImageRuntime().GetPruneImages()
+ if err != nil {
+ return err
+ }
+ for _, i := range pruneImages {
+ if err := i.Remove(true); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ pruned = append(pruned, i.ID())
+ }
+ return call.ReplyImagesPrune(pruned)
+}
diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go
index a29d22e7d..e50643dd0 100644
--- a/pkg/varlinkapi/system.go
+++ b/pkg/varlinkapi/system.go
@@ -102,6 +102,5 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error {
podmanInfo.Podman = pmaninfo
podmanInfo.Registries = registries
podmanInfo.Insecure_registries = insecureRegistries
-
return call.ReplyGetInfo(podmanInfo)
}