Skip to content

Commit

Permalink
Add apparmor support
Browse files Browse the repository at this point in the history
  • Loading branch information
SreeeS committed Oct 2, 2023
1 parent 54e8d53 commit 75aa213
Show file tree
Hide file tree
Showing 14 changed files with 442 additions and 60 deletions.
91 changes: 91 additions & 0 deletions ecs-init/apparmor/apparmor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package apparmor

import (
"fmt"
"os"
"path/filepath"
)

const (
ECSDefaultProfileName = "ecs-default"
appArmorProfileDir = "/etc/apparmor.d"
)

const ecsDefaultProfile = `
#include <tunables/global>
profile ecs-default flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
network inet, # Allow IPv4 traffic
network inet6, # Allow IPv6 traffic
capability net_admin, # Allow network configuration
capability sys_admin, # Allow ECS Agent to invoke the setns system call
file,
umount,
# Host (privileged) processes may send signals to container processes.
signal (receive) peer=unconfined,
# Container processes may send signals amongst themselves.
signal (send,receive) peer=ecs-default,
# ECS agent requires DBUS send
dbus (send) bus=system,
deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir)
# deny write to files not in /proc/<number>/** or /proc/sys/**
deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9/]*}/** w,
deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/kcore rwklx,
deny mount,
deny /sys/[^f]*/** wklx,
deny /sys/f[^s]*/** wklx,
deny /sys/fs/[^c]*/** wklx,
deny /sys/fs/c[^g]*/** wklx,
deny /sys/fs/cg[^r]*/** wklx,
deny /sys/firmware/** rwklx,
deny /sys/kernel/security/** rwklx,
# suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
ptrace (trace,read,tracedby,readby) peer=ecs-default,
}
`

var (
isProfileLoaded = isLoaded
loadPath = load
createFile = os.Create
)

// LoadDefaultProfile ensures the default profile to be loaded with the given name.
// Returns nil error if the profile is already loaded.
func LoadDefaultProfile(profileName string) error {
yes, err := isProfileLoaded(profileName)
if err != nil {
return err
}
if yes {
return nil
}

f, err := createFile(filepath.Join(appArmorProfileDir, profileName))
if err != nil {
return err
}
defer f.Close()
_, err = f.WriteString(ecsDefaultProfile)
if err != nil {
return err
}
path := f.Name()

if err := loadPath(path); err != nil {
return fmt.Errorf("error loading apparmor profile %s: %w", path, err)
}
return nil
}
101 changes: 101 additions & 0 deletions ecs-init/apparmor/apparmor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package apparmor

import (
"errors"
"github.com/containerd/containerd/pkg/apparmor"
"github.com/stretchr/testify/require"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

func TestLoadDefaultProfile(t *testing.T) {
testCases := []struct {
name string
profileName string
isLoadedResponse bool
isLoadedError error
loadError error
expectedError error
}{
{
name: "ProfileIsAlreadyLoaded",
profileName: "testProfile.txt",
isLoadedResponse: true,
isLoadedError: nil,
loadError: nil,
expectedError: nil,
},
{
name: "ProfileNotLoaded",
profileName: "testProfile.txt",
isLoadedResponse: false,
isLoadedError: nil,
loadError: nil,
expectedError: nil,
},
{
name: "IsLoadedError",
profileName: "testProfile.txt",
isLoadedResponse: false,
isLoadedError: errors.New("mock isLoaded error"),
loadError: nil,
expectedError: errors.New("mock isLoaded error"),
},
{
name: "LoadProfileError",
profileName: "testProfile.txt",
isLoadedResponse: false,
isLoadedError: nil,
loadError: errors.New("mock load error"),
expectedError: errors.New("mock load error"),
},
}
defer func() {
isProfileLoaded = isLoaded
loadPath = load
createFile = os.Create
}()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if !apparmor.HostSupports() {
t.Skip()
}
tmpdir := os.TempDir()
filePath, err := os.MkdirTemp(tmpdir, "test")
require.NoError(t, err)
createFile = func(profileName string) (*os.File, error) {
f, err := os.Create(filepath.Join(filePath, tc.profileName))
return f, err
}
defer os.RemoveAll(filePath)
isProfileLoaded = func(profileName string) (bool, error) {
return tc.isLoadedResponse, tc.isLoadedError
}
loadPath = func(profile string) error {
return tc.loadError
}
err = LoadDefaultProfile(tc.profileName)
if tc.loadError == nil {
assert.Equal(t, tc.expectedError, err)
} else {
assert.Error(t, err)
}
})
}
}
68 changes: 68 additions & 0 deletions ecs-init/apparmor/apparmor_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright The docker Authors.
Copyright The Moby Authors.
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package apparmor

import (
"bufio"
"fmt"
"io"
"os"
"strings"

exec "golang.org/x/sys/execabs"
)

// NOTE: This code is copied from <github.com/docker/docker/profiles/apparmor>.
// If you plan to make any changes, please make sure they are also sent
// upstream.

func load(path string) error {
out, err := aaParser("-Kr", path)
if err != nil {
return fmt.Errorf("parser error(%q): %w", strings.TrimSpace(out), err)
}
return nil
}

func aaParser(args ...string) (string, error) {
out, err := exec.Command("apparmor_parser", args...).CombinedOutput()
return string(out), err
}

func isLoaded(name string) (bool, error) {
f, err := os.Open("/sys/kernel/security/apparmor/profiles")
if err != nil {
return false, err
}
defer f.Close()
r := bufio.NewReader(f)
for {
p, err := r.ReadString('\n')
if err == io.EOF {
break
}
if err != nil {
return false, err
}
if strings.HasPrefix(p, name+" ") {
return true, nil
}
}
return false, nil
}
51 changes: 0 additions & 51 deletions ecs-init/config/development.go

This file was deleted.

3 changes: 0 additions & 3 deletions ecs-init/config/release.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//go:build !development
// +build !development

// Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
Expand Down
8 changes: 8 additions & 0 deletions ecs-init/docker/docker_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
package docker

import (
"fmt"

"github.com/aws/amazon-ecs-agent/ecs-init/apparmor"
"github.com/aws/amazon-ecs-agent/ecs-init/config"
ctrdapparmor "github.com/containerd/containerd/pkg/apparmor"
godocker "github.com/fsouza/go-dockerclient"
)

Expand Down Expand Up @@ -61,6 +65,10 @@ func createHostConfig(binds []string) *godocker.HostConfig {
Init: true,
}

if ctrdapparmor.HostSupports() {
hostConfig.SecurityOpt = []string{fmt.Sprintf("apparmor:%s", apparmor.ECSDefaultProfileName)}
}

if config.RunPrivileged() {
hostConfig.Privileged = true
}
Expand Down
Loading

0 comments on commit 75aa213

Please sign in to comment.