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 <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2020-05-04 15:09:08 +02:00
parent 99b4ed0bfd
commit 3e30f2cd1a
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
4 changed files with 80 additions and 25 deletions

View File

@ -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
ecs/pkg/amazon/iam.go Normal file
View File

@ -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"`
}

View File

@ -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
}

View File

@ -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,