Add SSH host/login/password/key when creating VPN servers

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
test2
2026-07-25 21:23:12 +03:00
co-authored by Cursor
parent e8fb5ca00c
commit 088f02cdbe
7 changed files with 442 additions and 34 deletions
+157 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
from pathlib import Path
from sqlalchemy import select
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
@@ -75,6 +75,162 @@ async def ensure_default_servers(session: AsyncSession) -> None:
await session.commit()
def _defaults_for_protocol(protocol: str) -> dict:
if protocol == Protocol.AWG2.value:
return {
"interface_name": settings.awg_interface,
"subnet": settings.awg_subnet,
"public_port": settings.awg_public_port,
"jc": 4,
"jmin": 40,
"jmax": 70,
"s1": 0,
"s2": 0,
"h1": 1,
"h2": 2,
"h3": 3,
"h4": 4,
}
return {
"interface_name": settings.wg_interface,
"subnet": settings.wg_subnet,
"public_port": settings.public_port,
"jc": None,
"jmin": None,
"jmax": None,
"s1": None,
"s2": None,
"h1": None,
"h2": None,
"h3": None,
"h4": None,
}
async def create_server(
session: AsyncSession,
*,
name: str,
protocol: str,
ssh_host: str,
ssh_username: str,
ssh_port: int = 22,
ssh_password: str | None = None,
ssh_private_key: str | None = None,
public_host: str | None = None,
public_port: int | None = None,
dns: str = "1.1.1.1",
) -> VpnServer:
protocol = protocol.strip().lower()
if protocol not in {Protocol.WIREGUARD.value, Protocol.AWG2.value}:
raise ValueError("Unsupported protocol")
password = (ssh_password or "").strip() or None
private_key = (ssh_private_key or "").strip() or None
if not password and not private_key:
raise ValueError("Укажите пароль SSH или приватный ключ")
host = ssh_host.strip()
if not host:
raise ValueError("Укажите SSH host")
username = ssh_username.strip()
if not username:
raise ValueError("Укажите SSH логин")
defaults = _defaults_for_protocol(protocol)
keys = generate_keypair()
# Unique interface per server id is assigned after insert; use temp then update
existing_count = await session.scalar(select(func.count()).select_from(VpnServer)) or 0
iface_base = "awg" if protocol == Protocol.AWG2.value else "wg"
interface_name = f"{iface_base}{existing_count}"
# Unique subnet per server: 10.{8+n}.0.0/24 or 10.{9+n}.0.0/24
octet = (8 if protocol == Protocol.WIREGUARD.value else 9) + int(existing_count)
if octet > 250:
raise ValueError("Слишком много серверов для авто-подсети")
subnet = f"10.{octet}.0.0/24"
server = VpnServer(
name=name.strip() or host,
protocol=protocol,
public_host=(public_host or host).strip(),
public_port=public_port or int(defaults["public_port"]),
interface_name=interface_name,
subnet=subnet,
dns=(dns or "1.1.1.1").strip(),
server_private_key=keys.private_key,
server_public_key=keys.public_key,
ssh_host=host,
ssh_port=ssh_port or 22,
ssh_username=username,
ssh_password=password,
ssh_private_key=private_key,
jc=defaults["jc"],
jmin=defaults["jmin"],
jmax=defaults["jmax"],
s1=defaults["s1"],
s2=defaults["s2"],
h1=defaults["h1"],
h2=defaults["h2"],
h3=defaults["h3"],
h4=defaults["h4"],
is_enabled=True,
)
session.add(server)
await session.commit()
await session.refresh(server)
await sync_server_config(session, server.id)
return server
async def update_server_settings(
session: AsyncSession,
server_id: int,
*,
name: str | None = None,
public_host: str,
public_port: int,
dns: str = "1.1.1.1",
ssh_host: str | None = None,
ssh_port: int = 22,
ssh_username: str | None = None,
ssh_password: str | None = None,
ssh_private_key: str | None = None,
clear_ssh_password: bool = False,
clear_ssh_private_key: bool = False,
) -> VpnServer:
server = await session.get(VpnServer, server_id)
if not server:
raise ValueError("Server not found")
if name is not None and name.strip():
server.name = name.strip()
server.public_host = public_host.strip()
server.public_port = public_port
server.dns = dns.strip() or "1.1.1.1"
if ssh_host is not None:
server.ssh_host = ssh_host.strip() or None
server.ssh_port = ssh_port or 22
if ssh_username is not None:
server.ssh_username = ssh_username.strip() or None
if clear_ssh_password:
server.ssh_password = None
elif ssh_password is not None and ssh_password.strip():
server.ssh_password = ssh_password.strip()
if clear_ssh_private_key:
server.ssh_private_key = None
elif ssh_private_key is not None and ssh_private_key.strip():
server.ssh_private_key = ssh_private_key.strip()
await session.commit()
await session.refresh(server)
await sync_server_config(session, server_id)
return server
async def create_client(
session: AsyncSession,
*,