Skip to content

Commit a0a62d1

Browse files
authored
Merge pull request #2828 from Sliiiin/nilbrand-feature-private-apigw-public-custom-domain
New serverless pattern - private-apigw-public-custom-domain added
2 parents 2ace8a9 + efde3c8 commit a0a62d1

File tree

9 files changed

+1002
-0
lines changed

9 files changed

+1002
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Amazon Private API Gateway with VPC Endpoints and Public Domain
2+
3+
This pattern creates an Amazon Private API Gateway that is only accessible through VPC endpoints, with public custom domain name resolution for internal only access through an Amazon internal Application Load Balancer.
4+
5+
This architecture is intended for use cases which require private APIs, which are only accessible from on-premises via VPN or Direct Connect, while the DNS can be resolved publicly.
6+
7+
Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/private-apigw-custom-domain](https://serverlessland.com/patterns/private-apigw-custom-domain)
8+
9+
Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.
10+
11+
## Project Structure
12+
13+
```
14+
├── app.py # CDK app entry point
15+
├── cdk.json # CDK configuration
16+
├── requirements.txt # Python dependencies
17+
├── private_api_gateway/
18+
│ ├── __init__.py
19+
│ └── private_api_gateway_stack.py # Main stack definition
20+
└── README.md # This file
21+
```
22+
## Architecture
23+
24+
- **VPC**: 10.0.0.0/16 with DNS support
25+
- **Subnets**: 2 public + 2 private subnets across 2 AZs
26+
- **NAT Gateway**: Managed by CDK in public subnets
27+
- **Private API Gateway**: PetStore sample API with VPC endpoint restriction
28+
- **Application Load Balancer**: Internal ALB for SSL termination
29+
- **Lambda Automation**: Custom resource for VPC endpoint target registration
30+
31+
![image](architecture/architecture.png)
32+
33+
## Requirements
34+
Create an AWS account if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
35+
36+
* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
37+
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
38+
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
39+
* [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/v2/guide/getting-started.html) (AWS CDK) installed
40+
* [Amazon Route 53](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-configuring.html) configured as DNS service and public hosted zone created
41+
* [Public Certificate](https://docs.aws.amazon.com/acm/latest/userguide/acm-public-certificates.html) requested in Amazon Certificate Manager (ACM)
42+
* [Python 3.12+](https://www.python.org/downloads/) installed
43+
44+
45+
## Deployment Instructions
46+
47+
### 1. Install Dependencies
48+
```bash
49+
# Create virtual environment
50+
python3 -m venv .venv
51+
52+
# Activate virtual environment
53+
source .venv/bin/activate # or your venv activation command
54+
55+
# Install CDK dependencies
56+
pip install -r requirements.txt
57+
```
58+
59+
### 2. Get Parameters
60+
61+
You must provide both context parameters:
62+
63+
1. **domain_name**: Your custom domain name (e.g., api.example.com)
64+
2. **certificate_arn**: ARN of your ACM certificate that covers the domain
65+
66+
### 3. CDK Deployment
67+
68+
```bash
69+
# Deploy with both required parameters
70+
cdk deploy \
71+
-c domain_name=api.example.com \
72+
-c certificate_arn=arn:aws:acm:region:account:certificate/certificate-id
73+
74+
# Example with subdomain
75+
cdk deploy \
76+
-c domain_name=privateapi.mycompany.com \
77+
-c certificate_arn=arn:aws:acm:region:account:certificate/certificate-id
78+
```
79+
80+
#### CDK Output
81+
82+
The stack provides this output:
83+
- **ALBDNSName**: ALB DNS name for CNAME record
84+
85+
86+
### 4. DNS Configuration
87+
88+
After deployment, you must create a DNS record in your domain's hosted zone:
89+
90+
1. **Get ALB DNS name** from CDK output
91+
2. **Create CNAME record**:
92+
```
93+
api.example.com -> internal-alb-xxx.region.elb.amazonaws.com
94+
```
95+
96+
## Testing
97+
98+
Test from within the VPC (EC2 instance or Client VPN):
99+
```bash
100+
curl https://api.example.com/pets
101+
curl https://api.example.com/pets/2
102+
```
103+
104+
### Expected Responses
105+
```json
106+
# GET /pets
107+
[
108+
{"id": 1, "type": "dog", "price": 249.99},
109+
{"id": 2, "type": "cat", "price": 124.99},
110+
{"id": 3, "type": "fish", "price": 0.99}
111+
]
112+
113+
# GET /pets/2
114+
{
115+
"id": 2,
116+
"type": "cat",
117+
"price": 124.99
118+
}
119+
```
120+
121+
## Security Features
122+
123+
- API only accessible through VPC endpoint
124+
- Security groups restrict access to VPC and Client VPN ranges
125+
- ALB provides SSL termination with your certificate
126+
- Resource policies enforce VPC endpoint access only
127+
128+
## Troubleshooting
129+
130+
### Certificate Issues
131+
- Ensure certificate is in the same region
132+
- Verify certificate covers your domain name
133+
- Check certificate validation status
134+
135+
### DNS Issues
136+
- Verify CNAME or ALIAS record points to ALB DNS name
137+
- Check DNS propagation with `nslookup your-domain.com`
138+
139+
### Target Registration
140+
- Manually check target group health in AWS Console
141+
- Verify VPC endpoint has network interfaces created
142+
143+
## Cleanup
144+
145+
### Delete manually created Resources
146+
- delete Amazon Route 53 Hosted Zone record
147+
- delete Amazon Route 53 Public Hosted Zone
148+
- delete Certificate in AWS Certificate Manager
149+
150+
### Delete CDK Deployment
151+
```bash
152+
cdk destroy -c domain_name=api.example.com -c certificate_arn=YOUR-CERT-ARN
153+
```
154+
155+
-
156+
157+
----
158+
Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
159+
160+
SPDX-License-Identifier: MIT-0
161+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python3
2+
import aws_cdk as cdk
3+
from private_api_gateway.private_api_gateway_stack import PrivateApiGatewayStack
4+
5+
app = cdk.App()
6+
7+
# Get required context parameters
8+
domain_name = app.node.try_get_context("domain_name")
9+
certificate_arn = app.node.try_get_context("certificate_arn")
10+
11+
if not domain_name:
12+
raise ValueError("domain_name context parameter is required. Use: cdk deploy -c domain_name=api.example.com")
13+
14+
if not certificate_arn:
15+
raise ValueError("certificate_arn context parameter is required. Use: cdk deploy -c certificate_arn=arn:aws:acm:...")
16+
17+
PrivateApiGatewayStack(
18+
app,
19+
"PrivateApiGatewayStack",
20+
domain_name=domain_name,
21+
certificate_arn=certificate_arn,
22+
env=cdk.Environment(
23+
account=app.account,
24+
region=app.region
25+
)
26+
)
27+
28+
app.synth()
66.3 KB
Loading
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"app": "python app.py",
3+
"watch": {
4+
"include": [
5+
"**"
6+
],
7+
"exclude": [
8+
"README.md",
9+
"cdk*.json",
10+
"requirements*.txt",
11+
"source.bat",
12+
"**/__pycache__",
13+
"**/*.pyc"
14+
]
15+
},
16+
"context": {
17+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
18+
"@aws-cdk/core:checkSecretUsage": true,
19+
"@aws-cdk/core:target-partitions": [
20+
"aws",
21+
"aws-cn"
22+
],
23+
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
24+
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
25+
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
26+
"@aws-cdk/aws-iam:minimizePolicies": true,
27+
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
28+
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
29+
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
30+
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
31+
"@aws-cdk/aws-apigateway:disableCloudWatchRole": false,
32+
"@aws-cdk/core:enablePartitionLiterals": true,
33+
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
34+
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
35+
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
36+
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
37+
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
38+
"@aws-cdk/aws-route53-patters:useCertificate": true,
39+
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
40+
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
41+
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
42+
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
43+
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
44+
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
45+
"@aws-cdk/aws-redshift:columnId": true,
46+
"@aws-cdk/aws-stepfunctions-tasks:enableLoggingConfiguration": true,
47+
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
48+
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
49+
"@aws-cdk/aws-kms:aliasNameRef": true,
50+
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
51+
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
52+
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
53+
"@aws-cdk/aws-opensearchservice:enableLogging": true,
54+
"@aws-cdk/aws-lambda:useLatestRuntimeVersion": true
55+
}
56+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"title": "Amazon Private API Gateway with a public custom domain",
3+
"description": "Create a Amazon Private API Gateway with a public custom domain.",
4+
"language": "Python",
5+
"level": "200",
6+
"framework": "AWS CDK",
7+
"introBox": {
8+
"headline": "How it works",
9+
"text": [
10+
"This pattern creates an Amazon Private API Gateway that is only accessible through VPC endpoints, with public custom domain name resolution for internal only access through an Amazon internal Application Load Balancer.",
11+
"This architecture is intended for use cases which require private APIs, which are only accessible from on-premises via VPN or Direct Connect, while the DNS can be resolved publicly."
12+
]
13+
},
14+
"gitHub": {
15+
"template": {
16+
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/private-apigw-public-custom-domain",
17+
"templateURL": "serverless-patterns/private-apigw-public-custom-domain",
18+
"projectFolder": "private-apigw-public-custom-domain",
19+
"templateFile": "private_api_gateway/private_api_gateway_stack.py"
20+
}
21+
},
22+
"resources": {
23+
"bullets": [
24+
{
25+
"text": "Private REST APIs in API Gateway",
26+
"link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-apis.html"
27+
},
28+
{
29+
"text": "Working with public hosted zones",
30+
"link": "https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/AboutHZWorkingWith.html"
31+
},
32+
{
33+
"text": "Create an Application Load Balancer",
34+
"link": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-application-load-balancer.html"
35+
}
36+
]
37+
},
38+
"deploy": {
39+
"text": [
40+
"cdk deploy"
41+
]
42+
},
43+
"testing": {
44+
"text": [
45+
"See the GitHub repo for detailed testing instructions."
46+
]
47+
},
48+
"cleanup": {
49+
"text": [
50+
"Delete the stack: <code>cdk destroy</code>."
51+
]
52+
},
53+
"authors": [
54+
{
55+
"name": "Nils Brandes",
56+
"image": "https://avatars.githubusercontent.com/u/179606611",
57+
"linkedin": "nils-brandes",
58+
"bio": "Nils is an AWS Solutions Architect with over 7 years of experience helping enterprise-level manufacturing and industrial companies architect and implement large-scale cloud solutions."
59+
},
60+
{
61+
"name": "Bruno Quintas",
62+
"image":"https://avatars.githubusercontent.com/u/17204295",
63+
"linkedin": "brunoquintas",
64+
"bio": "Bruno Quintas is an AWS Principal Cloud Operations Architect. He's been at AWS for more than 10 years and has held different roles spanning Support Engineering, Technical Account management and Solutions Architecture"
65+
}
66+
]
67+
}

0 commit comments

Comments
 (0)