Implemented support for configurable client configuration fragments

This commit is contained in:
Erik Thuning 2025-03-04 13:03:04 +01:00
parent 49abf8ad21
commit dc051e9439
2 changed files with 45 additions and 24 deletions

@ -1,8 +1,8 @@
from configparser import ConfigParser
from datetime import datetime
from pathlib import Path
from textwrap import dedent
from time import sleep
import configparser
import ipaddress
import json
import subprocess
@ -72,24 +72,23 @@ def generate_user_serverside_config(config_id: str,
user_name: str,
client_ip: ipaddress,
client_pubkey: str):
config = {}
config = ConfigParser(allow_no_value=True)
config['Peer'] = {
f'# {user_name}/{config_id}': None,
'PublicKey': client_pubkey,
'AllowedIPs': f'{client_ip}/32'
}
return dict_to_ini(config)
return config
def generate_user_clientside_config(client_ip: str,
client_privkey: str,
server_address: ipaddress,
server_port: int,
server_pubkey: str,
dns_server: str):
config = {}
fragment_file: Path=None):
config = ConfigParser()
config['Interface'] = {
'Address': f'{client_ip}/32',
'DNS': dns_server,
'PrivateKey': client_privkey
}
config['Peer'] = {
@ -97,7 +96,13 @@ def generate_user_clientside_config(client_ip: str,
'Endpoint': f'{server_address}:{server_port}',
'PublicKey': server_pubkey
}
return dict_to_ini(config)
if fragment_file:
fragment = ConfigParser()
fragment.read(fragment_file)
for section, contents in fragment.items():
for key, value in contents.items():
config[section][key] = value
return config
def run_wg(*args, input: str=None):
result = subprocess.run(['wg', *args],
@ -129,14 +134,16 @@ class WireGuard:
self.tunnel_id = config['tunnel_id']
self.server_address = ipaddress.ip_address(config['server_address'])
self.server_port = int(config['server_port'])
self.dns_server = ipaddress.ip_address(config['dns_server'])
self.client_network = ipaddress.ip_network(config['client_network'])
self.configs_base = Path(config['configs_base'])
self.max_clients = config.getint('user_client_limit', fallback=0)
self.server_config_base = None
if 'server_extra_config' in config.keys():
if 'server_extra_config' in config:
self.server_config_base = Path(config['server_extra_config'])
self.server_client_base = None
if 'client_extra_config' in config:
self.client_config_base = Path(config['client_extra_config'])
self.server_config_file = safe_join(workdir, self.tunnel_id + '.conf')
@ -193,16 +200,20 @@ class WireGuard:
return self.filepath(f'{config_id}{metasuffix}')
def generate_server_config(self):
config = {}
config = ConfigParser()
config['Interface'] = {
'Address': self.server_address,
'ListenPort': self.server_port,
'PrivateKey': self.server_privkey
}
server_config = dict_to_ini(config)
if self.server_config_base:
with open(self.server_config_base, 'r') as cb:
server_config += cb.read()
fragment = ConfigParser()
fragment.read(self.server_config_base)
for key, value in fragment['Interface'].items():
config['Interface'][key] = value
# We need to leave configparser-land here due to lots
# of duplicated sections when configuring peers
server_config = dict_to_ini(config)
for conffile in self.configs_base.glob('*/*'+serversuffix):
with open(conffile, 'r') as cf:
server_config += '\n' + cf.read()
@ -240,17 +251,23 @@ class WireGuard:
'minutes')}
mf.write(json.dumps(metadata))
cf.write(generate_user_clientside_config(client_ip,
client_privkey,
self.server_address,
self.server_port,
self.server_pubkey,
self.dns_server))
client_config = generate_user_clientside_config(
client_ip,
client_privkey,
self.server_address,
self.server_port,
self.server_pubkey,
self.client_config_base
)
client_config.write(cf)
sf.write(generate_user_serverside_config(config_id,
self.user_name,
client_ip,
client_pubkey))
server_config = generate_user_serverside_config(
config_id,
self.user_name,
client_ip,
client_pubkey
)
server_config.write(sf)
self.wg_updated = True
return client_ip
@ -286,7 +303,7 @@ class WireGuard:
if not path.exists():
raise FileNotFoundError(path)
config = configparser.ConfigParser()
config = ConfigParser()
config.read(config_path)
client_ip = config['Interface']['Address']

@ -24,6 +24,10 @@ client_network = a.network.in.cidr/notation
# Optional.
server_extra_config = path/to/a/conf/fragment
# Any extra configuration directives to include in client configs.
# Optional. Will override defaults.
client_extra_config = path/to/another/fragment
# The maximum number of clients to allow per user.
# Optional, defaults to unlimited, equivalent to setting this value to 0.
user_client_limit = 3