Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: refactor security spec #2271

Merged
merged 3 commits into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ script: |
make coverage
elif [[ "${TEST_SUITE}" = "criv1alpha1test" ]]; then
make build
TEST_FLAGS= make build-daemon-integration
TEST_FLAGS= BUILDTAGS="selinux seccomp apparmor" make build-daemon-integration
sudo env "PATH=$PATH" make install

sudo env "PATH=$PATH" make download-dependencies
sudo env "PATH=$PATH" make cri-v1alpha1-test
make coverage
elif [[ "${TEST_SUITE}" = "criv1alpha2test" ]]; then
make build
TEST_FLAGS= make build-daemon-integration
TEST_FLAGS= BUILDTAGS="selinux seccomp apparmor" make build-daemon-integration
sudo env "PATH=$PATH" make install

sudo env "PATH=$PATH" make download-dependencies
Expand Down
16 changes: 16 additions & 0 deletions INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,22 @@ Makefile target named `build` will compile the pouch and pouchd binaries into cu
make build && make install
```

#### Build Tags

Pouch not use build tags by default, if you want pouchd to support additional security options, build pouchd with tags like

```
make BUILDTAGS='seccomp apparmor selinux'
```

Supported build Tags

| Tags | Description |
|----------|---------------------------------------------------------|
| seccomp | filter syscalls |
| apparmor | restrict program capabilities with per-program profiles |
| selinux | selinux process and mount label |

### Start PouchContainer

With all needed binaries installed, you could start pouchd via:
Expand Down
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ DEFAULT_LDFLAGS="-X ${VERSION_PKG}/version.GitCommit=${GIT_COMMIT} \
-X ${VERSION_PKG}/version.ApiVersion=${API_VERSION} \
-X ${VERSION_PKG}/version.BuildTime=${BUILD_TIME}"

GOBUILD_TAGS=$(if $(BUILDTAGS),-tags "$(BUILDTAGS)",)

# COVERAGE_PACKAGES is the coverage we care about.
COVERAGE_PACKAGES=$(shell go list ./... | \
grep -v github.com/alibaba/pouch$$ | \
Expand All @@ -74,7 +76,7 @@ build: build-daemon build-cli ## build PouchContainer both daemon and cli binari
build-daemon: modules plugin ## build PouchContainer daemon binary
@echo "$@: bin/${DAEMON_BINARY_NAME}"
@mkdir -p bin
@GOOS=linux go build -ldflags ${DEFAULT_LDFLAGS} -o bin/${DAEMON_BINARY_NAME} -tags 'selinux'
@GOOS=linux go build -ldflags ${DEFAULT_LDFLAGS} ${GOBUILD_TAGS} -o bin/${DAEMON_BINARY_NAME}

build-cli: ## build PouchContainer cli binary
@echo "$@: bin/${CLI_BINARY_NAME}"
Expand All @@ -84,7 +86,7 @@ build-cli: ## build PouchContainer cli binary
build-daemon-integration: modules plugin ## build PouchContainer daemon integration testing binary
@echo $@
@mkdir -p bin
go test -c ${TEST_FLAGS} \
go test -c ${TEST_FLAGS} ${GOBUILD_TAGS} \
-cover -covermode=atomic -coverpkg ${COVERAGE_PACKAGES_LIST} \
-o bin/${DAEMON_INTEGRATION_BINARY_NAME}

Expand Down
3 changes: 3 additions & 0 deletions daemon/mgr/container_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ var (

// DefaultStatsInterval is the interval configured for stats.
DefaultStatsInterval = time.Duration(time.Second)

// ProfileUnconfined means run a container without the default seccomp profile.
ProfileUnconfined = "unconfined"
)

var (
Expand Down
6 changes: 0 additions & 6 deletions daemon/mgr/container_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,6 @@ func parseSecurityOpts(c *Container, securityOpts []string) error {
c.AppArmorProfile = value
case "seccomp":
c.SeccompProfile = value
case "no-new-privileges":
noNewPrivileges, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("invalid --security-opt: %q", securityOpt)
}
c.NoNewPrivileges = noNewPrivileges
case "label":
labelOpts = append(labelOpts, value)
default:
Expand Down
18 changes: 17 additions & 1 deletion daemon/mgr/container_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,23 @@ func (mgr *ContainerManager) validateConfig(c *Container, update bool) ([]string
return warnings, err
}

// TODO: validate config
// validate seccomp, apparmor security parameters
sysInfo := system.NewInfo()
if !sysInfo.Seccomp {
if c.SeccompProfile != "" || c.SeccompProfile != ProfileUnconfined {
warnings = append(warnings, fmt.Sprintf("Current Kernel does not support seccomp, discard --security-opt seccomp=%s", c.SeccompProfile))
}
// always set SeccompProfile to unconfined if kernel not support seccomp
c.SeccompProfile = ProfileUnconfined

}
if !sysInfo.AppArmor {
if c.AppArmorProfile != "" {
warnings = append(warnings, fmt.Sprintf("Current Kernel does not support apparmor, discard --security-opt apparmor=%s", c.AppArmorProfile))
}
c.AppArmorProfile = ""
}

return warnings, nil
}

Expand Down
28 changes: 28 additions & 0 deletions daemon/mgr/spec_apparmor_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// +build apparmor,linux

package mgr

import (
"context"

"github.com/opencontainers/runc/libcontainer/apparmor"
specs "github.com/opencontainers/runtime-spec/specs-go"
)

func setupAppArmor(ctx context.Context, c *Container, s *specs.Spec) error {
if apparmor.IsEnabled() {
appArmorProfile := ""
if c.AppArmorProfile != "" {
appArmorProfile = c.AppArmorProfile
} else if c.HostConfig.Privileged {
appArmorProfile = "unconfined"
} else {
// TODO: generate pouch-default apparmor profile
// appArmorProfile = "pouch-default"
}

s.Process.ApparmorProfile = appArmorProfile
}

return nil
}
14 changes: 14 additions & 0 deletions daemon/mgr/spec_apparmor_linux_unsupported.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// +build !apparmor

package mgr

import (
"context"

specs "github.com/opencontainers/runtime-spec/specs-go"
)

// do nothing in this case
func setupAppArmor(ctx context.Context, c *Container, s *specs.Spec) error {
return nil
}
34 changes: 0 additions & 34 deletions daemon/mgr/spec_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package mgr

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
Expand All @@ -14,7 +12,6 @@ import (
"github.com/alibaba/pouch/apis/opts"
"github.com/alibaba/pouch/apis/types"

"github.com/containerd/containerd/contrib/seccomp"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
specs "github.com/opencontainers/runtime-spec/specs-go"
Expand Down Expand Up @@ -84,37 +81,6 @@ func populatePlatform(ctx context.Context, c *Container, specWrapper *SpecWrappe
return setupNamespaces(ctx, c, specWrapper)
}

// setupSeccomp creates seccomp security settings spec.
func setupSeccomp(ctx context.Context, c *Container, s *specs.Spec) error {
if c.HostConfig.Privileged {
return nil
}

if s.Linux.Seccomp == nil {
s.Linux.Seccomp = &specs.LinuxSeccomp{}
}

// TODO: check whether seccomp is enable in your kernel, if not, cannot run a custom seccomp prifle.
seccompProfile := c.SeccompProfile
switch seccompProfile {
case ProfileNameUnconfined:
return nil
case ProfilePouchDefault, "":
s.Linux.Seccomp = seccomp.DefaultProfile(s)
default:
data, err := ioutil.ReadFile(seccompProfile)
if err != nil {
return fmt.Errorf("failed to load seccomp profile %q: %v", seccompProfile, err)
}
err = json.Unmarshal(data, s.Linux.Seccomp)
if err != nil {
return fmt.Errorf("failed to decode seccomp profile %q: %v", seccompProfile, err)
}
}

return nil
}

// setupResource creates linux resource spec.
func setupResource(ctx context.Context, c *Container, s *specs.Spec) error {
if s.Linux.Resources == nil {
Expand Down
44 changes: 0 additions & 44 deletions daemon/mgr/spec_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package mgr
import (
"context"
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/alibaba/pouch/apis/types"
Expand Down Expand Up @@ -38,7 +36,6 @@ func setupProcess(ctx context.Context, c *Container, s *specs.Spec) error {
if !c.HostConfig.Privileged {
s.Process.SelinuxLabel = c.ProcessLabel
s.Process.NoNewPrivileges = c.NoNewPrivileges

}

if err := setupUser(ctx, c, s); err != nil {
Expand Down Expand Up @@ -110,47 +107,6 @@ func setupCapabilities(ctx context.Context, hostConfig *types.HostConfig, s *spe
return nil
}

// isAppArmorEnabled returns true if apparmor is enabled for the host.
// This function is forked from
// https://github.com/opencontainers/runc/blob/1a81e9ab1f138c091fe5c86d0883f87716088527/libcontainer/apparmor/apparmor.go
// to avoid the libapparmor dependency.
func isAppArmorEnabled() bool {
if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil && os.Getenv("container") == "" {
if _, err = os.Stat("/sbin/apparmor_parser"); err == nil {
buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
return err == nil && len(buf) > 1 && buf[0] == 'Y'
}
}
return false
}

func setupAppArmor(ctx context.Context, c *Container, s *specs.Spec) error {
if !isAppArmorEnabled() {
// Return if the apparmor is disabled.
return nil
}

appArmorProfile := c.AppArmorProfile
switch appArmorProfile {
case ProfileNameUnconfined:
return nil
case ProfileRuntimeDefault:
// TODO: handle runtime default case.
return nil
case "":
if c.HostConfig.Privileged {
return nil
}
// TODO: if user does not specify the AppArmor and the container is not in privilege mode,
// we need to specify it as default case, handle it later.
return nil
default:
s.Process.ApparmorProfile = appArmorProfile
}

return nil
}

func setupRlimits(ctx context.Context, hostConfig *types.HostConfig, s *specs.Spec) error {
var rlimits []specs.POSIXRlimit
for _, ul := range hostConfig.Ulimits {
Expand Down
43 changes: 43 additions & 0 deletions daemon/mgr/spec_seccomp_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// +build linux,seccomp

package mgr

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"

"github.com/containerd/containerd/contrib/seccomp"
specs "github.com/opencontainers/runtime-spec/specs-go"
)

// setupSeccomp creates seccomp security settings spec.
func setupSeccomp(ctx context.Context, c *Container, s *specs.Spec) error {
if c.HostConfig.Privileged {
return nil
}

if s.Linux.Seccomp == nil {
s.Linux.Seccomp = &specs.LinuxSeccomp{}
}

seccompProfile := c.SeccompProfile
switch seccompProfile {
case ProfileNameUnconfined:
return nil
case ProfilePouchDefault, "":
s.Linux.Seccomp = seccomp.DefaultProfile(s)
default:
data, err := ioutil.ReadFile(seccompProfile)
if err != nil {
return fmt.Errorf("failed to load seccomp profile %q: %v", seccompProfile, err)
}
err = json.Unmarshal(data, s.Linux.Seccomp)
if err != nil {
return fmt.Errorf("failed to decode seccomp profile %q: %v", seccompProfile, err)
}
}

return nil
}
18 changes: 18 additions & 0 deletions daemon/mgr/spec_seccomp_unsupported.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// +build linux,!seccomp

package mgr

import (
"context"
"fmt"

specs "github.com/opencontainers/runtime-spec/specs-go"
)

func setupSeccomp(ctx context.Context, c *Container, s *specs.Spec) error {
if c.SeccompProfile != "" && c.SeccompProfile != "unconfined" {
return fmt.Errorf("Seccomp is not support by pouch, can not set seccomp profile %s", c.SeccompProfile)
}

return nil
}
Loading