From da7339675ebeaf77b9d9d5b22d34820328dec887 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Sun, 29 Sep 2019 16:15:36 -0400 Subject: [PATCH] tests/service/apigateway: Use internal implementation for TLS key/certificate Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/10023 Switches `aws_api_gateway_base_path_mapping` testing to the simplest `aws_api_gateway_domain_name` configuration with `regional_certificate_arn`. Requires new environment variables for `certificate_name` testing as CloudFront now strictly requires a publicly trusted key/certificate: ``` --- FAIL: TestAccAWSAPIGatewayDomainName_CertificateName (5.58s) testing.go:569: Step 0 error: errors during apply: Error: Error creating API Gateway Domain Name: BadRequestException: The certificate that is attached to your distribution was not issued by a trusted Certificate Authority. For more details, see: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html#alternate-domain-names-requirements (Service: AmazonCloudFront; Status Code: 400; Error Code: InvalidViewerCertificate; Request ID: e052f947-e261-11e9-a70c-b3beead7f798) ``` Output from acceptance testing: ``` --- PASS: TestAccAWSAPIGatewayBasePathMapping_basic (103.50s) --- PASS: TestAccAWSAPIGatewayBasePathMapping_BasePath_Empty (61.24s) --- SKIP: TestAccAWSAPIGatewayDomainName_CertificateArn (0.00s) --- SKIP: TestAccAWSAPIGatewayDomainName_CertificateName (0.00s) --- SKIP: TestAccAWSAPIGatewayDomainName_RegionalCertificateName (0.00s) --- PASS: TestAccAWSAPIGatewayDomainName_RegionalCertificateArn (20.45s) --- PASS: TestAccAWSAPIGatewayDomainName_SecurityPolicy (104.68s) ``` --- ..._aws_api_gateway_base_path_mapping_test.go | 100 +++----- ...source_aws_api_gateway_domain_name_test.go | 241 +++++++----------- aws/tls.go | 117 +++++++++ aws/tls_test.go | 24 +- 4 files changed, 262 insertions(+), 220 deletions(-) diff --git a/aws/resource_aws_api_gateway_base_path_mapping_test.go b/aws/resource_aws_api_gateway_base_path_mapping_test.go index 524cb18cab3..12951cc0d9f 100644 --- a/aws/resource_aws_api_gateway_base_path_mapping_test.go +++ b/aws/resource_aws_api_gateway_base_path_mapping_test.go @@ -67,16 +67,18 @@ func TestDecodeApiGatewayBasePathMappingId(t *testing.T) { func TestAccAWSAPIGatewayBasePathMapping_basic(t *testing.T) { var conf apigateway.BasePathMapping - // Our test cert is for a wildcard on this domain name := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + key := tlsRsaPrivateKeyPem(2048) + certificate := tlsRsaX509SelfSignedCertificatePem(key, name) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProvidersWithTLS, + Providers: testAccProviders, CheckDestroy: testAccCheckAWSAPIGatewayBasePathDestroy(name), Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayBasePathConfig(name), + Config: testAccAWSAPIGatewayBasePathConfigBasePath(name, key, certificate, "tf-acc-test"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayBasePathExists("aws_api_gateway_base_path_mapping.test", name, &conf), ), @@ -94,16 +96,18 @@ func TestAccAWSAPIGatewayBasePathMapping_basic(t *testing.T) { func TestAccAWSAPIGatewayBasePathMapping_BasePath_Empty(t *testing.T) { var conf apigateway.BasePathMapping - // Our test cert is for a wildcard on this domain name := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + key := tlsRsaPrivateKeyPem(2048) + certificate := tlsRsaX509SelfSignedCertificatePem(key, name) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProvidersWithTLS, + Providers: testAccProviders, CheckDestroy: testAccCheckAWSAPIGatewayBasePathDestroy(name), Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayEmptyBasePathConfig(name), + Config: testAccAWSAPIGatewayBasePathConfigBasePath(name, key, certificate, ""), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayBasePathExists("aws_api_gateway_base_path_mapping.test", name, &conf), ), @@ -184,11 +188,29 @@ func testAccCheckAWSAPIGatewayBasePathDestroy(name string) resource.TestCheckFun } } -func testAccAWSAPIGatewayBasePathConfig(domainName string) string { +func testAccAWSAPIGatewayBasePathConfigBase(domainName, key, certificate string) string { return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + certificate_body = "%[2]s" + private_key = "%[3]s" +} + +resource "aws_api_gateway_domain_name" "test" { + domain_name = %[1]q + regional_certificate_arn = "${aws_acm_certificate.test.arn}" + + endpoint_configuration { + types = ["REGIONAL"] + } +} + resource "aws_api_gateway_rest_api" "test" { name = "tf-acc-apigateway-base-path-mapping" description = "Terraform Acceptance Tests" + + endpoint_configuration { + types = ["REGIONAL"] + } } # API gateway won't let us deploy an empty API @@ -217,70 +239,16 @@ resource "aws_api_gateway_deployment" "test" { stage_name = "test" depends_on = ["aws_api_gateway_integration.test"] } - -resource "aws_api_gateway_base_path_mapping" "test" { - api_id = "${aws_api_gateway_rest_api.test.id}" - base_path = "tf-acc" - stage_name = "${aws_api_gateway_deployment.test.stage_name}" - domain_name = "${aws_api_gateway_domain_name.test.domain_name}" -} - -resource "aws_api_gateway_domain_name" "test" { - domain_name = "%s" - certificate_name = "tf-apigateway-base-path-mapping-test" - certificate_body = "${tls_locally_signed_cert.leaf.cert_pem}" - certificate_chain = "${tls_self_signed_cert.ca.cert_pem}" - certificate_private_key = "${tls_private_key.test.private_key_pem}" -} - -%s - -`, domainName, testAccAWSAPIGatewayCerts(domainName)) -} - -func testAccAWSAPIGatewayEmptyBasePathConfig(domainName string) string { - return fmt.Sprintf(` -resource "aws_api_gateway_rest_api" "test" { - name = "tf-acc-apigateway-base-path-mapping" - description = "Terraform Acceptance Tests" -} - -resource "aws_api_gateway_method" "test" { - rest_api_id = "${aws_api_gateway_rest_api.test.id}" - resource_id = "${aws_api_gateway_rest_api.test.root_resource_id}" - http_method = "GET" - authorization = "NONE" -} - -resource "aws_api_gateway_integration" "test" { - rest_api_id = "${aws_api_gateway_rest_api.test.id}" - resource_id = "${aws_api_gateway_rest_api.test.root_resource_id}" - http_method = "${aws_api_gateway_method.test.http_method}" - type = "MOCK" -} - -resource "aws_api_gateway_deployment" "test" { - rest_api_id = "${aws_api_gateway_rest_api.test.id}" - stage_name = "test" - depends_on = ["aws_api_gateway_integration.test"] +`, domainName, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key)) } +func testAccAWSAPIGatewayBasePathConfigBasePath(domainName, key, certificate, basePath string) string { + return testAccAWSAPIGatewayBasePathConfigBase(domainName, key, certificate) + fmt.Sprintf(` resource "aws_api_gateway_base_path_mapping" "test" { api_id = "${aws_api_gateway_rest_api.test.id}" - base_path = "" + base_path = %[1]q stage_name = "${aws_api_gateway_deployment.test.stage_name}" domain_name = "${aws_api_gateway_domain_name.test.domain_name}" } - -resource "aws_api_gateway_domain_name" "test" { - domain_name = "%s" - certificate_name = "tf-apigateway-base-path-mapping-test" - certificate_body = "${tls_locally_signed_cert.leaf.cert_pem}" - certificate_chain = "${tls_self_signed_cert.ca.cert_pem}" - certificate_private_key = "${tls_private_key.test.private_key_pem}" -} - -%s - -`, domainName, testAccAWSAPIGatewayCerts(domainName)) +`, basePath) } diff --git a/aws/resource_aws_api_gateway_domain_name_test.go b/aws/resource_aws_api_gateway_domain_name_test.go index 8666c741193..500ea2ee4c7 100644 --- a/aws/resource_aws_api_gateway_domain_name_test.go +++ b/aws/resource_aws_api_gateway_domain_name_test.go @@ -14,13 +14,6 @@ import ( ) func TestAccAWSAPIGatewayDomainName_CertificateArn(t *testing.T) { - // This test must always run in us-east-1 - // BadRequestException: Invalid certificate ARN: arn:aws:acm:us-west-2:123456789012:certificate/xxxxx. Certificate must be in 'us-east-1'. - oldvar := os.Getenv("AWS_DEFAULT_REGION") - os.Setenv("AWS_DEFAULT_REGION", "us-east-1") - defer os.Setenv("AWS_DEFAULT_REGION", oldvar) - - // For now, use an environment variable to limit running this test certificateArn := os.Getenv("AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_ARN") if certificateArn == "" { t.Skip( @@ -29,13 +22,19 @@ func TestAccAWSAPIGatewayDomainName_CertificateArn(t *testing.T) { "an ISSUED ACM certificate in us-east-1 to enable this test.") } + // This test must always run in us-east-1 + // BadRequestException: Invalid certificate ARN: arn:aws:acm:us-west-2:123456789012:certificate/xxxxx. Certificate must be in 'us-east-1'. + oldvar := os.Getenv("AWS_DEFAULT_REGION") + os.Setenv("AWS_DEFAULT_REGION", "us-east-1") + defer os.Setenv("AWS_DEFAULT_REGION", oldvar) + var domainName apigateway.DomainName resourceName := "aws_api_gateway_domain_name.test" rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProvidersWithTLS, + Providers: testAccProviders, CheckDestroy: testAccCheckAWSAPIGatewayDomainNameDestroy, Steps: []resource.TestStep{ { @@ -52,45 +51,53 @@ func TestAccAWSAPIGatewayDomainName_CertificateArn(t *testing.T) { } func TestAccAWSAPIGatewayDomainName_CertificateName(t *testing.T) { - var conf apigateway.DomainName + certificateBody := os.Getenv("AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_BODY") + if certificateBody == "" { + t.Skip( + "Environment variable AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_BODY is not set. " + + "This environment variable must be set to any non-empty value " + + "with a publicly trusted certificate body to enable the test.") + } + + certificateChain := os.Getenv("AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_CHAIN") + if certificateChain == "" { + t.Skip( + "Environment variable AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_CHAIN is not set. " + + "This environment variable must be set to any non-empty value " + + "with a chain certificate acceptable for the certificate to enable the test.") + } + + certificatePrivateKey := os.Getenv("AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_PRIVATE_KEY") + if certificatePrivateKey == "" { + t.Skip( + "Environment variable AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_PRIVATE_KEY is not set. " + + "This environment variable must be set to any non-empty value " + + "with a private key of a publicly trusted certificate to enable the test.") + } - rString := acctest.RandString(8) - name := fmt.Sprintf("tf-acc-%s.terraformtest.com", rString) - nameModified := fmt.Sprintf("tf-acc-%s-modified.terraformtest.com", rString) - commonName := "*.terraformtest.com" - certRe := regexp.MustCompile("^-----BEGIN CERTIFICATE-----\n") - keyRe := regexp.MustCompile("^-----BEGIN RSA PRIVATE KEY-----\n") + domainName := os.Getenv("AWS_API_GATEWAY_DOMAIN_NAME_DOMAIN_NAME") + if domainName == "" { + t.Skip( + "Environment variable AWS_API_GATEWAY_DOMAIN_NAME_DOMAIN_NAME is not set. " + + "This environment variable must be set to any non-empty value " + + "with a domain name acceptable for the certificate to enable the test.") + } + + var conf apigateway.DomainName resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProvidersWithTLS, + Providers: testAccProviders, CheckDestroy: testAccCheckAWSAPIGatewayDomainNameDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayDomainNameConfig_CertificateName(name, commonName), + Config: testAccAWSAPIGatewayDomainNameConfig_CertificateName(domainName, certificatePrivateKey, certificateBody, certificateChain), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayDomainNameExists("aws_api_gateway_domain_name.test", &conf), - resource.TestMatchResourceAttr("aws_api_gateway_domain_name.test", "certificate_body", certRe), - resource.TestMatchResourceAttr("aws_api_gateway_domain_name.test", "certificate_chain", certRe), resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "certificate_name", "tf-acc-apigateway-domain-name"), - resource.TestMatchResourceAttr("aws_api_gateway_domain_name.test", "certificate_private_key", keyRe), resource.TestCheckResourceAttrSet("aws_api_gateway_domain_name.test", "cloudfront_domain_name"), resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "cloudfront_zone_id", "Z2FDTNDATAQYW2"), - resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "domain_name", name), - resource.TestCheckResourceAttrSet("aws_api_gateway_domain_name.test", "certificate_upload_date"), - ), - }, - { - Config: testAccAWSAPIGatewayDomainNameConfig_CertificateName(nameModified, commonName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayDomainNameExists("aws_api_gateway_domain_name.test", &conf), - resource.TestMatchResourceAttr("aws_api_gateway_domain_name.test", "certificate_body", certRe), - resource.TestMatchResourceAttr("aws_api_gateway_domain_name.test", "certificate_chain", certRe), - resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "certificate_name", "tf-acc-apigateway-domain-name"), - resource.TestMatchResourceAttr("aws_api_gateway_domain_name.test", "certificate_private_key", keyRe), - resource.TestCheckResourceAttrSet("aws_api_gateway_domain_name.test", "cloudfront_domain_name"), - resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "cloudfront_zone_id", "Z2FDTNDATAQYW2"), - resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "domain_name", nameModified), + resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "domain_name", domainName), resource.TestCheckResourceAttrSet("aws_api_gateway_domain_name.test", "certificate_upload_date"), ), }, @@ -109,13 +116,16 @@ func TestAccAWSAPIGatewayDomainName_RegionalCertificateArn(t *testing.T) { resourceName := "aws_api_gateway_domain_name.test" rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + key := tlsRsaPrivateKeyPem(2048) + certificate := tlsRsaX509SelfSignedCertificatePem(key, rName) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProvidersWithTLS, + Providers: testAccProviders, CheckDestroy: testAccCheckAWSAPIGatewayDomainNameDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateArn(rName), + Config: testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateArn(rName, key, certificate), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), resource.TestCheckResourceAttr(resourceName, "domain_name", rName), @@ -145,23 +155,25 @@ func TestAccAWSAPIGatewayDomainName_RegionalCertificateName(t *testing.T) { resourceName := "aws_api_gateway_domain_name.test" rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) - commonName := "*.terraformtest.com" - certRe := regexp.MustCompile("^-----BEGIN CERTIFICATE-----\n") - keyRe := regexp.MustCompile("^-----BEGIN RSA PRIVATE KEY-----\n") + + caKey := tlsRsaPrivateKeyPem(2048) + caCertificate := tlsRsaX509SelfSignedCaCertificatePem(caKey) + key := tlsRsaPrivateKeyPem(2048) + certificate := tlsRsaX509LocallySignedCertificatePem(caKey, caCertificate, key, "*.terraformtest.com") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProvidersWithTLS, + Providers: testAccProviders, CheckDestroy: testAccCheckAWSAPIGatewayDomainNameDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateName(rName, commonName), + Config: testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateName(rName, key, certificate, caCertificate), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), - resource.TestMatchResourceAttr(resourceName, "certificate_body", certRe), - resource.TestMatchResourceAttr(resourceName, "certificate_chain", certRe), + resource.TestCheckResourceAttr(resourceName, "certificate_body", certificate), + resource.TestCheckResourceAttr(resourceName, "certificate_chain", caCertificate), resource.TestCheckResourceAttr(resourceName, "certificate_name", "tf-acc-apigateway-domain-name"), - resource.TestMatchResourceAttr(resourceName, "certificate_private_key", keyRe), + resource.TestCheckResourceAttr(resourceName, "certificate_private_key", key), resource.TestCheckResourceAttrSet(resourceName, "certificate_upload_date"), resource.TestCheckResourceAttr(resourceName, "domain_name", rName), resource.TestMatchResourceAttr(resourceName, "regional_domain_name", regexp.MustCompile(`.*\.execute-api\..*`)), @@ -177,13 +189,16 @@ func TestAccAWSAPIGatewayDomainName_SecurityPolicy(t *testing.T) { resourceName := "aws_api_gateway_domain_name.test" rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + key := tlsRsaPrivateKeyPem(2048) + certificate := tlsRsaX509SelfSignedCertificatePem(key, rName) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProvidersWithTLS, + Providers: testAccProviders, CheckDestroy: testAccCheckAWSAPIGatewayDomainNameDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayDomainNameConfig_SecurityPolicy(rName, apigateway.SecurityPolicyTls12), + Config: testAccAWSAPIGatewayDomainNameConfig_SecurityPolicy(rName, key, certificate, apigateway.SecurityPolicyTls12), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), resource.TestCheckResourceAttr(resourceName, "security_policy", apigateway.SecurityPolicyTls12), @@ -254,88 +269,6 @@ func testAccCheckAWSAPIGatewayDomainNameDestroy(s *terraform.State) error { return nil } -func testAccAWSAPIGatewayCerts(commonName string) string { - return fmt.Sprintf(` -resource "tls_private_key" "test" { - algorithm = "RSA" -} - -resource "tls_self_signed_cert" "ca" { - key_algorithm = "RSA" - private_key_pem = "${tls_private_key.test.private_key_pem}" - is_ca_certificate = true - validity_period_hours = 12 - - subject { - common_name = "ACME Root CA" - organization = "ACME Example Holdings" - } - - allowed_uses = [ - "key_encipherment", - "digital_signature", - "server_auth", - ] -} - -resource "tls_cert_request" "test" { - dns_names = [%[1]q] - key_algorithm = "RSA" - private_key_pem = "${tls_private_key.test.private_key_pem}" - - subject { - common_name = %[1]q - organization = "ACME Example Holdings, Inc" - } -} - -resource "tls_locally_signed_cert" "leaf" { - cert_request_pem = "${tls_cert_request.test.cert_request_pem}" - ca_key_algorithm = "RSA" - ca_private_key_pem = "${tls_private_key.test.private_key_pem}" - ca_cert_pem = "${tls_self_signed_cert.ca.cert_pem}" - validity_period_hours = 12 - - allowed_uses = [ - "key_encipherment", - "digital_signature", - "server_auth", - ] -} -`, commonName) -} - -func testAccAWSAPIGatewayDomainNameConfigAcmCertificateBase(commonName string) string { - return fmt.Sprintf(` -resource "tls_private_key" "test" { - algorithm = "RSA" -} - -resource "tls_self_signed_cert" "test" { - allowed_uses = [ - "key_encipherment", - "digital_signature", - "server_auth", - ] - - dns_names = [%[1]q] - key_algorithm = "RSA" - private_key_pem = "${tls_private_key.test.private_key_pem}" - validity_period_hours = 12 - - subject { - common_name = %[1]q - organization = "ACME Examples, Inc" - } -} - -resource "aws_acm_certificate" "test" { - certificate_body = "${tls_self_signed_cert.test.cert_pem}" - private_key = "${tls_private_key.test.private_key_pem}" -} -`, commonName) -} - func testAccAWSAPIGatewayDomainNameConfig_CertificateArn(domainName, certificateArn string) string { return fmt.Sprintf(` resource "aws_api_gateway_domain_name" "test" { @@ -349,23 +282,25 @@ resource "aws_api_gateway_domain_name" "test" { `, domainName, certificateArn) } -func testAccAWSAPIGatewayDomainNameConfig_CertificateName(domainName, commonName string) string { +func testAccAWSAPIGatewayDomainNameConfig_CertificateName(domainName, key, certificate, chainCertificate string) string { return fmt.Sprintf(` resource "aws_api_gateway_domain_name" "test" { - domain_name = "%s" - certificate_body = "${tls_locally_signed_cert.leaf.cert_pem}" - certificate_chain = "${tls_self_signed_cert.ca.cert_pem}" + domain_name = "%[1]s" + certificate_body = "%[2]s" + certificate_chain = "%[3]s" certificate_name = "tf-acc-apigateway-domain-name" - certificate_private_key = "${tls_private_key.test.private_key_pem}" + certificate_private_key = "%[4]s" +} +`, domainName, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(chainCertificate), tlsPemEscapeNewlines(key)) } -%s - -`, domainName, testAccAWSAPIGatewayCerts(commonName)) +func testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateArn(domainName, key, certificate string) string { + return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + certificate_body = "%[2]s" + private_key = "%[3]s" } -func testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateArn(domainName string) string { - return testAccAWSAPIGatewayDomainNameConfigAcmCertificateBase(domainName) + fmt.Sprintf(` resource "aws_api_gateway_domain_name" "test" { domain_name = %[1]q regional_certificate_arn = "${aws_acm_certificate.test.arn}" @@ -374,38 +309,40 @@ resource "aws_api_gateway_domain_name" "test" { types = ["REGIONAL"] } } -`, domainName) +`, domainName, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key)) } -func testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateName(domainName, commonName string) string { +func testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateName(domainName, key, certificate, chainCertificate string) string { return fmt.Sprintf(` resource "aws_api_gateway_domain_name" "test" { - certificate_body = "${tls_locally_signed_cert.leaf.cert_pem}" - certificate_chain = "${tls_self_signed_cert.ca.cert_pem}" - certificate_private_key = "${tls_private_key.test.private_key_pem}" - domain_name = "%s" + certificate_body = "%[2]s" + certificate_chain = "%[3]s" + certificate_private_key = "%[4]s" + domain_name = "%[1]s" regional_certificate_name = "tf-acc-apigateway-domain-name" endpoint_configuration { types = ["REGIONAL"] } } +`, domainName, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(chainCertificate), tlsPemEscapeNewlines(key)) +} -%s - -`, domainName, testAccAWSAPIGatewayCerts(commonName)) +func testAccAWSAPIGatewayDomainNameConfig_SecurityPolicy(domainName, key, certificate, securityPolicy string) string { + return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + certificate_body = "%[2]s" + private_key = "%[3]s" } -func testAccAWSAPIGatewayDomainNameConfig_SecurityPolicy(domainName, securityPolicy string) string { - return testAccAWSAPIGatewayDomainNameConfigAcmCertificateBase(domainName) + fmt.Sprintf(` resource "aws_api_gateway_domain_name" "test" { domain_name = %[1]q regional_certificate_arn = "${aws_acm_certificate.test.arn}" - security_policy = %[2]q + security_policy = %[4]q endpoint_configuration { types = ["REGIONAL"] } } -`, domainName, securityPolicy) +`, domainName, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key), securityPolicy) } diff --git a/aws/tls.go b/aws/tls.go index bbb807d74dc..e91f9ecba77 100644 --- a/aws/tls.go +++ b/aws/tls.go @@ -3,6 +3,7 @@ package aws import ( "crypto/rand" "crypto/rsa" + "crypto/sha1" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -63,6 +64,122 @@ func tlsRsaPublicKeyPem(keyPem string) string { return string(pem.EncodeToMemory(block)) } +// tlsRsaX509LocallySignedCertificatePem generates a local CA x509 certificate PEM string. +// Wrap with tlsPemEscapeNewlines() to allow simple fmt.Sprintf() +// configurations such as: certificate_pem = "%[1]s" +func tlsRsaX509LocallySignedCertificatePem(caKeyPem, caCertificatePem, keyPem, commonName string) string { + caCertificateBlock, _ := pem.Decode([]byte(caCertificatePem)) + + caCertificate, err := x509.ParseCertificate(caCertificateBlock.Bytes) + + if err != nil { + panic(err) + } + + caKeyBlock, _ := pem.Decode([]byte(caKeyPem)) + + caKey, err := x509.ParsePKCS1PrivateKey(caKeyBlock.Bytes) + + if err != nil { + panic(err) + } + + keyBlock, _ := pem.Decode([]byte(keyPem)) + + key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) + + if err != nil { + panic(err) + } + + serialNumber, err := rand.Int(rand.Reader, tlsX509CertificateSerialNumberLimit) + + if err != nil { + panic(err) + } + + certificate := &x509.Certificate{ + BasicConstraintsValid: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + NotAfter: time.Now().Add(24 * time.Hour), + NotBefore: time.Now(), + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: commonName, + Organization: []string{"ACME Examples, Inc"}, + }, + } + + certificateBytes, err := x509.CreateCertificate(rand.Reader, certificate, caCertificate, &key.PublicKey, caKey) + + if err != nil { + panic(err) + } + + certificateBlock := &pem.Block{ + Bytes: certificateBytes, + Type: pemBlockTypeCertificate, + } + + return string(pem.EncodeToMemory(certificateBlock)) +} + +// tlsRsaX509SelfSignedCaCertificatePem generates a x509 CA certificate PEM string. +// Wrap with tlsPemEscapeNewlines() to allow simple fmt.Sprintf() +// configurations such as: root_certificate_pem = "%[1]s" +func tlsRsaX509SelfSignedCaCertificatePem(keyPem string) string { + keyBlock, _ := pem.Decode([]byte(keyPem)) + + key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) + + if err != nil { + panic(err) + } + + publicKeyBytes, err := x509.MarshalPKIXPublicKey(&key.PublicKey) + + if err != nil { + panic(err) + } + + publicKeyBytesSha1 := sha1.Sum(publicKeyBytes) + + serialNumber, err := rand.Int(rand.Reader, tlsX509CertificateSerialNumberLimit) + + if err != nil { + panic(err) + } + + certificate := &x509.Certificate{ + BasicConstraintsValid: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + IsCA: true, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + NotAfter: time.Now().Add(24 * time.Hour), + NotBefore: time.Now(), + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: "ACME Root CA", + Organization: []string{"ACME Examples, Inc"}, + }, + SubjectKeyId: publicKeyBytesSha1[:], + } + + certificateBytes, err := x509.CreateCertificate(rand.Reader, certificate, certificate, &key.PublicKey, key) + + if err != nil { + panic(err) + } + + certificateBlock := &pem.Block{ + Bytes: certificateBytes, + Type: pemBlockTypeCertificate, + } + + return string(pem.EncodeToMemory(certificateBlock)) +} + // tlsRsaX509SelfSignedCertificatePem generates a x509 certificate PEM string. // Wrap with tlsPemEscapeNewlines() to allow simple fmt.Sprintf() // configurations such as: private_key_pem = "%[1]s" diff --git a/aws/tls_test.go b/aws/tls_test.go index 259f766e5d1..315907f4db9 100644 --- a/aws/tls_test.go +++ b/aws/tls_test.go @@ -22,11 +22,31 @@ func TestTlsRsaPublicKeyPem(t *testing.T) { } } -func TestTlsRsaX509CertificatePem(t *testing.T) { +func TestTlsRsaX509LocallySignedCertificatePem(t *testing.T) { + caKey := tlsRsaPrivateKeyPem(2048) + caCertificate := tlsRsaX509SelfSignedCaCertificatePem(caKey) + key := tlsRsaPrivateKeyPem(2048) + certificate := tlsRsaX509LocallySignedCertificatePem(caKey, caCertificate, key, "example.com") + + if !strings.Contains(certificate, pemBlockTypeCertificate) { + t.Errorf("certificate does not contain CERTIFICATE: %s", certificate) + } +} + +func TestTlsRsaX509SelfSignedCaCertificatePem(t *testing.T) { + caKey := tlsRsaPrivateKeyPem(2048) + caCertificate := tlsRsaX509SelfSignedCaCertificatePem(caKey) + + if !strings.Contains(caCertificate, pemBlockTypeCertificate) { + t.Errorf("CA certificate does not contain CERTIFICATE: %s", caCertificate) + } +} + +func TestTlsRsaX509SelfSignedCertificatePem(t *testing.T) { key := tlsRsaPrivateKeyPem(2048) certificate := tlsRsaX509SelfSignedCertificatePem(key, "example.com") if !strings.Contains(certificate, pemBlockTypeCertificate) { - t.Errorf("key does not contain CERTIFICATE: %s", certificate) + t.Errorf("certificate does not contain CERTIFICATE: %s", certificate) } }