A Go application that schedules a daily task to fetch the public IP, check for changes, and update Zonomi DNS if necessary. Runs in Docker with health checks and supports encrypted API keys.
Note This does not implement all APIs supported by Zonomi
- Scheduled daily IP fetch at configurable time (default: 23:59 Europe/London).
- IP change detection with persistent logging in JSON format.
- Zonomi DNS update for multiple hosts on IP change.
- Health check endpoint at
/health. - Run-once mode for testing.
- Encrypted Zonomi API key support.
- Unit tests for core functionality.
- JSON-formatted application logs to stdout.
All configurations are via environment variables:
API_URL: IP fetch API (default: https://api.ipify.org?format=json)OUTPUT_FILE: IP log file path (default: /app/data/ip_log.txt)MAX_RETRIES: Max retries for API calls (default: 3)TIMEZONE: Time zone (default: Europe/London)SCHEDULE_TIME: Schedule time (format: HH:MM, default: 23:59)ZONOMI_HOSTS: Comma-separated list of Zonomi hosts (required, e.g., "host1.example.com,host2.example.com")ZONOMI_API_KEY: Zonomi API key (required)ZONOMI_API_ENCRYPTED: Set to "true" if API key is encrypted (default: false)ZONOMI_ENCRYPT_KEY: 32-byte encryption key for decrypting API keyRUN_ONCE: Set to "true" to run the task once and exit (for testing)
The API key can be encrypted using AES-256-GCM for security. Use the following Go code to encrypt your API key:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"os"
)
func main() {
if len(os.Args) != 3 {
fmt.Println("Usage: go run encrypt.go <api_key> <encrypt_key>")
os.Exit(1)
}
apiKey := []byte(os.Args[1])
encryptKey := []byte(os.Args[2])
if len(encryptKey) != 32 {
fmt.Println("Encryption key must be 32 bytes")
os.Exit(1)
}
encrypted, err := encrypt(apiKey, encryptKey)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Encrypted API Key:", encrypted)
}
func encrypt(plaintext, key []byte) (string, error) {
c, err := aes.NewCipher(key)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return "", err
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return "", err
}
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}- Save the above code as
encrypt.go. - Run:
go run encrypt.go "your-api-key" "your-32-byte-encrypt-key". - Use the output as
ZONOMI_API_KEYin your environment. - Set
ZONOMI_API_ENCRYPTED=trueandZONOMI_ENCRYPT_KEY=your-32-byte-encrypt-key.
Note: The encryption key should be securely stored (e.g., in Docker secrets). Base64 is used for encoding the ciphertext.
- Build the image:
docker build -t zonocaller . - Run with volume for persistence and secrets:
mkdir data docker run --rm -v $(pwd)/data:/app/data \ -e ZONOMI_HOSTS=host1.example.com,host2.example.com \ -e ZONOMI_API_KEY=your-encrypted-key \ -e ZONOMI_API_ENCRYPTED=true \ -e ZONOMI_ENCRYPT_KEY=your-encrypt-key \ zonocaller
For testing in run-once mode:
docker run --rm -v $(pwd)/data:/app/data \
-e RUN_ONCE=true \
-e ZONOMI_HOSTS=host1.example.com,host2.example.com \
-e ZONOMI_API_KEY=your-encrypted-key \
-e ZONOMI_API_ENCRYPTED=true \
-e ZONOMI_ENCRYPT_KEY=your-encrypt-key \
zonocaller
Run unit tests:
go test ./...
- github.com/go-co-op/gocron/v2
- github.com/cenkalti/backoff/v4
- github.com/stretchr/testify (for tests)
Install:
go get github.com/go-co-op/gocron/v2
go get github.com/cenkalti/backoff/v4
go get github.com/stretchr/testify
Not all Zonomi APIs are supported.
Building and Running in Docker
docker build -t zonocaller .Run the container with a volume for persistence and secure API key:
mkdir data
docker run --rm -v $(pwd)/data:/app/data \
-e ZONOMI_HOSTS=host1.example.com,host2.example.com \
-e ZONOMI_API_KEY=your-actual-api-key \
zonocallerReplace your-actual-api-key with your real Zonomi API key. The -v flag mounts ./data to /app/data for ip_log.log persistence.
Override other environment variables if needed:
docker run --rm -v $(pwd)/data:/app/data \
-e ZONOMI_HOSTS=host1.example.com,host2.example.com \
-e ZONOMI_API_KEY=your-actual-api-key \
-e TIMEZONE=UTC \
-e SCHEDULE_TIME=23:00 \
zonocaller- IP Log File: Appended entries in
data/ip_log.login JSON Lines format (e.g.,{"ip":"203.0.113.1","timestamp":"2025-08-30T23:59:00Z"}). - Application Logs: Sent to stdout in JSON format and captured by Docker. Persist logs using a logging driver:
docker run --rm -v $(pwd)/data:/app/data \
-e ZONOMI_HOSTS=host1.example.com,host2.example.com \
-e ZONOMI_API_KEY=your-actual-api-key \
--log-driver=json-file \
--log-opt max-size=10m \
zonocaller