Remake Ansible playbook to target MikroTik router
Basically, I've exported configuration from Mikrotik router using /export and vibe-coded playbook using the file.
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "openwrt/roles/ansible-openwrt"]
|
|
||||||
path = openwrt/roles/ansible-openwrt
|
|
||||||
url = https://github.com/gekmihesg/ansible-openwrt.git
|
|
||||||
10
README.md
10
README.md
@@ -141,7 +141,7 @@ Currently the k8s cluster consists of single node (hostname anapistula-delrosala
|
|||||||
|
|
||||||
## Software stack
|
## Software stack
|
||||||
|
|
||||||
The cluster itself is based on [Talos Linux](https://www.talos.dev/) (which is also a Kubernetes distribution) and uses [Cilium](https://cilium.io/) as CNI, IPAM, kube-proxy replacement, Load Balancer, and BGP control plane. Persistent volumes are managed by [OpenEBS LVM LocalPV](https://openebs.io/docs/user-guides/local-storage-user-guide/local-pv-lvm/lvm-overview). Applications are deployed using GitOps (this repo) and reconciled on cluster using [Flux](https://fluxcd.io/). Git repository is hosted on [Gitea](https://gitea.io/) running on a cluster itself. Secets are kept in [OpenBao](https://openbao.org/) (HashiCorp Vault fork) running on a cluster and synced to cluster objects using [Vault Secrets Operator](https://github.com/hashicorp/vault-secrets-operator). Deployments are kept up to date using self hosted [Renovate](https://www.mend.io/renovate/) bot updating manifests in the Git repository. Incoming HTTP traffic is routed to cluster using [Nginx Ingress Controller](https://kubernetes.github.io/ingress-nginx/) and certificates are issued by [cert-manager](https://cert-manager.io/) with [Let's Encrypt](https://letsencrypt.org/) ACME issuer with [cert-manager-webhook-ovh](https://github.com/aureq/cert-manager-webhook-ovh) resolving DNS-01 challanges. Cluster also runs [CloudNativePG](https://cloudnative-pg.io/) operator for managing PostgreSQL databases. High level core cluster software architecture is shown on the diagram below.
|
The cluster itself is based on [Talos Linux](https://www.talos.dev/) (which is also a Kubernetes distribution) and uses [Cilium](https://cilium.io/) as CNI, IPAM, kube-proxy replacement, Load Balancer, and BGP control plane. Persistent volumes are managed by [OpenEBS LVM LocalPV](https://openebs.io/docs/user-guides/local-storage-user-guide/local-pv-lvm/lvm-overview). Applications are deployed using GitOps (this repo) and reconciled on cluster using [Flux](https://fluxcd.io/). Git repository is hosted on [Gitea](https://gitea.io/) running on a cluster itself. Secets are kept in [OpenBao](https://openbao.org/) (HashiCorp Vault fork) running on a cluster and synced to cluster objects using [Vault Secrets Operator](https://github.com/hashicorp/vault-secrets-operator). Deployments are kept up to date using self hosted [Renovate](https://www.mend.io/renovate/) bot updating manifests in the Git repository. Incoming HTTP traffic is routed to cluster using [Nginx Ingress Controller](https://kubernetes.github.io/ingress-nginx/) and certificates are issued by [cert-manager](https://cert-manager.io/) with [Let's Encrypt](https://letsencrypt.org/) ACME issuer with [cert-manager-webhook-ovh](https://github.com/aureq/cert-manager-webhook-ovh) resolving DNS-01 challanges. Cluster also runs [CloudNativePG](https://cloudnative-pg.io/) operator for managing PostgreSQL databases. Router is running [Mikrotik RouterOS](https://help.mikrotik.com/docs/spaces/ROS/pages/328059/RouterOS) and its configuration is managed via [Ansible](https://docs.ansible.com/) playbook in this repo. High level core cluster software architecture is shown on the diagram below.
|
||||||
|
|
||||||
> Talos Linux is an immutable Linux distribution purpose-built for running Kubernetes. The OS is distributed as an OCI (Docker) image and does not contain any package manager, shell, SSH, or any other tools for managing the system. Instead, all operations are performed using API, which can be accessed using `talosctl` CLI tool.
|
> Talos Linux is an immutable Linux distribution purpose-built for running Kubernetes. The OS is distributed as an OCI (Docker) image and does not contain any package manager, shell, SSH, or any other tools for managing the system. Instead, all operations are performed using API, which can be accessed using `talosctl` CLI tool.
|
||||||
|
|
||||||
@@ -227,6 +227,14 @@ Talos config in this repo is stored as yaml patches under [talos/patches](talos/
|
|||||||
|
|
||||||
To compile config, you need to have secrets file, which contains certificates and keys for cluster. Those secrets are then incorporated into final config files. That is also why we can not store full config in repo.
|
To compile config, you need to have secrets file, which contains certificates and keys for cluster. Those secrets are then incorporated into final config files. That is also why we can not store full config in repo.
|
||||||
|
|
||||||
|
### Router config changes
|
||||||
|
|
||||||
|
Router config is stored as Ansible playbook under `ansible/` directory. To apply changes to router, run `ansible-playbook playbooks/routeros.yml` command in `ansible/` directory Before running playbook, you can check what changes will be applied to router using `--check` flag to `ansible-playbook` command, which will run playbook in "check mode" and show you the changes that would be applied without actually applying them. This is useful for verifying that your changes are correct before applying them to the router.
|
||||||
|
|
||||||
|
To run Ansible playbook, you need to have required Ansible collections installed. You can install them using `ansible-galaxy collection install -r ansible/requirements.yml` command. Configuring this in devenv is yet to be done, so you might need to install collections manually for now.
|
||||||
|
|
||||||
|
Secrets needed to access the router API are stored in OpenBao and loaded on demand when running playbook so you need to have access to appropriate secrets.
|
||||||
|
|
||||||
### Kube API access
|
### Kube API access
|
||||||
|
|
||||||
To generate kubeconfig for accessing cluster API, run `make get-kubeconfig` command, which will generate kubeconfig under `talos/generated/kubeconfig` path. Devenv automatically sets `KUBECONFIG` enviornment variable to point to this file, so you can start using `kubectl` right away.
|
To generate kubeconfig for accessing cluster API, run `make get-kubeconfig` command, which will generate kubeconfig under `talos/generated/kubeconfig` path. Devenv automatically sets `KUBECONFIG` enviornment variable to point to this file, so you can start using `kubectl` right away.
|
||||||
|
|||||||
20
ansible/README.md
Normal file
20
ansible/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
## RouterOS Ansible
|
||||||
|
|
||||||
|
This directory contains the new Ansible automation for the MikroTik router.
|
||||||
|
|
||||||
|
- Transport: RouterOS API (`community.routeros` collection), not SSH CLI scraping.
|
||||||
|
- Layout: one playbook (`playbooks/routeros.yml`) importing domain task files from `tasks/`.
|
||||||
|
- Goal: idempotent convergence using `community.routeros.api_modify` for managed paths.
|
||||||
|
|
||||||
|
### Quick start
|
||||||
|
|
||||||
|
1. Install dependencies:
|
||||||
|
- `ansible-galaxy collection install -r ansible/requirements.yml`
|
||||||
|
- `python -m pip install librouteros hvac`
|
||||||
|
2. Configure secret references in `ansible/vars/routeros-secrets.yml`.
|
||||||
|
3. Store required fields in OpenBao under configured KV path.
|
||||||
|
4. Export token (`OPENBAO_TOKEN` or `VAULT_TOKEN`).
|
||||||
|
5. Run:
|
||||||
|
- `ANSIBLE_CONFIG=ansible/ansible.cfg ansible-playbook ansible/playbooks/routeros.yml`
|
||||||
|
|
||||||
|
More details and design rationale: `docs/ansible/routeros-design.md`.
|
||||||
5
ansible/ansible.cfg
Normal file
5
ansible/ansible.cfg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[defaults]
|
||||||
|
inventory = inventory/hosts.yml
|
||||||
|
host_key_checking = False
|
||||||
|
retry_files_enabled = False
|
||||||
|
result_format = yaml
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
[openwrt]
|
|
||||||
2001:470:61a3:100:ffff:ffff:ffff:ffff ansible_scp_extra_args="-O"
|
|
||||||
6
ansible/inventory/hosts.yml
Normal file
6
ansible/inventory/hosts.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
all:
|
||||||
|
children:
|
||||||
|
mikrotik:
|
||||||
|
hosts:
|
||||||
|
crs418:
|
||||||
|
ansible_host: 192.168.255.10
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
- name: Configure router
|
|
||||||
hosts: openwrt
|
|
||||||
remote_user: root
|
|
||||||
roles:
|
|
||||||
- ansible-openwrt
|
|
||||||
- router
|
|
||||||
92
ansible/playbooks/routeros.yml
Normal file
92
ansible/playbooks/routeros.yml
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
---
|
||||||
|
- name: Converge MikroTik RouterOS config
|
||||||
|
hosts: mikrotik
|
||||||
|
gather_facts: false
|
||||||
|
connection: local
|
||||||
|
|
||||||
|
vars_files:
|
||||||
|
- ../vars/routeros-secrets.yml
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Load router secrets from OpenBao
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
routeros_api_username: >-
|
||||||
|
{{
|
||||||
|
lookup(
|
||||||
|
'community.hashi_vault.vault_kv2_get',
|
||||||
|
openbao_fields.routeros_api.path,
|
||||||
|
engine_mount_point=openbao_kv_mount
|
||||||
|
).secret[openbao_fields.routeros_api.username_key]
|
||||||
|
}}
|
||||||
|
routeros_api_password: >-
|
||||||
|
{{
|
||||||
|
lookup(
|
||||||
|
'community.hashi_vault.vault_kv2_get',
|
||||||
|
openbao_fields.routeros_api.path,
|
||||||
|
engine_mount_point=openbao_kv_mount
|
||||||
|
).secret[openbao_fields.routeros_api.password_key]
|
||||||
|
}}
|
||||||
|
routeros_pppoe_username: >-
|
||||||
|
{{
|
||||||
|
lookup(
|
||||||
|
'community.hashi_vault.vault_kv2_get',
|
||||||
|
openbao_fields.wan_pppoe.path,
|
||||||
|
engine_mount_point=openbao_kv_mount
|
||||||
|
).secret[openbao_fields.wan_pppoe.username_key]
|
||||||
|
}}
|
||||||
|
routeros_pppoe_password: >-
|
||||||
|
{{
|
||||||
|
lookup(
|
||||||
|
'community.hashi_vault.vault_kv2_get',
|
||||||
|
openbao_fields.wan_pppoe.path,
|
||||||
|
engine_mount_point=openbao_kv_mount
|
||||||
|
).secret[openbao_fields.wan_pppoe.password_key]
|
||||||
|
}}
|
||||||
|
routeros_tailscale_container_password: >-
|
||||||
|
{{
|
||||||
|
lookup(
|
||||||
|
'community.hashi_vault.vault_kv2_get',
|
||||||
|
openbao_fields.routeros_tailscale_container.path,
|
||||||
|
engine_mount_point=openbao_kv_mount
|
||||||
|
).secret[openbao_fields.routeros_tailscale_container.container_password_key]
|
||||||
|
}}
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
module_defaults:
|
||||||
|
group/community.routeros.api:
|
||||||
|
hostname: "{{ ansible_host }}"
|
||||||
|
username: "{{ routeros_api_username }}"
|
||||||
|
password: "{{ routeros_api_password }}"
|
||||||
|
tls: true
|
||||||
|
validate_certs: false
|
||||||
|
validate_cert_hostname: false
|
||||||
|
force_no_cert: true
|
||||||
|
encoding: UTF-8
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Preflight checks
|
||||||
|
ansible.builtin.import_tasks: ../tasks/preflight.yml
|
||||||
|
|
||||||
|
- name: Base network configuration
|
||||||
|
ansible.builtin.import_tasks: ../tasks/base.yml
|
||||||
|
|
||||||
|
- name: WAN and tunnel interfaces
|
||||||
|
ansible.builtin.import_tasks: ../tasks/wan.yml
|
||||||
|
|
||||||
|
- name: Hardware and platform tuning
|
||||||
|
ansible.builtin.import_tasks: ../tasks/hardware.yml
|
||||||
|
|
||||||
|
- name: RouterOS container configuration
|
||||||
|
ansible.builtin.import_tasks: ../tasks/containers.yml
|
||||||
|
|
||||||
|
- name: Addressing configuration
|
||||||
|
ansible.builtin.import_tasks: ../tasks/addressing.yml
|
||||||
|
|
||||||
|
- name: Firewall configuration
|
||||||
|
ansible.builtin.import_tasks: ../tasks/firewall.yml
|
||||||
|
|
||||||
|
- name: Routing configuration
|
||||||
|
ansible.builtin.import_tasks: ../tasks/routing.yml
|
||||||
|
|
||||||
|
- name: System configuration
|
||||||
|
ansible.builtin.import_tasks: ../tasks/system.yml
|
||||||
5
ansible/requirements.yml
Normal file
5
ansible/requirements.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
collections:
|
||||||
|
- name: community.routeros
|
||||||
|
version: ">=3.16.0"
|
||||||
|
- name: community.hashi_vault
|
||||||
|
version: ">=7.1.0"
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
# Would never work without this awesome blogpost
|
|
||||||
# https://farcaller.net/2024/making-cilium-bgp-work-with-ipv6/
|
|
||||||
|
|
||||||
log "/tmp/bird.log" all;
|
|
||||||
log syslog all;
|
|
||||||
|
|
||||||
#Router ID
|
|
||||||
router id 192.168.1.1;
|
|
||||||
|
|
||||||
protocol kernel kernel4 {
|
|
||||||
learn;
|
|
||||||
scan time 10;
|
|
||||||
merge paths yes;
|
|
||||||
ipv4 {
|
|
||||||
import none;
|
|
||||||
export all;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol kernel kernel6 {
|
|
||||||
learn;
|
|
||||||
scan time 10;
|
|
||||||
merge paths yes;
|
|
||||||
ipv6 {
|
|
||||||
import none;
|
|
||||||
export all;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol device {
|
|
||||||
scan time 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol direct {
|
|
||||||
interface "*";
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol bgp homelab {
|
|
||||||
debug { events };
|
|
||||||
passive;
|
|
||||||
direct;
|
|
||||||
local 2001:470:61a3:100:ffff:ffff:ffff:ffff as 65000;
|
|
||||||
neighbor range 2001:470:61a3:100::/64 as 65000;
|
|
||||||
ipv4 {
|
|
||||||
extended next hop yes;
|
|
||||||
import all;
|
|
||||||
export all;
|
|
||||||
};
|
|
||||||
ipv6 {
|
|
||||||
import all;
|
|
||||||
export all;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- name: Reload bird
|
|
||||||
service:
|
|
||||||
name: bird
|
|
||||||
state: restarted
|
|
||||||
enabled: true
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Install bird2
|
|
||||||
opkg:
|
|
||||||
name: "{{ item }}"
|
|
||||||
state: present
|
|
||||||
# Workaround for opkg module not handling multiple names at once well
|
|
||||||
loop:
|
|
||||||
- bird2
|
|
||||||
- bird2c
|
|
||||||
|
|
||||||
- name: Set up bird.conf
|
|
||||||
ansible.builtin.copy:
|
|
||||||
src: bird.conf
|
|
||||||
dest: /etc/bird.conf
|
|
||||||
mode: "644"
|
|
||||||
notify: Reload bird
|
|
||||||
48
ansible/tasks/addressing.yml
Normal file
48
ansible/tasks/addressing.yml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
- name: Configure IPv4 addresses
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ip address
|
||||||
|
data:
|
||||||
|
- address: 172.17.0.1/16
|
||||||
|
interface: dockers
|
||||||
|
network: 172.17.0.0
|
||||||
|
- address: 192.168.4.1/24
|
||||||
|
interface: lo
|
||||||
|
network: 192.168.4.0
|
||||||
|
- address: 192.168.100.20/24
|
||||||
|
interface: sfp-sfpplus1
|
||||||
|
network: 192.168.100.0
|
||||||
|
- address: 192.168.255.10/24
|
||||||
|
interface: bridge1
|
||||||
|
network: 192.168.255.0
|
||||||
|
- address: 192.168.0.1/24
|
||||||
|
interface: vlan2
|
||||||
|
network: 192.168.0.0
|
||||||
|
- address: 192.168.1.1/24
|
||||||
|
interface: vlan4
|
||||||
|
network: 192.168.1.0
|
||||||
|
- address: 192.168.3.1/24
|
||||||
|
interface: vlan3
|
||||||
|
network: 192.168.3.0
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure IPv6 addresses
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ipv6 address
|
||||||
|
data:
|
||||||
|
- address: 2001:470:70:dd::2/64
|
||||||
|
advertise: false
|
||||||
|
interface: sit1
|
||||||
|
- address: ::ffff:ffff:ffff:ffff/64
|
||||||
|
from-pool: pool1
|
||||||
|
interface: vlan2
|
||||||
|
- address: 2001:470:61a3:500:ffff:ffff:ffff:ffff/64
|
||||||
|
interface: dockers
|
||||||
|
- address: 2001:470:61a3:100::1/64
|
||||||
|
advertise: false
|
||||||
|
interface: vlan4
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
226
ansible/tasks/base.yml
Normal file
226
ansible/tasks/base.yml
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
---
|
||||||
|
- name: Configure bridges
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface bridge
|
||||||
|
data:
|
||||||
|
- name: bridge1
|
||||||
|
vlan-filtering: true
|
||||||
|
- name: dockers
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure VLAN interfaces
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface vlan
|
||||||
|
data:
|
||||||
|
- name: vlan2
|
||||||
|
comment: LAN (PC, WIFI)
|
||||||
|
interface: bridge1
|
||||||
|
vlan-id: 2
|
||||||
|
- name: vlan3
|
||||||
|
comment: KAMERY
|
||||||
|
interface: bridge1
|
||||||
|
vlan-id: 3
|
||||||
|
- name: vlan4
|
||||||
|
comment: SERVER LAN
|
||||||
|
interface: bridge1
|
||||||
|
vlan-id: 4
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure interface lists
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface list
|
||||||
|
data:
|
||||||
|
- name: wan
|
||||||
|
comment: contains interfaces facing internet
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure interface list members
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface list member
|
||||||
|
data:
|
||||||
|
- interface: pppoe-gpon
|
||||||
|
list: wan
|
||||||
|
- interface: lte1
|
||||||
|
list: wan
|
||||||
|
- interface: sit1
|
||||||
|
list: wan
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure bridge ports
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface bridge port
|
||||||
|
data:
|
||||||
|
- bridge: dockers
|
||||||
|
interface: veth1
|
||||||
|
comment: Tailscale container interface
|
||||||
|
- bridge: bridge1
|
||||||
|
interface: ether1
|
||||||
|
pvid: 2
|
||||||
|
- bridge: bridge1
|
||||||
|
interface: ether2
|
||||||
|
pvid: 2
|
||||||
|
- bridge: bridge1
|
||||||
|
interface: ether8
|
||||||
|
pvid: 4
|
||||||
|
- bridge: bridge1
|
||||||
|
interface: ether9
|
||||||
|
pvid: 2
|
||||||
|
- bridge: bridge1
|
||||||
|
interface: ether10
|
||||||
|
pvid: 3
|
||||||
|
- bridge: bridge1
|
||||||
|
interface: sfp-sfpplus2
|
||||||
|
- bridge: bridge1
|
||||||
|
interface: ether11
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure bridge VLAN membership
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface bridge vlan
|
||||||
|
data:
|
||||||
|
- bridge: bridge1
|
||||||
|
tagged: sfp-sfpplus2
|
||||||
|
untagged: ether1,ether2,ether9
|
||||||
|
vlan-ids: 2
|
||||||
|
- bridge: bridge1
|
||||||
|
tagged: sfp-sfpplus2
|
||||||
|
untagged: ether10
|
||||||
|
vlan-ids: 3
|
||||||
|
- bridge: bridge1
|
||||||
|
untagged: ether8
|
||||||
|
vlan-ids: 4
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure IPv4 pools
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ip pool
|
||||||
|
data:
|
||||||
|
- name: dhcp_pool0
|
||||||
|
ranges: 192.168.0.50-192.168.0.250
|
||||||
|
comment: LAN DHCP pool
|
||||||
|
- name: dhcp_pool1
|
||||||
|
ranges: 192.168.255.1-192.168.255.9,192.168.255.11-192.168.255.254
|
||||||
|
comment: MGMT DHCP pool
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure DHCP servers
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ip dhcp-server
|
||||||
|
data:
|
||||||
|
- name: dhcp1
|
||||||
|
address-pool: dhcp_pool0
|
||||||
|
interface: vlan2
|
||||||
|
lease-time: 30m
|
||||||
|
comment: LAN
|
||||||
|
- name: dhcp2
|
||||||
|
address-pool: dhcp_pool1
|
||||||
|
interface: bridge1
|
||||||
|
lease-time: 30m
|
||||||
|
comment: MGMT
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure DHCP networks
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ip dhcp-server network
|
||||||
|
data:
|
||||||
|
- address: 192.168.0.0/24
|
||||||
|
dns-server: 192.168.0.1
|
||||||
|
gateway: 192.168.0.1
|
||||||
|
- address: 192.168.255.0/24
|
||||||
|
dns-none: true
|
||||||
|
gateway: 192.168.255.10
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
# TODO: IPv6 pools are useful when we have dynamic prefix, but we don't
|
||||||
|
# We can remove it now
|
||||||
|
- name: Configure IPv6 pools
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ipv6 pool
|
||||||
|
data:
|
||||||
|
- name: pool1
|
||||||
|
prefix: 2001:470:61a3::/48
|
||||||
|
prefix-length: 64
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure DNS
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: ip dns
|
||||||
|
find: {}
|
||||||
|
values:
|
||||||
|
allow-remote-requests: true
|
||||||
|
cache-size: 20480
|
||||||
|
servers: 1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001
|
||||||
|
|
||||||
|
- name: Configure NAT-PMP global settings
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: ip nat-pmp
|
||||||
|
find: {}
|
||||||
|
values:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- name: Configure NAT-PMP interfaces
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ip nat-pmp interfaces
|
||||||
|
data:
|
||||||
|
- interface: dockers
|
||||||
|
type: internal
|
||||||
|
- interface: pppoe-gpon
|
||||||
|
type: external
|
||||||
|
- interface: vlan2
|
||||||
|
type: internal
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure UPnP global settings
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: ip upnp
|
||||||
|
find: {}
|
||||||
|
values:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- name: Configure UPnP interfaces
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ip upnp interfaces
|
||||||
|
data:
|
||||||
|
- interface: dockers
|
||||||
|
type: internal
|
||||||
|
- interface: pppoe-gpon
|
||||||
|
type: external
|
||||||
|
- interface: vlan2
|
||||||
|
type: internal
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure IPv6 ND defaults
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: ipv6 nd
|
||||||
|
find:
|
||||||
|
default: true
|
||||||
|
values:
|
||||||
|
advertise-dns: true
|
||||||
66
ansible/tasks/containers.yml
Normal file
66
ansible/tasks/containers.yml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
- name: Configure container runtime defaults
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: container config
|
||||||
|
find: {}
|
||||||
|
values:
|
||||||
|
registry-url: https://ghcr.io
|
||||||
|
tmpdir: /tmp1/pull
|
||||||
|
|
||||||
|
- name: Configure container env lists
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: container envs
|
||||||
|
data:
|
||||||
|
- key: ADVERTISE_ROUTES
|
||||||
|
list: tailscale
|
||||||
|
value: 192.168.0.0/24,192.168.1.0/24,192.168.4.1/32,192.168.100.1/32,192.168.255.0/24,10.42.0.0/16,10.43.0.0/16,10.44.0.0/16,2001:470:61a3::/48
|
||||||
|
- key: CONTAINER_GATEWAY
|
||||||
|
list: tailscale
|
||||||
|
value: 172.17.0.1
|
||||||
|
- key: PASSWORD
|
||||||
|
list: tailscale
|
||||||
|
value: "{{ routeros_tailscale_container_password }}"
|
||||||
|
- key: TAILSCALE_ARGS
|
||||||
|
list: tailscale
|
||||||
|
value: --accept-routes --advertise-exit-node --snat-subnet-routes=false
|
||||||
|
- key: UPDATE_TAILSCALE
|
||||||
|
list: tailscale
|
||||||
|
value: y
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure container mounts
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: container mounts
|
||||||
|
data:
|
||||||
|
- dst: /var/lib/tailscale
|
||||||
|
list: tailscale
|
||||||
|
src: /usb1/tailscale
|
||||||
|
- dst: /root
|
||||||
|
list: tailscale-root
|
||||||
|
src: /tmp1/tailscale-root
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure tailscale container
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: container
|
||||||
|
data:
|
||||||
|
- dns: 172.17.0.1
|
||||||
|
envlists: tailscale
|
||||||
|
hostname: mikrotik
|
||||||
|
interface: veth1
|
||||||
|
layer-dir: ""
|
||||||
|
mountlists: tailscale
|
||||||
|
name: tailscale-mikrotik:latest
|
||||||
|
remote-image: fluent-networks/tailscale-mikrotik:latest
|
||||||
|
root-dir: /usb1/containers/tailscale
|
||||||
|
start-on-boot: true
|
||||||
|
tmpfs: /tmp:67108864:01777
|
||||||
|
workdir: /
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
480
ansible/tasks/firewall.yml
Normal file
480
ansible/tasks/firewall.yml
Normal file
@@ -0,0 +1,480 @@
|
|||||||
|
---
|
||||||
|
- name: Configure IPv4 firewall filter rules
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ip firewall filter
|
||||||
|
data:
|
||||||
|
- action: fasttrack-connection
|
||||||
|
chain: forward
|
||||||
|
connection-state: established,related
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow all already established connections
|
||||||
|
connection-state: established,related
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow LTE modem management (next rule forbids it otherwise)
|
||||||
|
dst-address: 192.168.8.1
|
||||||
|
out-interface: lte1
|
||||||
|
- action: reject
|
||||||
|
chain: forward
|
||||||
|
comment: Forbid forwarding 192.168.0.0/16 to WAN
|
||||||
|
dst-address: 192.168.0.0/16
|
||||||
|
out-interface-list: wan
|
||||||
|
reject-with: icmp-network-unreachable
|
||||||
|
- action: reject
|
||||||
|
chain: forward
|
||||||
|
comment: Forbid forwarding 10.0.0.0/8 to WAN
|
||||||
|
dst-address: 10.0.0.0/8
|
||||||
|
out-interface-list: wan
|
||||||
|
reject-with: icmp-network-unreachable
|
||||||
|
- action: reject
|
||||||
|
chain: forward
|
||||||
|
comment: Forbid forwarding 172.16.0.0/12 to WAN
|
||||||
|
dst-address: 172.16.0.0/12
|
||||||
|
out-interface-list: wan
|
||||||
|
reject-with: icmp-network-unreachable
|
||||||
|
- action: reject
|
||||||
|
chain: forward
|
||||||
|
comment: Forbid forwarding 100.64.0.0/10 to WAN
|
||||||
|
dst-address: 100.64.0.0/10
|
||||||
|
out-interface-list: wan
|
||||||
|
reject-with: icmp-network-unreachable
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from LAN to everywhere
|
||||||
|
in-interface: vlan2
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from SRV to internet
|
||||||
|
in-interface: vlan4
|
||||||
|
out-interface-list: wan
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from SRV to CAM
|
||||||
|
in-interface: vlan4
|
||||||
|
out-interface: vlan3
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from dockers to everywhere
|
||||||
|
in-interface: dockers
|
||||||
|
- action: jump
|
||||||
|
chain: forward
|
||||||
|
comment: Allow port forwards
|
||||||
|
in-interface: pppoe-gpon
|
||||||
|
jump-target: allow-ports
|
||||||
|
- action: reject
|
||||||
|
chain: forward
|
||||||
|
comment: Reject all remaining (port unreachable from WAN)
|
||||||
|
in-interface-list: wan
|
||||||
|
log-prefix: FORWARD REJECT
|
||||||
|
reject-with: icmp-port-unreachable
|
||||||
|
- action: reject
|
||||||
|
chain: forward
|
||||||
|
comment: Reject all remaining (net prohibited from LAN)
|
||||||
|
log-prefix: FORWARD REJECT
|
||||||
|
reject-with: icmp-net-prohibited
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow all already established connections
|
||||||
|
connection-state: established,related
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow HE tunnel
|
||||||
|
in-interface: pppoe-gpon
|
||||||
|
protocol: ipv6-encap
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow ICMP
|
||||||
|
protocol: icmp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow Winbox
|
||||||
|
dst-port: 8291
|
||||||
|
log: true
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow SSH Mikrotik
|
||||||
|
dst-port: 2137
|
||||||
|
log: true
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow RouterOS API-SSL from MGMT
|
||||||
|
dst-port: 8729
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow DNS from LAN
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: vlan2
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: vlan2
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow DNS from SRV
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: vlan4
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: vlan4
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow DNS from dockers
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: dockers
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: dockers
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow BGP from SRV
|
||||||
|
dst-port: 179
|
||||||
|
in-interface: vlan4
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: NAT-PMP from LAN
|
||||||
|
dst-port: 5351
|
||||||
|
in-interface: vlan2
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: NAT-PMP from dockers (for tailscale)
|
||||||
|
dst-port: 5351
|
||||||
|
in-interface: dockers
|
||||||
|
protocol: udp
|
||||||
|
- action: reject
|
||||||
|
chain: input
|
||||||
|
comment: Reject all remaining
|
||||||
|
log-prefix: INPUT REJECT
|
||||||
|
reject-with: icmp-port-unreachable
|
||||||
|
- action: accept
|
||||||
|
chain: allow-ports
|
||||||
|
comment: Allow TS3
|
||||||
|
dst-port: 9987
|
||||||
|
out-interface: vlan4
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: allow-ports
|
||||||
|
dst-port: 30033
|
||||||
|
out-interface: vlan4
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: allow-ports
|
||||||
|
comment: Allow HTTP
|
||||||
|
dst-port: 80
|
||||||
|
out-interface: vlan4
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: allow-ports
|
||||||
|
comment: Allow HTTPS
|
||||||
|
dst-port: 443
|
||||||
|
out-interface: vlan4
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: allow-ports
|
||||||
|
comment: Allow SSH Gitea
|
||||||
|
dst-port: 22
|
||||||
|
out-interface: vlan4
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: allow-ports
|
||||||
|
comment: Allow anything udp to Tailscale
|
||||||
|
dst-address: 172.17.0.2
|
||||||
|
out-interface: dockers
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: allow-ports
|
||||||
|
comment: Allow anything from GPON to LAN (NAT-PMP)
|
||||||
|
dst-address: 192.168.0.0/24
|
||||||
|
in-interface: pppoe-gpon
|
||||||
|
out-interface: vlan2
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure IPv4 NAT rules
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ip firewall nat
|
||||||
|
data:
|
||||||
|
- action: masquerade
|
||||||
|
chain: srcnat
|
||||||
|
comment: Masquerade to internet
|
||||||
|
out-interface-list: wan
|
||||||
|
- action: masquerade
|
||||||
|
chain: srcnat
|
||||||
|
comment: GPON ONT management
|
||||||
|
dst-address: 192.168.100.1
|
||||||
|
- action: masquerade
|
||||||
|
chain: srcnat
|
||||||
|
comment: LTE Modem management
|
||||||
|
dst-address: 192.168.8.1
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
comment: TS3
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 9987
|
||||||
|
protocol: udp
|
||||||
|
to-addresses: 10.44.0.0
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 30033
|
||||||
|
protocol: tcp
|
||||||
|
to-addresses: 10.44.0.0
|
||||||
|
- action: src-nat
|
||||||
|
chain: srcnat
|
||||||
|
comment: src-nat from LAN to TS3 to some Greenland address
|
||||||
|
dst-address: 10.44.0.0
|
||||||
|
dst-port: 9987
|
||||||
|
in-interface: '!pppoe-gpon'
|
||||||
|
protocol: udp
|
||||||
|
to-addresses: 128.0.70.5
|
||||||
|
- action: src-nat
|
||||||
|
chain: srcnat
|
||||||
|
dst-address: 10.44.0.0
|
||||||
|
dst-port: 30033
|
||||||
|
in-interface: '!pppoe-gpon'
|
||||||
|
protocol: tcp
|
||||||
|
to-addresses: 128.0.70.5
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
comment: HTTPS
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 443
|
||||||
|
protocol: tcp
|
||||||
|
to-addresses: 10.44.0.6
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
comment: HTTP
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 80
|
||||||
|
protocol: tcp
|
||||||
|
to-addresses: 10.44.0.6
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
comment: SSH Gitea
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 22
|
||||||
|
protocol: tcp
|
||||||
|
to-addresses: 10.44.0.6
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
comment: sunshine
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 47984
|
||||||
|
in-interface: pppoe-gpon
|
||||||
|
protocol: tcp
|
||||||
|
to-addresses: 192.168.0.67
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
comment: sunshine
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 47989
|
||||||
|
in-interface: pppoe-gpon
|
||||||
|
protocol: tcp
|
||||||
|
to-addresses: 192.168.0.67
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
comment: sunshine
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 48010
|
||||||
|
in-interface: pppoe-gpon
|
||||||
|
protocol: tcp
|
||||||
|
to-addresses: 192.168.0.67
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
comment: sunshine
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 48010
|
||||||
|
in-interface: pppoe-gpon
|
||||||
|
protocol: udp
|
||||||
|
to-addresses: 192.168.0.67
|
||||||
|
- action: dst-nat
|
||||||
|
chain: dstnat
|
||||||
|
comment: sunshine
|
||||||
|
dst-address: 139.28.40.212
|
||||||
|
dst-port: 47998-48000
|
||||||
|
in-interface: pppoe-gpon
|
||||||
|
protocol: udp
|
||||||
|
to-addresses: 192.168.0.67
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure IPv6 firewall filter rules
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ipv6 firewall filter
|
||||||
|
data:
|
||||||
|
- action: fasttrack-connection
|
||||||
|
chain: forward
|
||||||
|
connection-state: established,related
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow all already established connections
|
||||||
|
connection-state: established,related
|
||||||
|
- action: reject
|
||||||
|
chain: forward
|
||||||
|
comment: Forbid forwarding routed /48 from tunnelbroker to WAN
|
||||||
|
dst-address: 2001:470:61a3::/48
|
||||||
|
out-interface-list: wan
|
||||||
|
reject-with: icmp-no-route
|
||||||
|
- action: reject
|
||||||
|
chain: forward
|
||||||
|
comment: Forbid forwarding routed /64 from tunnelbroker to WAN
|
||||||
|
dst-address: 2001:470:71:dd::/64
|
||||||
|
out-interface-list: wan
|
||||||
|
reject-with: icmp-no-route
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from LAN to everywhere
|
||||||
|
in-interface: vlan2
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow ICMPv6 from internet to LAN
|
||||||
|
in-interface-list: wan
|
||||||
|
out-interface: vlan2
|
||||||
|
protocol: icmpv6
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from SRV to internet
|
||||||
|
in-interface: vlan4
|
||||||
|
out-interface-list: wan
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from internet to SRV nodes
|
||||||
|
dst-address: 2001:470:61a3:100::/64
|
||||||
|
in-interface-list: wan
|
||||||
|
out-interface: vlan4
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from internet to homelab LB
|
||||||
|
dst-address: 2001:470:61a3:400::/112
|
||||||
|
in-interface-list: wan
|
||||||
|
out-interface: vlan4
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from SRV to CAM
|
||||||
|
in-interface: vlan4
|
||||||
|
out-interface: vlan3
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from dockers to everywhere
|
||||||
|
in-interface: dockers
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow from internet to dockers
|
||||||
|
dst-address: 2001:470:61a3:500::/64
|
||||||
|
in-interface-list: wan
|
||||||
|
out-interface: dockers
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow tcp transmission port to LAN
|
||||||
|
dst-port: 51413
|
||||||
|
out-interface: vlan2
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: forward
|
||||||
|
comment: Allow udp transmission port to LAN
|
||||||
|
dst-port: 51413
|
||||||
|
out-interface: vlan2
|
||||||
|
protocol: udp
|
||||||
|
- action: reject
|
||||||
|
chain: forward
|
||||||
|
comment: Reject all remaining
|
||||||
|
reject-with: icmp-no-route
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow all already established connections
|
||||||
|
connection-state: established,related
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow ICMPv6
|
||||||
|
protocol: icmpv6
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow Winbox
|
||||||
|
dst-port: 8291
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow SSH Mikrotik
|
||||||
|
dst-port: 2137
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow DNS from LAN
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: vlan2
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: vlan2
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow DNS from SRV
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: vlan4
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: vlan4
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow DNS from dockers
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: dockers
|
||||||
|
protocol: udp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
dst-port: 53
|
||||||
|
in-interface: dockers
|
||||||
|
protocol: tcp
|
||||||
|
- action: accept
|
||||||
|
chain: input
|
||||||
|
comment: Allow BGP from SRV
|
||||||
|
dst-port: 179
|
||||||
|
in-interface: vlan4
|
||||||
|
protocol: tcp
|
||||||
|
src-address: 2001:470:61a3:100::/64
|
||||||
|
- action: reject
|
||||||
|
chain: input
|
||||||
|
comment: Reject all remaining
|
||||||
|
reject-with: icmp-admin-prohibited
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure IPv6 NAT rules
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ipv6 firewall nat
|
||||||
|
data:
|
||||||
|
- action: src-nat
|
||||||
|
chain: srcnat
|
||||||
|
comment: src-nat tailnet to internet
|
||||||
|
out-interface-list: wan
|
||||||
|
src-address: fd7a:115c:a1e0::/48
|
||||||
|
to-address: 2001:470:61a3:600::/64
|
||||||
|
- action: masquerade
|
||||||
|
chain: srcnat
|
||||||
|
disabled: true
|
||||||
|
in-interface: vlan2
|
||||||
|
out-interface: vlan4
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
103
ansible/tasks/hardware.yml
Normal file
103
ansible/tasks/hardware.yml
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
- name: Configure ethernet interface metadata and SFP options
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: interface ethernet
|
||||||
|
find:
|
||||||
|
default-name: "{{ item.default_name }}"
|
||||||
|
values: "{{ item.config }}"
|
||||||
|
loop:
|
||||||
|
- default_name: ether1
|
||||||
|
config:
|
||||||
|
comment: Mój pc
|
||||||
|
- default_name: ether2
|
||||||
|
config:
|
||||||
|
comment: Wifi środek
|
||||||
|
- default_name: ether8
|
||||||
|
config:
|
||||||
|
comment: Serwer
|
||||||
|
- default_name: ether9
|
||||||
|
config:
|
||||||
|
comment: Wifi góra
|
||||||
|
- default_name: ether10
|
||||||
|
config:
|
||||||
|
comment: Kamera na domu
|
||||||
|
- default_name: ether11
|
||||||
|
config:
|
||||||
|
comment: KVM serwer
|
||||||
|
- default_name: sfp-sfpplus1
|
||||||
|
config:
|
||||||
|
auto-negotiation: false
|
||||||
|
comment: GPON WAN
|
||||||
|
speed: 2.5G-baseX
|
||||||
|
- default_name: sfp-sfpplus2
|
||||||
|
config:
|
||||||
|
comment: GARAŻ
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.default_name }}"
|
||||||
|
|
||||||
|
- name: Configure LTE interface defaults
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: interface lte
|
||||||
|
find:
|
||||||
|
default-name: lte1
|
||||||
|
values:
|
||||||
|
apn-profiles: default-nodns
|
||||||
|
comment: Backup LTE WAN
|
||||||
|
|
||||||
|
- name: Configure LTE APN profiles
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface lte apn
|
||||||
|
data:
|
||||||
|
- add-default-route: false
|
||||||
|
apn: internet
|
||||||
|
comment: default but without dns and default route
|
||||||
|
ipv6-interface: lte1
|
||||||
|
name: default-nodns
|
||||||
|
use-network-apn: true
|
||||||
|
use-peer-dns: false
|
||||||
|
# Default APN we can't really remove yet I don't want to reconfigure it
|
||||||
|
- add-default-route: true
|
||||||
|
apn: internet
|
||||||
|
authentication: none
|
||||||
|
default-route-distance: 2
|
||||||
|
ip-type: auto
|
||||||
|
name: default
|
||||||
|
use-network-apn: true
|
||||||
|
use-peer-dns: true
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
|
||||||
|
- name: Configure temporary disk for containers
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: disk
|
||||||
|
data:
|
||||||
|
- slot: tmp1
|
||||||
|
type: tmpfs
|
||||||
|
# This is not ideal, there's no unique identifier for usb disk,
|
||||||
|
# after reinstall it might be assigned to another slot
|
||||||
|
# Just adding disk with slot usb1 and not specifying anything else
|
||||||
|
# so ansible doesn't touch it
|
||||||
|
- slot: usb1
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
|
||||||
|
- name: Configure switch settings
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: interface ethernet switch
|
||||||
|
find:
|
||||||
|
.id: "0"
|
||||||
|
values:
|
||||||
|
qos-hw-offloading: true
|
||||||
|
# Enabling L3 offloading would cause all packets to skip firewall and NAT
|
||||||
|
l3-hw-offloading: false
|
||||||
|
|
||||||
|
- name: Configure neighbor discovery settings
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: ip neighbor discovery-settings
|
||||||
|
find: {}
|
||||||
|
values:
|
||||||
|
discover-interface-list: '!dynamic'
|
||||||
46
ansible/tasks/preflight.yml
Normal file
46
ansible/tasks/preflight.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
- name: Verify API connectivity and fetch basic facts
|
||||||
|
community.routeros.api_facts:
|
||||||
|
gather_subset:
|
||||||
|
- default
|
||||||
|
- hardware
|
||||||
|
|
||||||
|
- name: Show target identity
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "Managing {{ ansible_host }} ({{ ansible_facts['net_model'] | default('unknown model') }})"
|
||||||
|
|
||||||
|
- name: Assert expected router model
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ansible_facts['net_model'] is defined
|
||||||
|
- ansible_facts['net_model'] == "CRS418-8P-8G-2S+"
|
||||||
|
fail_msg: "Unexpected router model: {{ ansible_facts['net_model'] | default('unknown') }}"
|
||||||
|
success_msg: "Router model matches expected CRS418-8P-8G-2S+"
|
||||||
|
|
||||||
|
- name: Read RouterOS device-mode flags
|
||||||
|
community.routeros.api:
|
||||||
|
path: system/device-mode
|
||||||
|
register: routeros_device_mode
|
||||||
|
check_mode: false
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Assert container feature is enabled in device mode
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- not (routeros_device_mode.skipped | default(false))
|
||||||
|
- (routeros_device_mode | to_nice_json | lower) is search('container[^a-z0-9]+(yes|true)')
|
||||||
|
fail_msg: "RouterOS device-mode does not report container as enabled. Payload: {{ routeros_device_mode | to_nice_json }}"
|
||||||
|
success_msg: "RouterOS device-mode confirms container=yes"
|
||||||
|
|
||||||
|
- name: Read configured disks
|
||||||
|
community.routeros.api_info:
|
||||||
|
path: disk
|
||||||
|
register: routeros_disks
|
||||||
|
check_mode: false
|
||||||
|
|
||||||
|
- name: Assert usb1 disk is present
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- (routeros_disks.result | selectattr('slot', 'equalto', 'usb1') | list | length) > 0
|
||||||
|
fail_msg: "Required disk slot usb1 is not present on router."
|
||||||
|
success_msg: "Required disk usb1 is present"
|
||||||
99
ansible/tasks/routing.yml
Normal file
99
ansible/tasks/routing.yml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
- name: Configure IPv4 routes
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ip route
|
||||||
|
data:
|
||||||
|
- comment: Tailnet
|
||||||
|
disabled: false
|
||||||
|
distance: 1
|
||||||
|
dst-address: 100.64.0.0/10
|
||||||
|
gateway: 172.17.0.2
|
||||||
|
routing-table: main
|
||||||
|
scope: 30
|
||||||
|
suppress-hw-offload: false
|
||||||
|
target-scope: 10
|
||||||
|
- disabled: false
|
||||||
|
distance: 1
|
||||||
|
dst-address: 0.0.0.0/0
|
||||||
|
gateway: pppoe-gpon
|
||||||
|
routing-table: main
|
||||||
|
scope: 30
|
||||||
|
suppress-hw-offload: false
|
||||||
|
target-scope: 10
|
||||||
|
vrf-interface: pppoe-gpon
|
||||||
|
- disabled: false
|
||||||
|
distance: 2
|
||||||
|
dst-address: 0.0.0.0/0
|
||||||
|
gateway: 192.168.8.1
|
||||||
|
routing-table: main
|
||||||
|
scope: 30
|
||||||
|
suppress-hw-offload: false
|
||||||
|
target-scope: 10
|
||||||
|
vrf-interface: lte1
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
|
||||||
|
- name: Configure IPv6 routes
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: ipv6 route
|
||||||
|
data:
|
||||||
|
- disabled: false
|
||||||
|
distance: 1
|
||||||
|
dst-address: 2000::/3
|
||||||
|
gateway: 2001:470:70:dd::1
|
||||||
|
scope: 30
|
||||||
|
target-scope: 10
|
||||||
|
- comment: Tailnet
|
||||||
|
disabled: false
|
||||||
|
dst-address: fd7a:115c:a1e0::/48
|
||||||
|
gateway: 2001:470:61a3:500::1
|
||||||
|
pref-src: ""
|
||||||
|
routing-table: main
|
||||||
|
suppress-hw-offload: false
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
|
||||||
|
- name: Configure BGP instance
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: routing bgp instance
|
||||||
|
data:
|
||||||
|
- name: bgp-homelab
|
||||||
|
as: 65000
|
||||||
|
disabled: false
|
||||||
|
router-id: 192.168.1.1
|
||||||
|
routing-table: main
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure BGP templates
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: routing bgp template
|
||||||
|
data:
|
||||||
|
- name: klaster
|
||||||
|
afi: ip,ipv6
|
||||||
|
as: 6500
|
||||||
|
disabled: false
|
||||||
|
# Default template
|
||||||
|
- name: default
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
|
||||||
|
- name: Configure BGP connections
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: routing bgp connection
|
||||||
|
data:
|
||||||
|
- name: bgp1
|
||||||
|
afi: ip,ipv6
|
||||||
|
as: 65000
|
||||||
|
connect: true
|
||||||
|
disabled: false
|
||||||
|
instance: bgp-homelab
|
||||||
|
listen: true
|
||||||
|
local.role: ibgp
|
||||||
|
remote.address: 2001:470:61a3:100::3/128
|
||||||
|
routing-table: main
|
||||||
|
templates: klaster
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
43
ansible/tasks/system.yml
Normal file
43
ansible/tasks/system.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
- name: Configure system clock
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: system clock
|
||||||
|
find: {}
|
||||||
|
values:
|
||||||
|
time-zone-name: Europe/Warsaw
|
||||||
|
|
||||||
|
- name: Configure dedicated Ansible management user
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: user
|
||||||
|
data:
|
||||||
|
- name: "{{ routeros_api_username }}"
|
||||||
|
group: full
|
||||||
|
password: "{{ routeros_api_password }}"
|
||||||
|
disabled: false
|
||||||
|
comment: "Ansible management user"
|
||||||
|
handle_absent_entries: ignore
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
|
||||||
|
- name: Configure service ports and service enablement
|
||||||
|
community.routeros.api_find_and_modify:
|
||||||
|
ignore_dynamic: false
|
||||||
|
path: ip service
|
||||||
|
find:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
values: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- name: ftp
|
||||||
|
disabled: true
|
||||||
|
- name: telnet
|
||||||
|
disabled: true
|
||||||
|
- name: www
|
||||||
|
disabled: true
|
||||||
|
- name: ssh
|
||||||
|
port: 2137
|
||||||
|
- name: api
|
||||||
|
disabled: true
|
||||||
|
- name: api-ssl
|
||||||
|
disabled: false
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
44
ansible/tasks/wan.yml
Normal file
44
ansible/tasks/wan.yml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
- name: Configure PPPoE client
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface pppoe-client
|
||||||
|
data:
|
||||||
|
- disabled: false
|
||||||
|
interface: sfp-sfpplus1
|
||||||
|
keepalive-timeout: 2
|
||||||
|
name: pppoe-gpon
|
||||||
|
password: "{{ routeros_pppoe_password }}"
|
||||||
|
use-peer-dns: true
|
||||||
|
user: "{{ routeros_pppoe_username }}"
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure 6to4 tunnel interface
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface 6to4
|
||||||
|
data:
|
||||||
|
- comment: Hurricane Electric IPv6 Tunnel Broker
|
||||||
|
local-address: 139.28.40.212
|
||||||
|
mtu: 1472
|
||||||
|
name: sit1
|
||||||
|
remote-address: 216.66.80.162
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
|
|
||||||
|
- name: Configure veth interface for containers
|
||||||
|
community.routeros.api_modify:
|
||||||
|
path: interface veth
|
||||||
|
data:
|
||||||
|
- address: 172.17.0.2/16,2001:470:61a3:500::1/64
|
||||||
|
container-mac-address: 7E:7E:A1:B1:2A:7C
|
||||||
|
dhcp: false
|
||||||
|
gateway: 172.17.0.1
|
||||||
|
gateway6: 2001:470:61a3:500:ffff:ffff:ffff:ffff
|
||||||
|
mac-address: 7E:7E:A1:B1:2A:7B
|
||||||
|
name: veth1
|
||||||
|
comment: Tailscale container
|
||||||
|
handle_absent_entries: remove
|
||||||
|
handle_entries_content: remove_as_much_as_possible
|
||||||
|
ensure_order: true
|
||||||
19
ansible/vars/routeros-secrets.yml
Normal file
19
ansible/vars/routeros-secrets.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
# Secret references only; actual values are loaded from OpenBao/Vault at runtime.
|
||||||
|
|
||||||
|
# KVv2 mount and secret path (full secret path is <mount>/data/<path>).
|
||||||
|
openbao_kv_mount: secret
|
||||||
|
|
||||||
|
# Field names expected in the OpenBao secret.
|
||||||
|
openbao_fields:
|
||||||
|
routeros_api:
|
||||||
|
path: routeros_api
|
||||||
|
username_key: username
|
||||||
|
password_key: password
|
||||||
|
wan_pppoe:
|
||||||
|
path: wan_pppoe
|
||||||
|
username_key: username
|
||||||
|
password_key: password
|
||||||
|
routeros_tailscale_container:
|
||||||
|
path: router_tailscale
|
||||||
|
container_password_key: container_password
|
||||||
@@ -4,6 +4,7 @@ let
|
|||||||
# Python with hvac package
|
# Python with hvac package
|
||||||
python = pkgs.python313.withPackages (python-pkgs: with python-pkgs; [
|
python = pkgs.python313.withPackages (python-pkgs: with python-pkgs; [
|
||||||
hvac
|
hvac
|
||||||
|
librouteros
|
||||||
]);
|
]);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -35,7 +36,6 @@ in
|
|||||||
openebs
|
openebs
|
||||||
browse-pvc
|
browse-pvc
|
||||||
]))
|
]))
|
||||||
ansible
|
|
||||||
fluxcd
|
fluxcd
|
||||||
restic
|
restic
|
||||||
openbao
|
openbao
|
||||||
@@ -59,4 +59,9 @@ in
|
|||||||
echo "Running tests"
|
echo "Running tests"
|
||||||
git --version | grep --color=auto "${pkgs.git.version}"
|
git --version | grep --color=auto "${pkgs.git.version}"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
languages.ansible.enable = true;
|
||||||
|
# TODO: automatically manage collections from ansible/requirements.yml
|
||||||
|
# For now, we need to manually install them with `ansible-galaxy collection install -r ansible/requirements.yml`
|
||||||
|
# This is not implemented in devenv
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user