ADD import roles
This commit is contained in:
parent
4798dde38c
commit
e6027e9078
176
.gitignore
vendored
176
.gitignore
vendored
@ -45,3 +45,179 @@ tags
|
|||||||
.ionide
|
.ionide
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/ansible,vim,visualstudiocode
|
# End of https://www.toptal.com/developers/gitignore/api/ansible,vim,visualstudiocode
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/python
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
||||||
|
|
||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
### Python Patch ###
|
||||||
|
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||||
|
poetry.toml
|
||||||
|
|
||||||
|
# ruff
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# LSP config files
|
||||||
|
pyrightconfig.json
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/python
|
||||||
|
|||||||
3
roles/bird/defaults/main.yml
Normal file
3
roles/bird/defaults/main.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
bird_enabled: false
|
||||||
|
bird_bgp_advertised_networks: []
|
||||||
|
bird_bgp_neighbors: []
|
||||||
5
roles/bird/handlers/main.yml
Normal file
5
roles/bird/handlers/main.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
- name: Reconfigure bird
|
||||||
|
shell: /usr/sbin/birdc configure
|
||||||
|
- name: Reload sysctl
|
||||||
|
ansible.builtin.command: sysctl --system
|
||||||
62
roles/bird/tasks/main.yml
Normal file
62
roles/bird/tasks/main.yml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
- name: Set OS dependent variables
|
||||||
|
include_vars: "{{ lookup('first_found', params) }}"
|
||||||
|
vars:
|
||||||
|
params:
|
||||||
|
files:
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}.yml"
|
||||||
|
- "{{ ansible_os_family | lower }}.yml"
|
||||||
|
- "{{ ansible_system | lower }}.yml"
|
||||||
|
paths:
|
||||||
|
- "{{ role_path }}/vars"
|
||||||
|
ignore_errors: True
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: OS is supported
|
||||||
|
assert:
|
||||||
|
that: __os_supported
|
||||||
|
quiet: True
|
||||||
|
vars:
|
||||||
|
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: Install required packages
|
||||||
|
package:
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: present
|
||||||
|
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||||
|
tags:
|
||||||
|
- install
|
||||||
|
|
||||||
|
- name: Enable routing in sysctl
|
||||||
|
template:
|
||||||
|
src: sysctl.conf.j2
|
||||||
|
dest: /etc/sysctl.d/90-routing.conf
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
notify: Reload sysctl
|
||||||
|
|
||||||
|
- name: Install bird configuration
|
||||||
|
template:
|
||||||
|
src: bird.conf.j2
|
||||||
|
dest: "/etc/bird/bird.conf"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0755
|
||||||
|
tags:
|
||||||
|
- configure
|
||||||
|
no_log: false
|
||||||
|
when: bird_enabled
|
||||||
|
notify: Reconfigure bird
|
||||||
|
|
||||||
|
- name: Enable bird service
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: bird.service
|
||||||
|
state: "{{ 'started' if bird_enabled else 'stopped' }}"
|
||||||
|
enabled: "{{ bird_enabled }}"
|
||||||
|
when: not ansible_check_mode
|
||||||
261
roles/bird/templates/bird.conf.j2
Normal file
261
roles/bird/templates/bird.conf.j2
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
|
||||||
|
router id {{ bird_router_id }};
|
||||||
|
|
||||||
|
|
||||||
|
protocol device {
|
||||||
|
scan time 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol kernel {
|
||||||
|
scan time 5;
|
||||||
|
|
||||||
|
ipv6 {
|
||||||
|
import none;
|
||||||
|
export filter {
|
||||||
|
{% if bird_prefsrc is defined %}
|
||||||
|
krt_prefsrc = {{ bird_prefsrc }};
|
||||||
|
{% endif %}
|
||||||
|
if source = RTS_BGP then accept;
|
||||||
|
if source = RTS_STATIC then accept;
|
||||||
|
if source = RTS_DEVICE then reject;
|
||||||
|
accept;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol kernel {
|
||||||
|
scan time 5;
|
||||||
|
|
||||||
|
ipv4 {
|
||||||
|
import none;
|
||||||
|
export filter {
|
||||||
|
{% if bird_prefsrc_v4 is defined %}
|
||||||
|
krt_prefsrc = {{ bird_prefsrc_v4 }};
|
||||||
|
{% endif %}
|
||||||
|
if source = RTS_BGP then accept;
|
||||||
|
if source = RTS_STATIC then accept;
|
||||||
|
if source = RTS_DEVICE then reject;
|
||||||
|
accept;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol static {
|
||||||
|
{% for net in bird_bgp_advertised_networks %}
|
||||||
|
route {{ net }} reject;
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
ipv6 {
|
||||||
|
import all;
|
||||||
|
export none;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol static {
|
||||||
|
{% for net in bird_bgp_advertised_networks_v4 | default([]) %}
|
||||||
|
route {{ net }} reject;
|
||||||
|
{% endfor %}
|
||||||
|
ipv4 {
|
||||||
|
import all;
|
||||||
|
export none;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
{%if bird_ospf_interfaces | default([]) | length > 0%}
|
||||||
|
############################################
|
||||||
|
# _|_| _|_|_| _|_|_| _|_|_|_| #
|
||||||
|
# _| _| _| _| _| _| #
|
||||||
|
# _| _| _|_| _|_|_| _|_|_| #
|
||||||
|
# _| _| _| _| _| #
|
||||||
|
# _|_| _|_|_| _| _| #
|
||||||
|
############################################
|
||||||
|
|
||||||
|
# Filter to export all direct and bgp routes into OSPF
|
||||||
|
filter export_all_OSPF {
|
||||||
|
if ( source = RTS_DEVICE ) then accept;
|
||||||
|
if ( source = RTS_STATIC_DEVICE ) then accept;
|
||||||
|
if ( source = RTS_STATIC ) then accept;
|
||||||
|
if ( source = RTS_BGP ) then accept;
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol ospf v3 ospf6 {
|
||||||
|
ipv6 {
|
||||||
|
{% if bird_ospf_export_all | default(false) %}
|
||||||
|
export filter export_all_OSPF;
|
||||||
|
{% endif %}
|
||||||
|
};
|
||||||
|
area 0 {
|
||||||
|
{%for iface in bird_ospf_interfaces%}
|
||||||
|
interface "{{ iface.name }}" {
|
||||||
|
{%if iface.stub | default(false)%}
|
||||||
|
stub;
|
||||||
|
{%endif%}
|
||||||
|
};
|
||||||
|
{%endfor%}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{%endif%}
|
||||||
|
|
||||||
|
|
||||||
|
{% if bird_bgp_neighbors | default([]) | length > 0%}
|
||||||
|
###################################
|
||||||
|
# _|_|_| _|_|_| _|_|_| #
|
||||||
|
# _| _| _| _| _| #
|
||||||
|
# _|_|_| _| _|_| _|_|_| #
|
||||||
|
# _| _| _| _| _| #
|
||||||
|
# _|_|_| _|_|_| _| #
|
||||||
|
###################################
|
||||||
|
define OWNNETSET = [{{ bird_bgp_advertised_networks | join(', ') }}];
|
||||||
|
define OWNNETSET_v4 = [{{ bird_bgp_advertised_networks_v4 | default([]) | join(', ') }}];
|
||||||
|
define OWNASN = {{ bird_bgp_local_asn }};
|
||||||
|
|
||||||
|
function is_self_net() {
|
||||||
|
if net ~ OWNNETSET then return true;
|
||||||
|
if net ~ OWNNETSET_v4 then return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function has_private_asn() {
|
||||||
|
# Check if the AS path contains any private ASN
|
||||||
|
if (bgp_path ~ [64512..65534]) || (bgp_path ~ [4200000000..4294967294]) then {
|
||||||
|
print "Rejecting route with private ASN in AS path: ", bgp_path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_reserved_net() {
|
||||||
|
# ULA
|
||||||
|
if net ~ fc00::/7 then return true;
|
||||||
|
# Link-local
|
||||||
|
if net ~ fe80::/10 then return true;
|
||||||
|
# Documentation
|
||||||
|
if net ~ 2001:db8::/32 then return true;
|
||||||
|
# Discard
|
||||||
|
if net ~ 100::/64 then return true;
|
||||||
|
# Orchid
|
||||||
|
if net ~ 2001:20::/28 then return true;
|
||||||
|
|
||||||
|
# IPv4
|
||||||
|
if net ~ 0.0.0.0/8 then return true;
|
||||||
|
if net ~ 10.0.0.0/8 then return true;
|
||||||
|
if net ~ 100.64.0.0/10 then return true;
|
||||||
|
if net ~ 127.0.0.0/8 then return true;
|
||||||
|
if net ~ 169.254.0.0/16 then return true;
|
||||||
|
if net ~ 172.16.0.0/12 then return true;
|
||||||
|
if net ~ 192.0.0.0/24 then return true;
|
||||||
|
if net ~ 192.0.2.0/24 then return true;
|
||||||
|
if net ~ 192.88.99.0/24 then return true;
|
||||||
|
if net ~ 192.168.0.0/16 then return true;
|
||||||
|
if net ~ 198.18.0.0/15 then return true;
|
||||||
|
if net ~ 198.51.100.0/24 then return true;
|
||||||
|
if net ~ 203.0.113.0/24 then return true;
|
||||||
|
if net ~ 224.0.0.0/4 then return true;
|
||||||
|
if net ~ 233.252.0.0/24 then return true;
|
||||||
|
if net ~ 240.0.0.0/4 then return true;
|
||||||
|
if net ~ 255.255.255.255/32 then return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{%for peer in bird_bgp_neighbors%}
|
||||||
|
# {{ peer.name }}
|
||||||
|
{% set peer_name = peer.name | lower | regex_replace('[\s\.\-\$%&/()=]', '_') %}
|
||||||
|
|
||||||
|
filter {{ peer_name }}_BGP_IMPORT {
|
||||||
|
# don't re-import networks announced by ourself
|
||||||
|
if is_self_net() then reject;
|
||||||
|
{% if not (peer.allow_reserved_networks | default(bird_bgp_allow_reserved_networks | default(false))) %}
|
||||||
|
# don't import reserved networks
|
||||||
|
if is_reserved_net() then reject;
|
||||||
|
{% endif %}
|
||||||
|
# never accept routes more specific than /{{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }}
|
||||||
|
if net.len > {{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }} then reject;
|
||||||
|
{% if not (peer.allow_private_asn | default(bird_bgp_allow_private_asn | default(false))) %}
|
||||||
|
# prevent importing paths with private ASNs
|
||||||
|
if has_private_asn() then reject;
|
||||||
|
{% endif %}
|
||||||
|
{% if (peer.max_as_path_length is defined) or (bird_bgp_max_as_path_length is defined) %}
|
||||||
|
if bgp_path.len > {{ peer.max_as_path_length | default(bird_bgp_max_as_path_length) }} then reject;
|
||||||
|
{% endif %}
|
||||||
|
{% if (peer.restrict_prefixes | default([]) | length > 0) %}
|
||||||
|
if net !~ [{{ peer.restrict_prefixes | join(', ') }}] then reject;
|
||||||
|
{% endif %}
|
||||||
|
accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
# export rules for {{ peer_name }}
|
||||||
|
filter {{ peer_name }}_ALLOWED_BGP {
|
||||||
|
{% for n in range(peer.as_prepend | default(1)) %}
|
||||||
|
bgp_path.prepend({{ peer.local_asn | default('OWNASN') }});
|
||||||
|
{% endfor %}
|
||||||
|
{% if not (peer.allow_reserved_networks | default(bird_bgp_allow_reserved_networks | default(false))) %}
|
||||||
|
# don't export reserved networks
|
||||||
|
if is_reserved_net() then reject;
|
||||||
|
{% endif %}
|
||||||
|
# never send routes more specific than /{{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }}
|
||||||
|
if net.len > {{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }} then reject;
|
||||||
|
{% if not (peer.allow_private_asn | default(bird_bgp_allow_private_asn | default(false))) %}
|
||||||
|
# prevent exposing paths with private ASNs
|
||||||
|
if has_private_asn() then reject;
|
||||||
|
{% endif %}
|
||||||
|
if is_self_net() then accept;
|
||||||
|
{% if (peer.redistribute_learned | default(false)) %}
|
||||||
|
# redistribute all other routes learned from BGP
|
||||||
|
if source = RTS_BGP then {
|
||||||
|
{% if not (peer.redistribute_reserved | default(false)) %}
|
||||||
|
# don't redistribute reserved networks
|
||||||
|
if is_reserved_net() then reject;
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% for n in range(peer.redistribute_prepend | default(0)) %}
|
||||||
|
bgp_path.prepend({{ peer.local_asn | default('OWNASN') }});
|
||||||
|
{% endfor %}
|
||||||
|
accept;
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
protocol bgp {{ peer_name }} {
|
||||||
|
local {{ peer.local_ip }} as {{ peer.local_asn | default('OWNASN') }};
|
||||||
|
neighbor {{ peer.neighbor_ip }} as {{ peer.neighbor_asn }};
|
||||||
|
description "{{ peer.description | default(peer.name) }}";
|
||||||
|
{% if peer.password is defined %}
|
||||||
|
password "{{ peer.password }}";
|
||||||
|
{% endif %}
|
||||||
|
{% if peer.metric is defined %}
|
||||||
|
path metric {{ peer.metric }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if peer.ipv4 | default(false) == true %}
|
||||||
|
ipv4 {
|
||||||
|
import filter {{ peer_name }}_BGP_IMPORT;
|
||||||
|
export filter {{ peer_name }}_ALLOWED_BGP;
|
||||||
|
{% if peer.import_limit | default(1000) != false %}
|
||||||
|
import limit {{ peer.import_limit | default(1000) }} action block;
|
||||||
|
{% endif %}
|
||||||
|
next hop {{ ('address ' + peer.next_hop) if peer.next_hop is defined else 'self' }};
|
||||||
|
};
|
||||||
|
{% else %}
|
||||||
|
ipv6 {
|
||||||
|
import filter {{ peer_name }}_BGP_IMPORT;
|
||||||
|
export filter {{ peer_name }}_ALLOWED_BGP;
|
||||||
|
{% if peer.import_limit | default(1000) != false %}
|
||||||
|
import limit {{ peer.import_limit | default(1000) }} action block;
|
||||||
|
{% endif %}
|
||||||
|
next hop {{ ('address ' + peer.next_hop) if peer.next_hop is defined else 'self' }};
|
||||||
|
};
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{%endfor%}
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: roa with routinator...
|
||||||
|
|
||||||
|
# roa4 table dn42_roa;
|
||||||
|
# protocol static {
|
||||||
|
# roa4 { table dn42_roa; };
|
||||||
|
# include "/etc/bird/roa_dn42.conf";
|
||||||
|
# };
|
||||||
|
{% endif %}
|
||||||
2
roles/bird/templates/sysctl.conf.j2
Normal file
2
roles/bird/templates/sysctl.conf.j2
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
net.ipv4.ip_forward = 1
|
||||||
|
net.ipv6.conf.all.forwarding = 1
|
||||||
3
roles/bird/vars/debian.yml
Normal file
3
roles/bird/vars/debian.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
bird_os_supported: true
|
||||||
|
bird_packages:
|
||||||
|
- bird2
|
||||||
1
roles/bird/vars/main.yml
Normal file
1
roles/bird/vars/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
bird_os_supported: false
|
||||||
2
roles/interfaces/defaults/main.yml
Normal file
2
roles/interfaces/defaults/main.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
net_interfaces: []
|
||||||
|
interfaces_manage_ifupdown: true
|
||||||
12
roles/interfaces/handlers/main.yml
Normal file
12
roles/interfaces/handlers/main.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
- name: Reload udev
|
||||||
|
# TODO! FIXME!
|
||||||
|
systemd_service:
|
||||||
|
name: systemd-udev-trigger.service
|
||||||
|
state: restarted
|
||||||
|
- name: Reload ifupdown
|
||||||
|
# TODO! FIXME! This drops some packages. Can this be done better?
|
||||||
|
systemd_service:
|
||||||
|
name: networking.service
|
||||||
|
state: restarted
|
||||||
|
throttle: 1
|
||||||
57
roles/interfaces/tasks/main.yml
Normal file
57
roles/interfaces/tasks/main.yml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
- name: Set OS dependent variables
|
||||||
|
include_vars: "{{ lookup('first_found', params) }}"
|
||||||
|
vars:
|
||||||
|
params:
|
||||||
|
files:
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}.yml"
|
||||||
|
- "{{ ansible_os_family | lower }}.yml"
|
||||||
|
- "{{ ansible_system | lower }}.yml"
|
||||||
|
paths:
|
||||||
|
- "{{ role_path }}/vars"
|
||||||
|
ignore_errors: True
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: OS is supported
|
||||||
|
assert:
|
||||||
|
that: __os_supported
|
||||||
|
quiet: True
|
||||||
|
vars:
|
||||||
|
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: Install required packages
|
||||||
|
package:
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: present
|
||||||
|
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||||
|
tags:
|
||||||
|
- install
|
||||||
|
|
||||||
|
- name: Install persistent if-names
|
||||||
|
template:
|
||||||
|
src: 60-persistent-net.rules.j2
|
||||||
|
dest: "/etc/udev/rules.d/60-persistent-net.rules"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
tags:
|
||||||
|
- configure
|
||||||
|
no_log: false
|
||||||
|
notify: Reload udev
|
||||||
|
|
||||||
|
- name: Install ifupdown config
|
||||||
|
template:
|
||||||
|
src: ifupdown2.j2
|
||||||
|
dest: "/etc/network/interfaces.d/ansible"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
tags:
|
||||||
|
- configure
|
||||||
|
no_log: false
|
||||||
|
notify: Reload ifupdown
|
||||||
5
roles/interfaces/templates/60-persistent-net.rules.j2
Normal file
5
roles/interfaces/templates/60-persistent-net.rules.j2
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% for iface in net_interfaces %}
|
||||||
|
{% if iface.name is defined and iface.mac is defined %}
|
||||||
|
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="{{ iface.mac }}", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="e*", NAME="{{ iface.name }}"
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
43
roles/interfaces/templates/ifupdown2.j2
Normal file
43
roles/interfaces/templates/ifupdown2.j2
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
|
||||||
|
{% macro iface_config(iface, type, inet) %}
|
||||||
|
iface {{ iface.name }} {{ type }} {{ inet.conf | default('manual') }}
|
||||||
|
{% if iface.vlan_raw_device is defined %}
|
||||||
|
vlan-raw-device {{ iface.vlan_raw_device }}
|
||||||
|
{% endif %}
|
||||||
|
{% if iface.dummy | default(false) %}
|
||||||
|
pre-up ip link show $IFACE > /dev/null || sudo ip link add $IFACE type dummy
|
||||||
|
post-down ip link show $IFACE > /dev/null && sudo ip link del $IFACE
|
||||||
|
{% endif %}
|
||||||
|
{% for addr in inet.addr %}
|
||||||
|
up ip addr add {{ addr }} dev $IFACE
|
||||||
|
pre-down ip addr del {{ addr }} dev $IFACE
|
||||||
|
{% endfor %}
|
||||||
|
{% if inet.gateway is defined and inet.gateway %}
|
||||||
|
up ip r add default via {{ inet.gateway }} dev $IFACE
|
||||||
|
pre-down ip r del default via {{ inet.gateway }} dev $IFACE
|
||||||
|
{% endif %}
|
||||||
|
{% if inet.routes is defined and inet.routes %}
|
||||||
|
{% for route in inet.routes %}
|
||||||
|
post-up ip r add {{ route.dst }} via {{ route.via }} dev $IFACE
|
||||||
|
pre-down ip r del {{ route.dst }} via {{ route.via }} dev $IFACE
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if inet.custom is defined and inet.custom %}
|
||||||
|
{{ inet.custom | default('') | indent(4) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% for iface in net_interfaces %}
|
||||||
|
{% if interfaces_manage_ifupdown or (iface.ifupdown | default(false)) %}
|
||||||
|
# {{ iface.name }}
|
||||||
|
allow-hotplug {{ iface.name }}
|
||||||
|
{% if iface.inet is defined and iface.inet %}
|
||||||
|
{{ iface_config(iface, 'inet', iface.inet) }}
|
||||||
|
{% endif %}
|
||||||
|
{% if iface.inet6 is defined and iface.inet6 %}
|
||||||
|
{{ iface_config(iface, 'inet6', iface.inet6) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
5
roles/interfaces/vars/debian.yml
Normal file
5
roles/interfaces/vars/debian.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
interfaces_os_supported: true
|
||||||
|
interfaces_packages:
|
||||||
|
- ifupdown
|
||||||
|
- udev
|
||||||
|
- vlan
|
||||||
1
roles/interfaces/vars/main.yml
Normal file
1
roles/interfaces/vars/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
interfaces_os_supported: false
|
||||||
1
roles/isc_dhcp/defaults/main.yml
Normal file
1
roles/isc_dhcp/defaults/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
isc_dhcp_enabled: false
|
||||||
6
roles/isc_dhcp/handlers/main.yml
Normal file
6
roles/isc_dhcp/handlers/main.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Restart isc_dhcp
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: isc-dhcp-server.service
|
||||||
|
state: restarted
|
||||||
|
enabled: true
|
||||||
64
roles/isc_dhcp/tasks/main.yml
Normal file
64
roles/isc_dhcp/tasks/main.yml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
- name: Set OS dependent variables
|
||||||
|
include_vars: "{{ lookup('first_found', params) }}"
|
||||||
|
vars:
|
||||||
|
params:
|
||||||
|
files:
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}.yml"
|
||||||
|
- "{{ ansible_os_family | lower }}.yml"
|
||||||
|
- "{{ ansible_system | lower }}.yml"
|
||||||
|
paths:
|
||||||
|
- "{{ role_path }}/vars"
|
||||||
|
ignore_errors: True
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: OS is supported
|
||||||
|
assert:
|
||||||
|
that: __os_supported
|
||||||
|
quiet: True
|
||||||
|
vars:
|
||||||
|
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: Install required packages
|
||||||
|
package:
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: present
|
||||||
|
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||||
|
when: radvd_enabled
|
||||||
|
tags:
|
||||||
|
- install
|
||||||
|
|
||||||
|
- name: Install dhcpd configuration
|
||||||
|
template:
|
||||||
|
src: dhcpd.conf.j2
|
||||||
|
dest: "/etc/dhcp/dhcpd.conf"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
tags:
|
||||||
|
- configure
|
||||||
|
no_log: false
|
||||||
|
notify: Restart isc_dhcp
|
||||||
|
|
||||||
|
- name: Install dhcpd configuration
|
||||||
|
template:
|
||||||
|
src: isc-dhcp-server.j2
|
||||||
|
dest: "/etc/default/isc-dhcp-server"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
tags:
|
||||||
|
- configure
|
||||||
|
no_log: false
|
||||||
|
notify: Restart isc_dhcp
|
||||||
|
|
||||||
|
- name: Enable dhcpd service
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: isc-dhcp-server.service
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
21
roles/isc_dhcp/templates/dhcpd.conf.j2
Normal file
21
roles/isc_dhcp/templates/dhcpd.conf.j2
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
|
||||||
|
ddns-update-style none;
|
||||||
|
authoritative;
|
||||||
|
|
||||||
|
{% for net in isc_dhcp_networks | default([]) %}
|
||||||
|
# {{ net.interface }}
|
||||||
|
subnet {{ net.subnet }} netmask {{ net.netmask }} {
|
||||||
|
interface {{ net.interface }};
|
||||||
|
option routers {{ net.router }};
|
||||||
|
option subnet-mask {{ net.netmask }};
|
||||||
|
{% if net.dns is defined %}
|
||||||
|
option domain-name-servers {{ net.dns }};
|
||||||
|
{% endif %}
|
||||||
|
option domain-name "{{ net.domain | default('hiwi.net.scc.kit.edu') }}";
|
||||||
|
range {{ net.range_start }} {{ net.range_end }};
|
||||||
|
default-lease-time 600;
|
||||||
|
max-lease-time 7200;
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
2
roles/isc_dhcp/templates/isc-dhcp-server.j2
Normal file
2
roles/isc_dhcp/templates/isc-dhcp-server.j2
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
INTERFACESv4="{{ isc_dhcp_networks | map(attribute='interface') | join(' ') }}"
|
||||||
3
roles/isc_dhcp/vars/debian.yml
Normal file
3
roles/isc_dhcp/vars/debian.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
isc_dhcp_os_supported: true
|
||||||
|
isc_dhcp_packages:
|
||||||
|
- isc-dhcp-server
|
||||||
1
roles/isc_dhcp/vars/main.yml
Normal file
1
roles/isc_dhcp/vars/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
isc_dhcp_os_supported: false
|
||||||
2
roles/keepalived/defaults/main.yml
Normal file
2
roles/keepalived/defaults/main.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
keepalived_enabled: false
|
||||||
|
keepalived_interfaces: []
|
||||||
9
roles/keepalived/handlers/main.yml
Normal file
9
roles/keepalived/handlers/main.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
- name: Restart keepalived
|
||||||
|
systemd_service:
|
||||||
|
name: keepalived.service
|
||||||
|
state: restarted
|
||||||
|
enabled: true
|
||||||
|
when: not ansible_check_mode
|
||||||
|
- name: Reload sysctl
|
||||||
|
ansible.builtin.command: sysctl --system
|
||||||
52
roles/keepalived/tasks/main.yml
Normal file
52
roles/keepalived/tasks/main.yml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
- name: Set OS dependent variables
|
||||||
|
include_vars: "{{ lookup('first_found', params) }}"
|
||||||
|
vars:
|
||||||
|
params:
|
||||||
|
files:
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}.yml"
|
||||||
|
- "{{ ansible_os_family | lower }}.yml"
|
||||||
|
- "{{ ansible_system | lower }}.yml"
|
||||||
|
paths:
|
||||||
|
- "{{ role_path }}/vars"
|
||||||
|
ignore_errors: True
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: OS is supported
|
||||||
|
assert:
|
||||||
|
that: __os_supported
|
||||||
|
quiet: True
|
||||||
|
vars:
|
||||||
|
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: Install required packages
|
||||||
|
package:
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: present
|
||||||
|
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||||
|
tags:
|
||||||
|
- install
|
||||||
|
|
||||||
|
- name: Install keepalived configuration
|
||||||
|
template:
|
||||||
|
src: keepalived.conf.j2
|
||||||
|
dest: "/etc/keepalived/keepalived.conf"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
tags:
|
||||||
|
- configure
|
||||||
|
no_log: false
|
||||||
|
notify: Restart keepalived
|
||||||
|
|
||||||
|
- name: Enable keepalived service
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: keepalived.service
|
||||||
|
state: "{{ 'started' if keepalived_enabled else 'stopped' }}"
|
||||||
|
enabled: "{{ keepalived_enabled }}"
|
||||||
|
when: not ansible_check_mode
|
||||||
27
roles/keepalived/templates/keepalived.conf.j2
Normal file
27
roles/keepalived/templates/keepalived.conf.j2
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
vrrp_sync_group G1 {
|
||||||
|
group {
|
||||||
|
{%for iface in keepalived_interfaces %}
|
||||||
|
{{ iface.name }}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{%for iface in keepalived_interfaces %}
|
||||||
|
vrrp_instance {{ iface.name }} {
|
||||||
|
state {{ keepalived_init_state | default('BACKUP') }}
|
||||||
|
interface {{ iface.interface | default(iface.name) }}
|
||||||
|
virtual_router_id 42
|
||||||
|
priority {{ keepalived_host_prio }}
|
||||||
|
advert_int 2
|
||||||
|
authentication {
|
||||||
|
auth_type PASS
|
||||||
|
auth_pass {{ iface.password | default(keepalived_password) }}
|
||||||
|
}
|
||||||
|
virtual_ipaddress {
|
||||||
|
{% for addr in iface.addresses %}
|
||||||
|
{{ addr }}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
3
roles/keepalived/vars/debian.yml
Normal file
3
roles/keepalived/vars/debian.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
keepalived_os_supported: true
|
||||||
|
keepalived_packages:
|
||||||
|
- keepalived
|
||||||
1
roles/keepalived/vars/main.yml
Normal file
1
roles/keepalived/vars/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
keepalived_os_supported: false
|
||||||
1
roles/radvd/defaults/main.yml
Normal file
1
roles/radvd/defaults/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
radvd_enabled: false
|
||||||
6
roles/radvd/handlers/main.yml
Normal file
6
roles/radvd/handlers/main.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Restart radvd
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: radvd.service
|
||||||
|
state: restarted
|
||||||
|
enabled: true
|
||||||
52
roles/radvd/tasks/main.yml
Normal file
52
roles/radvd/tasks/main.yml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
- name: Set OS dependent variables
|
||||||
|
include_vars: "{{ lookup('first_found', params) }}"
|
||||||
|
vars:
|
||||||
|
params:
|
||||||
|
files:
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}.yml"
|
||||||
|
- "{{ ansible_os_family | lower }}.yml"
|
||||||
|
- "{{ ansible_system | lower }}.yml"
|
||||||
|
paths:
|
||||||
|
- "{{ role_path }}/vars"
|
||||||
|
ignore_errors: True
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: OS is supported
|
||||||
|
assert:
|
||||||
|
that: __os_supported
|
||||||
|
quiet: True
|
||||||
|
vars:
|
||||||
|
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
|
|
||||||
|
- name: Install required packages
|
||||||
|
package:
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: present
|
||||||
|
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||||
|
when: radvd_enabled
|
||||||
|
tags:
|
||||||
|
- install
|
||||||
|
|
||||||
|
- name: Install radvd configuration
|
||||||
|
template:
|
||||||
|
src: radvd.conf.j2
|
||||||
|
dest: "/etc/radvd.conf"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
tags:
|
||||||
|
- configure
|
||||||
|
no_log: false
|
||||||
|
notify: Restart radvd
|
||||||
|
|
||||||
|
- name: Enable radvd service
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: radvd.service
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
24
roles/radvd/templates/radvd.conf.j2
Normal file
24
roles/radvd/templates/radvd.conf.j2
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
{% for iface in radvd_interfaces | default([]) %}
|
||||||
|
interface {{ iface.name }} {
|
||||||
|
AdvSendAdvert on;
|
||||||
|
MinRtrAdvInterval 5;
|
||||||
|
MaxRtrAdvInterval 15;
|
||||||
|
route ::/0 {};
|
||||||
|
AdvManagedFlag off;
|
||||||
|
AdvOtherConfigFlag off;
|
||||||
|
prefix {{ iface.prefix }} {
|
||||||
|
AdvOnLink on;
|
||||||
|
AdvAutonomous on;
|
||||||
|
AdvRouterAddr on;
|
||||||
|
};
|
||||||
|
{% if iface.source is defined %}
|
||||||
|
AdvRASrcAddress {
|
||||||
|
{{ iface.source }};
|
||||||
|
};
|
||||||
|
{% endif %}
|
||||||
|
RDNSS {{ iface.dns | default(['2a00:1398::1', '2a00:1398::2']) | join(' ') }} {
|
||||||
|
AdvRDNSSLifetime {{ iface.dns_lifetime | default(3600) }};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
{% endfor %}
|
||||||
3
roles/radvd/vars/debian.yml
Normal file
3
roles/radvd/vars/debian.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
radvd_os_supported: true
|
||||||
|
radvd_packages:
|
||||||
|
- radvd
|
||||||
1
roles/radvd/vars/main.yml
Normal file
1
roles/radvd/vars/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
bird_os_supported: false
|
||||||
193
roles/router_bundle/filter_plugins/router_filters.py
Normal file
193
roles/router_bundle/filter_plugins/router_filters.py
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
import ipaddress
|
||||||
|
|
||||||
|
class FilterModule(object):
|
||||||
|
def filters(self):
|
||||||
|
return {
|
||||||
|
'generate_radvd': self.generate_radvd,
|
||||||
|
'generate_keepalived': self.generate_keepalived,
|
||||||
|
'generate_interfaces': self.generate_interfaces,
|
||||||
|
'generate_nftables': self.generate_nftables,
|
||||||
|
'generate_dhcpd': self.generate_dhcpd,
|
||||||
|
'generate_ospf': self.generate_ospf,
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_radvd(self, cluster_networks):
|
||||||
|
result = []
|
||||||
|
for net in cluster_networks:
|
||||||
|
if 'prefix' not in net:
|
||||||
|
continue
|
||||||
|
if 'dns' in net:
|
||||||
|
dns = net['dns']
|
||||||
|
else:
|
||||||
|
dns = ['2a00:1398::64:1', '2a00:1398::64:2']
|
||||||
|
radvd = {
|
||||||
|
'name': net['interface'],
|
||||||
|
'prefix': net['prefix'],
|
||||||
|
'source': 'fe80::1',
|
||||||
|
'dns': dns,
|
||||||
|
}
|
||||||
|
result.append(radvd)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def generate_keepalived(self, cluster_networks):
|
||||||
|
result = []
|
||||||
|
for net in cluster_networks:
|
||||||
|
keepalived = {
|
||||||
|
'name': net['interface'],
|
||||||
|
'addresses': ['fe80::1/64'],
|
||||||
|
}
|
||||||
|
result.append(keepalived)
|
||||||
|
if 'prefix_v4' in net:
|
||||||
|
keepalived = {
|
||||||
|
'name': net['interface'] + '_v4',
|
||||||
|
'interface': net['interface'],
|
||||||
|
'addresses': [self.nth_addr_v4(net['prefix_v4'], 1)],
|
||||||
|
}
|
||||||
|
result.append(keepalived)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def nth_addr(self, prefix: str, n:int) -> str:
|
||||||
|
network = ipaddress.IPv6Network(prefix)
|
||||||
|
# Check if n is within the valid range
|
||||||
|
if n >= network.num_addresses or n < 0:
|
||||||
|
raise ValueError(f"n must be between 0 and {network.num_addresses - 1}")
|
||||||
|
# Calculate the n-th address
|
||||||
|
nth_address = network.network_address + n
|
||||||
|
return str(nth_address) + '/' + str(network.prefixlen)
|
||||||
|
|
||||||
|
def nth_addr_v4(self, prefix: str, n:int, with_prefix: bool = True) -> str:
|
||||||
|
network = ipaddress.IPv4Network(prefix)
|
||||||
|
# Check if n is within the valid range
|
||||||
|
if n >= network.num_addresses or n < 0:
|
||||||
|
raise ValueError(f"n must be between 0 and {network.num_addresses - 1}")
|
||||||
|
# Calculate the n-th address
|
||||||
|
nth_address = network.network_address + n
|
||||||
|
if with_prefix:
|
||||||
|
return str(nth_address) + '/' + str(network.prefixlen)
|
||||||
|
else:
|
||||||
|
return str(nth_address)
|
||||||
|
|
||||||
|
def generate_interfaces(self, cluster_networks, addr_num):
|
||||||
|
result = []
|
||||||
|
for net in cluster_networks:
|
||||||
|
interface = {
|
||||||
|
'name': net['interface'],
|
||||||
|
'mac': net['mac'],
|
||||||
|
}
|
||||||
|
if 'prefix' in net:
|
||||||
|
inet6 = {
|
||||||
|
'addr': [self.nth_addr(net['prefix'], addr_num)],
|
||||||
|
'gateway': False,
|
||||||
|
'custom': False,
|
||||||
|
'routes': False,
|
||||||
|
}
|
||||||
|
interface['inet6'] = inet6
|
||||||
|
if 'prefix_v4' in net:
|
||||||
|
inet4 = {
|
||||||
|
'addr': [self.nth_addr_v4(net['prefix_v4'], addr_num)],
|
||||||
|
'gateway': False,
|
||||||
|
'custom': False,
|
||||||
|
'routes': False,
|
||||||
|
}
|
||||||
|
interface['inet'] = inet4
|
||||||
|
result.append(interface)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def clean_port_range(self, port_range):
|
||||||
|
if ('{' not in port_range) and ('-' in port_range or ',' in port_range):
|
||||||
|
return '{{' + port_range + '}}'
|
||||||
|
return port_range
|
||||||
|
|
||||||
|
def generate_nftables(self, cluster_networks, upstream_interface):
|
||||||
|
result = []
|
||||||
|
nat_rules = []
|
||||||
|
default_policies = []
|
||||||
|
for net in cluster_networks:
|
||||||
|
if 'prefix' in net:
|
||||||
|
if net.get('firewall', None) is not None and net['firewall'].get('enabled', False) == True:
|
||||||
|
for rule in net['firewall'].get('rules', []):
|
||||||
|
if rule.get('dst', None) is None:
|
||||||
|
rule['dst'] = '%net'
|
||||||
|
|
||||||
|
nft_rule = 'oifname {} '.format(net['interface'])
|
||||||
|
if rule.get('src', '') != '':
|
||||||
|
nft_rule += 'ip6 saddr {} '.format(rule['src'].replace('%net', net['prefix']))
|
||||||
|
if rule.get('dst', '') != '':
|
||||||
|
nft_rule += 'ip6 daddr {} '.format(rule['dst'].replace('%net', net['prefix']))
|
||||||
|
if rule.get('sport', '') != '':
|
||||||
|
nft_rule += '{{proto}} sport {} '.format(self.clean_port_range(rule['sport']))
|
||||||
|
if rule.get('dport', '') != '':
|
||||||
|
nft_rule += '{{proto}} dport {} '.format(self.clean_port_range(rule['dport']))
|
||||||
|
nft_rule += rule['action']
|
||||||
|
for proto in rule.get('protocols', ['tcp', 'udp']):
|
||||||
|
result.append(nft_rule.format(proto=proto))
|
||||||
|
default_policies.append('oifname {} ip6 daddr {} {}'.format(net['interface'], net['prefix'], net['firewall']['policy']))
|
||||||
|
else:
|
||||||
|
default_policies.append('oifname {} ip6 daddr {} {}'.format(net['interface'], net['prefix'], 'accept'))
|
||||||
|
# NAT
|
||||||
|
if net.get('nat', None) is not None and net['nat'].get('enabled', False) == True:
|
||||||
|
if net['nat'].get('source', None) is not None and net.get('prefix_v4', None) is not None:
|
||||||
|
nat_rules.append('ip saddr {prefix} oif {dest_iface} snat to {source}'.format(prefix=net['prefix_v4'], dest_iface=upstream_interface, source=net['nat']['source']))
|
||||||
|
|
||||||
|
default_policies.append('oifname {} accept'.format(upstream_interface))
|
||||||
|
|
||||||
|
full_rules = "\n".join(result + default_policies)
|
||||||
|
|
||||||
|
full_nat = "\n".join(nat_rules)
|
||||||
|
|
||||||
|
ret = [
|
||||||
|
{
|
||||||
|
'name': 'cluster_router_bundle',
|
||||||
|
'rule': full_rules,
|
||||||
|
'chain': 'forward',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'cluster_router_bundle',
|
||||||
|
'rule': full_nat,
|
||||||
|
'chain': 'postrouting',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def is_v4(self, addr: str) -> bool:
|
||||||
|
try:
|
||||||
|
ipaddress.IPv4Address(addr)
|
||||||
|
return True
|
||||||
|
except ipaddress.AddressValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def generate_dhcpd(self, cluster_networks):
|
||||||
|
result = []
|
||||||
|
for net in cluster_networks:
|
||||||
|
if net.get('prefix_v4', None) is None:
|
||||||
|
continue
|
||||||
|
if net.get('dhcp', None) is None or net['dhcp'].get('enabled', False) == False:
|
||||||
|
continue
|
||||||
|
router = self.nth_addr_v4(net['prefix_v4'], 1, False)
|
||||||
|
subnet = self.nth_addr_v4(net['prefix_v4'], 0, False)
|
||||||
|
netmask = str(ipaddress.IPv4Network(net['prefix_v4']).netmask)
|
||||||
|
range_start = self.nth_addr_v4(net['prefix_v4'], net['dhcp'].get('range_start', 20), False)
|
||||||
|
range_end = self.nth_addr_v4(net['prefix_v4'], net['dhcp'].get('range_end', 150), False)
|
||||||
|
dhcpd = {
|
||||||
|
'interface': net['interface'],
|
||||||
|
'subnet': subnet,
|
||||||
|
'router': router,
|
||||||
|
'netmask': netmask,
|
||||||
|
'range_start': range_start,
|
||||||
|
'range_end': range_end,
|
||||||
|
}
|
||||||
|
dns = ', '.join(list(filter(lambda x: self.is_v4(x), net.get('dns', []))))
|
||||||
|
if len(dns) > 0:
|
||||||
|
dhcpd['dns'] = dns
|
||||||
|
result.append(dhcpd)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def generate_ospf(self, cluster_networks):
|
||||||
|
result = []
|
||||||
|
for net in cluster_networks:
|
||||||
|
ospf = {
|
||||||
|
'name': net['interface'],
|
||||||
|
'stub': True,
|
||||||
|
}
|
||||||
|
result.append(ospf)
|
||||||
|
return result
|
||||||
19
roles/router_bundle/tasks/main.yml
Normal file
19
roles/router_bundle/tasks/main.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
- name: Generate radvd config
|
||||||
|
set_fact:
|
||||||
|
radvd_interfaces: "{{ cluster_networks | generate_radvd | union(radvd_interfaces | default([])) }}"
|
||||||
|
- name: Generate keepalived config
|
||||||
|
set_fact:
|
||||||
|
keepalived_interfaces: "{{ cluster_networks | generate_keepalived | union(keepalived_interfaces | default([])) }}"
|
||||||
|
- name: Generate interfaces config
|
||||||
|
set_fact:
|
||||||
|
net_interfaces: "{{ cluster_networks | generate_interfaces(cluster_v6_end) | union(net_interfaces | default([])) }}"
|
||||||
|
- name: Generate firewall config
|
||||||
|
set_fact:
|
||||||
|
nftables_rules: "{{ cluster_networks | generate_nftables(cluster_upstream_interface) | union(nftables_rules | default([])) }}"
|
||||||
|
- name: Generate dhcp config
|
||||||
|
set_fact:
|
||||||
|
isc_dhcp_networks: "{{ cluster_networks | generate_dhcpd | union(isc_dhcp_networks | default([])) }}"
|
||||||
|
# - name: Generate ospf config
|
||||||
|
# set_fact:
|
||||||
|
# bird_ospf_interfaces: "{{ cluster_networks | generate_ospf | union(bird_ospf_interfaces | default([])) }}"
|
||||||
Loading…
x
Reference in New Issue
Block a user