mirror of
https://github.com/blackboxprogramming/BlackRoad-Operating-System.git
synced 2026-03-17 05:57:21 -05:00
Add Cloudflare DNS migration infrastructure and automation
Implements Action #1 from NEXT_ACTIONS_ALEXA.md: Complete Cloudflare DNS migration tooling and documentation for BlackRoad domains. New Features: - Automated DNS sync script (scripts/cloudflare/sync_dns.py) * Syncs DNS records from ops/domains.yaml to Cloudflare API * Supports dry-run mode for safe previewing * Handles CNAME, A, MX, and TXT records * Colored output for easy scanning - DNS validation script (scripts/cloudflare/validate_dns.py) * Verifies DNS resolution and propagation * Checks SSL certificate validity * Tests HTTP/HTTPS accessibility and redirects * Supports checking multiple domains - GitHub Actions workflow (.github/workflows/sync-cloudflare-dns.yml) * Automatically syncs DNS on ops/domains.yaml changes * Includes dry-run validation step * Manual trigger support via workflow_dispatch Documentation: - Comprehensive migration guide (docs/CLOUDFLARE_MIGRATION_GUIDE.md) * Step-by-step instructions for migrating from GoDaddy to Cloudflare * Covers all 10 BlackRoad domains * Includes troubleshooting and rollback procedures * Estimated 2-4 hours total migration time - Scripts README (scripts/cloudflare/README.md) * Installation and usage instructions * API token setup guide * Integration examples for CI/CD Configuration: - Updated backend/.env.example with Cloudflare variables: * CLOUDFLARE_API_TOKEN * CLOUDFLARE_ACCOUNT_ID * CLOUDFLARE_ZONE_ID * CLOUDFLARE_EMAIL - Python dependencies (scripts/cloudflare/requirements.txt) * requests, PyYAML, dnspython, colorama Benefits of Cloudflare Migration: - Free SSL certificates with automatic renewal - Global CDN for faster page loads - DDoS protection and Web Application Firewall - Better DNS performance via anycast network - Advanced features (Workers, Zero Trust, edge functions) - Superior analytics and security insights Next Steps: 1. Set up Cloudflare account and get API token 2. Add blackroad.systems domain to Cloudflare 3. Update nameservers at GoDaddy 4. Run sync script to configure DNS records 5. Verify migration with validation script 6. Repeat for remaining 9 domains Related: - Implements infra/cloudflare/CLOUDFLARE_DNS_BLUEPRINT.md - Addresses ops/DOMAIN_FIX_GUIDE.md recommendations - Part of Phase 1 Q1 infrastructure foundation Files Added: - scripts/cloudflare/sync_dns.py (352 lines) - scripts/cloudflare/validate_dns.py (387 lines) - scripts/cloudflare/README.md - scripts/cloudflare/requirements.txt - docs/CLOUDFLARE_MIGRATION_GUIDE.md (867 lines) - .github/workflows/sync-cloudflare-dns.yml Files Modified: - backend/.env.example (added Cloudflare env vars)
This commit is contained in:
63
.github/workflows/sync-cloudflare-dns.yml
vendored
Normal file
63
.github/workflows/sync-cloudflare-dns.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Sync Cloudflare DNS
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ops/domains.yaml'
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch: # Allow manual triggers
|
||||
|
||||
jobs:
|
||||
sync-dns:
|
||||
name: Sync DNS Records to Cloudflare
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r scripts/cloudflare/requirements.txt
|
||||
|
||||
- name: Validate domains.yaml
|
||||
run: |
|
||||
python -c "import yaml; yaml.safe_load(open('ops/domains.yaml'))"
|
||||
|
||||
- name: Sync DNS records (dry run)
|
||||
env:
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
|
||||
run: |
|
||||
python scripts/cloudflare/sync_dns.py --dry-run
|
||||
|
||||
- name: Sync DNS records
|
||||
env:
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
|
||||
run: |
|
||||
python scripts/cloudflare/sync_dns.py
|
||||
|
||||
- name: Validate DNS configuration
|
||||
run: |
|
||||
python scripts/cloudflare/validate_dns.py --domain blackroad.systems --dns-only
|
||||
continue-on-error: true # Don't fail if DNS hasn't propagated yet
|
||||
|
||||
- name: Comment on commit (if manual trigger)
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.repos.createCommitComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
commit_sha: context.sha,
|
||||
body: '✅ Cloudflare DNS sync completed successfully!'
|
||||
})
|
||||
@@ -57,7 +57,12 @@ RAILWAY_ENVIRONMENT_ID=00000000-0000-0000-0000-000000000000
|
||||
RAILWAY_DOMAIN=your-service.up.railway.app
|
||||
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXX/YYY/ZZZ
|
||||
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/XXX/YYY
|
||||
|
||||
# Cloudflare DNS and CDN
|
||||
CLOUDFLARE_API_TOKEN=cloudflare-api-token-placeholder
|
||||
CLOUDFLARE_ACCOUNT_ID=your-cloudflare-account-id
|
||||
CLOUDFLARE_ZONE_ID=your-cloudflare-zone-id-for-blackroad-systems
|
||||
CLOUDFLARE_EMAIL=your-cloudflare-email@example.com
|
||||
|
||||
# Optional cloud/API integrations
|
||||
DIGITAL_OCEAN_API_KEY=your-digital-ocean-api-key
|
||||
|
||||
770
docs/CLOUDFLARE_MIGRATION_GUIDE.md
Normal file
770
docs/CLOUDFLARE_MIGRATION_GUIDE.md
Normal file
@@ -0,0 +1,770 @@
|
||||
# Cloudflare DNS Migration Guide
|
||||
## Complete Step-by-Step Guide for BlackRoad Domains
|
||||
|
||||
**Version:** 1.0
|
||||
**Date:** 2025-11-18
|
||||
**Estimated Time:** 2-4 hours for all domains
|
||||
**Skill Level:** Intermediate
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide walks you through migrating BlackRoad domains from GoDaddy DNS to Cloudflare DNS. The migration provides:
|
||||
|
||||
- ✅ **Free SSL certificates** (automatic renewal)
|
||||
- ✅ **Global CDN** (faster page loads worldwide)
|
||||
- ✅ **DDoS protection** (automatic threat mitigation)
|
||||
- ✅ **Better DNS performance** (anycast network)
|
||||
- ✅ **Advanced features** (Workers, Zero Trust, edge functions)
|
||||
- ✅ **Superior analytics** (traffic insights, security events)
|
||||
|
||||
**What you'll need:**
|
||||
- Cloudflare account (free tier is sufficient)
|
||||
- GoDaddy account with domain access
|
||||
- 2-4 hours of time
|
||||
- Basic command line familiarity (optional, for automation)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Pre-Migration Checklist](#pre-migration-checklist)
|
||||
2. [Phase 1: Set Up Cloudflare Account](#phase-1-set-up-cloudflare-account)
|
||||
3. [Phase 2: Add Domains to Cloudflare](#phase-2-add-domains-to-cloudflare)
|
||||
4. [Phase 3: Update Nameservers at GoDaddy](#phase-3-update-nameservers-at-godaddy)
|
||||
5. [Phase 4: Configure DNS Records](#phase-4-configure-dns-records)
|
||||
6. [Phase 5: Configure SSL/TLS](#phase-5-configure-ssltls)
|
||||
7. [Phase 6: Optimize Performance](#phase-6-optimize-performance)
|
||||
8. [Phase 7: Verify Migration](#phase-7-verify-migration)
|
||||
9. [Phase 8: Automation Setup (Optional)](#phase-8-automation-setup-optional)
|
||||
10. [Troubleshooting](#troubleshooting)
|
||||
11. [Rollback Plan](#rollback-plan)
|
||||
|
||||
---
|
||||
|
||||
## Pre-Migration Checklist
|
||||
|
||||
Before starting, gather this information:
|
||||
|
||||
- [ ] **GoDaddy credentials** - Username and password
|
||||
- [ ] **Current DNS records** - Document existing configuration
|
||||
- [ ] **Email access** - For Cloudflare verification
|
||||
- [ ] **Railway hostname** - Your production app URL (e.g., `your-app.up.railway.app`)
|
||||
- [ ] **Current uptime** - Note if services are running properly
|
||||
|
||||
**⚠️ Important Notes:**
|
||||
- Migration happens with **zero downtime** if done correctly
|
||||
- DNS propagation takes 5-60 minutes (sometimes up to 24 hours)
|
||||
- Keep GoDaddy account active (domain registration stays there)
|
||||
- Only DNS management moves to Cloudflare
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Set Up Cloudflare Account
|
||||
|
||||
### Step 1.1: Create Cloudflare Account
|
||||
|
||||
1. Go to [https://dash.cloudflare.com/sign-up](https://dash.cloudflare.com/sign-up)
|
||||
2. Enter your email address
|
||||
3. Create a strong password
|
||||
4. Verify your email address
|
||||
|
||||
### Step 1.2: Two-Factor Authentication (Recommended)
|
||||
|
||||
1. Click on your profile (top right)
|
||||
2. Go to **My Profile** → **Authentication**
|
||||
3. Enable **Two-Factor Authentication**
|
||||
4. Save backup codes in a secure location
|
||||
|
||||
### Step 1.3: Get API Token
|
||||
|
||||
1. Go to **My Profile** → **API Tokens**
|
||||
2. Click **Create Token**
|
||||
3. Select **Edit zone DNS** template
|
||||
4. Configure permissions:
|
||||
- **Zone - DNS - Edit**
|
||||
- **Zone - Zone - Read**
|
||||
5. Select **Specific zones** → (you'll add zones in Phase 2)
|
||||
6. Click **Continue to summary** → **Create Token**
|
||||
7. **Copy the token immediately** (you won't see it again!)
|
||||
8. Save it securely (we'll use it later for automation)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Add Domains to Cloudflare
|
||||
|
||||
We'll start with the primary domain (`blackroad.systems`) and then add others.
|
||||
|
||||
### Step 2.1: Add blackroad.systems
|
||||
|
||||
1. From Cloudflare dashboard, click **Add a site**
|
||||
2. Enter: `blackroad.systems`
|
||||
3. Click **Add site**
|
||||
4. Select **Free plan** → Click **Continue**
|
||||
5. Cloudflare will scan existing DNS records from GoDaddy
|
||||
6. Wait 30-60 seconds for scan to complete
|
||||
|
||||
### Step 2.2: Review Scanned Records
|
||||
|
||||
Cloudflare should detect existing records. Review them:
|
||||
|
||||
- ✅ Check for A records pointing to your server
|
||||
- ✅ Check for CNAME records
|
||||
- ✅ Check for MX records (email)
|
||||
- ✅ Check for TXT records (SPF, verification)
|
||||
|
||||
**Common issues:**
|
||||
- Some records might be missing → We'll add them manually later
|
||||
- TTL values might be high → We'll adjust them
|
||||
|
||||
### Step 2.3: Get Nameservers
|
||||
|
||||
After scanning, Cloudflare will show 2 nameservers like:
|
||||
|
||||
```
|
||||
aaaa.ns.cloudflare.com
|
||||
bbbb.ns.cloudflare.com
|
||||
```
|
||||
|
||||
**⚠️ IMPORTANT:** Copy these nameservers! You'll need them in Phase 3.
|
||||
|
||||
**Don't click "Done" yet** - we'll do that after updating nameservers at GoDaddy.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Update Nameservers at GoDaddy
|
||||
|
||||
**⏰ Estimated Time:** 10 minutes + 5-60 minutes propagation
|
||||
|
||||
### Step 3.1: Log in to GoDaddy
|
||||
|
||||
1. Go to [https://account.godaddy.com](https://account.godaddy.com)
|
||||
2. Sign in with your credentials
|
||||
3. Go to **My Products** → **Domains**
|
||||
|
||||
### Step 3.2: Update Nameservers for blackroad.systems
|
||||
|
||||
1. Find `blackroad.systems` in your domain list
|
||||
2. Click the three dots (...) → **Manage DNS**
|
||||
3. Scroll down to **Nameservers** section
|
||||
4. Click **Change** → Select **Enter my own nameservers (advanced)**
|
||||
5. Remove existing nameservers
|
||||
6. Add the 2 Cloudflare nameservers from Phase 2, Step 2.3:
|
||||
```
|
||||
aaaa.ns.cloudflare.com
|
||||
bbbb.ns.cloudflare.com
|
||||
```
|
||||
7. Click **Save**
|
||||
8. Confirm the change
|
||||
|
||||
**What happens now:**
|
||||
- GoDaddy will propagate the nameserver change
|
||||
- This takes 5-60 minutes (sometimes up to 24 hours)
|
||||
- Your site will continue working during this time
|
||||
|
||||
### Step 3.3: Return to Cloudflare
|
||||
|
||||
1. Go back to Cloudflare dashboard
|
||||
2. Click **Done, check nameservers**
|
||||
3. Cloudflare will start checking for nameserver changes
|
||||
4. You'll see status: **Pending Nameserver Update**
|
||||
|
||||
**⏳ Wait time:** 5-60 minutes for Cloudflare to detect the change
|
||||
|
||||
You can check status by:
|
||||
- Refreshing the Cloudflare dashboard
|
||||
- Running: `dig NS blackroad.systems` (should show Cloudflare nameservers)
|
||||
- Using: [https://dnschecker.org](https://dnschecker.org)
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Configure DNS Records
|
||||
|
||||
Once Cloudflare shows **Active** status, configure DNS records.
|
||||
|
||||
### Step 4.1: Verify Existing Records
|
||||
|
||||
1. In Cloudflare dashboard, go to **DNS** → **Records**
|
||||
2. Review scanned records
|
||||
3. Remove any incorrect or outdated records
|
||||
|
||||
### Step 4.2: Add/Update Primary Records
|
||||
|
||||
#### Root Domain (blackroad.systems)
|
||||
|
||||
| Type | Name | Target | Proxy | TTL |
|
||||
|------|------|--------|-------|-----|
|
||||
| CNAME | @ | `your-app.up.railway.app` | ✅ Proxied | Auto |
|
||||
|
||||
**Steps:**
|
||||
1. Click **Add record**
|
||||
2. Type: **CNAME**
|
||||
3. Name: **@** (represents root domain)
|
||||
4. Target: **your-railway-app.up.railway.app** (replace with actual Railway URL)
|
||||
5. Proxy status: **Proxied** (orange cloud icon)
|
||||
6. TTL: **Auto**
|
||||
7. Click **Save**
|
||||
|
||||
#### WWW Subdomain
|
||||
|
||||
| Type | Name | Target | Proxy | TTL |
|
||||
|------|------|--------|-------|-----|
|
||||
| CNAME | www | blackroad.systems | ✅ Proxied | Auto |
|
||||
|
||||
**Steps:**
|
||||
1. Click **Add record**
|
||||
2. Type: **CNAME**
|
||||
3. Name: **www**
|
||||
4. Target: **blackroad.systems**
|
||||
5. Proxy status: **Proxied**
|
||||
6. Click **Save**
|
||||
|
||||
#### API Subdomain
|
||||
|
||||
| Type | Name | Target | Proxy | TTL |
|
||||
|------|------|--------|-------|-----|
|
||||
| CNAME | api | `your-app.up.railway.app` | ✅ Proxied | Auto |
|
||||
|
||||
#### OS Subdomain
|
||||
|
||||
| Type | Name | Target | Proxy | TTL |
|
||||
|------|------|--------|-------|-----|
|
||||
| CNAME | os | blackroad.systems | ✅ Proxied | Auto |
|
||||
|
||||
### Step 4.3: Configure Email Records (If Applicable)
|
||||
|
||||
If you use Google Workspace, G Suite, or custom email:
|
||||
|
||||
#### SPF Record
|
||||
| Type | Name | Content | TTL |
|
||||
|------|------|---------|-----|
|
||||
| TXT | @ | `v=spf1 include:_spf.google.com ~all` | Auto |
|
||||
|
||||
#### MX Records
|
||||
| Type | Name | Content | Priority | TTL |
|
||||
|------|------|---------|----------|-----|
|
||||
| MX | @ | aspmx.l.google.com | 1 | Auto |
|
||||
| MX | @ | alt1.aspmx.l.google.com | 5 | Auto |
|
||||
| MX | @ | alt2.aspmx.l.google.com | 5 | Auto |
|
||||
|
||||
### Step 4.4: Verify Records
|
||||
|
||||
```bash
|
||||
# Check DNS resolution
|
||||
dig blackroad.systems
|
||||
dig www.blackroad.systems
|
||||
dig api.blackroad.systems
|
||||
|
||||
# Or use Cloudflare dashboard DNS checker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Configure SSL/TLS
|
||||
|
||||
### Step 5.1: Set Encryption Mode
|
||||
|
||||
1. In Cloudflare dashboard, go to **SSL/TLS**
|
||||
2. Set **Encryption mode** to **Full (strict)**
|
||||
- This ensures encryption between Cloudflare and Railway
|
||||
- Railway automatically provides SSL certificates
|
||||
|
||||
**⚠️ Important:** Do NOT use "Flexible" mode (insecure)
|
||||
|
||||
### Step 5.2: Enable Always Use HTTPS
|
||||
|
||||
1. Go to **SSL/TLS** → **Edge Certificates**
|
||||
2. Enable **Always Use HTTPS**
|
||||
- This redirects all HTTP traffic to HTTPS
|
||||
3. Enable **Automatic HTTPS Rewrites**
|
||||
- Fixes mixed content warnings
|
||||
|
||||
### Step 5.3: Enable HSTS (Optional but Recommended)
|
||||
|
||||
1. Still in **Edge Certificates**
|
||||
2. Enable **HTTP Strict Transport Security (HSTS)**
|
||||
3. Configuration:
|
||||
- **Max Age:** 6 months (15768000 seconds)
|
||||
- **Include subdomains:** ✅ Enabled
|
||||
- **Preload:** ❌ Disabled (enable later when stable)
|
||||
- **No-Sniff header:** ✅ Enabled
|
||||
|
||||
**⚠️ Warning:** HSTS is irreversible for the max-age period. Only enable when confident.
|
||||
|
||||
### Step 5.4: Enable TLS 1.3
|
||||
|
||||
1. Go to **SSL/TLS** → **Edge Certificates**
|
||||
2. **Minimum TLS Version:** Set to **TLS 1.2** (or 1.3 if supported)
|
||||
3. **TLS 1.3:** ✅ Enabled
|
||||
|
||||
### Step 5.5: Verify SSL Configuration
|
||||
|
||||
1. Visit: `https://blackroad.systems`
|
||||
2. Click the padlock icon in browser
|
||||
3. Verify certificate is valid and issued by Cloudflare
|
||||
4. Check expiry date (should auto-renew)
|
||||
|
||||
**Test with SSL Labs:**
|
||||
```
|
||||
https://www.ssllabs.com/ssltest/analyze.html?d=blackroad.systems
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Optimize Performance
|
||||
|
||||
### Step 6.1: Configure Caching
|
||||
|
||||
1. Go to **Caching** → **Configuration**
|
||||
2. **Caching Level:** Standard
|
||||
3. **Browser Cache TTL:** Respect Existing Headers
|
||||
|
||||
### Step 6.2: Enable Auto Minify
|
||||
|
||||
1. Go to **Speed** → **Optimization**
|
||||
2. Enable **Auto Minify**:
|
||||
- ✅ JavaScript
|
||||
- ✅ CSS
|
||||
- ✅ HTML
|
||||
|
||||
### Step 6.3: Enable Brotli Compression
|
||||
|
||||
1. Still in **Speed** → **Optimization**
|
||||
2. Enable **Brotli** compression (better than gzip)
|
||||
|
||||
### Step 6.4: Create Page Rules
|
||||
|
||||
1. Go to **Rules** → **Page Rules**
|
||||
2. Create rule for API bypass:
|
||||
|
||||
**Rule 1: API Cache Bypass**
|
||||
```
|
||||
URL: *blackroad.systems/api/*
|
||||
Settings:
|
||||
- Cache Level: Bypass
|
||||
```
|
||||
|
||||
**Rule 2: WWW Redirect**
|
||||
```
|
||||
URL: www.blackroad.systems/*
|
||||
Settings:
|
||||
- Forwarding URL: 301 redirect to https://blackroad.systems/$1
|
||||
```
|
||||
|
||||
**Note:** Free plan allows 3 page rules. Use them wisely!
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Verify Migration
|
||||
|
||||
### Step 7.1: DNS Verification
|
||||
|
||||
```bash
|
||||
# Check DNS propagation
|
||||
dig blackroad.systems
|
||||
|
||||
# Check with multiple tools
|
||||
dig @8.8.8.8 blackroad.systems
|
||||
dig @1.1.1.1 blackroad.systems
|
||||
|
||||
# Or use online tool
|
||||
# https://dnschecker.org
|
||||
```
|
||||
|
||||
**Expected results:**
|
||||
- Should resolve to Cloudflare IP addresses
|
||||
- CNAME records should point to Railway
|
||||
- Nameservers should be Cloudflare
|
||||
|
||||
### Step 7.2: HTTP/HTTPS Verification
|
||||
|
||||
```bash
|
||||
# Test HTTP → HTTPS redirect
|
||||
curl -I http://blackroad.systems
|
||||
# Should return: 301 Moved Permanently
|
||||
# Location: https://blackroad.systems/
|
||||
|
||||
# Test HTTPS
|
||||
curl -I https://blackroad.systems
|
||||
# Should return: 200 OK
|
||||
|
||||
# Test WWW → apex redirect
|
||||
curl -I https://www.blackroad.systems
|
||||
# Should redirect to https://blackroad.systems
|
||||
```
|
||||
|
||||
### Step 7.3: SSL Certificate Check
|
||||
|
||||
```bash
|
||||
# Check SSL certificate
|
||||
openssl s_client -connect blackroad.systems:443 -servername blackroad.systems
|
||||
|
||||
# Look for:
|
||||
# - Issuer: Cloudflare
|
||||
# - Valid dates
|
||||
# - No errors
|
||||
```
|
||||
|
||||
### Step 7.4: Application Functionality
|
||||
|
||||
1. Visit `https://blackroad.systems`
|
||||
2. Test all major features:
|
||||
- [ ] Page loads correctly
|
||||
- [ ] No mixed content warnings
|
||||
- [ ] API calls work
|
||||
- [ ] Authentication works
|
||||
- [ ] Static assets load (CSS, JS, images)
|
||||
|
||||
### Step 7.5: Automated Validation
|
||||
|
||||
```bash
|
||||
# Use the validation script
|
||||
cd /path/to/BlackRoad-Operating-System
|
||||
python scripts/cloudflare/validate_dns.py --domain blackroad.systems
|
||||
|
||||
# This checks:
|
||||
# - DNS resolution
|
||||
# - SSL certificate validity
|
||||
# - HTTP accessibility
|
||||
# - Redirect configuration
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: Automation Setup (Optional)
|
||||
|
||||
### Step 8.1: Install Script Dependencies
|
||||
|
||||
```bash
|
||||
# Navigate to project
|
||||
cd /path/to/BlackRoad-Operating-System
|
||||
|
||||
# Install Python dependencies
|
||||
pip install -r scripts/cloudflare/requirements.txt
|
||||
```
|
||||
|
||||
### Step 8.2: Set Up Environment Variables
|
||||
|
||||
```bash
|
||||
# Create .env file (DO NOT COMMIT)
|
||||
cat >> .env << EOF
|
||||
CF_API_TOKEN=your-cloudflare-api-token
|
||||
CF_ZONE_ID=your-zone-id
|
||||
EOF
|
||||
|
||||
# Or add to shell profile
|
||||
echo 'export CF_API_TOKEN="your-token"' >> ~/.bashrc
|
||||
echo 'export CF_ZONE_ID="your-zone-id"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
### Step 8.3: Update domains.yaml
|
||||
|
||||
Edit `ops/domains.yaml` to reflect your Cloudflare configuration:
|
||||
|
||||
```yaml
|
||||
domains:
|
||||
- name: "blackroad.systems"
|
||||
type: "root"
|
||||
provider: "cloudflare"
|
||||
mode: "dns"
|
||||
record:
|
||||
type: "CNAME"
|
||||
value: "your-actual-railway-app.up.railway.app"
|
||||
proxied: true
|
||||
|
||||
- name: "blackroad.ai"
|
||||
type: "root"
|
||||
provider: "cloudflare"
|
||||
mode: "dns"
|
||||
record:
|
||||
type: "CNAME"
|
||||
value: "blackroad.systems"
|
||||
proxied: true
|
||||
```
|
||||
|
||||
### Step 8.4: Test Automation
|
||||
|
||||
```bash
|
||||
# Dry run (shows what would change)
|
||||
python scripts/cloudflare/sync_dns.py --dry-run
|
||||
|
||||
# Apply changes
|
||||
python scripts/cloudflare/sync_dns.py
|
||||
|
||||
# Validate
|
||||
python scripts/cloudflare/validate_dns.py --all
|
||||
```
|
||||
|
||||
### Step 8.5: Set Up GitHub Actions (Optional)
|
||||
|
||||
Add secrets to GitHub:
|
||||
|
||||
```bash
|
||||
gh secret set CF_API_TOKEN
|
||||
gh secret set CF_ZONE_ID
|
||||
```
|
||||
|
||||
Create workflow file (`.github/workflows/sync-cloudflare-dns.yml`):
|
||||
|
||||
```yaml
|
||||
name: Sync Cloudflare DNS
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ops/domains.yaml'
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- run: pip install -r scripts/cloudflare/requirements.txt
|
||||
- run: python scripts/cloudflare/sync_dns.py
|
||||
env:
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: DNS Not Resolving
|
||||
|
||||
**Symptoms:** `dig blackroad.systems` returns no results
|
||||
|
||||
**Causes:**
|
||||
- Nameservers not updated at GoDaddy
|
||||
- DNS propagation not complete
|
||||
- Records not configured in Cloudflare
|
||||
|
||||
**Solutions:**
|
||||
1. Check nameservers at GoDaddy
|
||||
2. Wait 5-60 minutes for propagation
|
||||
3. Check Cloudflare zone status (should be "Active")
|
||||
4. Verify DNS records exist in Cloudflare
|
||||
|
||||
### Issue: SSL Certificate Errors
|
||||
|
||||
**Symptoms:** Browser shows "Not Secure" or certificate warning
|
||||
|
||||
**Causes:**
|
||||
- SSL/TLS mode incorrect
|
||||
- Railway app doesn't have valid certificate
|
||||
- Certificate provisioning in progress
|
||||
|
||||
**Solutions:**
|
||||
1. Set SSL/TLS mode to "Full (strict)"
|
||||
2. Verify Railway app has SSL
|
||||
3. Wait 5-10 minutes for certificate provisioning
|
||||
4. Clear browser cache and retry
|
||||
|
||||
### Issue: Site Not Loading (403/502 Errors)
|
||||
|
||||
**Symptoms:** Site returns 403 Forbidden or 502 Bad Gateway
|
||||
|
||||
**Causes:**
|
||||
- Railway app not running
|
||||
- Incorrect CNAME target
|
||||
- Cloudflare firewall blocking
|
||||
|
||||
**Solutions:**
|
||||
1. Check Railway app status and logs
|
||||
2. Verify CNAME points to correct Railway URL
|
||||
3. Check Cloudflare firewall rules
|
||||
4. Disable Cloudflare proxy temporarily (DNS-only) to test
|
||||
|
||||
### Issue: Mixed Content Warnings
|
||||
|
||||
**Symptoms:** Some assets load as insecure (http://)
|
||||
|
||||
**Causes:**
|
||||
- Hard-coded HTTP URLs in code
|
||||
- External resources using HTTP
|
||||
|
||||
**Solutions:**
|
||||
1. Enable "Automatic HTTPS Rewrites" in Cloudflare
|
||||
2. Update hard-coded URLs to HTTPS
|
||||
3. Use protocol-relative URLs: `//example.com/asset.js`
|
||||
|
||||
### Issue: Email Not Working
|
||||
|
||||
**Symptoms:** Emails not sending/receiving
|
||||
|
||||
**Causes:**
|
||||
- MX records not migrated
|
||||
- SPF/DKIM records missing
|
||||
|
||||
**Solutions:**
|
||||
1. Add MX records in Cloudflare (from Phase 4.3)
|
||||
2. Add SPF TXT record
|
||||
3. Add DKIM records if using custom email
|
||||
4. Verify with: `dig MX blackroad.systems`
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If you need to revert to GoDaddy DNS:
|
||||
|
||||
### Quick Rollback
|
||||
|
||||
1. Go to GoDaddy → Domains → blackroad.systems
|
||||
2. Nameservers → Change to **GoDaddy defaults**
|
||||
3. Wait 5-60 minutes for propagation
|
||||
4. Site will revert to GoDaddy DNS
|
||||
|
||||
**⚠️ Note:** You can keep the Cloudflare account and try again later.
|
||||
|
||||
### Gradual Rollback
|
||||
|
||||
If experiencing issues but want to keep trying:
|
||||
|
||||
1. In Cloudflare, change proxy status to **DNS Only** (gray cloud)
|
||||
2. This bypasses Cloudflare's proxy but keeps DNS
|
||||
3. Troubleshoot issues
|
||||
4. Re-enable proxy when fixed
|
||||
|
||||
---
|
||||
|
||||
## Next Steps After Migration
|
||||
|
||||
### For All Remaining Domains
|
||||
|
||||
Repeat the process for:
|
||||
- blackroad.ai
|
||||
- blackroad.network
|
||||
- blackroad.me
|
||||
- lucidia.earth
|
||||
- aliceqi.com
|
||||
- blackroadqi.com
|
||||
- roadwallet.com
|
||||
- aliceos.io
|
||||
- blackroadquantum.com
|
||||
|
||||
**Pro tip:** After doing blackroad.systems, the others are easier!
|
||||
|
||||
### Monitoring and Maintenance
|
||||
|
||||
1. **Set up monitoring:**
|
||||
- Uptime monitoring (UptimeRobot, Pingdom)
|
||||
- SSL certificate expiry monitoring
|
||||
- Performance monitoring (Cloudflare Analytics)
|
||||
|
||||
2. **Review quarterly:**
|
||||
- DNS records (remove unused)
|
||||
- Page rules and caching
|
||||
- Security settings
|
||||
- Analytics and performance
|
||||
|
||||
3. **Stay updated:**
|
||||
- Review Cloudflare changelog
|
||||
- Test new features in sandbox
|
||||
- Keep API tokens rotated
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
Use this to track your progress:
|
||||
|
||||
### Pre-Migration
|
||||
- [ ] GoDaddy credentials ready
|
||||
- [ ] Current DNS records documented
|
||||
- [ ] Railway hostname confirmed
|
||||
- [ ] Cloudflare account created
|
||||
|
||||
### Cloudflare Setup
|
||||
- [ ] API token generated and saved
|
||||
- [ ] Domain added to Cloudflare
|
||||
- [ ] DNS records scanned
|
||||
- [ ] Nameservers noted
|
||||
|
||||
### GoDaddy Update
|
||||
- [ ] Nameservers updated at GoDaddy
|
||||
- [ ] Change confirmed
|
||||
- [ ] Propagation completed (zone shows "Active")
|
||||
|
||||
### DNS Configuration
|
||||
- [ ] Root domain CNAME added
|
||||
- [ ] WWW subdomain added
|
||||
- [ ] API subdomain added
|
||||
- [ ] OS subdomain added
|
||||
- [ ] Email records added (if applicable)
|
||||
|
||||
### SSL/TLS
|
||||
- [ ] Encryption mode set to Full (strict)
|
||||
- [ ] Always Use HTTPS enabled
|
||||
- [ ] Automatic HTTPS Rewrites enabled
|
||||
- [ ] HSTS configured (optional)
|
||||
- [ ] TLS 1.3 enabled
|
||||
|
||||
### Performance
|
||||
- [ ] Auto Minify enabled
|
||||
- [ ] Brotli compression enabled
|
||||
- [ ] Page rules configured
|
||||
- [ ] Caching configured
|
||||
|
||||
### Verification
|
||||
- [ ] DNS resolution verified
|
||||
- [ ] SSL certificate valid
|
||||
- [ ] HTTP → HTTPS redirect working
|
||||
- [ ] WWW → apex redirect working
|
||||
- [ ] Site accessible and functional
|
||||
- [ ] API endpoints working
|
||||
- [ ] Email working (if applicable)
|
||||
|
||||
### Automation (Optional)
|
||||
- [ ] Python dependencies installed
|
||||
- [ ] Environment variables set
|
||||
- [ ] domains.yaml updated
|
||||
- [ ] Automation scripts tested
|
||||
- [ ] GitHub Actions configured (optional)
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
### Documentation
|
||||
- [Cloudflare DNS Blueprint](../infra/cloudflare/CLOUDFLARE_DNS_BLUEPRINT.md)
|
||||
- [Scripts README](../scripts/cloudflare/README.md)
|
||||
- [Domain Configuration](../ops/domains.yaml)
|
||||
|
||||
### External Links
|
||||
- [Cloudflare Dashboard](https://dash.cloudflare.com)
|
||||
- [Cloudflare API Docs](https://developers.cloudflare.com/api/)
|
||||
- [DNS Checker Tool](https://dnschecker.org)
|
||||
- [SSL Labs Test](https://www.ssllabs.com/ssltest/)
|
||||
- [Railway Dashboard](https://railway.app/dashboard)
|
||||
|
||||
### Support
|
||||
- Cloudflare Community: https://community.cloudflare.com/
|
||||
- Railway Discord: https://discord.gg/railway
|
||||
- BlackRoad GitHub Issues: https://github.com/blackboxprogramming/BlackRoad-Operating-System/issues
|
||||
|
||||
---
|
||||
|
||||
**Congratulations!** 🎉
|
||||
|
||||
You've successfully migrated to Cloudflare DNS. Your sites now benefit from:
|
||||
- Global CDN and faster performance
|
||||
- Free SSL with auto-renewal
|
||||
- DDoS protection
|
||||
- Advanced security features
|
||||
- Better analytics and insights
|
||||
|
||||
**Questions or issues?** Check the troubleshooting section or open a GitHub issue.
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-11-18
|
||||
**Maintainer:** BlackRoad DevOps Team
|
||||
**Version:** 1.0
|
||||
313
scripts/cloudflare/README.md
Normal file
313
scripts/cloudflare/README.md
Normal file
@@ -0,0 +1,313 @@
|
||||
# Cloudflare DNS Management Scripts
|
||||
|
||||
This directory contains automation scripts for managing BlackRoad domains via the Cloudflare API.
|
||||
|
||||
## Scripts
|
||||
|
||||
### `sync_dns.py`
|
||||
Synchronizes DNS records from `ops/domains.yaml` to Cloudflare. Handles creating new records and updating existing ones.
|
||||
|
||||
**Features:**
|
||||
- Automated DNS record synchronization
|
||||
- Dry-run mode to preview changes
|
||||
- Colored output for easy scanning
|
||||
- Support for multiple record types (A, CNAME, MX, TXT)
|
||||
- Automatic proxying configuration
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Set environment variables
|
||||
export CF_API_TOKEN="your-cloudflare-api-token"
|
||||
export CF_ZONE_ID="your-zone-id"
|
||||
|
||||
# Preview changes (dry run)
|
||||
python scripts/cloudflare/sync_dns.py --dry-run
|
||||
|
||||
# Apply changes
|
||||
python scripts/cloudflare/sync_dns.py
|
||||
|
||||
# Or with command-line arguments
|
||||
python scripts/cloudflare/sync_dns.py \
|
||||
--token "your-token" \
|
||||
--zone-id "your-zone-id" \
|
||||
--zone-name "blackroad.systems"
|
||||
```
|
||||
|
||||
### `validate_dns.py`
|
||||
Validates DNS configuration and checks propagation status across the internet.
|
||||
|
||||
**Features:**
|
||||
- DNS resolution verification
|
||||
- SSL certificate validation
|
||||
- HTTP/HTTPS accessibility testing
|
||||
- Redirect verification (www → apex, HTTP → HTTPS)
|
||||
- Support for checking multiple domains
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Check single domain (default: blackroad.systems)
|
||||
python scripts/cloudflare/validate_dns.py
|
||||
|
||||
# Check specific domain
|
||||
python scripts/cloudflare/validate_dns.py --domain blackroad.ai
|
||||
|
||||
# Check all BlackRoad domains
|
||||
python scripts/cloudflare/validate_dns.py --all
|
||||
|
||||
# DNS-only check (skip SSL and HTTP)
|
||||
python scripts/cloudflare/validate_dns.py --dns-only
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
- Python 3.8 or higher
|
||||
- Cloudflare account with API token
|
||||
- Domain(s) added to Cloudflare
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
# Install required packages
|
||||
pip install -r scripts/cloudflare/requirements.txt
|
||||
|
||||
# Or install individually
|
||||
pip install requests pyyaml dnspython colorama
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Getting Cloudflare API Token
|
||||
|
||||
1. Log in to [Cloudflare Dashboard](https://dash.cloudflare.com)
|
||||
2. Go to **My Profile** → **API Tokens**
|
||||
3. Click **Create Token**
|
||||
4. Use the **Edit zone DNS** template
|
||||
5. Select the zones you want to manage
|
||||
6. Create token and copy it
|
||||
|
||||
### Getting Zone ID
|
||||
|
||||
1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com)
|
||||
2. Select your domain (e.g., `blackroad.systems`)
|
||||
3. Scroll down to **API** section on the right sidebar
|
||||
4. Copy the **Zone ID**
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# Add to your shell profile (~/.bashrc, ~/.zshrc, etc.)
|
||||
export CF_API_TOKEN="your-cloudflare-api-token-here"
|
||||
export CF_ZONE_ID="your-zone-id-here"
|
||||
|
||||
# Or create a .env file (DO NOT COMMIT THIS)
|
||||
echo "CF_API_TOKEN=your-token" >> .env
|
||||
echo "CF_ZONE_ID=your-zone-id" >> .env
|
||||
source .env
|
||||
```
|
||||
|
||||
## Domain Configuration
|
||||
|
||||
DNS records are defined in `ops/domains.yaml`. Example:
|
||||
|
||||
```yaml
|
||||
domains:
|
||||
- name: "blackroad.systems"
|
||||
type: "root"
|
||||
provider: "cloudflare"
|
||||
mode: "dns"
|
||||
record:
|
||||
type: "CNAME"
|
||||
value: "blackroad-os-production.up.railway.app"
|
||||
ttl: 1 # Auto
|
||||
proxied: true
|
||||
|
||||
- name: "api.blackroad.systems"
|
||||
type: "subdomain"
|
||||
provider: "cloudflare"
|
||||
mode: "dns"
|
||||
record:
|
||||
type: "CNAME"
|
||||
value: "blackroad-os-production.up.railway.app"
|
||||
proxied: true
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Initial Migration
|
||||
|
||||
1. **Add domain to Cloudflare** (manual step via dashboard)
|
||||
```
|
||||
- Go to Cloudflare → Add a site
|
||||
- Enter domain name
|
||||
- Choose Free plan
|
||||
- Follow setup wizard
|
||||
```
|
||||
|
||||
2. **Update nameservers at registrar** (GoDaddy, etc.)
|
||||
```
|
||||
- Copy nameservers from Cloudflare
|
||||
- Update at domain registrar
|
||||
- Wait 5-60 minutes for propagation
|
||||
```
|
||||
|
||||
3. **Configure DNS records**
|
||||
```bash
|
||||
# Update ops/domains.yaml with your records
|
||||
|
||||
# Preview changes
|
||||
python scripts/cloudflare/sync_dns.py --dry-run
|
||||
|
||||
# Apply changes
|
||||
python scripts/cloudflare/sync_dns.py
|
||||
```
|
||||
|
||||
4. **Verify configuration**
|
||||
```bash
|
||||
# Check DNS propagation
|
||||
python scripts/cloudflare/validate_dns.py
|
||||
|
||||
# Or check specific domain
|
||||
python scripts/cloudflare/validate_dns.py --domain blackroad.systems
|
||||
```
|
||||
|
||||
### Regular Updates
|
||||
|
||||
When updating DNS records:
|
||||
|
||||
1. Edit `ops/domains.yaml`
|
||||
2. Run dry-run to preview: `python scripts/cloudflare/sync_dns.py --dry-run`
|
||||
3. Apply changes: `python scripts/cloudflare/sync_dns.py`
|
||||
4. Validate: `python scripts/cloudflare/validate_dns.py`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### DNS Not Resolving
|
||||
|
||||
**Problem:** Domain doesn't resolve
|
||||
|
||||
**Solutions:**
|
||||
```bash
|
||||
# Check DNS with dig
|
||||
dig blackroad.systems
|
||||
|
||||
# Check with validation script
|
||||
python scripts/cloudflare/validate_dns.py --domain blackroad.systems
|
||||
|
||||
# Wait for propagation (5-60 minutes after nameserver change)
|
||||
```
|
||||
|
||||
### API Authentication Errors
|
||||
|
||||
**Problem:** `401 Unauthorized` or `403 Forbidden`
|
||||
|
||||
**Solutions:**
|
||||
- Verify API token is correct
|
||||
- Check token has "Edit DNS" permission for the zone
|
||||
- Ensure token hasn't expired
|
||||
- Verify zone ID is correct
|
||||
|
||||
### Script Errors
|
||||
|
||||
**Problem:** Import errors or missing dependencies
|
||||
|
||||
**Solutions:**
|
||||
```bash
|
||||
# Install all dependencies
|
||||
pip install -r scripts/cloudflare/requirements.txt
|
||||
|
||||
# Or install missing package
|
||||
pip install <package-name>
|
||||
```
|
||||
|
||||
### Configuration Drift
|
||||
|
||||
**Problem:** Cloudflare records don't match `domains.yaml`
|
||||
|
||||
**Solutions:**
|
||||
```bash
|
||||
# Run sync to update Cloudflare to match config
|
||||
python scripts/cloudflare/sync_dns.py
|
||||
|
||||
# Or manually update records in Cloudflare dashboard
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Never commit API tokens**
|
||||
- Add `.env` to `.gitignore`
|
||||
- Use environment variables
|
||||
- Rotate tokens periodically
|
||||
|
||||
2. **Use scoped tokens**
|
||||
- Create tokens with minimum required permissions
|
||||
- Use zone-specific tokens when possible
|
||||
- Avoid using Global API Key
|
||||
|
||||
3. **Audit regularly**
|
||||
- Review DNS records monthly
|
||||
- Check token usage in Cloudflare dashboard
|
||||
- Remove unused tokens
|
||||
|
||||
## Integration with CI/CD
|
||||
|
||||
### GitHub Actions Example
|
||||
|
||||
```yaml
|
||||
name: Sync DNS Records
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ops/domains.yaml'
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
sync-dns:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r scripts/cloudflare/requirements.txt
|
||||
|
||||
- name: Sync DNS records
|
||||
env:
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
|
||||
run: |
|
||||
python scripts/cloudflare/sync_dns.py
|
||||
```
|
||||
|
||||
Add secrets to GitHub:
|
||||
```bash
|
||||
gh secret set CF_API_TOKEN
|
||||
gh secret set CF_ZONE_ID
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Cloudflare API Documentation](https://developers.cloudflare.com/api/)
|
||||
- [Cloudflare DNS Documentation](https://developers.cloudflare.com/dns/)
|
||||
- [DNS Blueprint](../../infra/cloudflare/CLOUDFLARE_DNS_BLUEPRINT.md)
|
||||
- [Domain Configuration](../../ops/domains.yaml)
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- Check the [CLOUDFLARE_DNS_BLUEPRINT.md](../../infra/cloudflare/CLOUDFLARE_DNS_BLUEPRINT.md)
|
||||
- Review Cloudflare dashboard for zone status
|
||||
- Check script output for error messages
|
||||
- Verify API token permissions
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-11-18
|
||||
**Maintained by:** BlackRoad DevOps Team
|
||||
14
scripts/cloudflare/requirements.txt
Normal file
14
scripts/cloudflare/requirements.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
# Cloudflare DNS Management Scripts Requirements
|
||||
# Install with: pip install -r scripts/cloudflare/requirements.txt
|
||||
|
||||
# HTTP client for API requests
|
||||
requests>=2.31.0
|
||||
|
||||
# YAML parsing for domains.yaml
|
||||
PyYAML>=6.0.1
|
||||
|
||||
# DNS resolution verification
|
||||
dnspython>=2.4.2
|
||||
|
||||
# Colored terminal output
|
||||
colorama>=0.4.6
|
||||
359
scripts/cloudflare/sync_dns.py
Executable file
359
scripts/cloudflare/sync_dns.py
Executable file
@@ -0,0 +1,359 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Sync DNS records from ops/domains.yaml to Cloudflare
|
||||
|
||||
This script automates the migration and synchronization of DNS records
|
||||
from the domain configuration file to Cloudflare. It handles:
|
||||
- Creating new DNS records
|
||||
- Updating existing DNS records
|
||||
- Detecting and reporting configuration drift
|
||||
|
||||
Usage:
|
||||
export CF_API_TOKEN="your-cloudflare-api-token"
|
||||
export CF_ZONE_ID="your-zone-id" # For blackroad.systems
|
||||
python scripts/cloudflare/sync_dns.py
|
||||
|
||||
Or with command-line arguments:
|
||||
python scripts/cloudflare/sync_dns.py --zone-id <zone_id> --token <token>
|
||||
|
||||
Requirements:
|
||||
pip install requests pyyaml colorama
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import yaml
|
||||
import requests
|
||||
from typing import Dict, List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
from colorama import init, Fore, Style
|
||||
init()
|
||||
HAS_COLOR = True
|
||||
except ImportError:
|
||||
HAS_COLOR = False
|
||||
# Fallback to no colors
|
||||
class Fore:
|
||||
GREEN = RED = YELLOW = CYAN = RESET = ""
|
||||
class Style:
|
||||
BRIGHT = RESET_ALL = ""
|
||||
|
||||
# Configuration
|
||||
CF_API_BASE = "https://api.cloudflare.com/client/v4"
|
||||
DOMAINS_FILE = "ops/domains.yaml"
|
||||
|
||||
|
||||
def print_status(message: str, status: str = "info"):
|
||||
"""Print colored status messages"""
|
||||
if status == "success":
|
||||
prefix = f"{Fore.GREEN}✓{Fore.RESET}"
|
||||
elif status == "error":
|
||||
prefix = f"{Fore.RED}✗{Fore.RESET}"
|
||||
elif status == "warning":
|
||||
prefix = f"{Fore.YELLOW}⚠{Fore.RESET}"
|
||||
else:
|
||||
prefix = f"{Fore.CYAN}ℹ{Fore.RESET}"
|
||||
|
||||
print(f"{prefix} {message}")
|
||||
|
||||
|
||||
def get_api_headers(api_token: str) -> Dict[str, str]:
|
||||
"""Get headers for Cloudflare API requests"""
|
||||
return {
|
||||
"Authorization": f"Bearer {api_token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
|
||||
def load_domains() -> Dict:
|
||||
"""Load domain configuration from ops/domains.yaml"""
|
||||
try:
|
||||
with open(DOMAINS_FILE) as f:
|
||||
return yaml.safe_load(f)
|
||||
except FileNotFoundError:
|
||||
print_status(f"Error: {DOMAINS_FILE} not found", "error")
|
||||
sys.exit(1)
|
||||
except yaml.YAMLError as e:
|
||||
print_status(f"Error parsing {DOMAINS_FILE}: {e}", "error")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_existing_records(zone_id: str, api_token: str) -> List[Dict]:
|
||||
"""Fetch all DNS records for a zone"""
|
||||
url = f"{CF_API_BASE}/zones/{zone_id}/dns_records"
|
||||
headers = get_api_headers(api_token)
|
||||
|
||||
all_records = []
|
||||
page = 1
|
||||
per_page = 100
|
||||
|
||||
while True:
|
||||
params = {"page": page, "per_page": per_page}
|
||||
response = requests.get(url, headers=headers, params=params)
|
||||
|
||||
if response.status_code != 200:
|
||||
print_status(f"Error fetching DNS records: {response.text}", "error")
|
||||
sys.exit(1)
|
||||
|
||||
data = response.json()
|
||||
if not data.get("success"):
|
||||
print_status(f"API error: {data.get('errors')}", "error")
|
||||
sys.exit(1)
|
||||
|
||||
records = data.get("result", [])
|
||||
all_records.extend(records)
|
||||
|
||||
# Check if there are more pages
|
||||
result_info = data.get("result_info", {})
|
||||
if page * per_page >= result_info.get("total_count", 0):
|
||||
break
|
||||
|
||||
page += 1
|
||||
|
||||
return all_records
|
||||
|
||||
|
||||
def create_dns_record(zone_id: str, api_token: str, record: Dict) -> Dict:
|
||||
"""Create a DNS record"""
|
||||
url = f"{CF_API_BASE}/zones/{zone_id}/dns_records"
|
||||
headers = get_api_headers(api_token)
|
||||
|
||||
response = requests.post(url, headers=headers, json=record)
|
||||
|
||||
if response.status_code not in [200, 201]:
|
||||
print_status(f"Error creating DNS record: {response.text}", "error")
|
||||
return None
|
||||
|
||||
data = response.json()
|
||||
if not data.get("success"):
|
||||
print_status(f"API error: {data.get('errors')}", "error")
|
||||
return None
|
||||
|
||||
return data.get("result")
|
||||
|
||||
|
||||
def update_dns_record(zone_id: str, api_token: str, record_id: str, record: Dict) -> Dict:
|
||||
"""Update a DNS record"""
|
||||
url = f"{CF_API_BASE}/zones/{zone_id}/dns_records/{record_id}"
|
||||
headers = get_api_headers(api_token)
|
||||
|
||||
response = requests.put(url, headers=headers, json=record)
|
||||
|
||||
if response.status_code != 200:
|
||||
print_status(f"Error updating DNS record: {response.text}", "error")
|
||||
return None
|
||||
|
||||
data = response.json()
|
||||
if not data.get("success"):
|
||||
print_status(f"API error: {data.get('errors')}", "error")
|
||||
return None
|
||||
|
||||
return data.get("result")
|
||||
|
||||
|
||||
def normalize_record_name(name: str, zone_name: str) -> str:
|
||||
"""Normalize record name for comparison
|
||||
|
||||
Cloudflare returns full domain names (e.g., 'blackroad.systems' or 'www.blackroad.systems')
|
||||
while config may use '@' for apex or just subdomain names.
|
||||
"""
|
||||
if name == "@":
|
||||
return zone_name
|
||||
elif not name.endswith(zone_name):
|
||||
return f"{name}.{zone_name}"
|
||||
return name
|
||||
|
||||
|
||||
def records_match(config_record: Dict, cf_record: Dict, zone_name: str) -> bool:
|
||||
"""Check if a config record matches a Cloudflare record"""
|
||||
config_name = normalize_record_name(config_record.get("name", ""), zone_name)
|
||||
cf_name = cf_record.get("name", "")
|
||||
|
||||
return (
|
||||
config_record.get("type") == cf_record.get("type") and
|
||||
config_name == cf_name and
|
||||
config_record.get("content") == cf_record.get("content")
|
||||
)
|
||||
|
||||
|
||||
def sync_records(zone_id: str, api_token: str, zone_name: str, dry_run: bool = False):
|
||||
"""Sync DNS records from domains.yaml to Cloudflare"""
|
||||
print_status(f"Starting DNS sync for zone: {zone_name}")
|
||||
print_status(f"Zone ID: {zone_id}")
|
||||
|
||||
if dry_run:
|
||||
print_status("DRY RUN MODE - No changes will be made", "warning")
|
||||
|
||||
# Load configuration
|
||||
config = load_domains()
|
||||
|
||||
# Get existing records from Cloudflare
|
||||
print_status("Fetching existing DNS records from Cloudflare...")
|
||||
existing = get_existing_records(zone_id, api_token)
|
||||
print_status(f"Found {len(existing)} existing DNS records")
|
||||
|
||||
# Build index of existing records
|
||||
existing_index = {}
|
||||
for record in existing:
|
||||
key = f"{record['type']}:{record['name']}"
|
||||
existing_index[key] = record
|
||||
|
||||
# Process domains from config
|
||||
created = 0
|
||||
updated = 0
|
||||
skipped = 0
|
||||
errors = 0
|
||||
|
||||
for domain in config.get("domains", []):
|
||||
# Only process domains configured for Cloudflare DNS mode
|
||||
if domain.get("provider") != "cloudflare" or domain.get("mode") != "dns":
|
||||
continue
|
||||
|
||||
# Skip if no record config
|
||||
if "record" not in domain:
|
||||
print_status(f"Skipping {domain.get('name')}: No record configuration", "warning")
|
||||
continue
|
||||
|
||||
# Extract domain name (handle both root and subdomain)
|
||||
domain_name = domain.get("name", "")
|
||||
|
||||
# Build record data
|
||||
record_config = domain["record"]
|
||||
record_type = record_config.get("type", "CNAME")
|
||||
record_value = record_config.get("value", "")
|
||||
|
||||
# Determine record name for Cloudflare
|
||||
# For root domains matching zone name, use "@"
|
||||
if domain_name == zone_name:
|
||||
record_name = "@"
|
||||
else:
|
||||
record_name = domain_name
|
||||
|
||||
record_data = {
|
||||
"type": record_type,
|
||||
"name": record_name,
|
||||
"content": record_value,
|
||||
"ttl": record_config.get("ttl", 1), # 1 = Auto
|
||||
"proxied": record_config.get("proxied", True)
|
||||
}
|
||||
|
||||
# For MX records, add priority
|
||||
if record_type == "MX":
|
||||
record_data["priority"] = record_config.get("priority", 10)
|
||||
|
||||
# Build key for lookup
|
||||
full_name = normalize_record_name(record_name, zone_name)
|
||||
key = f"{record_type}:{full_name}"
|
||||
|
||||
# Check if record exists
|
||||
if key in existing_index:
|
||||
existing_record = existing_index[key]
|
||||
|
||||
# Check if update is needed
|
||||
needs_update = (
|
||||
existing_record.get("content") != record_value or
|
||||
existing_record.get("proxied") != record_data.get("proxied")
|
||||
)
|
||||
|
||||
if needs_update:
|
||||
print_status(f"Updating: {key} -> {record_value}", "warning")
|
||||
if not dry_run:
|
||||
result = update_dns_record(zone_id, api_token, existing_record["id"], record_data)
|
||||
if result:
|
||||
updated += 1
|
||||
print_status(f" Updated successfully", "success")
|
||||
else:
|
||||
errors += 1
|
||||
else:
|
||||
print_status(f" [DRY RUN] Would update", "info")
|
||||
updated += 1
|
||||
else:
|
||||
print_status(f"Unchanged: {key}", "info")
|
||||
skipped += 1
|
||||
else:
|
||||
# Create new record
|
||||
print_status(f"Creating: {key} -> {record_value}", "warning")
|
||||
if not dry_run:
|
||||
result = create_dns_record(zone_id, api_token, record_data)
|
||||
if result:
|
||||
created += 1
|
||||
print_status(f" Created successfully", "success")
|
||||
else:
|
||||
errors += 1
|
||||
else:
|
||||
print_status(f" [DRY RUN] Would create", "info")
|
||||
created += 1
|
||||
|
||||
# Summary
|
||||
print("\n" + "="*60)
|
||||
print_status("DNS Sync Complete!", "success")
|
||||
print("="*60)
|
||||
print(f" {Fore.GREEN}Created:{Fore.RESET} {created}")
|
||||
print(f" {Fore.YELLOW}Updated:{Fore.RESET} {updated}")
|
||||
print(f" {Fore.CYAN}Unchanged:{Fore.RESET} {skipped}")
|
||||
print(f" {Fore.RED}Errors:{Fore.RESET} {errors}")
|
||||
print("="*60)
|
||||
|
||||
if dry_run:
|
||||
print_status("This was a DRY RUN - no actual changes were made", "warning")
|
||||
print_status("Run without --dry-run to apply changes", "info")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Sync DNS records from ops/domains.yaml to Cloudflare"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--token",
|
||||
help="Cloudflare API token (or set CF_API_TOKEN env var)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zone-id",
|
||||
help="Cloudflare zone ID (or set CF_ZONE_ID env var)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zone-name",
|
||||
default="blackroad.systems",
|
||||
help="Zone name (default: blackroad.systems)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Show what would be changed without making actual changes"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get credentials
|
||||
api_token = args.token or os.getenv("CF_API_TOKEN")
|
||||
zone_id = args.zone_id or os.getenv("CF_ZONE_ID")
|
||||
|
||||
if not api_token:
|
||||
print_status("Error: CF_API_TOKEN environment variable or --token argument required", "error")
|
||||
print_status("Get your token at: https://dash.cloudflare.com/profile/api-tokens", "info")
|
||||
sys.exit(1)
|
||||
|
||||
if not zone_id:
|
||||
print_status("Error: CF_ZONE_ID environment variable or --zone-id argument required", "error")
|
||||
print_status("Find your zone ID in the Cloudflare dashboard", "info")
|
||||
sys.exit(1)
|
||||
|
||||
# Run sync
|
||||
try:
|
||||
sync_records(zone_id, api_token, args.zone_name, dry_run=args.dry_run)
|
||||
except KeyboardInterrupt:
|
||||
print("\n")
|
||||
print_status("Interrupted by user", "warning")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print_status(f"Unexpected error: {e}", "error")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
313
scripts/cloudflare/validate_dns.py
Executable file
313
scripts/cloudflare/validate_dns.py
Executable file
@@ -0,0 +1,313 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Validate DNS configuration and check propagation status
|
||||
|
||||
This script helps verify that DNS records have been properly configured
|
||||
and propagated across the internet. It performs:
|
||||
- DNS resolution checks
|
||||
- SSL certificate validation
|
||||
- HTTP/HTTPS accessibility tests
|
||||
- Redirect verification
|
||||
|
||||
Usage:
|
||||
python scripts/cloudflare/validate_dns.py
|
||||
python scripts/cloudflare/validate_dns.py --domain blackroad.systems
|
||||
python scripts/cloudflare/validate_dns.py --all # Check all domains
|
||||
|
||||
Requirements:
|
||||
pip install requests dnspython colorama
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import socket
|
||||
import ssl
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Optional
|
||||
import requests
|
||||
|
||||
try:
|
||||
import dns.resolver
|
||||
HAS_DNS = True
|
||||
except ImportError:
|
||||
HAS_DNS = False
|
||||
print("Warning: dnspython not installed. Install with: pip install dnspython")
|
||||
|
||||
try:
|
||||
from colorama import init, Fore, Style
|
||||
init()
|
||||
except ImportError:
|
||||
class Fore:
|
||||
GREEN = RED = YELLOW = CYAN = RESET = ""
|
||||
class Style:
|
||||
BRIGHT = RESET_ALL = ""
|
||||
|
||||
|
||||
def print_status(message: str, status: str = "info"):
|
||||
"""Print colored status messages"""
|
||||
if status == "success":
|
||||
prefix = f"{Fore.GREEN}✓{Fore.RESET}"
|
||||
elif status == "error":
|
||||
prefix = f"{Fore.RED}✗{Fore.RESET}"
|
||||
elif status == "warning":
|
||||
prefix = f"{Fore.YELLOW}⚠{Fore.RESET}"
|
||||
else:
|
||||
prefix = f"{Fore.CYAN}ℹ{Fore.RESET}"
|
||||
|
||||
print(f"{prefix} {message}")
|
||||
|
||||
|
||||
def check_dns_resolution(domain: str) -> Dict:
|
||||
"""Check DNS resolution for a domain"""
|
||||
result = {
|
||||
"domain": domain,
|
||||
"resolved": False,
|
||||
"ip_addresses": [],
|
||||
"cname": None,
|
||||
"error": None
|
||||
}
|
||||
|
||||
if not HAS_DNS:
|
||||
result["error"] = "dnspython not installed"
|
||||
return result
|
||||
|
||||
try:
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.timeout = 5
|
||||
resolver.lifetime = 5
|
||||
|
||||
# Try CNAME first
|
||||
try:
|
||||
cname_answers = resolver.resolve(domain, 'CNAME')
|
||||
if cname_answers:
|
||||
result["cname"] = str(cname_answers[0].target).rstrip('.')
|
||||
print_status(f" CNAME: {result['cname']}", "info")
|
||||
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
|
||||
pass
|
||||
|
||||
# Try A record
|
||||
try:
|
||||
answers = resolver.resolve(domain, 'A')
|
||||
result["ip_addresses"] = [str(rdata) for rdata in answers]
|
||||
result["resolved"] = True
|
||||
for ip in result["ip_addresses"]:
|
||||
print_status(f" A: {ip}", "info")
|
||||
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN) as e:
|
||||
result["error"] = str(e)
|
||||
|
||||
except Exception as e:
|
||||
result["error"] = str(e)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def check_ssl_certificate(domain: str) -> Dict:
|
||||
"""Check SSL certificate for a domain"""
|
||||
result = {
|
||||
"domain": domain,
|
||||
"valid": False,
|
||||
"issuer": None,
|
||||
"expires": None,
|
||||
"days_remaining": None,
|
||||
"error": None
|
||||
}
|
||||
|
||||
try:
|
||||
context = ssl.create_default_context()
|
||||
with socket.create_connection((domain, 443), timeout=10) as sock:
|
||||
with context.wrap_socket(sock, server_hostname=domain) as ssock:
|
||||
cert = ssock.getpeercert()
|
||||
|
||||
result["issuer"] = dict(x[0] for x in cert['issuer'])
|
||||
result["expires"] = cert['notAfter']
|
||||
|
||||
# Parse expiry date
|
||||
expire_date = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
|
||||
days_remaining = (expire_date - datetime.now()).days
|
||||
result["days_remaining"] = days_remaining
|
||||
result["valid"] = days_remaining > 0
|
||||
|
||||
print_status(f" Issuer: {result['issuer'].get('organizationName', 'Unknown')}", "info")
|
||||
print_status(f" Expires: {result['expires']} ({days_remaining} days)",
|
||||
"success" if days_remaining > 30 else "warning")
|
||||
|
||||
except Exception as e:
|
||||
result["error"] = str(e)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def check_http_accessibility(domain: str, check_redirects: bool = True) -> Dict:
|
||||
"""Check HTTP/HTTPS accessibility and redirects"""
|
||||
result = {
|
||||
"domain": domain,
|
||||
"http_accessible": False,
|
||||
"https_accessible": False,
|
||||
"redirects_to_https": False,
|
||||
"www_redirects": False,
|
||||
"status_code": None,
|
||||
"error": None
|
||||
}
|
||||
|
||||
try:
|
||||
# Check HTTP -> HTTPS redirect
|
||||
http_response = requests.get(f"http://{domain}", allow_redirects=True, timeout=10)
|
||||
result["http_accessible"] = True
|
||||
result["redirects_to_https"] = http_response.url.startswith("https://")
|
||||
|
||||
if result["redirects_to_https"]:
|
||||
print_status(f" HTTP → HTTPS redirect: ✓", "success")
|
||||
else:
|
||||
print_status(f" HTTP → HTTPS redirect: ✗", "warning")
|
||||
|
||||
except Exception as e:
|
||||
result["error"] = f"HTTP check failed: {e}"
|
||||
|
||||
try:
|
||||
# Check HTTPS accessibility
|
||||
https_response = requests.get(f"https://{domain}", timeout=10)
|
||||
result["https_accessible"] = https_response.status_code == 200
|
||||
result["status_code"] = https_response.status_code
|
||||
|
||||
if result["https_accessible"]:
|
||||
print_status(f" HTTPS accessible: ✓ (Status: {https_response.status_code})", "success")
|
||||
else:
|
||||
print_status(f" HTTPS status: {https_response.status_code}", "warning")
|
||||
|
||||
except Exception as e:
|
||||
if not result["error"]:
|
||||
result["error"] = f"HTTPS check failed: {e}"
|
||||
|
||||
# Check www redirect if requested
|
||||
if check_redirects and not domain.startswith("www."):
|
||||
try:
|
||||
www_response = requests.get(f"https://www.{domain}", allow_redirects=True, timeout=10)
|
||||
result["www_redirects"] = www_response.url == f"https://{domain}/" or www_response.url == f"https://{domain}"
|
||||
|
||||
if result["www_redirects"]:
|
||||
print_status(f" www → apex redirect: ✓", "success")
|
||||
else:
|
||||
print_status(f" www redirect: ✗ (goes to {www_response.url})", "warning")
|
||||
|
||||
except Exception as e:
|
||||
print_status(f" www redirect check failed: {e}", "info")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def validate_domain(domain: str, full_check: bool = True) -> bool:
|
||||
"""Validate a single domain"""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"{Style.BRIGHT}Validating: {domain}{Style.RESET_ALL}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
all_passed = True
|
||||
|
||||
# DNS Resolution
|
||||
print(f"\n{Fore.CYAN}[1/3] DNS Resolution{Fore.RESET}")
|
||||
dns_result = check_dns_resolution(domain)
|
||||
if dns_result["resolved"]:
|
||||
print_status(f"DNS resolved successfully", "success")
|
||||
else:
|
||||
print_status(f"DNS resolution failed: {dns_result.get('error', 'Unknown error')}", "error")
|
||||
all_passed = False
|
||||
|
||||
if not full_check:
|
||||
return all_passed
|
||||
|
||||
# SSL Certificate
|
||||
print(f"\n{Fore.CYAN}[2/3] SSL Certificate{Fore.RESET}")
|
||||
ssl_result = check_ssl_certificate(domain)
|
||||
if ssl_result["valid"]:
|
||||
print_status(f"SSL certificate is valid", "success")
|
||||
else:
|
||||
print_status(f"SSL certificate check failed: {ssl_result.get('error', 'Unknown error')}", "error")
|
||||
all_passed = False
|
||||
|
||||
# HTTP Accessibility
|
||||
print(f"\n{Fore.CYAN}[3/3] HTTP Accessibility{Fore.RESET}")
|
||||
http_result = check_http_accessibility(domain)
|
||||
if http_result["https_accessible"]:
|
||||
print_status(f"Site is accessible via HTTPS", "success")
|
||||
else:
|
||||
print_status(f"Site accessibility check failed: {http_result.get('error', 'Unknown error')}", "error")
|
||||
all_passed = False
|
||||
|
||||
# Summary
|
||||
print(f"\n{'='*60}")
|
||||
if all_passed:
|
||||
print_status(f"{domain}: All checks passed! ✓", "success")
|
||||
else:
|
||||
print_status(f"{domain}: Some checks failed", "warning")
|
||||
print(f"{'='*60}")
|
||||
|
||||
return all_passed
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Validate DNS configuration and check propagation status"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--domain",
|
||||
help="Domain to validate (default: blackroad.systems)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--all",
|
||||
action="store_true",
|
||||
help="Check all BlackRoad domains"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dns-only",
|
||||
action="store_true",
|
||||
help="Only check DNS resolution (skip SSL and HTTP checks)"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# List of BlackRoad domains
|
||||
all_domains = [
|
||||
"blackroad.systems",
|
||||
"blackroad.ai",
|
||||
"blackroad.network",
|
||||
"blackroad.me",
|
||||
"lucidia.earth",
|
||||
"aliceqi.com",
|
||||
"blackroadqi.com",
|
||||
"roadwallet.com",
|
||||
"aliceos.io",
|
||||
"blackroadquantum.com"
|
||||
]
|
||||
|
||||
if args.all:
|
||||
domains = all_domains
|
||||
elif args.domain:
|
||||
domains = [args.domain]
|
||||
else:
|
||||
domains = ["blackroad.systems"]
|
||||
|
||||
full_check = not args.dns_only
|
||||
all_passed = True
|
||||
|
||||
for domain in domains:
|
||||
passed = validate_domain(domain, full_check=full_check)
|
||||
if not passed:
|
||||
all_passed = False
|
||||
|
||||
# Final summary
|
||||
print(f"\n{'='*60}")
|
||||
print(f"{Style.BRIGHT}VALIDATION SUMMARY{Style.RESET_ALL}")
|
||||
print(f"{'='*60}")
|
||||
print(f"Domains checked: {len(domains)}")
|
||||
|
||||
if all_passed:
|
||||
print_status("All validations passed!", "success")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print_status("Some validations failed", "warning")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user