From 115d484024a669999540917d530fa35d46614191 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Thu, 19 Nov 2020 11:57:16 +0100 Subject: [PATCH 1/2] introduce x-aws-certification to support SSL termination by ALB Signed-off-by: Nicolas De Loof --- ecs/awsResources.go | 4 ++++ ecs/cloudformation.go | 15 +++++++++++---- ecs/cloudformation_test.go | 20 ++++++++++++++++++++ ecs/x.go | 1 + 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/ecs/awsResources.go b/ecs/awsResources.go index 012b31822..b8d1e6b0e 100644 --- a/ecs/awsResources.go +++ b/ecs/awsResources.go @@ -451,6 +451,10 @@ func portIsHTTP(it types.ServicePortConfig) bool { protocol := v.(string) return protocol == "http" || protocol == "https" } + if _, ok := it.Extensions[extensionCertificate]; ok { + // setting certificate implies protocol = https + return true + } return it.Target == 80 || it.Target == 443 } diff --git a/ecs/cloudformation.go b/ecs/cloudformation.go index 0c52fba2d..ca0545f0d 100644 --- a/ecs/cloudformation.go +++ b/ecs/cloudformation.go @@ -133,7 +133,7 @@ func (b *ecsAPIService) createService(project *types.Project, service types.Serv protocol = elbv2.ProtocolEnumHttp } targetGroupName := b.createTargetGroup(project, service, port, template, protocol, resources.vpc) - listenerName := b.createListener(service, port, template, targetGroupName, resources.loadBalancer, protocol) + listenerName := b.createListener(project, service, port, template, targetGroupName, resources.loadBalancer, protocol) dependsOn = append(dependsOn, listenerName) serviceLB = append(serviceLB, ecs.Service_LoadBalancer{ ContainerName: service.Name, @@ -290,15 +290,21 @@ func computeRollingUpdateLimits(service types.ServiceConfig) (int, int, error) { return minPercent, maxPercent, nil } -func (b *ecsAPIService) createListener(service types.ServiceConfig, port types.ServicePortConfig, - template *cloudformation.Template, - targetGroupName string, loadBalancer awsResource, protocol string) string { +func (b *ecsAPIService) createListener(project *types.Project, service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, targetGroupName string, loadBalancer awsResource, protocol string) string { listenerName := fmt.Sprintf( "%s%s%dListener", normalizeResourceName(service.Name), strings.ToUpper(port.Protocol), port.Target, ) + var certificates []elasticloadbalancingv2.Listener_Certificate + if secret, ok := port.Extensions[extensionCertificate]; ok { + arn := project.Secrets[secret.(string)].Name + certificates = append(certificates, elasticloadbalancingv2.Listener_Certificate{ + CertificateArn: arn, + }) + } + //add listener to dependsOn //https://stackoverflow.com/questions/53971873/the-target-group-does-not-have-an-associated-load-balancer template.Resources[listenerName] = &elasticloadbalancingv2.Listener{ @@ -317,6 +323,7 @@ func (b *ecsAPIService) createListener(service types.ServiceConfig, port types.S LoadBalancerArn: loadBalancer.ARN(), Protocol: protocol, Port: int(port.Target), + Certificates: certificates, } return listenerName } diff --git a/ecs/cloudformation_test.go b/ecs/cloudformation_test.go index 275721c37..33262e5da 100644 --- a/ecs/cloudformation_test.go +++ b/ecs/cloudformation_test.go @@ -351,6 +351,26 @@ services: assert.Check(t, loadBalancer.Type == elbv2.LoadBalancerTypeEnumNetwork) } +func TestServiceCertificate(t *testing.T) { + template := convertYaml(t, ` +services: + test: + image: nginx + ports: + - target: 443 + x-aws-certificate: certificate +secrets: + certificate: + external: true + name: "arn:123:abc" +`, useDefaultVPC) + l := template.Resources["Test443Listener"] + assert.Check(t, l != nil) + listener := *l.(*elasticloadbalancingv2.Listener) + assert.Equal(t, len(listener.Certificates), 1) + assert.Equal(t, listener.Certificates[0].CertificateArn, "arn:123:abc") +} + func TestUseExternalNetwork(t *testing.T) { template := convertYaml(t, ` services: diff --git a/ecs/x.go b/ecs/x.go index 6968ba997..480268888 100644 --- a/ecs/x.go +++ b/ecs/x.go @@ -30,4 +30,5 @@ const ( extensionRole = "x-aws-role" extensionManagedPolicies = "x-aws-policies" extensionAutoScaling = "x-aws-autoscaling" + extensionCertificate = "x-aws-certificate" ) From e1fe19942bb6ebd038e264dbadc4df491b034b78 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Mon, 23 Nov 2020 11:38:30 +0100 Subject: [PATCH 2/2] Introduce x-aws-certificate to configure ssl port Signed-off-by: Nicolas De Loof --- ecs/cloudformation.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ecs/cloudformation.go b/ecs/cloudformation.go index ca0545f0d..af3a49865 100644 --- a/ecs/cloudformation.go +++ b/ecs/cloudformation.go @@ -128,9 +128,14 @@ func (b *ecsAPIService) createService(project *types.Project, service types.Serv } protocol := strings.ToUpper(port.Protocol) + if p, ok := port.Extensions[extensionProtocol]; ok { + protocol = strings.ToUpper(p.(string)) + } if resources.loadBalancerType == elbv2.LoadBalancerTypeEnumApplication { - // we don't set Https as a certificate must be specified for HTTPS listeners protocol = elbv2.ProtocolEnumHttp + if _, ok := port.Extensions[extensionCertificate]; ok { + protocol = elbv2.ProtocolEnumHttps + } } targetGroupName := b.createTargetGroup(project, service, port, template, protocol, resources.vpc) listenerName := b.createListener(project, service, port, template, targetGroupName, resources.loadBalancer, protocol)