porkbun_ddns_script/README.md
2025-11-24 14:13:09 -05:00

418 lines
No EOL
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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: Name of the machine for tracking
MACHINE_ID="exmample_machine"
```
### 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:<MACHINE_ID>
```
- 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:<MACHINE_ID>"`
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:<MACHINE_ID>` note and the script will stop managing it.
## 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.
## Systemd integration helpers
This repository also includes helper scripts to install the updater as a systemd service,
uninstall it cleanly, and view logs more easily.
### `install.sh` install systemd timer + service
`install.sh` sets up a `porkbun-ddns` systemd service and timer that will run
`updateDNS.sh` for you on a schedule (default: every 5 minutes).
What it does:
- Verifies required files exist in the current directory:
- `updateDNS.sh`
- `.env`
- `porkbun-ddns.service`
- `porkbun-ddns.timer`
- Ensures `updateDNS.sh` is executable.
- Checks for required dependencies: `curl`, `jq`, `systemctl`.
- Validates that `.env` contains `API_KEY`, `SECRET_KEY`, `DOMAIN`, and `MACHINE_ID`.
- Runs `updateDNS.sh` once as a test (with your current config).
- Installs and enables:
- `/etc/systemd/system/porkbun-ddns.service`
- `/etc/systemd/system/porkbun-ddns.timer`
- Starts the timer and shows its status.
Usage:
```bash
cd /path/to/porkbun
./install.sh
```
Notes:
- Do **not** run `install.sh` directly as root; it will use `sudo` when needed.
- The service runs as the current user (it rewrites `User=` and `Group=` in
the service file to match who ran `install.sh`).
After installation, some useful commands:
```bash
systemctl status porkbun-ddns.timer
systemctl status porkbun-ddns.service
journalctl -u porkbun-ddns.service -n 50
```
### `uninstall.sh` remove systemd timer + service
`uninstall.sh` cleanly removes the `porkbun-ddns` systemd units while leaving your
scripts and configuration files intact.
What it does:
- Stops `porkbun-ddns.timer` and `porkbun-ddns.service` if they are running.
- Disables the timer so it no longer starts at boot.
- Removes `/etc/systemd/system/porkbun-ddns.service` and
`/etc/systemd/system/porkbun-ddns.timer`.
- Reloads systemd and resets any failed state for these units.
Usage:
```bash
cd /path/to/porkbun
./uninstall.sh
```
Notes:
- Do **not** run `uninstall.sh` as root; it will use `sudo` for systemd operations.
- Your local files remain:
- `updateDNS.sh`
- `.env`
- `porkbun-ddns.service`
- `porkbun-ddns.timer`
so you can reinstall later with `./install.sh`.
### `logs.sh` convenient log viewer
`logs.sh` is a small wrapper around `journalctl` and `systemctl` to make it easier
to inspect the `porkbun-ddns.service` logs and status.
Basic usage (show last 50 lines):
```bash
cd /path/to/porkbun
./logs.sh
```
Common options:
- Follow logs in real-time (like `tail -f`):
```bash
./logs.sh -f
```
- Show last N lines:
```bash
./logs.sh -n 100
```
- Show logs from today only:
```bash
./logs.sh -t
```
- Show logs from the last hour:
```bash
./logs.sh -h
```
- Show only error messages:
```bash
./logs.sh -e
```
- Show service and timer status plus recent runs:
```bash
./logs.sh -s
```
Run `./logs.sh --help` for the full list of options.
## 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. **Delete Own Records** - Removes this machine's previous A records (by matching notes field)
7. **Verify Cleanup** - Ensures all old records were deleted before creating new ones
8. **Create New Record** - Adds fresh A record with current IP and machine ID in notes
9. **Verify Creation** - Confirms exactly one record exists for this machine
> Note: The script only deletes and verifies records that it manages
> (those whose notes are exactly `ddns:<MACHINE_ID>`). 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
## Troubleshooting
### 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:<MACHINE_ID>`)
### IP not updating
- Check script is running
- 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: EXAMPLE_MACHINE
Mon Nov 24 12:04:38 PM EST 2025 [INFO] Domain: example.com
Mon Nov 24 12:04:38 PM EST 2025 [INFO] Processing DNS record for: example.com (IP: 45.73.134.145)
Mon Nov 24 12:04:38 PM EST 2025 [INFO] Machine ID: EXAMPLE_MACHINE
Mon Nov 24 12:04:39 PM EST 2025 [INFO] Existing records for FQDN 'example.com':
Type: A, Name: example.com, Content: 45.73.134.145, Notes: ddns:EXAMPLE_MACHINE, Managed: true, ID: 508029248
Type: NS, Name: example.com, Content: curitiba.porkbun.com, Notes: none, Managed: false, ID: 506591857
Type: NS, Name: example.com, Content: fortaleza.porkbun.com, Notes: none, Managed: false, ID: 506591856
Type: NS, Name: example.com, Content: maceio.porkbun.com, Notes: none, Managed: false, ID: 506591854
Type: NS, Name: example.com, 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: *.example.com (IP: 45.73.134.145)
Mon Nov 24 12:04:39 PM EST 2025 [INFO] Machine ID: EXAMPLE_MACHINE
Mon Nov 24 12:04:40 PM EST 2025 [INFO] Existing records for FQDN '*.example.com':
Type: A, Name: *.example.com, Content: 45.73.134.145, Notes: ddns:EXAMPLE_MACHINE, 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.
## Contributing
Improvements welcome! Consider adding:
- IPv6 support (AAAA records)
- Email notifications on IP change
- Health check endpoint