debops.dnsmasq default variables

APT packages

dnsmasq__base_packages

List of APT packages to install for dnsmasq support.

dnsmasq__base_packages: [ 'dnsmasq' ]
dnsmasq__packages

List of additional APT packages to install during dnsmasq configuration.

dnsmasq__packages: []

Global options

dnsmasq__dhcpv4

Enable or disable DHCPv4 support.

dnsmasq__dhcpv4: True
dnsmasq__dhcpv6

Enable or disable DHCPv6 support (router support is required).

dnsmasq__dhcpv6: True

Configuration specific to network interfaces

These variables define dnsmasq configuration related to a particular network interface. See dnsmasq__interfaces for more details.

dnsmasq__default_interfaces

List of network interfaces for which dnsmasq configuration will be generated by default.

dnsmasq__default_interfaces:

    # Name of the interface.
  - name: 'br2'
    state: '{{ "present"
               if (hostvars[inventory_hostname]["ansible_br2"] is defined)
               else "absent" }}'
dnsmasq__interfaces

List of network interfaces for which dnsmasq configuration will be generated, defined by the user.

dnsmasq__interfaces: []
dnsmasq__combined_interfaces

Combined list of network interfaces for which dnsmasq configuration will be generated, used in role tasks and templates.

dnsmasq__combined_interfaces: '{{ dnsmasq__default_interfaces
                                  + dnsmasq__interfaces }}'

DNS options

dnsmasq__hostname

The router hostname used by dnsmasq. It will be used to configure DNS A and AAAA records pointing to the dnsmasq server.

dnsmasq__hostname: '{{ ansible_hostname }}'
dnsmasq__base_domain

The DNS domain which will be used as a base for interface-based subdomains if none are explicitly configured.

dnsmasq__base_domain: '{{ ansible_domain }}'
dnsmasq__base_domain_rebind_ok

When True, dnsmasq will accept DNS records for the base DNS domain that specify IP addresses in private address ranges. This is needed when another DHCP/DNS server is managing the IP address leases, otherwise they will be unresolvable.

dnsmasq__base_domain_rebind_ok: True
dnsmasq__etc_hosts

List of absolute paths of the additional hosts(5) database files to read by dnsmasq. This is useful if you don't want to keep the host list in the system-wide /etc/hosts database.

You can provide the files using the debops.resources Ansible role, non-existent files will be silently ignored by dnsmasq.

dnsmasq__etc_hosts: []
dnsmasq__nameservers

List of upstream DNS nameservers where dnsmasq should forward its DNS queries which it can not answer by itself. If empty, system nameservers (for example received via DHCP) will be used by default.

dnsmasq__nameservers: []
dnsmasq__public_dns

Enable or disable access to the local DNS from upstream networks using the firewall. You can publish your subdomain in the public DNS by delegating it in your zone configuration. Be wary of the DNS reflection and amplification attacks!

dnsmasq__public_dns: False
dnsmasq__public_dns_allow

List of IP addresses or CIDR subnets which are allowed to connect to dnsmasq DNS service in public DNS mode. If the list is empty, any hosts can connect. The configuration will be set in the firewall.

dnsmasq__public_dns_allow: []

TFTP options

dnsmasq__boot_enabled

Enable or disable support for BOOTP/PXE boot of remote hosts.

dnsmasq__boot_enabled: True
dnsmasq__boot_ipxe_enabled

Enable or disable support for iPXE boot menu. The iPXE configuration can be done using the debops.ipxe Ansible role.

dnsmasq__boot_ipxe_enabled: True
dnsmasq__boot_server

Specify the IP address of the "next server" which provides a TFTP service with boot files. If not specified, dnsmasq server will be contacted by the clients instead for the boot files.

dnsmasq__boot_server: ''
dnsmasq__boot_tftp_root

Absolute path of the TFTP root directory from which boot files will be served. This path needs to exist, otherwise dnsmasq service will refuse to start.

dnsmasq__boot_tftp_root: '/srv/tftp'
dnsmasq__boot_filename

Name of the file located in the TFTP root directory which will be sent to clients telling them which file to download and boot from.

dnsmasq__boot_filename: '{{ "menu.ipxe" if dnsmasq__boot_ipxe_enabled | bool else "pxelinux.0" }}'

DHCP hosts, DNS resource records

The variables below can be used to configure DHCP client entries as well as DNS resource records published by dnsmasq; syntax for both of these variables is the same. See dnsmasq__dhcp_hosts, dnsmasq__dns_records for more details.

dnsmasq__dhcp_hosts

List of DHCP clients that dnsmasq knows about.

dnsmasq__dhcp_hosts: []
dnsmasq__dns_records

List of DNS resource records that dnsmasq publishes.

dnsmasq__dns_records: []
dnsmasq__dhcp_dns_filename

Name of the configuration file that contains DHCP and DNS entries, located in the /etc/dnsmasq.d/ directory.

dnsmasq__dhcp_dns_filename: 'host-resource-records.conf'

The dnsmasq configuration files

These variables define the contents of the dnsmasq configuration files located in the /etc/dnsmasq.d/ directory. See dnsmasq__configuration for more details.

dnsmasq__default_configuration

The configuration defined by the role by default.

dnsmasq__default_configuration:

  # Remove the old default configuration file
  - name: '00_main.conf'
    state: 'absent'

  - name: 'global.conf'
    options:

      - name: 'conntrack'
        comment: 'Enable connection tracking support for firewalls'
        raw: |
          conntrack
        state: 'present'

      - name: 'enable-ra'
        comment: 'Enable support for IPv6 Router Advertisements using dnsmasq'
        raw: |
          enable-ra
        state: 'present'

      - name: 'bind-interfaces'
        comment: |
          Bind only to the network interfaces explicitly set in the
          configuration. This is required to allow additional dnsmasq instances
          managed by, for example, libvirt.
        raw: |
          bind-dynamic
        state: 'present'

      - name: 'loopback-interface'
        comment: 'Bind to loopback interface for local DNS queries'
        raw: |
          interface = lo
          no-dhcp-interface = lo
        state: 'present'

      - name: 'addn-hosts'
        comment: 'Read hosts information from additional files or directories'
        value: '{{ dnsmasq__etc_hosts }}'
        state: '{{ "present" if dnsmasq__etc_hosts | d() else "absent" }}'

  - name: 'consul.conf'
    comment: |
      Support for Consul Agent DNS service on localhost
      Ref: https://www.consul.io/docs/agent/dns.html
    raw: |
      server = /consul/127.0.0.1#8600
    state: '{{ "present"
               if (ansible_local.consul.installed | d() | bool)
               else "init" }}'

  - name: 'lxd-override'
    filename: 'lxd'
    comment: |
      Tell any system-wide dnsmasq instance to make sure to bind to interfaces
      instead of listening on 0.0.0.0
    raw: |
      bind-dynamic
      except-interface = lxdbr0
    state: '{{ "present" if (ansible_distribution == "Ubuntu") else "ignore" }}'

  - name: 'reserved-domains.conf'
    options:

      - name: 'reserved-domains'
        comment: |
          Do not forward the reserved top level domains to upstream nameservers
        raw: |
          # Ref: https://tools.ietf.org/html/rfc2606
          local = /test/example/invalid/

          # Ref: https://tools.ietf.org/html/rfc6762
          local = /local/

          # Ref: https://tools.ietf.org/html/rfc7686
          local = /onion/
        state: 'present'

      - name: 'private-domains'
        comment: |
          Do not forward the following private top level DNS names to upstream
          DNS servers because RFC 6762 recommends not to use unregistered
          top-level domains (https://tools.ietf.org/html/rfc6762#appendix-G)
        raw: |
          local = /intranet/internal/private/corp/home/lan/
        state: 'present'

  - name: 'block-dns-over-https'
    comment: |
      Blocking the 'use-application-dns.net' domain instructs the applications
      that support DNS over HTTPS to not use it and rely on the system resolver
      instead. This might be required for certain applications to support
      access to internal services, resolve split-DNS correctly, etc.

      Ref: https://support.mozilla.org/en-US/kb/canary-domain-use-application-dnsnet
    raw: |
      server = /use-application-dns.net/
    state: 'present'

  - name: 'dns-global.conf'
    options:

      - name: 'localise-queries'
        comment: |
          Return localized answers to DNS queries from '/etc/hosts' depending
          on the originating network interface
        raw: |
          localise-queries
        state: 'present'

      - name: 'domain-needed'
        comment: |
          Never forward plain hostname queries for A or AAAA records to
          upstream servers
        raw: |
          domain-needed
        state: 'present'

      - name: 'expand-hosts'
        comment: |
          Expand short hostnames found in the '/etc/hosts' file to full FQDN
          addresses
        raw: |
          expand-hosts
        state: 'present'

      - name: 'stop-dns-rebind'
        comment: |
          Reject addresses from the upstream DNS nameservers which are located
          in the private IP address ranges
        raw: |
          stop-dns-rebind
        state: 'present'

      - name: 'rebind-localhost-ok'
        comment: |
          Skip rebinding checks for '127.0.0.0/8' IP address range. This range
          is used by the realtime black hole (RBL) servers.
        raw: |
          rebind-localhost-ok
        state: 'present'

      - name: 'rebind-local-domain-ok'
        comment: |
          Skip rebinding checks for local domain, in case dnsmasq is used as
          a DNS cache and forwarder on a host that is a part of a network with
          private IP address ranges, with a different DHCP/DNS server
          maintaining the leases.
        option: 'rebind-domain-ok'
        value: '{{ dnsmasq__base_domain }}'
        state: '{{ "present"
                   if (dnsmasq__base_domain_rebind_ok | bool and
                       dnsmasq__base_domain | d())
                   else "absent" }}'

      - name: 'rebind-parent-domain-ok'
        comment: |
          Skip rebinding checks for the parent domain if it has 4 or more
          levels, which is most likely an internal domain on a network with
          private IP address ranges.
        option: 'rebind-domain-ok'
        value: '{{ dnsmasq__base_domain.split(".")[1:] | join(".") }}'
        state: '{{ "present"
                   if (dnsmasq__base_domain_rebind_ok | bool and
                        dnsmasq__base_domain | d() and
                       (dnsmasq__base_domain.split(".") | length >= 4))
                   else "absent" }}'

      - name: 'bogus-priv'
        comment: |
          Do not forward reverse DNS queries for private IP addresses to
          upstream DNS servers.
          When an LXC network support is enabled, this parameter is commented
          out to allow revDNS queries. It will also be commented out when
          upstream nameservers are located in a private network to allow DNS
          queries to reach them. Ref: https://bugs.debian.org/461054
        raw: |
          bogus-priv
        state: '{{ "comment"
                   if ((ansible_local.lxc.net_domain | d()) or
                       (ansible_local.resolvconf.upstream_nameservers
                        | d(ansible_dns.nameservers)
                        | ansible.utils.ipaddr("private")))
                   else "present" }}'

      - name: 'resolv-file'
        comment: |
          Use custom list of nameservers instead of the system upstream
          nameservers
        value: '/etc/resolvconf/upstream.conf'
        state: '{{ "present" if dnsmasq__nameservers | d() else "absent" }}'


  - name: 'lxc-net.conf'
    comment: |
      Support for resolving LXC container hosts that use the 'lxc-net' bridge
      configuration
    options:

      - name: 'local'
        value: '{{ "/" + (ansible_local.lxc.net_domain | d(""))
                   + "/" + ansible_local.lxc.net_address | d("") }}'

      # Create a separate 'lxc' host record that points to the 'lxcbr0'
      # interface from the outside, if there's no external domain set.
      - name: 'host-record'
        value: '{{ ansible_local.lxc.net_domain | d("")
                   + "," + ansible_local.lxc.net_address | d("") }}'
        state: '{{ "present"
                   if ("." not in ansible_local.lxc.net_domain | d())
                   else "absent" }}'

      - name: 'rev-server'
        value: '{{ ansible_local.lxc.net_subnet | d("")
                   + "," + ansible_local.lxc.net_address | d("") }}'

      - name: 'rebind-domain-ok'
        value: '{{ ansible_local.lxc.net_domain | d("") }}'

    state: '{{ "present"
               if (ansible_local.lxc.net_domain | d())
               else "init" }}'

  - name: 'dhcp-boot.conf'
    comment: |
      This configuration file contains dnsmasq options related to booting
      remote hosts using iPXE boot menu
    options:

      - name: 'dhcp-match-ipxe'
        comment: |
          Tag all DHCP requests with option 175 as coming from iPXE to avoid
          recursive loops
        option: 'dhcp-match'
        value: 'set:ipxe,175'

      - name: 'dhcp-match-d-i'
        comment: |
          Tag all DHCP requests with 'd-i' vendor class as coming from the
          Debian Installer
        option: 'dhcp-match'
        value: 'set:debian-installer,option:vendor-class,"d-i"'

      - name: 'vendor-match'
        comment: |
          Inspect the vendor class string and match the text to set the tag
          Ref: https://tools.ietf.org/html/rfc4578#section-2.1
        raw: |
          dhcp-vendorclass = BIOS,PXEClient:Arch:00000
          dhcp-vendorclass = UEFI32,PXEClient:Arch:00006
          dhcp-vendorclass = UEFI,PXEClient:Arch:00007
          dhcp-vendorclass = UEFI64,PXEClient:Arch:00009

      - name: 'boot-ipxe-local'
        comment: 'Set the boot file name based on the matching tag from the vendor class (above)'
        raw: |
          {% if dnsmasq__boot_ipxe_enabled | bool %}
          # Redirect non-iPXE clients to iPXE
          dhcp-boot = tag:!ipxe,tag:BIOS,undionly.kpxe
          dhcp-boot = tag:!ipxe,tag:UEFI32,i386-efi/ipxe.efi
          dhcp-boot = tag:!ipxe,tag:UEFI,ipxe.efi
          dhcp-boot = tag:!ipxe,tag:UEFI64,ipxe.efi

          # Load the main menu in iPXE clients
          {% endif %}
          dhcp-boot = {{ dnsmasq__boot_filename }}
        state: '{{ "absent" if dnsmasq__boot_server | d() else "present" }}'

      - name: 'boot-ipxe-remote'
        comment: 'Set the boot file name based on the matching tag from the vendor class (above)'
        raw: |
          {% if dnsmasq__boot_ipxe_enabled | bool %}
          # Redirect non-iPXE clients to iPXE
          dhcp-boot = tag:!ipxe,tag:BIOS,undionly.kpxe,,{{ dnsmasq__boot_server }}
          dhcp-boot = tag:!ipxe,tag:UEFI32,i386-efi/ipxe.efi,,{{ dnsmasq__boot_server }}
          dhcp-boot = tag:!ipxe,tag:UEFI,ipxe.efi,,{{ dnsmasq__boot_server }}
          dhcp-boot = tag:!ipxe,tag:UEFI64,ipxe.efi,,{{ dnsmasq__boot_server }}

          # Load the main menu in iPXE clients
          {% endif %}
          dhcp-boot = {{ dnsmasq__boot_filename }},,{{ dnsmasq__boot_server }}
        state: '{{ "present" if dnsmasq__boot_server | d() else "absent" }}'

    state: '{{ "present" if dnsmasq__boot_enabled | bool else "init" }}'
dnsmasq__interface_configuration

Automatically generated dnsmasq configuration for each of the network interfaces defined in the dnsmasq__combined_interfaces variable.

dnsmasq__interface_configuration: '{{ lookup("template", "lookup/dnsmasq__interface_configuration.j2",
                                             convert_data=False) | from_yaml }}'
dnsmasq__configuration

The configuration which should be present on all hosts in the Ansible inventory.

dnsmasq__configuration: []
dnsmasq__group_configuration

The configuration which should be present on hosts in a specific Ansible inventory group.

dnsmasq__group_configuration: []
dnsmasq__host_configuration

The configuration which should be present on specific hosts in the Ansible inventory.

dnsmasq__host_configuration: []
dnsmasq__combined_configuration

The variable which combines all of the other configuration variables and is used in the Ansible tasks.

dnsmasq__combined_configuration: '{{ dnsmasq__default_configuration
                                     + dnsmasq__interface_configuration
                                     + dnsmasq__configuration
                                     + dnsmasq__group_configuration
                                     + dnsmasq__host_configuration }}'

Configuration for other Ansible roles

dnsmasq__ferm__dependent_rules

Configuration for the debops.ferm Ansible role.

dnsmasq__ferm__dependent_rules:

  - type: 'accept'
    by_role: 'debops.dnsmasq'
    name: 'dns'
    weight: '40'
    protocol: [ 'udp', 'tcp' ]
    saddr: '{{ dnsmasq__public_dns_allow }}'
    dport: [ 'domain' ]
    accept_any: True
    interface: '{{ []
                   if (dnsmasq__public_dns | bool)
                   else (dnsmasq__combined_interfaces | flatten | debops.debops.parse_kv_items
                         | selectattr("state", "equalto", "present")
                         | map(attribute="name") | list) }}'
    rule_state: '{{ "present"
                    if ((dnsmasq__public_dns | bool) or
                        (dnsmasq__combined_interfaces | flatten | debops.debops.parse_kv_items
                            | selectattr("state", "equalto", "present")
                            | map(attribute="name") | list))
                    else "absent" }}'

  - type: 'accept'
    by_role: 'debops.dnsmasq'
    name: 'dhcpv4'
    weight: '40'
    protocol: [ 'udp' ]
    dport: [ 'bootps' ]
    interface: '{{ dnsmasq__combined_interfaces | flatten | debops.debops.parse_kv_items
                   | selectattr("state", "equalto", "present")
                   | map(attribute="name") | list }}'
    rule_state: '{{ "present"
                    if (dnsmasq__dhcpv4 | bool and
                        (dnsmasq__combined_interfaces | flatten | debops.debops.parse_kv_items
                         | selectattr("state", "equalto", "present")
                         | map(attribute="name") | list))
                    else "absent" }}'

  - type: 'accept'
    by_role: 'debops.dnsmasq'
    name: 'dhcpv6'
    weight: '40'
    saddr: [ 'fe80::/10' ]
    daddr: [ 'ff02::1:2' ]
    # https://tools.ietf.org/html/rfc3315#section-13
    protocol: [ 'udp' ]
    sport: [ 'dhcpv6-client' ]
    dport: [ 'dhcpv6-server' ]
    interface: '{{ dnsmasq__combined_interfaces | flatten | debops.debops.parse_kv_items
                   | selectattr("state", "equalto", "present")
                   | map(attribute="name") | list }}'
    rule_state: '{{ "present"
                    if (dnsmasq__dhcpv6 | bool and
                        (dnsmasq__combined_interfaces | flatten | debops.debops.parse_kv_items
                         | selectattr("state", "equalto", "present")
                         | map(attribute="name") | list))
                    else "absent" }}'

  - type: 'accept'
    by_role: 'debops.dnsmasq'
    filename: 'tftp'
    weight: '40'
    dport: [ 'tftp' ]
    protocol: [ 'udp' ]
    interface: '{{ dnsmasq__combined_interfaces | flatten | debops.debops.parse_kv_items
                   | selectattr("state", "equalto", "present")
                   | map(attribute="name") | list }}'
    rule_state: '{{ "present"
                    if (dnsmasq__boot_enabled | bool and
                        (dnsmasq__combined_interfaces | flatten | debops.debops.parse_kv_items
                         | selectattr("state", "equalto", "present")
                         | map(attribute="name") | list))
                    else "absent" }}'
dnsmasq__tcpwrappers__dependent_allow

Configuration for the debops.tcpwrappers Ansible role.

dnsmasq__tcpwrappers__dependent_allow: '{{ lookup("template", "lookup/dnsmasq__tcpwrappers__dependent_allow.j2",
                                             convert_data=False) | from_yaml }}'
dnsmasq__apparmor__local_dependent_config

Configuration for the debops-contrib.apparmor Ansible role.

dnsmasq__apparmor__local_dependent_config:

  'usr.sbin.dnsmasq':
    - comment: 'Allow dnsmasq to read upstream DNS servers'
      rules:
        - '/etc/resolvconf/upstream.conf r'
        - '/etc/hosts.dnsmasq r'
    - comment: 'Allow dnsmasq to read /usr/share/dnsmasq-base/trust-anchors.conf provided by dnsmasq-base'
      rules:
        - '/usr/share/dnsmasq-base/* r'
dnsmasq__persistent_paths__dependent_paths

Configuration for the debops.persistent_paths Ansible role.

dnsmasq__persistent_paths__dependent_paths:

  '50_debops_dnsmasq':
    by_role: 'debops.dnsmasq'
    paths:
      - '/etc/ansible'
      - '/etc/dnsmasq.d'
      - '/etc/default/dnsmasq'
      - '/etc/resolvconf/upstream.conf'
      - '/etc/hosts.dnsmasq'