commit 51ca26f5ce8cf1c6e053ab9236ca567c615dc7aa Author: KacperLa Date: Mon Nov 24 12:56:35 2025 -0500 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..3985c34 --- /dev/null +++ b/README.md @@ -0,0 +1,327 @@ +# Porkbun Dynamic DNS Update Script + +A bash script for automatically updating DNS A records on Porkbun with your current public IP address. Supports multi-machine failover configurations where multiple servers can independently update their own A records for the same domain. + +## Features + +- ✅ **Automatic IP Detection** - Uses Porkbun's API and multiple fallback methods +- ✅ **Multi-Machine Failover** - Multiple machines can maintain A records for the same domain +- ✅ **Machine Identification** - Uses notes field to track which machine owns each record +- ✅ **Secure** - API credentials stored in `.env` file (excluded from git) +- ✅ **Error Handling** - Comprehensive validation and error messages +- ✅ **Wildcard Support** - Updates both root and wildcard DNS records + +## Requirements + +- `bash` shell +- `curl` - for API calls +- `jq` - for JSON parsing +- Porkbun API access enabled for your domain + +## Installation + +1. Clone or download this script to your machine + +2. Make the script executable: + ```bash + chmod +x updateDNS.sh + ``` + +3. Copy the `.env` file and configure it (see Configuration below) + +## Configuration + +Edit the `.env` file with your settings: + +```bash +# Required: Your Porkbun API credentials +API_KEY=pk1_your_api_key_here +SECRET_KEY=sk1_your_secret_key_here + +# Required: The domain to update +DOMAIN=example.com + +# Required for multi-machine setup: Unique identifier for this machine +# Each machine should have a different MACHINE_ID +MACHINE_ID=server1 +``` + +### Getting Porkbun API Credentials + +1. Log in to your [Porkbun account](https://porkbun.com/account/api) +2. Navigate to **Account > API Access** +3. Generate API keys for your domain +4. **Important**: Enable API access for your specific domain in the domain settings + +## Usage + +### Single Machine (Simple Dynamic DNS) + +Basic configuration on one machine: + +```bash +# .env +API_KEY=pk1_xxxxx +SECRET_KEY=sk1_xxxxx +DOMAIN=example.com +MACHINE_ID=home-server +``` + +Run the script: +```bash +./updateDNS.sh +``` + +This will update: +- `example.com` → Your current public IP +- `*.example.com` → Your current public IP + +### Multi-Machine Failover Setup + +Configure multiple machines with **the same domain** but **different MACHINE_IDs**: + +**Server 1:** +```bash +# .env +API_KEY=pk1_xxxxx +SECRET_KEY=sk1_xxxxx +DOMAIN=example.com +MACHINE_ID=server1 +``` + +**Server 2:** +```bash +# .env +API_KEY=pk1_xxxxx +SECRET_KEY=sk1_xxxxx +DOMAIN=example.com +MACHINE_ID=server2 +``` + +**Server 3:** +```bash +# .env +API_KEY=pk1_xxxxx +SECRET_KEY=sk1_xxxxx +DOMAIN=example.com +MACHINE_ID=backup-server +``` + +Each machine runs the same script independently. The result will be: + +``` +example.com. 300 IN A 203.0.113.10 (server1's IP) +example.com. 300 IN A 198.51.100.20 (server2's IP) +example.com. 300 IN A 192.0.2.30 (backup-server's IP) +``` + +DNS clients will receive all three IPs, providing: +- **Automatic failover** - if one server is down, clients use others +- **Load distribution** - traffic is distributed across servers +- **Independent updates** - each machine manages only its own record + +### Managed records and notes tagging + +The script only manages records that it created itself, identified via the DNS **notes** field. + +- Internally it uses a managed-notes prefix: + + ```bash + NOTES_PREFIX="ddns:" + ``` + +- For each machine, the script sets the notes value to: + + ```text + ddns: + ``` + +- When cleaning up or checking for duplicates, it **only** looks at records where: + + - `name` matches the FQDN being updated (e.g. `example.com` or `*.example.com`), and + - `type` is `A`, `ALIAS`, or `CNAME`, and + - `notes == "ddns:"` + +This means: + +- Manually managed records without this prefix are left untouched. +- Multiple machines (different `MACHINE_ID`s) can safely coexist on the same domain. +- If you ever want to manually take control of a record that was created by this script, + you can remove or change the `ddns:` note and the script will stop managing it. + +### Automated Updates with Cron + +To automatically update DNS every 5 minutes: + +```bash +crontab -e +``` + +Add this line (adjust path as needed): +```cron +*/5 * * * * /home/user/porkbun/updateDNS.sh >> /var/log/porkbun-ddns.log 2>&1 +``` + +Or every hour: +```cron +0 * * * * /home/user/porkbun/updateDNS.sh >> /var/log/porkbun-ddns.log 2>&1 +``` + +## Dry-run mode + +You can run the script in **dry-run** mode to see what it would do without actually +creating or deleting any DNS records. + +Set the `DRY_RUN` environment variable to `1` when invoking the script: + +```bash +DRY_RUN=1 ./updateDNS.sh +``` + +In dry-run mode: + +- The script still loads configuration and detects your public IP. +- It still retrieves and displays existing DNS records. +- It logs which records **would** be deleted and what **would** be created. +- It does **not** call Porkbun's `dns/delete` or `dns/create` endpoints. + +This is useful when first configuring the script or making changes to your DNS setup, +so you can verify behavior before applying it. + +## How It Works + +1. **Load Configuration** - Reads API credentials and settings from `.env` +2. **Detect Public IP** - Uses Porkbun's `/ping` endpoint or fallback services (api.ipify.org, icanhazip.com) +3. **Retrieve DNS Records** - Gets all existing DNS records for the domain and displays them +4. **Check for Duplicates** - Detects if multiple records exist for this machine ID +5. **Skip if Unchanged** - If IP matches and exactly one record exists, skips update +6. **Clean Old Records** - Deletes A/ALIAS/CNAME records with no machine ID (migration cleanup) +7. **Delete Own Records** - Removes this machine's previous A records (by matching notes field) +8. **Verify Cleanup** - Ensures all old records were deleted before creating new ones +9. **Create New Record** - Adds fresh A record with current IP and machine ID in notes +10. **Verify Creation** - Confirms exactly one record exists for this machine + +> Note: The script now only deletes and verifies records that it manages +> (those whose notes are exactly `ddns:`). Other records in the +> same zone are left untouched. + +## Security Best Practices + +### Protect Your API Keys + +1. **Never commit `.env` to version control**: + ```bash + echo "*.env" >> .gitignore + echo ".env" >> .gitignore + ``` + +2. **Rotate your API keys regularly** in the Porkbun dashboard + +3. **Use restrictive file permissions**: + ```bash + chmod 600 .env + ``` + +4. **Use different API keys** for development/production if possible + +### If You Accidentally Exposed Keys + +1. **Immediately revoke** the API keys in your Porkbun account +2. **Generate new keys** and update your `.env` file +3. **Remove secrets from git history** if they were committed: + ```bash + git filter-branch --force --index-filter \ + "git rm --cached --ignore-unmatch .env" \ + --prune-empty --tag-name-filter cat -- --all + ``` + +## Troubleshooting + +### Error: "dig: command not found" +The script now uses `curl` instead of `dig`, so this shouldn't happen. If you see this, update to the latest version. + +### Error: "API access not enabled" +1. Log in to Porkbun +2. Go to your domain settings +3. Enable API access for the specific domain +4. Wait a few minutes for changes to propagate + +### Error: "Failed to create DNS record" +- Check that API credentials are correct +- Verify API access is enabled for your domain +- Ensure MACHINE_ID doesn't contain special characters +- Check that you're not exceeding Porkbun's rate limits + +### Multiple machines overwriting each other +- Make sure each machine has a **unique MACHINE_ID** +- Verify each machine is using the latest version of the script +- Check DNS records in Porkbun dashboard to see notes field (look for `ddns:`) + +### IP not updating +- Check script is running (add to cron for automatic updates) +- Verify your public IP actually changed +- Check Porkbun API status +- Review logs for error messages + +## API Rate Limits + +Porkbun's API has rate limits. For dynamic DNS: +- **Recommended**: Run every 5-15 minutes +- **Don't**: Run more than once per minute +- The script only makes API calls when it runs, not continuously + +## Example Output + +Example output with the new structured logging format: + +```text +Mon Nov 24 12:04:38 PM EST 2025 [INFO] Using environment file: /home/user/porkbun/.env +IP source: Porkbun API (/ping) +Mon Nov 24 12:04:38 PM EST 2025 [INFO] Current Public IP: 45.73.134.145 +Mon Nov 24 12:04:38 PM EST 2025 [INFO] Machine ID: KACPER_NIX +Mon Nov 24 12:04:38 PM EST 2025 [INFO] Domain: aksal.cloud + +Mon Nov 24 12:04:38 PM EST 2025 [INFO] Processing DNS record for: aksal.cloud (IP: 45.73.134.145) +Mon Nov 24 12:04:38 PM EST 2025 [INFO] Machine ID: KACPER_NIX +Mon Nov 24 12:04:39 PM EST 2025 [INFO] Existing records for FQDN 'aksal.cloud': + Type: A, Name: aksal.cloud, Content: 45.73.134.145, Notes: ddns:KACPER_NIX, Managed: true, ID: 508029248 + Type: NS, Name: aksal.cloud, Content: curitiba.porkbun.com, Notes: none, Managed: false, ID: 506591857 + Type: NS, Name: aksal.cloud, Content: fortaleza.porkbun.com, Notes: none, Managed: false, ID: 506591856 + Type: NS, Name: aksal.cloud, Content: maceio.porkbun.com, Notes: none, Managed: false, ID: 506591854 + Type: NS, Name: aksal.cloud, Content: salvador.porkbun.com, Notes: none, Managed: false, ID: 506591855 +Mon Nov 24 12:04:39 PM EST 2025 [INFO] Record already exists with correct IP (45.73.134.145). No update needed. + +Mon Nov 24 12:04:39 PM EST 2025 [INFO] Processing DNS record for: *.aksal.cloud (IP: 45.73.134.145) +Mon Nov 24 12:04:39 PM EST 2025 [INFO] Machine ID: KACPER_NIX +Mon Nov 24 12:04:40 PM EST 2025 [INFO] Existing records for FQDN '*.aksal.cloud': + Type: A, Name: *.aksal.cloud, Content: 45.73.134.145, Notes: ddns:KACPER_NIX, Managed: true, ID: 508029259 +Mon Nov 24 12:04:40 PM EST 2025 [INFO] Record already exists with correct IP (45.73.134.145). No update needed. + +Mon Nov 24 12:04:40 PM EST 2025 [INFO] DNS update completed successfully +``` + +## Files + +- `updateDNS.sh` - Main script +- `.env` - Configuration file (contains secrets, **DO NOT COMMIT**) +- `README.md` - This file + +## License + +This script is provided as-is for use with Porkbun DNS services. + +## Support + +For issues with: +- **This script**: Open an issue or review the code +- **Porkbun API**: Contact [Porkbun Support](https://porkbun.com/support) +- **DNS concepts**: See [Porkbun DNS Documentation](https://kb.porkbun.com/) + +## Contributing + +Improvements welcome! Consider adding: +- IPv6 support (AAAA records) +- Email notifications on IP change +- Systemd service file +- Docker container version +- Health check endpoint