diff --git a/filter_plugins/enquote.py b/filter_plugins/enquote.py new file mode 100644 index 0000000..576bf3f --- /dev/null +++ b/filter_plugins/enquote.py @@ -0,0 +1,16 @@ +class FilterModule: + def filters(self): + return { + "enquote": enquote, + } + + +def enquote(string, delimiter='"', escape="\\"): + translation = str.maketrans( + { + delimiter: f"{escape}{delimiter}", + escape: f"{escape}{escape}", + } + ) + escaped = string.translate(translation) + return f"{delimiter}{escaped}{delimiter}" diff --git a/playbooks/bird.yml b/playbooks/bird.yml new file mode 100755 index 0000000..f66c767 --- /dev/null +++ b/playbooks/bird.yml @@ -0,0 +1,49 @@ +#!/usr/bin/env ansible-playbook +--- +- hosts: + - isp-1.rtr.infra.auro.re + - isp-2.rtr.infra.auro.re + vars: + bird__router_ids: + isp-1.rtr.infra.auro.re: 10.136.0.1 + isp-2.rtr.infra.auro.re: 10.136.0.2 + bird__router_id: "{{ bird__router_ids[inventory_hostname] }}" + bird__ospf_broadcast_interfaces: + ens20: null + bird__ospf_stub_interfaces: + - client-0 + - client-1 + - client-2 + - client-3 + - client-4 + bird__radv_interfaces: + client-0: + prefix: + - 2a09:6841::/56 + domain_search: + - client-0.isp.auro.re + client-1: + prefix: + - 2a09:6841:0:100::/56 + domain_search: + - client-1.isp.auro.re + client-2: + prefix: + - 2a09:6841:0:200::/56 + domain_search: + - client-2.isp.auro.re + client-3: + prefix: + - 2a09:6841:0:300::/56 + domain_search: + - client-3.isp.auro.re + client-4: + prefix: + - 2a09:6841:0:400::/56 + domain_search: + - client-4.isp.auro.re + bird__radv_dns_servers: + - 2a09:6840:128::127 + roles: + - bird +... diff --git a/roles/bird/defaults/main.yml b/roles/bird/defaults/main.yml new file mode 100644 index 0000000..e053da4 --- /dev/null +++ b/roles/bird/defaults/main.yml @@ -0,0 +1,11 @@ +--- +bird__ospf_stub_interfaces: [] +bird__ospf_broadcast_interfaces: {} +bird__ospf_hello: 2 +bird__ospf_retransmit: 5 +bird__ospf_wait: 10 +bird__ospf_dead: 20 +bird__radv_interfaces: {} +bird__radv_dns_servers: [] +bird__radv_max_interval: 5 +... diff --git a/roles/bird/handlers/main.yml b/roles/bird/handlers/main.yml new file mode 100644 index 0000000..7de17ff --- /dev/null +++ b/roles/bird/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: Reload bird + systemd: + name: bird.service + state: reloaded +... diff --git a/roles/bird/tasks/main.yml b/roles/bird/tasks/main.yml new file mode 100644 index 0000000..4b896ef --- /dev/null +++ b/roles/bird/tasks/main.yml @@ -0,0 +1,21 @@ +--- +- name: Install bird + apt: + name: bird2 + +- name: Configure bird + template: + src: bird.conf.j2 + dest: /etc/bird/bird.conf + owner: root + group: bird + mode: u=rw,g=r,o= + notify: + - Reload bird + +- name: Enable and start bird + systemd: + name: bird.service + state: started + enabled: true +... diff --git a/roles/bird/templates/bird.conf.j2 b/roles/bird/templates/bird.conf.j2 new file mode 100644 index 0000000..fa187f9 --- /dev/null +++ b/roles/bird/templates/bird.conf.j2 @@ -0,0 +1,105 @@ +{{ ansible_managed | comment }} + +log syslog all; + +router id {{ bird__router_id }}; + +protocol device { + scan time 10; +} + +protocol kernel { + ipv4 { + import all; + export all; + }; +} + +protocol kernel { + ipv6 { + import all; + export all; + }; +} + +protocol ospf v2 { + + ipv4 { + import all; + export where source = RTS_STATIC; + }; + + area 0 { +{% for name, iface in bird__ospf_broadcast_interfaces.items() %} + interface {{ name | enquote }} { + type broadcast; + hello {{ iface.hello | default(bird__ospf_hello) | int }}; + retransmit {{ iface.retransmit + | default(bird__ospf_retransmit) + | int }}; + wait {{ iface.wait | default(bird__ospf_wait) | int }}; + dead {{ iface.dead | default(bird__ospf_dead) | int }}; + }; +{% endfor %} +{% for name in bird__ospf_stub_interfaces %} + interface {{ name | enquote }} { + stub; + }; +{% endfor %} + }; + +} + +protocol ospf v3 { + + ipv6 { + import all; + export where source = RTS_STATIC; + }; + + area 0 { +{% for name, iface in bird__ospf_broadcast_interfaces.items() %} + interface {{ name | enquote }} { + type broadcast; + hello {{ iface.hello | default(bird__ospf_hello) | int }}; + retransmit {{ iface.retransmit + | default(bird__ospf_retransmit) + | int }}; + wait {{ iface.wait | default(bird__ospf_wait) | int }}; + dead {{ iface.dead | default(bird__ospf_dead) | int }}; + }; +{% endfor %} +{% for name in bird__ospf_stub_interfaces %} + interface {{ name | enquote }} { + stub; + }; +{% endfor %} + }; + +} + +{% if bird__radv_interfaces %} +protocol radv { + + ipv6 { + export all; + }; + +{% for name, iface in bird__radv_interfaces.items() %} + interface {{ name | enquote }} { + max ra interval {{ bird__radv_max_interval | int }}; +{% for prefix in iface.prefix | default([]) %} + prefix {{ prefix | ipaddr }}; +{% endfor %} +{% for domain in iface.domain_search | default([]) %} + dnssl {{ domain | enquote }}; +{% endfor %} + }; +{% endfor %} + +{% for address in bird__radv_dns_servers %} + rdnss {{ address | ipaddr }}; +{% endfor %} + +} +{% endif %}