Secure Dynamic DNS with Cloudflare and 1Password
• DevOps, Security
Setting up dynamic DNS for home servers while keeping API credentials secure using 1Password service accounts.
The Challenge
Home internet connections typically have dynamic IP addresses that change periodically. If you're running services (like a media server) that need to be accessible via a domain name, you need a way to automatically update DNS records when your IP changes.
Traditional DDNS services work, but using Cloudflare gives you more control, better security features, and integration with your existing DNS setup.
Security First: Why Not Store API Keys on Disk?
Most DDNS scripts store API credentials in plain text files. This creates security risks:
- Credentials visible in process lists
- Accessible to any user with file system access
- Risk of accidental commits to version control
- No audit trail for credential access
The Solution: 1Password Service Accounts
1Password service accounts are designed for automated systems. They provide:
- Programmatic access to secrets without user interaction
- Audit logging of credential access
- Centralized credential management
- Easy credential rotation
Setup Steps
1. Create Cloudflare API Token
In Cloudflare dashboard → My Profile → API Tokens:
- Create Token → Custom token
- Permissions:
Zone:Zone:ReadZone:DNS:Edit
- Zone Resources: Include → Specific zone → your domain
- Copy the generated token
2. Store Credentials in 1Password
- Create a dedicated vault for automation credentials (e.g., "dev" vault) Note: Service accounts may not have access to your default "Personal" vault. Creating a separate vault specifically for automation credentials provides better security isolation.
- Create new Secure Note item named "cloudflare" in the dedicated vault
- Add fields:
zone_id: From Cloudflare dashboard sidebarapi_token: The token from step 1
3. Create 1Password Service Account
- Go to 1Password → Settings → Developer → Service Accounts
- Create service account with vault access to your dedicated vault (not Personal vault)
- Grant read-only access to the specific vault containing your credentials
- Save the service account token (starts with
ops_)
4. Install 1Password CLI
curl -sS https://downloads.1password.com/linux/keys/1password.asc | \
sudo gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg
echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] \
https://downloads.1password.com/linux/debian/amd64 stable main' | \
sudo tee /etc/apt/sources.list.d/1password.list
sudo apt update && sudo apt install 1password-cli
The DDNS Script
Create ~/.local/bin/update-ddns.sh:
#!/bin/bash
source ~/.env
# Authenticate with 1Password service account
export OP_SERVICE_ACCOUNT_TOKEN
DOMAIN="yourdomain.com"
SUBDOMAIN="movies" # Creates movies.yourdomain.com
ZONE_ID=$(op read "op://dev/cloudflare/zone_id")
API_TOKEN=$(op read "op://dev/cloudflare/api_token")
IP=$(curl -s ifconfig.me)
# Get DNS record ID
RESPONSE=$(curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name=$SUBDOMAIN.$DOMAIN" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json")
RECORD_ID=$(echo $RESPONSE | jq -r '.result[0].id')
if [ "$RECORD_ID" = "null" ]; then
# Create new DNS record
echo "Creating new DNS record..."
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
--data "{\"type\":\"A\",\"name\":\"$SUBDOMAIN\",\"content\":\"$IP\",\"ttl\":1}"
else
# Update existing DNS record
echo "Updating existing DNS record..."
curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
--data "{\"type\":\"A\",\"name\":\"$SUBDOMAIN\",\"content\":\"$IP\",\"ttl\":1}"
fi
Make it executable: chmod +x ~/.local/bin/update-ddns.sh
Environment Configuration
Create ~/.env with your service account token:
OP_SERVICE_ACCOUNT_TOKEN=ops_your_service_account_token_here
Secure the file: chmod 600 ~/.env
Automation with Cron
Add to crontab (crontab -e) to check every 10 minutes:
*/10 * * * * /home/username/.local/bin/update-ddns.sh >/dev/null 2>&1
How It Works
- Script sources environment variables (including service account token)
- Uses 1Password CLI to fetch API credentials securely
- Gets current public IP address
- Checks if DNS record exists, creates or updates accordingly
- Runs automatically via cron without human intervention
Advantages of This Approach
- Security: API credentials never stored on disk
- Auditability: 1Password logs all credential access
- Maintainability: Easy to rotate credentials
- Reliability: Handles both record creation and updates
- Flexibility: Works with Cloudflare's full feature set
Troubleshooting
Permission Errors
Ensure your API token has Zone:DNS:Edit permissions, not just read access.
Service Account Issues
Verify the service account has access to the correct vault containing your credentials.
Testing
Run the script manually first to verify it works before adding to cron.