summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common.go4
-rw-r--r--cmd/podman/shared/create.go30
-rw-r--r--cmd/podman/shared/intermediate.go1
-rw-r--r--docs/source/markdown/podman-create.1.md6
-rw-r--r--docs/source/markdown/podman-run.1.md6
-rw-r--r--pkg/api/handlers/generic/swagger.go1
-rw-r--r--pkg/api/server/register_images.go44
-rw-r--r--pkg/api/server/register_pods.go27
-rw-r--r--pkg/spec/config_linux_cgo.go12
-rw-r--r--pkg/spec/createconfig.go67
-rw-r--r--test/e2e/config.go9
-rw-r--r--test/e2e/run_seccomp.go70
12 files changed, 225 insertions, 52 deletions
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index dc7590590..9064ec219 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -538,6 +538,10 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
"workdir", "w", "",
"Working directory inside the container",
)
+ createFlags.String(
+ "seccomp-policy", "default",
+ "Policy for selecting a seccomp profile (experimental)",
+ )
}
func getFormat(c *cliconfig.PodmanCommand) (string, error) {
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 05a3f5598..50a64b01c 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -31,6 +31,10 @@ import (
"github.com/sirupsen/logrus"
)
+// seccompAnnotationKey is the key of the image annotation embedding a seccomp
+// profile.
+const seccompAnnotationKey = "io.containers.seccomp.profile"
+
func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) {
var (
healthCheck *manifest.Schema2HealthConfig
@@ -67,7 +71,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
imageName := ""
- var data *inspect.ImageData = nil
+ var imageData *inspect.ImageData = nil
// Set the storage if there is no rootfs specified
if rootfs == "" {
@@ -99,17 +103,17 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
if err != nil {
return nil, nil, err
}
- data, err = newImage.Inspect(ctx)
+ imageData, err = newImage.Inspect(ctx)
if err != nil {
return nil, nil, err
}
- if overrideOS == "" && data.Os != goruntime.GOOS {
- return nil, nil, errors.Errorf("incompatible image OS %q on %q host", data.Os, goruntime.GOOS)
+ if overrideOS == "" && imageData.Os != goruntime.GOOS {
+ return nil, nil, errors.Errorf("incompatible image OS %q on %q host", imageData.Os, goruntime.GOOS)
}
- if overrideArch == "" && data.Architecture != goruntime.GOARCH {
- return nil, nil, errors.Errorf("incompatible image architecture %q on %q host", data.Architecture, goruntime.GOARCH)
+ if overrideArch == "" && imageData.Architecture != goruntime.GOARCH {
+ return nil, nil, errors.Errorf("incompatible image architecture %q on %q host", imageData.Architecture, goruntime.GOARCH)
}
names := newImage.Names()
@@ -171,7 +175,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
}
- createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, data)
+ createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, imageData)
if err != nil {
return nil, nil, err
}
@@ -712,6 +716,18 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
return nil, err
}
+ // SECCOMP
+ if data != nil {
+ if value, exists := data.Annotations[seccompAnnotationKey]; exists {
+ secConfig.SeccompProfileFromImage = value
+ }
+ }
+ if policy, err := cc.LookupSeccompPolicy(c.String("seccomp-policy")); err != nil {
+ return nil, err
+ } else {
+ secConfig.SeccompPolicy = policy
+ }
+
config := &cc.CreateConfig{
Annotations: annotations,
BuiltinImgVolumes: ImageVolumes,
diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go
index e985e4dc0..d1f0e602e 100644
--- a/cmd/podman/shared/intermediate.go
+++ b/cmd/podman/shared/intermediate.go
@@ -463,6 +463,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes
m["volume"] = newCRStringArray(c, "volume")
m["volumes-from"] = newCRStringSlice(c, "volumes-from")
m["workdir"] = newCRString(c, "workdir")
+ m["seccomp-policy"] = newCRString(c, "seccomp-policy")
// global flag
if !remote {
m["authfile"] = newCRString(c, "authfile")
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index abde899bf..81fae0b25 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -676,6 +676,12 @@ If specified, the first argument refers to an exploded container on the file sys
This is useful to run a container without requiring any image management, the rootfs
of the container is assumed to be managed externally.
+**--seccomp-policy**=*policy*
+
+Specify the policy to select the seccomp profile. If set to *image*, Podman will look for a "io.podman.seccomp.profile" annotation in the container image and use its value as a seccomp profile. Otherwise, Podman will follow the *default* policy by applying the default profile unless specified otherwise via *--security-opt seccomp* as described below.
+
+Note that this feature is experimental and may change in the future.
+
**--security-opt**=*option*
Security Options
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 9484ad347..af5bb814d 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -697,6 +697,12 @@ of the container is assumed to be managed externally.
Note: On `SELinux` systems, the rootfs needs the correct label, which is by default
`unconfined_u:object_r:container_file_t`.
+**--seccomp-policy**=*policy*
+
+Specify the policy to select the seccomp profile. If set to *image*, Podman will look for a "io.podman.seccomp.profile" annotation in the container image and use its value as a seccomp profile. Otherwise, Podman will follow the *default* policy by applying the default profile unless specified otherwise via *--security-opt seccomp* as described below.
+
+Note that this feature is experimental and may change in the future.
+
**--security-opt**=*option*
Security Options
diff --git a/pkg/api/handlers/generic/swagger.go b/pkg/api/handlers/generic/swagger.go
index 8df961b94..27e1fc18d 100644
--- a/pkg/api/handlers/generic/swagger.go
+++ b/pkg/api/handlers/generic/swagger.go
@@ -16,7 +16,6 @@ type swagCtrWaitResponse struct {
Body struct {
// container exit code
StatusCode int
- error message
Error struct {
Message string
}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 7298f3c61..916534fc7 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -10,7 +10,7 @@ import (
)
func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
- // swagger:operation POST /images/create images createImage
+ // swagger:operation POST /images/create compat_images createImage
//
// ---
// summary: Create an image from an image
@@ -40,7 +40,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// schema:
// $ref: '#/responses/GenericError'
r.Handle(VersionedPath("/images/create"), APIHandler(s.Context, generic.CreateImageFromImage)).Methods(http.MethodPost).Queries("fromImage", "{fromImage}")
- // swagger:operation POST /images/create images createImage
+ // swagger:operation POST /images/create compat_images createImage
// ---
// summary: Create an image from Source
// description: Create an image by either pulling it from a registry or importing it.
@@ -69,7 +69,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// schema:
// $ref: '#/responses/GenericError'
r.Handle(VersionedPath("/images/create"), APIHandler(s.Context, generic.CreateImageFromSrc)).Methods(http.MethodPost).Queries("fromSrc", "{fromSrc}")
- // swagger:operation GET /images/json images listImages
+ // swagger:operation GET /images/json compat_images listImages
// ---
// summary: List Images
// description: Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image.
@@ -85,7 +85,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// '500':
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/json"), APIHandler(s.Context, generic.GetImages)).Methods(http.MethodGet)
- // swagger:operation POST /images/load images loadImage
+ // swagger:operation POST /images/load compat_images loadImage
//
// ---
// summary: Import image
@@ -107,7 +107,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// '500':
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/load"), APIHandler(s.Context, handlers.LoadImage)).Methods(http.MethodPost)
- // swagger:operation POST /images/prune images pruneImages
+ // swagger:operation POST /images/prune compat_images pruneImages
// ---
// summary: Prune unused images
// description: Remove images from local storage that are not being used by a container
@@ -132,7 +132,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// '500':
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/prune"), APIHandler(s.Context, generic.PruneImages)).Methods(http.MethodPost)
- // swagger:operation GET /images/search images searchImages
+ // swagger:operation GET /images/search compat_images searchImages
// ---
// summary: Search images
// description: Search registries for an image
@@ -161,7 +161,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// '500':
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/search"), APIHandler(s.Context, handlers.SearchImages)).Methods(http.MethodGet)
- // swagger:operation DELETE /images/{nameOrID} images removeImage
+ // swagger:operation DELETE /images/{nameOrID} compat_images removeImage
// ---
// summary: Remove Image
// description: Delete an image from local storage
@@ -173,20 +173,20 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - in: query
// name: noprune
// type: bool
- // description: not supported
+ // description: not supported. will be logged as an invalid parameter if enabled
// produces:
// - application/json
// responses:
// '200':
// $ref: "#/responses/DocsImageDeleteResponse"
- // '404':
+ // '400':
// $ref: '#/responses/BadParamError'
// '409':
// $ref: '#/responses/ConflictError'
// '500':
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/{name:..*}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
- // swagger:operation GET /images/{nameOrID}/get images exportImage
+ // swagger:operation GET /images/{nameOrID}/get compat_images exportImage
// ---
// summary: Export an image
// description: Export an image in tarball format
@@ -200,10 +200,13 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// responses:
// '200':
// description: no error
+ // schema:
+ // type: string
+ // format: binary
// '500':
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/{name:..*}/get"), APIHandler(s.Context, generic.ExportImage)).Methods(http.MethodGet)
- // swagger:operation GET /images/{nameOrID}/history images imageHistory
+ // swagger:operation GET /images/{nameOrID}/history compat_images imageHistory
// ---
// summary: History of an image
// description: Return parent layers of an image.
@@ -222,7 +225,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// '500':
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/images/{name:..*}/history"), APIHandler(s.Context, handlers.HistoryImage)).Methods(http.MethodGet)
- // swagger:operation GET /images/{nameOrID}/json images inspectImage
+ // swagger:operation GET /images/{nameOrID}/json compat_images inspectImage
// ---
// summary: Inspect an image
// description: Return low-level information about an image.
@@ -241,7 +244,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// '500':
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/images/{name:..*}/json"), APIHandler(s.Context, generic.GetImage))
- // swagger:operation POST /images/{nameOrID}/tag images tagImage
+ // swagger:operation POST /images/{nameOrID}/tag compat_images tagImage
// ---
// summary: Tag an image
// description: Tag an image so that it becomes part of a repository.
@@ -272,7 +275,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/{name:..*}/tag"), APIHandler(s.Context, handlers.TagImage)).Methods(http.MethodPost)
- // swagger:operation POST /commit/ commit commitContainer
+ // swagger:operation POST /commit/ compat_commit commitContainer
// ---
// summary: Create a new image from a container
// parameters:
@@ -377,7 +380,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// swagger:operation POST /libpod/images/load images libpodLoadImage
// ---
// summary: Import image
- // description: tbd
+ // description: Load a set of images and tags into a repository.
// parameters:
// - in: query
// name: quiet
@@ -463,10 +466,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// name: force
// type: bool
// description: remove the image even if used by containers or has other tags
- // - in: query
- // name: noprune
- // type: bool
- // description: not supported
// produces:
// - application/json
// responses:
@@ -474,6 +473,8 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// schema:
// items:
// $ref: "#/responses/DocsIageDeleteResponse"
+ // '400':
+ // $ref: "#/responses/BadParamError"
// '404':
// $ref: '#/responses/NoSuchImage'
// '409':
@@ -503,6 +504,9 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// responses:
// '200':
// description: no error
+ // schema:
+ // type: string
+ // format: binary
// '404':
// $ref: '#/responses/NoSuchImage'
// '500':
@@ -521,8 +525,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - application/json
// responses:
// '200':
- // schema:
- // items:
// $ref: "#/responses/DocsLibpodInspectImageResponse"
// '404':
// $ref: '#/responses/NoSuchImage'
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index 1f09e8f0e..5069326b6 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -19,11 +19,14 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// descriptions: needs description and plumbing for filters
// responses:
// '200':
- // $ref: "#/responses/ListPodsResponse"
+ // properties:
+ // items:
+ // $ref: "#/responses/ListPodsResponse"
+ // type: array
// '400':
// $ref: "#/responses/BadParamError"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/json"), APIHandler(s.Context, libpod.Pods)).Methods(http.MethodGet)
r.Handle(VersionedPath("/libpod/pods/create"), APIHandler(s.Context, libpod.PodCreate)).Methods(http.MethodPost)
// swagger:operation POST /libpod/pods/prune pods PrunePods
@@ -43,7 +46,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '400':
// $ref: "#/responses/BadParamError"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/prune"), APIHandler(s.Context, libpod.PodPrune)).Methods(http.MethodPost)
// swagger:operation DELETE /libpod/pods/{nameOrID} pods removePod
// ---
@@ -67,7 +70,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '404':
// $ref: "#/responses/NoSuchPod"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name:..*}"), APIHandler(s.Context, libpod.PodDelete)).Methods(http.MethodDelete)
// swagger:operation GET /libpod/pods/{nameOrID}/json pods inspectPod
// ---
@@ -85,7 +88,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '404':
// $ref: "#/responses/NoSuchPod"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name:..*}/json"), APIHandler(s.Context, libpod.PodInspect)).Methods(http.MethodGet)
// swagger:operation GET /libpod/pods/{nameOrID}/exists pods podExists
// ---
@@ -104,7 +107,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '404':
// $ref: "#/responses/NoSuchPod"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name:..*}/exists"), APIHandler(s.Context, libpod.PodExists)).Methods(http.MethodGet)
// swagger:operation POST /libpod/pods/{nameOrID}/kill pods killPod
// ---
@@ -130,7 +133,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '409':
// $ref: "#/responses/ConflictError"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name:..*}/kill"), APIHandler(s.Context, libpod.PodKill)).Methods(http.MethodPost)
// swagger:operation POST /libpod/pods/{nameOrID}/pause pods pausePod
// ---
@@ -148,7 +151,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '404':
// $ref: "#/responses/NoSuchPod"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name:..*}/pause"), APIHandler(s.Context, libpod.PodPause)).Methods(http.MethodPost)
// swagger:operation POST /libpod/pods/{nameOrID}/restart pods restartPod
// ---
@@ -166,7 +169,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '404':
// $ref: "#/responses/NoSuchPod"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name:..*}/restart"), APIHandler(s.Context, libpod.PodRestart)).Methods(http.MethodPost)
// swagger:operation POST /libpod/pods/{nameOrID}/start pods startPod
// ---
@@ -186,7 +189,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '404':
// $ref: "#/responses/NoSuchPod"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name:..*}/start"), APIHandler(s.Context, libpod.PodStart)).Methods(http.MethodPost)
// swagger:operation POST /libpod/pods/{nameOrID}/stop pods stopPod
// ---
@@ -212,7 +215,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '404':
// $ref: "#/responses/NoSuchPod"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name:..*}/stop"), APIHandler(s.Context, libpod.PodStop)).Methods(http.MethodPost)
// swagger:operation POST /libpod/pods/{nameOrID}/unpause pods unpausePod
// ---
@@ -230,7 +233,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// '404':
// $ref: "#/responses/NoSuchPod"
// '500':
- // $ref: "#responses/InternalError"
+ // $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name:..*}/unpause"), APIHandler(s.Context, libpod.PodUnpause)).Methods(http.MethodPost)
return nil
}
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 6d058229b..fb222083b 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
diff --git a/test/e2e/config.go b/test/e2e/config.go
index aeb7affee..12d0e545e 100644
--- a/test/e2e/config.go
+++ b/test/e2e/config.go
@@ -14,4 +14,13 @@ var (
BB = "docker.io/library/busybox:latest"
healthcheck = "docker.io/libpod/alpine_healthcheck:latest"
ImageCacheDir = "/tmp/podman/imagecachedir"
+
+ // This image has seccomp profiles that blocks all syscalls.
+ // The intention behind blocking all syscalls is to prevent
+ // regressions in the future. The required syscalls can vary
+ // depending on which runtime we're using.
+ alpineSeccomp = "docker.io/libpod/alpine-with-seccomp:latest"
+ // This image has a bogus/invalid seccomp profile which should
+ // yield a json error when being read.
+ alpineBogusSeccomp = "docker.io/libpod/alpine-with-bogus-seccomp:latest"
)
diff --git a/test/e2e/run_seccomp.go b/test/e2e/run_seccomp.go
new file mode 100644
index 000000000..dcf938ad6
--- /dev/null
+++ b/test/e2e/run_seccomp.go
@@ -0,0 +1,70 @@
+// +build !remoteclient
+
+package integration
+
+import (
+ "os"
+
+ . "github.com/containers/libpod/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman run", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ podmanTest.Setup()
+ podmanTest.SeedImages()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ processTestResult(f)
+
+ })
+
+ It("podman run --seccomp-policy default", func() {
+ session := podmanTest.Podman([]string{"run", "--seccomp-policy", "default", alpineSeccomp, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
+ It("podman run --seccomp-policy ''", func() {
+ // Empty string is interpreted as "default".
+ session := podmanTest.Podman([]string{"run", "--seccomp-policy", "", alpineSeccomp, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
+ It("podman run --seccomp-policy invalid", func() {
+ session := podmanTest.Podman([]string{"run", "--seccomp-policy", "invalid", alpineSeccomp, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("podman run --seccomp-policy image (block all syscalls)", func() {
+ session := podmanTest.Podman([]string{"run", "--seccomp-policy", "image", alpineSeccomp, "ls"})
+ session.WaitWithDefaultTimeout()
+ // TODO: we're getting a "cannot start a container that has
+ // stopped" error which seems surprising. Investigate
+ // why that is so.
+ Expect(session.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("podman run --seccomp-policy image (bogus profile)", func() {
+ session := podmanTest.Podman([]string{"run", "--seccomp-policy", "image", alpineBogusSeccomp, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(125))
+ })
+})