summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Vazgenovich Shakaryan <dvshakaryan@gmail.com>2025-12-10 01:06:58 -0800
committerDavid Vazgenovich Shakaryan <dvshakaryan@gmail.com>2025-12-10 01:06:58 -0800
commit2157a8d75726c9d28a62c298df279e4ed917f16f (patch)
tree5f8016ef24fd0ab2782e2555f1f46a0e063e86cc
parentaf89e59722d7bb0df8f84828e007e225ce0aaafd (diff)
downloadwg-genconf-2157a8d75726c9d28a62c298df279e4ed917f16f.tar.gz
wg-genconf-2157a8d75726c9d28a62c298df279e4ed917f16f.tar.xz
support templated interface name and privkey path
-rwxr-xr-xwg-genconf.py53
1 files changed, 31 insertions, 22 deletions
diff --git a/wg-genconf.py b/wg-genconf.py
index adc81e9..3a8269b 100755
--- a/wg-genconf.py
+++ b/wg-genconf.py
@@ -8,8 +8,8 @@ import functools
import io
import ipaddress
import os
-import pathlib
import re
+import string
import shutil
import sys
import tomllib
@@ -55,8 +55,9 @@ class Interface(collections.UserDict):
'port',
'fwmark',
- 'prefix',
'file-prefix',
+ 'name-format',
+ 'privkey-path',
]
def __init__(self, data, name, peer):
@@ -84,11 +85,15 @@ class Interface(collections.UserDict):
return self.peer.network
@functools.cached_property
- def qualified_name(self):
- name = f'{self.get('prefix', '')}{self.network.name}'
- if self.name:
- name += f'-{self.name}'
- return name
+ def formatted_name(self):
+ return self.format_string(self.get('name-format', '$network$_if'))
+
+ def format_string(self, s):
+ return string.Template(s).substitute({
+ 'network': self.network.name,
+ 'peer': self.peer.name,
+ 'if': self.name,
+ '_if' : f'-{self.name}' if self.name else ''})
def deep_merge(d, src):
@@ -150,10 +155,10 @@ def expand_peerspecs(peer, peerspecs):
for x in (
auto_peerspecs(peer) if peerspec.get('auto') else [peerspec])]
-def gc_if_data(if_, privkeys):
+def gc_if_data(if_):
if_data = {
- 'name': if_.qualified_name,
- 'privkey': privkeys.get(if_.network.name, 'FIXME'),
+ 'name': if_.formatted_name,
+ 'privkey': get_privkey(if_),
'addrs': ipspecs_to_ips(
if_.peer, if_.get('ips', ['{peer/-}']), interface=True),
'port': if_.get('port'),
@@ -250,22 +255,22 @@ def buf_to_file(buf, path, mode=None):
buf.seek(0)
shutil.copyfileobj(buf, f)
-def create_if_files(if_, privkeys):
- if_data = gc_if_data(if_, privkeys)
+def create_if_files(if_):
+ if_data = gc_if_data(if_)
file_prefix = f'out/{if_.get('file-prefix', '')}'
if if_.get('type') == 'systemd':
buf_to_file(
gc_if_systemd_netdev(if_data),
- f'{file_prefix}{if_.qualified_name}.netdev',
+ f'{file_prefix}{if_.formatted_name}.netdev',
mode=0o640)
buf_to_file(
gc_if_systemd_network(if_data),
- f'{file_prefix}{if_.qualified_name}.network')
+ f'{file_prefix}{if_.formatted_name}.network')
else:
buf_to_file(
gc_if_wgquick(if_data),
- f'{file_prefix}{if_.qualified_name}.conf')
+ f'{file_prefix}{if_.formatted_name}.conf')
def load_network_peer_interfaces(config, peer):
ifs = peer.pop('if', {})
@@ -300,12 +305,17 @@ def load_networks(config):
load_network_peers(config, net)
return nets
-def load_privkeys():
- privkeys = {}
- for path in pathlib.Path.home().joinpath('.wg-genconf').glob('*.privkey'):
+@functools.cache
+def read_privkey(path):
+ try:
with open(path, 'r') as f:
- privkeys[path.stem] = f.read().rstrip()
- return privkeys
+ return f.read().rstrip()
+ except OSError:
+ return 'FIXME'
+
+def get_privkey(if_):
+ return read_privkey(if_.format_string(os.path.expanduser(
+ if_.get('privkey-path', '~/.wg-genconf/${network}.privkey'))))
def main():
conf_file = sys.argv[1]
@@ -314,7 +324,6 @@ def main():
with open(conf_file, 'rb') as f:
config = tomllib.load(f)
networks = load_networks(config)
- privkeys = load_privkeys()
if not config['peer'].get(peer_name):
sys.exit(f"peer '{peer_name}' not configured")
@@ -326,6 +335,6 @@ def main():
# config files, useful when an auto-peer interface is being
# separately created
if if_.get('file', True):
- create_if_files(if_, privkeys)
+ create_if_files(if_)
main()