Files
OpenVPN-Monitoring-Simple/APP_PROFILER/services/generator.py
T
iclaoudezinandClaude Sonnet 5.5 11c1b6379b Harden auth and API: username change, 2FA fixes, input validation
- Add POST /api/auth/change-username (password + OTP when 2FA is on,
  format/reserved-name checks, uniqueness) and a Change Username modal
  in Account.vue; use the real username for the 2FA provisioning URI.
- Stop creating the built-in admin/password user; the initial admin is
  seeded only from OVPMON_INITIAL_ADMIN_USER/PASSWORD.
- Reject 2FA-pending temporary tokens on all protected routes (Flask
  token_required, Profiler verify_token); only /api/auth/verify-2fa
  accepts them.
- Stop logging the OTP and TOTP secret in enable_2fa.
- Profiler: validate profile username (pattern + realpath checks in
  schema, router, pki and generator) to prevent path traversal.
- Restrict CORS to the panel origin in Profiler and Flask APIs.
- UI: header username no longer sticks to the hardcoded Admin fallback;
  it is synced from /user/me and updated after a rename.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 12:03:36 +00:00

116 lines
4.1 KiB
Python

import os
import logging
from jinja2 import Environment, FileSystemLoader
from sqlalchemy.orm import Session
from .config import get_system_settings, get_pki_settings
from .pki import PKI_DIR
logger = logging.getLogger(__name__)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATES_DIR = os.path.join(BASE_DIR, "templates")
env = Environment(loader=FileSystemLoader(TEMPLATES_DIR))
def generate_server_config(db: Session, output_path: str = "server.conf"):
settings = get_system_settings(db)
pki_settings = get_pki_settings(db)
template = env.get_template("server.conf.j2")
# Rendering Path
file_ca_path = os.path.join(PKI_DIR, "ca.crt")
file_srv_cert_path = os.path.join(PKI_DIR, "issued", f"{pki_settings.fqdn_server}.crt")
file_srv_key_path = os.path.join(PKI_DIR, "private", f"{pki_settings.fqdn_server}.key")
file_dh_path = os.path.join(PKI_DIR, "dh.pem")
file_ta_path = os.path.join(PKI_DIR, "ta.key")
file_crl_path = os.path.join(PKI_DIR, "crl.pem")
# Render template
config_content = template.render(
protocol=settings.protocol,
port=settings.port,
ca_path=file_ca_path,
srv_cert_path=file_srv_cert_path,
srv_key_path=file_srv_key_path,
dh_path=file_dh_path,
ta_path=file_ta_path,
crl_path=file_crl_path,
vpn_network=settings.vpn_network,
vpn_netmask=settings.vpn_netmask,
tunnel_type=settings.tunnel_type,
split_routes=settings.split_routes,
user_defined_dns=settings.user_defined_dns,
dns_servers=settings.dns_servers,
client_to_client=settings.client_to_client,
duplicate_cn=settings.duplicate_cn,
crl_verify=settings.crl_verify,
user_defined_cdscripts=settings.user_defined_cdscripts,
connect_script=settings.connect_script,
disconnect_script=settings.disconnect_script,
management_interface=settings.management_interface,
management_interface_address=settings.management_interface_address,
management_port=settings.management_port,
tun_mtu=settings.tun_mtu,
mssfix=settings.mssfix
)
# Write to file
with open(output_path, "w") as f:
f.write(config_content)
return config_content
def generate_client_config(db: Session, username: str, output_path: str):
from .pki import validate_username
validate_username(username)
base = os.path.realpath(os.path.dirname(output_path) or ".")
if os.path.realpath(output_path) != os.path.join(base, os.path.basename(output_path)) or os.path.basename(output_path) != f"{username}.ovpn":
raise ValueError("Invalid output path")
settings = get_system_settings(db)
pki = get_pki_settings(db)
# Read Certs and Keys
# Note: filenames in easy-rsa pki structure
# ca: pki/ca.crt
# cert: pki/issued/<username>.crt
# key: pki/private/<username>.key
# ta: pki/ta.key
def read_file(path):
try:
with open(path, "r") as f:
return f.read().strip()
except FileNotFoundError:
logger.error(f"File not found: {path}")
return f"Error: {path} not found"
ca_cert = read_file(os.path.join(PKI_DIR, "ca.crt"))
client_cert = read_file(os.path.join(PKI_DIR, "issued", f"{username}.crt"))
client_key = read_file(os.path.join(PKI_DIR, "private", f"{username}.key"))
tls_auth = read_file(os.path.join(PKI_DIR, "ta.key"))
# Determine Remote IP
if settings.public_ip:
remote_ip = settings.public_ip
else:
from .utils import get_public_ip
remote_ip = get_public_ip()
template = env.get_template("client.ovpn.j2")
config_content = template.render(
protocol=settings.protocol,
remote_ip=remote_ip,
port=settings.port,
ca_cert=ca_cert,
client_cert=client_cert,
client_key=client_key,
tls_auth=tls_auth,
tun_mtu=settings.tun_mtu
)
with open(output_path, "w") as f:
f.write(config_content)
return config_content