From f3f4c54f2abc341cee1e7b83e9538d91a3c627e3 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <rothberg@redhat.com>
Date: Tue, 7 Jan 2020 16:48:07 +0100
Subject: policy for seccomp-profile selection

Implement a policy for selecting a seccomp profile.  In addition to the
default behaviour (default profile unless --security-opt seccomp is set)
add a second policy doing a lookup in the image annotation.

If the image has the "io.containers.seccomp.profile" set its value will be
interpreted as a seccomp profile.  The policy can be selected via the
new --seccomp-policy CLI flag.

Once the containers.conf support is merged into libpod, we can add an
option there as well.

Note that this feature is marked as experimental and may change in the
future.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
---
 pkg/spec/config_linux_cgo.go | 12 ++++++++
 pkg/spec/createconfig.go     | 67 ++++++++++++++++++++++++++++++++++++--------
 2 files changed, 68 insertions(+), 11 deletions(-)

(limited to 'pkg')

diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go
index c47156456..ae83c9d52 100644
--- a/pkg/spec/config_linux_cgo.go
+++ b/pkg/spec/config_linux_cgo.go
@@ -8,13 +8,24 @@ import (
 	spec "github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 	seccomp "github.com/seccomp/containers-golang"
+	"github.com/sirupsen/logrus"
 )
 
 func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
 	var seccompConfig *spec.LinuxSeccomp
 	var err error
 
+	if config.SeccompPolicy == SeccompPolicyImage && config.SeccompProfileFromImage != "" {
+		logrus.Debug("Loading seccomp profile from the security config")
+		seccompConfig, err = seccomp.LoadProfile(config.SeccompProfileFromImage, configSpec)
+		if err != nil {
+			return nil, errors.Wrap(err, "loading seccomp profile failed")
+		}
+		return seccompConfig, nil
+	}
+
 	if config.SeccompProfilePath != "" {
+		logrus.Debugf("Loading seccomp profile from %q", config.SeccompProfilePath)
 		seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath)
 		if err != nil {
 			return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath)
@@ -24,6 +35,7 @@ func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.Linu
 			return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
 		}
 	} else {
+		logrus.Debug("Loading default seccomp profile")
 		seccompConfig, err = seccomp.GetDefaultProfile(configSpec)
 		if err != nil {
 			return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 244a8d1cd..827caacd6 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -2,6 +2,7 @@ package createconfig
 
 import (
 	"os"
+	"sort"
 	"strconv"
 	"strings"
 	"syscall"
@@ -106,19 +107,63 @@ type NetworkConfig struct {
 	PublishAll   bool     //publish-all
 }
 
+// SeccompPolicy determines which seccomp profile gets applied to the container.
+type SeccompPolicy int
+
+const (
+	// SeccompPolicyDefault - if set use SecurityConfig.SeccompProfilePath,
+	// otherwise use the default profile.  The SeccompProfilePath might be
+	// explicitly set by the user.
+	SeccompPolicyDefault SeccompPolicy = iota
+	// SeccompPolicyImage - if set use SecurityConfig.SeccompProfileFromImage,
+	// otherwise follow SeccompPolicyDefault.
+	SeccompPolicyImage
+)
+
+// Map for easy lookups of supported policies.
+var supportedSeccompPolicies = map[string]SeccompPolicy{
+	"":        SeccompPolicyDefault,
+	"default": SeccompPolicyDefault,
+	"image":   SeccompPolicyImage,
+}
+
+// LookupSeccompPolicy looksup the corresponding SeccompPolicy for the specified
+// string. If none is found, an errors is returned including the list of
+// supported policies.
+// Note that an empty string resolved to SeccompPolicyDefault.
+func LookupSeccompPolicy(s string) (SeccompPolicy, error) {
+	policy, exists := supportedSeccompPolicies[s]
+	if exists {
+		return policy, nil
+	}
+
+	// Sort the keys first as maps are non-deterministic.
+	keys := []string{}
+	for k := range supportedSeccompPolicies {
+		if k != "" {
+			keys = append(keys, k)
+		}
+	}
+	sort.Strings(keys)
+
+	return -1, errors.Errorf("invalid seccomp policy %q: valid policies are %+q", s, keys)
+}
+
 // SecurityConfig configures the security features for the container
 type SecurityConfig struct {
-	CapAdd             []string // cap-add
-	CapDrop            []string // cap-drop
-	LabelOpts          []string //SecurityOpts
-	NoNewPrivs         bool     //SecurityOpts
-	ApparmorProfile    string   //SecurityOpts
-	SeccompProfilePath string   //SecurityOpts
-	SecurityOpts       []string
-	Privileged         bool              //privileged
-	ReadOnlyRootfs     bool              //read-only
-	ReadOnlyTmpfs      bool              //read-only-tmpfs
-	Sysctl             map[string]string //sysctl
+	CapAdd                  []string // cap-add
+	CapDrop                 []string // cap-drop
+	LabelOpts               []string //SecurityOpts
+	NoNewPrivs              bool     //SecurityOpts
+	ApparmorProfile         string   //SecurityOpts
+	SeccompProfilePath      string   //SecurityOpts
+	SeccompProfileFromImage string   // seccomp profile from the container image
+	SeccompPolicy           SeccompPolicy
+	SecurityOpts            []string
+	Privileged              bool              //privileged
+	ReadOnlyRootfs          bool              //read-only
+	ReadOnlyTmpfs           bool              //read-only-tmpfs
+	Sysctl                  map[string]string //sysctl
 }
 
 // CreateConfig is a pre OCI spec structure.  It represents user input from varlink or the CLI
-- 
cgit v1.2.3-54-g00ecf