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:
Антон
2026-01-12 11:44:50 +03:00
parent 839dd4994f
commit 6df0f5e180
10 changed files with 1175 additions and 59 deletions

155
APP/config_manager.py Normal file
View 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)