new calculation approach with unique sessions, new API endpoint to get list of active sessions, fix for UNDEF user, UI and Back to support certificate management still under development
This commit is contained in:
155
APP/config_manager.py
Normal file
155
APP/config_manager.py
Normal file
@@ -0,0 +1,155 @@
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
class ConfigManager:
|
||||
def __init__(self, template_dir, output_dir):
|
||||
self.template_dir = template_dir
|
||||
self.output_dir = output_dir
|
||||
self.env = Environment(loader=FileSystemLoader(template_dir))
|
||||
self.server_conf_path = Path(output_dir) / "server.conf"
|
||||
|
||||
def read_server_config(self):
|
||||
"""Parse existing server config into a dictionary"""
|
||||
if not self.server_conf_path.exists():
|
||||
return {}
|
||||
|
||||
config = {}
|
||||
try:
|
||||
with open(self.server_conf_path, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Regex mappings for simple key-value pairs
|
||||
mappings = {
|
||||
'port': r'^port\s+(\d+)',
|
||||
'proto': r'^proto\s+(\w+)',
|
||||
'dev': r'^dev\s+(\w+)',
|
||||
'server_network': r'^server\s+([\d\.]+)',
|
||||
'server_netmask': r'^server\s+[\d\.]+\s+([\d\.]+)',
|
||||
'topology': r'^topology\s+(\w+)',
|
||||
'cipher': r'^cipher\s+([\w\-]+)',
|
||||
'data_ciphers': r'^data-ciphers\s+([\w\-:]+)',
|
||||
'data_ciphers_fallback': r'^data-ciphers-fallback\s+([\w\-]+)',
|
||||
'status_log': r'^status\s+(.+)',
|
||||
'log_file': r'^log-append\s+(.+)',
|
||||
'ipp_path': r'^ifconfig-pool-persist\s+(.+)',
|
||||
'auth_algo': r'^auth\s+(\w+)',
|
||||
'tun_mtu': r'^tun-mtu\s+(\d+)',
|
||||
'mssfix': r'^mssfix\s+(\d+)'
|
||||
}
|
||||
|
||||
for key, pattern in mappings.items():
|
||||
match = re.search(pattern, content, re.MULTILINE)
|
||||
if match:
|
||||
config[key] = match.group(1)
|
||||
|
||||
# Boolean flags
|
||||
config['client_to_client'] = bool(re.search(r'^client-to-client', content, re.MULTILINE))
|
||||
# redirect-gateway is usually pushed
|
||||
config['redirect_gateway'] = bool(re.search(r'push "redirect-gateway', content, re.MULTILINE))
|
||||
config['crl_verify'] = bool(re.search(r'^crl-verify', content, re.MULTILINE))
|
||||
|
||||
# DNS
|
||||
# push "dhcp-option DNS 8.8.8.8"
|
||||
dns_matches = re.findall(r'push "dhcp-option DNS ([\d\.]+)"', content)
|
||||
if dns_matches:
|
||||
config['dns_servers'] = dns_matches
|
||||
|
||||
# Routes
|
||||
# push "route 192.168.1.0 255.255.255.0"
|
||||
route_matches = re.findall(r'push "route ([\d\.]+ [\d\.]+)"', content)
|
||||
if route_matches:
|
||||
config['routes'] = route_matches
|
||||
|
||||
return config
|
||||
except Exception as e:
|
||||
print(f"Error reading config: {e}")
|
||||
return {}
|
||||
|
||||
def generate_server_config(self, params):
|
||||
"""Generate server.conf from template"""
|
||||
# Defaults
|
||||
defaults = {
|
||||
'port': 1194,
|
||||
'proto': 'udp',
|
||||
'server_network': '10.8.0.0',
|
||||
'server_netmask': '255.255.255.0',
|
||||
'topology': 'subnet',
|
||||
'cipher': 'AES-256-GCM',
|
||||
'auth_algo': 'SHA256',
|
||||
'data_ciphers': 'AES-256-GCM:AES-128-GCM',
|
||||
'data_ciphers_fallback': None,
|
||||
'status_log': '/var/log/openvpn/openvpn-status.log',
|
||||
'log_file': '/var/log/openvpn/openvpn.log',
|
||||
'crl_verify': True,
|
||||
'client_to_client': False,
|
||||
'redirect_gateway': True,
|
||||
'dns_servers': ['8.8.8.8', '8.8.4.4'],
|
||||
'routes': [],
|
||||
'tun_mtu': None,
|
||||
'mssfix': None
|
||||
}
|
||||
|
||||
# Merge params
|
||||
ctx = {**defaults, **params}
|
||||
|
||||
try:
|
||||
template = self.env.get_template('server.conf.j2')
|
||||
output = template.render(ctx)
|
||||
|
||||
with open(self.server_conf_path, 'w') as f:
|
||||
f.write(output)
|
||||
|
||||
return True, str(self.server_conf_path)
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
def generate_client_config(self, client_name, pki_path, server_config=None, extra_params=None):
|
||||
"""Generate client .ovpn content
|
||||
server_config: dict of server security/network settings
|
||||
extra_params: dict of specific overrides (remote_host, port, proto)
|
||||
"""
|
||||
# Checks
|
||||
pki = Path(pki_path)
|
||||
ca_path = pki / "ca.crt"
|
||||
cert_path = pki / "issued" / f"{client_name}.crt"
|
||||
key_path = pki / "private" / f"{client_name}.key"
|
||||
ta_path = pki / "ta.key"
|
||||
|
||||
if not (ca_path.exists() and cert_path.exists() and key_path.exists()):
|
||||
return False, "Certificate files missing"
|
||||
|
||||
try:
|
||||
# Read contents
|
||||
ca = ca_path.read_text().strip()
|
||||
cert = cert_path.read_text().strip()
|
||||
# Cert file often contains text before -----BEGIN CERTIFICATE-----
|
||||
if "-----BEGIN CERTIFICATE-----" in cert:
|
||||
cert = cert[cert.find("-----BEGIN CERTIFICATE-----"):]
|
||||
|
||||
key = key_path.read_text().strip()
|
||||
ta = ta_path.read_text().strip() if ta_path.exists() else None
|
||||
|
||||
ctx = {
|
||||
'client_name': client_name,
|
||||
'ca': ca,
|
||||
'cert': cert,
|
||||
'key': key,
|
||||
'tls_auth': ta
|
||||
}
|
||||
|
||||
# Merge server config if present
|
||||
if server_config:
|
||||
ctx.update(server_config)
|
||||
|
||||
# Merge extra params (host, port, proto) - takes precedence
|
||||
if extra_params:
|
||||
ctx.update(extra_params)
|
||||
|
||||
template = self.env.get_template('client.ovpn.j2')
|
||||
output = template.render(ctx)
|
||||
return True, output
|
||||
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
Reference in New Issue
Block a user