diff --git a/README.md b/README.md index 1fc059f..f7fce10 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,256 @@ -# ansible-role-ddclient-cloudflare +# Ansible Role: ddclient -Simple Ansible for setting up Dynamic DNS with ddclient and cloudflare \ No newline at end of file +Configures and manages ddclient for dynamic DNS updates. + +## Example Playbook + +```yaml +--- +- hosts: servers + roles: + - role: ddclient + vars: + ddclient_daemon_interval: 300 # Checks IP every 5 minutes (default, can be omitted) + ddclient_ipv4_detection: # Use ipify service to get your public IPv4 address + method: webv4 + source: ipify-ipv4 + ddclient_ipv6_detection: {} # Disable ipv6 detection + ddclient_protocols: # Use cloudflare to update your DNS records + - protocol: cloudflare + zone: example.com + password: "{{ vault_cloudflare_token }}" + ttl: 600 + domains: + - home.example.com + - vpn.example.com +``` + +## Role Variables + +> [!NOTE] +> Read the [ddclient general documentation](https://ddclient.net/general.html) to familiarize yourself with the global configuration options. + +### IPv4 Detection Configuration + +The `ddclient_ipv4_detection` dictionary contains: + +| Key | Default | Description | +|-----|---------|-------------| +| `method` | `webv4` | Detection method: `ipv4`, `ifv4`, `webv4`, `fwv4`, `cmdv4` | +| `source` | `dyndns` | Source URL/interface/command (provider name or full URL) | +| `skip_pattern` | | Pattern to skip in output | + +### IPv6 Detection Configuration + +The `ddclient_ipv6_detection` dictionary contains the same keys as IPv4, with `method` supporting: `ipv6`, `ifv6`, `webv6`, `fwv6`, `cmdv6` + +To disable either IPv6 or IPv4 detection, set to empty dict: +```yaml +ddclient_ipv6_detection: {} +``` + +### Firewall Authentication +Only applicable if using the IP detection method `fw`. + +| Variable | Default | Description | +|----------|---------|-------------| +| `ddclient_fw_login` | | Firewall login username | +| `ddclient_fw_password` | | Firewall password | + +### Protocol Configuration + +**Common keys:** + +| Key | Required | Description | +|-----|----------|-------------| +| `protocol` | Yes | Protocol type (e.g., `cloudflare`, `dyndns2`, `namecheap`) | +| `domains` | Yes | List of hostnames to update | +| `login` | Yes* | Username/email/domain | +| `password` | Yes* | Password/API key/token | +| `zone` | No | Zone/domain name (Cloudflare, nsupdate) | +| `ttl` | No | DNS TTL in seconds | +| `server` | No | update DNS information on this server | +| `mx` | No | Mail exchanger hostname | +| `backupmx` | No | Backup MX flag (yes/no) | +| `wildcard` | No | Wildcard DNS flag (yes/no) | +| `custom` | No | Custom domain flag (yes/no) | +| `tcp` | No | Use TCP instead of UDP (yes/no) | + + +> [!NOTE] +> *Required fields depend on the protocol +> Read the [ddclient protocol documentation](https://ddclient.net/protocols.html) to know which options must be specified for a given protocol. + + +### Daemon Settings + +| Variable | Default | Description | +|----------|---------|-------------| +| `ddclient_daemon_interval` | `300` | Check interval in seconds (0 = run once) | +| `ddclient_foreground` | `false` | Run in foreground (don't fork) | +| `ddclient_pid` | `""` | PID file path (empty = use default) | +| `ddclient_cache` | `""` | Cache file path (empty = use default) | + +### Network Settings + +| Variable | Default | Description | +|----------|---------|-------------| +| `ddclient_ssl` | `true` | Use SSL/HTTPS for updates | +| `ddclient_proxy` | | HTTP proxy hostname (empty = no proxy) | +| `ddclient_timeout` | `0` | Connection timeout in seconds (0 = no timeout) | + +### Logging Settings + +| Variable | Default | Description | +|----------|---------|-------------| +| `ddclient_syslog` | `true` | Log to syslog | +| `ddclient_facility` | `daemon` | Syslog facility | +| `ddclient_priority` | `notice` | Syslog priority | +| `ddclient_mail` | | Email address for notifications | +| `ddclient_mail_failure` | | Email address for failure notifications | +| `ddclient_verbose` | `false` | Verbose output | +| `ddclient_quiet` | `false` | Suppress unnecessary update messages | +| `ddclient_debug` | `false` | Debug output | + +### Update Behavior + +| Variable | Default | Description | +|----------|---------|-------------| +| `ddclient_exec` | `true` | Execute updates (false = dry-run) | +| `ddclient_retry` | `false` | Retry failed updates | +| `ddclient_force` | `false` | Force updates even if unnecessary | +| `ddclient_postscript` | | Script to run after update | + +## More Example Playbooks + +### Multiple Providers + +```yaml +--- +- hosts: servers + roles: + - role: ddclient + vars: + ddclient_protocols: + - protocol: cloudflare + zone: company.com + login: token + password: "{{ vault_cloudflare_password }}" + domains: + - www.company.com + - api.company.com + - protocol: dyndns2 + server: domains.google.com + login: service-login + password: "{{ vault_service_password }}" + domains: + - backup.ddns.net +``` + +### Interface-based Detection (Server with Public IP) + +```yaml +--- +- hosts: servers + roles: + - role: ddclient + vars: + ddclient_ipv4_detection: + method: ifv4 + source: eth0 + ddclient_protocols: + - protocol: cloudflare + zone: example.com + login: token + password: "{{ vault_cloudflare_token }}" + domains: + - server.example.com +``` + +### Dual-Stack (IPv4 + IPv6) + +```yaml +--- +- hosts: servers + roles: + - role: ddclient + vars: + ddclient_ipv4_detection: + method: webv4 + source: checkip.amazonaws.com + ddclient_ipv6_detection: + method: webv6 + source: checkipv6.dyndns.org + ddclient_protocols: + - protocol: cloudflare + zone: example.com + login: admin@example.com + password: "{{ vault_cloudflare_token }}" + ttl: 600 + domains: + - dualstack.example.com +``` + +### Dry-Run Mode (Testing) + +```yaml +--- +- hosts: servers + roles: + - role: ddclient + vars: + ddclient_exec: false # Don't actually update DNS + ddclient_verbose: true + ddclient_protocols: + - protocol: cloudflare + zone: example.com + login: token + password: test-token + domains: + - test.example.com +``` + +## IP Detection Methods + +### Web-based (webv4/webv6) +Queries a web service to detect public IP. Best for systems behind NAT. + +```yaml +ddclient_ipv4_detection: + method: webv4 + source: checkip.amazonaws.com # or dyndns, googledomains, etc. +``` + +### Interface-based (ifv4/ifv6) +Reads IP directly from a network interface. Best for servers with public IPs. + +```yaml +ddclient_ipv4_detection: + method: ifv4 + source: eth0 +``` + +### Firewall/Router-based (fwv4/fwv6) +Queries router's status page for WAN IP. + +```yaml +ddclient_ipv4_detection: + method: fwv4 + source: 192.168.1.1/Status.htm + skip_pattern: "WAN IP Address" +ddclient_fw_login: admin +ddclient_fw_password: routerpass +``` + +### Command-based (cmdv4/cmdv6) +Executes a custom script/command to get IP. + +```yaml +ddclient_ipv4_detection: + method: cmdv4 + source: /usr/local/bin/get-public-ip.sh +``` + +## License + +MIT diff --git a/defaults/main.yaml b/defaults/main.yaml new file mode 100755 index 0000000..e64b0a3 --- /dev/null +++ b/defaults/main.yaml @@ -0,0 +1,50 @@ +--- +# Daemon settings +ddclient_daemon_interval: 300 # Check interval in seconds (0 = run once) +ddclient_foreground: false # Run in foreground (don't fork) +ddclient_pid: "" # PID file path +ddclient_cache: "" # Cache file path + +# Network settings +ddclient_ssl: true # Use SSL/HTTPS +ddclient_proxy: "" # HTTP proxy (empty = no proxy) +ddclient_timeout: 0 # Connection timeout (0 = no timeout) + +# Logging settings +ddclient_syslog: true # Log to syslog +ddclient_facility: daemon # Syslog facility +ddclient_priority: notice # Syslog priority +ddclient_mail: "" # Email address for notifications +ddclient_mail_failure: "" # Email address for failure notifications +ddclient_verbose: false # Verbose output +ddclient_quiet: false # Suppress unnecessary update messages +ddclient_debug: false # Debug output + +# Update behavior +ddclient_exec: true # Actually execute updates (false = dry-run) +ddclient_retry: false # Retry failed updates +ddclient_force: false # Force updates even if unnecessary +ddclient_postscript: "" # Script to run after update + +# IP Detection configuration +ddclient_ipv4_detection: + static_ip: "" # Set static IP address (skips detection) + method: webv4 # Method: ip, if, web, fw, cmd + source: dyndns # Source for web/fw methods + address: "" # IP address for method=ip + interface: "" # Network interface (for method=if) + command: "" # Command to run (for method=cmd) + skip: "" # Pattern to skip in output + +ddclient_ipv6_detection: + static_ip: "" # Set static IP address (skips detection) + method: webv6 # Method: ip, if, web, fw, cmd + source: dyndns # Source for web/fw methods + address: "" # IP address for method=ip + interface: "" # Network interface (for method=if) + command: "" # Command to run (for method=cmd) + skip: "" # Pattern to skip in output + +fw_login: "" # Firewall login (for method=fw) +fw_password: "" # Firewall password (for method=fw) + diff --git a/handlers/main.yaml b/handlers/main.yaml new file mode 100755 index 0000000..2e6c69c --- /dev/null +++ b/handlers/main.yaml @@ -0,0 +1,5 @@ +--- +- name: Restart ddclient service + ansible.builtin.service: + name: ddclient + state: restarted diff --git a/meta/main.yaml b/meta/main.yaml new file mode 100644 index 0000000..784964e --- /dev/null +++ b/meta/main.yaml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: ddclient + author: Patrick Jaroszewski + description: Ansible role to install and configure ddclient + license: MIT License + min_ansible_version: 2.11 + platforms: + - name: Debian + versions: all + - name: Ubuntu + version: all + galaxy_tags: + - ddns + - dynamicdns + - ddclient diff --git a/tasks/main.yaml b/tasks/main.yaml new file mode 100755 index 0000000..8e627b2 --- /dev/null +++ b/tasks/main.yaml @@ -0,0 +1,21 @@ +--- +- name: Install ddclient + ansible.builtin.package: + name: ddclient + state: latest + +- name: Copy over main ddclient configuration file + ansible.builtin.template: + src: "../templates/ddclient.conf.j2" + dest: /etc/ddclient.conf + owner: root + mode: "0600" + notify: Restart ddclient service + +- name: Copy over ddclient service configuration + ansible.builtin.template: + src: "../templates/ddclient.j2" + dest: /etc/default/ddclient + owner: root + mode: "0600" + notify: Restart ddclient service diff --git a/templates/ddclient.conf.j2 b/templates/ddclient.conf.j2 new file mode 100755 index 0000000..5f540dc --- /dev/null +++ b/templates/ddclient.conf.j2 @@ -0,0 +1,101 @@ +# {{ ansible_managed }} +# /etc/ddclient.conf + + +# ============================================================ +# Daemon Settings +# ============================================================ +daemon={{ ddclient_daemon_interval }} +foreground={{ ddclient_foreground | ternary('yes', 'no') }} +{% if ddclient_pid %} +pid={{ ddclient_pid }} +{% endif %} +{% if ddclient_cache %} +cache={{ ddclient_cache }} +{% endif %} + +# ============================================================ +# Network Settings +# ============================================================ +ssl={{ ddclient_ssl | ternary('yes', 'no') }} +{% if ddclient_proxy %} +proxy={{ ddclient_proxy }} +{% endif %} +{% if ddclient_timeout %} +timeout={{ ddclient_timeout }} +{% endif %} + +# ============================================================ +# Logging Settings +# ============================================================ +syslog={{ ddclient_syslog | ternary('yes', 'no') }} +{% if ddclient_facility %} +facility={{ ddclient_facility }} +{% endif %} +{% if ddclient_priority %} +priority={{ ddclient_priority }} +{% endif %} +{% if ddclient_mail %} +mail={{ ddclient_mail }} +{% endif %} +{% if ddclient_mail_failure %} +mail-failure={{ ddclient_mail_failure }} +{% endif %} +verbose={{ ddclient_verbose | ternary('yes', 'no') }} +quiet={{ ddclient_quiet | ternary('yes', 'no') }} +debug={{ ddclient_debug | ternary('yes', 'no') }} + +# ============================================================ +# Update Behavior +# ============================================================ +retry={{ ddclient_retry | ternary('yes', 'no') }} +force={{ ddclient_force | ternary('yes', 'no') }} +{% if ddclient_postscript %} +postscript={{ ddclient_postscript }} +{% endif %} + + +# ============================================================ +# IP Detection Method +# ============================================================ + +# Disable the legacy use setting +use=no + +{% if ddclient_ipv4_detection is defined and ddclient_ipv4_detection %} +usev4={{ ddclient_ipv4_detection.method }} +{{ ddclient_ipv4_detection.method }}={{ ddclient_ipv4_detection.source }} +{% if ddclient_ipv4_detection.skip_pattern is defined %} +{{ ddclient_ipv4_detection.method }}-skip='{{ ddclient_ipv4_detection.skip_pattern }}' +{% endif %} +{% endif %} + +{% if ddclient_ipv6_detection is defined and ddclient_ipv6_detection %} +usev6={{ ddclient_ipv6_detection.method }} +{{ ddclient_ipv6_detection.method }}={{ ddclient_ipv6_detection.source }} +{% if ddclient_ipv6_detection.skip_pattern is defined %} +{{ ddclient_ipv6_detection.method }}-skip='{{ ddclient_ipv6_detection.skip_pattern }}' +{% endif %} +{% endif %} + +{% if ddclient_fw_login is defined %} +fw-login={{ ddclient_fw_login }} +{% if ddclient_fw_password is defined %} +fw-password={{ ddclient_fw_password }} +{% endif %} +{% endif %} + + +# ============================================================ +# Protocol Configurations +# ============================================================ +{% for proto in ddclient_protocols %} + +{% for key, value in proto.items() %} +{% if key != 'domains' %} +{{ key }}={{ value }} +{% endif %} +{% endfor %} +{{ proto.domains | join(',') }} + +{% endfor %} \ No newline at end of file diff --git a/templates/ddclient.j2 b/templates/ddclient.j2 new file mode 100755 index 0000000..e2539a5 --- /dev/null +++ b/templates/ddclient.j2 @@ -0,0 +1,16 @@ +# Configuration for ddclient scripts +# {{ ansible_managed }} +# +# /etc/default/ddclient + +# Set to "true" if ddclient should be run every time DHCP client ('dhclient' +# from package isc-dhcp-client) updates the systems IP address. +run_dhclient="false" + +# Set to "true" if ddclient should be run every time a new ppp connection is +# established. This might be useful, if you are using dial-on-demand. +run_ipup="false" + +# Set the time interval between the updates of the dynamic DNS name in seconds. +# This option only takes effect if the ddclient runs in daemon mode. +daemon_interval={{ ddclient_daemon_interval }} \ No newline at end of file