├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── defaults └── main.yml ├── handlers └── main.yml ├── meta └── main.yml ├── tasks └── main.yml └── templates └── etc_dnsmasq.conf.j2 /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | 3 | # Hidden Vagrant-directory 4 | .vagrant 5 | 6 | # Backup files (e.g. Vim, Gedit, etc.) 7 | *~ 8 | .*~ 9 | 10 | # Vagrant base boxes (you never know when someone puts one in the repository) 11 | *.box 12 | *~ 13 | 14 | # BATS 15 | bats/ 16 | 17 | # Ignore the test directory 18 | tests/ 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | env: 4 | global: 5 | - container_id: $(mktemp) 6 | - role: /etc/ansible/roles/role_under_test 7 | - test_playbook: ${role}/tests/test.yml 8 | 9 | matrix: 10 | - distribution: ubuntu # Linux distribution 11 | version: 12.04 # Distribution version 12 | init: /sbin/init # Path to init executable (differs for SysVInit/Systemd) 13 | run_opts: "" # Additional options for running the Docker container 14 | - distribution: ubuntu 15 | version: 14.04 16 | init: /sbin/init 17 | run_opts: "" 18 | - distribution: centos 19 | version: 6 20 | init: /sbin/init 21 | run_opts: "" 22 | - distribution: centos 23 | version: 7 24 | init: /usr/lib/systemd/systemd 25 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 26 | 27 | services: 28 | - docker 29 | 30 | before_install: 31 | - sudo apt-get update 32 | - sudo apt-get --yes install dnsutils 33 | # Pull container 34 | - sudo docker pull bertvv/ansible-testing:${distribution}_${version} 35 | 36 | script: 37 | # Run container in detached state 38 | - > 39 | sudo docker run --detach --volume="${PWD}:${role}:ro" ${run_opts} 40 | bertvv/ansible-testing:${distribution}_${version} "${init}" > "${container_id}" 41 | 42 | # Ansible syntax check. 43 | - > 44 | sudo docker exec --tty "$(cat ${container_id})" env TERM=xterm 45 | ansible-playbook ${test_playbook} --syntax-check 46 | 47 | # Test role. 48 | - > 49 | sudo docker exec --tty "$(cat ${container_id})" env TERM=xterm 50 | ansible-playbook ${test_playbook} 51 | 52 | # Test role idempotence. 53 | - > 54 | sudo docker exec "$(cat ${container_id})" ansible-playbook ${test_playbook} 55 | | grep -q 'changed=0.*failed=0' 56 | && (echo 'Idempotence test: pass' && exit 0) 57 | || (echo 'Idempotence test: fail' && exit 1) 58 | 59 | - > 60 | "${role}/tests/runbats.sh" || ls -R "${role}" 61 | 62 | # Clean up 63 | - 'sudo docker stop "$(cat ${container_id})"' 64 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | This file contains al notable changes to the `bertvv.dnsmasq` Ansible role. 4 | 5 | This file adheres to the guidelines of [http://keepachangelog.com/](http://keepachangelog.com/). Versioning follows [Semantic Versioning](http://semver.org/). 6 | 7 | ## 2.1.0 - 2016-05-20 8 | 9 | ### Added 10 | 11 | - (GH-8) Specify upstream DNS servers with `dnsmasq_upstream_servers` (credits to [Niklas Juslin](https://github.com/JZfi)) 12 | - (GH-8) Specify SRV records with `dnsmasq_srv_hosts` (credits to [Niklas Juslin](https://github.com/JZfi)) 13 | 14 | ## 2.0.0 - 2016-04-22 15 | 16 | ### Added 17 | 18 | - Support for Fedora, including tests (on Fedora 23). 19 | 20 | ### Changed 21 | 22 | Some of the changes below result in different default behaviour of the role compared to previous version. These are considered breaking backwards compatibility, hence the major version increment. 23 | 24 | - Make `dnsmasq_listen_address` optional. This changes the default behaviour into listening to all interfaces. 25 | - Enable `dnsmasq_bogus_priv` by default for better security. 26 | - Enable `dnsmasq_domain_needed` by default for better security. 27 | - Use generic ‘package’ module for installation. This is a feature new in Ansible 2.0, so this version of the role can no longer be used on older versions of Ansible. This is also considered as a breaking change. 28 | - Separated test code into a different branch, `tests`. 29 | 30 | ## 1.2.1 - 2016-03-18 31 | 32 | ### Added 33 | 34 | - Functional tests (with [BATS](https://github.com/sstephenson/bats)) 35 | 36 | ### Removed 37 | 38 | - The `version:` field in `meta/main.yml` was removed because it is no longer accepted in Ansible 2.0. Unfortunately, this change breaks compatibility with `librarian-ansible`. For more info on this issue, see [ansible/ansible#](https://github.com/ansible/ansible/issues/13496). 39 | 40 | ## 1.2.0 - 2016-03-12 41 | 42 | ### Added 43 | 44 | - Role variable `dnsmasq_interface` (credits to [David Wittman](https://github.com/DavidWittman)) 45 | - Role variable `dnsmasq_server` (credits to [David Wittman](https://github.com/DavidWittman)) 46 | 47 | ### Removed 48 | 49 | - Setting firewall rules. This should not be a concern of this role. Use another role that manages the firewall specific to your distribution (e.g. [bertvv.el7](https://galaxy.ansible.com/bertvv/el7/)). 50 | 51 | ## 1.1.0 - 2015-08-31 52 | 53 | ### Added 54 | 55 | - Role variable `dnsmasq_authoritative`: when true, dnsmasq will function as an authoritative name server. (credits to [Chris James](https://github.com/etcet)) 56 | - Config files in `/etc/dnsmasq.d/` will now also be read 57 | 58 | ### Changed 59 | 60 | - Fixed typo (credits to [Chris James](https://github.com/etcet)) 61 | 62 | ## 1.0.1 - 2015-03-14 63 | 64 | ### Added 65 | 66 | - Role test with Vagrant 67 | 68 | ### Changed 69 | 70 | - Remodeled firewall rules 71 | - Updated documentation 72 | - Coding style: use valid YAML instead of Ansible specific `var=val` syntax. 73 | 74 | ## 1.0.0 - 2014-08-15 75 | 76 | ### Added 77 | 78 | - DNS forwarding 79 | - DHCP server 80 | 81 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Simplified BSD License 2 | 3 | Copyright (c) 2014, Bert Van Vreckem . 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dnsmasq 2 | 3 | An Ansible role for setting up Dnsmasq under CentOS/RHEL 7 and Fedora 16 or newer as a simple DNS forwarder, and/or DHCP server. Specifically, the responsibilities of this role are to install the necessary packages and manage the configuration. 4 | 5 | Configuring the firewall is outside the scope of this role. Use another role suitable for your distribution, e.g. [bertvv.el7](https://galaxy.ansible.com/bertvv/el7/). 6 | 7 | If you like/use this role, please consider starring it. Thanks! 8 | 9 | 10 | ## Requirements 11 | 12 | No specific requirements. 13 | 14 | ## Role Variables 15 | 16 | None of the variables below are required. 17 | 18 | | Variable | Default | Comments | 19 | | :--- | :--- | :--- | 20 | | `dnsmasq_addn_hosts` | - | Set this to specify a custom host file that should be read in addition to `/etc/hosts`. | 21 | | `dnsmasq_authoritative` | `false` | When `true`, dnsmasq will function as an authoritative name server. | 22 | | `dnsmasq_bogus_priv` | `true` | When `true`, Dnsmasq will not forward addresses in the non-routed address spaces. | 23 | | `dnsmasq_dhcp_hosts` | - | Array of hashes specifying IP address reservations for hosts, with keys `name` (optional), `mac` and `ip` for each reservation. See below. | 24 | | `dnsmasq_dhcp_ranges` | - | Array of hashes specifying DHCP ranges (with keys `start_addr`, `end_addr`, and `lease_time`) for each address pool. This also enables DHCP. See below. | 25 | | `dnsmasq_domain_needed` | `true` | When `true`, local requests (i.e. without domain name) are not forwarded. | 26 | | `dnsmasq_domain` | - | The domain for Dnsmasq. | 27 | | `dnsmasq_expand_hosts` | `false` | Set this (and `dnsmasq_domain`) if you want to have a domain automatically added to simple names in a hosts-file. | 28 | | `dnsmasq_listen_address` | - | The IP address of the interface that should listen to DNS/DHCP requests. | 29 | | `dnsmasq_interface` | - | The network interface that should listen to DNS/DHCP requests. | 30 | | `dnsmasq_option_router` | - | The default gateway to be sent to clients. | 31 | | `dnsmasq_port` | - | Set this to listen on a custom port. | 32 | | `dnsmasq_resolv_file` | - | Set this to specify a custom `resolv.conf` file. | 33 | | `dnsmasq_upstream_servers` | - | Set this to specify the IP address of upstream DNS servers directly. You can specify one ore more servers as a list. | 34 | | `dnsmasq_srv_hosts` | - | Array of hashes specifying SRV records, with keys `name` (mandatory), `target`, `port`, `priority` and `weight` for each record. See below. | 35 | 36 | ### DNS settings 37 | 38 | One or more upstream DNS servers can can be specified with the variable `dnsmasq_server`, e.g.: 39 | 40 | ```Yaml 41 | dnsmasq_upstream_servers: ns1.example.com 42 | OR 43 | dnsmasq_upstream_servers: 44 | - 8.8.4.4 45 | - 8.8.8.8 46 | ``` 47 | 48 | SRV records (see [dnsmasq(8)](http://linux.die.net/man/8/dnsmasq) or [RFC 2782](https://www.ietf.org/rfc/rfc2782.txt)) can be specified with `dnsmasq_srv_hosts`, e.g.: 49 | 50 | ```Yaml 51 | dnsmasq_srv_hosts: 52 | - name: _ldap._tcp.example.com 53 | target: ldap01.example.com 54 | port: 389 55 | - name: _ldap._tcp.example.com 56 | target: ldap02.example.com 57 | port: 389 58 | ``` 59 | 60 | ### DHCP settings 61 | 62 | A DHCP range can be specified with the variable `dnsmasq_dhcp_ranges`, e.g.: 63 | 64 | ```Yaml 65 | dnsmasq_dhcp_ranges: 66 | - start_addr: '192.168.6.150' 67 | end_addr: '192.168.6.253' 68 | lease_time: '8h' 69 | ``` 70 | 71 | IP address reservations based on MAC addres can be specified with `dnsmasq_dhcp_hosts`, e.g.: 72 | 73 | ```Yaml 74 | dnsmasq_dhcp_hosts: 75 | - name: 'alpha' 76 | mac: '11:22:33:44:55:66' 77 | ip: '192.168.6.10' 78 | - name: 'bravo' 79 | mac: 'aa:bb:cc:dd:ee:ff' 80 | ip: '192.168.6.11' 81 | ``` 82 | 83 | ## Dependencies 84 | 85 | None, but role [bertvv.hosts](https://galaxy.ansible.com/bertvv/hosts/) may come in handy if you want an easy way to manage the contents of `/etc/hosts`. 86 | 87 | ## Example Playbook 88 | 89 | Most Dnsmasq settings have sane defaults and don't have to be specified. The simplest configuration would be a DNS forwarder with default settings: 90 | 91 | ```Yaml 92 | - hosts: server 93 | roles: 94 | - bertvv.dnsmasq 95 | ``` 96 | 97 | A more elaborate example, with DHCP can be found in the [test playbook](https://github.com/bertvv/ansible-dnsmasq/blob/tests/test.yml). 98 | 99 | ## Testing 100 | 101 | ### Setting up the test environment 102 | 103 | Tests for this role are provided in the form of a Vagrant environment that is kept in a separate branch, `tests`. I use [git-worktree(1)](https://git-scm.com/docs/git-worktree) to include the test code into the working directory. Instructions for running the tests: 104 | 105 | 1. Fetch the tests branch: `git fetch origin tests` 106 | 2. Create a Git worktree for the test code: `git worktree add tests tests` (remark: this requires at least Git v2.5.0). This will create a directory `tests/`. 107 | 3. `cd tests/` 108 | 4. `vagrant up` will then create test VMs for all supported distros and apply a test playbook (`test.yml`) to each one. 109 | 110 | ### Running the tests 111 | 112 | The directory also contains a set of functional tests that validate whether the Dnsmasq service actually works on the supported distributions. You can run the tests from the host system by executing the script `runtests.sh`. When needed, the script will install [BATS](https://github.com/sstephenson/bats), a testing framework for Bash. 113 | 114 | | **Hostname** | **IP** | 115 | | :--- | :--- | 116 | | `centos72-dnsmasq | 192.168.6.66 | 117 | | `fedora23-dnsmasq | 192.168.6.67 | 118 | 119 | Run the test script from within its containing directory. If successful, you should see the following output: 120 | 121 | ``` 122 | $ ./runtests.sh 123 | --- Running tests for host 192.168.6.66 --- 124 | ✓ The `dig` command should be installed 125 | ✓ Forward lookups 126 | ✓ Reverse lookups 127 | 128 | 3 tests, 0 failures 129 | --- Running tests for host 192.168.6.67 --- 130 | ✓ The `dig` command should be installed 131 | ✓ Forward lookups 132 | ✓ Reverse lookups 133 | 134 | 3 tests, 0 failures 135 | ``` 136 | 137 | In the console transcript above, the output of installing BATS is not shown. 138 | 139 | ## See also 140 | 141 | Debian/Ubuntu users can take a look at [Debops](https://galaxy.ansible.com/debops/)'s [Dnsmasq role](https://galaxy.ansible.com/debops/dnsmasq/). 142 | 143 | ## Contributing 144 | 145 | Issues, feature requests, ideas are appreciated and can be posted in the Issues section. Pull requests are also very welcome. Preferably, create a topic branch and when submitting, squash your commits into one (with a descriptive message). 146 | 147 | ## License 148 | 149 | Licensed under the 2-clause "Simplified BSD License". See [LICENSE.md](/LICENSE.md) for details. 150 | 151 | ## Contributors 152 | 153 | - [Bert Van Vreckem](https://github.com/bertvv) (maintainer) 154 | - [Chris James](https://github.com/etcet) 155 | - [David Wittman](https://github.com/DavidWittman) 156 | - [Niklas Juslin](https://github.com/JZfi) 157 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | # roles/dnsmasq/defaults/main.yml 2 | --- 3 | dnsmasq_domain_needed: false 4 | dnsmasq_bogus_priv: true 5 | dnsmasq_authoritative: false 6 | dnsmasq_expand_hosts: false 7 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | # roles/dnsmasq/handlers/main.yml 2 | --- 3 | - name: restart dnsmasq 4 | service: 5 | name: dnsmasq 6 | state: restarted 7 | 8 | - name: restart firewalld 9 | service: 10 | name: dnsmasq 11 | state: restarted 12 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Bert Van Vreckem 4 | description: A role for setting up a Dnsmasq server on CentOS/RHEL 7 or Fedora 16 or newer. 5 | license: Simplified BSD License 6 | min_ansible_version: 2.0 7 | platforms: 8 | - name: EL 9 | versions: 10 | - 7 11 | - name: Fedora 12 | versions: 13 | - 16 14 | - 17 15 | - 18 16 | - 19 17 | - 20 18 | - 21 19 | - 22 20 | - 23 21 | galaxy_tags: 22 | - networking 23 | - bind 24 | - dns 25 | dependencies: [] 26 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | # roles/dnsmasq/tasks/main.yml 2 | --- 3 | - name: Install Dnsmasq 4 | package: 5 | name: dnsmasq 6 | state: present 7 | tags: dnsmasq 8 | 9 | - name: Set configuration file 10 | template: 11 | src: etc_dnsmasq.conf.j2 12 | dest: /etc/dnsmasq.conf 13 | validate: 'dnsmasq --test --conf-file=%s' 14 | notify: restart dnsmasq 15 | tags: dnsmasq 16 | 17 | - name: Make sure Dnsmasq is running 18 | service: 19 | name: dnsmasq 20 | state: started 21 | enabled: yes 22 | tags: dnsmasq 23 | -------------------------------------------------------------------------------- /templates/etc_dnsmasq.conf.j2: -------------------------------------------------------------------------------- 1 | # Dnsmasq configuration 2 | # {{ ansible_managed }} 3 | 4 | {% if dnsmasq_listen_address is defined %} 5 | listen-address={{ dnsmasq_listen_address }} 6 | {% endif %} 7 | {% if dnsmasq_interface is defined %} 8 | interface={{ dnsmasq_interface }} 9 | {% endif %} 10 | {% if dnsmasq_port is defined %} 11 | port={{ dnsmasq_port }} 12 | 13 | {% endif %} 14 | {% if dnsmasq_domain_needed %} 15 | domain-needed 16 | {% endif %} 17 | {% if dnsmasq_bogus_priv %} 18 | bogus-priv 19 | {% endif %} 20 | 21 | {% if dnsmasq_resolv_file is defined %} 22 | resolv-file={{ dnsmasq_resolv_file }} 23 | 24 | {% endif %} 25 | {% if dnsmasq_addn_hosts is defined %} 26 | addn-hosts={{ dnsmasq_addn_hosts }} 27 | 28 | {% endif %} 29 | {% if dnsmasq_expand_hosts %} 30 | expand-hosts 31 | {% endif %} 32 | {% if dnsmasq_domain is defined %} 33 | domain={{ dnsmasq_domain }} 34 | {% endif %} 35 | {% if dnsmasq_dhcp_ranges is defined %} 36 | {% for range in dnsmasq_dhcp_ranges %} 37 | dhcp-range={{ range.start_addr }},{{ range.end_addr }},{{ range.lease_time }} 38 | {% endfor%} 39 | 40 | {% endif %} 41 | {% if dnsmasq_dhcp_hosts is defined %} 42 | {% for host in dnsmasq_dhcp_hosts %} 43 | dhcp-host={{ host.mac }},{{ host.ip }}{% if host.name is defined %},{{ host.name }}{% endif %} 44 | 45 | {% endfor %} 46 | 47 | {% endif %} 48 | {% if dnsmasq_option_router is defined %} 49 | dhcp-option=option:router,{{ dnsmasq_option_router }} 50 | {% endif %} 51 | 52 | {% if dnsmasq_upstream_servers is defined %} 53 | {% if dnsmasq_upstream_servers is iterable %} 54 | {% for host in dnsmasq_upstream_servers %} 55 | server={{ host }} 56 | {% endfor %} 57 | {% else %} 58 | server={{ dnsmasq_upstream_servers }} 59 | {% endif %} 60 | {% endif %} 61 | 62 | {% if dnsmasq_authoritative %} 63 | dhcp-authoritative 64 | {% endif %} 65 | 66 | {% if dnsmasq_srv_hosts is defined %} 67 | {% for host in dnsmasq_srv_hosts %} 68 | srv-host={{ host.name }}{% if host.target is defined %},{{ host.target }}{% endif %}{% if host.port is defined %},{{ host.port }}{% endif %}{% if host.priority is defined %},{{ host.priority }}{% endif %}{% if host.weight is defined %},{{ host.weight }}{% endif %} 69 | 70 | {% endfor %} 71 | {% endif %} 72 | 73 | conf-dir=/etc/dnsmasq.d 74 | --------------------------------------------------------------------------------