Skip to content

Commit 52b8f0f

Browse files
committed
added custom domain route53 provider to dstack ingress
1 parent 681f00c commit 52b8f0f

File tree

6 files changed

+680
-4
lines changed

6 files changed

+680
-4
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
3+
Parameters:
4+
HostedZoneId:
5+
Type: String
6+
Default:
7+
Description: Route53 Hosted Zone ID
8+
UserName:
9+
Type: String
10+
Description: IAM user that can only assume the Route53 role
11+
12+
Resources:
13+
User:
14+
Type: AWS::IAM::User
15+
Properties:
16+
UserName: !Ref UserName
17+
18+
AccessKey:
19+
Type: AWS::IAM::AccessKey
20+
Properties:
21+
UserName: !Ref User
22+
Status: Active
23+
24+
Route53Role:
25+
Type: AWS::IAM::Role
26+
Properties:
27+
RoleName: !Sub '${UserName}-route53-role'
28+
AssumeRolePolicyDocument:
29+
Version: '2012-10-17'
30+
Statement:
31+
- Effect: Allow
32+
# The *account root* as trusted principal.
33+
# This avoids invalid-principal errors while remaining safe,
34+
# because the USER policy enforces the actual restriction.
35+
Principal:
36+
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
37+
Action: sts:AssumeRole
38+
Policies:
39+
- PolicyName: Route53DnsChallenges
40+
PolicyDocument:
41+
Version: '2012-10-17'
42+
Statement:
43+
- Sid: AllowDnsChallengeChanges
44+
Effect: Allow
45+
Action:
46+
- route53:ChangeResourceRecordSets
47+
Resource: !Sub arn:aws:route53:::hostedzone/${HostedZoneId}
48+
- Sid: AllowListingForDnsChallenge
49+
Effect: Allow
50+
Action:
51+
- route53:ListHostedZonesByName
52+
- route53:ListHostedZones
53+
- route53:GetChange
54+
- route53:ListResourceRecordSets
55+
Resource: "*"
56+
57+
UserAssumeRolePolicy:
58+
Type: AWS::IAM::Policy
59+
Properties:
60+
PolicyName: !Sub '${UserName}-assume-route53-role'
61+
Users:
62+
- !Ref User
63+
PolicyDocument:
64+
Version: '2012-10-17'
65+
Statement:
66+
- Effect: Allow
67+
Action:
68+
- sts:AssumeRole
69+
Resource: !Sub arn:aws:iam::${AWS::AccountId}:role/${UserName}-route53-role
70+
71+
Outputs:
72+
AWSAccessKeyId:
73+
Description: Access key ID for the IAM user
74+
Value: !Ref AccessKey
75+
76+
AWSSecretAccessKey:
77+
Description: Secret access key for the IAM user
78+
Value: !GetAtt AccessKey.SecretAccessKey
79+
80+
AWSUserArn:
81+
Description: IAM User ARN
82+
Value: !Sub arn:aws:iam::${AWS::AccountId}:user/${UserName}
83+
84+
Route53RoleArn:
85+
Description: ARN of the Route53 role used by Certbot
86+
Value: !Sub arn:aws:iam::${AWS::AccountId}:role/${UserName}-route53-role

custom-domain/dstack-ingress/DNS_PROVIDERS.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This guide explains how to configure dstack-ingress to work with different DNS p
77
- **Cloudflare** - The original and default provider
88
- **Linode DNS** - For Linode-hosted domains
99
- **Namecheap** - For Namecheap-hosted domains
10+
- **Route53** - For AWS hosted domains
1011

1112
## Environment Variables
1213

@@ -73,6 +74,37 @@ NAMECHEAP_CLIENT_IP=your-client-ip
7374
- Namecheap doesn't support CAA records through their API currently
7475
- The certbot plugin uses the format `certbot-dns-namecheap` package
7576

77+
### Route53
78+
79+
```bash
80+
DNS_PROVIDER=route53
81+
AWS_PROFILE=your-injected-aws-profile
82+
```
83+
84+
**Required Permissions:**
85+
```yaml
86+
PolicyDocument:
87+
Version: '2012-10-17'
88+
Statement:
89+
- Sid: AllowDnsChallengeChanges
90+
Effect: Allow
91+
Action:
92+
- route53:ChangeResourceRecordSets
93+
Resource: !Sub arn:aws:route53:::hostedzone/${HostedZoneId}
94+
- Sid: AllowListingForDnsChallenge
95+
Effect: Allow
96+
Action:
97+
- route53:ListHostedZonesByName
98+
- route53:ListHostedZones
99+
- route53:GetChange
100+
- route53:ListResourceRecordSets
101+
```
102+
103+
**Important Notes for Route53:**
104+
- The certbot plugin uses the format `certbot-dns-route53` package
105+
- CAA will merge AWS & Let's Encrypt CA domains to existing records if they exist
106+
- It is recommended that the AWS service account can only assume the limited role. See cloudformation example.
107+
76108
## Docker Compose Examples
77109

78110
### Linode Example
@@ -127,6 +159,51 @@ services:
127159
- ./evidences:/evidences
128160
```
129161

162+
### Route53 Example
163+
164+
```yaml
165+
services:
166+
dstack-ingress:
167+
image: dstack-ingress:latest
168+
restart: unless-stopped
169+
volumes:
170+
- /var/run/dstack.sock:/var/run/dstack.sock
171+
- cert-data:/etc/letsencrypt
172+
ports:
173+
- 443:443
174+
configs:
175+
- source: aws_config
176+
target: /root/.aws/config
177+
mode: 0600
178+
- source: aws_credentials
179+
target: /root/.aws/credentials
180+
mode: 0600
181+
environment:
182+
DNS_PROVIDER: route53
183+
DOMAIN: app.example.com
184+
GATEWAY_DOMAIN: _.${DSTACK_GATEWAY_DOMAIN}
185+
186+
CERTBOT_EMAIL: ${CERTBOT_EMAIL}
187+
TARGET_ENDPOINT: http://backend:8080
188+
189+
AWS_PROFILE: certbot
190+
SET_CAA: 'true'
191+
192+
configs:
193+
aws_config:
194+
content: |
195+
[profile certbot]
196+
role_arn=${ROUTE53_AWS_ROLE_ARN}
197+
source_profile=certbot-source
198+
region=${AWS_REGION}
199+
200+
aws_credentials:
201+
content: |
202+
[certbot-source]
203+
aws_access_key_id=${ROUTE53_AWS_ACCESS_KEY_ID}
204+
aws_secret_access_key=${ROUTE53_AWS_SECRET_ACCESS_KEY}
205+
```
206+
130207
## Migration from Cloudflare-only Setup
131208

132209
If you're currently using the Cloudflare-only version:
@@ -166,4 +243,4 @@ Ensure your API tokens/credentials have the necessary permissions listed above f
166243
1. Go to https://ap.www.namecheap.com/settings/tools/api-access/
167244
2. Enable API access for your account
168245
3. Note down your API key and username
169-
4. Make sure your IP address is whitelisted in the API settings
246+
4. Make sure your IP address is whitelisted in the API settings

custom-domain/dstack-ingress/scripts/certman.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ def _build_certbot_command(self, action: str, domain: str, email: str) -> List[s
291291
if os.environ.get("CERTBOT_STAGING", "false") == "true":
292292
base_cmd.extend(["--staging"])
293293

294-
if getattr(self.provider, 'CERTBOT_PROPAGATION_SECONDS'):
294+
if getattr(self.provider, 'CERTBOT_PROPAGATION_SECONDS') and self.provider.CERTBOT_PROPAGATION_SECONDS is not None:
295295
propagation_seconds = self.provider.CERTBOT_PROPAGATION_SECONDS
296296
propagation_param = f"--dns-{self.provider_type}-propagation-seconds={propagation_seconds}"
297297
base_cmd.extend([propagation_param])

custom-domain/dstack-ingress/scripts/dns_providers/factory.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .cloudflare import CloudflareDNSProvider
77
from .linode import LinodeDNSProvider
88
from .namecheap import NamecheapDNSProvider
9+
from .route53 import Route53DNSProvider
910

1011

1112
class DNSProviderFactory:
@@ -15,6 +16,7 @@ class DNSProviderFactory:
1516
"cloudflare": CloudflareDNSProvider,
1617
"linode": LinodeDNSProvider,
1718
"namecheap": NamecheapDNSProvider,
19+
"route53": Route53DNSProvider,
1820
}
1921

2022
@classmethod
@@ -67,4 +69,4 @@ def _detect_provider_type(cls) -> str:
6769
@classmethod
6870
def get_supported_providers(cls) -> list:
6971
"""Get list of supported DNS providers."""
70-
return list(cls.PROVIDERS.keys())
72+
return list(cls.PROVIDERS.keys())

0 commit comments

Comments
 (0)