feat: add support for awg2, mtproxy, and aivpn protocols, and implement user role-based access control.
This commit is contained in:
@@ -68,3 +68,4 @@ scripts/regen_qr.php
|
|||||||
scripts/test_xray_install.sh
|
scripts/test_xray_install.sh
|
||||||
scripts/test_online.php
|
scripts/test_online.php
|
||||||
API_AWG_DOCS.md
|
API_AWG_DOCS.md
|
||||||
|
log.txt
|
||||||
|
|||||||
@@ -800,8 +800,18 @@ SH;
|
|||||||
$res['client_id'] = $m[1];
|
$res['client_id'] = $m[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract secret (for MTProxy and similar protocols)
|
||||||
|
if (preg_match('/Secret:\s*([a-fA-F0-9]+)/i', $output, $m)) {
|
||||||
|
$res['secret'] = $m[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract server host/IP
|
||||||
|
if (preg_match('/Server\s*Host:\s*(\S+)/i', $output, $m)) {
|
||||||
|
$res['server_host'] = trim($m[1], "'\"");
|
||||||
|
}
|
||||||
|
|
||||||
// Generic variable extraction (Variable: KEY=VALUE)
|
// Generic variable extraction (Variable: KEY=VALUE)
|
||||||
if (preg_match_all('/Variable:\\s*([a-zA-Z0-9_]+)=(.*)/', $output, $matches, PREG_SET_ORDER)) {
|
if (preg_match_all('/Variable:\s*([a-zA-Z0-9_]+)=(.*)/', $output, $matches, PREG_SET_ORDER)) {
|
||||||
foreach ($matches as $m) {
|
foreach ($matches as $m) {
|
||||||
$key = trim($m[1]);
|
$key = trim($m[1]);
|
||||||
$val = trim($m[2]);
|
$val = trim($m[2]);
|
||||||
@@ -811,6 +821,25 @@ SH;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback: parse any remaining "Key: Value" lines not yet captured
|
||||||
|
// This catches protocol-specific variables like custom fields
|
||||||
|
$lines = preg_split('/\r?\n/', $output);
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
if ($line === '') continue;
|
||||||
|
// Skip set -x trace lines (start with +)
|
||||||
|
if (preg_match('/^\+/', $line)) continue;
|
||||||
|
if (preg_match('/^([A-Za-z][A-Za-z0-9 _-]*?)\s*:\s*(.+)$/', $line, $m)) {
|
||||||
|
$rawKey = trim($m[1]);
|
||||||
|
$rawVal = trim($m[2], " \t'\"");
|
||||||
|
$normalized = strtolower(preg_replace('/\s+/', '_', $rawKey));
|
||||||
|
// Don't overwrite already extracted keys
|
||||||
|
if (!array_key_exists($normalized, $res)) {
|
||||||
|
$res[$normalized] = $rawVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -295,6 +295,8 @@ class InstallProtocolManager
|
|||||||
'server_public_key' => $result['server_public_key'] ?? null,
|
'server_public_key' => $result['server_public_key'] ?? null,
|
||||||
'preshared_key' => $result['preshared_key'] ?? null,
|
'preshared_key' => $result['preshared_key'] ?? null,
|
||||||
'awg_params' => $result['awg_params'] ?? null,
|
'awg_params' => $result['awg_params'] ?? null,
|
||||||
|
'secret' => $result['secret'] ?? null,
|
||||||
|
'server_host' => $result['server_host'] ?? null,
|
||||||
];
|
];
|
||||||
if (($protocol['slug'] ?? '') === 'xray-vless') {
|
if (($protocol['slug'] ?? '') === 'xray-vless') {
|
||||||
foreach (['client_id', 'container_name', 'server_port', 'xray_port', 'reality_public_key', 'reality_private_key', 'reality_short_id', 'reality_server_name'] as $k) {
|
foreach (['client_id', 'container_name', 'server_port', 'xray_port', 'reality_public_key', 'reality_private_key', 'reality_short_id', 'reality_server_name'] as $k) {
|
||||||
@@ -653,6 +655,9 @@ class InstallProtocolManager
|
|||||||
'privatekey' => 'reality_private_key',
|
'privatekey' => 'reality_private_key',
|
||||||
'shortid' => 'reality_short_id',
|
'shortid' => 'reality_short_id',
|
||||||
'servername' => 'reality_server_name',
|
'servername' => 'reality_server_name',
|
||||||
|
'secret' => 'secret',
|
||||||
|
'serverhost' => 'server_host',
|
||||||
|
'server_host' => 'server_host',
|
||||||
];
|
];
|
||||||
|
|
||||||
$finalKey = $keyMap[$normalizedKey] ?? $normalizedKey;
|
$finalKey = $keyMap[$normalizedKey] ?? $normalizedKey;
|
||||||
@@ -899,7 +904,7 @@ class InstallProtocolManager
|
|||||||
*/
|
*/
|
||||||
private static function isAwgProtocol(string $slug, array $protocol): bool
|
private static function isAwgProtocol(string $slug, array $protocol): bool
|
||||||
{
|
{
|
||||||
if (in_array($slug, ['amnezia-wg', 'amnezia-wg-advanced'], true)) {
|
if (in_array($slug, ['amnezia-wg', 'amnezia-wg-advanced', 'awg2'], true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$installScript = (string) ($protocol['install_script'] ?? '');
|
$installScript = (string) ($protocol['install_script'] ?? '');
|
||||||
|
|||||||
+7
-6
@@ -67,7 +67,7 @@ class VpnClient
|
|||||||
$protoRow = $stmtProto2->fetch();
|
$protoRow = $stmtProto2->fetch();
|
||||||
}
|
}
|
||||||
$slug = $protoRow['slug'] ?? ($serverData['install_protocol'] ?? 'amnezia-wg');
|
$slug = $protoRow['slug'] ?? ($serverData['install_protocol'] ?? 'amnezia-wg');
|
||||||
$isWireguard = in_array($slug, ['amnezia-wg-advanced', 'wireguard-standard', 'amnezia-wg'], true);
|
$isWireguard = in_array($slug, ['amnezia-wg-advanced', 'wireguard-standard', 'amnezia-wg', 'awg2'], true);
|
||||||
|
|
||||||
// Auto-sync server keys from container EVERY TIME for WireGuard protocols
|
// Auto-sync server keys from container EVERY TIME for WireGuard protocols
|
||||||
// This ensures we always use current container configuration even if it was recreated
|
// This ensures we always use current container configuration even if it was recreated
|
||||||
@@ -1121,7 +1121,7 @@ class VpnClient
|
|||||||
$stmt = $pdo->prepare('SELECT slug FROM protocols WHERE id = ?');
|
$stmt = $pdo->prepare('SELECT slug FROM protocols WHERE id = ?');
|
||||||
$stmt->execute([$protocolId]);
|
$stmt->execute([$protocolId]);
|
||||||
$slug = (string) $stmt->fetchColumn();
|
$slug = (string) $stmt->fetchColumn();
|
||||||
return in_array($slug, ['amnezia-wg-advanced', 'wireguard-standard', 'amnezia-wg'], true);
|
return in_array($slug, ['amnezia-wg-advanced', 'wireguard-standard', 'amnezia-wg', 'awg2'], true);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1309,7 +1309,7 @@ class VpnClient
|
|||||||
$protoRow = $stmt->fetch();
|
$protoRow = $stmt->fetch();
|
||||||
}
|
}
|
||||||
$slug = $protoRow['slug'] ?? '';
|
$slug = $protoRow['slug'] ?? '';
|
||||||
$isWireguard = in_array($slug, ['amnezia-wg-advanced', 'wireguard-standard', 'amnezia-wg'], true);
|
$isWireguard = in_array($slug, ['amnezia-wg-advanced', 'wireguard-standard', 'amnezia-wg', 'awg2'], true);
|
||||||
|
|
||||||
if (!$isWireguard) {
|
if (!$isWireguard) {
|
||||||
return ['success' => false, 'error' => 'not_wireguard_protocol', 'protocol_slug' => $slug];
|
return ['success' => false, 'error' => 'not_wireguard_protocol', 'protocol_slug' => $slug];
|
||||||
@@ -1335,7 +1335,7 @@ class VpnClient
|
|||||||
|
|
||||||
// If AWG params are missing (common after reinstall), fetch them directly from wg0.conf
|
// If AWG params are missing (common after reinstall), fetch them directly from wg0.conf
|
||||||
// to avoid falling back to template defaults that will not match the server.
|
// to avoid falling back to template defaults that will not match the server.
|
||||||
if ($slug === 'amnezia-wg-advanced') {
|
if (in_array($slug, ['amnezia-wg-advanced', 'awg2'], true)) {
|
||||||
$needKeys = ['JC', 'JMIN', 'JMAX', 'S1', 'S2', 'H1', 'H2', 'H3', 'H4'];
|
$needKeys = ['JC', 'JMIN', 'JMAX', 'S1', 'S2', 'H1', 'H2', 'H3', 'H4'];
|
||||||
$missing = false;
|
$missing = false;
|
||||||
foreach ($needKeys as $k) {
|
foreach ($needKeys as $k) {
|
||||||
@@ -1346,8 +1346,9 @@ class VpnClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($missing) {
|
if ($missing) {
|
||||||
$containerName = $serverData['container_name'] ?? 'amnezia-awg';
|
$containerName = $serverData['container_name'] ?? ($slug === 'awg2' ? 'amnezia-awg2' : 'amnezia-awg');
|
||||||
$direct = self::extractAwgParamsFromWg0Conf($server, $containerName, '/opt/amnezia/awg/wg0.conf');
|
$configDir = $slug === 'awg2' ? '/opt/amnezia/awg2' : '/opt/amnezia/awg';
|
||||||
|
$direct = self::extractAwgParamsFromWg0Conf($server, $containerName, $configDir . '/wg0.conf');
|
||||||
if (empty($direct)) {
|
if (empty($direct)) {
|
||||||
$direct = self::extractAwgParamsFromWg0Conf($server, $containerName, '/etc/wireguard/wg0.conf');
|
$direct = self::extractAwgParamsFromWg0Conf($server, $containerName, '/etc/wireguard/wg0.conf');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
-- Migration: Add user roles and permissions
|
||||||
|
-- Date: 2025-11-10
|
||||||
|
|
||||||
|
-- User roles table
|
||||||
|
CREATE TABLE IF NOT EXISTS user_roles (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
display_name VARCHAR(100) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
permissions JSON NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- Add role to users table
|
||||||
|
ALTER TABLE users
|
||||||
|
ADD COLUMN role VARCHAR(50) DEFAULT 'viewer' AFTER ldap_dn,
|
||||||
|
ADD INDEX idx_role (role);
|
||||||
|
|
||||||
|
-- Insert default roles
|
||||||
|
INSERT IGNORE INTO user_roles (name, display_name, description, permissions) VALUES
|
||||||
|
('admin', 'Administrator', 'Full access to all features', JSON_ARRAY('*')),
|
||||||
|
('manager', 'Manager', 'Can manage servers and clients', JSON_ARRAY('servers.view', 'servers.create', 'servers.edit', 'clients.view', 'clients.create', 'clients.edit', 'clients.delete')),
|
||||||
|
('viewer', 'Viewer', 'Can only view own clients', JSON_ARRAY('clients.view_own', 'clients.download_own'));
|
||||||
|
|
||||||
|
-- Insert default LDAP group mappings (examples)
|
||||||
|
INSERT IGNORE INTO ldap_group_mappings (ldap_group, role_name, description) VALUES
|
||||||
|
('vpn-admins', 'admin', 'VPN administrators with full access'),
|
||||||
|
('vpn-managers', 'manager', 'VPN managers who can create and manage clients'),
|
||||||
|
('vpn-users', 'viewer', 'Regular VPN users with view-only access');
|
||||||
|
|
||||||
|
-- Update existing users to admin role (backward compatibility)
|
||||||
|
UPDATE users SET role = 'admin' WHERE role IS NULL OR role = '';
|
||||||
@@ -0,0 +1,381 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- Migration 058: Add AmneziaWG 2.0 protocol (amneziawg-go userspace)
|
||||||
|
-- Uses amneziawg-go (Go userspace) instead of kernel module
|
||||||
|
-- https://github.com/amnezia-vpn/amneziawg-go
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
-- 1. Insert the protocol entry (clone output_template from amnezia-wg-advanced)
|
||||||
|
INSERT INTO protocols (name, slug, description, install_script, uninstall_script, output_template, ubuntu_compatible, is_active, definition, created_at, updated_at)
|
||||||
|
SELECT
|
||||||
|
'AmneziaWG 2.0',
|
||||||
|
'awg2',
|
||||||
|
'AmneziaWG 2.0 — userspace Go implementation (amneziawg-go). No kernel module required.',
|
||||||
|
'#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Use exported variables from panel (SERVER_PORT, SERVER_CONTAINER) or defaults
|
||||||
|
CONTAINER_NAME="${SERVER_CONTAINER:-amnezia-awg2}"
|
||||||
|
PORT_RANGE_START=${PORT_RANGE_START:-30000}
|
||||||
|
PORT_RANGE_END=${PORT_RANGE_END:-65000}
|
||||||
|
VPN_PORT="${SERVER_PORT:-$((RANDOM % (PORT_RANGE_END - PORT_RANGE_START + 1) + PORT_RANGE_START))}"
|
||||||
|
MTU=${MTU:-1420}
|
||||||
|
|
||||||
|
# Install git if not available
|
||||||
|
if ! command -v git &> /dev/null; then
|
||||||
|
apt-get update -qq && apt-get install -y -qq git >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /opt/amnezia/awg2
|
||||||
|
|
||||||
|
# Clone amneziawg-go source for Docker build
|
||||||
|
if [ ! -d /opt/amnezia/awg2/src ]; then
|
||||||
|
git clone --depth=1 https://github.com/amnezia-vpn/amneziawg-go.git /opt/amnezia/awg2/src
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build Docker image using the repo Dockerfile (multi-stage: Go compile + tools)
|
||||||
|
docker build --no-cache -t amnezia-awg2 /opt/amnezia/awg2/src
|
||||||
|
|
||||||
|
# Run container (userspace: no SYS_MODULE, no /lib/modules)
|
||||||
|
EXISTING=$(docker ps -aq -f "name=$CONTAINER_NAME" 2>/dev/null | head -1)
|
||||||
|
if [ -z "$EXISTING" ]; then
|
||||||
|
docker run -d --name "$CONTAINER_NAME" --restart always --cap-add=NET_ADMIN --device /dev/net/tun -p "${VPN_PORT}:${VPN_PORT}/udp" -v /opt/amnezia/awg2:/opt/amnezia/awg amnezia-awg2 sh -c "while [ ! -f /opt/amnezia/awg/wg0.conf ]; do sleep 1; done; WG_QUICK_USERSPACE_IMPLEMENTATION=amneziawg-go awg-quick up /opt/amnezia/awg/wg0.conf && sleep infinity"
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
STATUS=$(docker inspect --format="{{.State.Status}}" "$CONTAINER_NAME" 2>/dev/null || echo "")
|
||||||
|
if [ \"$STATUS\" != \"running\" ]; then
|
||||||
|
docker start \"$CONTAINER_NAME\" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for existing config
|
||||||
|
if [ -f /opt/amnezia/awg2/wg0.conf ]; then
|
||||||
|
PORT=$(grep -E "^ListenPort" /opt/amnezia/awg2/wg0.conf | cut -d= -f2 | tr -d "[:space:]")
|
||||||
|
PSK=$(cat /opt/amnezia/awg2/wireguard_psk.key 2>/dev/null || true)
|
||||||
|
if [ -z "$PSK" ]; then
|
||||||
|
PSK=$(grep -E "^PresharedKey" /opt/amnezia/awg2/wg0.conf | cut -d= -f2 | tr -d "[:space:]")
|
||||||
|
fi
|
||||||
|
PUBKEY=$(cat /opt/amnezia/awg2/wireguard_server_public_key.key 2>/dev/null || true)
|
||||||
|
if [ -z "$PUBKEY" ]; then
|
||||||
|
PRIVKEY=$(cat /opt/amnezia/awg2/wireguard_server_private_key.key 2>/dev/null || true)
|
||||||
|
if [ -n "$PRIVKEY" ]; then
|
||||||
|
PUBKEY=$(echo "$PRIVKEY" | docker exec -i "$CONTAINER_NAME" wg pubkey)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Using existing AmneziaWG 2.0 configuration"
|
||||||
|
echo "Port: ${PORT:-$VPN_PORT}"
|
||||||
|
if [ -n "${PUBKEY:-}" ]; then echo "Server Public Key: $PUBKEY"; fi
|
||||||
|
if [ -n "${PSK:-}" ]; then echo "PresharedKey = $PSK"; fi
|
||||||
|
|
||||||
|
EXTERNAL_IP=$(curl -s -4 ifconfig.me 2>/dev/null || curl -s -4 icanhazip.com 2>/dev/null || echo "YOUR_SERVER_IP")
|
||||||
|
echo "Server Host: $EXTERNAL_IP"
|
||||||
|
|
||||||
|
# Output AWG params from existing config
|
||||||
|
for P in Jc Jmin Jmax S1 S2 S3 S4 H1 H2 H3 H4; do
|
||||||
|
VAL=$(grep -E "^$P " /opt/amnezia/awg2/wg0.conf | cut -d= -f2 | tr -d "[:space:]")
|
||||||
|
if [ -n "$VAL" ]; then echo "Variable: $P=$VAL"; fi
|
||||||
|
done
|
||||||
|
echo "Variable: dns_servers=1.1.1.1, 1.0.0.1"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate keys
|
||||||
|
PRIVATE_KEY=$(docker exec "$CONTAINER_NAME" wg genkey)
|
||||||
|
PUBLIC_KEY=$(echo "$PRIVATE_KEY" | docker exec -i "$CONTAINER_NAME" wg pubkey)
|
||||||
|
PRESHARED_KEY=$(docker exec "$CONTAINER_NAME" wg genpsk)
|
||||||
|
|
||||||
|
# AWG obfuscation parameters
|
||||||
|
JC=5
|
||||||
|
JMIN=50
|
||||||
|
JMAX=1000
|
||||||
|
S1_VAL=50
|
||||||
|
S2_VAL=100
|
||||||
|
S3_VAL=20
|
||||||
|
S4_VAL=10
|
||||||
|
# H1-H4: header ranges (string format "x-y" per AWG2 spec)
|
||||||
|
H1_VAL="1-4294967295"
|
||||||
|
H2_VAL="1-4294967295"
|
||||||
|
H3_VAL="1-4294967295"
|
||||||
|
H4_VAL="1-4294967295"
|
||||||
|
|
||||||
|
# Write config
|
||||||
|
cat > /opt/amnezia/awg2/wg0.conf << EOF
|
||||||
|
[Interface]
|
||||||
|
PrivateKey = $PRIVATE_KEY
|
||||||
|
Address = 10.8.1.1/24
|
||||||
|
ListenPort = $VPN_PORT
|
||||||
|
MTU = $MTU
|
||||||
|
Jc = $JC
|
||||||
|
Jmin = $JMIN
|
||||||
|
Jmax = $JMAX
|
||||||
|
S1 = $S1_VAL
|
||||||
|
S2 = $S2_VAL
|
||||||
|
S3 = $S3_VAL
|
||||||
|
S4 = $S4_VAL
|
||||||
|
H1 = $H1_VAL
|
||||||
|
H2 = $H2_VAL
|
||||||
|
H3 = $H3_VAL
|
||||||
|
H4 = $H4_VAL
|
||||||
|
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey =
|
||||||
|
PresharedKey = $PRESHARED_KEY
|
||||||
|
AllowedIPs = 10.8.1.2/32
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "$PRIVATE_KEY" > /opt/amnezia/awg2/wireguard_server_private_key.key
|
||||||
|
echo "$PUBLIC_KEY" > /opt/amnezia/awg2/wireguard_server_public_key.key
|
||||||
|
echo "$PRESHARED_KEY" > /opt/amnezia/awg2/wireguard_psk.key
|
||||||
|
echo "[]" > /opt/amnezia/awg2/clientsTable
|
||||||
|
|
||||||
|
# Get external IP
|
||||||
|
EXTERNAL_IP=$(curl -s -4 ifconfig.me 2>/dev/null || curl -s -4 icanhazip.com 2>/dev/null || echo "YOUR_SERVER_IP")
|
||||||
|
|
||||||
|
echo "AmneziaWG 2.0 installed successfully"
|
||||||
|
echo "Port: $VPN_PORT"
|
||||||
|
echo "Server Public Key: $PUBLIC_KEY"
|
||||||
|
echo "PresharedKey = $PRESHARED_KEY"
|
||||||
|
echo "Server Host: $EXTERNAL_IP"
|
||||||
|
echo "Variable: Jc=$JC"
|
||||||
|
echo "Variable: Jmin=$JMIN"
|
||||||
|
echo "Variable: Jmax=$JMAX"
|
||||||
|
echo "Variable: S1=$S1_VAL"
|
||||||
|
echo "Variable: S2=$S2_VAL"
|
||||||
|
echo "Variable: S3=$S3_VAL"
|
||||||
|
echo "Variable: S4=$S4_VAL"
|
||||||
|
echo "Variable: H1=$H1_VAL"
|
||||||
|
echo "Variable: H2=$H2_VAL"
|
||||||
|
echo "Variable: H3=$H3_VAL"
|
||||||
|
echo "Variable: H4=$H4_VAL"
|
||||||
|
echo "Variable: dns_servers=1.1.1.1, 1.0.0.1"',
|
||||||
|
'#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_NAME="${CONTAINER_NAME:-amnezia-awg2}"
|
||||||
|
|
||||||
|
docker stop "$CONTAINER_NAME" 2>/dev/null || true
|
||||||
|
docker rm -fv "$CONTAINER_NAME" 2>/dev/null || true
|
||||||
|
docker image rm amnezia-awg2 2>/dev/null || true
|
||||||
|
rm -rf /opt/amnezia/awg2 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "{\"success\":true,\"message\":\"AmneziaWG 2.0 uninstalled\"}"',
|
||||||
|
p.output_template,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
JSON_OBJECT(
|
||||||
|
'engine', 'shell',
|
||||||
|
'metadata', JSON_OBJECT(
|
||||||
|
'container_name', 'amnezia-awg2',
|
||||||
|
'vpn_subnet', '10.8.1.0/24',
|
||||||
|
'port_range', JSON_ARRAY(30000, 65000),
|
||||||
|
'config_dir', '/opt/amnezia/awg2'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
NOW(),
|
||||||
|
NOW()
|
||||||
|
FROM protocols p
|
||||||
|
WHERE p.slug = 'amnezia-wg-advanced'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocols WHERE slug = 'awg2');
|
||||||
|
|
||||||
|
-- 2. Clone protocol variables from amnezia-wg-advanced to awg2
|
||||||
|
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
|
||||||
|
SELECT
|
||||||
|
(SELECT id FROM protocols WHERE slug = 'awg2' LIMIT 1),
|
||||||
|
src.variable_name,
|
||||||
|
src.variable_type,
|
||||||
|
src.default_value,
|
||||||
|
src.description,
|
||||||
|
src.required
|
||||||
|
FROM protocol_variables src
|
||||||
|
WHERE src.protocol_id = (SELECT id FROM protocols WHERE slug = 'amnezia-wg-advanced' LIMIT 1)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM protocol_variables ev
|
||||||
|
WHERE ev.protocol_id = (SELECT id FROM protocols WHERE slug = 'awg2' LIMIT 1)
|
||||||
|
AND ev.variable_name = src.variable_name
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 3. Clone protocol templates from amnezia-wg-advanced to awg2
|
||||||
|
INSERT INTO protocol_templates (protocol_id, template_name, template_content, is_default)
|
||||||
|
SELECT
|
||||||
|
(SELECT id FROM protocols WHERE slug = 'awg2' LIMIT 1),
|
||||||
|
src.template_name,
|
||||||
|
src.template_content,
|
||||||
|
src.is_default
|
||||||
|
FROM protocol_templates src
|
||||||
|
WHERE src.protocol_id = (SELECT id FROM protocols WHERE slug = 'amnezia-wg-advanced' LIMIT 1)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM protocol_templates et
|
||||||
|
WHERE et.protocol_id = (SELECT id FROM protocols WHERE slug = 'awg2' LIMIT 1)
|
||||||
|
AND et.template_name = src.template_name
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 4. Update install_script for existing awg2 protocol (in case migration was already run)
|
||||||
|
UPDATE protocols SET install_script = '#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_NAME="${SERVER_CONTAINER:-amnezia-awg2}"
|
||||||
|
PORT_RANGE_START=${PORT_RANGE_START:-30000}
|
||||||
|
PORT_RANGE_END=${PORT_RANGE_END:-65000}
|
||||||
|
VPN_PORT="${SERVER_PORT:-$((RANDOM % (PORT_RANGE_END - PORT_RANGE_START + 1) + PORT_RANGE_START))}"
|
||||||
|
MTU=${MTU:-1420}
|
||||||
|
|
||||||
|
if ! command -v git &> /dev/null; then
|
||||||
|
apt-get update -qq && apt-get install -y -qq git >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /opt/amnezia/awg2
|
||||||
|
|
||||||
|
if [ ! -d /opt/amnezia/awg2/src ]; then
|
||||||
|
git clone --depth=1 https://github.com/amnezia-vpn/amneziawg-go.git /opt/amnezia/awg2/src
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker build --no-cache -t amnezia-awg2 /opt/amnezia/awg2/src
|
||||||
|
|
||||||
|
EXISTING=$(docker ps -aq -f "name=$CONTAINER_NAME" 2>/dev/null | head -1)
|
||||||
|
if [ -z "$EXISTING" ]; then
|
||||||
|
docker run -d --name "$CONTAINER_NAME" --restart always --cap-add=NET_ADMIN --device /dev/net/tun -p "${VPN_PORT}:${VPN_PORT}/udp" -v /opt/amnezia/awg2:/opt/amnezia/awg amnezia-awg2 sh -c "while [ ! -f /opt/amnezia/awg/wg0.conf ]; do sleep 1; done; WG_QUICK_USERSPACE_IMPLEMENTATION=amneziawg-go awg-quick up /opt/amnezia/awg/wg0.conf && sleep infinity"
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
STATUS=$(docker inspect --format="{{.State.Status}}" "$CONTAINER_NAME" 2>/dev/null || echo "")
|
||||||
|
if [ \"$STATUS\" != \"running\" ]; then
|
||||||
|
docker start \"$CONTAINER_NAME\" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /opt/amnezia/awg2/wg0.conf ]; then
|
||||||
|
PORT=$(grep -E "^ListenPort" /opt/amnezia/awg2/wg0.conf | cut -d= -f2 | tr -d "[:space:]")
|
||||||
|
PSK=$(cat /opt/amnezia/awg2/wireguard_psk.key 2>/dev/null || true)
|
||||||
|
if [ -z "$PSK" ]; then
|
||||||
|
PSK=$(grep -E "^PresharedKey" /opt/amnezia/awg2/wg0.conf | cut -d= -f2 | tr -d "[:space:]")
|
||||||
|
fi
|
||||||
|
PUBKEY=$(cat /opt/amnezia/awg2/wireguard_server_public_key.key 2>/dev/null || true)
|
||||||
|
if [ -z "$PUBKEY" ]; then
|
||||||
|
PRIVKEY=$(cat /opt/amnezia/awg2/wireguard_server_private_key.key 2>/dev/null || true)
|
||||||
|
if [ -n "$PRIVKEY" ]; then
|
||||||
|
PUBKEY=$(echo "$PRIVKEY" | docker exec -i "$CONTAINER_NAME" wg pubkey)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Using existing AmneziaWG 2.0 configuration"
|
||||||
|
echo "Port: ${PORT:-$VPN_PORT}"
|
||||||
|
if [ -n "${PUBKEY:-}" ]; then echo "Server Public Key: $PUBKEY"; fi
|
||||||
|
if [ -n "${PSK:-}" ]; then echo "PresharedKey = $PSK"; fi
|
||||||
|
|
||||||
|
EXTERNAL_IP=$(curl -s -4 ifconfig.me 2>/dev/null || curl -s -4 icanhazip.com 2>/dev/null || echo "YOUR_SERVER_IP")
|
||||||
|
echo "Server Host: $EXTERNAL_IP"
|
||||||
|
|
||||||
|
for P in Jc Jmin Jmax S1 S2 S3 S4 H1 H2 H3 H4; do
|
||||||
|
VAL=$(grep -E "^$P " /opt/amnezia/awg2/wg0.conf | cut -d= -f2 | tr -d "[:space:]")
|
||||||
|
if [ -n "$VAL" ]; then echo "Variable: $P=$VAL"; fi
|
||||||
|
done
|
||||||
|
echo "Variable: dns_servers=1.1.1.1, 1.0.0.1"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
PRIVATE_KEY=$(docker exec "$CONTAINER_NAME" wg genkey)
|
||||||
|
PUBLIC_KEY=$(echo "$PRIVATE_KEY" | docker exec -i "$CONTAINER_NAME" wg pubkey)
|
||||||
|
PRESHARED_KEY=$(docker exec "$CONTAINER_NAME" wg genpsk)
|
||||||
|
|
||||||
|
JC=5
|
||||||
|
JMIN=50
|
||||||
|
JMAX=1000
|
||||||
|
S1_VAL=50
|
||||||
|
S2_VAL=100
|
||||||
|
S3_VAL=20
|
||||||
|
S4_VAL=10
|
||||||
|
H1_VAL="1-4294967295"
|
||||||
|
H2_VAL="1-4294967295"
|
||||||
|
H3_VAL="1-4294967295"
|
||||||
|
H4_VAL="1-4294967295"
|
||||||
|
|
||||||
|
cat > /opt/amnezia/awg2/wg0.conf << EOF
|
||||||
|
[Interface]
|
||||||
|
PrivateKey = $PRIVATE_KEY
|
||||||
|
Address = 10.8.1.1/24
|
||||||
|
ListenPort = $VPN_PORT
|
||||||
|
MTU = $MTU
|
||||||
|
Jc = $JC
|
||||||
|
Jmin = $JMIN
|
||||||
|
Jmax = $JMAX
|
||||||
|
S1 = $S1_VAL
|
||||||
|
S2 = $S2_VAL
|
||||||
|
S3 = $S3_VAL
|
||||||
|
S4 = $S4_VAL
|
||||||
|
H1 = $H1_VAL
|
||||||
|
H2 = $H2_VAL
|
||||||
|
H3 = $H3_VAL
|
||||||
|
H4 = $H4_VAL
|
||||||
|
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey =
|
||||||
|
PresharedKey = $PRESHARED_KEY
|
||||||
|
AllowedIPs = 10.8.1.2/32
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "$PRIVATE_KEY" > /opt/amnezia/awg2/wireguard_server_private_key.key
|
||||||
|
echo "$PUBLIC_KEY" > /opt/amnezia/awg2/wireguard_server_public_key.key
|
||||||
|
echo "$PRESHARED_KEY" > /opt/amnezia/awg2/wireguard_psk.key
|
||||||
|
echo "[]" > /opt/amnezia/awg2/clientsTable
|
||||||
|
|
||||||
|
EXTERNAL_IP=$(curl -s -4 ifconfig.me 2>/dev/null || curl -s -4 icanhazip.com 2>/dev/null || echo "YOUR_SERVER_IP")
|
||||||
|
|
||||||
|
echo "AmneziaWG 2.0 installed successfully"
|
||||||
|
echo "Port: $VPN_PORT"
|
||||||
|
echo "Server Public Key: $PUBLIC_KEY"
|
||||||
|
echo "PresharedKey = $PRESHARED_KEY"
|
||||||
|
echo "Server Host: $EXTERNAL_IP"
|
||||||
|
echo "Variable: Jc=$JC"
|
||||||
|
echo "Variable: Jmin=$JMIN"
|
||||||
|
echo "Variable: Jmax=$JMAX"
|
||||||
|
echo "Variable: S1=$S1_VAL"
|
||||||
|
echo "Variable: S2=$S2_VAL"
|
||||||
|
echo "Variable: S3=$S3_VAL"
|
||||||
|
echo "Variable: S4=$S4_VAL"
|
||||||
|
echo "Variable: H1=$H1_VAL"
|
||||||
|
echo "Variable: H2=$H2_VAL"
|
||||||
|
echo "Variable: H3=$H3_VAL"
|
||||||
|
echo "Variable: H4=$H4_VAL"
|
||||||
|
echo "Variable: dns_servers=1.1.1.1, 1.0.0.1"'
|
||||||
|
WHERE slug = 'awg2';
|
||||||
|
|
||||||
|
-- 5. Update output_template for AWG2 (add S3/S4 padding params)
|
||||||
|
UPDATE protocols SET output_template = '[Interface]
|
||||||
|
PrivateKey = {{private_key}}
|
||||||
|
Address = {{client_ip}}/32
|
||||||
|
DNS = {{dns_servers}}
|
||||||
|
MTU = 1280
|
||||||
|
Jc = {{Jc}}
|
||||||
|
Jmin = {{Jmin}}
|
||||||
|
Jmax = {{Jmax}}
|
||||||
|
S1 = {{S1}}
|
||||||
|
S2 = {{S2}}
|
||||||
|
S3 = {{S3}}
|
||||||
|
S4 = {{S4}}
|
||||||
|
H1 = {{H1}}
|
||||||
|
H2 = {{H2}}
|
||||||
|
H3 = {{H3}}
|
||||||
|
H4 = {{H4}}
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = {{server_public_key}}
|
||||||
|
PresharedKey = {{preshared_key}}
|
||||||
|
AllowedIPs = 0.0.0.0/0, ::/0
|
||||||
|
Endpoint = {{server_host}}:{{server_port}}
|
||||||
|
PersistentKeepalive = 25'
|
||||||
|
WHERE slug = 'awg2';
|
||||||
|
|
||||||
|
-- 6. Add S3/S4 protocol variables for awg2
|
||||||
|
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
|
||||||
|
SELECT p.id, 'S3', 'number', '20', 'Padding of handshake cookie message', false
|
||||||
|
FROM protocols p WHERE p.slug = 'awg2'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'S3');
|
||||||
|
|
||||||
|
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
|
||||||
|
SELECT p.id, 'S4', 'number', '10', 'Padding of transport messages', false
|
||||||
|
FROM protocols p WHERE p.slug = 'awg2'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'S4');
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- Migration 059: Add MTProxy (Telegram) protocol
|
||||||
|
-- https://hub.docker.com/r/telegrammessenger/proxy/
|
||||||
|
-- Zero-configuration Telegram MTProto proxy server
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
-- 1. Insert the MTProxy protocol
|
||||||
|
INSERT INTO protocols (name, slug, description, install_script, uninstall_script, output_template, show_text_content, ubuntu_compatible, is_active, definition, created_at, updated_at)
|
||||||
|
SELECT
|
||||||
|
'MTProxy (Telegram)',
|
||||||
|
'mtproxy',
|
||||||
|
'Telegram MTProto proxy — zero-configuration proxy server for Telegram messenger.',
|
||||||
|
'#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Use exported variables from panel (SERVER_PORT, SERVER_CONTAINER) or defaults
|
||||||
|
CONTAINER_NAME="${SERVER_CONTAINER:-amnezia-mtproxy}"
|
||||||
|
PORT_RANGE_START=${PORT_RANGE_START:-30000}
|
||||||
|
PORT_RANGE_END=${PORT_RANGE_END:-65000}
|
||||||
|
MTPROXY_PORT="${SERVER_PORT:-$((RANDOM % (PORT_RANGE_END - PORT_RANGE_START + 1) + PORT_RANGE_START))}"
|
||||||
|
|
||||||
|
mkdir -p /opt/amnezia/mtproxy
|
||||||
|
|
||||||
|
# Generate secret if not exists
|
||||||
|
if [ -f /opt/amnezia/mtproxy/secret ]; then
|
||||||
|
SECRET=$(cat /opt/amnezia/mtproxy/secret)
|
||||||
|
echo "Using existing MTProxy secret"
|
||||||
|
else
|
||||||
|
SECRET=$(cat /dev/urandom | tr -dc a-f0-9 | head -c 32 || true)
|
||||||
|
echo "$SECRET" > /opt/amnezia/mtproxy/secret
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Store port
|
||||||
|
echo "$MTPROXY_PORT" > /opt/amnezia/mtproxy/port
|
||||||
|
|
||||||
|
# Remove existing container
|
||||||
|
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Run MTProxy container (single line for heredoc compatibility)
|
||||||
|
docker run -d --name "$CONTAINER_NAME" --restart always -p "${MTPROXY_PORT}:443" -v /opt/amnezia/mtproxy:/data -e SECRET="$SECRET" telegrammessenger/proxy:latest
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Get external IP
|
||||||
|
EXTERNAL_IP=$(curl -s -4 ifconfig.me 2>/dev/null || curl -s -4 icanhazip.com 2>/dev/null || echo "YOUR_SERVER_IP")
|
||||||
|
|
||||||
|
echo "MTProxy installed successfully"
|
||||||
|
echo "Port: $MTPROXY_PORT"
|
||||||
|
echo "Secret: $SECRET"
|
||||||
|
echo "Server Host: $EXTERNAL_IP"',
|
||||||
|
'#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_NAME="${CONTAINER_NAME:-amnezia-mtproxy}"
|
||||||
|
|
||||||
|
docker stop "$CONTAINER_NAME" 2>/dev/null || true
|
||||||
|
docker rm -fv "$CONTAINER_NAME" 2>/dev/null || true
|
||||||
|
docker image rm telegrammessenger/proxy:latest 2>/dev/null || true
|
||||||
|
rm -rf /opt/amnezia/mtproxy 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "{\"success\":true,\"message\":\"MTProxy uninstalled\"}"',
|
||||||
|
'tg://proxy?server={{server_host}}&port={{server_port}}&secret={{secret}}',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
JSON_OBJECT(
|
||||||
|
'engine', 'shell',
|
||||||
|
'metadata', JSON_OBJECT(
|
||||||
|
'container_name', 'amnezia-mtproxy',
|
||||||
|
'port_range', JSON_ARRAY(30000, 65000),
|
||||||
|
'config_dir', '/opt/amnezia/mtproxy'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
NOW(),
|
||||||
|
NOW()
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM protocols WHERE slug = 'mtproxy');
|
||||||
|
|
||||||
|
-- 2. Add protocol variables for MTProxy
|
||||||
|
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
|
||||||
|
SELECT p.id, 'secret', 'string', '', 'MTProxy secret (32 hex chars)', true
|
||||||
|
FROM protocols p WHERE p.slug = 'mtproxy'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'secret');
|
||||||
|
|
||||||
|
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
|
||||||
|
SELECT p.id, 'server_host', 'string', '', 'Server hostname or IP', true
|
||||||
|
FROM protocols p WHERE p.slug = 'mtproxy'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'server_host');
|
||||||
|
|
||||||
|
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
|
||||||
|
SELECT p.id, 'server_port', 'number', '443', 'MTProxy external port', true
|
||||||
|
FROM protocols p WHERE p.slug = 'mtproxy'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'server_port');
|
||||||
|
|
||||||
|
-- 3. Add default template for MTProxy
|
||||||
|
INSERT INTO protocol_templates (protocol_id, template_name, template_content, is_default)
|
||||||
|
SELECT p.id, 'Default MTProxy', 'tg://proxy?server={{server_host}}&port={{server_port}}&secret={{secret}}', true
|
||||||
|
FROM protocols p WHERE p.slug = 'mtproxy'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_templates WHERE protocol_id = p.id AND template_name = 'Default MTProxy');
|
||||||
|
|
||||||
|
-- 4. Add QR code template (same as output)
|
||||||
|
UPDATE protocols SET
|
||||||
|
qr_code_template = 'tg://proxy?server={{server_host}}&port={{server_port}}&secret={{secret}}',
|
||||||
|
qr_code_format = 'raw'
|
||||||
|
WHERE slug = 'mtproxy';
|
||||||
|
|
||||||
|
-- 5. Add translations for MTProxy
|
||||||
|
INSERT INTO translations (locale, category, key_name, translation) VALUES
|
||||||
|
('en', 'protocols', 'protocol_mtproxy', 'MTProxy (Telegram)')
|
||||||
|
ON DUPLICATE KEY UPDATE translation = VALUES(translation);
|
||||||
|
|
||||||
|
INSERT INTO translations (locale, category, key_name, translation) VALUES
|
||||||
|
('ru', 'protocols', 'protocol_mtproxy', 'MTProxy (Telegram)')
|
||||||
|
ON DUPLICATE KEY UPDATE translation = VALUES(translation);
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- Migration 060: Add AIVPN protocol (AI-powered VPN with traffic disguise)
|
||||||
|
-- https://github.com/infosave2007/aivpn
|
||||||
|
-- Neural Resonance AI for DPI bypass, Zero-RTT, PFS
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
-- 1. Insert the AIVPN protocol
|
||||||
|
INSERT INTO protocols (name, slug, description, install_script, uninstall_script, output_template, show_text_content, ubuntu_compatible, is_active, definition, created_at, updated_at)
|
||||||
|
SELECT
|
||||||
|
'AIVPN',
|
||||||
|
'aivpn',
|
||||||
|
'AIVPN — AI-powered VPN с маскировкой трафика под реальные приложения (Zoom, TikTok, DNS). Neural Resonance для обхода DPI.',
|
||||||
|
'#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Use exported variables from panel (SERVER_PORT, SERVER_CONTAINER) or defaults
|
||||||
|
CONTAINER_NAME="${SERVER_CONTAINER:-aivpn-server}"
|
||||||
|
VPN_PORT="${SERVER_PORT:-443}"
|
||||||
|
CONFIG_DIR="/etc/aivpn"
|
||||||
|
|
||||||
|
# Install git and iptables if not available
|
||||||
|
if ! command -v git &> /dev/null || ! command -v iptables &> /dev/null; then
|
||||||
|
apt-get update -qq
|
||||||
|
if ! command -v git &> /dev/null; then
|
||||||
|
apt-get install -y -qq git >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
if ! command -v iptables &> /dev/null; then
|
||||||
|
apt-get install -y -qq iptables >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install Docker if not available
|
||||||
|
if ! command -v docker &> /dev/null; then
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq apt-transport-https ca-certificates curl gnupg lsb-release >/dev/null 2>&1
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
|
||||||
|
apt-get update -qq && apt-get install -y -qq docker-ce docker-ce-cli containerd.io >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$CONFIG_DIR"
|
||||||
|
|
||||||
|
# Enable IP forwarding
|
||||||
|
sysctl -w net.ipv4.ip_forward=1 2>/dev/null || true
|
||||||
|
|
||||||
|
# Generate server key if not exists
|
||||||
|
if [ ! -f "$CONFIG_DIR/server.key" ]; then
|
||||||
|
openssl rand 32 > "$CONFIG_DIR/server.key"
|
||||||
|
chmod 600 "$CONFIG_DIR/server.key"
|
||||||
|
echo "Generated new AIVPN server key"
|
||||||
|
else
|
||||||
|
echo "Using existing AIVPN server key"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Setup NAT
|
||||||
|
iptables -t nat -C POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE 2>/dev/null || \
|
||||||
|
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
|
||||||
|
|
||||||
|
# Get external IP
|
||||||
|
EXTERNAL_IP=$(curl -s -4 ifconfig.me 2>/dev/null || curl -s -4 icanhazip.com 2>/dev/null || echo "YOUR_SERVER_IP")
|
||||||
|
|
||||||
|
# Clone AIVPN source for Docker build
|
||||||
|
if [ ! -d /opt/amnezia/aivpn ]; then
|
||||||
|
git clone --depth=1 https://github.com/infosave2007/aivpn.git /opt/amnezia/aivpn
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build Docker image
|
||||||
|
cd /opt/amnezia/aivpn
|
||||||
|
docker build --no-cache -t aivpn-server -f Dockerfile .
|
||||||
|
|
||||||
|
# Remove existing container
|
||||||
|
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Run AIVPN container
|
||||||
|
docker run -d --name "$CONTAINER_NAME" --restart always --cap-add=NET_ADMIN --device /dev/net/tun --network host -v "$CONFIG_DIR:/etc/aivpn" aivpn-server --listen "0.0.0.0:${VPN_PORT}" --key-file /etc/aivpn/server.key
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Check container status
|
||||||
|
STATUS=$(docker inspect --format="{{.State.Status}}" "$CONTAINER_NAME" 2>/dev/null || echo "unknown")
|
||||||
|
if [ "$STATUS" != "running" ]; then
|
||||||
|
echo "ERROR: AIVPN container is not running"
|
||||||
|
docker logs "$CONTAINER_NAME" 2>&1
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "AIVPN installed successfully"
|
||||||
|
echo "Port: $VPN_PORT"
|
||||||
|
echo "ExternalIP: $EXTERNAL_IP"
|
||||||
|
echo "ConfigDir: $CONFIG_DIR"',
|
||||||
|
'#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_NAME="${CONTAINER_NAME:-aivpn-server}"
|
||||||
|
|
||||||
|
docker stop "$CONTAINER_NAME" 2>/dev/null || true
|
||||||
|
docker rm -fv "$CONTAINER_NAME" 2>/dev/null || true
|
||||||
|
docker image rm aivpn-server 2>/dev/null || true
|
||||||
|
rm -rf /opt/amnezia/aivpn 2>/dev/null || true
|
||||||
|
|
||||||
|
# Remove NAT rules
|
||||||
|
iptables -t nat -D POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "{\"success\":true,\"message\":\"AIVPN uninstalled\"}"',
|
||||||
|
'aivpn://{{connection_key}}',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
JSON_OBJECT(
|
||||||
|
'engine', 'shell',
|
||||||
|
'metadata', JSON_OBJECT(
|
||||||
|
'container_name', 'aivpn-server',
|
||||||
|
'port_range', JSON_ARRAY(443, 443),
|
||||||
|
'config_dir', '/etc/aivpn',
|
||||||
|
'vpn_subnet', '10.0.0.0/24',
|
||||||
|
'requires_docker_build', true,
|
||||||
|
'git_repo', 'https://github.com/infosave2007/aivpn.git'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
NOW(),
|
||||||
|
NOW()
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM protocols WHERE slug = 'aivpn');
|
||||||
|
|
||||||
|
-- 2. Add protocol variables for AIVPN
|
||||||
|
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
|
||||||
|
SELECT p.id, 'connection_key', 'string', '', 'AIVPN connection key (generated by server)', true
|
||||||
|
FROM protocols p WHERE p.slug = 'aivpn'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'connection_key');
|
||||||
|
|
||||||
|
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
|
||||||
|
SELECT p.id, 'server_host', 'string', '', 'Server hostname or IP', true
|
||||||
|
FROM protocols p WHERE p.slug = 'aivpn'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'server_host');
|
||||||
|
|
||||||
|
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
|
||||||
|
SELECT p.id, 'server_port', 'number', '443', 'AIVPN server port', true
|
||||||
|
FROM protocols p WHERE p.slug = 'aivpn'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'server_port');
|
||||||
|
|
||||||
|
-- 3. Add default template for AIVPN
|
||||||
|
INSERT INTO protocol_templates (protocol_id, template_name, template_content, is_default)
|
||||||
|
SELECT p.id, 'Default AIVPN', 'aivpn://{{connection_key}}', true
|
||||||
|
FROM protocols p WHERE p.slug = 'aivpn'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM protocol_templates WHERE protocol_id = p.id AND template_name = 'Default AIVPN');
|
||||||
|
|
||||||
|
-- 4. Add translations for AIVPN
|
||||||
|
INSERT INTO translations (locale, category, key_name, translation) VALUES
|
||||||
|
('en', 'protocols', 'protocol_aivpn', 'AIVPN (AI-Powered)')
|
||||||
|
ON DUPLICATE KEY UPDATE translation = VALUES(translation);
|
||||||
|
|
||||||
|
INSERT INTO translations (locale, category, key_name, translation) VALUES
|
||||||
|
('ru', 'protocols', 'protocol_aivpn', 'AIVPN (ИИ-протокол)')
|
||||||
|
ON DUPLICATE KEY UPDATE translation = VALUES(translation);
|
||||||
+3
-1
@@ -1152,7 +1152,7 @@ Router::get('/clients/{id}', function ($params) {
|
|||||||
}
|
}
|
||||||
if ($protocol && ($protocol['output_template'] ?? '') !== '') {
|
if ($protocol && ($protocol['output_template'] ?? '') !== '') {
|
||||||
$slug = $protocol['slug'] ?? '';
|
$slug = $protocol['slug'] ?? '';
|
||||||
$isWireguard = in_array($slug, ['amnezia-wg-advanced', 'wireguard-standard', 'amnezia-wg'], true);
|
$isWireguard = in_array($slug, ['amnezia-wg-advanced', 'wireguard-standard', 'amnezia-wg', 'awg2'], true);
|
||||||
if ($isWireguard) {
|
if ($isWireguard) {
|
||||||
// For WG, we don’t render protocol_output; config is downloadable
|
// For WG, we don’t render protocol_output; config is downloadable
|
||||||
$protocolOutput = '';
|
$protocolOutput = '';
|
||||||
@@ -1771,6 +1771,8 @@ Router::post('/api/servers/create', function () {
|
|||||||
'port' => $port,
|
'port' => $port,
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
|
'install_protocol' => trim($input['install_protocol'] ?? ''),
|
||||||
|
'install_options' => $input['install_options'] ?? null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
http_response_code(201);
|
http_response_code(201);
|
||||||
|
|||||||
Reference in New Issue
Block a user