mirror of
https://github.com/karen07/openwrt-mesh-builder.git
synced 2026-10-19 15:33:30 +00:00
- generate router-*.mesh DNS records in dhcp_part from both router-level and access-group allow_to_router rules - expand allow_to_router=all to all other routers and merge explicit targets - preserve manual dhcp_part entries and remove stale generated DNS records - use anonymous UCI hostrecord sections for generated mesh DNS - switch collect_link_speeds.py to asyncio-based parallel measurements - add -j/--jobs to limit concurrent iperf3 measurements - prevent simultaneous measurements that share a router/server endpoint - make generated exit rule/route UCI sections anonymous - migrate legacy owmb_exit_rule_* and owmb_exit_route_* sections - update network validation for anonymous exit policy objects - include dhcp_part in generated/unmanaged file tracking - document mesh DNS behavior, parallel measurements and AWG access options
416 lines
14 KiB
Python
Executable file
416 lines
14 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
import sys
|
|
|
|
sys.dont_write_bytecode = True
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
try:
|
|
from .common import *
|
|
from .generate_firewall import update_firewall_part
|
|
from .generate_dhcp import update_dhcp_part
|
|
from .generate_router_misc import update_bootstrap
|
|
from .generate_access import build_access_state
|
|
from .generate_clients import (
|
|
generate_openvpn_access,
|
|
generate_openvpn_client_files,
|
|
generate_wireguard_client_files,
|
|
update_openvpn_uci,
|
|
)
|
|
from .generate_exit import (
|
|
build_exit_exit_server_conf,
|
|
build_exit_in_network_interface_block,
|
|
build_exit_ipip_interface_block,
|
|
build_exit_out_network_interface_block,
|
|
build_exit_rule_network_blocks,
|
|
build_material_for_exit,
|
|
build_material_for_exit_exit,
|
|
build_material_for_exit_reverse,
|
|
build_server_direct_conf,
|
|
build_server_reverse_conf,
|
|
)
|
|
from .generate_mesh import build_mesh_state
|
|
from .generate_network import update_babeld, update_network_part
|
|
from .generate_runtime import (
|
|
build_direct_ipset_lines,
|
|
write_router_ipsets,
|
|
write_server_ipsets,
|
|
)
|
|
from .generate_server_runtime import (
|
|
build_server_babeld_conf,
|
|
build_server_env,
|
|
ensure_server_layout,
|
|
reconcile_server_dirs,
|
|
remove_stale_server_amneziawg_confs,
|
|
)
|
|
from .tunnel_model import (
|
|
exit_hub_is_public,
|
|
router_is_public_mesh_hub,
|
|
)
|
|
except ImportError:
|
|
from common import *
|
|
from generate_firewall import update_firewall_part
|
|
from generate_dhcp import update_dhcp_part
|
|
from generate_router_misc import update_bootstrap
|
|
from generate_access import build_access_state
|
|
from generate_clients import (
|
|
generate_openvpn_access,
|
|
generate_openvpn_client_files,
|
|
generate_wireguard_client_files,
|
|
update_openvpn_uci,
|
|
)
|
|
from generate_exit import (
|
|
build_exit_exit_server_conf,
|
|
build_exit_in_network_interface_block,
|
|
build_exit_ipip_interface_block,
|
|
build_exit_out_network_interface_block,
|
|
build_exit_rule_network_blocks,
|
|
build_material_for_exit,
|
|
build_material_for_exit_exit,
|
|
build_material_for_exit_reverse,
|
|
build_server_direct_conf,
|
|
build_server_reverse_conf,
|
|
)
|
|
from generate_mesh import build_mesh_state
|
|
from generate_network import update_babeld, update_network_part
|
|
from generate_runtime import (
|
|
build_direct_ipset_lines,
|
|
write_router_ipsets,
|
|
write_server_ipsets,
|
|
)
|
|
from generate_server_runtime import (
|
|
build_server_babeld_conf,
|
|
build_server_env,
|
|
ensure_server_layout,
|
|
reconcile_server_dirs,
|
|
remove_stale_server_amneziawg_confs,
|
|
)
|
|
from tunnel_model import (
|
|
exit_hub_is_public,
|
|
router_is_public_mesh_hub,
|
|
)
|
|
|
|
|
|
# ============================================================
|
|
# ACCESS OPENVPN HELPERS
|
|
# ============================================================
|
|
|
|
|
|
# ============================================================
|
|
# MAIN
|
|
# ============================================================
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> None:
|
|
ap = argparse.ArgumentParser(
|
|
description=(
|
|
"Generator: mesh/exit AmneziaWG + access WireGuard/AmneziaWG/OpenVPN + "
|
|
"babeld + firewall + bootstrap updates"
|
|
)
|
|
)
|
|
ap.add_argument(
|
|
"--config",
|
|
default=str(CONFIG_PATH),
|
|
help="path to JSON config file (default: config.json)",
|
|
)
|
|
ap.add_argument(
|
|
"--force",
|
|
action="store_true",
|
|
help="regenerate mesh/exit keys; access secrets are preserved",
|
|
)
|
|
ap.add_argument(
|
|
"--verbose",
|
|
action="store_true",
|
|
help="print detailed generation information",
|
|
)
|
|
ap.add_argument(
|
|
"--skip-direct-downloads",
|
|
action="store_true",
|
|
help=(
|
|
"do not download dynamic direct-list country/ASN IP sets; "
|
|
"generated direct.txt will contain only static direct entries"
|
|
),
|
|
)
|
|
args = ap.parse_args(argv)
|
|
|
|
raw_cfg = load_json_config(Path(args.config))
|
|
cfg = build_config_data(raw_cfg)
|
|
static_direct_lines, direct_lines = build_direct_ipset_lines(
|
|
cfg,
|
|
skip_dynamic_downloads=args.skip_direct_downloads,
|
|
)
|
|
|
|
need("wg", "openssl")
|
|
existing = load_existing_network_cfgs(cfg)
|
|
|
|
secrets_force = args.force
|
|
access_force = False
|
|
|
|
access_blocks, access_states, access_names = build_access_state(
|
|
cfg=cfg,
|
|
existing_all=existing,
|
|
access_groups=cfg.access,
|
|
force=access_force,
|
|
verbose=args.verbose,
|
|
)
|
|
|
|
generate_openvpn_access(cfg=cfg, force=access_force, verbose=args.verbose)
|
|
generate_openvpn_client_files(cfg=cfg, force=access_force, verbose=args.verbose)
|
|
generate_wireguard_client_files(
|
|
cfg=cfg,
|
|
states_by_router=access_states,
|
|
verbose=args.verbose,
|
|
)
|
|
|
|
keep_exit_hubs = {h.name for h in cfg.exit_hubs}
|
|
reconcile_server_dirs(keep_exit_hubs)
|
|
mesh_blocks, mesh_ifaces, _mesh_states = build_mesh_state(
|
|
cfg=cfg,
|
|
existing_all=existing,
|
|
force=secrets_force,
|
|
verbose=args.verbose,
|
|
)
|
|
|
|
router_exit_blocks: dict[str, dict[str, str]] = {r: {} for r in cfg.router_names}
|
|
router_exit_ipip_blocks: dict[str, dict[str, str]] = {
|
|
r: {} for r in cfg.router_names
|
|
}
|
|
router_exit_ifaces: dict[str, list[str]] = {r: [] for r in cfg.router_names}
|
|
router_exit_ipip_ifaces: dict[str, list[str]] = {r: [] for r in cfg.router_names}
|
|
hub_states: dict[str, list[RouterExitState]] = {h.name: [] for h in cfg.exit_hubs}
|
|
exit_exit_aliases_by_hub: dict[str, list[str]] = {h.name: [] for h in cfg.exit_hubs}
|
|
|
|
for hub in cfg.exit_hubs:
|
|
ensure_server_layout(hub.name)
|
|
|
|
for left_name, right_name in exit_exit_link_pairs(cfg):
|
|
left_hub = cfg.exit_hubs_by_name[left_name]
|
|
right_hub = cfg.exit_hubs_by_name[right_name]
|
|
left_alias = build_exit_exit_alias(cfg, left_hub.name, right_hub.name)
|
|
right_alias = build_exit_exit_alias(cfg, right_hub.name, left_hub.name)
|
|
link = compute_exit_exit_link_params(cfg, left_hub, right_hub)
|
|
key = exit_exit_link_key(left_hub.name, right_hub.name)
|
|
awg = awg_for_infra_link(key)
|
|
keys = build_material_for_exit_exit(
|
|
cfg=cfg,
|
|
left_hub=left_hub,
|
|
right_hub=right_hub,
|
|
force=secrets_force,
|
|
)
|
|
|
|
write(
|
|
server_client_conf_path(left_hub.name, left_alias),
|
|
build_exit_exit_server_conf(left_hub, right_hub, link, keys, awg),
|
|
)
|
|
write(
|
|
server_client_conf_path(right_hub.name, right_alias),
|
|
build_exit_exit_server_conf(right_hub, left_hub, link, keys, awg),
|
|
)
|
|
exit_exit_aliases_by_hub[left_hub.name].append(left_alias)
|
|
exit_exit_aliases_by_hub[right_hub.name].append(right_alias)
|
|
|
|
if args.verbose:
|
|
print(
|
|
f"EXIT-EXIT {left_hub.name:<8} Out -> {right_hub.name:<8} In "
|
|
f"aliases={left_alias}/{right_alias} "
|
|
f"ports={link.left_port}/{link.right_port}"
|
|
)
|
|
|
|
for hub in cfg.exit_hubs:
|
|
for router_name in cfg.router_names:
|
|
router_cfg = existing[router_name]
|
|
blocks: list[str] = []
|
|
|
|
if exit_hub_is_public(hub):
|
|
client_alias = build_exit_client_alias(cfg, hub.name, router_name)
|
|
link = compute_exit_link_params(cfg, hub, router_name)
|
|
awg = awg_for_infra_link(exit_link_key(hub.name, router_name))
|
|
iface_name = exit_out_iface_name(hub.name)
|
|
|
|
keys = build_material_for_exit(
|
|
router_name=router_name,
|
|
hub=hub,
|
|
client_alias=client_alias,
|
|
router_iface_name=iface_name,
|
|
router_cfg=router_cfg,
|
|
force=secrets_force,
|
|
)
|
|
|
|
write(
|
|
server_client_conf_path(hub.name, client_alias),
|
|
build_server_direct_conf(client_alias, hub, link, keys, awg),
|
|
)
|
|
blocks.append(
|
|
build_exit_out_network_interface_block(hub, link, keys, awg)
|
|
)
|
|
router_exit_ifaces[router_name].append(iface_name)
|
|
|
|
hub_states[hub.name].append(
|
|
RouterExitState(
|
|
router_name=router_name,
|
|
client_alias=client_alias,
|
|
hub=hub,
|
|
link=link,
|
|
keys=keys,
|
|
awg=awg,
|
|
)
|
|
)
|
|
|
|
if args.verbose:
|
|
print(
|
|
f"EXIT-OUT {router_name:<10} -> {hub.name:<8} "
|
|
f"alias={client_alias:<16} port={link.port:<5}"
|
|
)
|
|
|
|
if router_is_public_mesh_hub(cfg, router_name):
|
|
client_alias = build_exit_reverse_client_alias(
|
|
cfg, hub.name, router_name
|
|
)
|
|
link = compute_exit_reverse_link_params(cfg, hub, router_name)
|
|
key = exit_reverse_link_key(hub.name, router_name)
|
|
awg = awg_for_infra_link(key)
|
|
iface_name = exit_in_iface_name(hub.name)
|
|
|
|
keys = build_material_for_exit_reverse(
|
|
hub=hub,
|
|
client_alias=client_alias,
|
|
router_iface_name=iface_name,
|
|
router_cfg=router_cfg,
|
|
force=secrets_force,
|
|
)
|
|
|
|
write(
|
|
server_client_conf_path(hub.name, client_alias),
|
|
build_server_reverse_conf(
|
|
cfg, router_name, client_alias, hub, link, keys, awg
|
|
),
|
|
)
|
|
blocks.append(
|
|
build_exit_in_network_interface_block(hub, link, keys, awg)
|
|
)
|
|
router_exit_ifaces[router_name].append(iface_name)
|
|
|
|
hub_states[hub.name].append(
|
|
RouterExitState(
|
|
router_name=router_name,
|
|
client_alias=client_alias,
|
|
hub=hub,
|
|
link=link,
|
|
keys=keys,
|
|
awg=awg,
|
|
)
|
|
)
|
|
|
|
if args.verbose:
|
|
print(
|
|
f"EXIT-IN {hub.name:<8} -> {router_name:<10} "
|
|
f"alias={client_alias:<16} port={link.port:<5}"
|
|
)
|
|
|
|
if blocks:
|
|
router_exit_blocks[router_name][hub.name] = "\n".join(
|
|
block.strip() for block in blocks
|
|
)
|
|
|
|
router_exit_ipip_blocks[router_name][hub.name] = (
|
|
build_exit_ipip_interface_block(
|
|
cfg,
|
|
router_name,
|
|
hub,
|
|
)
|
|
)
|
|
router_exit_ipip_ifaces[router_name].append(
|
|
router_exit_ipip_iface_name(hub.name)
|
|
)
|
|
|
|
keep_server_aliases: set[str] = set(exit_exit_aliases_by_hub[hub.name])
|
|
if exit_hub_is_public(hub):
|
|
keep_server_aliases |= {
|
|
build_exit_client_alias(cfg, hub.name, router_name)
|
|
for router_name in cfg.router_names
|
|
}
|
|
keep_server_aliases |= {
|
|
build_exit_reverse_client_alias(cfg, hub.name, mesh_hub.name)
|
|
for mesh_hub in cfg.mesh_hubs
|
|
}
|
|
remove_stale_server_amneziawg_confs(hub.name, keep_server_aliases)
|
|
|
|
write(
|
|
server_babeld_conf_path(hub.name),
|
|
build_server_babeld_conf(
|
|
hub, hub_states[hub.name], exit_exit_aliases_by_hub[hub.name]
|
|
),
|
|
)
|
|
write(
|
|
server_path(hub.name, "etc", "awg-server.env"),
|
|
build_server_env(
|
|
cfg, hub, hub_states[hub.name], exit_exit_aliases_by_hub[hub.name]
|
|
),
|
|
)
|
|
write_server_ipsets(
|
|
cfg,
|
|
hub.name,
|
|
static_lines=static_direct_lines,
|
|
direct_lines=direct_lines,
|
|
)
|
|
|
|
for router_name in cfg.router_names:
|
|
mesh_text = mesh_blocks[router_name].strip()
|
|
exit_text = "\n".join(
|
|
router_exit_blocks[router_name][hub.name].strip()
|
|
for hub in cfg.exit_hubs
|
|
if hub.name in router_exit_blocks[router_name]
|
|
).strip()
|
|
ipip_text = "\n".join(
|
|
router_exit_ipip_blocks[router_name][hub.name].strip()
|
|
for hub in router_required_exit_hubs(cfg, router_name)
|
|
if hub.name in router_exit_ipip_blocks[router_name]
|
|
).strip()
|
|
exit_rule_text = build_exit_rule_network_blocks(cfg, router_name).strip()
|
|
access_text = access_blocks.get(router_name, "").strip()
|
|
|
|
update_network_part(
|
|
cfg=cfg,
|
|
router_name=router_name,
|
|
mesh_text=mesh_text,
|
|
exit_text=exit_text,
|
|
ipip_text=ipip_text,
|
|
exit_rule_text=exit_rule_text,
|
|
access_text=access_text,
|
|
access_names=access_names,
|
|
)
|
|
update_babeld(
|
|
cfg=cfg,
|
|
router_name=router_name,
|
|
mesh_ifaces=mesh_ifaces[router_name],
|
|
exit_ifaces=sorted(set(router_exit_ifaces[router_name])),
|
|
)
|
|
active_exit_ipip_ifaces = [
|
|
router_exit_ipip_iface_name(hub.name)
|
|
for hub in router_required_exit_hubs(cfg, router_name)
|
|
]
|
|
update_firewall_part(
|
|
cfg=cfg,
|
|
router_name=router_name,
|
|
mesh_ifaces=mesh_ifaces[router_name],
|
|
exit_ifaces=sorted(set(router_exit_ifaces[router_name])),
|
|
exit_ipip_ifaces=sorted(set(active_exit_ipip_ifaces)),
|
|
access_groups_for_router=cfg.access.get(router_name, []),
|
|
)
|
|
update_dhcp_part(cfg=cfg, router_name=router_name)
|
|
update_bootstrap(cfg=cfg, router_name=router_name)
|
|
update_openvpn_uci(
|
|
cfg=cfg,
|
|
router_name=router_name,
|
|
groups=cfg.access.get(router_name, []),
|
|
)
|
|
write_router_ipsets(
|
|
cfg,
|
|
router_name,
|
|
static_lines=static_direct_lines,
|
|
direct_lines=direct_lines,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|