Subdomain Provisioning & Custom Domain SSL¶
How CampusCore automatically provisions subdomains and SSL certificates for each deployment, and how clients can use custom domains.
Overview¶
Each CampusCore deployment gets a subdomain under campuscoreai.com (e.g., vsu-troy-pilot.campuscoreai.com) with an automatically provisioned ACM certificate for HTTPS. Clients who want to use their own domain CNAME to the CampusCore subdomain.
Architecture¶
Central Admin Account Client Account (e.g., VSU)
┌──────────────────────────┐ ┌──────────────────────────┐
│ Route53: campuscoreai.com │ │ ALB (port 80 + 443) │
│ │ │ │
│ CNAME: vsu-troy-pilot │───────────▶│ alb-xyz.elb.amazonaws.com│
│ .campuscoreai.com │ │ │
│ │ │ ACM cert for │
│ CNAME: _acm-validation │ │ vsu-troy-pilot │
│ (for cert issuance) │ │ .campuscoreai.com │
└──────────────────────────┘ └──────────────────────────┘
Client's DNS (external)
┌──────────────────────────┐
│ ai.vsu.edu CNAME ────────┼───▶ vsu-troy-pilot.campuscoreai.com
└──────────────────────────┘
How It Works¶
Subdomain Provisioning (Terraform)¶
When ENABLE_CUSTOM_DOMAIN_WITH_SSL is set to true in the GitHub Environment:
- Route53 CNAME — The
subdomainmodule creates{env}.campuscoreai.compointing to the ALB DNS in the admin account's Route53 hosted zone - ACM Certificate — An ACM certificate is requested in the client's AWS account for
{env}.campuscoreai.com - DNS Validation — ACM validation CNAME records are created in the admin account's Route53 zone
- HTTPS Listener — Once the cert is validated, an HTTPS listener is added to the ALB in
base/main.tf - HTTP Redirect — A listener rule redirects all HTTP traffic to HTTPS
Cross-Account Model¶
This uses two AWS provider aliases:
- aws (default) — client account, via assume_role. Used for ACM certificate, ALB listener
- aws.admin — admin account, no assume_role. Used for Route53 records
Circular Dependency Resolution¶
The ALB is created by the ecs_cluster module, and the subdomain module needs the ALB DNS name. But the HTTPS listener needs the certificate ARN from subdomain. This is resolved by:
ecs_clustercreates the ALB and HTTP listener (always forward)subdomaincreates the Route53 CNAME and ACM cert using the ALB DNSbase/main.tfcreates the HTTPS listener and HTTP redirect rule using both the ALB ARN and cert ARN
Custom Domain SSL Modes¶
The admin UI (Settings > Domain) offers two SSL paths when a CampusCore subdomain is provisioned:
CampusCore SSL (Recommended)¶
- Client sets a CNAME:
ai.university.edu→{env}.campuscoreai.com - SSL is handled by the ACM certificate on the ALB
- No SSL configuration needed by the client
Self-Managed SSL¶
- Client sets a CNAME:
ai.university.edu→ ALB DNS directly - Client manages their own SSL (e.g., via Cloudflare, nginx reverse proxy)
- The ALB serves HTTP; the client's proxy terminates SSL
The SSL mode is stored in AppConfig.custom_domain_ssl_mode and affects DNS verification — the domain is cross-checked against the appropriate target (CampusCore subdomain or ALB DNS).
Configuration¶
GitHub Environment Variables¶
| Variable | Required | Description |
|---|---|---|
ENABLE_CUSTOM_DOMAIN_WITH_SSL |
No | Set to true to enable. Defaults to false |
CAMPUSCORE_HOSTED_ZONE_ID |
When provisioning | Route53 hosted zone ID for campuscoreai.com |
Django Settings¶
| Setting | Source | Description |
|---|---|---|
CAMPUSCORE_SUBDOMAIN |
Env var | e.g., vsu-troy-pilot.campuscoreai.com. Empty when not provisioned |
Terraform Variables¶
| Variable | Module | Description |
|---|---|---|
enable_custom_domain_with_ssl |
base |
Bool, gates all subdomain resources |
campuscore_hosted_zone_id |
base |
Passed to subdomain module |
Files¶
| File | Purpose |
|---|---|
infrastructure/modules/subdomain/main.tf |
Route53 CNAME, ACM cert, DNS validation |
infrastructure/modules/subdomain/variables.tf |
Module inputs |
infrastructure/modules/subdomain/outputs.tf |
certificate_arn, campuscore_subdomain |
infrastructure/base/main.tf |
HTTPS listener, HTTP redirect rule, subdomain module wiring |
campuscore_app/apps/main_app/models.py |
custom_domain fields on AppConfig |
campuscore_app/apps/main_app/services/domain_service.py |
DNS verification with subdomain cross-check |
campuscore_app/apps/main_app/apis/settings_apis.py |
Domain settings endpoints |
campuscore_app/templates/partials/settings/domain.html |
Custom domain UI |
Deploying Without Subdomain Provisioning¶
Set ENABLE_CUSTOM_DOMAIN_WITH_SSL to false (or omit it). The subdomain module produces empty outputs, no Route53/ACM resources are created, and no HTTPS listener is added. The ALB serves HTTP only and the custom domain settings UI is hidden.
IAM Permissions¶
- Admin role (
admin-oidc-role.yaml): Route53 permissions for managingcampuscoreai.comDNS records - Client deploy role (
deploy-role.yaml): ACM permissions for requesting and managing certificates, CloudFormation self-update permissions for automatic role updates via pipeline