Add AmneziaWG mode (wireproxy-awg userspace SOCKS)

Third mode alongside proxy and VLESS+Reality. Bundles wireproxy-awg built
from a pinned, audited source commit (fork of wireproxy over the official
amneziawg-go), exposing a local SOCKS5 tunnel with no TUN/driver/admin.
User picks their AmneziaWG .conf; the app appends a [Socks5] section and
routes Claude through it with the same fail-closed firewall.

build-wireproxy.ps1 reproducibly builds the binary (portable Go + pinned
commit); it is embedded as a resource and not stored in git.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 14:58:21 +03:00
parent 88d5486607
commit 39b58e1a77
8 changed files with 266 additions and 39 deletions
+19 -14
View File
@@ -10,9 +10,10 @@ namespace ClaudeProxy
{
class Config
{
public string Mode = "proxy"; // proxy | vless
public string Mode = "proxy"; // proxy | vless | awg
public string Proxy = Core.DefaultProxy;
public string Vless = "";
public string Awg = ""; // path to the user's AmneziaWG .conf
public int LocalPort = 10808;
public bool FailClosed = true;
}
@@ -163,24 +164,27 @@ namespace ClaudeProxy
catch { }
return set;
}
static HashSet<int> XrayPids()
static HashSet<int> ProcPids(string name)
{
var set = new HashSet<int>();
foreach (var p in Process.GetProcessesByName("xray"))
foreach (var p in Process.GetProcessesByName(name))
try { if (p.MainModule.FileName.StartsWith(Core.BinDir, StringComparison.OrdinalIgnoreCase)) set.Add(p.Id); }
catch { }
return set;
}
static HashSet<int> XrayPids() { return ProcPids("xray"); }
static HashSet<int> AwgPids() { return ProcPids("wireproxy-awg"); }
public static VerifyResult Run(Config cfg)
{
var r = new VerifyResult { Mode = cfg.Mode };
HashSet<string> proxySet;
bool loopIsProxy;
if (cfg.Mode == "vless")
if (cfg.Mode == "vless" || cfg.Mode == "awg")
{
proxySet = new HashSet<string> { "127.0.0.1", "::1" };
r.Endpoint = "127.0.0.1:" + cfg.LocalPort + " (VLESS+Reality)";
string lbl = cfg.Mode == "awg" ? "AmneziaWG" : "VLESS+Reality";
r.Endpoint = "127.0.0.1:" + cfg.LocalPort + " (" + lbl + ")";
loopIsProxy = true;
}
else
@@ -205,22 +209,23 @@ namespace ClaudeProxy
r.Leaks.Add(entry);
}
if (cfg.Mode == "vless")
if (cfg.Mode == "vless" || cfg.Mode == "awg")
{
var xp = XrayPids();
if (xp.Count == 0) r.Tunnel = "xray НЕ запущен";
string proc = cfg.Mode == "awg" ? "wireproxy-awg" : "xray";
var tp = cfg.Mode == "awg" ? AwgPids() : XrayPids();
if (tp.Count == 0) r.Tunnel = proc + " НЕ запущен";
else
{
var xc = TcpConns(xp).FirstOrDefault(c => c.State == "Established" && c.Remote != "127.0.0.1" && c.Remote != "::1");
r.Tunnel = xc != null ? "xray -> " + xc.Remote + ":" + xc.RemotePort : "xray запущен, внешнего коннекта пока нет";
var xc = TcpConns(tp).FirstOrDefault(c => c.State == "Established" && c.Remote != "127.0.0.1" && c.Remote != "::1");
r.Tunnel = xc != null ? proc + " -> " + xc.Remote + ":" + xc.RemotePort : proc + " запущен, внешнего коннекта пока нет";
}
}
bool tunnelDown = cfg.Mode == "vless" && r.Tunnel == "xray НЕ запущен";
string modeLabel = cfg.Mode == "vless" ? "VLESS+Reality" : "прямой прокси";
bool tunnelDown = (cfg.Mode == "vless" || cfg.Mode == "awg") && r.Tunnel.EndsWith("НЕ запущен");
string modeLabel = cfg.Mode == "awg" ? "AmneziaWG" : (cfg.Mode == "vless" ? "VLESS+Reality" : "прямой прокси");
r.Lines.Add("Проверка трафика Claude (" + modeLabel + ")");
r.Lines.Add(" вход : " + r.Endpoint);
if (cfg.Mode == "vless") r.Lines.Add(" тоннель : " + r.Tunnel);
if (cfg.Mode == "vless" || cfg.Mode == "awg") r.Lines.Add(" тоннель : " + r.Tunnel);
r.Lines.Add(" процессов Claude: " + r.Procs);
r.Lines.Add(" через прокси : " + r.ViaProxy.Count);
r.Lines.Add(" локальные : " + r.Local.Count);
@@ -233,7 +238,7 @@ namespace ClaudeProxy
else if (r.Leaks.Count > 0) r.Verdict = "ПРОВАЛ: есть прямой трафик мимо прокси";
else if (tunnelDown) r.Verdict = "ПРОВАЛ: тоннель (xray) не запущен";
else if (r.ViaProxy.Count == 0) r.Verdict = "НЕОПРЕДЕЛЁННО: нет активных коннектов (подожди загрузки Claude)";
else if (cfg.Mode == "vless") r.Verdict = "OK: весь трафик Claude идёт в VLESS+Reality тоннель";
else if (cfg.Mode == "vless" || cfg.Mode == "awg") r.Verdict = "OK: весь трафик Claude идёт в тоннель (" + modeLabel + ")";
else r.Verdict = "OK: весь внешний трафик Claude идёт через прокси";
r.Lines.Add(" ВЕРДИКТ : " + r.Verdict);
r.Ok = r.Leaks.Count == 0 && r.ViaProxy.Count > 0 && !tunnelDown;