Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New serverless pattern - Private Api Gateway Custom Domain Name #2523

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
61 changes: 61 additions & 0 deletions private-apigw-custom-domain/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Private Amazon API Gateway with Private Custom Domain Name
usama-khan98 marked this conversation as resolved.
Show resolved Hide resolved

The SAM template deploys a Private Amazon API Gateway with a Private Custom Domain Name mapped to deployed stage. This template also create a Route53 A-Alias record in a Private Hosted Zone to map the Private Custom Domain Name (e.g. private.mydomain.com) to the Target VPC Endpoint DNS Name. (e.g. vpce-abcdefgh123456789-abcd1234.execute-api.us-east-1.vpce.amazonaws.com).
usama-khan98 marked this conversation as resolved.
Show resolved Hide resolved

Learn more about this pattern at [Serverless Land Patterns](https://serverlessland.com/patterns/private-apigw-custom-domain)

## Requirements

* An [AWS account](https://signin.aws.amazon.com/signup?request_type=register) with an IAM user or role that has sufficient permissions to make the necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured.
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).
* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed.
* An [execute-api VPC Endpoint](https://docs.aws.amazon.com/vpc/latest/privatelink/interface-endpoints.html).
* A Route 53 [Private Hosted Zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-private.html). (*You can also use Public Hosted Zone but it is recommnded to use Private Hosted Zone to make sure that the Domain Name is only resolvable from within the VPC*)
* An SSL/TLS certificate in [AWS Certificate Manager](https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-specify-certificate-for-custom-domain-name.html#how-to-specify-certificate-for-custom-domain-name-setup).

## Deployment Instructions

1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
```bash
git clone https://github.com/aws-samples/serverless-patterns
```
2. Change directory to the pattern directory:
```bash
cd serverless-patterns/private-apigw-custom-domain
```
3. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file:
```bash
sam deploy --guided
```
4. During the prompts:
- Enter **stack name** and desired **AWS Region**.
- Enter **DNS Name** of the **execute-api** VPC endpoint for the VpcEndpointDNSName parameter. (e.g. vpce-abcdefgh123456789-abcd1234.execute-api.us-east-1.vpce.amazonaws.com)
- Enter **Hosted Zone ID** of the **execute-api** VPC endpoint for the VPCEndpointHostedZoneID parameter. (This can be found along with the DNS Name of the VPC endpoing on the AWS Console.)
- Enter **Private Custom Domain Name** (e.g. private.mydomain.com) for the CustomDomainName parameter.
- Enter **ACM Certificate ARN** from the same region as Private Amazon API Gateway for the CertificateArn parameter. The certificate must cover the Private Custom Domain name entered in the previous step.
- Enter **Private Hosted Zone ID** that has the domain name you would like to use for the parameter PrivateHostedZoneId.
- Allow SAM to create roles with the required permissions if needed.

Once you have run guided mode once, you can use `sam deploy` in future to use these defaults.

1. Note the outputs from the SAM deployment process. This contain the curl command to test the Private Custom Domain Name.

## Testing

The stack will output the **Private Custom Domain Name**. You can use `curl` to send a HTTP request to the Private Custom Domain endpoint to test the correct mapping to your API.

```bash
curl https://{PrivateCustomDomainName}/
```

## Cleanup

1. Delete the stack
```bash
sam delete
```
1. Confirm the stack has been deleted
```bash
aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus"
```
58 changes: 58 additions & 0 deletions private-apigw-custom-domain/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"title": "Private Amazon API Gateway with Private Custom Domain Name",
usama-khan98 marked this conversation as resolved.
Show resolved Hide resolved
"description": "Create a Private API Gateway with a Private Custom Domain Name, configure access based on VPC endpoint, and set up DNS routing using Amazon Route 53 private hosted zone.",
usama-khan98 marked this conversation as resolved.
Show resolved Hide resolved
"language": "",
"level": "200",
"framework": "SAM",
"introBox": {
"headline": "How it works",
"text": [
"This sam template demonstrates how to create a Private Amazon API Gateway with a Private Custom Domain Name, configure secure access based on a specific VPC endpoint, and route traffic through Route 53 in a private hosted zone.",
usama-khan98 marked this conversation as resolved.
Show resolved Hide resolved
"Private Custom Domain name is only accessible from a VPC endpoint, which is mapped to a stage in private Amazon API Gateway. A custom domain name is configured with an SSL/TLS certificate to provide secure access, and an associated Route 53 A-Alias record ensures that traffic is routed to the API.",
usama-khan98 marked this conversation as resolved.
Show resolved Hide resolved
"As prerequisites for this pattern, you must have:"
"* A DNS Name of execute-api VPC Endpoint",
usama-khan98 marked this conversation as resolved.
Show resolved Hide resolved
"* A custom domain name that you would like to create (e.g. private.mydomain.com)",
"* A valid certificate in ACM (Amazon Certificate Manager) in the same Region as Private Amazon API Gateway, that covers the namespace of the domain you would like to use (i.e. *.mydomain.com).",
"* A Route 53 Private Hosted Zone ID that has the domain name you would like to use (e.g. mydomain.com).",
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/private-apigw-custom-domain",
"templateURL": "serverless-patterns/private-apigw-custom-domain",
"projectFolder": "private-apigw-custom-domain",
"templateFile": "template.yaml"
}
},
"resources": {
"bullets": [
{
"text": "Custom domain names for private APIs in API Gateway",
"link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-custom-domains.html"
}
]
},
"deploy": {
"text": [
"Deploy the stack: <code>sam deploy --guided</code>
]
},
"testing": {
"text": [
"See the GitHub repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"Delete the stack: <code>sam delete</code>."
]
},
"authors": [
{
"name": "Usama Ali Khan",
"image": "https://media.licdn.com/dms/image/v2/D4E03AQHcLMpZ1LV9UQ/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1685892371158?e=1737590400&v=beta&t=RaPZkIgm7m3thW4PyKSQNn_w9fMbYBeu5PPrQ6K4vBU",
"bio": "Usama is a Technical Account Manager at Amazon Web Services.",
"linkedin": "usama-ali-khan"
}
]
}
112 changes: 112 additions & 0 deletions private-apigw-custom-domain/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
Serverless patterns - Amazon Private API Gateway endpoint with Private Custom Domain Name

Parameters:
VpcEndpointDNSName:
Type: String
VPCEndpointHostedZoneID:
Type: String
CustomDomainName:
Type: String
CertificateArn:
Type: String
PrivateHostedZoneId:
Type: String

Resources:
PrivateApi:
Type: AWS::Serverless::Api
Properties:
EndpointConfiguration: PRIVATE
StageName: Prod
AlwaysDeploy: true
DefinitionBody:
openapi: "3.0.1"
info:
version: "1.0"
title: !Sub "PrivateApi-${AWS::StackName}"
paths:
/:
get:
responses:
"200":
description: "200 response"
content:
application/json:
schema:
$ref: "#/components/schemas/Empty"
x-amazon-apigateway-integration:
type: "mock"
responses:
default:
statusCode: "200"
responseTemplates:
application/json: '{\"message\":\"Hello from Amazon Private API Gateway\"\}'
requestTemplates:
application/json: "{\"statusCode\": 200}"
passthroughBehavior: "when_no_match"
x-amazon-apigateway-policy:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal: "*"
Action: "execute-api:Invoke"
Resource: "execute-api:/*"
Condition:
StringEquals:
aws:sourceVpce: !Join ["-", [!Select [0, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]], !Select [1, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]]]]

PrivateDomainName:
Type: AWS::ApiGateway::DomainNameV2
Properties:
DomainName: !Ref CustomDomainName
CertificateArn: !Ref CertificateArn
EndpointConfiguration:
Types:
- PRIVATE
SecurityPolicy: TLS_1_2
Policy:
Statement:
- Action: 'execute-api:Invoke'
Effect: Allow
Principal: '*'
Resource: 'execute-api:/*'
- Action: 'execute-api:Invoke'
Condition:
StringNotEquals:
aws:SourceVpce : !Join ["-", [!Select [0, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]], !Select [1, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]]]]
Effect: Deny
Principal: '*'
Resource: 'execute-api:/*'
Version: 2012-10-17

BasePathMapping:
Type: AWS::ApiGateway::BasePathMappingV2
Properties:
RestApiId: !Ref PrivateApi
DomainNameArn: !GetAtt PrivateDomainName.DomainNameArn
Stage: !Ref PrivateApi.Stage

DomainNameAccessAssociation:
Type: AWS::ApiGateway::DomainNameAccessAssociation
Properties:
DomainNameArn: !GetAtt PrivateDomainName.DomainNameArn
AccessAssociationSource: !Join ["-", [!Select [0, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]], !Select [1, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]]]]
AccessAssociationSourceType: VPCE

R53Alias:
Type: AWS::Route53::RecordSet
Properties:
Name: !Ref CustomDomainName
HostedZoneId: !Ref PrivateHostedZoneId
Type: A
AliasTarget:
DNSName: !Ref VpcEndpointDNSName
HostedZoneId: !Ref VPCEndpointHostedZoneID

Outputs:
CURLCommand:
Description: "Curl Command to test"
Value: !Sub "curl https://${CustomDomainName}/"