Merge pull request #5 from drybjed/dhcp-relay
Various fixes in the debops.dhcpd role
This commit is contained in:
commit
cff2f573d5
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
|
sudo: True
|
||||||
language: 'python'
|
language: 'python'
|
||||||
python: '2.7'
|
python: '2.7'
|
||||||
|
|
||||||
|
8
CHANGES.rst
Normal file
8
CHANGES.rst
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
Changelog
|
||||||
|
=========
|
||||||
|
|
||||||
|
v0.1.0
|
||||||
|
------
|
||||||
|
|
||||||
|
- First release [drybjed]
|
||||||
|
|
11
README.md
11
README.md
@ -2,7 +2,14 @@
|
|||||||
|
|
||||||
[![Travis CI](http://img.shields.io/travis/debops/ansible-dhcpd.svg?style=flat)](http://travis-ci.org/debops/ansible-dhcpd) [![test-suite](http://img.shields.io/badge/test--suite-ansible--dhcpd-blue.svg?style=flat)](https://github.com/debops/test-suite/tree/master/ansible-dhcpd/) [![Ansible Galaxy](http://img.shields.io/badge/galaxy-debops.dhcpd-660198.svg?style=flat)](https://galaxy.ansible.com/list#/roles/1559)
|
[![Travis CI](http://img.shields.io/travis/debops/ansible-dhcpd.svg?style=flat)](http://travis-ci.org/debops/ansible-dhcpd) [![test-suite](http://img.shields.io/badge/test--suite-ansible--dhcpd-blue.svg?style=flat)](https://github.com/debops/test-suite/tree/master/ansible-dhcpd/) [![Ansible Galaxy](http://img.shields.io/badge/galaxy-debops.dhcpd-660198.svg?style=flat)](https://galaxy.ansible.com/list#/roles/1559)
|
||||||
|
|
||||||
Install and configure [ISC DHCP Server](https://www.isc.org/downloads/dhcp/).
|
`debops.dhcpd` role can be used to configure an [ISC DHCP
|
||||||
|
Server](https://www.isc.org/downloads/dhcp/) as standalone or in a 2-host
|
||||||
|
failover configuration. Alternatively, you can configure an DHCP relay on
|
||||||
|
a host connected to multiple networks which will relay DHCP/BOOTP messages
|
||||||
|
to your DHCP server.
|
||||||
|
|
||||||
|
`dhcp-probe` script will be used to scan the network for unauthorized DHCP
|
||||||
|
servers and notify administrators if they are found.
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
@ -13,7 +20,7 @@ This role requires at least Ansible `v1.7.0`. To install it, run:
|
|||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
More information about `debops.dhcpd` can be found in the
|
More information about `debops.dhcpd` can be found in the
|
||||||
[official debops.dhcpd documentation](http://docs.debops.org/en/latest/ansible/roles/debops.dhcpd.html).
|
[official debops.dhcpd documentation](http://docs.debops.org/en/latest/ansible/roles/ansible-dhcpd/docs/).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,285 +1,276 @@
|
|||||||
---
|
---
|
||||||
|
# Default variables
|
||||||
|
# =================
|
||||||
|
|
||||||
# ---- Global ISC DHCP Server configuration ----
|
# .. contents:: Sections
|
||||||
|
# :local:
|
||||||
|
#
|
||||||
|
# -------------------
|
||||||
|
# General options
|
||||||
|
# -------------------
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_mode
|
||||||
|
#
|
||||||
|
# What service type to configure on this host:
|
||||||
|
#
|
||||||
|
# - ``server``: host is an ISC DHCP server, see ``dhcpd(8)``
|
||||||
|
#
|
||||||
|
# - ``relay``: host is an ISC DHCP relay, see dhcrelay(8)
|
||||||
|
#
|
||||||
|
# - ``probe``: configure only ``dhcp-probe`` when enabled
|
||||||
|
#
|
||||||
|
dhcpd_mode: 'server'
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_ipversion
|
||||||
|
#
|
||||||
|
# Internet Protocol version to configure: ``4`` or ``6``
|
||||||
|
dhcpd_ipversion: '4'
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_base_packages_map
|
||||||
|
#
|
||||||
|
# What packages should be installed, depending on mode of operation
|
||||||
|
dhcpd_base_packages_map:
|
||||||
|
'server': [ 'isc-dhcp-server' ]
|
||||||
|
'relay': [ 'isc-dhcp-relay' ]
|
||||||
|
'probe': []
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------
|
||||||
|
# ISC DHCP Relay configuration
|
||||||
|
# --------------------------------
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_relay_servers
|
||||||
|
#
|
||||||
|
# List of DHCP servers which should receive the relayed packets
|
||||||
|
dhcpd_relay_servers: [ '{{ ansible_default_ipv4.gateway }}' ]
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_relay_interfaces
|
||||||
|
#
|
||||||
|
# List of network interfaces that dhcrelay should listen on
|
||||||
|
dhcpd_relay_interfaces: []
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_relay_options
|
||||||
|
#
|
||||||
|
# Additional dhcrelay options
|
||||||
|
dhcpd_relay_options: '{{ "-" + dhcpd_ipversion }}'
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------
|
||||||
|
# ISC DHCP Server configuration
|
||||||
|
# ---------------------------------
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_server_options
|
||||||
|
#
|
||||||
|
# dhcpd(8) options
|
||||||
|
dhcpd_server_options: '{{ "-" + dhcpd_ipversion }}'
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------
|
||||||
|
# DHCP main configuration
|
||||||
|
# ---------------------------
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_authoritative
|
||||||
|
#
|
||||||
# Is this DHCP server authoritative?
|
# Is this DHCP server authoritative?
|
||||||
dhcpd_authoritative: False
|
dhcpd_authoritative: False
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_log_facility
|
||||||
|
#
|
||||||
|
# Log facility to use
|
||||||
|
dhcpd_log_facility: 'local7'
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_interfaces
|
||||||
|
#
|
||||||
# List of network interfaces to listen on for DHCP requests
|
# List of network interfaces to listen on for DHCP requests
|
||||||
# If this list is empty, Ansible will try to guess correct interfaces
|
# If this list is empty, Ansible will try to guess correct interfaces
|
||||||
# automatically
|
# automatically
|
||||||
dhcpd_interfaces: []
|
dhcpd_interfaces: []
|
||||||
|
|
||||||
# Default domain to use
|
|
||||||
dhcpd_domain: '{{ ansible_domain }}'
|
|
||||||
|
|
||||||
# List of default DNS servers. By default, point users to the same host that
|
|
||||||
# serves them DHCP requests, on default interface. If this host is a router,
|
|
||||||
# you might need to set DNS server to internal interface IP address.
|
|
||||||
dhcpd_dns_servers: [ '{{ ansible_default_ipv4.address }}' ]
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_lease_time
|
||||||
|
#
|
||||||
# Max lease time in hours (default lease time is calculated below)
|
# Max lease time in hours (default lease time is calculated below)
|
||||||
dhcpd_lease_time: 24
|
dhcpd_lease_time: '24'
|
||||||
|
|
||||||
# Default global options formatted as a text block
|
|
||||||
dhcpd_global_options: |
|
|
||||||
option domain-name "{{ ansible_domain }}";
|
|
||||||
option domain-name-servers {{ dhcpd_dns_servers | join(' ') }};
|
|
||||||
default-lease-time {{ (((dhcpd_lease_time / 2) + 6) * 60 * 60)|round|int }};
|
|
||||||
max-lease-time {{ (dhcpd_lease_time * 60 * 60)|round|int }};
|
|
||||||
log-facility local7;
|
|
||||||
|
|
||||||
# Custom options formatted as a text block
|
# .. envvar:: dhcpd_global_default_lease_time
|
||||||
|
#
|
||||||
|
# Default lease time for all IP address leases (18 hours)
|
||||||
|
dhcpd_global_default_lease_time: '{{ (((dhcpd_lease_time|int / 2) + 6) * 60 * 60)|round|int }}'
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_global_max_lease_time
|
||||||
|
#
|
||||||
|
# Maximum lease time for all IP addresses (24 hours)
|
||||||
|
dhcpd_global_max_lease_time: '{{ (dhcpd_lease_time|int * 60 * 60)|round|int }}'
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------
|
||||||
|
# DHCP advertised options
|
||||||
|
# ---------------------------
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_auto_options
|
||||||
|
#
|
||||||
|
# If enabled, ISC DHCP server will be configured with a set of automatically
|
||||||
|
# detected options. See ``auto_options.j2`` template for more details.
|
||||||
|
dhcpd_auto_options: True
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_domain_name
|
||||||
|
#
|
||||||
|
# Default host domain to advertise
|
||||||
|
dhcpd_domain_name: '{{ ansible_domain }}'
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_domain_search
|
||||||
|
#
|
||||||
|
# List of additional domains which should be checked when looking for hostnames
|
||||||
|
dhcpd_domain_search: []
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_nameservers
|
||||||
|
#
|
||||||
|
# List of nameservers to advertise by default
|
||||||
|
# If it's not specified, nameservers from ``/etc/resolv.conf`` will be used
|
||||||
|
# instead.
|
||||||
|
dhcpd_nameservers: []
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_options
|
||||||
|
#
|
||||||
|
# Custom global options formatted as a text block
|
||||||
dhcpd_options: False
|
dhcpd_options: False
|
||||||
|
|
||||||
|
|
||||||
# ---- ISC DHCP Server configuration scopes ----
|
# ----------------------------------------
|
||||||
|
# ISC DHCP Server configuration scopes
|
||||||
|
# ----------------------------------------
|
||||||
|
|
||||||
# These lists allow you to generate nested configuration scopes in
|
# These lists allow you to generate nested configuration scopes in
|
||||||
# dhcpd.conf. Most of the information about them can be found in dhcpd.conf(5)
|
# dhcpd.conf. Most of the information about them can be found in dhcpd.conf(5)
|
||||||
# manual page. You can create nested configuration using Ansible variable
|
# manual page. You can create nested configuration using Ansible variable
|
||||||
# expansion (examples below).
|
# expansion.
|
||||||
|
|
||||||
# List of general configuration parameters (work in any configuration scope):
|
|
||||||
# - comment: '' add a comment to a scope
|
|
||||||
# - options: | custom options for that scope defined as a text block
|
|
||||||
# - include: '' path to external file to include in this scope
|
|
||||||
|
|
||||||
# List of hosts (works in groups, subnets):
|
|
||||||
# - hosts: '' or [] list of hosts to configure in that scope; if this is
|
|
||||||
# a path to a file, dhcpd will include an external file
|
|
||||||
# in this scope
|
|
||||||
|
|
||||||
# List of parameters specific to dhcpd_classes:
|
|
||||||
# - class: '' class name
|
|
||||||
# - subclass: this is a hash with expression as key and additional
|
|
||||||
# options as value in a text block (see example below);
|
|
||||||
# each match expression must end with a colon to indicate
|
|
||||||
# hash key; optional
|
|
||||||
|
|
||||||
# List of parameters specific to dhcpd_groups:
|
|
||||||
# - subnets: [] list of subnet scopes to group together
|
|
||||||
# - groups: [] list of other group scopes to include. No recursion!
|
|
||||||
|
|
||||||
# List of parameters specific to dhcpd_shared_networks:
|
|
||||||
# - name: '' name of shared network
|
|
||||||
# - subnets: [] list of subnets in a shared network (do not use
|
|
||||||
# dhcpd_subnets here, because they will be duplicated
|
|
||||||
# and DHCP server will not start)
|
|
||||||
|
|
||||||
# List of parameters specific to dhcpd_subnets:
|
|
||||||
# - subnet: '' start of a subnet range (ie.: 192.168.1.0)
|
|
||||||
# - netmask: '' netmask for this subnet (ie.: 255.255.255.0)
|
|
||||||
# - routers: '' or [] address or list of addresses of gateway for that
|
|
||||||
# subnet (ie.: 192.168.1.1)
|
|
||||||
|
|
||||||
# List of parameters specific to dhcpd_hosts:
|
|
||||||
# - hostname: '' hostname, without domain part
|
|
||||||
# - address: '' IP address reserved for that host, optional
|
|
||||||
# - ethernet: '' Ethernet MAC address of this host, optional
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_keys
|
||||||
|
#
|
||||||
|
# List of secret keys used for Dynamic DNS configuration. See
|
||||||
|
# :ref:`dhcpd_keys` for more details.
|
||||||
dhcpd_keys: []
|
dhcpd_keys: []
|
||||||
#- key: "secure-key"
|
|
||||||
# algorithm: "hmac-md5"
|
|
||||||
# secret: "JFw7jM2/KVU2hIB4xkDSQmHB6JJOLUu4xkzwLNNpR88="
|
|
||||||
|
|
||||||
# List of classes
|
|
||||||
|
# .. envvar:: dhcpd_zones
|
||||||
|
#
|
||||||
|
# List of DNS zones to update with Dynamic DNS configuration. See
|
||||||
|
# :ref:`dhcpd_zones` for more details.
|
||||||
|
dhcpd_zones: []
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_classes
|
||||||
|
#
|
||||||
|
# List of client classes (see dhcpd.conf(5)). More informaction can be found in
|
||||||
|
# :ref:`dhcpd_classes`.
|
||||||
dhcpd_classes: []
|
dhcpd_classes: []
|
||||||
#- class 'example-class'
|
|
||||||
# subclass:
|
|
||||||
# 'match1':
|
|
||||||
# 'match2': |
|
|
||||||
# # match2 options in a text block;
|
|
||||||
|
|
||||||
#- class 'example-empty-class'
|
|
||||||
|
|
||||||
|
|
||||||
# List of groups
|
# .. envvar:: dhcpd_groups
|
||||||
|
#
|
||||||
|
# List of configuration scopes groped together. See :ref:`dhcpd_groups` for
|
||||||
|
# more details.
|
||||||
dhcpd_groups: []
|
dhcpd_groups: []
|
||||||
#- comment: 'First group'
|
|
||||||
# hosts: '/etc/dhcp/dhcpd-group1-hosts.conf'
|
|
||||||
# groups: '{{ dhcpd_group_second }}'
|
|
||||||
|
|
||||||
# An example of group nesting
|
|
||||||
#dhcpd_group_second:
|
|
||||||
# - comment: 'Second group'
|
|
||||||
# hosts: '/etc/dhcp/dhcpd-group2-hosts.conf'
|
|
||||||
|
|
||||||
|
|
||||||
# List of shared networks
|
# .. envvar:: dhcpd_shared_networks
|
||||||
|
#
|
||||||
|
# List of shared networks grouping specified subnets together. See
|
||||||
|
# :ref:`dhcpd_shared_networks` for more details.
|
||||||
dhcpd_shared_networks: []
|
dhcpd_shared_networks: []
|
||||||
#- name: 'shared-net'
|
|
||||||
# comment: "Local shared network"
|
|
||||||
# subnets: '{{ dhcpd_subnets_local }}'
|
|
||||||
# options: |
|
|
||||||
# default-lease-time 600;
|
|
||||||
# max-lease-time 900;
|
|
||||||
|
|
||||||
|
|
||||||
# List of subnets not in a shared network
|
# .. envvar:: dhcpd_subnets
|
||||||
dhcpd_subnets:
|
#
|
||||||
- subnet: '{{ ansible_default_ipv4.network }}'
|
# List of subnets not in a shared network. See :ref:`dhcpd_subnets` for more
|
||||||
netmask: '{{ ansible_default_ipv4.netmask }}'
|
# details.
|
||||||
|
dhcpd_subnets: [ '{{ dhcpd_subnet_default[dhcpd_ipversion] }}' ]
|
||||||
|
|
||||||
|
# Default subnet managed automatically
|
||||||
|
dhcpd_subnet_default:
|
||||||
|
'4':
|
||||||
|
subnet: '{{ ansible_default_ipv4.network + "/" + ansible_default_ipv4.netmask }}'
|
||||||
|
routers: '{{ ansible_default_ipv4.gateway | default("") }}'
|
||||||
|
comment: 'Generated automatically by Ansible'
|
||||||
|
'6':
|
||||||
|
subnet: '{{ ansible_default_ipv6.address + "/" + ansible_default_ipv6.prefix }}'
|
||||||
comment: 'Generated automatically by Ansible'
|
comment: 'Generated automatically by Ansible'
|
||||||
|
|
||||||
#- subnet: 'dead:be:ef::/64'
|
|
||||||
# ipv6: True
|
# .. envvar:: dhcpd_hosts
|
||||||
# routers: '10.0.10.1'
|
|
||||||
# comment: "Example IPv6 subnet"
|
|
||||||
# options: |
|
|
||||||
# default-lease-time 300;
|
|
||||||
# max-lease-time 7200;
|
|
||||||
#
|
#
|
||||||
#- subnet: '10.0.20.0'
|
# Global list of hosts in DHCP. See ref:`dhcpd_hosts` for more details.
|
||||||
# netmask: '255.255.255.0'
|
|
||||||
# comment: 'Ignored subnet'
|
|
||||||
|
|
||||||
# An example subnets included in a shared network
|
|
||||||
#dhcpd_subnets_local:
|
|
||||||
# - subnet: '10.0.30.0'
|
|
||||||
# netmask: '255.255.255.0'
|
|
||||||
# routers: [ '10.0.30.1', '10.0.30.2' ]
|
|
||||||
#
|
|
||||||
# - subnet: '10.0.40.0'
|
|
||||||
# netmask: '255.255.255.0'
|
|
||||||
# routers: '19.0.40.1'
|
|
||||||
# options: |
|
|
||||||
# default-lease-time 300;
|
|
||||||
# max-lease-time 7200;
|
|
||||||
# pools:
|
|
||||||
# - comment: "A pool in a subnet"
|
|
||||||
# range: '10.0.30.10 10.0.30.20'
|
|
||||||
|
|
||||||
|
|
||||||
# Global list of hosts in DHCP
|
|
||||||
dhcpd_hosts: []
|
dhcpd_hosts: []
|
||||||
# - hostname: 'examplehost'
|
|
||||||
# address: '10.0.10.1'
|
|
||||||
# ethernet: '00:00:00:00:00:00'
|
|
||||||
|
|
||||||
# Example global list of hosts read from an external file
|
|
||||||
#dhcpd_hosts: '/etc/dhcp/dhcpd.hosts.conf'
|
|
||||||
|
|
||||||
|
|
||||||
# List of external files to include
|
# List of external files to include. See :ref:`dhcpd_includes` for more
|
||||||
|
# details.
|
||||||
dhcpd_includes: []
|
dhcpd_includes: []
|
||||||
#- '/etc/dhcp/example.conf'
|
|
||||||
|
|
||||||
# ---- ISC DHCP failover configuration ----
|
|
||||||
#
|
# .. envvar:: dhcpd_failovers
|
||||||
# Each 'failover pair' declaration consists of primary and secondary host,
|
|
||||||
# no more than two nodes failover is currently allowed by isc-dhcpd.
|
|
||||||
#
|
|
||||||
# You must specify which failover pair each pool should use by specifying a
|
|
||||||
# 'failover peer' statement under an 'options' block in each pool declaration.
|
|
||||||
# e.g:
|
|
||||||
#
|
|
||||||
# dhcpd_failovers:
|
|
||||||
# - failover: "my-failover"
|
|
||||||
# primary: '10.0.30.1'
|
|
||||||
# secondary: '10.0.30.2'
|
|
||||||
# ...
|
|
||||||
#
|
|
||||||
# dhcpd_subnets:
|
|
||||||
# - subnet: ...
|
|
||||||
# ...
|
|
||||||
# pools:
|
|
||||||
# - comment: "My pool with failover"
|
|
||||||
# range: '10.0.30.10 10.0.30.20'
|
|
||||||
# options: |
|
|
||||||
# failover peer "my-failover";
|
|
||||||
#
|
|
||||||
# Each failover declaration has a set of an mandatory fields, which is:
|
|
||||||
# primary: "" Ansible inventory name of a primary DHCP host, if
|
|
||||||
# you need failover to work on different IP,
|
|
||||||
# see primary_fo_addr option below.
|
|
||||||
#
|
|
||||||
# secondary: "" Ansible inventory name of a secondary DHCP host, if
|
|
||||||
# you need failover to work on different IP,
|
|
||||||
# see secondary_fo_addr option below.
|
|
||||||
#
|
|
||||||
# Ansible inventory name is either IP ot hostname specified in inventory file.
|
|
||||||
#
|
|
||||||
# mclt: 3600 Max Client Lead Time. The maximum amount of time
|
|
||||||
# that one server can extend a lease for a DHCP
|
|
||||||
# client beyond the time known by the partner server.
|
|
||||||
#
|
|
||||||
# split: [0-255] Specifies the split between the primary and
|
|
||||||
# secondary for the purposes of load balancing.
|
|
||||||
# Whenever a client makes a DHCP request, the DHCP
|
|
||||||
# server runs a hash on the client identification,
|
|
||||||
# resulting in value from 0 to 255. This is used as
|
|
||||||
# an index into a 256 bit field. If the bit at that
|
|
||||||
# index is set, the primary is responsible. If
|
|
||||||
# the bit at that index is not set, the secondary
|
|
||||||
# is responsible.
|
|
||||||
# -- or --
|
|
||||||
# hba: ([0-9a-f]{2}:){32} Specifies the split between the primary and
|
|
||||||
# secondary as a bitmap rather than a cutoff, which
|
|
||||||
# theoretically allows for finer-grained control.
|
|
||||||
# In practice, there is probably no need for such
|
|
||||||
# fine-grained control, however.
|
|
||||||
# max_response_delay: 5 Tells the DHCP server how many seconds may pass
|
|
||||||
# without receiving a message from its failover peer
|
|
||||||
# before it assumes that connection has failed.
|
|
||||||
# This is mandatory according to dhcpd.conf man page.
|
|
||||||
# max_unacked_updates: 10 Tells the remote DHCP server how many BNDUPD
|
|
||||||
# messages it can send before it receives a BNDACK
|
|
||||||
# from the local system.
|
|
||||||
# This is mandatory according to dhcpd.conf man page.
|
|
||||||
#
|
|
||||||
# You must use either 'split' or 'hba' statement. Split has a preference, so
|
|
||||||
# if it's defined, 'hba' will be omitted by configuration template.
|
|
||||||
# Optional field are mostly desribed in dhcpd.conf man page:
|
|
||||||
# port: 647 Specifies port on which primary and secondary
|
|
||||||
# nodes will listen for failover connection.
|
|
||||||
# Diffirent ports for primary and secondary is
|
|
||||||
# currently unsupported.
|
|
||||||
#
|
|
||||||
# primary_fo_addr: "" IP/Hostname of a primary DHCP host. This option
|
|
||||||
# is used if you need failover address be different
|
|
||||||
# from ansible inventory IP/hostname.
|
|
||||||
# If omitted, then 'primary' is used.
|
|
||||||
#
|
|
||||||
# secondary_fo_addr: "" IP/Hostname of a secondary DHCP host. This option
|
|
||||||
# is used if you need failover address be different
|
|
||||||
# from ansible inventory IP/hostname.
|
|
||||||
# If omitted, then 'secondary' is used.
|
|
||||||
#
|
|
||||||
# auto_partner_down: 0 Number of second to start serving partners IPs
|
|
||||||
# after the partner's failure.
|
|
||||||
#
|
|
||||||
# load_balance_max_seconds: 5
|
|
||||||
# max_lease_misbalance: 15
|
|
||||||
# max_lease_ownership: 10
|
|
||||||
# min_balance: 60
|
|
||||||
# max_balance: 3600
|
|
||||||
#
|
#
|
||||||
|
# DHCP failover configuration. See :ref:`dhcpd_failovers` for more details.
|
||||||
dhcpd_failovers: []
|
dhcpd_failovers: []
|
||||||
## Following is full cluster configuration
|
|
||||||
#- failover: 'failover-localsubnet'
|
|
||||||
# primary: '10.0.10.1'
|
# -----------------------------
|
||||||
# primary_fo_addr: '10.5.10.1'
|
# dhcp-probe configuration
|
||||||
# secondary: '10.0.10.2'
|
# -----------------------------
|
||||||
# secondary_fo_addr: '10.5.10.2'
|
|
||||||
# port: 1337
|
# .. envvar:: dhcpd_probe
|
||||||
# split: 128
|
#
|
||||||
# hba: aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa
|
# Enable or disable ``dhcp-probe`` script
|
||||||
# max_response_delay: 5
|
dhcpd_probe: True
|
||||||
# max_unacked_updates: 10
|
|
||||||
# load_balance_max_seconds: 5
|
|
||||||
# auto_partner_down: 0
|
# .. envvar:: dhcpd_probe_mail_to
|
||||||
# max_lease_misbalance: 15
|
#
|
||||||
# max_lease_ownership: 10
|
# List of mail recipients which will receive messages about unauthorized DHCP
|
||||||
# min_balance: 60
|
# servers. Set to ``[]`` to disable.
|
||||||
# max_balance: 3600
|
dhcpd_probe_mail_to: [ 'root@{{ ansible_domain }}' ]
|
||||||
#
|
|
||||||
## Following is minimal cluster configuration
|
|
||||||
#- failover: 'failover-san'
|
# .. envvar:: dhcpd_probe_page_to
|
||||||
# primary: '10.0.10.1'
|
#
|
||||||
# secondary: '10.0.10.2'
|
# Alternative list of mail recipients which will receive mail messages. Meant
|
||||||
# mclt: 3600
|
# to be used as a "pager service", you can use ``debops.smstools`` role to
|
||||||
# split: 128
|
# setup a mail-SMS gateway and send the SMS messages that way.
|
||||||
# max_response_delay: 5
|
dhcpd_probe_page_to: []
|
||||||
# max_unacked_updates: 10
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_probe_mail_timeout
|
||||||
|
#
|
||||||
|
# Number of seconds between to wait between sending new mail messages
|
||||||
|
dhcpd_probe_mail_timeout: '{{ (20 * 60) }}'
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_probe_page_timeout
|
||||||
|
#
|
||||||
|
# Number of seconds between to wait between sending new pager messages
|
||||||
|
dhcpd_probe_page_timeout: '{{ (20 * 60) }}'
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_probe_legal_servers
|
||||||
|
#
|
||||||
|
# List of IP addresses of the host which are authorized DHCP servers.
|
||||||
|
dhcpd_probe_legal_servers: []
|
||||||
|
|
||||||
|
|
||||||
|
# .. envvar:: dhcpd_probe_options
|
||||||
|
#
|
||||||
|
# Additional ``dhcp-probe`` options specified as a YAML text block.
|
||||||
|
dhcpd_probe_options: ''
|
||||||
|
|
||||||
|
1
docs/changelog.rst
Normal file
1
docs/changelog.rst
Normal file
@ -0,0 +1 @@
|
|||||||
|
.. include:: ../CHANGES.rst
|
21
docs/copyright.rst
Normal file
21
docs/copyright.rst
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Copyright
|
||||||
|
=========
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Copyright (C) 2014 Maciej Delmanowski <drybjed@gmail.com>
|
||||||
|
Copyright (C) 2014 DebOps Project http://debops.org/
|
||||||
|
[see Credits for more details]
|
||||||
|
|
||||||
|
his program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License version 3, as
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see http://www.gnu.org/licenses/
|
||||||
|
|
16
docs/credits.rst
Normal file
16
docs/credits.rst
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Credits, in chronological order
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
* Maciej Delmanowski <drybjed_at_gmail.com>
|
||||||
|
|
||||||
|
* creator of the DebOps Project
|
||||||
|
|
||||||
|
* current project maintainer
|
||||||
|
|
||||||
|
* RedRampage
|
||||||
|
|
||||||
|
* Added support for DHCP failover and Dynamic DNS keys
|
||||||
|
|
468
docs/defaults-configuration.rst
Normal file
468
docs/defaults-configuration.rst
Normal file
@ -0,0 +1,468 @@
|
|||||||
|
Default variables: configuration
|
||||||
|
================================
|
||||||
|
|
||||||
|
some of ``debops.dhcpd`` default variables have more extensive configuration
|
||||||
|
than simple strings or lists, here you can find documentation and examples for
|
||||||
|
them.
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
:depth: 1
|
||||||
|
|
||||||
|
|
||||||
|
.. _dhcpd_keys:
|
||||||
|
|
||||||
|
dhcpd_keys
|
||||||
|
----------
|
||||||
|
|
||||||
|
This list lets you define symmetric keys used to update dynamic DNS with
|
||||||
|
information configured using DHCP.
|
||||||
|
|
||||||
|
``key``
|
||||||
|
Name of the key used to select it in specific scope
|
||||||
|
|
||||||
|
``algorithm``
|
||||||
|
Name of the algorithm to use for key encryption
|
||||||
|
|
||||||
|
``secret``
|
||||||
|
Encrypted symmetric key shared between DHCP and DNS servers
|
||||||
|
|
||||||
|
``comment``
|
||||||
|
An optional comment added in the configuration file
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
# Read the secret key from an external file
|
||||||
|
dhcpd_secret_secure_key: '{{ lookup("password",
|
||||||
|
secret + "/" + ansible_domain +
|
||||||
|
"/shared/ddns/keys/secure-key" }}'
|
||||||
|
|
||||||
|
dhcpd_keys:
|
||||||
|
- key: "secure-key"
|
||||||
|
algorithm: "hmac-md5"
|
||||||
|
secret: "{{ dhcpd_secret_secure_key }}"
|
||||||
|
|
||||||
|
|
||||||
|
.. _dhcpd_zones:
|
||||||
|
|
||||||
|
dhcpd_zones
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This list lets you define DNS zones used to update dynamic DNS with information
|
||||||
|
configured using DHCP.
|
||||||
|
|
||||||
|
``zone``
|
||||||
|
DNS domain name of a zone, needs to end with a dot (``.``)
|
||||||
|
|
||||||
|
``primary``
|
||||||
|
Address of the primary DNS server serving the specified zone
|
||||||
|
|
||||||
|
``key``
|
||||||
|
Name of the symmetric key used to authorize Dynamic DNS updates of the
|
||||||
|
specified zone
|
||||||
|
|
||||||
|
``comment``
|
||||||
|
An optional comment added in the configuration file
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
dhcpd_zones:
|
||||||
|
- zone: "example.org."
|
||||||
|
primary: "127.0.0.1"
|
||||||
|
key: "secure-key"
|
||||||
|
|
||||||
|
|
||||||
|
.. _dhcpd_classes:
|
||||||
|
|
||||||
|
dhcpd_classes
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Here you can define host classes and custom options for each class.
|
||||||
|
|
||||||
|
``class``
|
||||||
|
Name of the host class
|
||||||
|
|
||||||
|
``comment``
|
||||||
|
Optional comment added in the configuration file
|
||||||
|
|
||||||
|
``options``
|
||||||
|
Text block with options for a particular class scope
|
||||||
|
|
||||||
|
``include``
|
||||||
|
Include an external file
|
||||||
|
|
||||||
|
``subclass``
|
||||||
|
Dict. You can specify matches for a class in two ways:
|
||||||
|
|
||||||
|
- a dict key without a value will create a simple match for that host. You
|
||||||
|
need to specify dict key with colon (``:``) at the end to indicate that
|
||||||
|
this is a dict key, see examples below
|
||||||
|
|
||||||
|
- a dict with a text block as a value will create an extended match scope
|
||||||
|
with options specified in the text block inside that scope
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
dhcpd_classes:
|
||||||
|
|
||||||
|
- class: 'empty-class'
|
||||||
|
|
||||||
|
- class: 'allocation-class-1'
|
||||||
|
|
||||||
|
options: |
|
||||||
|
match pick-first-value (option dhcp-client-identifier, hardware);
|
||||||
|
|
||||||
|
subclass:
|
||||||
|
# Simple match
|
||||||
|
'00:11:22:33:44:55':
|
||||||
|
|
||||||
|
# Extended match
|
||||||
|
'00:11:22:33:22:11': |
|
||||||
|
option root-path "samsara:/var/diskless/alphapc";
|
||||||
|
filename "/tftpboot/netbsd.alphapc-diskless";
|
||||||
|
|
||||||
|
|
||||||
|
.. _dhcpd_groups:
|
||||||
|
|
||||||
|
dhcpd_groups
|
||||||
|
------------
|
||||||
|
|
||||||
|
Group related configuration together.
|
||||||
|
|
||||||
|
``comment``
|
||||||
|
Optional comment added in the configuration file
|
||||||
|
|
||||||
|
``options``
|
||||||
|
Text block with options for a particular group
|
||||||
|
|
||||||
|
``include``
|
||||||
|
Include an external file
|
||||||
|
|
||||||
|
``groups``
|
||||||
|
Include another group definition of the group in this group. Child group
|
||||||
|
should be defined in a separate YAML dict. Recursion is not allowed.
|
||||||
|
|
||||||
|
``hosts``
|
||||||
|
List of hosts included in this group. Use the same format as the
|
||||||
|
``dhcpd_hosts`` list.
|
||||||
|
|
||||||
|
``subnets``
|
||||||
|
List of subnets included in this group. Use the same format as the
|
||||||
|
``dhcpd_subnets`` list.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
dhcpd_groups:
|
||||||
|
- comment: 'First group'
|
||||||
|
hosts: '/etc/dhcp/dhcpd-group1-hosts.conf'
|
||||||
|
groups: '{{ dhcpd_group_second }}'
|
||||||
|
|
||||||
|
# An example of group nesting
|
||||||
|
dhcpd_group_second:
|
||||||
|
- comment: 'Second group'
|
||||||
|
hosts: '/etc/dhcp/dhcpd-group2-hosts.conf'
|
||||||
|
|
||||||
|
|
||||||
|
.. _dhcpd_shared_networks:
|
||||||
|
|
||||||
|
dhcpd_shared_networks
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
List of shared networks which combine specified subnets together.
|
||||||
|
|
||||||
|
``name``
|
||||||
|
Name of a shared network
|
||||||
|
|
||||||
|
``comment``
|
||||||
|
A comment added to this shared network in the configuration
|
||||||
|
|
||||||
|
``options``
|
||||||
|
Custom options in the text block format for this shared network
|
||||||
|
|
||||||
|
``include``
|
||||||
|
Include an external file in this shared network scope
|
||||||
|
|
||||||
|
``subnets``
|
||||||
|
List of subnets included in this shared network. Use the same format as the
|
||||||
|
``dhcpd_subnets`` list.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
dhcpd_shared_networks:
|
||||||
|
- name: 'shared-net'
|
||||||
|
comment: "Local shared network"
|
||||||
|
subnets: '{{ dhcpd_subnets_local }}'
|
||||||
|
options: |
|
||||||
|
default-lease-time 600;
|
||||||
|
max-lease-time 900;
|
||||||
|
|
||||||
|
dhcpd_subnets_local:
|
||||||
|
- subnet: '10.0.30.0'
|
||||||
|
netmask: '255.255.255.0'
|
||||||
|
routers: [ '10.0.30.1', '10.0.30.2' ]
|
||||||
|
|
||||||
|
- subnet: '10.0.40.0'
|
||||||
|
netmask: '255.255.255.0'
|
||||||
|
routers: '19.0.40.1'
|
||||||
|
options: |
|
||||||
|
default-lease-time 300;
|
||||||
|
max-lease-time 7200;
|
||||||
|
pools:
|
||||||
|
- comment: "A pool in a subnet"
|
||||||
|
range: '10.0.30.10 10.0.30.20'
|
||||||
|
|
||||||
|
|
||||||
|
.. _dhcpd_subnets:
|
||||||
|
|
||||||
|
dhcpd_subnets
|
||||||
|
-------------
|
||||||
|
|
||||||
|
List of subnets included in a specified group.
|
||||||
|
|
||||||
|
``subnet``
|
||||||
|
IP address of the subnet. If it's IPv4, it should be the first IP address in
|
||||||
|
the subnet, if it's IPv6, it should be specified with the prefix.
|
||||||
|
|
||||||
|
``netmask``
|
||||||
|
If the subnet is IPv4, specify it's netmask in "normal" IP address form, not
|
||||||
|
the CIDR form.
|
||||||
|
|
||||||
|
``ipv6``
|
||||||
|
Set to ``True`` if managed subnet is IPv6.
|
||||||
|
|
||||||
|
``routers``
|
||||||
|
String (if just one), or list (if many) of IP addresses of the routers for
|
||||||
|
this subnet
|
||||||
|
|
||||||
|
``comment``
|
||||||
|
A comment added to this subnet in the configuration
|
||||||
|
|
||||||
|
``options``
|
||||||
|
Custom options in the text block format for this subnet
|
||||||
|
|
||||||
|
``include``
|
||||||
|
Include an external file in this subnet scope
|
||||||
|
|
||||||
|
``pools``
|
||||||
|
List of different address pools within specified subnet. Each pool should be
|
||||||
|
specified as a dict, following keys are recognized:
|
||||||
|
|
||||||
|
- ``range``: a string which defines the range of the specific pool, with IP
|
||||||
|
addresses of the start and end delimited by space
|
||||||
|
|
||||||
|
- ``comment``: a comment added to this host in the configuration
|
||||||
|
|
||||||
|
- ``options``: custom options in the text block format for this host
|
||||||
|
|
||||||
|
- ``include``: include an external file in this pool
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
# List of subnets
|
||||||
|
dhcpd_subnets: [ '{{ dhcpd_subnet_default }}' ]
|
||||||
|
|
||||||
|
dhcpd_subnet_default:
|
||||||
|
subnet: '{{ ansible_default_ipv4.network }}'
|
||||||
|
netmask: '{{ ansible_default_ipv4.netmask }}'
|
||||||
|
comment: 'Generated automatically by Ansible'
|
||||||
|
|
||||||
|
# An IPv6 subnet
|
||||||
|
example_ipv6_subnet:
|
||||||
|
subnet: 'dead:be:ef::/64'
|
||||||
|
ipv6: True
|
||||||
|
routers: 'dead:be:ef::1'
|
||||||
|
comment: "Example IPv6 subnet"
|
||||||
|
options: |
|
||||||
|
default-lease-time 300;
|
||||||
|
max-lease-time 7200;
|
||||||
|
|
||||||
|
.. _dhcpd_hosts:
|
||||||
|
|
||||||
|
dhcpd_hosts
|
||||||
|
-----------
|
||||||
|
|
||||||
|
String or list. If string, include an external file with host list in this
|
||||||
|
place of the configuration. If list, specify a list of dicts describing the
|
||||||
|
hosts. Each dict can have following keys:
|
||||||
|
|
||||||
|
``hostname``
|
||||||
|
Name of the host
|
||||||
|
|
||||||
|
``ethernet``
|
||||||
|
Ethernet address of this host
|
||||||
|
|
||||||
|
``address``
|
||||||
|
IP address of this host
|
||||||
|
|
||||||
|
``comment``
|
||||||
|
A comment added to this host in the configuration
|
||||||
|
|
||||||
|
``options``
|
||||||
|
Custom options in the text block format for this host
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
# External file with list of hosts
|
||||||
|
dhcpd_hosts: '/etc/dhcp/dhcp-hosts.conf'
|
||||||
|
|
||||||
|
# List of hosts
|
||||||
|
dhcpd_hosts:
|
||||||
|
- hostname: 'examplehost'
|
||||||
|
address: '10.0.10.1'
|
||||||
|
ethernet: '00:00:00:00:00:00'
|
||||||
|
|
||||||
|
.. _dhcpd_includes:
|
||||||
|
|
||||||
|
dhcpd_includes
|
||||||
|
--------------
|
||||||
|
|
||||||
|
List of external files to include in DHCP configuration. Use absolute paths for
|
||||||
|
the files.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
dhcpd_includes:
|
||||||
|
- '/etc/dhcp/other-options.conf'
|
||||||
|
|
||||||
|
.. _dhcpd_failovers:
|
||||||
|
|
||||||
|
dhcpd_failovers
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Each 'failover pair' declaration consists of primary and secondary host,
|
||||||
|
no more than two nodes failover is currently allowed by ``isc-dhcpd``.
|
||||||
|
|
||||||
|
You must specify which failover pair each pool should use by specifying
|
||||||
|
a 'failover peer' statement under an ``options`` block in each pool
|
||||||
|
declaration. e.g::
|
||||||
|
|
||||||
|
dhcpd_failovers:
|
||||||
|
- failover: "my-failover"
|
||||||
|
primary: '10.0.30.1'
|
||||||
|
secondary: '10.0.30.2'
|
||||||
|
...
|
||||||
|
|
||||||
|
dhcpd_subnets:
|
||||||
|
- subnet: ...
|
||||||
|
...
|
||||||
|
pools:
|
||||||
|
- comment: "My pool with failover"
|
||||||
|
range: '10.0.30.10 10.0.30.20'
|
||||||
|
options: |
|
||||||
|
failover peer "my-failover";
|
||||||
|
|
||||||
|
Each failover declaration has a set of an mandatory fields, which is:
|
||||||
|
|
||||||
|
``primary``
|
||||||
|
Ansible inventory name of a primary DHCP host, if you need failover to work
|
||||||
|
on different IP, see ``primary_fo_addr`` option below.
|
||||||
|
|
||||||
|
``secondary``
|
||||||
|
Ansible inventory name of a secondary DHCP host, if you need failover to work
|
||||||
|
on different IP, see secondary_fo_addr option below.
|
||||||
|
|
||||||
|
Ansible inventory name is either IP ot hostname specified in inventory file.
|
||||||
|
|
||||||
|
``mclt``
|
||||||
|
Max Client Lead Time. The maximum amount of time that one server can extend
|
||||||
|
a lease for a DHCP client beyond the time known by the partner server.
|
||||||
|
|
||||||
|
Default value: ``3600``
|
||||||
|
|
||||||
|
Split configuration between two failover DHCP servers:
|
||||||
|
|
||||||
|
``split``
|
||||||
|
Percentage value between ``0`` and ``255``.
|
||||||
|
|
||||||
|
Specifies the split between the primary and secondary servers for the
|
||||||
|
purposes of load balancing. Whenever a client makes a DHCP request, the DHCP
|
||||||
|
server runs a hash on the client identification, resulting in value from 0 to
|
||||||
|
255. This is used as an index into a 256 bit field. If the bit at that index
|
||||||
|
is set, the primary is responsible. If the bit at that index is not set, the
|
||||||
|
secondary is responsible. Instead of ``split``, you can use ``hba``.
|
||||||
|
|
||||||
|
``hba``
|
||||||
|
32 character string in the regexp: ``([0-9a-f]{2}:){32}``
|
||||||
|
|
||||||
|
Specifies the split between the primary and secondary as a bitmap rather than
|
||||||
|
a cutoff, which theoretically allows for finer-grained control. In practice,
|
||||||
|
there is probably no need for such fine-grained control, however.
|
||||||
|
|
||||||
|
You must use either 'split' or 'hba' statement. Split has a preference, so
|
||||||
|
if it's defined, 'hba' will be omitted by configuration template.
|
||||||
|
|
||||||
|
``max_response_delay``
|
||||||
|
Tells the DHCP server how many seconds may pass without receiving a message
|
||||||
|
from its failover peer before it assumes that connection has failed. This is
|
||||||
|
mandatory according to ``dhcpd.conf`` man page.
|
||||||
|
|
||||||
|
Default value: ``5``
|
||||||
|
|
||||||
|
``max_unacked_updates``
|
||||||
|
Tells the remote DHCP server how many ``BNDUPD`` messages it can send before
|
||||||
|
it receives a ``BNDACK`` from the local system. This is mandatory according
|
||||||
|
to ``dhcpd.conf`` man page.
|
||||||
|
|
||||||
|
Default value: ``10``
|
||||||
|
|
||||||
|
Optional field are mostly desribed in ``dhcpd.conf`` man page:
|
||||||
|
|
||||||
|
``port``
|
||||||
|
Specifies port on which primary and secondary nodes will listen for failover
|
||||||
|
connection. Diffirent ports for primary and secondary is currently
|
||||||
|
unsupported.
|
||||||
|
|
||||||
|
Default value: ``647``
|
||||||
|
|
||||||
|
``primary_fo_addr``
|
||||||
|
IP/Hostname of a primary DHCP host. This option is used if you need
|
||||||
|
failover address be different from ansible inventory IP/hostname. If
|
||||||
|
omitted, then ``primary`` is used.
|
||||||
|
|
||||||
|
``secondary_fo_addr``
|
||||||
|
IP/Hostname of a secondary DHCP host. This option is used if you need
|
||||||
|
failover address be different from ansible inventory IP/hostname. If
|
||||||
|
omitted, then ``secondary`` is used.
|
||||||
|
|
||||||
|
``auto_partner_down``
|
||||||
|
Number of seconds to start serving partners IPs after the partner's failure.
|
||||||
|
|
||||||
|
Other parameters::
|
||||||
|
|
||||||
|
load_balance_max_seconds: 5
|
||||||
|
max_lease_misbalance: 15
|
||||||
|
max_lease_ownership: 10
|
||||||
|
min_balance: 60
|
||||||
|
max_balance: 3600
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
# Full cluster configuration
|
||||||
|
dhcpd_failovers:
|
||||||
|
- failover: 'failover-localsubnet'
|
||||||
|
primary: '10.0.10.1'
|
||||||
|
primary_fo_addr: '10.5.10.1'
|
||||||
|
secondary: '10.0.10.2'
|
||||||
|
secondary_fo_addr: '10.5.10.2'
|
||||||
|
port: 1337
|
||||||
|
split: 128
|
||||||
|
hba: aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa
|
||||||
|
max_response_delay: 5
|
||||||
|
max_unacked_updates: 10
|
||||||
|
load_balance_max_seconds: 5
|
||||||
|
auto_partner_down: 0
|
||||||
|
max_lease_misbalance: 15
|
||||||
|
max_lease_ownership: 10
|
||||||
|
min_balance: 60
|
||||||
|
max_balance: 3600
|
||||||
|
|
||||||
|
# Minimal cluster configuration
|
||||||
|
dhcpd_failovers:
|
||||||
|
- failover: 'failover-san'
|
||||||
|
primary: '10.0.10.1'
|
||||||
|
secondary: '10.0.10.2'
|
||||||
|
mclt: 3600
|
||||||
|
split: 128
|
||||||
|
max_response_delay: 5
|
||||||
|
max_unacked_updates: 10
|
||||||
|
|
18
docs/getting-started.rst
Normal file
18
docs/getting-started.rst
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Getting started
|
||||||
|
===============
|
||||||
|
|
||||||
|
By default ``debops.dhcpd`` installs a DHCP server with some default
|
||||||
|
configuration. Server will not be authoritative, and will have a default subnet
|
||||||
|
configuration taken from ``ansible_default_ipv4`` network configuration.
|
||||||
|
|
||||||
|
An example playbook which uses ``debops.dhcpd`` role::
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Manage DHCP server
|
||||||
|
hosts: debops_dhcpd
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: debops.dhcpd
|
||||||
|
tags: dhcpd
|
||||||
|
|
6
docs/guides.rst
Normal file
6
docs/guides.rst
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Guides and examples
|
||||||
|
===================
|
||||||
|
|
||||||
|
This section will contain guides for configuring ``debops.dhcpd`` in various
|
||||||
|
scenarios.
|
||||||
|
|
22
docs/index.rst
Normal file
22
docs/index.rst
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
debops.dhcpd
|
||||||
|
============
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 3
|
||||||
|
|
||||||
|
introduction
|
||||||
|
installation
|
||||||
|
getting-started
|
||||||
|
defaults
|
||||||
|
defaults-configuration
|
||||||
|
guides
|
||||||
|
troubleshooting
|
||||||
|
copyright
|
||||||
|
credits
|
||||||
|
changelog
|
||||||
|
|
||||||
|
..
|
||||||
|
Local Variables:
|
||||||
|
mode: rst
|
||||||
|
ispell-local-dictionary: "american"
|
||||||
|
End:
|
7
docs/installation.rst
Normal file
7
docs/installation.rst
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
This role requires at least Ansible ``v1.7.0``. To install it, run::
|
||||||
|
|
||||||
|
ansible-galaxy install debops.dhcpd
|
||||||
|
|
18
docs/introduction.rst
Normal file
18
docs/introduction.rst
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
``debops.dhcpd`` role can be used to configure an `ISC DHCP Server`_ as
|
||||||
|
standalone or in a 2-host failover configuration. Alternatively, you can
|
||||||
|
configure an DHCP relay on a host connected to multiple networks which will
|
||||||
|
relay DHCP/BOOTP messages to your DHCP server.
|
||||||
|
|
||||||
|
``dhcp-probe`` script will be used to scan the network for unauthorized DHCP
|
||||||
|
servers and notify administrators if they are found.
|
||||||
|
|
||||||
|
.. _ISC DHCP Server: https://www.isc.org/downloads/dhcp/
|
||||||
|
|
||||||
|
..
|
||||||
|
Local Variables:
|
||||||
|
mode: rst
|
||||||
|
ispell-local-dictionary: "american"
|
||||||
|
End:
|
6
docs/troubleshooting.rst
Normal file
6
docs/troubleshooting.rst
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Troubleshooting
|
||||||
|
===============
|
||||||
|
|
||||||
|
This section will contain information about fixing issues with
|
||||||
|
``debops.dhcpd`` role.
|
||||||
|
|
@ -1,6 +1,23 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- name: Restart isc-dhcp-server
|
- name: Restart isc-dhcp-server
|
||||||
service: name=isc-dhcp-server state=restarted
|
service:
|
||||||
|
name: 'isc-dhcp-server'
|
||||||
|
state: 'restarted'
|
||||||
|
|
||||||
|
- name: Restart isc-dhcp-relay
|
||||||
|
service:
|
||||||
|
name: 'isc-dhcp-relay'
|
||||||
|
state: 'restarted'
|
||||||
|
|
||||||
|
- name: Restart dhcp-probe
|
||||||
|
service:
|
||||||
|
name: 'dhcp-probe'
|
||||||
|
state: 'stopped'
|
||||||
|
notify: [ 'Start dhcp-probe' ]
|
||||||
|
|
||||||
|
- name: Start dhcp-probe
|
||||||
|
service:
|
||||||
|
name: 'dhcp-probe'
|
||||||
|
state: 'started'
|
||||||
|
|
||||||
|
@ -17,5 +17,11 @@ ansigenome_info:
|
|||||||
github: 'drybjed'
|
github: 'drybjed'
|
||||||
|
|
||||||
synopsis: |
|
synopsis: |
|
||||||
Install and configure [ISC DHCP Server](https://www.isc.org/downloads/dhcp/).
|
`debops.dhcpd` role can be used to configure an [ISC DHCP
|
||||||
|
Server](https://www.isc.org/downloads/dhcp/) as standalone or in a 2-host
|
||||||
|
failover configuration. Alternatively, you can configure an DHCP relay on
|
||||||
|
a host connected to multiple networks which will relay DHCP/BOOTP messages
|
||||||
|
to your DHCP server.
|
||||||
|
|
||||||
|
`dhcp-probe` script will be used to scan the network for unauthorized DHCP
|
||||||
|
servers and notify administrators if they are found.
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
dependencies: []
|
dependencies:
|
||||||
|
|
||||||
|
- role: debops.secret
|
||||||
|
|
||||||
galaxy_info:
|
galaxy_info:
|
||||||
author: 'Maciej Delmanowski'
|
author: 'Maciej Delmanowski'
|
||||||
|
28
tasks/dhcp-probe.yml
Normal file
28
tasks/dhcp-probe.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Create dhcp-probe lib directory
|
||||||
|
file:
|
||||||
|
path: '{{ ansible_local.root.lib + "/dhcp-probe" }}'
|
||||||
|
state: 'directory'
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Manage dhcp-probe notification scripts
|
||||||
|
template:
|
||||||
|
src: 'usr/local/lib/dhcp-probe/{{ item }}.j2'
|
||||||
|
dest: '{{ ansible_local.root.lib + "/dhcp-probe/" + item }}'
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: '0755'
|
||||||
|
with_items: [ 'dhcp_probe_notify2', 'mail-throttled' ]
|
||||||
|
|
||||||
|
- name: Configure dhcp-probe
|
||||||
|
template:
|
||||||
|
src: 'etc/dhcp_probe.cf.j2'
|
||||||
|
dest: '/etc/dhcp_probe.cf'
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: '0644'
|
||||||
|
notify: [ 'Restart dhcp-probe' ]
|
||||||
|
|
@ -1,13 +1,58 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- name: Install DHCP server packages
|
- name: Configure DHCP relay in debconf
|
||||||
apt: pkg={{ item }} state=latest install_recommends=no
|
debconf:
|
||||||
with_items: [ 'isc-dhcp-server' ]
|
name: 'isc-dhcp-relay'
|
||||||
|
question: 'isc-dhcp-relay/{{ item.key }}'
|
||||||
|
vtype: 'string'
|
||||||
|
value: '{{ item.value }}'
|
||||||
|
with_dict:
|
||||||
|
servers: '{{ dhcpd_relay_servers | join(" ") }}'
|
||||||
|
interfaces: '{{ dhcpd_relay_interfaces | join(" ") }}'
|
||||||
|
options: '{{ dhcpd_relay_options }}'
|
||||||
|
register: dhcpd_register_relay_debconf
|
||||||
|
when: dhcpd_mode == 'relay'
|
||||||
|
|
||||||
|
- name: Install DHCP packages
|
||||||
|
apt:
|
||||||
|
name: '{{ item }}'
|
||||||
|
state: 'present'
|
||||||
|
install_recommends: False
|
||||||
|
with_flattened:
|
||||||
|
- dhcpd_base_packages_map[dhcpd_mode]
|
||||||
|
- [ '{{ "dhcp-probe" if (dhcpd_probe|d() and dhcpd_probe) else [] }}' ]
|
||||||
|
|
||||||
|
- name: Reconfigure ISC DHCP relay
|
||||||
|
command: dpkg-reconfigure --frontend=noninteractive isc-dhcp-relay
|
||||||
|
notify: [ 'Restart isc-dhcp-relay' ]
|
||||||
|
when: dhcpd_register_relay_debconf|d() and dhcpd_register_relay_debconf.changed
|
||||||
|
|
||||||
|
- name: Get list of nameservers configured in /etc/resolv.conf
|
||||||
|
shell: grep -E '^nameserver\s' /etc/resolv.conf | awk '{print $2}' | sed -e 'N;s/\n/ /'
|
||||||
|
register: dhcpd_register_nameservers
|
||||||
|
changed_when: False
|
||||||
|
when: dhcpd_mode == 'server'
|
||||||
|
|
||||||
|
- name: Convert list of nameservers to Ansible list
|
||||||
|
set_fact:
|
||||||
|
dhcpd_runtime_nameservers: "{{ dhcpd_register_nameservers.stdout.split(' ') }}"
|
||||||
|
when: (dhcpd_register_nameservers is defined and dhcpd_register_nameservers.stdout)
|
||||||
|
|
||||||
- name: Configure DHCP server
|
- name: Configure DHCP server
|
||||||
template: src={{ item }}.j2 dest=/{{ item }} owner=root group=root mode=0644
|
template:
|
||||||
|
src: '{{ item }}.j2'
|
||||||
|
dest: '/{{ item }}'
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: '0644'
|
||||||
with_items: [ 'etc/default/isc-dhcp-server', 'etc/dhcp/dhcpd.conf' ]
|
with_items: [ 'etc/default/isc-dhcp-server', 'etc/dhcp/dhcpd.conf' ]
|
||||||
notify: Restart isc-dhcp-server
|
notify: [ 'Restart isc-dhcp-server' ]
|
||||||
|
when: dhcpd_mode == 'server'
|
||||||
|
|
||||||
|
- name: Make sure that IPv6 lease file exists
|
||||||
|
command: touch /var/lib/dhcp/dhcpd6.leases creates=/var/lib/dhcp/dhcpd6.leases
|
||||||
|
when: dhcpd_ipversion == '6'
|
||||||
|
|
||||||
|
- include: dhcp-probe.yml
|
||||||
|
when: dhcpd_probe|d() and dhcpd_probe
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
# Additional options to start dhcpd with.
|
# Additional options to start dhcpd with.
|
||||||
# Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead
|
# Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead
|
||||||
#OPTIONS=""
|
OPTIONS="{{ dhcpd_server_options }}"
|
||||||
|
|
||||||
# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
|
# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
|
||||||
# Separate multiple interfaces with spaces, e.g. "eth0 eth1".
|
# Separate multiple interfaces with spaces, e.g. "eth0 eth1".
|
||||||
|
30
templates/etc/dhcp/auto_options.j2
Normal file
30
templates/etc/dhcp/auto_options.j2
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{% if dhcpd_domain_name|d() and dhcpd_domain_name %}
|
||||||
|
{% set dhcpd_tpl_domain_search = [ dhcpd_domain_name ] + dhcpd_domain_search|d([]) %}
|
||||||
|
option domain-name "{{ dhcpd_domain_name }}";
|
||||||
|
|
||||||
|
option domain-search "{{ dhcpd_tpl_domain_search | join('", "') }}";
|
||||||
|
option dhcp6.domain-search "{{ dhcpd_tpl_domain_search | join('", "') }}";
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if dhcpd_nameservers|d() and dhcpd_nameservers %}
|
||||||
|
{% set dhcpd_tpl_nameservers = dhcpd_nameservers %}
|
||||||
|
{% elif dhcpd_runtime_nameservers|d() and dhcpd_runtime_nameservers %}
|
||||||
|
{% set dhcpd_tpl_nameservers = [] %}
|
||||||
|
{% for server in dhcpd_runtime_nameservers %}
|
||||||
|
{% if server not in [ '127.0.0.1', '::1' ] %}
|
||||||
|
{% set _ = dhcpd_tpl_nameservers.append(server) %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if dhcpd_tpl_nameservers %}
|
||||||
|
{% if dhcpd_tpl_nameservers | ipv4 %}
|
||||||
|
option domain-name-servers {{ dhcpd_tpl_nameservers | ipv4 | join(", ") }};
|
||||||
|
{% endif %}
|
||||||
|
{% if dhcpd_tpl_nameservers | ipv6 %}
|
||||||
|
option dhcp6.name-servers {{ dhcpd_tpl_nameservers | ipv6 | join(", ") }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{#
|
||||||
|
vim: ft=dhcpd
|
||||||
|
#}
|
@ -1,192 +1,5 @@
|
|||||||
# This file is managed by Ansible, all changes will be lost
|
{% import 'macros.j2' as print with context %}
|
||||||
{% macro print_class(class) %}
|
# {{ ansible_managed }}
|
||||||
{% if class.comment is defined and class.comment %}
|
|
||||||
# {{ class.comment }}
|
|
||||||
{% endif %}
|
|
||||||
class "{{ class.class }}" {
|
|
||||||
{% if class.options is defined and class.options %}
|
|
||||||
{{ class.options | indent(8,true) }}
|
|
||||||
{% endif %}
|
|
||||||
{% if class.include is defined and class.include %}
|
|
||||||
include "{{ class.include }}";
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
{% if class.subclass is defined and class.subclass %}
|
|
||||||
|
|
||||||
{% for key, value in class.subclass.iteritems() %}
|
|
||||||
{% if value is defined and value %}
|
|
||||||
subclass "{{ class.class }}" "{{ key }}" {
|
|
||||||
{{ value | indent(8,true) }}
|
|
||||||
}
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
subclass "{{ class.class }}" {{ key }};
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endmacro %}
|
|
||||||
{% macro print_group(group) %}
|
|
||||||
{% if group.comment is defined and group.comment %}
|
|
||||||
# {{ group.comment }}
|
|
||||||
{% endif %}
|
|
||||||
group {
|
|
||||||
{% if group.options is defined and group.options %}
|
|
||||||
{{ group.options | indent(8,true) }}
|
|
||||||
{% endif %}
|
|
||||||
{% if group.include is defined and group.include %}
|
|
||||||
include "{{ group.include }}";
|
|
||||||
{% endif %}
|
|
||||||
{% if group.groups is defined and group.groups %}
|
|
||||||
{% for group in group.groups %}
|
|
||||||
{{ print_group(group) | indent(8, true) }}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% if group.subnets is defined and group.subnets %}
|
|
||||||
{% for subnet in group.subnets %}
|
|
||||||
{{ print_subnet(subnet) | indent(8, true) }}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% if group.hosts is defined and group.hosts %}
|
|
||||||
{{ print_hosts(group.hosts) | indent(8, true) }}
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
{% endmacro %}
|
|
||||||
{% macro print_subnet(subnet) %}
|
|
||||||
{% if subnet.comment is defined and subnet.comment %}
|
|
||||||
# {{ subnet.comment }}
|
|
||||||
{% endif %}
|
|
||||||
{% if subnet.ipv6 is defined and subnet.ipv6 %}
|
|
||||||
subnet6 {{ subnet.subnet }} {
|
|
||||||
{% else %}
|
|
||||||
subnet {{ subnet.subnet }} netmask {{ subnet.netmask }} {
|
|
||||||
{% endif %}
|
|
||||||
{% if subnet.routers is defined and subnet.routers %}
|
|
||||||
{% if subnet.routers is string %}
|
|
||||||
option routers {{ subnet.routers }};
|
|
||||||
{% else %}
|
|
||||||
option routers {{ subnet.routers | join(' ') }};
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% if subnet.options is defined and subnet.options %}
|
|
||||||
{{ subnet.options | indent(8,true) }}
|
|
||||||
{% endif %}
|
|
||||||
{% if subnet.include is defined and subnet.include %}
|
|
||||||
include "{{ subnet.include }}";
|
|
||||||
{% endif %}
|
|
||||||
{% if subnet.pools is defined and subnet.pools %}
|
|
||||||
{% for pool in subnet.pools %}
|
|
||||||
pool {
|
|
||||||
{% if pool.comment is defined and pool.comment %}
|
|
||||||
# {{ pool.comment }}
|
|
||||||
{% endif %}
|
|
||||||
{% if subnet.ipv6 is defined and subnet.ipv6 %}
|
|
||||||
range6 {{ pool.range }};
|
|
||||||
{% else %}
|
|
||||||
range {{ pool.range }};
|
|
||||||
{% endif %}
|
|
||||||
{% if pool.options is defined and pool.options %}
|
|
||||||
{{ pool.options | indent(16,true) }}
|
|
||||||
{% endif %}
|
|
||||||
{% if pool.include is defined and pool.include %}
|
|
||||||
include "{{ pool.include }}";
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% if subnet.hosts is defined and subnet.hosts %}
|
|
||||||
{{ print_hosts(subnet.hosts) | indent(8, true) }}
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
{% endmacro %}
|
|
||||||
{% macro print_hosts(hosts) %}
|
|
||||||
{% if hosts is string %}
|
|
||||||
include "{{ hosts }}";
|
|
||||||
{% else %}
|
|
||||||
{% for host in hosts %}
|
|
||||||
{% if host.comment is defined and host.comment %}
|
|
||||||
# {{ host.comment }}
|
|
||||||
{% endif %}
|
|
||||||
host {{ host.hostname }} {
|
|
||||||
{% if host.options is defined and host.options %}
|
|
||||||
{{ host.options | indent(8,true) }}
|
|
||||||
{% endif %}
|
|
||||||
{% if host.ethernet is defined and host.ethernet %}
|
|
||||||
hardware ethernet {{ host.ethernet }};
|
|
||||||
{% endif %}
|
|
||||||
{% if host.address is defined and host.address %}
|
|
||||||
fixed-address {{ host.address }};
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endmacro %}
|
|
||||||
{% macro print_failover(failover) %}
|
|
||||||
{% if failover.comment is defined and failover.comment %}
|
|
||||||
# {{ failover.comment }}
|
|
||||||
{% endif %}
|
|
||||||
failover peer "{{ failover.failover }}" {
|
|
||||||
{% if failover.primary is defined and failover.primary == inventory_hostname %}
|
|
||||||
primary;
|
|
||||||
mclt {{ failover.mclt|default(3600) }};
|
|
||||||
{% if failover.primary_fo_addr is defined and failover.primary_fo_addr %}
|
|
||||||
address {{ failover.primary_fo_addr }};
|
|
||||||
{% else %}
|
|
||||||
address {{ failover.primary }};
|
|
||||||
{% endif %}
|
|
||||||
{% if failover.secondary_fo_addr is defined and failover.secondary_fo_addr %}
|
|
||||||
peer address {{ failover.secondary_fo_addr }};
|
|
||||||
{% else %}
|
|
||||||
peer address {{ failover.secondary }};
|
|
||||||
{% endif %}
|
|
||||||
{% if failover.split is defined and failover.split %}
|
|
||||||
split {{ failover.split }};
|
|
||||||
{% elif failover.hba is defined and failover.hba %}
|
|
||||||
hba {{ failover.hba }};
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
secondary;
|
|
||||||
{% if failover.secondary_fo_addr is defined and failover.secondary_fo_addr %}
|
|
||||||
address {{ failover.secondary_fo_addr }};
|
|
||||||
{% else %}
|
|
||||||
address {{ failover.secondary }};
|
|
||||||
{% endif %}
|
|
||||||
{% if failover.primary_fo_addr is defined and failover.primary_fo_addr %}
|
|
||||||
peer address {{ failover.primary_fo_addr }};
|
|
||||||
{% else %}
|
|
||||||
peer address {{ failover.primary }};
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
max-response-delay {{ failover.max_response_delay|default(30) }};
|
|
||||||
max-unacked-updates {{ failover.max_unacked_updates|default(10) }};
|
|
||||||
{% if failover.load_balance_max_seconds is defined and failover.load_balance_max_seconds %}
|
|
||||||
load balance max seconds {{ failover.load_balance_max_seconds }};
|
|
||||||
{% endif %}
|
|
||||||
{% if failover.max_lease_misbalance is defined and failover.max_lease_misbalance %}
|
|
||||||
max-lease-misbalance {{ failover.max_lease_misbalance }};
|
|
||||||
{% endif %}
|
|
||||||
{% if failover.max_lease_ownership is defined and failover.max_lease_ownership %}
|
|
||||||
max-lease-ownership {{ failover.max_lease_ownership }};
|
|
||||||
{% endif %}
|
|
||||||
{% if failover.min_balance is defined and failover.min_balance %}
|
|
||||||
min-balance {{ failover.min_balance }};
|
|
||||||
{% endif %}
|
|
||||||
{% if failover.max_balance is defined and failover.max_balance %}
|
|
||||||
max-balance {{ failover.max_balance }};
|
|
||||||
{% endif %}
|
|
||||||
{% if failover.auto_partner_down is defined and failover.auto_partner_down %}
|
|
||||||
auto-partner-down {{ failover.auto_partner_down }};
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
{% endmacro %}
|
|
||||||
{% macro print_key(key) %}
|
|
||||||
{% if key.comment is defined and key.comment %}
|
|
||||||
# {{ key.comment }}
|
|
||||||
{% endif %}
|
|
||||||
key "{{ key.key }}" {
|
|
||||||
algorithm {{ key.algorithm|default('hmac-md5') }};
|
|
||||||
secret {{ key.secret }};
|
|
||||||
}
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% if dhcpd_authoritative is defined and dhcpd_authoritative %}
|
{% if dhcpd_authoritative is defined and dhcpd_authoritative %}
|
||||||
authoritative;
|
authoritative;
|
||||||
@ -195,10 +8,19 @@ authoritative;
|
|||||||
not authoritative;
|
not authoritative;
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_global_options is defined and dhcpd_global_options %}
|
{% if dhcpd_global_default_lease_time|d() and dhcpd_global_default_lease_time %}
|
||||||
# Global configuration options
|
default-lease-time {{ dhcpd_global_default_lease_time }};
|
||||||
{{ dhcpd_global_options }}
|
{% endif %}
|
||||||
|
{% if dhcpd_global_max_lease_time|d() and dhcpd_global_max_lease_time %}
|
||||||
|
max-lease-time {{ dhcpd_global_max_lease_time }};
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if dhcpd_log_facility|d() and dhcpd_log_facility %}
|
||||||
|
log-facility {{ dhcpd_log_facility }};
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if dhcpd_auto_options|d() and dhcpd_auto_options %}
|
||||||
|
{% include 'auto_options.j2' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_options is defined and dhcpd_options %}
|
{% if dhcpd_options is defined and dhcpd_options %}
|
||||||
# Configuration options
|
# Configuration options
|
||||||
@ -207,17 +29,22 @@ not authoritative;
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_keys is defined and dhcpd_keys %}
|
{% if dhcpd_keys is defined and dhcpd_keys %}
|
||||||
{% for key in dhcpd_keys %}
|
{% for key in dhcpd_keys %}
|
||||||
{{ print_key(key) }}
|
{{ print.key(key) }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if dhcpd_zones is defined and dhcpd_zones %}
|
||||||
|
{% for zone in dhcpd_zones %}
|
||||||
|
{{ print.zone(zone) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_classes is defined and dhcpd_classes %}
|
{% if dhcpd_classes is defined and dhcpd_classes %}
|
||||||
{% for class in dhcpd_classes %}
|
{% for class in dhcpd_classes %}
|
||||||
{{ print_class(class) }}
|
{{ print.class(class) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_failovers is defined and dhcpd_failovers %}
|
{% if dhcpd_failovers is defined and dhcpd_failovers %}
|
||||||
{% for failover in dhcpd_failovers %}
|
{% for failover in dhcpd_failovers %}
|
||||||
{{ print_failover(failover) }}
|
{{ print.failover(failover) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_shared_networks is defined and dhcpd_shared_networks %}
|
{% if dhcpd_shared_networks is defined and dhcpd_shared_networks %}
|
||||||
@ -235,7 +62,7 @@ shared-network "{{ network.name }}" {
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% for subnet in network.subnets %}
|
{% for subnet in network.subnets %}
|
||||||
|
|
||||||
{{ print_subnet(subnet) | indent(8,true) }}
|
{{ print.subnet(subnet) | indent(8,true) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,16 +71,16 @@ shared-network "{{ network.name }}" {
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_groups is defined and dhcpd_groups %}
|
{% if dhcpd_groups is defined and dhcpd_groups %}
|
||||||
{% for group in dhcpd_groups %}
|
{% for group in dhcpd_groups %}
|
||||||
{{ print_group(group) }}
|
{{ print.group(group) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_subnets is defined and dhcpd_subnets %}
|
{% if dhcpd_subnets is defined and dhcpd_subnets %}
|
||||||
{% for subnet in dhcpd_subnets %}
|
{% for subnet in dhcpd_subnets %}
|
||||||
{{ print_subnet(subnet) }}
|
{{ print.subnet(subnet) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_hosts is defined and dhcpd_hosts %}
|
{% if dhcpd_hosts is defined and dhcpd_hosts %}
|
||||||
{{ print_hosts(dhcpd_hosts) }}
|
{{ print.hosts(dhcpd_hosts) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dhcpd_includes is defined and dhcpd_includes %}
|
{% if dhcpd_includes is defined and dhcpd_includes %}
|
||||||
{% for include in dhcpd_includes %}
|
{% for include in dhcpd_includes %}
|
||||||
|
231
templates/etc/dhcp/macros.j2
Normal file
231
templates/etc/dhcp/macros.j2
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
{#
|
||||||
|
# List of macros for ISC DHCP configuration, IPv4
|
||||||
|
# ===============================================
|
||||||
|
#
|
||||||
|
# ---- Macro: print.class() ----
|
||||||
|
#}
|
||||||
|
{% macro class(class) %}
|
||||||
|
{% if class.comment is defined and class.comment %}
|
||||||
|
# {{ class.comment }}
|
||||||
|
{% endif %}
|
||||||
|
class "{{ class.class }}" {
|
||||||
|
{% if class.options is defined and class.options %}
|
||||||
|
{{ class.options | indent(8,true) }}
|
||||||
|
{% endif %}
|
||||||
|
{% if class.include is defined and class.include %}
|
||||||
|
include "{{ class.include }}";
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% if class.subclass is defined and class.subclass %}
|
||||||
|
|
||||||
|
{% for key, value in class.subclass.iteritems() %}
|
||||||
|
{% if value is defined and value %}
|
||||||
|
subclass "{{ class.class }}" "{{ key }}" {
|
||||||
|
{{ value | indent(8,true) }}
|
||||||
|
}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
subclass "{{ class.class }}" {{ key }};
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{#
|
||||||
|
#
|
||||||
|
# ---- Macro: print.group() ----
|
||||||
|
#}
|
||||||
|
{% macro group(group) %}
|
||||||
|
{% if group.comment is defined and group.comment %}
|
||||||
|
# {{ group.comment }}
|
||||||
|
{% endif %}
|
||||||
|
group {
|
||||||
|
{% if group.options is defined and group.options %}
|
||||||
|
{{ group.options | indent(8,true) }}
|
||||||
|
{% endif %}
|
||||||
|
{% if group.include is defined and group.include %}
|
||||||
|
include "{{ group.include }}";
|
||||||
|
{% endif %}
|
||||||
|
{% if group.groups is defined and group.groups %}
|
||||||
|
{% for group in group.groups %}
|
||||||
|
{{ print.group(group) | indent(8, true) }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if group.subnets is defined and group.subnets %}
|
||||||
|
{% for subnet in group.subnets %}
|
||||||
|
{{ print.subnet(subnet) | indent(8, true) }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if group.hosts is defined and group.hosts %}
|
||||||
|
{{ print.hosts(group.hosts) | indent(8, true) }}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endmacro %}
|
||||||
|
{#
|
||||||
|
#
|
||||||
|
# ---- Macro: print.subnet() ----
|
||||||
|
#}
|
||||||
|
{% macro subnet(subnet) %}
|
||||||
|
{% if subnet.comment is defined and subnet.comment %}
|
||||||
|
# {{ subnet.comment }}
|
||||||
|
{% endif %}
|
||||||
|
{% if dhcpd_ipversion is defined and dhcpd_ipversion == '6' %}
|
||||||
|
subnet6 {{ subnet.subnet | ipaddr('network') + '/' + subnet.subnet | ipaddr('prefix') | string }} {
|
||||||
|
{% else %}
|
||||||
|
subnet {{ subnet.subnet | ipaddr('cidr') | ipaddr('network') }} netmask {{ subnet.netmask | default(subnet.subnet | ipaddr('netmask')) }} {
|
||||||
|
{% endif %}
|
||||||
|
{% if subnet.routers is defined and subnet.routers %}
|
||||||
|
{% if subnet.routers is string %}
|
||||||
|
option routers {{ subnet.routers }};
|
||||||
|
{% else %}
|
||||||
|
option routers {{ subnet.routers | join(', ') }};
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if subnet.options is defined and subnet.options %}
|
||||||
|
{{ subnet.options | indent(8,true) }}
|
||||||
|
{% endif %}
|
||||||
|
{% if subnet.include is defined and subnet.include %}
|
||||||
|
include "{{ subnet.include }}";
|
||||||
|
{% endif %}
|
||||||
|
{% if subnet.pools is defined and subnet.pools %}
|
||||||
|
{% for pool in subnet.pools %}
|
||||||
|
pool {
|
||||||
|
{% if pool.comment is defined and pool.comment %}
|
||||||
|
# {{ pool.comment }}
|
||||||
|
{% endif %}
|
||||||
|
{% if dhcpd_ipversion is defined and dhcpd_ipversion == '6' %}
|
||||||
|
range6 {{ pool.range }};
|
||||||
|
{% else %}
|
||||||
|
range {{ pool.range }};
|
||||||
|
{% endif %}
|
||||||
|
{% if pool.options is defined and pool.options %}
|
||||||
|
{{ pool.options | indent(16,true) }}
|
||||||
|
{% endif %}
|
||||||
|
{% if pool.include is defined and pool.include %}
|
||||||
|
include "{{ pool.include }}";
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if subnet.hosts is defined and subnet.hosts %}
|
||||||
|
{{ print.hosts(subnet.hosts) | indent(8, true) }}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endmacro %}
|
||||||
|
{#
|
||||||
|
#
|
||||||
|
# ---- Macro: print.hosts() ----
|
||||||
|
#}
|
||||||
|
{% macro hosts(hosts) %}
|
||||||
|
{% if hosts is string %}
|
||||||
|
include "{{ hosts }}";
|
||||||
|
{% else %}
|
||||||
|
{% for host in hosts %}
|
||||||
|
{% if host.comment is defined and host.comment %}
|
||||||
|
# {{ host.comment }}
|
||||||
|
{% endif %}
|
||||||
|
host {{ host.hostname }} {
|
||||||
|
{% if host.options is defined and host.options %}
|
||||||
|
{{ host.options | indent(8,true) }}
|
||||||
|
{% endif %}
|
||||||
|
{% if host.ethernet is defined and host.ethernet %}
|
||||||
|
hardware ethernet {{ host.ethernet }};
|
||||||
|
{% endif %}
|
||||||
|
{% if host.address is defined and host.address %}
|
||||||
|
{% if dhcpd_ipversion is defined and dhcpd_ipversion == '6' %}
|
||||||
|
fixed-address6 {{ host.address }};
|
||||||
|
{% else %}
|
||||||
|
fixed-address {{ host.address }};
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{#
|
||||||
|
#
|
||||||
|
# ---- Macro: print.failover() ----
|
||||||
|
#}
|
||||||
|
{% macro failover(failover) %}
|
||||||
|
{% if failover.comment is defined and failover.comment %}
|
||||||
|
# {{ failover.comment }}
|
||||||
|
{% endif %}
|
||||||
|
failover peer "{{ failover.failover }}" {
|
||||||
|
{% if failover.primary is defined and failover.primary == inventory_hostname %}
|
||||||
|
primary;
|
||||||
|
mclt {{ failover.mclt|default(3600) }};
|
||||||
|
{% if failover.primary_fo_addr is defined and failover.primary_fo_addr %}
|
||||||
|
address {{ failover.primary_fo_addr }};
|
||||||
|
{% else %}
|
||||||
|
address {{ failover.primary }};
|
||||||
|
{% endif %}
|
||||||
|
{% if failover.secondary_fo_addr is defined and failover.secondary_fo_addr %}
|
||||||
|
peer address {{ failover.secondary_fo_addr }};
|
||||||
|
{% else %}
|
||||||
|
peer address {{ failover.secondary }};
|
||||||
|
{% endif %}
|
||||||
|
{% if failover.split is defined and failover.split %}
|
||||||
|
split {{ failover.split }};
|
||||||
|
{% elif failover.hba is defined and failover.hba %}
|
||||||
|
hba {{ failover.hba }};
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
secondary;
|
||||||
|
{% if failover.secondary_fo_addr is defined and failover.secondary_fo_addr %}
|
||||||
|
address {{ failover.secondary_fo_addr }};
|
||||||
|
{% else %}
|
||||||
|
address {{ failover.secondary }};
|
||||||
|
{% endif %}
|
||||||
|
{% if failover.primary_fo_addr is defined and failover.primary_fo_addr %}
|
||||||
|
peer address {{ failover.primary_fo_addr }};
|
||||||
|
{% else %}
|
||||||
|
peer address {{ failover.primary }};
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
max-response-delay {{ failover.max_response_delay|default(30) }};
|
||||||
|
max-unacked-updates {{ failover.max_unacked_updates|default(10) }};
|
||||||
|
{% if failover.load_balance_max_seconds is defined and failover.load_balance_max_seconds %}
|
||||||
|
load balance max seconds {{ failover.load_balance_max_seconds }};
|
||||||
|
{% endif %}
|
||||||
|
{% if failover.max_lease_misbalance is defined and failover.max_lease_misbalance %}
|
||||||
|
max-lease-misbalance {{ failover.max_lease_misbalance }};
|
||||||
|
{% endif %}
|
||||||
|
{% if failover.max_lease_ownership is defined and failover.max_lease_ownership %}
|
||||||
|
max-lease-ownership {{ failover.max_lease_ownership }};
|
||||||
|
{% endif %}
|
||||||
|
{% if failover.min_balance is defined and failover.min_balance %}
|
||||||
|
min-balance {{ failover.min_balance }};
|
||||||
|
{% endif %}
|
||||||
|
{% if failover.max_balance is defined and failover.max_balance %}
|
||||||
|
max-balance {{ failover.max_balance }};
|
||||||
|
{% endif %}
|
||||||
|
{% if failover.auto_partner_down is defined and failover.auto_partner_down %}
|
||||||
|
auto-partner-down {{ failover.auto_partner_down }};
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endmacro %}
|
||||||
|
{#
|
||||||
|
#
|
||||||
|
# ---- Macro: print.key() ----
|
||||||
|
#}
|
||||||
|
{% macro key(key) %}
|
||||||
|
{% if key.comment is defined and key.comment %}
|
||||||
|
# {{ key.comment }}
|
||||||
|
{% endif %}
|
||||||
|
key {{ key.key }} {
|
||||||
|
algorithm {{ key.algorithm|default('hmac-md5') }};
|
||||||
|
secret {{ key.secret }};
|
||||||
|
}
|
||||||
|
{% endmacro %}
|
||||||
|
{#
|
||||||
|
#
|
||||||
|
# ---- Macro: print.zone() ----
|
||||||
|
#}
|
||||||
|
{% macro zone(zone) %}
|
||||||
|
{% if zone.comment is defined and zone.comment %}
|
||||||
|
# {{ zone.comment }}
|
||||||
|
{% endif %}
|
||||||
|
zone {{ zone.zone }} {
|
||||||
|
primary {{ zone.primary }};
|
||||||
|
key {{ zone.key }};
|
||||||
|
}
|
||||||
|
{% endmacro %}
|
17
templates/etc/dhcp_probe.cf.j2
Normal file
17
templates/etc/dhcp_probe.cf.j2
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
|
||||||
|
# Send mail messages about unauthorized DHCP servers
|
||||||
|
alert_program_name2 {{ ansible_local.root.lib + "/dhcp-probe/dhcp_probe_notify2" }}
|
||||||
|
|
||||||
|
{% if dhcpd_probe_legal_servers|d() and dhcpd_probe_legal_servers %}
|
||||||
|
# Legal DHCP servers
|
||||||
|
{% for address in dhcpd_probe_legal_servers %}
|
||||||
|
legal_server {{ address }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if dhcpd_probe_options|d() and dhcpd_probe_options %}
|
||||||
|
# Other options
|
||||||
|
{{ dhcpd_probe_options }}
|
||||||
|
|
||||||
|
{% endif %}
|
247
templates/usr/local/lib/dhcp-probe/dhcp_probe_notify2.j2
Executable file
247
templates/usr/local/lib/dhcp-probe/dhcp_probe_notify2.j2
Executable file
@ -0,0 +1,247 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
|
||||||
|
# {{ ansible_managed }}
|
||||||
|
|
||||||
|
# dhcp_probe_notify2 -p calling_prog_name -I interface_name -i IPaddress -m MACaddress [-y yiaddr]
|
||||||
|
#
|
||||||
|
# An external program called by dhcp_probe upon response of a response from an unexpected BootP/DHCP server.
|
||||||
|
#
|
||||||
|
# Called via specification of 'alert_program_name2' in /etc/dhcp_probe.cf file.
|
||||||
|
# This version obeys the syntax provided by the 'alert_program_name2' statement,
|
||||||
|
# not the syntax provided by the older 'alert_program_name' statement.
|
||||||
|
#
|
||||||
|
# Required options:
|
||||||
|
# -p calling_prog_name the name of the calling program (e.g. 'dhcp_probe')
|
||||||
|
# -I interface_name the name of the interface on which the unexpected response packet arrived (e.g. 'qfe0')
|
||||||
|
# -i IPaddress the IP source address of the unexpected response packet (e.g. '192.168.0.1')
|
||||||
|
# -m MACaddress the Ethernet source address of the unexpected response packet (e.g. '0:1:2:3:4:5')
|
||||||
|
#
|
||||||
|
# Optional options:
|
||||||
|
# -y yiaddr the response packet's non-zero yiaddr value, when it falls within a "Lease Network of Interest" (e.g. '172.16.1.2')
|
||||||
|
#
|
||||||
|
# May send email subject to throttling.
|
||||||
|
# May send page subject to throttling.
|
||||||
|
#
|
||||||
|
# You will need to edit the definitions below.
|
||||||
|
#
|
||||||
|
# Irwin Tillman
|
||||||
|
|
||||||
|
|
||||||
|
use Sys::Hostname;
|
||||||
|
use Sys::Syslog qw(:DEFAULT setlogsock);
|
||||||
|
use Time::HiRes qw(gettimeofday); # from CPAN
|
||||||
|
use Getopt::Std;
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
#
|
||||||
|
# Definitions you may need to edit
|
||||||
|
|
||||||
|
my $SYSLOG_FACILITY="daemon"; # name of facility to use if syslogging (e.g. 'daemon')
|
||||||
|
my $SYSLOG_OPT = 'pid,cons'; # comma-separated syslog options to use if syslogging (e.g. 'pid,cons') , ignored if not syslogging
|
||||||
|
|
||||||
|
use vars qw($VERBOSE);
|
||||||
|
$VERBOSE = 1; # set to true to produce more verbose messages
|
||||||
|
|
||||||
|
# We may send one piece of mail this way, subject to frequency throttling.
|
||||||
|
# Use this set of definitions for a piece of email that is delivered as regular email (not a page).
|
||||||
|
use vars qw($THROTTLE_MAIL_CMD $THROTTLE_MAIL_TIMEOUT $THROTTLE_MAIL_FROM $THROTTLE_MAIL_RECIPIENT $THROTTLE_MAIL_RECIPIENT_TEST $THROTTLE_MAIL_SUBJECT);
|
||||||
|
{% if dhcpd_probe_mail_to|d() and dhcpd_probe_mail_to %}
|
||||||
|
$THROTTLE_MAIL_CMD = "{{ ansible_local.root.lib + '/dhcp-probe/mail-throttled' }}"; # set to "" to disable
|
||||||
|
{% else %}
|
||||||
|
$THROTTLE_MAIL_CMD = ""; # set to "" to disable
|
||||||
|
{% endif %}
|
||||||
|
$THROTTLE_MAIL_TIMEOUT = {{ dhcpd_probe_mail_timeout | default('600') }}; # seconds
|
||||||
|
$THROTTLE_MAIL_FROM = "root"; # e.g. "root"
|
||||||
|
$THROTTLE_MAIL_RECIPIENT = "{{ dhcpd_probe_mail_to | join(' ') | replace('@','\@') }}"; # space-separated email addresses, remember to escape '@' characters
|
||||||
|
$THROTTLE_MAIL_SUBJECT = "Unexpected BOOTP/DHCP server";
|
||||||
|
|
||||||
|
|
||||||
|
# We may also send another piece of email this way, subject to frequency throttling.
|
||||||
|
# Use this set of definitions for a piece of email that is delivered to a pager (not as regular email).
|
||||||
|
use vars qw($THROTTLE_PAGE_CMD $THROTTLE_PAGE_TIMEOUT $THROTTLE_PAGE_RECIPIENT);
|
||||||
|
{% if dhcpd_probe_page_to|d() and dhcpd_probe_page_to %}
|
||||||
|
$THROTTLE_PAGE_CMD = "{{ ansible_local.root.lib + '/dhcp-probe/mail-throttled' }}"; # set to "" to disable
|
||||||
|
{% else %}
|
||||||
|
$THROTTLE_PAGE_CMD = ""; # set to "" to disable
|
||||||
|
{% endif %}
|
||||||
|
$THROTTLE_PAGE_TIMEOUT = {{ dhcpd_probe_page_timeout | default('600') }}; # seconds
|
||||||
|
$THROTTLE_PAGE_RECIPIENT = "{{ dhcpd_probe_page_to | join(' ') | replace('@','\@') }}"; # space-separated email addresses, remember to escape '@' characters
|
||||||
|
|
||||||
|
|
||||||
|
# End of definitions you may need to edit
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
(my $prog = $0) =~ s/.*\///;
|
||||||
|
|
||||||
|
# init our use of syslog
|
||||||
|
# setlogsock('unix'); # talk to syslog with UNIX domain socket, not INET domain. XXX causes failure in Solaris 7
|
||||||
|
openlog($prog, $SYSLOG_OPT, $SYSLOG_FACILITY);
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
#
|
||||||
|
# Parse options and arguments
|
||||||
|
|
||||||
|
# We must use getopt() instead of getopts() to avoid throwing an error
|
||||||
|
# if we are passed an unrecognized option.
|
||||||
|
# We must silently ignore unrecognized options to be forward compatible with enhancements to dhcp_probe.
|
||||||
|
use vars qw($opt_i $opt_I $opt_m $opt_p $opt_y);
|
||||||
|
# &getopt('p:i:I:m:y:');
|
||||||
|
&getopt('piImy');
|
||||||
|
|
||||||
|
# Required options
|
||||||
|
my $calling_program = $opt_p;
|
||||||
|
my $ifname = $opt_I;
|
||||||
|
my $ip_src = $opt_i;
|
||||||
|
my $ether_src = $opt_m;
|
||||||
|
#
|
||||||
|
# Optional options
|
||||||
|
my $yiaddr = $opt_y || "";
|
||||||
|
|
||||||
|
|
||||||
|
# Enforce presence of required options
|
||||||
|
unless ($calling_program) {
|
||||||
|
my_message('LOG_ERR', "${prog}: missing -p calling_program option");
|
||||||
|
exit 100;
|
||||||
|
}
|
||||||
|
unless ($ifname) {
|
||||||
|
my_message('LOG_ERR', "${prog}: missing -I interface_name option");
|
||||||
|
exit 101;
|
||||||
|
}
|
||||||
|
unless ($ip_src) {
|
||||||
|
my_message('LOG_ERR', "${prog}: missing -i ip_src_address option");
|
||||||
|
exit 102;
|
||||||
|
}
|
||||||
|
unless ($ether_src) {
|
||||||
|
my_message('LOG_ERR', "${prog}: missing -m ether_src_address option");
|
||||||
|
exit 103;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Done parsing options and arguments
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
# Miscellaneous Initialization
|
||||||
|
|
||||||
|
my $hostname = hostname();
|
||||||
|
|
||||||
|
my ($seconds, $microseconds) = gettimeofday; # from Time::HiRes on CPAN
|
||||||
|
my $timestamp = scalar(localtime($seconds));
|
||||||
|
$timestamp =~ s/(\d\d:\d\d:\d\d)/$1.$microseconds/; # glue microsends to end of seconds
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# When we pass a key to THROTTLE_MAIL_CMD, we want the key to also include an indication
|
||||||
|
# of whether the yiaddr option was specified. (If we didn't include such an indication,
|
||||||
|
# the following could happen: The first response we detect from a rogue server doesn't
|
||||||
|
# distribute an "address of concern", so the yiaddr option wasn't specified. We alert
|
||||||
|
# based on that. The next response we detect from a rogue server does distribute an
|
||||||
|
# "address of concern", so the yiaddr is specified. Our alert gets throttled since
|
||||||
|
# the first alert we sent was "recent". But this means the administrator doesn't get
|
||||||
|
# notified that the rogue distributed an "address of interest". So we need to
|
||||||
|
# include the state of yiaddr option in the key we pass to THROTTLE_MAIL_CMD,
|
||||||
|
# to ensure the second response isn't throttled using the same key as the first response.)
|
||||||
|
#
|
||||||
|
# We can't just use the value of yiaddr itself as the string to incorporate into the key,
|
||||||
|
# as that would result in a unqiue key for each distributed IP address. Instead, we
|
||||||
|
# will use the state of yiaddr (set or unset).
|
||||||
|
#
|
||||||
|
# Note this means that when a rogue DHCP server is distributing IP addresses that fall into "Networks of Concern",
|
||||||
|
# we will very likely send more than one notification for it within each throttle period.
|
||||||
|
# That's because while some of the responses from the rogue will have a yiaddr within a "Networks of Concern",
|
||||||
|
# others (for example, DHCPNAK responses) will not. This is unfortunate, but is better than the
|
||||||
|
# alternative approach (of not taking into account the yiaddr state in the key), since that will
|
||||||
|
# sometimes cause you to not be alerted at all to the "yiaddr falls into a Network of Concern" situation.
|
||||||
|
#
|
||||||
|
# Create a string based on the state of yiaddr, for later incorporation into the key.
|
||||||
|
my $yiaddr_option_state = $yiaddr ? "yiaddr=set" : "yiaddr=unset";
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
if ($THROTTLE_MAIL_CMD) {
|
||||||
|
# This command suppresses the message if it's sent a message to 'key' within 'throttle_seconds'
|
||||||
|
# I use the calling program's name and the offender's hardware address as the key.
|
||||||
|
|
||||||
|
my $subject_yiaddr_addendum = "";
|
||||||
|
$subject_yiaddr_addendum = ", YIADDR=$yiaddr" if $yiaddr;
|
||||||
|
|
||||||
|
unless (open(THROTTLE_MAIL, "| $THROTTLE_MAIL_CMD -l -k ${calling_program}_mail_${ether_src}_$yiaddr_option_state -t $THROTTLE_MAIL_TIMEOUT -f \"$THROTTLE_MAIL_FROM\" -r \"$THROTTLE_MAIL_RECIPIENT\" -s\"$THROTTLE_MAIL_SUBJECT (MAC=${ether_src}, IP=${ip_src}${subject_yiaddr_addendum})\"")) {
|
||||||
|
|
||||||
|
my_message('LOG_ERR', "${prog}: failure trying to send throttled email: can't execute '${THROTTLE_MAIL_CMD}': open(): $!");
|
||||||
|
exit 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
print THROTTLE_MAIL
|
||||||
|
$timestamp, "\n",
|
||||||
|
"\n",
|
||||||
|
"$calling_program detected an unexpected BOOTP/DHCP server.\n",
|
||||||
|
"Host=$hostname, interface=${ifname}, IP source=${ip_src}, Ethernet source=${ether_src}\n";
|
||||||
|
print THROTTLE_MAIL "\nThis means that *there is* a rogue BOOTP/DHCP server operating.\n" if $VERBOSE;
|
||||||
|
if ($yiaddr) {
|
||||||
|
print THROTTLE_MAIL
|
||||||
|
"The server distributed IP address $yiaddr, which falls into a network of special concern.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (close(THROTTLE_MAIL)) {
|
||||||
|
my_message('LOG_ERR',
|
||||||
|
"${prog}: failure trying to send throttled email: error executing '${THROTTLE_MAIL_CMD}': close(): " .
|
||||||
|
($! ?
|
||||||
|
"syserr closing pipe: $!"
|
||||||
|
:
|
||||||
|
"wait status $? from pipe"
|
||||||
|
) .
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
exit 21;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ($THROTTLE_PAGE_CMD) {
|
||||||
|
# This command suppresses the message if it's sent a message to 'key' within 'throttle_seconds'
|
||||||
|
# I use the calling program's name and the offender's hardware address as the key.
|
||||||
|
|
||||||
|
unless (open(THROTTLE_PAGE, "| $THROTTLE_PAGE_CMD -l -k ${calling_program}_page_${ether_src}_$yiaddr_option_state -t $THROTTLE_PAGE_TIMEOUT -r \"$THROTTLE_PAGE_RECIPIENT\"")) {
|
||||||
|
my_message('LOG_ERR', "${prog}: failure trying to send throttled page: can't execute '${THROTTLE_PAGE_CMD}': open(): $!\n");
|
||||||
|
exit 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
print THROTTLE_PAGE "Rogue DHCP server IP=$ip_src MAC=$ether_src seen via host $hostname interface $ifname\n";
|
||||||
|
print THROTTLE_PAGE "This means *there is* a rogue BOOTP/DHCP server operating.\n" if $VERBOSE;
|
||||||
|
if ($yiaddr) {
|
||||||
|
print THROTTLE_PAGE
|
||||||
|
"Rogue server distributed yiaddr=$yiaddr, a special concern.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (close(THROTTLE_PAGE)) {
|
||||||
|
my_message('LOG_ERR',
|
||||||
|
"${prog}: failure trying to send throttled page: error executing '${THROTTLE_PAGE_CMD}': close(): " .
|
||||||
|
($! ?
|
||||||
|
"syserr closing pipe: $!"
|
||||||
|
:
|
||||||
|
"wait status $? from pipe"
|
||||||
|
) .
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
exit 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
sub my_message {
|
||||||
|
# Call with a syslog priority constant and a message string.
|
||||||
|
# We write the message to syslog, using the specified priority.
|
||||||
|
# Your message should not contain a newline.
|
||||||
|
|
||||||
|
my($priority, $msg) = @_;
|
||||||
|
syslog($priority, $msg);
|
||||||
|
return;
|
||||||
|
}
|
173
templates/usr/local/lib/dhcp-probe/mail-throttled.j2
Executable file
173
templates/usr/local/lib/dhcp-probe/mail-throttled.j2
Executable file
@ -0,0 +1,173 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
|
||||||
|
# {{ ansible_managed }}
|
||||||
|
|
||||||
|
# $Header: /usr/local/etc/RCS/mail-throttled,v 1.11 2008/12/06 01:50:28 root Exp $
|
||||||
|
|
||||||
|
# mail-throttled [-l] [-D dbm_file] -k key -t throttle_seconds [-f from] -r recipient [-d] [-T tie_attempts_max] [-S tie_retry_sleep] [-s subject]
|
||||||
|
#
|
||||||
|
# Sends mail body (read from STDIN) to 'recipient', but avoids doing so "too frequently."
|
||||||
|
#
|
||||||
|
# You provide a 'key', which is an arbitrary string used to identify this notification.
|
||||||
|
# You also provide 'throttle_seconds', an integer. If we've sent anything that
|
||||||
|
# specified this 'key' within the last 'throttle_seconds', we do not send the message.
|
||||||
|
# Otherwise, we send the message, and the remember that we've sent a message for this 'key'
|
||||||
|
# at the current time.
|
||||||
|
#
|
||||||
|
# This key/timesent tuples are stored on-disk, in a dbm. As a result, the 'key'
|
||||||
|
# you supply must satisfy the syntactic requirements for dbm keys.
|
||||||
|
# The caller needs to have permission to read and write this DBM (and create it if
|
||||||
|
# it does not already exist). If you fail to specify a dbm_file, we'll use a default
|
||||||
|
# value, which may not be what you want (since the caller might not be able to r/w that
|
||||||
|
# particular DBM).
|
||||||
|
# We never clean this dbm. You can safely erase it entirely, if you don't mind losing
|
||||||
|
# the state, and you know the caller has permission to create a new instance of the DBM.
|
||||||
|
#
|
||||||
|
# The 'recipient' should be a valid email address. Naturally, it should not
|
||||||
|
# be one that will cause any ack or bounce mail to return to us!
|
||||||
|
# If there are several addresses (delimited by spaces), be sure to quote them as a single arg.
|
||||||
|
#
|
||||||
|
# If a subject is specified, be sure to quote it if it contains any spaces or other shell
|
||||||
|
# metachars.
|
||||||
|
#
|
||||||
|
# If -l is specified, then any errors, warnings, or debugging output is written to syslog
|
||||||
|
# in addition to its usual destination (STDERR). This is helpful if you call this from an
|
||||||
|
# environment where STDERR may get lost.
|
||||||
|
#
|
||||||
|
# Irwin Tillman
|
||||||
|
|
||||||
|
use Getopt::Std;
|
||||||
|
use GDBM_File;
|
||||||
|
use Errno qw(EAGAIN);
|
||||||
|
use Sys::Syslog qw(:DEFAULT setlogsock);
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use vars qw($DBM_FILE_DEFAULT $MAILCMD $MAILCMD_OPTS $FROM_DEFAULT);
|
||||||
|
$DBM_FILE_DEFAULT = '{{ ansible_local.root.lib + "/dhcp-probe/mail-throttled.gdbm" }}';
|
||||||
|
$MAILCMD = "/usr/lib/sendmail";
|
||||||
|
# $MAILCMD_OPTS = "-t -ODeliveryMode=queueonly";
|
||||||
|
$MAILCMD_OPTS = "-t";
|
||||||
|
$FROM_DEFAULT = "root";
|
||||||
|
|
||||||
|
my $SYSLOG_FACILITY="daemon"; # name of facility to use if syslogging
|
||||||
|
my $SYSLOG_OPT = 'pid,cons'; # syslog options to use if syslogging, ignored otherwise
|
||||||
|
my $SYSLOG_PRIORITY = 'LOG_ERR';
|
||||||
|
|
||||||
|
# The tie() call sometimes fails with EAGAIN.
|
||||||
|
# Perhaps that's due to some other process having the DBM open;
|
||||||
|
# in fact, that may be more likely if the process that calls us may calls us multiple times in quick succession.
|
||||||
|
# So when tie() fails with EAGAIN, we can sleep and retry some number of times before giving up entirely.
|
||||||
|
my $TIE_ATTEMPTS_MAX = 3; # number of times to try tie() before giving up
|
||||||
|
my $TIE_RETRY_SLEEP = 1; # seconds to sleep before retrying tie()
|
||||||
|
|
||||||
|
(my $prog = $0) =~ s/.*\///;
|
||||||
|
|
||||||
|
use vars qw($opt_f $opt_D $opt_d $opt_k $opt_l $opt_r $opt_s $opt_S $opt_t $opt_T);
|
||||||
|
&getopts('dD:f:k:lr:s:S:t:T:');
|
||||||
|
|
||||||
|
my $debug = $opt_d || "";
|
||||||
|
my $dbm_file = $opt_D || $DBM_FILE_DEFAULT;
|
||||||
|
my $key = $opt_k || "";
|
||||||
|
my $from = $opt_f || $FROM_DEFAULT;
|
||||||
|
my $recipient = $opt_r || "";
|
||||||
|
my $throttle_secs = $opt_t || 1;
|
||||||
|
my $subject = $opt_s || "";
|
||||||
|
my $also_syslog = $opt_l || "";
|
||||||
|
my $tie_attempts_max = $opt_T || $TIE_ATTEMPTS_MAX; # we deliberately override if CLI option specifies 0, as that makes no sense
|
||||||
|
my $tie_retry_sleep = $opt_S || $TIE_RETRY_SLEEP;
|
||||||
|
|
||||||
|
if ($also_syslog) {
|
||||||
|
# init our use of syslog
|
||||||
|
# setlogsock('unix'); # talk to syslog with UNIX domain socket, not INET domain. XXX causes failure in Solaris 7
|
||||||
|
openlog($prog, $SYSLOG_OPT, $SYSLOG_FACILITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
my_warn("${prog}:\nkey=$key\nthrottle_secs=$throttle_secs\nfrom=$from\nrecipient=$recipient\ntie_attempts_max=$tie_attempts_max\ntie_retry_sleep=$tie_retry_sleep\nsubject=$subject") if $debug;
|
||||||
|
|
||||||
|
# certain options and args are required
|
||||||
|
&Usage() unless ($key && $throttle_secs && $recipient);
|
||||||
|
|
||||||
|
my %last_sent = ();
|
||||||
|
my $tie_succeeded = 0;
|
||||||
|
my $tie_attempts_left = $TIE_ATTEMPTS_MAX;
|
||||||
|
|
||||||
|
while ($tie_attempts_left--) {
|
||||||
|
if (tie(%last_sent, 'GDBM_File', $dbm_file, &GDBM_WRCREAT, 0644)) {
|
||||||
|
$tie_succeeded = 1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
# the tie() failed
|
||||||
|
|
||||||
|
if ($! == EAGAIN) {
|
||||||
|
# The failure may be due to a transient problem.
|
||||||
|
# Retrying may help.
|
||||||
|
sleep $TIE_RETRY_SLEEP;
|
||||||
|
next;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
# Some other (presumably more serious) error.
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unless ($tie_succeeded) {
|
||||||
|
my_warn("${prog}: can't tie ${dbm_file}: $!");
|
||||||
|
exit 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @mailbody = "";
|
||||||
|
@mailbody = <STDIN>; # read it even if we decide not to send it
|
||||||
|
|
||||||
|
my $now = time;
|
||||||
|
|
||||||
|
my_warn("now = $now") if $debug;
|
||||||
|
|
||||||
|
$last_sent{$key} = 0 unless defined($last_sent{$key}); # so it's defined before we use it in subtraction (placate use strict)
|
||||||
|
|
||||||
|
if ($now - $last_sent{$key} >= $throttle_secs) {
|
||||||
|
my_warn("last_sent = $last_sent{$key}, will send") if $debug;
|
||||||
|
unless (open(MAIL, "| $MAILCMD $MAILCMD_OPTS -f\"$from\"")) {
|
||||||
|
my_warn("${prog}: error executing '${MAILCMD}': open(): $!");
|
||||||
|
exit 20;
|
||||||
|
}
|
||||||
|
print MAIL "From: $from\n",
|
||||||
|
"To: $recipient\n",
|
||||||
|
($subject ? "Subject: $subject\n" : "") ,
|
||||||
|
"\n",
|
||||||
|
@mailbody;
|
||||||
|
unless (close(MAIL)) {
|
||||||
|
my_warn("${prog}: error executing '${MAILCMD}': close(): " .
|
||||||
|
($! ?
|
||||||
|
"syserror closing pipe: $!"
|
||||||
|
:
|
||||||
|
"wait status $? from pipe"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
exit 21;
|
||||||
|
}
|
||||||
|
|
||||||
|
$last_sent{$key} = $now;
|
||||||
|
} else {
|
||||||
|
my_warn("last_sent = $last_sent{$key}, suppressing") if $debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
untie %last_sent;
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub Usage {
|
||||||
|
my_warn("Usage: $prog [-l] [-D dbm_file] -k key -t throttle_seconds [-f from] -r recipient [-T tie_attempts_max] [-S tie_retry_sleep] [-s subject]");
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub my_warn {
|
||||||
|
# Just a wrapper for warn, but with a possible copy to syslog too.
|
||||||
|
my $msg = shift;
|
||||||
|
warn $msg, "\n";
|
||||||
|
syslog($SYSLOG_PRIORITY, $msg) if $also_syslog;
|
||||||
|
return;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user