diff --git a/internal/pkg/deploy/cloudformation/stack/env.go b/internal/pkg/deploy/cloudformation/stack/env.go index 5591fd507d9..14074577316 100644 --- a/internal/pkg/deploy/cloudformation/stack/env.go +++ b/internal/pkg/deploy/cloudformation/stack/env.go @@ -105,7 +105,7 @@ func (e *EnvStackConfig) Template() (string, error) { PrivateImportedCertARNs: e.importPrivateCertARNs(), VPCConfig: e.vpcConfig(), CustomInternalALBSubnets: e.internalALBSubnets(), - AllowVPCIngress: e.in.AllowVPCIngress, // TODO(jwh): fetch AllowVPCIngress from Manifest or SSM. + AllowVPCIngress: aws.BoolValue(e.in.Mft.HTTPConfig.Private.SecurityGroupsConfig.Ingress.VPCIngress), Telemetry: e.telemetryConfig(), CDNConfig: e.cdnConfig(), diff --git a/internal/pkg/deploy/cloudformation/stack/env_integration_test.go b/internal/pkg/deploy/cloudformation/stack/env_integration_test.go index 3e6316dadfc..3ad3ada4c85 100644 --- a/internal/pkg/deploy/cloudformation/stack/env_integration_test.go +++ b/internal/pkg/deploy/cloudformation/stack/env_integration_test.go @@ -35,6 +35,10 @@ http: certificates: - cert-1 - cert-2 + private: + security_groups: + ingress: + from_vpc: true observability: container_insights: true # Enable container insights.` var mft manifest.Environment @@ -54,9 +58,8 @@ observability: "DNSDelegationFunction": "https://mockbucket.s3-us-west-2.amazonaws.com/dns-delegation", "CustomDomainFunction": "https://mockbucket.s3-us-west-2.amazonaws.com/custom-domain", }, - AllowVPCIngress: true, - Mft: &mft, - RawMft: []byte(rawMft), + Mft: &mft, + RawMft: []byte(rawMft), } }(), wantedFileName: "template-with-imported-certs-observability.yml", @@ -82,9 +85,8 @@ type: Environment` "DNSDelegationFunction": "https://mockbucket.s3-us-west-2.amazonaws.com/dns-delegation", "CustomDomainFunction": "https://mockbucket.s3-us-west-2.amazonaws.com/custom-domain", }, - AllowVPCIngress: true, - Mft: &mft, - RawMft: []byte(rawMft), + Mft: &mft, + RawMft: []byte(rawMft), } }(), wantedFileName: "template-with-basic-manifest.yml", diff --git a/internal/pkg/deploy/cloudformation/stack/env_test.go b/internal/pkg/deploy/cloudformation/stack/env_test.go index 41842a04328..195e33a5924 100644 --- a/internal/pkg/deploy/cloudformation/stack/env_test.go +++ b/internal/pkg/deploy/cloudformation/stack/env_test.go @@ -14,6 +14,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/config" "github.com/aws/copilot-cli/internal/pkg/deploy" "github.com/aws/copilot-cli/internal/pkg/deploy/cloudformation/stack/mocks" + "github.com/aws/copilot-cli/internal/pkg/manifest" "github.com/aws/copilot-cli/internal/pkg/template" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -33,7 +34,7 @@ func TestEnv_Template(t *testing.T) { AppName: "project", EnvName: "env", VPCConfig: template.VPCConfig{ - Imported: &template.ImportVPC{}, + Imported: nil, Managed: template.ManagedVPC{ CIDR: DefaultVPCCIDR, PrivateSubnetCIDRs: DefaultPrivateSubnetCIDRs, @@ -55,7 +56,11 @@ func TestEnv_Template(t *testing.T) { Key: "mockkey4", }, }, - ForceUpdateID: "mockPreviousForceUpdateID", + Telemetry: &template.Telemetry{ + EnableContainerInsights: false, + }, + SerializedManifest: "name: env\ntype: Environment\n", + ForceUpdateID: "mockPreviousForceUpdateID", }, data) return &template.Content{Buffer: bytes.NewBufferString("mockTemplate")}, nil }) @@ -95,7 +100,7 @@ func TestEnv_Parameters(t *testing.T) { deploymentInputWithDNS := mockDeployEnvironmentInput() deploymentInputWithDNS.App.Domain = "ecs.aws" deploymentInputWithPrivateDNS := mockDeployEnvironmentInput() - deploymentInputWithPrivateDNS.ImportCertARNs = []string{"arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"} + deploymentInputWithPrivateDNS.Mft.HTTPConfig.Private.Certificates = []string{"arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"} testCases := map[string]struct { input *deploy.CreateEnvironmentInput oldParams []*cloudformation.Parameter @@ -671,6 +676,14 @@ func mockDeployEnvironmentInput() *deploy.CreateEnvironmentInput { "DNSDelegationFunction": "https://mockbucket.s3-us-west-2.amazonaws.com/mockkey2", "CustomDomainFunction": "https://mockbucket.s3-us-west-2.amazonaws.com/mockkey4", }, - ImportVPCConfig: &config.ImportVPC{}, + Mft: &manifest.Environment{ + Workload: manifest.Workload{ + Name: aws.String("env"), + Type: aws.String("Environment"), + }, + }, + RawMft: []byte(`name: env +type: Environment +`), } } diff --git a/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-basic-manifest.yml b/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-basic-manifest.yml index c964a6a438c..ae3816949d4 100644 --- a/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-basic-manifest.yml +++ b/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-basic-manifest.yml @@ -645,30 +645,6 @@ Resources: GroupId: !Ref InternalLoadBalancerSecurityGroup IpProtocol: -1 SourceSecurityGroupId: !Ref EnvironmentSecurityGroup - InternalLoadBalancerSecurityGroupIngressFromHttp: - Metadata: - 'aws:copilot:description': 'An inbound rule to the internal load balancer security group for port 80 within the VPC' - Type: AWS::EC2::SecurityGroupIngress - Condition: CreateInternalALB - Properties: - Description: Allow from within the VPC on port 80 - CidrIp: 0.0.0.0/0 - FromPort: 80 - ToPort: 80 - IpProtocol: tcp - GroupId: !Ref InternalLoadBalancerSecurityGroup - InternalLoadBalancerSecurityGroupIngressFromHttps: - Metadata: - 'aws:copilot:description': 'An inbound rule to the internal load balancer security group for port 443 within the VPC' - Type: AWS::EC2::SecurityGroupIngress - Condition: ExportInternalHTTPSListener - Properties: - Description: Allow from within the VPC on port 443 - CidrIp: 0.0.0.0/0 - FromPort: 443 - ToPort: 443 - IpProtocol: tcp - GroupId: !Ref InternalLoadBalancerSecurityGroup PublicLoadBalancer: Metadata: 'aws:copilot:description': 'An Application Load Balancer to distribute public traffic to your services' diff --git a/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-imported-certs-observability.yml b/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-imported-certs-observability.yml index f97b44c2221..f1bc0d0c5b3 100644 --- a/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-imported-certs-observability.yml +++ b/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-imported-certs-observability.yml @@ -11,6 +11,10 @@ Metadata: certificates: - cert-1 - cert-2 + private: + security_groups: + ingress: + from_vpc: true observability: container_insights: true # Enable container insights. Parameters: diff --git a/internal/pkg/manifest/env.go b/internal/pkg/manifest/env.go index 54e8239d946..5f117bca19f 100644 --- a/internal/pkg/manifest/env.go +++ b/internal/pkg/manifest/env.go @@ -318,9 +318,11 @@ func (cfg *environmentHTTPConfig) loadLBConfig(env *config.CustomizeEnv) { if env.IsEmpty() { return } + if env.ImportVPC != nil && len(env.ImportVPC.PublicSubnetIDs) == 0 { cfg.Private.InternalALBSubnets = env.InternalALBSubnets cfg.Private.Certificates = env.ImportCertARNs + cfg.Private.SecurityGroupsConfig.Ingress.VPCIngress = aws.Bool(env.EnableInternalALBVPCIngress) return } cfg.Public.Certificates = env.ImportCertARNs @@ -336,11 +338,28 @@ func (cfg publicHTTPConfig) IsEmpty() bool { } type privateHTTPConfig struct { - InternalALBSubnets []string `yaml:"subnets,omitempty"` - Certificates []string `yaml:"certificates,omitempty"` + InternalALBSubnets []string `yaml:"subnets,omitempty"` + Certificates []string `yaml:"certificates,omitempty"` + SecurityGroupsConfig securityGroupsConfig `yaml:"security_groups,omitempty"` } // IsEmpty returns true if there is no customization to the internal ALB. func (cfg privateHTTPConfig) IsEmpty() bool { - return len(cfg.InternalALBSubnets) == 0 && len(cfg.Certificates) == 0 + return len(cfg.InternalALBSubnets) == 0 && len(cfg.Certificates) == 0 && cfg.SecurityGroupsConfig.isEmpty() +} + +type securityGroupsConfig struct { + Ingress ingress `yaml:"ingress"` +} + +func (cfg securityGroupsConfig) isEmpty() bool { + return cfg.Ingress.isEmpty() +} + +type ingress struct { + VPCIngress *bool `yaml:"from_vpc"` +} + +func (i ingress) isEmpty() bool { + return i.VPCIngress == nil } diff --git a/internal/pkg/manifest/env_test.go b/internal/pkg/manifest/env_test.go index 74d39b30b35..80955fecea6 100644 --- a/internal/pkg/manifest/env_test.go +++ b/internal/pkg/manifest/env_test.go @@ -278,6 +278,11 @@ func TestFromEnvConfig(t *testing.T) { Private: privateHTTPConfig{ InternalALBSubnets: []string{"subnet2"}, Certificates: []string{"arn:aws:acm:region:account:certificate/certificate_ID_1", "arn:aws:acm:region:account:certificate/certificate_ID_2"}, + SecurityGroupsConfig: securityGroupsConfig{ + Ingress: ingress{ + VPCIngress: aws.Bool(false), + }, + }, }, }, }, @@ -427,6 +432,10 @@ http: certificates: - cert-1 - cert-2 + private: + security_groups: + ingress: + from_vpc: false `, wantedStruct: &Environment{ Workload: Workload{ @@ -438,6 +447,13 @@ http: Public: publicHTTPConfig{ Certificates: []string{"cert-1", "cert-2"}, }, + Private: privateHTTPConfig{ + SecurityGroupsConfig: securityGroupsConfig{ + Ingress: ingress{ + VPCIngress: aws.Bool(false), + }, + }, + }, }, }, },