diff --git a/filter_plugins/net_utils.py b/filter_plugins/net_utils.py index 2fd4560..2b96aaa 100644 --- a/filter_plugins/net_utils.py +++ b/filter_plugins/net_utils.py @@ -1,4 +1,5 @@ import ipaddress +from operator import attrgetter import dns.name @@ -9,6 +10,8 @@ class FilterModule: "add_origin": add_origin, "add_origin_keys": add_origin_keys, "ip_filter": ip_filter, + "remove_domain_suffix": remove_domain_suffix, + "ipaddr_sort": ipaddr_sort, } @@ -26,3 +29,31 @@ def add_origin(name, origin="."): def add_origin_keys(dct, origin="."): return {add_origin(k, origin): v for k, v in dct.items()} + + +def remove_domain_suffix(name): + parent = dns.name.from_text(name).parent() + return parent.to_text() + + +def ipaddr_sort(addrs, types, unknown_after=True): + check_types = { + "global": attrgetter("is_global"), + "link-local": attrgetter("is_link_local"), + "loopback": attrgetter("is_loopback"), + "multicast": attrgetter("is_multicast"), + "private": attrgetter("is_private"), + "reserved": attrgetter("is_reserved"), + "site_local": attrgetter("is_site_local"), + "unspecified": attrgetter("is_unspecified"), + } + + def addr_weight(addr): + if isinstance(addr, str): + addr = ipaddress.ip_address(addr.split("/")[0]) + for index, ty in enumerate(types): + if check_types[ty](ipaddress.ip_address(addr)): + return index + return len(types) if unknown_after else -1 + + return sorted(addrs, key=addr_weight) diff --git a/hosts b/hosts index 9f4c2f4..5aab4a9 100644 --- a/hosts +++ b/hosts @@ -90,6 +90,7 @@ dhcp-fleming.adm.auro.re dhcp-fleming-backup.adm.auro.re dns-fleming.adm.auro.re dns-fleming-backup.adm.auro.re +ntp-1.int.infra.auro.re prometheus-fleming.adm.auro.re ns-master.int.infra.auro.re dns-1.int.infra.auro.re @@ -98,6 +99,11 @@ ldap-1.int.infra.auro.re radius-1.isp.infra.auro.re #prometheus-fleming-fo.adm.auro.re radius-fleming.adm.auro.re +dns-1.int.infra.auro.re +isp-1.rtr.infra.auro.re +isp-2.rtr.infra.auro.re +dhcp-1.isp.auro.re +dhcp-2.isp.auro.re radius-fleming-backup.adm.auro.re unifi-fleming.adm.auro.re routeur-fleming.adm.auro.re diff --git a/playbooks/chronyd.yml b/playbooks/chronyd.yml new file mode 100755 index 0000000..b2a5c56 --- /dev/null +++ b/playbooks/chronyd.yml @@ -0,0 +1,27 @@ +#!/usr/bin/env ansible-playbook +--- +- hosts: + - ntp-1.int.infra.auro.re + vars: + chronyd__allow_networks: + - 10.128.0.0/16 + - 2a09:6840:128::/48 + chronyd__pools: + - 0.pool.ntp.org + - 1.pool.ntp.org + - 2.pool.ntp.org + - 3.pool.ntp.org + chronyd__local_stratum: 10 + roles: + - chronyd + +- hosts: + - all + - "!ntp-1.int.infra.auro.re" + - "!unifi" + vars: + chronyd__pools: + - ntp-1.int.infra.auro.re + roles: + - chronyd +... diff --git a/playbooks/ifupdown2.yml b/playbooks/ifupdown2.yml new file mode 100755 index 0000000..fcd816c --- /dev/null +++ b/playbooks/ifupdown2.yml @@ -0,0 +1,213 @@ +#!/usr/bin/env ansible-playbook +--- +- hosts: + - ntp-1.int.infra.auro.re + - dns-1.int.infra.auro.re + - dhcp-1.isp.auro.re + - dhcp-2.isp.auro.re + - isp-1.rtr.infra.auro.re + - isp-2.rtr.infra.auro.re + vars: + # TODO: netbox + ifupdown2__hosts: + ntp-1.int.infra.auro.re: + ens18: + gateways: + - 2a09:6840:128::254 + - 10.128.0.254 + addresses: + - 2a09:6840:128::203/56 + - 10.128.0.203/16 + dns-1.int.infra.auro.re: + ens18: + gateways: + - 2a09:6840:128::254 + - 10.128.0.254 + addresses: + - 2a09:6840:128::127/56 + - 10.128.0.127/16 + dhcp-1.isp.auro.re: + ens18: + gateways: + - 2a09:6840:128::254 + - 10.128.0.254 + addresses: + - 2a09:6840:128::204/56 + - 10.128.0.204/16 + ens19: null + clients: + bridge_vlan_aware: true + bridge_ports: + - ens19 + bridge_vids: + - 1000-1004 + client-0: + addresses: + - 100.64.0.2/27 + vlan_id: 1000 + vlan_raw_device: clients + client-1: + addresses: + - 100.64.0.34/27 + vlan_id: 1001 + vlan_raw_device: clients + client-2: + addresses: + - 100.64.0.66/27 + vlan_id: 1002 + vlan_raw_device: clients + client-3: + addresses: + - 100.64.0.98/27 + vlan_id: 1003 + vlan_raw_device: clients + client-4: + addresses: + - 100.64.0.130/27 + vlan_id: 1004 + vlan_raw_device: clients + dhcp-2.isp.auro.re: + ens18: + gateways: + - 2a09:6840:128::254 + - 10.128.0.254 + addresses: + - 2a09:6840:128::91/56 + - 10.128.0.91/16 + ens19: null + clients: + bridge_vlan_aware: true + bridge_ports: + - ens19 + bridge_vids: + - 1000-1004 + client-0: + addresses: + - 100.64.0.3/27 + vlan_id: 1000 + vlan_raw_device: clients + client-1: + addresses: + - 100.64.0.35/27 + vlan_id: 1001 + vlan_raw_device: clients + client-2: + addresses: + - 100.64.0.67/27 + vlan_id: 1002 + vlan_raw_device: clients + client-3: + addresses: + - 100.64.0.99/27 + vlan_id: 1003 + vlan_raw_device: clients + client-4: + addresses: + - 100.64.0.131/27 + vlan_id: 1004 + vlan_raw_device: clients + isp-1.rtr.infra.auro.re: + ens18: + gateways: + - 2a09:6840:128::254 + - 10.128.0.254 + addresses: + - 2a09:6840:128::255/56 + - 10.128.0.255/16 + ens19: null + clients: + bridge_vlan_aware: true + bridge_ports: + - ens19 + bridge_vids: + - 1000-1004 + bridge_disable_pvid: true + forward: true + ipv6_addrgen: false + client-0: + forward: true + vlan_id: 1000 + vlan_raw_device: clients + ipv6_addrgen: false + client-1: + forward: true + vlan_id: 1001 + vlan_raw_device: clients + ipv6_addrgen: false + client-2: + forward: true + vlan_id: 1002 + vlan_raw_device: clients + ipv6_addrgen: false + client-3: + forward: true + vlan_id: 1003 + vlan_raw_device: clients + ipv6_addrgen: false + client-4: + forward: true + vlan_id: 1004 + vlan_raw_device: clients + ipv6_addrgen: false + isp-2.rtr.infra.auro.re: + ens18: + gateways: + - 2a09:6840:128::254 + - 10.128.0.254 + addresses: + - 2a09:6840:128::158/56 + - 10.128.0.158/16 + ens19: null + clients: + bridge_vlan_aware: true + bridge_ports: + - ens19 + bridge_vids: + - 1000-1004 + client-0: + forward: true + vlan_id: 1000 + vlan_raw_device: clients + ipv6_addrgen: false + client-1: + forward: true + vlan_id: 1001 + vlan_raw_device: clients + ipv6_addrgen: false + client-2: + forward: true + vlan_id: 1002 + vlan_raw_device: clients + ipv6_addrgen: false + client-3: + forward: true + vlan_id: 1003 + vlan_raw_device: clients + ipv6_addrgen: false + client-4: + forward: true + vlan_id: 1004 + vlan_raw_device: clients + ipv6_addrgen: false + ifupdown2__interfaces: "{{ ifupdown2__hosts[inventory_hostname] }}" + roles: + - ifupdown2 + +- hosts: + - ntp-1.int.infra.auro.re + - dns-1.int.infra.auro.re + - dhcp-1.isp.auro.re + - dhcp-2.isp.auro.re + - isp-1.rtr.infra.auro.re + - isp-2.rtr.infra.auro.re + vars: + resolvconf__nameservers: + - 2a09:6840:128::127 + - 10.128.0.127 + resolvconf__domain: auro.re + resolvconf__search: + - "{{ inventory_hostname | remove_domain_suffix }}" + - auro.re + roles: + - resolvconf +... diff --git a/playbooks/keepalived.yml b/playbooks/keepalived.yml new file mode 100755 index 0000000..e8239a1 --- /dev/null +++ b/playbooks/keepalived.yml @@ -0,0 +1,32 @@ +#!/usr/bin/env ansible-playbook +--- +- hosts: + - isp-1.rtr.infra.auro.re + - isp-2.rtr.infra.auro.re + vars: + keepalived__virtual_router_id: 80 + keepalived__interface: ens18 + keepalived__virtual_addresses: + client-0: + - 100.64.0.1/27 + - 2a09:6841::/56 + - fe80::1/10 + client-1: + - 100.64.0.33/27 + - 2a09:6841:0:100::/56 + - fe80::1/10 + client-2: + - 100.64.0.65/27 + - 2a09:6841:0:100::/56 + - fe80::1/10 + client-3: + - 100.64.0.97/27 + - 2a09:6841:0:200::/56 + - fe80::1/10 + client-4: + - 100.64.0.129/27 + - 2a09:6841:0:300::/56 + - fe80::1/10 + roles: + - keepalived +... diff --git a/roles/chronyd/defaults/main.yml b/roles/chronyd/defaults/main.yml new file mode 100644 index 0000000..8214c05 --- /dev/null +++ b/roles/chronyd/defaults/main.yml @@ -0,0 +1,11 @@ +--- +chronyd__pools: [] +chronyd__key_file: /etc/chrony/chrony.keys +chronyd__drift_file: /var/lib/chrony/chrony.drift +chronyd__nts_dump_dir: /var/lib/chrony +chronyd__log_dir: /var/log/chrony +chronyd__max_update_skew: 100.0 +chronyd__rtcsync: true +chronyd__allow_networks: [] +chronyd__log_change_seconds: 0.5 +... diff --git a/roles/chronyd/handlers/main.yml b/roles/chronyd/handlers/main.yml new file mode 100644 index 0000000..c6b9394 --- /dev/null +++ b/roles/chronyd/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: Restart chronyd + systemd: + name: chrony.service + state: restarted +... diff --git a/roles/chronyd/tasks/main.yml b/roles/chronyd/tasks/main.yml new file mode 100644 index 0000000..ee0bafc --- /dev/null +++ b/roles/chronyd/tasks/main.yml @@ -0,0 +1,32 @@ +--- +- name: Uninstall ntp and sntp + apt: + name: + - sntp + - ntp + - systemd-timesyncd + state: absent + +- name: Install chronyd + apt: + name: chrony + +- name: Configure chronyd + template: + src: "{{ item }}.j2" + dest: "/etc/chrony/{{ item }}" + owner: root + group: root + mode: u=rw,g=r,o= + loop: + - chrony.conf + - chrony.keys + notify: + - Restart chronyd + +- name: Enable and start chronyd + systemd: + name: chrony.service + enabled: true + state: started +... diff --git a/roles/chronyd/templates/chrony.conf.j2 b/roles/chronyd/templates/chrony.conf.j2 new file mode 100644 index 0000000..2db858a --- /dev/null +++ b/roles/chronyd/templates/chrony.conf.j2 @@ -0,0 +1,30 @@ +{{ ansible_managed | comment }} + +{% for pool in chronyd__pools %} +pool {{ pool }} iburst +{% endfor %} + +keyfile {{ chronyd__key_file }} +driftfile {{ chronyd__drift_file }} +ntsdumpdir {{ chronyd__nts_dump_dir }} +logdir {{ chronyd__log_dir }} + +log tracking measurements statistics + +maxupdateskew {{ chronyd__max_update_skew | float }} + +{% if chronyd__rtcsync %} +rtcsync +{% endif %} + +{% if chronyd__local_stratum is defined %} +local stratum {{ chronyd__local_stratum | int }} +{% endif %} + +logchange {{ chronyd__log_change_seconds | float }} + +leapsectz right/UTC + +{% for network in chronyd__allow_networks %} +allow {{ network | ipaddr }} +{% endfor %} diff --git a/roles/chronyd/templates/chrony.keys.j2 b/roles/chronyd/templates/chrony.keys.j2 new file mode 100644 index 0000000..5c02948 --- /dev/null +++ b/roles/chronyd/templates/chrony.keys.j2 @@ -0,0 +1 @@ +{{ ansible_managed | comment }} diff --git a/roles/ifupdown2/defaults/main.yml b/roles/ifupdown2/defaults/main.yml new file mode 100644 index 0000000..a419f07 --- /dev/null +++ b/roles/ifupdown2/defaults/main.yml @@ -0,0 +1,3 @@ +--- +ifupdown2__interfaces: {} +... diff --git a/roles/ifupdown2/handlers/main.yml b/roles/ifupdown2/handlers/main.yml new file mode 100644 index 0000000..9a5d0c0 --- /dev/null +++ b/roles/ifupdown2/handlers/main.yml @@ -0,0 +1,9 @@ +--- +- name: Restart networking + systemd: + name: networking.service + state: restarted + +- name: Bring all interfaces up + shell: /usr/sbin/ifup -a +... diff --git a/roles/ifupdown2/tasks/main.yml b/roles/ifupdown2/tasks/main.yml new file mode 100644 index 0000000..aa07c7f --- /dev/null +++ b/roles/ifupdown2/tasks/main.yml @@ -0,0 +1,42 @@ +--- +- name: Gather package facts + package_facts: + manager: apt + +- name: Check if ifupdown2 is installed + set_fact: + must_mask: "{{ 'ifupdown2' not in ansible_facts.packages }}" + +- name: Mask networking before installing ifupdown2 + systemd: + name: networking.service + masked: true + when: must_mask + +- name: Install ifupdown2 + apt: + name: ifupdown2 + +- name: Unmask networking now that ifupdown2 is installed + systemd: + name: networking.service + masked: false + when: must_mask + +- name: Configure ifupdown2 + template: + src: interfaces.j2 + dest: /etc/network/interfaces + owner: root + group: root + mode: u=rw,g=r,o= + notify: + - Restart networking + - Bring all interfaces up + +- name: Enable and start networking + systemd: + name: networking.service + state: started + enabled: true +... diff --git a/roles/ifupdown2/templates/interfaces.j2 b/roles/ifupdown2/templates/interfaces.j2 new file mode 100644 index 0000000..a1e8f8a --- /dev/null +++ b/roles/ifupdown2/templates/interfaces.j2 @@ -0,0 +1,41 @@ +{{ ansible_managed | comment }} + +{% for name, iface in ifupdown2__interfaces.items() %} +auto {{ name }} +iface {{ name }} +{% for address in iface.addresses | default([]) %} + address {{ address | ipaddr }} +{% endfor %} +{% for gateway in iface.gateways | default([]) %} + gateway {{ gateway | ipaddr }} +{% endfor %} +{% if iface.bridge_ports is defined %} + bridge-ports {{ iface.bridge_ports | join(" ") }} +{% endif %} +{% if iface.bridge_vlan_aware is defined %} + bridge-vlan-aware {{ iface.bridge_vlan_aware + | ternary("yes", "no") }} +{% endif %} +{% if iface.bridge_vids is defined %} + bridge-vids {{ iface.bridge_vids | join(",") }} +{% endif %} +{% if iface.vlan_id is defined %} + vlan-id {{ iface.vlan_id | int }} +{% endif %} +{% if iface.vlan_raw_device is defined %} + vlan-raw-device {{ iface.vlan_raw_device }} +{% endif %} +{% if iface.bridge_disable_pvid | default(false) %} + bridge-pvid 0 + post-up bridge vlan del dev {{ name }} vid 1 self +{% endif %} +{% if iface.forward | default(false) %} + ip-forward yes + ip6-forward yes +{% endif %} +{% if iface.ipv6_addrgen is defined %} + ipv6-addrgen {{ iface.ipv6_addrgen + | ternary("yes", "no") }} +{% endif %} + +{% endfor %} diff --git a/roles/keepalived/defaults/main.yml b/roles/keepalived/defaults/main.yml new file mode 100644 index 0000000..ee034f3 --- /dev/null +++ b/roles/keepalived/defaults/main.yml @@ -0,0 +1,7 @@ +--- +keepalived__virtual_addresses: {} +keepalived__notify_master: [] +keepalived__notify_backup: [] +keepalived__notify_fault: [] +keepalived__max_auto_priority: -1 +... diff --git a/roles/keepalived/handlers/main.yml b/roles/keepalived/handlers/main.yml new file mode 100644 index 0000000..df390cb --- /dev/null +++ b/roles/keepalived/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: Reload keepalived + systemd: + name: keepalived.service + state: reloaded +... diff --git a/roles/keepalived/tasks/main.yml b/roles/keepalived/tasks/main.yml new file mode 100644 index 0000000..6330901 --- /dev/null +++ b/roles/keepalived/tasks/main.yml @@ -0,0 +1,28 @@ +--- +- name: Install keepalived + apt: + name: keepalived + +- name: Configure keepalived + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: root + group: root + mode: "{{ item.mode }}" + loop: + - src: keepalived.conf.j2 + dest: /etc/keepalived/keepalived.conf + mode: u=rw,g=,o= + - src: notify.sh.j2 + dest: /etc/keepalived/notify.sh + mode: u=rwx,g=,o= + notify: + - Reload keepalived + +- name: Enable and start keepalived + systemd: + name: keepalived + enabled: true + state: started +... diff --git a/roles/keepalived/templates/keepalived.conf.j2 b/roles/keepalived/templates/keepalived.conf.j2 new file mode 100644 index 0000000..c99ae10 --- /dev/null +++ b/roles/keepalived/templates/keepalived.conf.j2 @@ -0,0 +1,92 @@ +{{ ansible_managed | comment }} + +global_defs { + dynamic_interfaces + script_user root + enable_script_security + vrrp_version 3 +{% if keepalived__max_auto_priority is defined %} + max_auto_priority {{ keepalived__max_auto_priority | int }} +{% endif %} +} + +{% +set ipv4_enabled = + keepalived__ipv4_enabled + | default(keepalived__virtual_addresses.values() + | flatten | ansible.utils.ipv4) +%} +{% +set ipv6_enabled = + keepalived__ipv6_enabled + | default(keepalived__virtual_addresses.values() + | flatten | ansible.utils.ipv6) +%} + +{% if ipv4_enabled and ipv6_enabled %} +vrrp_sync_group group { + group { +{% if ipv4_enabled %} + instance_v4 +{% endif %} +{% if ipv6_enabled %} + instance_v6 +{% endif %} + } + notify_master "/etc/keepalived/notify.sh master" + notify_backup "/etc/keepalived/notify.sh backup" + notify_fault "/etc/keepalived/notify.sh fault" +} +{% endif %} + +{% if ipv4_enabled %} +vrrp_instance instance_v4 { + virtual_router_id {{ keepalived__virtual_router_id | int }} + interface {{ keepalived__interface }} + state BACKUP + priority 250 + nopreempt + advert_int 1 + accept + virtual_ipaddress { +{% for dev, addresses in keepalived__virtual_addresses.items() %} +{% for address in addresses %} +{% if address | ansible.utils.ipv4 %} + {{ address }} dev {{ dev }} +{% endif %} +{% endfor %} +{% endfor %} + } +{% if not (ipv4_enabled and ipv6_enabled) %} + notify_master "/etc/keepalived/notify.sh master" + notify_backup "/etc/keepalived/notify.sh backup" + notify_fault "/etc/keepalived/notify.sh fault" +{% endif %} +} +{% endif %} + +{% if ipv6_enabled %} +vrrp_instance instance_v6 { + virtual_router_id {{ keepalived__virtual_router_id | int }} + interface {{ keepalived__interface }} + state BACKUP + priority 250 + nopreempt + advert_int 1 + accept + virtual_ipaddress { +{% for dev, addresses in keepalived__virtual_addresses.items() %} +{% for address in addresses | ipaddr_sort(["link-local"]) %} +{% if address | ansible.utils.ipv6 %} + {{ address }} dev {{ dev }} +{% endif %} +{% endfor %} +{% endfor %} + } +{% if not (ipv4_enabled and ipv6_enabled) %} + notify_master "/etc/keepalived/notify.sh master" + notify_backup "/etc/keepalived/notify.sh backup" + notify_fault "/etc/keepalived/notify.sh fault" +{% endif %} +} +{% endif %} diff --git a/roles/keepalived/templates/notify.sh.j2 b/roles/keepalived/templates/notify.sh.j2 new file mode 100644 index 0000000..4f58259 --- /dev/null +++ b/roles/keepalived/templates/notify.sh.j2 @@ -0,0 +1,33 @@ +#!/bin/bash + +master=( +{% for notify in keepalived__notify_master %} + {{ notify | quote }} +{% endfor %} +) + +backup=( +{% for notify in keepalived__notify_backup %} + {{ notify | quote }} +{% endfor %} +) + +fault=( +{% for notify in keepalived__notify_fault %} + {{ notify | quote }} +{% endfor %} +) + +case "$1" in + master | backup | fault) + scripts="$1[@]" + ;; + *) + echo "Usage: $0 (master|backup|fault)" >&2 + exit 1 +esac + +for script in "${!scripts}" +do + eval "${script}" +done diff --git a/roles/resolvconf/tasks/main.yml b/roles/resolvconf/tasks/main.yml new file mode 100644 index 0000000..d650b78 --- /dev/null +++ b/roles/resolvconf/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- name: Install resolv.conf + template: + src: resolv.conf.j2 + dest: /etc/resolv.conf + owner: root + group: root + mode: u=rw,g=r,o=r +... diff --git a/roles/resolvconf/templates/resolv.conf.j2 b/roles/resolvconf/templates/resolv.conf.j2 new file mode 100644 index 0000000..9376000 --- /dev/null +++ b/roles/resolvconf/templates/resolv.conf.j2 @@ -0,0 +1,11 @@ +{{ ansible_managed | comment }} + +{% for nameserver in resolvconf__nameservers %} +nameserver {{ nameserver | ipaddr }} +{% endfor %} +{% if resolvconf__domain is defined %} +domain {{ resolvconf__domain }} +{% endif %} +{% if resolvconf__search is defined %} +search {{ resolvconf__search | join(" ") }} +{% endif %}