|
| 1 | +# AWS CloudFront Proxy for LaunchDarkly |
| 2 | + |
| 3 | +A CloudFront distribution that acts as a reverse proxy for LaunchDarkly client SDK and events APIs. For when network calls need to come from a specific URL instead of LaunchDarkly. |
| 4 | + |
| 5 | +## Architecture |
| 6 | + |
| 7 | +``` |
| 8 | +Your Application → CloudFront Edge → LaunchDarkly APIs |
| 9 | + ↓ (cached) |
| 10 | +``` |
| 11 | + |
| 12 | +### Endpoint Mappings |
| 13 | + |
| 14 | +| **Path Pattern** | **Target Origin** | **LaunchDarkly Domain** | **Purpose** | |
| 15 | +|-----------------|-------------------|-------------------------|-------------| |
| 16 | +| `/` (default) | `LDClientSdkOrigin` | `clientsdk.launchdarkly.com` | Default flag polling | |
| 17 | +| `/eval/*` | `LDClientStreamOrigin` | `clientstream.launchdarkly.com` | 🔄 **Streaming evaluations** | |
| 18 | +| `/stream/*` | `LDClientStreamOrigin` | `clientstream.launchdarkly.com` | 🔄 **Real-time streaming** | |
| 19 | +| `/stream/eval/*` | `LDClientStreamOrigin` | `clientstream.launchdarkly.com` | 🔄 **Combined streaming** | |
| 20 | +| `/clientstream/*` | `LDClientStreamOrigin` | `clientstream.launchdarkly.com` | 🔄 **Alternative streaming** | |
| 21 | +| `/events/*` | `LDEventsOrigin` | `events.launchdarkly.com` | 📊 Event tracking | |
| 22 | +| `/goals/*` | `LDClientSdkOrigin` | `clientsdk.launchdarkly.com` | 🎯 Goal tracking | |
| 23 | +| `/sdk/eval/users/*` | `LDClientSdkOrigin` | `clientsdk.launchdarkly.com` | 👤 User-specific evaluations | |
| 24 | +| `/sdk/evalx/*` | `LDAppOrigin` | `app.launchdarkly.com` | 🔧 Extended evaluation APIs | |
| 25 | +| `/sdk/*` | `LDAppOrigin` | `app.launchdarkly.com` | ⚙️ SDK management APIs | |
| 26 | + |
| 27 | +> **Note:** All streaming endpoints (`/eval/*`, `/stream/*`, `/clientstream/*`) use **no-cache policies** for real-time updates. |
| 28 | +
|
| 29 | +## Quick Start |
| 30 | + |
| 31 | +### Prerequisites |
| 32 | + |
| 33 | +1. **AWS CLI configured** with appropriate permissions |
| 34 | +2. **AWS SSO login** (if using SSO) |
| 35 | + |
| 36 | +### Check AWS Authentication |
| 37 | + |
| 38 | +```bash |
| 39 | +# Check if you're logged in |
| 40 | +aws sts get-caller-identity |
| 41 | + |
| 42 | +# If you get "Token has expired and refresh failed", re-login: |
| 43 | +aws sso login --profile YOUR-PROFILE |
| 44 | +``` |
| 45 | + |
| 46 | +### Deploy the CloudFront Proxy |
| 47 | + |
| 48 | +```bash |
| 49 | +cd infrastructure |
| 50 | + |
| 51 | +aws cloudformation deploy \ |
| 52 | + --template-file templates/cloudfront.yaml \ |
| 53 | + --stack-name ld-cloudfront-proxy \ |
| 54 | + --parameter-overrides \ |
| 55 | + UseCustomDomain=false \ |
| 56 | + PriceClass=PriceClass_100 \ |
| 57 | + EnableLogging=false |
| 58 | +``` |
| 59 | + |
| 60 | +**⏱️ Deployment time:** ~15-20 minutes (CloudFront global propagation) |
| 61 | + |
| 62 | +### Get Your Proxy URL |
| 63 | + |
| 64 | +```bash |
| 65 | +aws cloudformation describe-stacks \ |
| 66 | + --stack-name ld-cloudfront-proxy \ |
| 67 | + --query 'Stacks[0].Outputs' \ |
| 68 | + --output table |
| 69 | +``` |
| 70 | + |
| 71 | +This will return your CloudFront domain (e.g., `d1a2b3c4d5e6f7.cloudfront.net`) |
| 72 | + |
| 73 | +## Configuration Options |
| 74 | + |
| 75 | +| Parameter | Default | Options | Description | |
| 76 | +|-----------|---------|---------|-------------| |
| 77 | +| `UseCustomDomain` | `false` | `true`/`false` | Use your own domain instead of CloudFront default | |
| 78 | +| `DomainName` | `""` | Your domain | Required if UseCustomDomain=true (e.g., `flags.my-super-awesome-company.com`) | |
| 79 | +| `AcmCertificateArn` | `""` | ACM ARN | Required if UseCustomDomain=true (must be in us-east-1) | |
| 80 | +| `AutoCreateDNS` | `false` | `true`/`false` | **NEW:** Automatically create Route 53 DNS record | |
| 81 | +| `HostedZoneId` | `""` | Route 53 Zone ID | Required if AutoCreateDNS=true (e.g., `Z1D633PJN98FT9`) | |
| 82 | +| `PriceClass` | `PriceClass_100` | `PriceClass_100`/`200`/`All` | Coverage: US/Canada/Europe/Asia (100) vs Global (All) | |
| 83 | +| `EnableLogging` | `false` | `true`/`false` | Enable CloudFront access logging | |
| 84 | +| `LoggingBucket` | `""` | S3 bucket name | Required if EnableLogging=true | |
| 85 | + |
| 86 | +### Price Class Options |
| 87 | + |
| 88 | +- **PriceClass_100** (Recommended): US, Canada, Europe, Asia - Lowest cost |
| 89 | +- **PriceClass_200**: Adds Middle East, Africa - Medium cost |
| 90 | +- **PriceClass_All**: Global coverage - Highest cost |
| 91 | + |
| 92 | +### Custom Domain Setup Options |
| 93 | + |
| 94 | +**⚠️ PREREQUISITES:** Before using `UseCustomDomain=true`, you must complete the following setup: |
| 95 | + |
| 96 | +#### Step 1: Get Your Route 53 Hosted Zone ID |
| 97 | +```bash |
| 98 | +# Find your hosted zone ID (replace with your domain) |
| 99 | +aws route53 list-hosted-zones --query 'HostedZones[?Name==`my-awesome-domain.com.`].[Id,Name]' --output table |
| 100 | + |
| 101 | +# Example output: Zone ID like Z04794713N147BEH1NVCF |
| 102 | +``` |
| 103 | + |
| 104 | +#### Step 2: Create ACM Certificate (Required) |
| 105 | +```bash |
| 106 | +# Request SSL certificate (MUST be in us-east-1 for CloudFront) |
| 107 | +aws acm request-certificate \ |
| 108 | + --domain-name ld.my-awesoome-domain.com \ |
| 109 | + --validation-method DNS \ |
| 110 | + --region us-east-1 |
| 111 | + |
| 112 | +# Save the Certificate ARN from the output! |
| 113 | +``` |
| 114 | + |
| 115 | +#### Step 3: Validate Certificate |
| 116 | +```bash |
| 117 | +# Get DNS validation record details |
| 118 | +aws acm describe-certificate --certificate-arn YOUR-CERT-ARN --region us-east-1 |
| 119 | + |
| 120 | +# Create validation record in Route 53 (replace with your values) |
| 121 | +aws route53 change-resource-record-sets --hosted-zone-id YOUR-ZONE-ID --change-batch '{ |
| 122 | + "Changes": [{ |
| 123 | + "Action": "CREATE", |
| 124 | + "ResourceRecordSet": { |
| 125 | + "Name": "_validation-string.ld.my-awesome-domain.com.", |
| 126 | + "Type": "CNAME", |
| 127 | + "TTL": 300, |
| 128 | + "ResourceRecords": [{"Value": "_validation-value.acm-validations.aws."}] |
| 129 | + } |
| 130 | + }] |
| 131 | +}' |
| 132 | + |
| 133 | +# Verify certificate is issued (wait 1-2 minutes) |
| 134 | +aws acm describe-certificate --certificate-arn YOUR-CERT-ARN --region us-east-1 \ |
| 135 | + --query 'Certificate.Status' --output text |
| 136 | +# Should return: ISSUED |
| 137 | +``` |
| 138 | + |
| 139 | +#### Option 1: Automatic DNS (Recommended) |
| 140 | +If you have a Route 53 hosted zone, the template can automatically create DNS records: |
| 141 | + |
| 142 | +```bash |
| 143 | +aws cloudformation deploy \ |
| 144 | + --template-file templates/cloudfront.yaml \ |
| 145 | + --stack-name ld-cloudfront-proxy \ |
| 146 | + --parameter-overrides \ |
| 147 | + UseCustomDomain=true \ |
| 148 | + DomainName=ld.my-awesome-domain.com \ |
| 149 | + AcmCertificateArn=my-awesome-arn \ |
| 150 | + AutoCreateDNS=true \ |
| 151 | + HostedZoneId=my-awesome-hosted-zone-id \ |
| 152 | + PriceClass=PriceClass_100 |
| 153 | +``` |
| 154 | + |
| 155 | +## 📱 SDK Configuration |
| 156 | + |
| 157 | +Once deployed, configure your LaunchDarkly SDKs to use your CloudFront proxy: |
| 158 | + |
| 159 | +### React SDK (React Applications) |
| 160 | +```javascript |
| 161 | +const LDProvider = await asyncWithLDProvider({ |
| 162 | + clientSideID: 'your-client-side-id', |
| 163 | + context: { |
| 164 | + kind: "device", |
| 165 | + key: "unique-device-id" |
| 166 | + }, |
| 167 | + options: { |
| 168 | + baseUrl: 'https://ld.my-awesome-domain.com', |
| 169 | + eventsUrl: 'https://ld.my-awesome-domain.com', |
| 170 | + streamUrl: 'https://ld.my-awesome-domain.com', |
| 171 | + streaming: true |
| 172 | + } |
| 173 | +}); |
| 174 | +``` |
| 175 | + |
| 176 | + |
| 177 | +## What Gets Deployed |
| 178 | + |
| 179 | +### Infrastructure Resources |
| 180 | +- **CloudFront Distribution** with 400+ global edge locations |
| 181 | +- **Cache Policies:** |
| 182 | + - Standard Cache Policy (5min default TTL, 10min max TTL) - for flag evaluations |
| 183 | + - No-Cache Policy (0-1s TTL) - for streaming endpoints |
| 184 | +- **Origin Request Policy** (forwards query strings and key headers) |
| 185 | +- **Response Headers Policy** (CORS configuration for client-side SDKs) |
| 186 | + |
| 187 | +### LaunchDarkly Origins |
| 188 | +- **`clientsdk.launchdarkly.com`** - Default flag polling, goals, user evaluations |
| 189 | +- **`clientstream.launchdarkly.com`** - Real-time streaming, SSE endpoints |
| 190 | +- **`events.launchdarkly.com`** - Event tracking and analytics |
| 191 | +- **`app.launchdarkly.com`** - SDK management and extended APIs |
| 192 | + |
| 193 | +## Cleanup |
| 194 | + |
| 195 | +### Automated Cleanup (Recommended) |
| 196 | +```bash |
| 197 | +aws cloudformation deploy \ |
| 198 | + --template-file templates/remove-cloudfront.yaml \ |
| 199 | + --stack-name cleanup-ld-cloudfront \ |
| 200 | + --capabilities CAPABILITY_NAMED_IAM \ |
| 201 | + --parameter-overrides StackNameToDelete=ld-cloudfront-proxy |
| 202 | +``` |
| 203 | + |
| 204 | +### Manual Cleanup |
| 205 | +```bash |
| 206 | +aws cloudformation delete-stack --stack-name ld-cloudfront-proxy |
| 207 | +``` |
| 208 | + |
| 209 | +**⏱️ Deletion time:** ~15-20 minutes (CloudFront global propagation) |
| 210 | + |
| 211 | +## Monitoring & Troubleshooting |
| 212 | + |
| 213 | +### Check Stack Status |
| 214 | +```bash |
| 215 | +aws cloudformation describe-stack-events --stack-name ld-cloudfront-proxy --output table |
| 216 | +``` |
| 217 | + |
| 218 | +### Verify AWS Configuration |
| 219 | +```bash |
| 220 | +# Check current region |
| 221 | +aws configure get region |
| 222 | + |
| 223 | +# List AWS profiles |
| 224 | +aws configure list-profiles |
| 225 | + |
| 226 | +# Test connectivity |
| 227 | +aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE --output table |
| 228 | +``` |
| 229 | + |
| 230 | +## SDK Compatibility |
| 231 | + |
| 232 | +| SDK Type | Supported | Notes | |
| 233 | +|----------|-----------|--------| |
| 234 | +| **Client-side SDKs** | ✅ Yes | JavaScript, React, iOS, Android, Flutter | |
| 235 | +| **Server-side SDKs** | ❌ No | Java, .NET, Python, Go, Node.js (server-side) | |
| 236 | +| **Event Tracking** | ✅ Yes | From any SDK type | |
| 237 | + |
| 238 | +**Note:** Server-side SDKs use different endpoints (`sdk.launchdarkly.com`) not currently proxied by this template. |
| 239 | + |
| 240 | +## Multi-Project Usage |
| 241 | + |
| 242 | +Different LaunchDarkly projects within the same organization can use different configurations: |
| 243 | + |
| 244 | +- **Project A**: Uses CloudFront proxy (this reverse proxy setup) |
| 245 | +- **Project B**: Connects directly to LaunchDarkly |
| 246 | +- **Project C**: Uses a different proxy or region |
| 247 | + |
| 248 | +Each project configures its SDK independently using different SDK keys and base URLs. |
| 249 | + |
| 250 | +## Contributing |
| 251 | + |
| 252 | +1. Test changes in a development AWS account first |
| 253 | +2. Validate CloudFormation templates before committing |
| 254 | +3. Update documentation for any parameter changes |
0 commit comments