diff --git a/README.md b/README.md index bc6645de146..1e9eb67a658 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,7 @@ Additionally, the following environment variable(s) can be used to configure the | `ECS_ALLOW_OFFHOST_INTROSPECTION_ACCESS` | <true | false> | By default, the ecs-init service adds an iptable rule to block access to ECS Agent's introspection port from off-host (or containers in awsvpc network mode), and removes the rule upon stop. If `ECS_ALLOW_OFFHOST_INTROSPECTION_ACCESS` is set to true, this rule will not be added/removed. | false | | `ECS_OFFHOST_INTROSPECTION_INTERFACE_NAME` | `eth0` | Primary network interface name to be used for blocking offhost agent introspection port access. By default, this value is `eth0` | `eth0` | | `ECS_AGENT_LABELS` | `{"test.label.1":"value1","test.label.2":"value2"}` | The labels to add to the ECS Agent container. | | +| `ECS_AGENT_APPARMOR_PROFILE` | `unconfined` | Specifies the name of the AppArmor profile to run the ecs-agent container under. This only applies to AppArmor-enabled systems, such as Ubuntu, Debian, and SUSE. If unset, defaults to the profile written out by ecs-init (ecs-agent-default). | `ecs-agent-default` | diff --git a/ecs-init/apparmor/apparmor.go b/ecs-init/apparmor/apparmor.go index 8e870bb554e..9034d802d89 100644 --- a/ecs-init/apparmor/apparmor.go +++ b/ecs-init/apparmor/apparmor.go @@ -1,3 +1,16 @@ +// Copyright 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 ( @@ -5,37 +18,36 @@ import ( "os" "path/filepath" + "github.com/aws/amazon-ecs-agent/ecs-init/config" "github.com/docker/docker/pkg/aaparser" aaprofile "github.com/docker/docker/profiles/apparmor" ) const ( - ECSDefaultProfileName = "ecs-default" - appArmorProfileDir = "/etc/apparmor.d" + ECSAgentDefaultProfileName = config.ECSAgentAppArmorDefaultProfileName + appArmorProfileDir = "/etc/apparmor.d" ) -const ecsDefaultProfile = ` +const ecsAgentDefaultProfile = ` #include -profile ecs-default flags=(attach_disconnected,mediate_deleted) { +profile ecs-agent-default flags=(attach_disconnected,mediate_deleted) { #include - 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 - capability dac_override, # Allow ECS Agent to file read, write, and execute permission - + network inet, + network inet6, + network netlink, + network unix, + capability, 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, + signal (send,receive) peer=ecs-agent-default, # ECS agent requires DBUS send - dbus (send) bus=system, + dbus (send,receive) bus=system, deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) # deny write to files not in /proc//** or /proc/sys/** @@ -56,7 +68,8 @@ profile ecs-default flags=(attach_disconnected,mediate_deleted) { 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, + ptrace (trace,read,tracedby,readby) peer=ecs-agent-default, + ptrace (trace,read,tracedby,readby) peer=docker-default, } ` @@ -69,10 +82,7 @@ var ( // 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 yes { - return nil - } + _, err := isProfileLoaded(profileName) if err != nil { return err } @@ -82,7 +92,7 @@ func LoadDefaultProfile(profileName string) error { return err } defer f.Close() - _, err = f.WriteString(ecsDefaultProfile) + _, err = f.WriteString(ecsAgentDefaultProfile) if err != nil { return err } diff --git a/ecs-init/apparmor/apparmor_test.go b/ecs-init/apparmor/apparmor_test.go index a3a82f4da80..46e5488c1af 100644 --- a/ecs-init/apparmor/apparmor_test.go +++ b/ecs-init/apparmor/apparmor_test.go @@ -1,10 +1,10 @@ -// Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 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/ +// 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 diff --git a/ecs-init/config/common.go b/ecs-init/config/common.go index e854f225228..e934eda563a 100644 --- a/ecs-init/config/common.go +++ b/ecs-init/config/common.go @@ -104,6 +104,11 @@ const ( // defaultCredentialsFetcherSocketPath is set to /var/credentials-fetcher/socket/credentials_fetcher.sock // in case path is not passed in the env variable DefaultCredentialsFetcherSocketPath = "/var/credentials-fetcher/socket/credentials_fetcher.sock" + + // ECSAgentAppArmorProfileNameEnvVar specifies the AppArmor profile name to use. Only applies + // on AppArmor-enabled platforms (such as Ubuntu and Debian). + ECSAgentAppArmorProfileNameEnvVar = "ECS_AGENT_APPARMOR_PROFILE" + ECSAgentAppArmorDefaultProfileName = "ecs-agent-default" ) // partitionBucketRegion provides the "partitional" bucket region @@ -249,7 +254,7 @@ func CgroupMountpoint() string { // MountDirectoryEBS returns the location on disk where EBS volumes will be mounted func MountDirectoryEBS() string { - return directoryPrefix + "/mnt/ecs/ebs" + return directoryPrefix + "/mnt/ecs/ebs" } // HostCertsDirPath() returns the CA store path on the host @@ -336,6 +341,15 @@ func RunningInExternal() bool { return envVar == "true" } +// ECSAgentApparmorProfileName returns the name of the AppArmor profile to use. +func ECSAgentAppArmorProfileName() string { + envVar := os.Getenv(ECSAgentAppArmorProfileNameEnvVar) + if len(strings.TrimSpace(envVar)) == 0 { + return ECSAgentAppArmorDefaultProfileName + } + return envVar +} + func agentArtifactName(version string, arch string) (string, error) { var interpose string switch arch { diff --git a/ecs-init/config/common_test.go b/ecs-init/config/common_test.go index 749833e0896..0e3be139216 100644 --- a/ecs-init/config/common_test.go +++ b/ecs-init/config/common_test.go @@ -50,6 +50,16 @@ func TestDockerUnixSocketWithDockerHost(t *testing.T) { } } +func TestECSAgentAppArmorProfileName(t *testing.T) { + profile := ECSAgentAppArmorProfileName() + assert.Equal(t, profile, "ecs-agent-default") + + os.Setenv(ECSAgentAppArmorProfileNameEnvVar, "docker-default") + defer os.Unsetenv(ECSAgentAppArmorProfileNameEnvVar) + profile = ECSAgentAppArmorProfileName() + assert.Equal(t, profile, "docker-default") +} + func TestGetAgentPartitionBucketRegion(t *testing.T) { testCases := []struct { region string diff --git a/ecs-init/docker/docker_config.go b/ecs-init/docker/docker_config.go index 0515edd026c..7a43f9a8974 100644 --- a/ecs-init/docker/docker_config.go +++ b/ecs-init/docker/docker_config.go @@ -16,7 +16,6 @@ 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" @@ -67,7 +66,7 @@ func createHostConfig(binds []string) *godocker.HostConfig { } if ctrdapparmor.HostSupports() { - hostConfig.SecurityOpt = []string{fmt.Sprintf("apparmor:%s", apparmor.ECSDefaultProfileName)} + hostConfig.SecurityOpt = []string{fmt.Sprintf("apparmor:%s", config.ECSAgentAppArmorProfileName())} } if config.RunPrivileged() { diff --git a/ecs-init/engine/engine.go b/ecs-init/engine/engine.go index b4bbbeb4710..cdaa7d19d28 100644 --- a/ecs-init/engine/engine.go +++ b/ecs-init/engine/engine.go @@ -206,12 +206,12 @@ func (e *Engine) PreStartGPU() error { return nil } -// PreStartAppArmor sets up the ecs-default AppArmor profile if we're running +// PreStartAppArmor sets up the ecs-agent-default AppArmor profile if we're running // on an AppArmor-enabled system. func (e *Engine) PreStartAppArmor() error { if hostSupports() { - log.Infof("pre-start: setting up %s AppArmor profile", apparmor.ECSDefaultProfileName) - return loadDefaultProfile(apparmor.ECSDefaultProfileName) + log.Infof("pre-start: setting up %s AppArmor profile", apparmor.ECSAgentDefaultProfileName) + return loadDefaultProfile(apparmor.ECSAgentDefaultProfileName) } return nil }