commit 2151b8e03198fbd2f7e0dc69a122a626127dee5c Author: Simeon Keske Date: Sun May 17 16:28:26 2020 +0200 initial commit diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..b945f0c --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,27 @@ +dn42_wg_private_key: "foobar2342" + +dn42_local_subnet_v4: "172.17.0.1/28" +dn42_local_subnet_v6: "fe80::1/56" +dn42_local_v4: "172.17.0.1" +dn42_local_v6: "fe80::1" +dn42_local_as: "424242424243" + +dn42_enable_roa: yes +dn42_roa_v4_source: "https://dn42.burble.com/roa/dn42_roa_bird2_4.conf" +dn42_roa_v4_location: "/etc/bird/roa_dn42.conf" +dn42_roa_v6_source: "https://dn42.burble.com/roa/dn42_roa_bird2_6.conf" +dn42_roa_v6_location: "/etc/bird/roa_dn42_v6.conf" + +dn42_roa_cronjob: "curl -sfSLR -o{{ dn42_roa_v4_location }} -z{{ dn42_roa_v4_location }} {{ dn42_roa_v4_source }} && curl -sfSLR -o{{ dn42_roa_v6_location }} -z{{ dn42_roa_v6_location }} {{ dn42_roa_v6_source }} && birdc configure" + +dn42_peers: + - name: "neighbour" + as: "4242424242" + v6: "fe80::42:1" + if: + v6: fe80::42:42:1 + wg: + port: 42424 + endpoint: "example.com:2342" + pubkey: "peers_public_key" + privkey: "your_private_key" \ No newline at end of file diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..c712ebd --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: reload bird + service: + name: "bird" + state: reloaded + +- name: restart networking + service: + name: "networking" + state: restarted diff --git a/tasks/bird-repos.yml b/tasks/bird-repos.yml new file mode 100644 index 0000000..ab096a6 --- /dev/null +++ b/tasks/bird-repos.yml @@ -0,0 +1,10 @@ +--- +- name: Add birds packaging Key + apt_key: + url: "http://bird.network.cz/debian/apt.gpg" + state: present + +- name: Ensure bird repos are present + apt_repository: + repo: "deb https://bird.network.cz/debian/ {{ ansible_distribution_release }} main" + update_cache: yes \ No newline at end of file diff --git a/tasks/bird2.yml b/tasks/bird2.yml new file mode 100644 index 0000000..7d363e4 --- /dev/null +++ b/tasks/bird2.yml @@ -0,0 +1,29 @@ +--- +#- import_tasks: bird-repos.yml +- name: Install bird2 + apt: + name: "bird2" + state: present + +- name: Copy bird config-file + template: + dest: /etc/bird/bird.conf + src: "bird2/bird.conf.j2" + notify: reload bird + +- name: Ensure birds peer foler exists + file: + path: "/etc/bird/peers" + state: directory + owner: "bird" + group: "bird" + +- name: Copy birds peer config-files + template: + dest: "/etc/bird/peers/{{ peer.name }}.conf" + src: "bird2/peer.conf.j2" + loop: "{{ dn42_peers }}" + loop_control: + loop_var: "peer" + notify: reload bird + diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..ef8bdfe --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,30 @@ +--- +- import_tasks: sysctl.yml +- import_tasks: bird2.yml +- include_tasks: wireguard.yml + loop: "{{ dn42_peers }}" + loop_control: + loop_var: "peer" + +- name: Download ROA-Tables + shell: "{{ dn42_roa_cronjob }}" + args: + creates: "{{ dn42_roa_v6_location }}" + when: "dn42_enable_roa" + +- name: Install ROA-Cron-Job + cron: + name: Download ROA-Tables + minute: "*/15" + job: "{{ dn42_roa_cronjob }}" + when: "dn42_enable_roa" + +- name: Configure Local IPs on loopback-interface + interfaces_file: + iface: "lo" + option: "up" + value: + - "ip a add {{ dn42_local_v4 }}/32 dev lo" + - "ip a add {{ dn42_local_v6 }}/128 dev lo" + state: present + notify: restart networking \ No newline at end of file diff --git a/tasks/sysctl.yml b/tasks/sysctl.yml new file mode 100644 index 0000000..a46bc3f --- /dev/null +++ b/tasks/sysctl.yml @@ -0,0 +1,25 @@ +--- +- name: Ensure forwarding of ipv4-packets is allowed + sysctl: + name: net.ipv4.ip_forward + value: '1' + state: present + +- name: Ensure forwarding of ipv6-packets is allowed + sysctl: + name: net.ipv6.conf.all.forwarding + value: '1' + state: present + +- name: Ensure reverse path filtering is disabled for v4 + sysctl: + name: net.ipv4.conf.all.rp_filter + value: '0' + state: present + +- name: Ensure default reverse path filtering is disabled for v4 + sysctl: + name: net.ipv4.conf.all.rp_filter + value: '0' + state: present + diff --git a/tasks/wireguard.yml b/tasks/wireguard.yml new file mode 100644 index 0000000..6c89b77 --- /dev/null +++ b/tasks/wireguard.yml @@ -0,0 +1,23 @@ + +- name: Install wg-quick@{{ peer.if.name | default("dn42_" + peer.name) }} config + template: + dest: "/etc/wireguard/{{ peer.if.name | default('dn42_' + peer.name) }}.conf" + src: "wg-quick.j2" + when: "peer.wg is defined" + register: "configuration" + +- name: Enable wg-quick@{{ peer.if.name | default("dn42_" + peer.name) }} service + service: + name: "wg-quick@{{ peer.if.name | default('dn42_' + peer.name) }}" + enabled: yes + +- name: Restart wg-quick@{{ peer.if.name | default('dn42_' + peer.name) }} + service: + name: "wg-quick@{{ peer.if.name | default('dn42_' + peer.name) }}" + state: restarted + when: "configuration is changed" + + + + + diff --git a/templates/bird/bird.conf.j2 b/templates/bird/bird.conf.j2 new file mode 100644 index 0000000..34e8b8f --- /dev/null +++ b/templates/bird/bird.conf.j2 @@ -0,0 +1,75 @@ +# Device status +protocol device { + scan time 10; # recheck every 10 seconds +} + +protocol static { + # Static routes to announce your own range(s) in dn42 + route {{ dn42_local_subnet_v4 }} reject; + import all; + export none; +}; + +# local configuration +###################### + +# keeping router specific in a seperate file, +# so this configuration can be reused on multiple routers in your network +include "/etc/bird/local4.conf"; + +# filter helpers +################# + +##include "/etc/bird/filter4.conf"; + +# Kernel routing tables +######################## + +/* + krt_prefsrc defines the source address for outgoing connections. + On Linux, this causes the "src" attribute of a route to be set. + + Without this option outgoing connections would use the peering IP which + would cause packet loss if some peering disconnects but the interface + is still available. (The route would still exist and thus route through + the TUN/TAP interface but the VPN daemon would simply drop the packet.) +*/ +protocol kernel { + scan time 20; + import none; + export filter { + if source = RTS_STATIC then reject; + krt_prefsrc = OWNIP; + accept; + }; +}; +# DN42 +####### + +template bgp dnpeers { + local as OWNAS; + # metric is the number of hops between us and the peer + path metric 1; + # this lines allows debugging filter rules + # filtered routes can be looked up in birdc using the "show route filtered" command + import keep filtered; + import filter { + # accept every subnet, except our own advertised subnet + # filtering is important, because some guys try to advertise routes like 0.0.0.0 + if is_valid_network() && !is_self_net() then { + accept; + } + reject; + }; + export filter { + # here we export the whole net + if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then { + accept; + } + reject; + }; + import limit 1000 action block; + #source address OWNIP; +}; + +include "/etc/bird/peers4/*"; \ No newline at end of file diff --git a/templates/bird/bird6.conf.j2 b/templates/bird/bird6.conf.j2 new file mode 100644 index 0000000..12c7d4e --- /dev/null +++ b/templates/bird/bird6.conf.j2 @@ -0,0 +1,66 @@ +protocol device { + scan time 10; +} + +# local configuration +###################### + +include "/etc/bird/local6.conf"; + +# filter helpers +################# + +##include "/etc/bird/filter6.conf"; + +# Kernel routing tables +######################## + + +/* + krt_prefsrc defines the source address for outgoing connections. + On Linux, this causes the "src" attribute of a route to be set. + + Without this option outgoing connections would use the peering IP which + would cause packet loss if some peering disconnects but the interface + is still available. (The route would still exist and thus route through + the TUN/TAP interface but the VPN daemon would simply drop the packet.) +*/ +protocol kernel { + scan time 20; + import none; + export filter { + if source = RTS_STATIC then reject; + krt_prefsrc = OWNIP; + accept; + }; +} + +# static routes +################ + +protocol static { + route {{ dn42_local_subnet_v6 }} reject; + import all; + export none; +} + +template bgp dnpeers { + local as OWNAS; + path metric 1; + import keep filtered; + import filter { + if is_valid_network() && !is_self_net() then { + accept; + } + reject; + }; + export filter { + if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then { + accept; + } + reject; + }; + import limit 1000 action block; +} + +include "/etc/bird/peers6/*"; \ No newline at end of file diff --git a/templates/bird/local4.conf.j2 b/templates/bird/local4.conf.j2 new file mode 100644 index 0000000..70c007c --- /dev/null +++ b/templates/bird/local4.conf.j2 @@ -0,0 +1,23 @@ +# should be a unique identifier, is what most people use. +router id {{ dn42_local_v4 }}; + +define OWNAS = {{ dn42_local_as }}; +define OWNIP = {{ dn42_local_v4 }}; + +function is_self_net() { + return net ~ [{{ dn42_local_subnet_v4 }}+]; +} + +function is_valid_network() { + return net ~ [ + 172.20.0.0/14{21,29}, # dn42 + 172.20.0.0/24{28,32}, # dn42 Anycast + 172.21.0.0/24{28,32}, # dn42 Anycast + 172.22.0.0/24{28,32}, # dn42 Anycast + 172.23.0.0/24{28,32}, # dn42 Anycast + 172.31.0.0/16+, # ChaosVPN + 10.100.0.0/14+, # ChaosVPN + 10.127.0.0/16{16,32}, # neonetwork + 10.0.0.0/8{15,24} # Freifunk.net + ]; +} \ No newline at end of file diff --git a/templates/bird/local6.conf.j2 b/templates/bird/local6.conf.j2 new file mode 100644 index 0000000..352a2aa --- /dev/null +++ b/templates/bird/local6.conf.j2 @@ -0,0 +1,15 @@ +# should be a unique identifier, use same id as for ipv4 +router id {{ dn42_local_v6 }}; + +define OWNAS = {{ dn42_local_as }}; +define OWNIP = {{ dn42_local_v6 }}; + +function is_self_net() { + return net ~ [{{ dn42_local_subnet_v6 }}+]; +} + +function is_valid_network() { + return net ~ [ + fd00::/8{44,64} # ULA address space as per RFC 4193 + ]; +} \ No newline at end of file diff --git a/templates/bird/peer4.j2 b/templates/bird/peer4.j2 new file mode 100644 index 0000000..de90f6e --- /dev/null +++ b/templates/bird/peer4.j2 @@ -0,0 +1,3 @@ +protocol bgp {{ peer.name }} from dnpeers { + neighbor {{ peer.v4 }} as {{ peer.as }}; +}; \ No newline at end of file diff --git a/templates/bird/peer6.j2 b/templates/bird/peer6.j2 new file mode 100644 index 0000000..3247ae8 --- /dev/null +++ b/templates/bird/peer6.j2 @@ -0,0 +1,8 @@ +protocol bgp {{ peer.name }} from dnpeers { + {% if not peer.linklocal|default(False) %} + neighbor {{ peer.v6 }} as {{ peer.as }}; + {% else %} + # if you use link-local ipv6 addresses for peering using the following + neighbor {{ peer.v6 }} % '{{ peer.if }}' as {{ peer.as }}; + {% endif %} +}; \ No newline at end of file diff --git a/templates/bird2/bird.conf.j2 b/templates/bird2/bird.conf.j2 new file mode 100644 index 0000000..cc4ee11 --- /dev/null +++ b/templates/bird2/bird.conf.j2 @@ -0,0 +1,155 @@ +################################################ +# Variable header # +################################################ + +define OWNAS = {{ dn42_local_as }}; +define OWNIP = {{ dn42_local_v4 }}; +define OWNIPv6 = {{ dn42_local_v6 }}; +define OWNNET = {{ dn42_local_subnet_v4 }}; +define OWNNETv6 = {{ dn42_local_subnet_v6 }}; +define OWNNETSET = [{{ dn42_local_subnet_v4 }}+]; +define OWNNETSETv6 = [{{ dn42_local_subnet_v6 }}+]; + +################################################ +# Header end # +################################################ + +router id OWNIP; + +protocol device { + scan time 10; +} + +/* + * Utility functions + */ + +function is_self_net() { + return net ~ OWNNETSET; +} + +function is_self_net_v6() { + return net ~ OWNNETSETv6; +} + +function is_valid_network() { + return net ~ [ + 172.20.0.0/14{21,29}, # dn42 + 172.20.0.0/24{28,32}, # dn42 Anycast + 172.21.0.0/24{28,32}, # dn42 Anycast + 172.22.0.0/24{28,32}, # dn42 Anycast + 172.23.0.0/24{28,32}, # dn42 Anycast + 172.31.0.0/16+, # ChaosVPN + 10.100.0.0/14+, # ChaosVPN + 10.127.0.0/16{16,32}, # neonetwork + 10.0.0.0/8{15,24} # Freifunk.net + ]; +} + +{% if dn42_enable_roa %} +roa4 table dn42_roa; +roa6 table dn42_roa_v6; + +protocol static { + roa4 { table dn42_roa; }; + include "{{ dn42_roa_v4_location }}"; +}; + +protocol static { + roa6 { table dn42_roa_v6; }; + include "{{ dn42_roa_v6_location }}"; +}; +{% endif %} + +function is_valid_network_v6() { + return net ~ [ + fd00::/8{44,64} # ULA address space as per RFC 4193 + ]; +} + +protocol kernel { + scan time 20; + + ipv6 { + import none; + export filter { + if source = RTS_STATIC then reject; + krt_prefsrc = OWNIPv6; + accept; + }; + }; +}; + +protocol kernel { + scan time 20; + + ipv4 { + import none; + export filter { + if source = RTS_STATIC then reject; + krt_prefsrc = OWNIP; + accept; + }; + }; +} + +protocol static { + route OWNNET reject; + + ipv4 { + import all; + export none; + }; +} + +protocol static { + route OWNNETv6 reject; + + ipv6 { + import all; + export none; + }; +} + +template bgp dnpeers { + local as OWNAS; + path metric 1; + + ipv4 { + import filter { + if is_valid_network() && !is_self_net() then { + {% if dn42_enable_roa %} + if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then { + print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; + reject; + } else accept; + {% else %} + accept; + {% endif %} + } else reject; + }; + + export filter { if is_valid_network() then accept; else reject; }; + import limit 1000 action block; + }; + + ipv6 { + import filter { + if is_valid_network_v6() && !is_self_net_v6() then { + {% if dn42_enable_roa %} + if (roa_check(dn42_roa_v6, net, bgp_path.last) != ROA_VALID) then { + print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; + reject; + } else accept; + {% else %} + accept; + {% endif %} + } else reject; + }; + export filter { if is_valid_network_v6() then accept; else reject; }; + import limit 1000 action block; + }; +} + + +include "/etc/bird/peers/*"; \ No newline at end of file diff --git a/templates/bird2/peer.conf.j2 b/templates/bird2/peer.conf.j2 new file mode 100644 index 0000000..c75410e --- /dev/null +++ b/templates/bird2/peer.conf.j2 @@ -0,0 +1,12 @@ +{% if peer.v4 is defined%} +protocol bgp {{ peer.name }} from dnpeers { + neighbor {{ peer.v4 }} as {{ peer.as }}; +}; +{% endif %} + +{% if peer.v6 is defined%} +protocol bgp {{ peer.name }}_v6 from dnpeers { + # if you use link-local ipv6 addresses for peering using the following + neighbor {{ peer.v6 }}%{{ peer.if.name | default('dn42_' + peer.name) }} as {{ peer.as }}; +}; +{% endif %} \ No newline at end of file diff --git a/templates/wg-quick.j2 b/templates/wg-quick.j2 new file mode 100644 index 0000000..e9e5a0e --- /dev/null +++ b/templates/wg-quick.j2 @@ -0,0 +1,15 @@ +[Interface] +PrivateKey = {{ peer.wg.privkey | default(dn42_wg_private_key) }} +Address = {{ peer.if.v4 | default(dn42_local_v4) }}/32, {{ peer.if.v6 | default(dn42_local_v6) }}/128 +PostUp = {% if peer.v4 is defined %}/sbin/ip addr del dev {{ peer.if.name | default("dn42_" + peer.name) }} {{ peer.if.v4 | default(dn42_local_v4) }}/32 && /sbin/ip addr add dev {{ peer.if.name | default("dn42_" + peer.name) }} {{ peer.if.v4 | default(dn42_local_v4) }}/32 peer {{ peer.v4 }}/32 &&/ {% endif %}{% if peer.v6 is defined %}/sbin/ip addr del dev {{ peer.if.name | default("dn42_" + peer.name) }} {{ peer.if.v6 | default(dn42_local_v6) }}/128 && /sbin/ip addr add dev {{ peer.if.name | default("dn42_" + peer.name) }} {{ peer.if.v6 | default(dn42_local_v6) }}/128 peer {{ peer.v6 }}/128{% endif %} + +Table = off +ListenPort = {{ peer.wg.port }} + +[Peer] +Endpoint = {{ peer.wg.endpoint }} +PublicKey = {{ peer.wg.pubkey }} +AllowedIPs = 172.16.0.0/12 +AllowedIPs = 10.0.0.0/8 +AllowedIPs = fd00::/8 +AllowedIPs = fe80::/10 \ No newline at end of file