Add SSH host/login/password/key when creating VPN servers
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+157
-1
@@ -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,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user