initial commit

This commit is contained in:
boris committed 2026-07-08 16:38:55 +01:00
commit 3586c571f3
24 files changed
+887

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
---
router_external_interface: eth0
router_sysctl:
net.ipv6.conf.all.forwarding: 1
net.ipv6.conf.default.forwarding: 1
net.ipv4.ip_forward: 1
wireguard_enable: true
wireguard_package_name: wireguard-tools
wireguard_interfaces: []
bird_enable: true
bird_track: bird2
bird_package_name: "{{ bird_track }}"
bird_package_version: ""
bird_cznic_repo_enabled: true
bird_cznic_repo_base_url: https://pkg.labs.nic.cz
bird_cznic_repo_distribution: "{{ ansible_facts.distribution_release | default('trixie') }}"
bird_cznic_key_url: https://pkg.labs.nic.cz/gpg
bird_config_path: /etc/bird/bird.conf
bird_envvars_path: /etc/bird/envvars
bird_service_name: bird
bird_router_id: 127.0.0.1
bird_local_as: 64512
bird_run_user: bird
bird_run_group: bird
bird_log_file: /var/log/bird/bird.log
bird_sbehop_gateway: ""
bird_static_routes_v6: []
bird_static_routes_v4: []
bird_reject_prefixes_v6: []
bird_bgp_sessions: []
+20
View File
@@ -0,0 +1,20 @@
---
- name: Reload sysctl
ansible.builtin.command: sysctl --system
listen: reload_sysctl
changed_when: false
- name: Restart WireGuard interfaces
ansible.builtin.systemd:
name: "wg-quick@{{ item.name }}"
state: restarted
loop: "{{ wireguard_interfaces }}"
listen: restart_wireguard_interfaces
when: wireguard_enable
- name: Restart BIRD
ansible.builtin.systemd:
name: "{{ bird_service_name }}"
state: restarted
listen: restart_bird
when: bird_enable
+131
View File
@@ -0,0 +1,131 @@
- name: Validate BIRD package track
ansible.builtin.assert:
that:
- bird_track in ['bird2', 'bird3']
fail_msg: "bird_track must be either bird2 or bird3."
when: bird_enable
- name: Install repository prerequisites
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- python3-debian
state: present
update_cache: true
when: bird_enable and bird_cznic_repo_enabled
- name: Configure CZ.NIC Labs BIRD apt repository
ansible.builtin.deb822_repository:
name: "cznic-labs-{{ bird_track }}"
types: deb
uris: "{{ bird_cznic_repo_base_url }}/{{ bird_track }}"
suites: "{{ bird_cznic_repo_distribution }}"
components: main
signed_by: "{{ bird_cznic_key_url }}"
state: present
when: bird_enable and bird_cznic_repo_enabled
- name: Update package cache after configuring CZ.NIC repository
ansible.builtin.apt:
update_cache: true
when: bird_enable and bird_cznic_repo_enabled
- name: Install WireGuard tools
ansible.builtin.apt:
name: "{{ wireguard_package_name }}"
state: present
when: wireguard_enable
- name: Install selected BIRD package
ansible.builtin.apt:
name: "{{ bird_package_name if bird_package_version | length == 0 else bird_package_name + '=' + bird_package_version }}"
state: present
when: bird_enable
- name: Configure router sysctl values
ansible.builtin.template:
src: router-sysctl.conf.j2
dest: /etc/sysctl.d/99-router.conf
owner: root
group: root
mode: "0644"
notify: reload_sysctl
- name: Ensure WireGuard configuration directory exists
ansible.builtin.file:
path: /etc/wireguard
state: directory
owner: root
group: root
mode: "0700"
when: wireguard_enable
- name: Validate WireGuard private keys are provided
ansible.builtin.assert:
that:
- >-
(item.private_key is defined and item.private_key | length > 0)
or
(item.private_key_env is defined and lookup('ansible.builtin.env', item.private_key_env) | length > 0)
fail_msg: "Private key for {{ item.name }} is missing. Set it via Ansible Vault or the matching environment lookup."
loop: "{{ wireguard_interfaces }}"
no_log: true
when: wireguard_enable
- name: Configure WireGuard interfaces
ansible.builtin.template:
src: wg.conf.j2
dest: "/etc/wireguard/{{ item.name }}.conf"
owner: root
group: root
mode: "0600"
loop: "{{ wireguard_interfaces }}"
no_log: true
notify: restart_wireguard_interfaces
when: wireguard_enable
- name: Enable WireGuard interfaces
ansible.builtin.systemd:
name: "wg-quick@{{ item.name }}"
enabled: true
state: started
loop: "{{ wireguard_interfaces }}"
when: wireguard_enable
- name: Ensure BIRD log directory exists
ansible.builtin.file:
path: "{{ bird_log_file | dirname }}"
state: directory
owner: "{{ bird_run_user }}"
group: "{{ bird_run_group }}"
mode: "0755"
when: bird_enable
- name: Configure BIRD environment
ansible.builtin.template:
src: bird-envvars.j2
dest: "{{ bird_envvars_path }}"
owner: root
group: root
mode: "0644"
notify: restart_bird
when: bird_enable
- name: Configure BIRD
ansible.builtin.template:
src: bird.conf.j2
dest: "{{ bird_config_path }}"
owner: root
group: "{{ bird_run_group }}"
mode: "0640"
validate: "bird -p -c %s"
notify: restart_bird
when: bird_enable
- name: Enable BIRD service
ansible.builtin.systemd:
name: "{{ bird_service_name }}"
enabled: true
state: started
when: bird_enable
@@ -0,0 +1,3 @@
BIRD_RUN_USER={{ bird_run_user }}
BIRD_RUN_GROUP={{ bird_run_group }}
#BIRD_ARGS=
+109
View File
@@ -0,0 +1,109 @@
router id {{ bird_router_id }};
log syslog all;
log "{{ bird_log_file }}" { debug, trace, remote, warning, error, auth, fatal, bug };
protocol device { scan time 5; }
protocol direct { ipv6; }
protocol kernel {
ipv6 { export none; };
scan time 15;
}
protocol kernel {
ipv4;
scan time 15;
}
protocol static announcev6 {
ipv6;
description "Static v6 Routes";
{% for route in bird_static_routes_v6 %}
{% if route.blackhole | default(false) %}
route {{ route.prefix }} blackhole;
{% else %}
route {{ route.prefix }} via {{ route.via }};
{% endif %}
{% endfor %}
}
protocol static announcev4 {
ipv4;
description "Static v4 Routes";
{% for route in bird_static_routes_v4 %}
{% if route.blackhole | default(false) %}
route {{ route.prefix }} blackhole;
{% else %}
route {{ route.prefix }} via {{ route.via }};
{% endif %}
{% endfor %}
}
protocol static {
ipv6;
}
protocol static {
ipv4;
}
filter rt_importv4 {
accept;
}
filter rt_importv6 {
accept;
}
filter rt_nothanks {
{% for prefix in bird_reject_prefixes_v6 %}
if (net ~ [ {{ prefix }} ]) then reject;
{% endfor %}
accept;
}
filter sbehop {
gw = {{ bird_sbehop_gateway }};
accept;
}
filter brt_importv6 {
reject;
}
filter rt_export {
if (proto = "announcev4") then accept;
if (proto = "announcev6") then accept;
reject;
}
{% for session in bird_bgp_sessions %}
protocol bgp {{ session.name }} {
local {{ session.local_address }} as {{ session.local_as }};
neighbor {{ session.neighbor_address }} as {{ session.neighbor_as }};
multihop {{ session.multihop | default(255) }};
{{ session.address_family | default('ipv6') }} {
{% if session.import_limit is defined %}
import limit {{ session.import_limit }};
{% endif %}
{% if session.export_limit is defined %}
export limit {{ session.export_limit }};
{% endif %}
{% if session.import_filter is defined %}
import filter {{ session.import_filter }};
{% else %}
import none;
{% endif %}
{% if session.export_filter is defined %}
export filter {{ session.export_filter }};
{% else %}
export none;
{% endif %}
{% if session.graceful_restart | default(false) %}
graceful restart on;
{% endif %}
};
}
{% endfor %}
@@ -0,0 +1,3 @@
{% for key, value in router_sysctl | dictsort %}
{{ key }} = {{ value }}
{% endfor %}
+39
View File
@@ -0,0 +1,39 @@
# {{ item.description | default(item.name) }}
[Interface]
{% if item.private_key is defined %}
PrivateKey = {{ item.private_key }}
{% else %}
PrivateKey = {{ lookup('ansible.builtin.env', item.private_key_env) }}
{% endif %}
Address = {{ item.address | join(',') }}
{% if item.listen_port is defined %}
ListenPort = {{ item.listen_port }}
{% endif %}
{% if item.mtu is defined %}
MTU = {{ item.mtu }}
{% endif %}
{% for command in item.pre_up | default([]) %}
PreUp = {{ command }}
{% endfor %}
{% for command in item.post_up | default([]) %}
PostUp = {{ command }}
{% endfor %}
{% for command in item.pre_down | default([]) %}
PreDown = {{ command }}
{% endfor %}
{% for command in item.post_down | default([]) %}
PostDown = {{ command }}
{% endfor %}
{% for peer in item.peers | default([]) %}
[Peer]
PublicKey = {{ peer.public_key }}
AllowedIPs = {{ peer.allowed_ips | join(',') }}
{% if peer.endpoint is defined %}
Endpoint = {{ peer.endpoint }}
{% endif %}
{% if peer.persistent_keepalive is defined %}
PersistentKeepalive = {{ peer.persistent_keepalive }}
{% endif %}
{% endfor %}
@@ -0,0 +1,10 @@
---
debian_upgrade_source_major_version: "12"
debian_upgrade_target_major_version: "13"
debian_upgrade_target_release: trixie
debian_upgrade_reboot_timeout: 1800
debian_upgrade_sources_list_path: /etc/apt/sources.list
debian_upgrade_sources_backup_path: /etc/apt/sources.list.bookworm.bak
debian_upgrade_deb822_sources_path: /etc/apt/sources.list.d/debian.sources
debian_upgrade_deb822_sources_backup_path: /etc/apt/sources.list.d/debian.sources.bookworm.bak
@@ -0,0 +1,6 @@
---
- name: Reboot after Debian dist upgrade
ansible.builtin.reboot:
msg: Rebooting after Debian dist upgrade
reboot_timeout: "{{ debian_upgrade_reboot_timeout }}"
listen: reboot_after_debian_dist_upgrade
+118
View File
@@ -0,0 +1,118 @@
---
- name: Validate current host is Debian
ansible.builtin.assert:
that:
- ansible_facts.distribution == "Debian"
fail_msg: "This role only supports Debian hosts."
- name: Validate expected source Debian major version
ansible.builtin.assert:
that:
- ansible_facts.distribution_major_version == debian_upgrade_source_major_version
fail_msg: >-
Expected Debian {{ debian_upgrade_source_major_version }} before upgrade,
found Debian {{ ansible_facts.distribution_major_version }}.
success_msg: "Host is Debian {{ debian_upgrade_source_major_version }} and ready for upgrade."
when: ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- name: Update package cache on current release
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
when: ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- name: Upgrade installed packages before release change
ansible.builtin.apt:
upgrade: dist
autoremove: true
autoclean: true
when: ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- name: Check for classic apt sources list
ansible.builtin.stat:
path: "{{ debian_upgrade_sources_list_path }}"
register: debian_upgrade_sources_list_stat
when: ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- name: Check for deb822 apt sources
ansible.builtin.stat:
path: "{{ debian_upgrade_deb822_sources_path }}"
register: debian_upgrade_deb822_sources_stat
when: ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- name: Back up current apt sources
ansible.builtin.copy:
src: "{{ debian_upgrade_sources_list_path }}"
dest: "{{ debian_upgrade_sources_backup_path }}"
remote_src: true
mode: "0644"
force: false
when:
- ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- debian_upgrade_sources_list_stat.stat.exists
- name: Back up current deb822 apt sources
ansible.builtin.copy:
src: "{{ debian_upgrade_deb822_sources_path }}"
dest: "{{ debian_upgrade_deb822_sources_backup_path }}"
remote_src: true
mode: "0644"
force: false
when:
- ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- debian_upgrade_deb822_sources_stat.stat.exists
- name: Configure Debian target release classic apt sources
ansible.builtin.template:
src: sources.list.j2
dest: "{{ debian_upgrade_sources_list_path }}"
owner: root
group: root
mode: "0644"
when:
- ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- debian_upgrade_sources_list_stat.stat.exists or not debian_upgrade_deb822_sources_stat.stat.exists
- name: Configure Debian target release deb822 apt sources
ansible.builtin.template:
src: debian.sources.j2
dest: "{{ debian_upgrade_deb822_sources_path }}"
owner: root
group: root
mode: "0644"
when:
- ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- debian_upgrade_deb822_sources_stat.stat.exists
- name: Update package cache for target release
ansible.builtin.apt:
update_cache: true
when: ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- name: Perform Debian dist upgrade
ansible.builtin.apt:
upgrade: dist
autoremove: true
autoclean: true
dpkg_options: force-confdef,force-confold
environment:
DEBIAN_FRONTEND: noninteractive
APT_LISTCHANGES_FRONTEND: none
notify: reboot_after_debian_dist_upgrade
when: ansible_facts.distribution_major_version != debian_upgrade_target_major_version
- name: Flush reboot handler before verification
ansible.builtin.meta: flush_handlers
- name: Gather facts after possible upgrade
ansible.builtin.setup:
- name: Verify Debian target version
ansible.builtin.assert:
that:
- ansible_facts.distribution == "Debian"
- ansible_facts.distribution_major_version == debian_upgrade_target_major_version
fail_msg: >-
Debian upgrade did not finish as expected. Found
{{ ansible_facts.distribution }} {{ ansible_facts.distribution_major_version }}.
success_msg: "Host is running Debian {{ debian_upgrade_target_major_version }}."
@@ -0,0 +1,11 @@
Types: deb
URIs: http://deb.debian.org/debian
Suites: {{ debian_upgrade_target_release }} {{ debian_upgrade_target_release }}-updates
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security
Suites: {{ debian_upgrade_target_release }}-security
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
@@ -0,0 +1,3 @@
deb http://deb.debian.org/debian {{ debian_upgrade_target_release }} main contrib non-free non-free-firmware
deb http://deb.debian.org/debian {{ debian_upgrade_target_release }}-updates main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security {{ debian_upgrade_target_release }}-security main contrib non-free non-free-firmware