Skip to content

Commit

Permalink
add custom domain support for apigw (#1679)
Browse files Browse the repository at this point in the history
### Feature or Bugfix
- Bugfix

### Detail
Adding custom domain support for apigw with minimum TLS1.2

Be aware that by enabling this feature the automatically generated apigw
URL will be disabled. As a prereq users must already have a custom
domain in Route53 and have an ACM certificate. The domain must be setup
according to [this](
https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-edge-optimized-custom-domain-name.html).
To enable this feature one must specify the following in `cdk.json`.

```json
  "context": {
    "DeploymentEnvironments": [
      {
        ...
        "apigw_custom_domain": {         
            "hosted_zone_name": "custom.domain.com",
            "hosted_zone_id": "...",
            "certificate_arn": "arn:aws:acm:us-east-1:..." # edge optimized domain thus cert must be in us-east-1
        }
        ...
    }
  }
```

### Relates
- <URL or Ticket>

### Security
Please answer the questions below briefly where applicable, or write
`N/A`. Based on
[OWASP 10](https://owasp.org/Top10/en/).

- Does this PR introduce or modify any input fields or queries - this
includes
fetching data from storage outside the application (e.g. a database, an
S3 bucket)?
  - Is the input sanitized?
- What precautions are you taking before deserializing the data you
consume?
  - Is injection prevented by parametrizing queries?
  - Have you ensured no `eval` or similar functions are used?
- Does this PR introduce any functionality or component that requires
authorization?
- How have you ensured it respects the existing AuthN/AuthZ mechanisms?
  - Are you logging failed auth attempts?
- Are you using or adding any cryptographic features?
  - Do you use a standard proven implementations?
  - Are the used keys controlled by the customer? Where are they stored?
- Are you introducing any new policies/roles/users?
  - Have you used the least-privilege principle? How?


By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
  • Loading branch information
petrkalos authored Nov 5, 2024
1 parent 223b0f5 commit f66bac0
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 2 deletions.
2 changes: 2 additions & 0 deletions deploy/stacks/backend_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def __init__(
vpc_endpoints_sg=None,
internet_facing=True,
custom_domain=None,
apigw_custom_domain=None,
ip_ranges=None,
apig_vpce=None,
prod_sizing=False,
Expand Down Expand Up @@ -201,6 +202,7 @@ def __init__(
email_custom_domain=ses_stack.ses_identity.email_identity_name if ses_stack is not None else None,
ses_configuration_set=ses_stack.configuration_set.configuration_set_name if ses_stack is not None else None,
custom_domain=custom_domain,
apigw_custom_domain=apigw_custom_domain,
custom_auth=custom_auth,
custom_waf_rules=custom_waf_rules,
allowed_origins=allowed_origins,
Expand Down
2 changes: 2 additions & 0 deletions deploy/stacks/backend_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def __init__(
vpc_endpoints_sg=None,
internet_facing=True,
custom_domain=None,
apigw_custom_domain=None,
ip_ranges=None,
apig_vpce=None,
prod_sizing=False,
Expand Down Expand Up @@ -56,6 +57,7 @@ def __init__(
vpc_endpoints_sg=vpc_endpoints_sg,
internet_facing=internet_facing,
custom_domain=custom_domain,
apigw_custom_domain=apigw_custom_domain,
ip_ranges=ip_ranges,
apig_vpce=apig_vpce,
prod_sizing=prod_sizing,
Expand Down
37 changes: 35 additions & 2 deletions deploy/stacks/lambda_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@
aws_kms as kms,
aws_sqs as sqs,
aws_logs as logs,
aws_route53 as r53,
aws_route53_targets as r53_targets,
Duration,
CfnOutput,
Fn,
RemovalPolicy,
BundlingOptions,
)
from aws_cdk.aws_apigateway import DomainNameOptions, EndpointType, SecurityPolicy
from aws_cdk.aws_certificatemanager import Certificate
from aws_cdk.aws_ec2 import (
InterfaceVpcEndpoint,
InterfaceVpcEndpointAwsService,
Expand Down Expand Up @@ -56,13 +60,14 @@ def __init__(
email_custom_domain=None,
ses_configuration_set=None,
custom_domain=None,
apigw_custom_domain=None,
custom_auth=None,
allowed_origins='*',
log_retention_duration=None,
**kwargs,
):
super().__init__(scope, id, **kwargs)

self.apigw_custom_domain = apigw_custom_domain
self.log_retention_duration = log_retention_duration
log_level = 'INFO' if prod_sizing else 'DEBUG'

Expand Down Expand Up @@ -662,15 +667,43 @@ def set_up_graphql_api_gateway(
types=[apigw.EndpointType.PRIVATE], vpc_endpoints=[api_vpc_endpoint]
),
policy=api_policy,
disable_execute_api_endpoint=bool(self.apigw_custom_domain),
)
else:
gw = apigw.RestApi(
self,
backend_api_name,
rest_api_name=backend_api_name,
deploy_options=api_deploy_options,
disable_execute_api_endpoint=bool(self.apigw_custom_domain),
)

if self.apigw_custom_domain:
certificate = Certificate.from_certificate_arn(
self, 'CustomDomainCertificate', self.apigw_custom_domain['certificate_arn']
)
gw.add_domain_name(
'ApiGwCustomDomainName',
certificate=certificate,
domain_name=self.apigw_custom_domain['hosted_zone_name'],
endpoint_type=EndpointType.EDGE if internet_facing else EndpointType.PRIVATE,
security_policy=SecurityPolicy.TLS_1_2,
)
r53.ARecord(
self,
'ApiGwARecordId',
zone=r53.HostedZone.from_hosted_zone_attributes(
self,
'ApiGwHostedZoneId',
hosted_zone_id=self.apigw_custom_domain['hosted_zone_id'],
zone_name=self.apigw_custom_domain['hosted_zone_name'],
),
target=r53.RecordTarget.from_alias(r53_targets.ApiGateway(gw)),
)
api_url = gw.url
api_url = f'https://{gw.domain_name.domain_name}/'
else:
api_url = gw.url

integration = apigw.LambdaIntegration(api_handler)
request_validator = apigw.RequestValidator(
self,
Expand Down
1 change: 1 addition & 0 deletions deploy/stacks/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ def set_backend_stage(self, target_env, repository_name):
vpc_restricted_nacls=target_env.get('vpc_restricted_nacl', False),
internet_facing=target_env.get('internet_facing', True),
custom_domain=target_env.get('custom_domain'),
apigw_custom_domain=target_env.get('apigw_custom_domain'),
ip_ranges=target_env.get('ip_ranges'),
apig_vpce=target_env.get('apig_vpce'),
prod_sizing=target_env.get('prod_sizing', True),
Expand Down

0 comments on commit f66bac0

Please sign in to comment.