Add umedbazarov/ruh-vpn: VPN/proxy manager for sing-box (#304)
* Add umedbazarov/ruh-vpn: VPN/proxy manager for sing-box New community plugin: bar widget, panel, service and control-center shortcut for managing SSH, VLESS, VMess, Shadowsocks and SOCKS5 connections through sing-box, with routing presets, custom rules, system-proxy/TUN modes and a kill switch. The bundled Python backend serves a loopback control API protected by a per-launch bearer token. * Address review: sanitize kill-switch ruleset, scope TUN capability, fix mux error path, disclose DNS - kill switch: only pre-resolved, canonicalized literal IPs enter the nft ruleset; domains are resolved first and anything unparseable is dropped, so subscription-supplied addresses can no longer inject nft syntax - TUN: CAP_NET_ADMIN is granted to a plugin-private copy of sing-box in a 0700 directory instead of the shared system binary; the copy is refreshed (clearing the cap) when the system binary changes, and the legacy grant on the shared binary is removed in the same polkit prompt - fix NameError in the mux startup failure path (undefined mux_name) that hid the log tail and skipped teardown - README: disclose plain-UDP DNS endpoints (8.8.8.8 via tunnel, 223.5.5.5 direct in rules mode) alongside the TUN DoH endpoint --------- Co-authored-by: Umedjon Bazarov <170195993+UmedjonBA@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from backend.models.server import parse_server
|
||||
from backend.storage.persistence import load_servers, save_servers
|
||||
from backend.paths import DATA_DIR
|
||||
|
||||
|
||||
def test_servers_round_trip_with_private_permissions():
|
||||
server = parse_server({
|
||||
"id": "p1", "name": "n", "protocol": "ssh",
|
||||
"host": "example.com", "port": 22, "user": "root", "password": "pw",
|
||||
})
|
||||
|
||||
async def run():
|
||||
await save_servers([server])
|
||||
return await load_servers()
|
||||
|
||||
loaded = asyncio.run(run())
|
||||
assert len(loaded) == 1
|
||||
assert loaded[0].id == "p1"
|
||||
assert loaded[0].password == "pw" # secrets persist on disk, 0600
|
||||
|
||||
server_files = [p for p in DATA_DIR.iterdir() if p.is_file()]
|
||||
assert server_files, "expected persisted files in the data dir"
|
||||
for path in server_files:
|
||||
assert (os.stat(path).st_mode & 0o777) == 0o600, path
|
||||
Reference in New Issue
Block a user