mirror of
https://github.com/andrey271192/amnezia_web-PRO.git
synced 2026-09-20 14:42:00 +00:00
fix: auto-detect wg binary in containers
This commit is contained in:
@@ -44,6 +44,8 @@
|
|||||||
|
|
||||||
Начиная с **v1.2.38** панель сама выбирает рабочие пути внутри контейнера: среди `/opt/amnezia/awg/awg0.conf`, `/opt/amnezia/awg/wg0.conf`, `/opt/amnezia/wireguard/wg0.conf` берётся конфиг с максимальным числом `[Peer]`; среди `/opt/amnezia/awg/clientsTable` и `/opt/amnezia/wireguard/clientsTable` берётся таблица с максимальным числом клиентов. Это закрывает установки, где приложение Amnezia создало старых клиентов в `wg0.conf`, а не в `awg0.conf`.
|
Начиная с **v1.2.38** панель сама выбирает рабочие пути внутри контейнера: среди `/opt/amnezia/awg/awg0.conf`, `/opt/amnezia/awg/wg0.conf`, `/opt/amnezia/wireguard/wg0.conf` берётся конфиг с максимальным числом `[Peer]`; среди `/opt/amnezia/awg/clientsTable` и `/opt/amnezia/wireguard/clientsTable` берётся таблица с максимальным числом клиентов. Это закрывает установки, где приложение Amnezia создало старых клиентов в `wg0.conf`, а не в `awg0.conf`.
|
||||||
|
|
||||||
|
Начиная с **v1.2.39** панель auto-detect делает не только для путей, но и для binary внутри контейнера. Если профиль указывает `wgBinary=awg`, а в контейнере есть только `wg`, команды `show`, `pubkey`, `genkey`, `syncconf` используют найденный binary. Это закрывает ошибку `sh: awg: not found` на контейнерах вида `amneziawg`.
|
||||||
|
|
||||||
## Лендинг не поднимается (порт 80 занят)
|
## Лендинг не поднимается (порт 80 занят)
|
||||||
|
|
||||||
При ошибке bind `:80` используйте при установке **`SKIP_LANDING=1`** или **`LANDING_PORT=8081`** — админка на `HOST_PORT` (например 8080) от этого не зависит.
|
При ошибке bind `:80` используйте при установке **`SKIP_LANDING=1`** или **`LANDING_PORT=8081`** — админка на `HOST_PORT` (например 8080) от этого не зависит.
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "amnezia-admin",
|
"name": "amnezia-admin",
|
||||||
"version": "1.2.38",
|
"version": "1.2.39",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "amnezia-admin",
|
"name": "amnezia-admin",
|
||||||
"version": "1.2.38",
|
"version": "1.2.39",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.21.2"
|
"express": "^4.21.2"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "amnezia-admin",
|
"name": "amnezia-admin",
|
||||||
"version": "1.2.38",
|
"version": "1.2.39",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "amnezia_web-PRO — веб-панель AmneziaWG: несколько инстансов, WARP, каскад/Endpoint, экспорт .conf (Docker)",
|
"description": "amnezia_web-PRO — веб-панель AmneziaWG: несколько инстансов, WARP, каскад/Endpoint, экспорт .conf (Docker)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
54
server.js
54
server.js
@@ -828,6 +828,7 @@ function createRuntime(profile) {
|
|||||||
let resolvedContainer = null;
|
let resolvedContainer = null;
|
||||||
let resolvedConfPath = null;
|
let resolvedConfPath = null;
|
||||||
let resolvedClientsPath = null;
|
let resolvedClientsPath = null;
|
||||||
|
let resolvedWgBinary = null;
|
||||||
const confPath = profile.confPath;
|
const confPath = profile.confPath;
|
||||||
const clientsPath = profile.clientsPath;
|
const clientsPath = profile.clientsPath;
|
||||||
const iface = profile.iface;
|
const iface = profile.iface;
|
||||||
@@ -928,6 +929,30 @@ function createRuntime(profile) {
|
|||||||
return resolvedClientsPath;
|
return resolvedClientsPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function resolveWgBinary() {
|
||||||
|
if (resolvedWgBinary) return resolvedWgBinary;
|
||||||
|
const container = await resolveContainer();
|
||||||
|
const preferred = uniqueList([
|
||||||
|
wgBinary,
|
||||||
|
ifaceFromConfPath(resolvedConfPath || confPath, iface) === "wg0" ? "wg" : "",
|
||||||
|
"awg",
|
||||||
|
"wg",
|
||||||
|
]);
|
||||||
|
for (const candidate of preferred) {
|
||||||
|
try {
|
||||||
|
await execDocker(["exec", container, "sh", "-c", `command -v '${candidate}' >/dev/null 2>&1`]);
|
||||||
|
resolvedWgBinary = candidate;
|
||||||
|
if (resolvedWgBinary !== wgBinary) {
|
||||||
|
console.log(`→ WG binary авто: «${resolvedWgBinary}» вместо «${wgBinary}» (${profile.label || profile.id})`);
|
||||||
|
}
|
||||||
|
return resolvedWgBinary;
|
||||||
|
} catch {
|
||||||
|
/* not present */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(`Не найден wg/awg binary в контейнере ${container}: проверял ${preferred.join(", ")}`);
|
||||||
|
}
|
||||||
|
|
||||||
async function dockerExec(cmd) {
|
async function dockerExec(cmd) {
|
||||||
const container = await resolveContainer();
|
const container = await resolveContainer();
|
||||||
const { stdout, stderr } = await execDocker(["exec", container, "sh", "-c", cmd]);
|
const { stdout, stderr } = await execDocker(["exec", container, "sh", "-c", cmd]);
|
||||||
@@ -968,8 +993,9 @@ function createRuntime(profile) {
|
|||||||
async function applySyncconf() {
|
async function applySyncconf() {
|
||||||
const activeConfPath = await resolveConfPath();
|
const activeConfPath = await resolveConfPath();
|
||||||
const activeIface = ifaceFromConfPath(activeConfPath, iface);
|
const activeIface = ifaceFromConfPath(activeConfPath, iface);
|
||||||
|
const activeBinary = await resolveWgBinary();
|
||||||
await dockerExec(
|
await dockerExec(
|
||||||
`wg-quick strip '${activeConfPath}' > /tmp/wg-admin-strip.conf && ${wgBinary} syncconf ${activeIface} /tmp/wg-admin-strip.conf`
|
`wg-quick strip '${activeConfPath}' > /tmp/wg-admin-strip.conf && ${activeBinary} syncconf ${activeIface} /tmp/wg-admin-strip.conf`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1012,8 +1038,12 @@ function createRuntime(profile) {
|
|||||||
get iface() {
|
get iface() {
|
||||||
return ifaceFromConfPath(resolvedConfPath || confPath, iface);
|
return ifaceFromConfPath(resolvedConfPath || confPath, iface);
|
||||||
},
|
},
|
||||||
|
get wgBinary() {
|
||||||
|
return resolvedWgBinary || wgBinary;
|
||||||
|
},
|
||||||
resolveConfPath,
|
resolveConfPath,
|
||||||
resolveClientsPath,
|
resolveClientsPath,
|
||||||
|
resolveWgBinary,
|
||||||
get clientsPath() {
|
get clientsPath() {
|
||||||
return resolvedClientsPath || clientsPath;
|
return resolvedClientsPath || clientsPath;
|
||||||
},
|
},
|
||||||
@@ -1416,7 +1446,8 @@ async function wgPubkeyFromPrivate(rt, privKeyB64) {
|
|||||||
throw new Error("Некорректный формат приватного ключа сервера в awg0.conf");
|
throw new Error("Некорректный формат приватного ключа сервера в awg0.conf");
|
||||||
}
|
}
|
||||||
const q = key.replace(/'/g, `'\\''`);
|
const q = key.replace(/'/g, `'\\''`);
|
||||||
const out = await rt.dockerExec(`printf '%s\\n' '${q}' | ${rt.profile.wgBinary} pubkey`);
|
const activeBinary = await rt.resolveWgBinary();
|
||||||
|
const out = await rt.dockerExec(`printf '%s\\n' '${q}' | ${activeBinary} pubkey`);
|
||||||
const pub = out.trim().split(/\s+/)[0];
|
const pub = out.trim().split(/\s+/)[0];
|
||||||
if (!pub) throw new Error("Не удалось получить публичный ключ сервера (wg pubkey).");
|
if (!pub) throw new Error("Не удалось получить публичный ключ сервера (wg pubkey).");
|
||||||
return pub;
|
return pub;
|
||||||
@@ -1485,7 +1516,8 @@ async function buildClientConfExport(rt, lc, ifaceMap, req, row, conf) {
|
|||||||
const endpointHost = resolveExportEndpointHost(lc, req);
|
const endpointHost = resolveExportEndpointHost(lc, req);
|
||||||
const listenPort = ifaceMap.ListenPort ? Number(ifaceMap.ListenPort) : NaN;
|
const listenPort = ifaceMap.ListenPort ? Number(ifaceMap.ListenPort) : NaN;
|
||||||
const portNum = Number(pickLc(lc, "port")) || (Number.isFinite(listenPort) ? listenPort : NaN);
|
const portNum = Number(pickLc(lc, "port")) || (Number.isFinite(listenPort) ? listenPort : NaN);
|
||||||
const defaultPort = rt.profile.wgBinary === "awg" ? 55424 : 51820;
|
const activeBinary = await rt.resolveWgBinary();
|
||||||
|
const defaultPort = activeBinary === "awg" ? 55424 : 51820;
|
||||||
const port = Number.isFinite(portNum) && portNum > 0 ? portNum : defaultPort;
|
const port = Number.isFinite(portNum) && portNum > 0 ? portNum : defaultPort;
|
||||||
|
|
||||||
const dns1 = String(pickLc(lc, "dns1") || process.env.CLIENT_EXPORT_DNS1?.trim() || "1.1.1.1");
|
const dns1 = String(pickLc(lc, "dns1") || process.env.CLIENT_EXPORT_DNS1?.trim() || "1.1.1.1");
|
||||||
@@ -1495,7 +1527,7 @@ async function buildClientConfExport(rt, lc, ifaceMap, req, row, conf) {
|
|||||||
const mtuVal = pickLc(lc, "mtu", "MTU");
|
const mtuVal = pickLc(lc, "mtu", "MTU");
|
||||||
const mtuLine = mtuVal ? `MTU = ${String(mtuVal).trim()}\n` : "";
|
const mtuLine = mtuVal ? `MTU = ${String(mtuVal).trim()}\n` : "";
|
||||||
|
|
||||||
if (rt.profile.wgBinary === "awg") {
|
if (activeBinary === "awg") {
|
||||||
const Jc = String(pickLc(lc, "Jc", "junk_packet_count", "junkPacketCount") ?? AWG_EXPORT_DEFAULTS.Jc);
|
const Jc = String(pickLc(lc, "Jc", "junk_packet_count", "junkPacketCount") ?? AWG_EXPORT_DEFAULTS.Jc);
|
||||||
const Jmin = String(pickLc(lc, "Jmin", "junk_packet_min_size", "junkPacketMinSize") ?? AWG_EXPORT_DEFAULTS.Jmin);
|
const Jmin = String(pickLc(lc, "Jmin", "junk_packet_min_size", "junkPacketMinSize") ?? AWG_EXPORT_DEFAULTS.Jmin);
|
||||||
const Jmax = String(pickLc(lc, "Jmax", "junk_packet_max_size", "junkPacketMaxSize") ?? AWG_EXPORT_DEFAULTS.Jmax);
|
const Jmax = String(pickLc(lc, "Jmax", "junk_packet_max_size", "junkPacketMaxSize") ?? AWG_EXPORT_DEFAULTS.Jmax);
|
||||||
@@ -1587,13 +1619,14 @@ function parseIpv4ToParts(ip) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function awgGenKeypair(rt) {
|
async function awgGenKeypair(rt) {
|
||||||
const privOut = await rt.dockerExec(`${rt.profile.wgBinary} genkey`);
|
const activeBinary = await rt.resolveWgBinary();
|
||||||
|
const privOut = await rt.dockerExec(`${activeBinary} genkey`);
|
||||||
const priv = privOut.trim().split(/\s+/)[0];
|
const priv = privOut.trim().split(/\s+/)[0];
|
||||||
if (!priv || !/^[A-Za-z0-9+/=_-]+$/.test(priv)) {
|
if (!priv || !/^[A-Za-z0-9+/=_-]+$/.test(priv)) {
|
||||||
throw new Error("Не удалось сгенерировать ключ клиента (genkey).");
|
throw new Error("Не удалось сгенерировать ключ клиента (genkey).");
|
||||||
}
|
}
|
||||||
const q = priv.replace(/'/g, `'\\''`);
|
const q = priv.replace(/'/g, `'\\''`);
|
||||||
const pubOut = await rt.dockerExec(`printf '%s\\n' '${q}' | ${rt.profile.wgBinary} pubkey`);
|
const pubOut = await rt.dockerExec(`printf '%s\\n' '${q}' | ${activeBinary} pubkey`);
|
||||||
const pub = pubOut.trim().split(/\s+/)[0];
|
const pub = pubOut.trim().split(/\s+/)[0];
|
||||||
if (!pub) throw new Error("Не удалось получить публичный ключ клиента.");
|
if (!pub) throw new Error("Не удалось получить публичный ключ клиента.");
|
||||||
return { priv, pub };
|
return { priv, pub };
|
||||||
@@ -2412,7 +2445,7 @@ app.get("/api/clients", requireAuth, async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
let wgShow = "";
|
let wgShow = "";
|
||||||
try {
|
try {
|
||||||
wgShow = await rt.dockerExec(`${rt.profile.wgBinary} show ${rt.iface}`);
|
wgShow = await rt.dockerExec(`${await rt.resolveWgBinary()} show ${rt.iface}`);
|
||||||
} catch {
|
} catch {
|
||||||
wgShow = "";
|
wgShow = "";
|
||||||
}
|
}
|
||||||
@@ -2497,6 +2530,7 @@ app.get("/api/clients", requireAuth, async (req, res) => {
|
|||||||
confPath: activeConfPath || rt.confPath,
|
confPath: activeConfPath || rt.confPath,
|
||||||
clientsPath: activeClientsPath || rt.clientsPath,
|
clientsPath: activeClientsPath || rt.clientsPath,
|
||||||
iface: rt.iface,
|
iface: rt.iface,
|
||||||
|
wgBinary: rt.wgBinary,
|
||||||
protocol: "AmneziaWG",
|
protocol: "AmneziaWG",
|
||||||
peerCount: conf.peers.length,
|
peerCount: conf.peers.length,
|
||||||
clients: rows,
|
clients: rows,
|
||||||
@@ -2557,6 +2591,7 @@ app.get("/api/clients/source-report", requireAuth, async (req, res) => {
|
|||||||
const container = await rt.resolveContainer();
|
const container = await rt.resolveContainer();
|
||||||
const activeConfPath = await rt.resolveConfPath();
|
const activeConfPath = await rt.resolveConfPath();
|
||||||
const activeClientsPath = await rt.resolveClientsPath();
|
const activeClientsPath = await rt.resolveClientsPath();
|
||||||
|
const activeBinary = await rt.resolveWgBinary();
|
||||||
let confText = "";
|
let confText = "";
|
||||||
let tableText = "";
|
let tableText = "";
|
||||||
let conf = { peers: [] };
|
let conf = { peers: [] };
|
||||||
@@ -2584,6 +2619,7 @@ app.get("/api/clients/source-report", requireAuth, async (req, res) => {
|
|||||||
container,
|
container,
|
||||||
confPath: activeConfPath,
|
confPath: activeConfPath,
|
||||||
iface: rt.iface,
|
iface: rt.iface,
|
||||||
|
wgBinary: activeBinary,
|
||||||
clientsPath: activeClientsPath,
|
clientsPath: activeClientsPath,
|
||||||
confBytes: Buffer.byteLength(confText, "utf8"),
|
confBytes: Buffer.byteLength(confText, "utf8"),
|
||||||
clientsTableBytes: Buffer.byteLength(tableText, "utf8"),
|
clientsTableBytes: Buffer.byteLength(tableText, "utf8"),
|
||||||
@@ -2713,7 +2749,7 @@ app.post("/api/clients/create", requireAuth, requireProTier, async (req, res) =>
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const defaultPort = rt.profile.wgBinary === "awg" ? 55424 : 51820;
|
const defaultPort = (await rt.resolveWgBinary()) === "awg" ? 55424 : 51820;
|
||||||
const endpointPort =
|
const endpointPort =
|
||||||
Number.isFinite(listenPort) && listenPort > 0 ? listenPort : defaultPort;
|
Number.isFinite(listenPort) && listenPort > 0 ? listenPort : defaultPort;
|
||||||
const psk = await rt.inferPskFromConf(conf);
|
const psk = await rt.inferPskFromConf(conf);
|
||||||
@@ -2819,7 +2855,7 @@ app.post("/api/clients/create-cascade", requireAuth, requireProTier, async (req,
|
|||||||
endpointPort =
|
endpointPort =
|
||||||
Number.isFinite(listenPort) && listenPort > 0
|
Number.isFinite(listenPort) && listenPort > 0
|
||||||
? listenPort
|
? listenPort
|
||||||
: rt.profile.wgBinary === "awg"
|
: (await rt.resolveWgBinary()) === "awg"
|
||||||
? 55424
|
? 55424
|
||||||
: 51820;
|
: 51820;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user