diff options
| author | David Vazgenovich Shakaryan <dvshakaryan@gmail.com> | 2026-08-06 01:43:02 -0700 |
|---|---|---|
| committer | David Vazgenovich Shakaryan <dvshakaryan@gmail.com> | 2026-08-06 01:48:03 -0700 |
| commit | 508efbdd70b76511f3f422f434be284d86196659 (patch) | |
| tree | 0ba8fa925d7dec94b44b0f861701d7516921fe88 | |
| parent | 6220653e3ea18a4a202cb5544abdbd7342b2bcf5 (diff) | |
| download | wg-genconf-508efbdd70b76511f3f422f434be284d86196659.tar.gz wg-genconf-508efbdd70b76511f3f422f434be284d86196659.tar.xz | |
| -rwxr-xr-x | wg-genconf.py | 51 |
1 files changed, 38 insertions, 13 deletions
diff --git a/wg-genconf.py b/wg-genconf.py index a8e8886..d02766b 100755 --- a/wg-genconf.py +++ b/wg-genconf.py @@ -51,6 +51,11 @@ class Peer(dict): for x in self.network['subnets'] if not version or x.version == int(version)] + def format_string(self, s): + return string.Template(s).safe_substitute({ + 'network': self.network.name, + 'peer': self.name}) + class Interface(collections.UserDict): _INHERITABLE = [ @@ -58,9 +63,9 @@ class Interface(collections.UserDict): 'fwmark', 'keepalive', - 'file-prefix', + 'keep-empty', 'name-format', - 'privkey-path', + 'systemd-prefix', ] def __init__(self, data, name, peer): @@ -90,10 +95,10 @@ class Interface(collections.UserDict): @functools.cached_property def formatted_name(self): - return self.format_string(self.get('name-format', '$network$_if')) + return self.format_string(self.get('name-format', 'wg$network$_if')) def format_string(self, s): - return string.Template(s).substitute({ + return string.Template(s).safe_substitute({ 'network': self.network.name, 'peer': self.peer.name, 'if': self.name, @@ -170,6 +175,12 @@ def expand_peerspecs(peer, peerspecs): def gc_if_data(if_): + if not re.fullmatch(r'[A-Za-z0-9+.=_-]{1,15}', if_.formatted_name): + print( + f'warning: interface {if_.formatted_name}: ' + f'invalid name, must match [A-Za-z0-9+.=_-]{{1,15}}', + file=sys.stderr) + if_data = { 'name': if_.formatted_name, 'privkey': get_privkey(if_), @@ -277,7 +288,7 @@ def gc_if_systemd_netdev(if_data): def buf_to_file(buf, path, *, mode=None): - print(f'Creating file {path}') + print(f'creating {path}') if mode: path = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, mode=mode) @@ -288,20 +299,22 @@ def buf_to_file(buf, path, *, mode=None): def create_if_files(if_, output_dir): if_data = gc_if_data(if_) - prefix = os.path.join(output_dir, if_.get('file-prefix', '')) if if_.get('type') == 'systemd': + prefix = if_.get('systemd-prefix', '99') + prefix = '' if prefix is None else f'{prefix}-' + path = os.path.join(output_dir, f'{prefix}{if_data['name']}') buf_to_file( gc_if_systemd_netdev(if_data), - f'{prefix}{if_.formatted_name}.netdev', + f'{path}.netdev', mode=0o640) buf_to_file( gc_if_systemd_network(if_data), - f'{prefix}{if_.formatted_name}.network') + f'{path}.network') else: buf_to_file( gc_if_wgquick(if_data), - f'{prefix}{if_.formatted_name}.conf') + os.path.join(output_dir, f'{if_data['name']}.conf')) def load_network_peer_interfaces(config, peer): @@ -310,7 +323,7 @@ def load_network_peer_interfaces(config, peer): for if_name, if_conf in ifs.items(): # remove self from interface peers peers = [x for x in if_conf['peers'] if x.get('name') != peer.name] - if not peers: + if not peers and not if_conf.get('keep-empty'): continue if_conf['peers'] = peers @@ -342,12 +355,24 @@ def read_privkey(path): with open(path) as f: return f.read().rstrip() except OSError: - return 'FIXME' + return None def get_privkey(if_): - return read_privkey(if_.format_string(os.path.expanduser( - if_.get('privkey-path', '~/.wg-genconf/${network}.privkey')))) + path = if_.peer.get( + 'privkey-path', '~/.wg-genconf/${peer}/${network}.privkey') + if path: + path = if_.peer.format_string(os.path.expanduser(path)) + + key = read_privkey(path) if path else None + if key is None: + print( + f'warning: interface {if_.formatted_name}: ' + f'failed to read privkey at {path}', + file=sys.stderr) + return 'FIXME' + + return key def argparse_dir(s): |
