Przeglądaj źródła

Create CloudWatch LogGroup and IAM TaskExecutionRole

As part of the CloudFormation template, create a LogGroup and configure
task with awslogs log-driver. Also create a dedicated IAM Role, with
AmazonECSTaskExecutionRolePolicy. This one will later be fine-tuned to
grant access to secrets/config and other AWS resources according to
custom extensions

close #42

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 5 lat temu
rodzic
commit
3e30f2cd1a

+ 19 - 7
ecs/pkg/amazon/cloudformation.go

@@ -6,15 +6,17 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/compose-spec/compose-go/types"
-	"github.com/sirupsen/logrus"
+	"github.com/awslabs/goformation/v4/cloudformation/logs"
 
 	ecsapi "github.com/aws/aws-sdk-go/service/ecs"
 	"github.com/awslabs/goformation/v4/cloudformation"
 	"github.com/awslabs/goformation/v4/cloudformation/ec2"
 	"github.com/awslabs/goformation/v4/cloudformation/ecs"
+	"github.com/awslabs/goformation/v4/cloudformation/iam"
+	"github.com/compose-spec/compose-go/types"
 	"github.com/docker/ecs-plugin/pkg/compose"
 	"github.com/docker/ecs-plugin/pkg/convert"
+	"github.com/sirupsen/logrus"
 )
 
 func (c client) Convert(ctx context.Context, project *compose.Project) (*cloudformation.Template, error) {
@@ -50,22 +52,32 @@ func (c client) Convert(ctx context.Context, project *compose.Project) (*cloudfo
 		VpcId:                vpc,
 	}
 
+	logGroup := fmt.Sprintf("/docker-compose/%s", project.Name)
+	template.Resources["LogGroup"] = &logs.LogGroup{
+		LogGroupName: logGroup,
+	}
+
 	for _, service := range project.Services {
 		definition, err := convert.Convert(project, service)
 		if err != nil {
 			return nil, err
 		}
 
-		role, err := c.GetEcsTaskExecutionRole(ctx, service)
-		if err != nil {
-			return nil, err
+		taskExecutionRole := fmt.Sprintf("%sTaskExecutionRole", service.Name)
+		template.Resources[taskExecutionRole] = &iam.Role{
+			AssumeRolePolicyDocument: assumeRolePolicyDocument,
+			// Here we can grant access to secrets/configs using a Policy { Allow,ssm:GetParameters,secret|config ARN}
+			ManagedPolicyArns: []string{
+				ECSTaskExecutionPolicy,
+			},
 		}
-		definition.TaskRoleArn = role
+		definition.ExecutionRoleArn = cloudformation.Ref(taskExecutionRole)
+		// FIXME definition.TaskRoleArn = ?
 
 		taskDefinition := fmt.Sprintf("%sTaskDefinition", service.Name)
 		template.Resources[taskDefinition] = definition
 
-		template.Resources[service.Name] = &ecs.Service{
+		template.Resources[fmt.Sprintf("%sService", service.Name)] = &ecs.Service{
 			Cluster:      c.Cluster,
 			DesiredCount: 1,
 			LaunchType:   ecsapi.LaunchTypeFargate,

+ 31 - 0
ecs/pkg/amazon/iam.go

@@ -0,0 +1,31 @@
+package amazon
+
+var assumeRolePolicyDocument = PolicyDocument{
+	Version: "2012-10-17", // https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html
+	Statement: []PolicyStatement{
+		{
+			Effect: "Allow",
+			Principal: PolicyPrincipal{
+				Service: "ecs-tasks.amazonaws.com",
+			},
+			Action: []string{"sts:AssumeRole"},
+		},
+	},
+}
+
+// could alternatively depend on https://github.com/kubernetes-sigs/cluster-api-provider-aws/blob/master/pkg/cloud/services/iam/types.go#L52
+type PolicyDocument struct {
+	Version   string            `json:",omitempty"`
+	Statement []PolicyStatement `json:",omitempty"`
+}
+
+type PolicyStatement struct {
+	Effect    string          `json:",omitempty"`
+	Action    []string        `json:",omitempty"`
+	Principal PolicyPrincipal `json:",omitempty"`
+	Resource  []string        `json:",omitempty"`
+}
+
+type PolicyPrincipal struct {
+	Service string `json:",omitempty"`
+}

+ 3 - 0
ecs/pkg/amazon/sdk.go

@@ -180,6 +180,9 @@ func (s sdk) CreateStack(ctx context.Context, name string, template *cf.Template
 		StackName:        aws.String(name),
 		TemplateBody:     aws.String(string(json)),
 		TimeoutInMinutes: aws.Int64(10),
+		Capabilities: []*string{
+			aws.String(cloudformation.CapabilityCapabilityIam),
+		},
 	})
 	return err
 }

+ 27 - 18
ecs/pkg/convert/convert.go

@@ -5,6 +5,7 @@ import (
 	"time"
 
 	ecsapi "github.com/aws/aws-sdk-go/service/ecs"
+	"github.com/awslabs/goformation/v4/cloudformation"
 	"github.com/awslabs/goformation/v4/cloudformation/ecs"
 	"github.com/compose-spec/compose-go/types"
 	"github.com/docker/cli/opts"
@@ -21,24 +22,32 @@ func Convert(project *compose.Project, service types.ServiceConfig) (*ecs.TaskDe
 		ContainerDefinitions: []ecs.TaskDefinition_ContainerDefinition{
 			// Here we can declare sidecars and init-containers using https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definition_dependson
 			{
-				Command:                service.Command,
-				Cpu:                    256,
-				DisableNetworking:      service.NetworkMode == "none",
-				DnsSearchDomains:       service.DNSSearch,
-				DnsServers:             service.DNS,
-				DockerLabels:           nil,
-				DockerSecurityOptions:  service.SecurityOpt,
-				EntryPoint:             service.Entrypoint,
-				Environment:            toKeyValuePair(service.Environment),
-				Essential:              true,
-				ExtraHosts:             toHostEntryPtr(service.ExtraHosts),
-				FirelensConfiguration:  nil,
-				HealthCheck:            toHealthCheck(service.HealthCheck),
-				Hostname:               service.Hostname,
-				Image:                  service.Image,
-				Interactive:            false,
-				Links:                  nil,
-				LinuxParameters:        toLinuxParameters(service),
+				Command:               service.Command,
+				Cpu:                   256,
+				DisableNetworking:     service.NetworkMode == "none",
+				DnsSearchDomains:      service.DNSSearch,
+				DnsServers:            service.DNS,
+				DockerLabels:          nil,
+				DockerSecurityOptions: service.SecurityOpt,
+				EntryPoint:            service.Entrypoint,
+				Environment:           toKeyValuePair(service.Environment),
+				Essential:             true,
+				ExtraHosts:            toHostEntryPtr(service.ExtraHosts),
+				FirelensConfiguration: nil,
+				HealthCheck:           toHealthCheck(service.HealthCheck),
+				Hostname:              service.Hostname,
+				Image:                 service.Image,
+				Interactive:           false,
+				Links:                 nil,
+				LinuxParameters:       toLinuxParameters(service),
+				LogConfiguration: &ecs.TaskDefinition_LogConfiguration{
+					LogDriver: ecsapi.LogDriverAwslogs,
+					Options: map[string]string{
+						"awslogs-region":        cloudformation.Ref("AWS::Region"),
+						"awslogs-group":         cloudformation.Ref("LogGroup"),
+						"awslogs-stream-prefix": service.Name,
+					},
+				},
 				Memory:                 toMemoryLimits(service.Deploy),
 				MemoryReservation:      toMemoryReservation(service.Deploy),
 				MountPoints:            nil,