release: v0.1.0
This commit is contained in:
30
Dockerfile
Normal file
30
Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
||||
# Build stage
|
||||
FROM golang:1.23-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Cache deps
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy source and build
|
||||
COPY . .
|
||||
RUN go build -o ddns-updater main.go
|
||||
|
||||
# Runtime stage
|
||||
FROM alpine:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Non-root user (optional)
|
||||
RUN adduser -D ddns
|
||||
USER ddns
|
||||
|
||||
# Copy binary
|
||||
COPY --from=build /app/ddns-updater /usr/local/bin/ddns-updater
|
||||
|
||||
# Config volume
|
||||
VOLUME ["/config"]
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/ddns-updater"]
|
||||
CMD ["-config", "/config/config.yaml"]
|
||||
17
LICENSE
17
LICENSE
@@ -1,18 +1,9 @@
|
||||
MIT License
|
||||
# MIT License
|
||||
|
||||
Copyright (c) 2026 kccleoc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
92
Makefile
Normal file
92
Makefile
Normal file
@@ -0,0 +1,92 @@
|
||||
APP_NAME = ddns-updater
|
||||
BIN_DIR = bin
|
||||
VERSION = 1.0.0
|
||||
|
||||
# Go parameters
|
||||
GOCMD = go
|
||||
GOBUILD = $(GOCMD) build
|
||||
GOCLEAN = $(GOCMD) clean
|
||||
GORUN = $(GOCMD) run
|
||||
|
||||
# Build targets
|
||||
.PHONY: all build clean run init-config install install-linux install-macos service-linux service-macos
|
||||
|
||||
all: build
|
||||
|
||||
# Build binary for current platform
|
||||
build:
|
||||
mkdir -p $(BIN_DIR)
|
||||
$(GOBUILD) -o $(BIN_DIR)/$(APP_NAME) -ldflags "-X main.Version=$(VERSION)" main.go
|
||||
@echo "Built $(BIN_DIR)/$(APP_NAME)"
|
||||
|
||||
# Build for multiple platforms
|
||||
build-all:
|
||||
mkdir -p $(BIN_DIR)
|
||||
GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BIN_DIR)/$(APP_NAME)-linux-amd64 main.go
|
||||
GOOS=linux GOARCH=arm64 $(GOBUILD) -o $(BIN_DIR)/$(APP_NAME)-linux-arm64 main.go
|
||||
GOOS=darwin GOARCH=amd64 $(GOBUILD) -o $(BIN_DIR)/$(APP_NAME)-darwin-amd64 main.go
|
||||
GOOS=darwin GOARCH=arm64 $(GOBUILD) -o $(BIN_DIR)/$(APP_NAME)-darwin-arm64 main.go
|
||||
@echo "Built all platform binaries in $(BIN_DIR)/"
|
||||
|
||||
# Run locally
|
||||
run:
|
||||
$(GORUN) main.go -config config.yaml
|
||||
|
||||
# Generate config template
|
||||
init-config:
|
||||
$(GORUN) main.go -init-config > config.yaml
|
||||
@echo "Generated config.yaml - edit with your Cloudflare credentials"
|
||||
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
$(GOCLEAN)
|
||||
rm -rf $(BIN_DIR)
|
||||
|
||||
# Install binary system-wide
|
||||
install: build
|
||||
sudo install -m 755 $(BIN_DIR)/$(APP_NAME) /usr/local/bin/$(APP_NAME)
|
||||
@echo "Installed to /usr/local/bin/$(APP_NAME)"
|
||||
|
||||
# Linux-specific installation
|
||||
install-linux: install
|
||||
sudo mkdir -p /etc/ddns-updater
|
||||
@if [ ! -f /etc/ddns-updater/config.yaml ]; then \
|
||||
sudo cp config.yaml /etc/ddns-updater/config.yaml; \
|
||||
sudo chmod 600 /etc/ddns-updater/config.yaml; \
|
||||
echo "Config installed to /etc/ddns-updater/config.yaml"; \
|
||||
else \
|
||||
echo "Config already exists at /etc/ddns-updater/config.yaml"; \
|
||||
fi
|
||||
|
||||
# macOS-specific installation
|
||||
install-macos: install
|
||||
sudo mkdir -p /usr/local/etc
|
||||
sudo mkdir -p /usr/local/var/log
|
||||
@if [ ! -f /usr/local/etc/ddns-updater.yaml ]; then \
|
||||
sudo cp config.yaml /usr/local/etc/ddns-updater.yaml; \
|
||||
sudo chmod 600 /usr/local/etc/ddns-updater.yaml; \
|
||||
echo "Config installed to /usr/local/etc/ddns-updater.yaml"; \
|
||||
else \
|
||||
echo "Config already exists at /usr/local/etc/ddns-updater.yaml"; \
|
||||
fi
|
||||
|
||||
# Set up systemd service (Linux)
|
||||
service-linux:
|
||||
@echo "Creating systemd service..."
|
||||
@echo "[Unit]\nDescription=DDNS Updater Service\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nExecStart=/usr/local/bin/$(APP_NAME) -config /etc/ddns-updater/config.yaml\nRestart=always\nRestartSec=10\nUser=root\nWorkingDirectory=/usr/local/bin\n\n[Install]\nWantedBy=multi-user.target" | sudo tee /etc/systemd/system/ddns-updater.service >/dev/null
|
||||
@echo "\nSystemd service created. Run these commands:"
|
||||
@echo " sudo systemctl daemon-reload"
|
||||
@echo " sudo systemctl enable ddns-updater"
|
||||
@echo " sudo systemctl start ddns-updater"
|
||||
@echo " sudo systemctl status ddns-updater"
|
||||
|
||||
# Set up launchd service (macOS)
|
||||
service-macos:
|
||||
@echo "Creating launchd plist..."
|
||||
@mkdir -p ~/Library/LaunchAgents
|
||||
@echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>com.ddns-updater</string>\n <key>ProgramArguments</key>\n <array>\n <string>/usr/local/bin/$(APP_NAME)</string>\n <string>-config</string>\n <string>/usr/local/etc/ddns-updater.yaml</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <true/>\n <key>StandardOutPath</key>\n <string>/usr/local/var/log/ddns-updater.log</string>\n <key>StandardErrorPath</key>\n <string>/usr/local/var/log/ddns-updater.err</string>\n</dict>\n</plist>" > ~/Library/LaunchAgents/com.ddns-updater.plist
|
||||
@echo "\nLaunchd plist created. Run these commands:"
|
||||
@echo " launchctl load ~/Library/LaunchAgents/com.ddns-updater.plist"
|
||||
@echo " launchctl start com.ddns-updater"
|
||||
@echo "\nView logs with:"
|
||||
@echo " tail -f /usr/local/var/log/ddns-updater.log"
|
||||
290
README.md
290
README.md
@@ -1,70 +1,149 @@
|
||||
# ddns-updater
|
||||
# DDNS Updater
|
||||
|
||||
A small Go daemon that keeps Cloudflare A records in sync with your current public IPv4 address.
|
||||
It reads a YAML config, checks your IP periodically, compares it to existing DNS records, and only updates when needed.
|
||||
A lightweight dynamic DNS updater for Cloudflare written in Go. Automatically detects your public IP changes and updates specified DNS A records.
|
||||
|
||||
## Features
|
||||
|
||||
- Uses Cloudflare API token (modern auth)
|
||||
- Supports multiple subdomains in one zone
|
||||
- IPv4 only (A records)
|
||||
- User-defined check interval (minutes)
|
||||
- Retries on failure, then logs and skips
|
||||
- Structured JSON logging to file
|
||||
- Automatic public IP detection with fallback services
|
||||
- Multiple subdomain support
|
||||
- Cloudflare API v4 integration
|
||||
- Configurable check intervals
|
||||
- Retry logic with exponential backoff
|
||||
- Structured JSON logging
|
||||
- Graceful shutdown handling
|
||||
- Cross-platform (Linux, macOS)
|
||||
|
||||
## Requirements
|
||||
## Quick Start
|
||||
|
||||
- Go 1.21+ (tested with 1.23)
|
||||
- A Cloudflare account
|
||||
- API token with:
|
||||
- Zone:DNS:Edit
|
||||
- Zone:Zone:Read
|
||||
- Existing zone and A records for your subdomains
|
||||
|
||||
## Installation
|
||||
### 1. Generate configuration template
|
||||
|
||||
```bash
|
||||
# 1. Create project directory
|
||||
mkdir ddns-updater
|
||||
cd ddns-updater
|
||||
|
||||
# 2. Initialize module
|
||||
go mod init github.com/yourname/ddns-updater
|
||||
|
||||
# 3. Add files
|
||||
# - main.go
|
||||
# - config.yaml
|
||||
# - (this README.md)
|
||||
|
||||
# 4. Fetch dependencies
|
||||
go mod tidy
|
||||
|
||||
# 5. Build binary (current platform)
|
||||
go build -o ddns-updater
|
||||
|
||||
# 6. Build for Linux (LXC, etc.)
|
||||
GOOS=linux GOARCH=amd64 go build -o ddns-updater-linux
|
||||
make init-config
|
||||
```
|
||||
|
||||
Copy the resulting binary and `config.yaml` onto your target machine or LXC container, e.g. `/opt/ddns-updater`.
|
||||
This creates `config.yaml` with default settings.
|
||||
|
||||
## Configuration
|
||||
### 2. Get Cloudflare credentials
|
||||
|
||||
Create `config.yaml` in the same directory as the binary:
|
||||
#### Generate API Token
|
||||
|
||||
1. Log in to [Cloudflare Dashboard](https://dash.cloudflare.com)
|
||||
2. Go to **Profile** → **API Tokens**
|
||||
3. Click **Create Token**
|
||||
4. Use the **Edit zone DNS** template or create a custom token with:
|
||||
- **Permissions**: Zone → DNS → Edit, Zone → Zone → Read
|
||||
- **Zone Resources**: Include → Specific zone → (select your domain)
|
||||
5. Copy the token (you'll only see it once)
|
||||
|
||||
#### Find Zone ID
|
||||
|
||||
1. In Cloudflare Dashboard, select your domain
|
||||
2. Scroll down on the **Overview** page
|
||||
3. Find **Zone ID** in the right sidebar under **API** section
|
||||
4. Copy the Zone ID
|
||||
|
||||
### 3. Configure
|
||||
|
||||
Edit `config.yaml`:
|
||||
|
||||
```yaml
|
||||
check_interval: 5 # minutes (e.g. 5, 15, 30)
|
||||
cloudflare:
|
||||
api_token: "your-token-here" # From step 2
|
||||
zone_id: "your-zone-id-here" # From step 2
|
||||
zone_name: "example.com" # Your domain
|
||||
subdomains:
|
||||
- name: "home" # Updates home.example.com
|
||||
proxied: false
|
||||
- name: "vpn"
|
||||
proxied: true # Route through Cloudflare proxy
|
||||
```
|
||||
|
||||
### 4. Run locally (test)
|
||||
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
|
||||
Logs will show IP detection and DNS updates in real-time.
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Linux (systemd)
|
||||
|
||||
#### Install and set up service
|
||||
|
||||
```bash
|
||||
# Build and install binary
|
||||
make install-linux
|
||||
|
||||
# Edit config with your credentials
|
||||
sudo nano /etc/ddns-updater/config.yaml
|
||||
|
||||
# Create systemd service
|
||||
make service-linux
|
||||
|
||||
# Enable and start
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable ddns-updater
|
||||
sudo systemctl start ddns-updater
|
||||
|
||||
# Check status
|
||||
sudo systemctl status ddns-updater
|
||||
|
||||
# View logs
|
||||
sudo journalctl -u ddns-updater -f
|
||||
```
|
||||
|
||||
#### Service management
|
||||
|
||||
```bash
|
||||
sudo systemctl stop ddns-updater # Stop service
|
||||
sudo systemctl restart ddns-updater # Restart service
|
||||
sudo systemctl disable ddns-updater # Disable auto-start
|
||||
```
|
||||
|
||||
### macOS (launchd)
|
||||
|
||||
#### Install and set up service
|
||||
|
||||
```bash
|
||||
# Build and install binary
|
||||
make install-macos
|
||||
|
||||
# Edit config with your credentials
|
||||
sudo nano /usr/local/etc/ddns-updater.yaml
|
||||
|
||||
# Create launchd service
|
||||
make service-macos
|
||||
|
||||
# Load and start
|
||||
launchctl load ~/Library/LaunchAgents/com.ddns-updater.plist
|
||||
launchctl start com.ddns-updater
|
||||
|
||||
# View logs
|
||||
tail -f /usr/local/var/log/ddns-updater.log
|
||||
```
|
||||
|
||||
#### Service management
|
||||
|
||||
```bash
|
||||
launchctl stop com.ddns-updater # Stop service
|
||||
launchctl unload ~/Library/LaunchAgents/com.ddns-updater.plist # Unload service
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
```yaml
|
||||
# Check interval in minutes (how often to check for IP changes)
|
||||
check_interval: 5
|
||||
|
||||
cloudflare:
|
||||
api_token: "YOUR_CF_API_TOKEN"
|
||||
zone_id: "YOUR_ZONE_ID"
|
||||
api_token: "your-api-token"
|
||||
zone_id: "your-zone-id"
|
||||
zone_name: "example.com"
|
||||
subdomains:
|
||||
- name: "lc"
|
||||
proxied: false
|
||||
- name: "git"
|
||||
proxied: false
|
||||
- name: "mempool"
|
||||
proxied: false
|
||||
- name: "subdomain"
|
||||
proxied: false # true = route through Cloudflare proxy (orange cloud)
|
||||
|
||||
logging:
|
||||
file: "ddns-updater.log"
|
||||
@@ -76,78 +155,77 @@ retry:
|
||||
|
||||
webhook:
|
||||
enabled: false
|
||||
url: ""
|
||||
url: "" # Optional webhook for notifications
|
||||
```
|
||||
|
||||
- `check_interval`: how often to check your current IP (in minutes).
|
||||
- `subdomains`: just the left part of the name (e.g. `git` for `git.example.com`).
|
||||
- `proxied`: `true` for orange-cloud (proxied), `false` for DNS only.
|
||||
- `logging.file`: log file path, relative to the working directory.
|
||||
- `retry`: per-subdomain retry behavior when the Cloudflare API or network fails temporarily.
|
||||
|
||||
## Usage
|
||||
|
||||
### Direct run
|
||||
|
||||
From the directory containing the binary and `config.yaml`:
|
||||
## Building from Source
|
||||
|
||||
```bash
|
||||
./ddns-updater
|
||||
# Build for current platform
|
||||
make build
|
||||
|
||||
# Build for all platforms
|
||||
make build-all
|
||||
|
||||
# Clean build artifacts
|
||||
make clean
|
||||
```
|
||||
|
||||
The process will:
|
||||
Built binaries will be in `./bin/`
|
||||
|
||||
1. Load `config.yaml`.
|
||||
2. Detect current public IPv4 using external services.
|
||||
3. For each subdomain:
|
||||
- Fetch its current A record from Cloudflare.
|
||||
- If the IP differs, update the record.
|
||||
4. Sleep for `check_interval` minutes and repeat.
|
||||
5. Log events to `ddns-updater.log`.
|
||||
## Troubleshooting
|
||||
|
||||
### Run in background (simple)
|
||||
### Authentication errors
|
||||
|
||||
- Verify API token has correct permissions (Zone → DNS → Edit, Zone → Zone → Read)
|
||||
- Confirm token is scoped to the correct zone
|
||||
- Check token hasn't expired
|
||||
|
||||
### DNS record not found
|
||||
|
||||
- Ensure `zone_name` matches your Cloudflare domain exactly
|
||||
- Verify subdomain exists in Cloudflare DNS settings
|
||||
- Check subdomain name doesn't include the domain (use `home`, not `home.example.com`)
|
||||
|
||||
### Service won't start
|
||||
|
||||
**Linux:**
|
||||
|
||||
```bash
|
||||
nohup ./ddns-updater >/dev/null 2>&1 &
|
||||
sudo journalctl -u ddns-updater -n 50
|
||||
```
|
||||
|
||||
### Example systemd service (on Linux/LXC)
|
||||
|
||||
Create `/etc/systemd/system/ddns-updater.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Cloudflare DDNS Updater
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/ddns-updater
|
||||
ExecStart=/opt/ddns-updater/ddns-updater
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Then:
|
||||
**macOS:**
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable ddns-updater
|
||||
sudo systemctl start ddns-updater
|
||||
sudo systemctl status ddns-updater
|
||||
cat /usr/local/var/log/ddns-updater.err
|
||||
```
|
||||
|
||||
Logs will go to `ddns-updater.log` in `/opt/ddns-updater` plus systemd’s journal.
|
||||
### Docker deployment
|
||||
|
||||
## Updating
|
||||
|
||||
To update the binary:
|
||||
Build the image:
|
||||
|
||||
```bash
|
||||
git pull # if you put it in a repo
|
||||
go build -o ddns-updater
|
||||
sudo systemctl restart ddns-updater
|
||||
docker build -t yourname/ddns-updater:latest .
|
||||
```
|
||||
|
||||
Run with a local config file:
|
||||
|
||||
```bash
|
||||
cp config.yaml.example config.yaml # or make init-config
|
||||
# edit config.yaml with your Cloudflare settings
|
||||
|
||||
docker run -d \
|
||||
--name ddns-updater \
|
||||
--restart unless-stopped \
|
||||
-v $(pwd)/config.yaml:/config/config.yaml:ro \
|
||||
yourname/ddns-updater:latest
|
||||
```
|
||||
|
||||
Or with Docker Compose:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
The container runs in the foreground inside Docker; restarts are handled by Docker’s restart policy.
|
||||
|
||||
7
docker-compose.yaml
Normal file
7
docker-compose.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
ddns-updater:
|
||||
image: yourname/ddns-updater:latest
|
||||
container_name: ddns-updater
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config.yaml:/config/config.yaml:ro
|
||||
66
main.go
66
main.go
@@ -25,6 +25,7 @@ type Config struct {
|
||||
Cloudflare struct {
|
||||
APIToken string `yaml:"api_token"`
|
||||
ZoneID string `yaml:"zone_id"`
|
||||
ZoneName string `yaml:"zone_name"` // <- add this
|
||||
Subdomains []struct {
|
||||
Name string `yaml:"name"`
|
||||
Proxied bool `yaml:"proxied"`
|
||||
@@ -54,10 +55,15 @@ type CloudflareDNSRecord struct {
|
||||
TTL int `json:"ttl"`
|
||||
}
|
||||
|
||||
// CloudflareResponse is the generic API response structure
|
||||
type CloudflareError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type CloudflareResponse[T any] struct {
|
||||
Success bool `json:"success"`
|
||||
Errors []string `json:"errors"`
|
||||
Errors []CloudflareError `json:"errors"` // ← was `string`, now `[]CloudflareError`
|
||||
Messages []string `json:"messages"`
|
||||
Result T `json:"result"`
|
||||
}
|
||||
|
||||
@@ -71,8 +77,14 @@ type DNSUpdater struct {
|
||||
|
||||
func main() {
|
||||
configPath := flag.String("config", "config.yaml", "path to config file")
|
||||
initConfig := flag.Bool("init-config", false, "print example config to stdout")
|
||||
flag.Parse()
|
||||
|
||||
if *initConfig {
|
||||
printConfigTemplate()
|
||||
return
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
config, err := loadConfig(*configPath)
|
||||
if err != nil {
|
||||
@@ -111,6 +123,37 @@ func main() {
|
||||
logger.Info("DDNS Updater stopped gracefully")
|
||||
}
|
||||
|
||||
func printConfigTemplate() {
|
||||
tmpl := `# Check interval in minutes (5, 15, or 30)
|
||||
check_interval: 5
|
||||
|
||||
cloudflare:
|
||||
api_token: "your-api-token-here"
|
||||
zone_id: "your-zone-id-here"
|
||||
zone_name: "example.com"
|
||||
subdomains:
|
||||
- name: "lc"
|
||||
proxied: false
|
||||
- name: "git"
|
||||
proxied: false
|
||||
- name: "mempool"
|
||||
proxied: false
|
||||
|
||||
logging:
|
||||
file: "ddns-updater.log"
|
||||
level: "info"
|
||||
|
||||
retry:
|
||||
max_attempts: 3
|
||||
delay_seconds: 5
|
||||
|
||||
webhook:
|
||||
enabled: false
|
||||
url: ""
|
||||
`
|
||||
fmt.Print(tmpl)
|
||||
}
|
||||
|
||||
// loadConfig reads and parses the YAML configuration file
|
||||
func loadConfig(path string) (*Config, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
@@ -201,6 +244,13 @@ func (u *DNSUpdater) run(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *DNSUpdater) fqdn(subdomain string) string {
|
||||
if subdomain == "@" || subdomain == "" {
|
||||
return u.config.Cloudflare.ZoneName // root record
|
||||
}
|
||||
return subdomain + "." + u.config.Cloudflare.ZoneName
|
||||
}
|
||||
|
||||
// updateDNS checks current IP and updates DNS records if needed
|
||||
func (u *DNSUpdater) updateDNS(ctx context.Context) error {
|
||||
// Get current public IP
|
||||
@@ -334,9 +384,11 @@ func (u *DNSUpdater) updateSubdomainAttempt(ctx context.Context, subdomain, ip s
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s",
|
||||
u.config.Cloudflare.ZoneID, recordID)
|
||||
|
||||
name := u.fqdn(subdomain)
|
||||
|
||||
payload := map[string]any{
|
||||
"type": "A",
|
||||
"name": subdomain,
|
||||
"name": name,
|
||||
"content": ip,
|
||||
"proxied": proxied,
|
||||
"ttl": 300,
|
||||
@@ -380,8 +432,12 @@ func (u *DNSUpdater) updateSubdomainAttempt(ctx context.Context, subdomain, ip s
|
||||
|
||||
// getDNSRecord retrieves the record ID and current IP for a subdomain
|
||||
func (u *DNSUpdater) getDNSRecord(ctx context.Context, subdomain string) (recordID, currentIP string, err error) {
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records?type=A&name=%s",
|
||||
u.config.Cloudflare.ZoneID, subdomain)
|
||||
name := u.fqdn(subdomain)
|
||||
url := fmt.Sprintf(
|
||||
"https://api.cloudflare.com/client/v4/zones/%s/dns_records?type=A&name=%s",
|
||||
u.config.Cloudflare.ZoneID,
|
||||
name,
|
||||
)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user