From 69f51bd5d7326dc5450bd117c5c92ef7481e8fda Mon Sep 17 00:00:00 2001 From: Ruslan Date: Tue, 14 Apr 2026 12:37:41 +0300 Subject: [PATCH] GUI: add enable/disable/delete peer actions and sync script-added peers --- gui/app.py | 124 +++++++++++++++++++++++++++++++++-- gui/templates/index.html | 12 ++++ server/wg-peerctl.sh | 138 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+), 4 deletions(-) diff --git a/gui/app.py b/gui/app.py index bdde0f0..f9bd6ac 100644 --- a/gui/app.py +++ b/gui/app.py @@ -40,12 +40,21 @@ def ensure_schema(): client_address TEXT, advertised_routes TEXT, client_conf TEXT, + peer_psk TEXT, + peer_allowed_ips TEXT, + enabled INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); """) cols = {row[1] for row in cur.execute("PRAGMA table_info(peers)").fetchall()} if "client_conf" not in cols: cur.execute("ALTER TABLE peers ADD COLUMN client_conf TEXT") + if "peer_psk" not in cols: + cur.execute("ALTER TABLE peers ADD COLUMN peer_psk TEXT") + if "peer_allowed_ips" not in cols: + cur.execute("ALTER TABLE peers ADD COLUMN peer_allowed_ips TEXT") + if "enabled" not in cols: + cur.execute("ALTER TABLE peers ADD COLUMN enabled INTEGER NOT NULL DEFAULT 1") conn.commit() @@ -282,13 +291,13 @@ def new_peer(): with db_conn() as conn: cur = conn.cursor() cur.execute( - "UPDATE peers SET name=?, client_address=?, advertised_routes=?, client_conf=? WHERE public_key=?", - (name, client_addr, routes, client_conf, client_pub), + "UPDATE peers SET name=?, client_address=?, advertised_routes=?, client_conf=?, peer_psk=?, peer_allowed_ips=?, enabled=1 WHERE public_key=?", + (name, client_addr, routes, client_conf, client_psk, client_addr + (("," + routes) if routes else ""), client_pub), ) if cur.rowcount == 0: cur.execute( - "INSERT INTO peers(name, public_key, client_address, advertised_routes, client_conf) VALUES (?,?,?,?,?)", - (name, client_pub, client_addr, routes, client_conf), + "INSERT INTO peers(name, public_key, client_address, advertised_routes, client_conf, peer_psk, peer_allowed_ips, enabled) VALUES (?,?,?,?,?,?,?,1)", + (name, client_pub, client_addr, routes, client_conf, client_psk, client_addr + (("," + routes) if routes else "")), ) conn.commit() @@ -327,6 +336,113 @@ def peer_view(peer_id: int): ) +@app.post("/peers//disable") +def peer_disable(peer_id: int): + with db_conn() as conn: + cur = conn.cursor() + cur.execute("SELECT * FROM peers WHERE id = ?", (peer_id,)) + row = cur.fetchone() + if not row: + flash("Клиент не найден", "error") + return redirect(url_for("index")) + item = dict(row) + + pk = item.get("public_key", "") + if not pk: + flash("Не найден public key", "error") + return redirect(url_for("index")) + + try: + run(["/usr/local/sbin/wg-peerctl", "remove", "--client-public-key", pk]) + except subprocess.CalledProcessError as e: + flash(f"Не удалось отключить peer: {e}", "error") + return redirect(url_for("index")) + + with db_conn() as conn: + cur = conn.cursor() + cur.execute("UPDATE peers SET enabled=0 WHERE id = ?", (peer_id,)) + conn.commit() + flash("Peer отключен", "ok") + return redirect(url_for("index")) + + +@app.post("/peers//enable") +def peer_enable(peer_id: int): + with db_conn() as conn: + cur = conn.cursor() + cur.execute("SELECT * FROM peers WHERE id = ?", (peer_id,)) + row = cur.fetchone() + if not row: + flash("Клиент не найден", "error") + return redirect(url_for("index")) + item = dict(row) + + name = item.get("name", "") + pk = item.get("public_key", "") + addr = item.get("client_address", "") + routes = item.get("advertised_routes", "") or "" + psk = item.get("peer_psk", "") or "" + if not (name and pk and addr and psk): + flash("Недостаточно данных для включения peer (нужны name/public key/address/psk)", "error") + return redirect(url_for("index")) + + cmd = [ + "/usr/local/sbin/wg-peerctl", + "add", + "--client-name", + name, + "--client-public-key", + pk, + "--client-address", + addr, + "--client-preshared-key", + psk, + "--persistent-keepalive", + "25", + ] + if routes: + cmd += ["--client-routes", routes] + + try: + run(cmd) + except subprocess.CalledProcessError as e: + flash(f"Не удалось включить peer: {e}", "error") + return redirect(url_for("index")) + + with db_conn() as conn: + cur = conn.cursor() + cur.execute("UPDATE peers SET enabled=1 WHERE id = ?", (peer_id,)) + conn.commit() + flash("Peer включен", "ok") + return redirect(url_for("index")) + + +@app.post("/peers//delete") +def peer_delete(peer_id: int): + with db_conn() as conn: + cur = conn.cursor() + cur.execute("SELECT * FROM peers WHERE id = ?", (peer_id,)) + row = cur.fetchone() + if not row: + flash("Клиент не найден", "error") + return redirect(url_for("index")) + item = dict(row) + + pk = item.get("public_key", "") + if pk: + try: + run(["/usr/local/sbin/wg-peerctl", "remove", "--client-public-key", pk]) + except Exception: + pass + + with db_conn() as conn: + cur = conn.cursor() + cur.execute("DELETE FROM peers WHERE id = ?", (peer_id,)) + conn.commit() + flash("Peer удален", "ok") + return redirect(url_for("index")) + + @app.route("/scripts") def scripts(): commands = { diff --git a/gui/templates/index.html b/gui/templates/index.html index 995ce0c..c908ba9 100644 --- a/gui/templates/index.html +++ b/gui/templates/index.html @@ -24,6 +24,18 @@ {% if p.id %} QR/Config + {% if p.status == 'online' %} +
+ +
+ {% else %} +
+ +
+ {% endif %} +
+ +
{% else %} - {% endif %} diff --git a/server/wg-peerctl.sh b/server/wg-peerctl.sh index de09546..a93d1ac 100755 --- a/server/wg-peerctl.sh +++ b/server/wg-peerctl.sh @@ -15,6 +15,7 @@ fi LOG_FILE="/var/log/wireguard-peerctl.log" WG_META_FILE="/etc/wireguard/wg-meta.env" +GUI_DB_FILE="/opt/wg-admin-gui/data/wgadmin.db" usage() { cat <<'USAGE' @@ -27,12 +28,89 @@ usage() { [--client-preshared-key ] \ [--persistent-keepalive 25] + wg-peerctl.sh remove \ + --client-public-key + Описание: Скрипт добавляет peer в конфигурацию WireGuard-сервера идемпотентно. Если peer с таким public key уже существует, повторно не добавляет. USAGE } +sql_escape() { + local s="$1" + s="${s//\'/\'\'}" + printf "%s" "$s" +} + +ensure_gui_db_schema() { + command -v sqlite3 >/dev/null 2>&1 || return 0 + [[ -f "$GUI_DB_FILE" ]] || return 0 + sqlite3 "$GUI_DB_FILE" <<'SQL' >/dev/null 2>&1 || true +CREATE TABLE IF NOT EXISTS peers ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + public_key TEXT UNIQUE NOT NULL, + client_address TEXT, + advertised_routes TEXT, + client_conf TEXT, + peer_psk TEXT, + peer_allowed_ips TEXT, + enabled INTEGER NOT NULL DEFAULT 1, + created_at TEXT NOT NULL DEFAULT (datetime('now')) +); +ALTER TABLE peers ADD COLUMN client_conf TEXT; +ALTER TABLE peers ADD COLUMN peer_psk TEXT; +ALTER TABLE peers ADD COLUMN peer_allowed_ips TEXT; +ALTER TABLE peers ADD COLUMN enabled INTEGER NOT NULL DEFAULT 1; +SQL +} + +sync_gui_db_upsert_peer() { + local name="$1" + local pubkey="$2" + local address="$3" + local routes="$4" + local psk="$5" + local peer_allowed_ips="$6" + local enabled="${7:-1}" + + command -v sqlite3 >/dev/null 2>&1 || return 0 + [[ -f "$GUI_DB_FILE" ]] || return 0 + ensure_gui_db_schema + + local e_name e_pub e_addr e_routes e_psk e_allowed + e_name="$(sql_escape "$name")" + e_pub="$(sql_escape "$pubkey")" + e_addr="$(sql_escape "$address")" + e_routes="$(sql_escape "$routes")" + e_psk="$(sql_escape "$psk")" + e_allowed="$(sql_escape "$peer_allowed_ips")" + + sqlite3 "$GUI_DB_FILE" </dev/null 2>&1 || true +INSERT INTO peers(name, public_key, client_address, advertised_routes, peer_psk, peer_allowed_ips, enabled) +VALUES ('$e_name', '$e_pub', '$e_addr', '$e_routes', '$e_psk', '$e_allowed', $enabled) +ON CONFLICT(public_key) +DO UPDATE SET + name=excluded.name, + client_address=excluded.client_address, + advertised_routes=excluded.advertised_routes, + peer_psk=excluded.peer_psk, + peer_allowed_ips=excluded.peer_allowed_ips, + enabled=excluded.enabled; +SQL +} + +sync_gui_db_set_enabled() { + local pubkey="$1" + local enabled="$2" + command -v sqlite3 >/dev/null 2>&1 || return 0 + [[ -f "$GUI_DB_FILE" ]] || return 0 + local e_pub + e_pub="$(sql_escape "$pubkey")" + sqlite3 "$GUI_DB_FILE" "UPDATE peers SET enabled=${enabled} WHERE public_key='${e_pub}';" >/dev/null 2>&1 || true +} + load_meta() { [[ -f "$WG_META_FILE" ]] || die "Не найден $WG_META_FILE. Сначала выполните install_server.sh" # shellcheck disable=SC1090 @@ -219,6 +297,7 @@ EOF_OUT } >> "$WG_CONF" apply_config + sync_gui_db_upsert_peer "$client_name" "$client_pubkey" "$client_address" "$client_routes" "$client_psk" "$peer_allowed_ips" 1 cat < "$tmp" + mv "$tmp" "$WG_CONF" + safe_chmod_600 "$WG_CONF" + + apply_config + sync_gui_db_set_enabled "$client_pubkey" 0 + + cat <