From a2f81b4057a00e0e51dc2d9599acb48b5371458a Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Sun, 16 Nov 2025 21:33:04 +0800 Subject: [PATCH] added support for multi-server networks --- pyproject.toml | 4 +- requirements-dev.txt | 106 ++++++++++++++++++++------------------ requirements.txt | 8 +-- src/wg_builder/builder.py | 34 ++++++++++-- 4 files changed, 90 insertions(+), 62 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 51f08ab..35fd5f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "wg_builder" -version = "0.0.1" +version = "0.0.2" authors = [ { name="Walter Oggioni", email="oggioni.walter@gmail.com" }, ] @@ -50,4 +50,4 @@ no_implicit_optional = true warn_return_any = true warn_unused_ignores = true exclude = ["scripts", "docs", "test"] -strict = true \ No newline at end of file +strict = true diff --git a/requirements-dev.txt b/requirements-dev.txt index 61c60ca..7a9718c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,102 +1,107 @@ # -# This file is autogenerated by pip-compile with Python 3.12 +# This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --extra=dev --output-file=requirements-dev.txt pyproject.toml +# pip-compile --allow-unsafe --extra=dev --output-file=requirements-dev.txt # --index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple --extra-index-url https://pypi.org/simple -asttokens==2.4.1 +asttokens==3.0.1 # via stack-data -build==1.2.2 +build==1.3.0 # via # pip-tools # wg_builder (pyproject.toml) -certifi==2024.8.30 +certifi==2025.11.12 # via requests -cffi==1.17.1 +cffi==2.0.0 # via cryptography -charset-normalizer==3.3.2 +charset-normalizer==3.4.4 # via requests -click==8.1.7 +click==8.3.1 # via pip-tools -cryptography==43.0.1 +cryptography==46.0.3 # via secretstorage -decorator==5.1.1 +decorator==5.2.1 # via # ipdb # ipython -docutils==0.21.2 +docutils==0.22.3 # via readme-renderer -executing==2.1.0 +executing==2.2.1 # via stack-data -idna==3.8 - # via requests -importlib-metadata==8.4.0 +id==1.5.0 # via twine +idna==3.11 + # via requests ipdb==0.13.13 # via wg_builder (pyproject.toml) -ipython==8.27.0 +ipython==9.7.0 # via ipdb +ipython-pygments-lexers==1.1.1 + # via ipython jaraco-classes==3.4.0 # via keyring jaraco-context==6.0.1 # via keyring -jaraco-functools==4.0.2 +jaraco-functools==4.3.0 # via keyring -jedi==0.19.1 +jedi==0.19.2 # via ipython -jeepney==0.8.0 +jeepney==0.9.0 # via # keyring # secretstorage -keyring==25.3.0 +keyring==25.6.0 # via twine -markdown-it-py==3.0.0 +markdown-it-py==4.0.0 # via rich -matplotlib-inline==0.1.7 +matplotlib-inline==0.2.1 # via ipython mdurl==0.1.2 # via markdown-it-py -more-itertools==10.5.0 +more-itertools==10.8.0 # via # jaraco-classes # jaraco-functools -mypy==1.11.2 +mypy==1.18.2 # via wg_builder (pyproject.toml) -mypy-extensions==1.0.0 +mypy-extensions==1.1.0 # via mypy netaddr==1.3.0 # via wg_builder (pyproject.toml) -nh3==0.2.18 +nh3==0.3.2 # via readme-renderer -packaging==24.1 - # via build -parso==0.8.4 +packaging==25.0 + # via + # build + # twine +parso==0.8.5 # via jedi +pathspec==0.12.1 + # via mypy pexpect==4.9.0 # via ipython -pip-tools==7.4.1 +pip-tools==7.5.2 # via wg_builder (pyproject.toml) -pkginfo==1.10.0 - # via twine -prompt-toolkit==3.0.47 +prompt-toolkit==3.0.52 # via ipython ptyprocess==0.7.0 # via pexpect pure-eval==0.2.3 # via stack-data -pwo==0.0.2 +pwo==0.0.7 # via wg_builder (pyproject.toml) -pycparser==2.22 +pycparser==2.23 # via cffi -pygments==2.18.0 +pygments==2.19.2 # via # ipython + # ipython-pygments-lexers # readme-renderer # rich -pyproject-hooks==1.1.0 +pyproject-hooks==1.2.0 # via # build # pip-tools @@ -104,20 +109,19 @@ python-wireguard==0.2.2 # via wg_builder (pyproject.toml) readme-renderer==44.0 # via twine -requests==2.32.3 +requests==2.32.5 # via + # id # requests-toolbelt # twine requests-toolbelt==1.0.0 # via twine rfc3986==2.0.0 # via twine -rich==13.8.0 +rich==14.2.0 # via twine -secretstorage==3.3.3 +secretstorage==3.4.1 # via keyring -six==1.16.0 - # via asttokens stack-data==0.6.3 # via ipython toml==0.10.2 @@ -126,24 +130,24 @@ traitlets==5.14.3 # via # ipython # matplotlib-inline -twine==5.1.1 +twine==6.2.0 # via wg_builder (pyproject.toml) -typing-extensions==4.7.1 +typing-extensions==4.15.0 # via # mypy # pwo # wg_builder (pyproject.toml) -urllib3==2.2.2 +urllib3==2.5.0 # via # requests # twine -wcwidth==0.2.13 +wcwidth==0.2.14 # via prompt-toolkit -wheel==0.44.0 +wheel==0.45.1 # via pip-tools -zipp==3.20.1 - # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -# pip -# setuptools +pip==25.3 + # via pip-tools +setuptools==80.9.0 + # via pip-tools diff --git a/requirements.txt b/requirements.txt index ba7a8d8..9bf3137 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,21 @@ # -# This file is autogenerated by pip-compile with Python 3.12 +# This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --output-file=requirements.txt pyproject.toml +# pip-compile --allow-unsafe --output-file=requirements.txt # --index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple --extra-index-url https://pypi.org/simple netaddr==1.3.0 # via wg_builder (pyproject.toml) -pwo==0.0.2 +pwo==0.0.7 # via wg_builder (pyproject.toml) python-wireguard==0.2.2 # via wg_builder (pyproject.toml) toml==0.10.2 # via wg_builder (pyproject.toml) -typing-extensions==4.7.1 +typing-extensions==4.15.0 # via # pwo # wg_builder (pyproject.toml) diff --git a/src/wg_builder/builder.py b/src/wg_builder/builder.py index 68c3eee..a744dfc 100644 --- a/src/wg_builder/builder.py +++ b/src/wg_builder/builder.py @@ -60,11 +60,24 @@ def build_network(input_file: Path = Path('/dev/stdin'), output_folder: Path = P self.private_key, self.public_key = Key.key_pair() self.allowed_ip = str(address_it.__next__()) - servers = [Server(server) for server in configuration['server']] + servers = [Server(server) for server in configuration.get('server', tuple())] - peers = [Peer(peer) for peer in configuration['peer']] + peers = [Peer(peer) for peer in configuration.get('peer', tuple())] - pre_shared_keys = {(server, peer): genPsk() for peer in peers for server in servers} + def link_id(peer1: any, peer2: any) -> tuple[int, int]: + table_key = [id(peer1), id(peer2)] + table_key.sort() + return tuple(table_key) + + pre_shared_keys = {} + + for server in servers: + for server2 in servers: + if not server is server2: + pre_shared_keys.setdefault(link_id(server, server2), genPsk()) + for peer in peers: + pre_shared_keys.setdefault(link_id(server, peer), genPsk()) + def write_route_add_script(file_obj): text = dedent(f"""\ @@ -88,13 +101,24 @@ def build_network(input_file: Path = Path('/dev/stdin'), output_folder: Path = P PrivateKey = {str(server.private_key)} """) file_obj.write(text) + for peer in servers: + if not peer is server: + text = dedent(f"""\ + + [Peer] + PublicKey = {peer.public_key} + AllowedIPs = {peer.address}/32 + PresharedKey = {pre_shared_keys[link_id(server, peer)]} + Endpoint = {peer.endpoint} + """) + file_obj.write(text) for peer in peers: text = dedent(f"""\ [Peer] PublicKey = {peer.public_key} AllowedIPs = {peer.allowed_ip}/32 - PresharedKey = {pre_shared_keys[(server, peer)]} + PresharedKey = {pre_shared_keys[link_id(server, peer)]} """) file_obj.write(text) @@ -114,7 +138,7 @@ def build_network(input_file: Path = Path('/dev/stdin'), output_folder: Path = P [Peer] PublicKey = {server.public_key} - PresharedKey = {pre_shared_keys[(server, peer)]} + PresharedKey = {pre_shared_keys[link_id(server, peer)]} Endpoint = {server.endpoint}:{server.port} AllowedIPs = {str(network)} """)