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'
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
@ -13,7 +20,7 @@ This role requires at least Ansible `v1.7.0`. To install it, run:
|
||||
### Documentation
|
||||
|
||||
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?
|
||||
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
|
||||
# If this list is empty, Ansible will try to guess correct interfaces
|
||||
# automatically
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
# ---- ISC DHCP Server configuration scopes ----
|
||||
# ----------------------------------------
|
||||
# ISC DHCP Server configuration scopes
|
||||
# ----------------------------------------
|
||||
|
||||
# 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)
|
||||
# manual page. You can create nested configuration using Ansible variable
|
||||
# expansion (examples below).
|
||||
|
||||
# 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
|
||||
# expansion.
|
||||
|
||||
# .. envvar:: dhcpd_keys
|
||||
#
|
||||
# List of secret keys used for Dynamic DNS configuration. See
|
||||
# :ref:`dhcpd_keys` for more details.
|
||||
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: []
|
||||
#- 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: []
|
||||
#- 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: []
|
||||
#- 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
|
||||
dhcpd_subnets:
|
||||
- subnet: '{{ ansible_default_ipv4.network }}'
|
||||
netmask: '{{ ansible_default_ipv4.netmask }}'
|
||||
# .. envvar:: dhcpd_subnets
|
||||
#
|
||||
# List of subnets not in a shared network. See :ref:`dhcpd_subnets` for more
|
||||
# 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'
|
||||
|
||||
#- subnet: 'dead:be:ef::/64'
|
||||
# ipv6: True
|
||||
# routers: '10.0.10.1'
|
||||
# comment: "Example IPv6 subnet"
|
||||
# options: |
|
||||
# default-lease-time 300;
|
||||
# max-lease-time 7200;
|
||||
#
|
||||
#- subnet: '10.0.20.0'
|
||||
# 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' ]
|
||||
# .. envvar:: dhcpd_hosts
|
||||
#
|
||||
# - 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
|
||||
# Global list of hosts in DHCP. See ref:`dhcpd_hosts` for more details.
|
||||
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: []
|
||||
#- '/etc/dhcp/example.conf'
|
||||
|
||||
# ---- ISC DHCP failover configuration ----
|
||||
#
|
||||
# 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
|
||||
|
||||
# .. envvar:: dhcpd_failovers
|
||||
#
|
||||
# DHCP failover configuration. See :ref:`dhcpd_failovers` for more details.
|
||||
dhcpd_failovers: []
|
||||
## Following is full cluster configuration
|
||||
#- 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
|
||||
#
|
||||
## Following is minimal cluster configuration
|
||||
#- 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
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# dhcp-probe configuration
|
||||
# -----------------------------
|
||||
|
||||
# .. envvar:: dhcpd_probe
|
||||
#
|
||||
# Enable or disable ``dhcp-probe`` script
|
||||
dhcpd_probe: True
|
||||
|
||||
|
||||
# .. envvar:: dhcpd_probe_mail_to
|
||||
#
|
||||
# List of mail recipients which will receive messages about unauthorized DHCP
|
||||
# servers. Set to ``[]`` to disable.
|
||||
dhcpd_probe_mail_to: [ 'root@{{ ansible_domain }}' ]
|
||||
|
||||
|
||||
# .. envvar:: dhcpd_probe_page_to
|
||||
#
|
||||
# Alternative list of mail recipients which will receive mail messages. Meant
|
||||
# to be used as a "pager service", you can use ``debops.smstools`` role to
|
||||
# setup a mail-SMS gateway and send the SMS messages that way.
|
||||
dhcpd_probe_page_to: []
|
||||
|
||||
|
||||
# .. 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
|
||||
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'
|
||||
|
||||
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:
|
||||
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
|
||||
apt: pkg={{ item }} state=latest install_recommends=no
|
||||
with_items: [ 'isc-dhcp-server' ]
|
||||
- name: Configure DHCP relay in debconf
|
||||
debconf:
|
||||
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
|
||||
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' ]
|
||||
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.
|
||||
# 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?
|
||||
# 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
|
||||
{% macro print_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(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 %}
|
||||
{% import 'macros.j2' as print with context %}
|
||||
# {{ ansible_managed }}
|
||||
|
||||
{% if dhcpd_authoritative is defined and dhcpd_authoritative %}
|
||||
authoritative;
|
||||
@ -195,10 +8,19 @@ authoritative;
|
||||
not authoritative;
|
||||
|
||||
{% endif %}
|
||||
{% if dhcpd_global_options is defined and dhcpd_global_options %}
|
||||
# Global configuration options
|
||||
{{ dhcpd_global_options }}
|
||||
{% if dhcpd_global_default_lease_time|d() and dhcpd_global_default_lease_time %}
|
||||
default-lease-time {{ dhcpd_global_default_lease_time }};
|
||||
{% 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 %}
|
||||
{% if dhcpd_options is defined and dhcpd_options %}
|
||||
# Configuration options
|
||||
@ -207,17 +29,22 @@ not authoritative;
|
||||
{% endif %}
|
||||
{% if dhcpd_keys is defined and 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 %}
|
||||
{% endif %}
|
||||
{% if dhcpd_classes is defined and dhcpd_classes %}
|
||||
{% for class in dhcpd_classes %}
|
||||
{{ print_class(class) }}
|
||||
{{ print.class(class) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if dhcpd_failovers is defined and dhcpd_failovers %}
|
||||
{% for failover in dhcpd_failovers %}
|
||||
{{ print_failover(failover) }}
|
||||
{{ print.failover(failover) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if dhcpd_shared_networks is defined and dhcpd_shared_networks %}
|
||||
@ -235,7 +62,7 @@ shared-network "{{ network.name }}" {
|
||||
{% endif %}
|
||||
{% for subnet in network.subnets %}
|
||||
|
||||
{{ print_subnet(subnet) | indent(8,true) }}
|
||||
{{ print.subnet(subnet) | indent(8,true) }}
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
@ -244,16 +71,16 @@ shared-network "{{ network.name }}" {
|
||||
{% endif %}
|
||||
{% if dhcpd_groups is defined and dhcpd_groups %}
|
||||
{% for group in dhcpd_groups %}
|
||||
{{ print_group(group) }}
|
||||
{{ print.group(group) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if dhcpd_subnets is defined and dhcpd_subnets %}
|
||||
{% for subnet in dhcpd_subnets %}
|
||||
{{ print_subnet(subnet) }}
|
||||
{{ print.subnet(subnet) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if dhcpd_hosts is defined and dhcpd_hosts %}
|
||||
{{ print_hosts(dhcpd_hosts) }}
|
||||
{{ print.hosts(dhcpd_hosts) }}
|
||||
{% endif %}
|
||||
{% if dhcpd_includes is defined and 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