[PR #2882] Preserve AmneziaWG obfuscation params in WireGuardClientConfig (fixes obfuscation lost on upgrade) #3001

Open
opened 2026-10-14 21:06:47 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/amnezia-vpn/amnezia-client/pull/2882
Author: @yesihaveidea
Created: 7/26/2026
Status: 🔄 Open

Base: dev ← Head: fix/wireguard-obfuscation-roundtrip


📝 Commits (1)

  • 3633c85 fix(wireguard): preserve AmneziaWG obfuscation params across config model

📊 Changes

2 files changed (+131 additions, -3 deletions)

View changed files

📝 client/core/models/protocols/wireGuardProtocolConfig.cpp (+102 -2)
📝 client/core/models/protocols/wireGuardProtocolConfig.h (+29 -1)

📄 Description

Preserve AmneziaWG obfuscation params in WireGuardClientConfig (fixes obfuscation lost on upgrade)

Problem it solves

An existing WireGuard profile that had "Enable WireGuard obfuscation" turned on stops passing traffic after the app is updated — with no change to the profile, the server, or the network. The connection still "connects" (handshake completes), but nothing behind the tunnel is reachable; on a DPI network the server's replies stop a few seconds after the handshake. Re-importing the same config with the obfuscation checkbox makes it work again, which is exactly the surprising part: the upgrade silently strips obfuscation from an already-working profile.

Root cause

When a native WireGuard config is imported with obfuscation enabled (ImportController::processNativeWireGuardConfig), the AmneziaWG parameters (Jc/Jmin/Jmax, S1–S4, H1–H4, I1–I5, header protection key, content padding, awg timers) are stored inside the WireGuard container alongside the isObfuscationEnabled flag.

WireGuardClientConfig::fromJson/toJson (client/core/models/protocols/wireGuardProtocolConfig.cpp) only round-trips isObfuscationEnabled and drops every actual parameter. So the first time the profile passes through the typed config model (i.e. on the next launch / after an upgrade), the junk parameters are gone and the config that reaches the daemon and the tunnel is plain WireGuard. AwgClientConfig round-trips these fields; WireGuardClientConfig was missing them.

Downstream this also means localSocketController never forwards the awg params (the else if there requires the full Jc…H4 set to be defined), so the tunnel comes up unobfuscated.

Steps to reproduce

  1. Import a native WireGuard .conf and enable "Enable WireGuard obfuscation" on import.
  2. Connect — it works (obfuscated). awg showconf <tunnel> shows Jc, Jmin, Jmax, I1, etc.
  3. Update the app to a build that reads the profile through the current config model (or simply relaunch so the profile is re-serialized through WireGuardClientConfig).
  4. Connect to the same profile again.

Expected: still obfuscated, still works.
Actual: the tunnel now runs plain WireGuard — awg showconf shows no Jc/I1. On a network that blocks WireGuard by DPI, the handshake completes and a few packets flow, then the server's replies stop (~4 s in) and nothing behind the tunnel is reachable; rx_bytes freezes while tx_bytes keeps growing.

Verified on Windows ARM64: an obfuscated profile that works on 4.8 stops passing traffic on 5.0 with the same tunnel.dll, same routing, same keys; a NIC packet capture showed plain-WireGuard frames after the model round-trip, and awg showconf on the still-working 4.8 tunnel showed the obfuscation params 5.0 had dropped.

Fix

Add the AmneziaWG obfuscation fields to WireGuardClientConfig and round-trip them in fromJson/toJson, mirroring AwgClientConfig. Values are written only when present, so a genuinely plain WireGuard config emits no obfuscation keys and is unaffected.

Testing

  • Obfuscated native-WireGuard profile that regressed after the upgrade now keeps working without re-import: obfuscation params survive the round-trip, awg showconf shows jc/jmin/jmax/i1, rx_bytes grows steadily instead of freezing, RDP/traffic behind the tunnel works.
  • Plain WireGuard config (no obfuscation) unchanged — no obfuscation keys emitted, behaviour identical to before.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/amnezia-vpn/amnezia-client/pull/2882 **Author:** [@yesihaveidea](https://github.com/yesihaveidea) **Created:** 7/26/2026 **Status:** 🔄 Open **Base:** `dev` ← **Head:** `fix/wireguard-obfuscation-roundtrip` --- ### 📝 Commits (1) - [`3633c85`](https://github.com/amnezia-vpn/amnezia-client/commit/3633c853dad4ae542c1d6b3124b76e2a35b87af9) fix(wireguard): preserve AmneziaWG obfuscation params across config model ### 📊 Changes **2 files changed** (+131 additions, -3 deletions) <details> <summary>View changed files</summary> 📝 `client/core/models/protocols/wireGuardProtocolConfig.cpp` (+102 -2) 📝 `client/core/models/protocols/wireGuardProtocolConfig.h` (+29 -1) </details> ### 📄 Description # Preserve AmneziaWG obfuscation params in WireGuardClientConfig (fixes obfuscation lost on upgrade) ## Problem it solves An existing WireGuard profile that had **"Enable WireGuard obfuscation"** turned on stops passing traffic after the app is updated — with no change to the profile, the server, or the network. The connection still "connects" (handshake completes), but nothing behind the tunnel is reachable; on a DPI network the server's replies stop a few seconds after the handshake. Re-importing the same config with the obfuscation checkbox makes it work again, which is exactly the surprising part: **the upgrade silently strips obfuscation from an already-working profile.** ## Root cause When a native WireGuard config is imported with obfuscation enabled (`ImportController::processNativeWireGuardConfig`), the AmneziaWG parameters (`Jc`/`Jmin`/`Jmax`, `S1`–`S4`, `H1`–`H4`, `I1`–`I5`, header protection key, content padding, awg timers) are stored **inside the WireGuard container** alongside the `isObfuscationEnabled` flag. `WireGuardClientConfig::fromJson`/`toJson` (`client/core/models/protocols/wireGuardProtocolConfig.cpp`) only round-trips `isObfuscationEnabled` and **drops every actual parameter**. So the first time the profile passes through the typed config model (i.e. on the next launch / after an upgrade), the junk parameters are gone and the config that reaches the daemon and the tunnel is **plain WireGuard**. `AwgClientConfig` round-trips these fields; `WireGuardClientConfig` was missing them. Downstream this also means `localSocketController` never forwards the awg params (the `else if` there requires the full `Jc…H4` set to be defined), so the tunnel comes up unobfuscated. ## Steps to reproduce 1. Import a **native WireGuard** `.conf` and enable **"Enable WireGuard obfuscation"** on import. 2. Connect — it works (obfuscated). `awg showconf <tunnel>` shows `Jc`, `Jmin`, `Jmax`, `I1`, etc. 3. Update the app to a build that reads the profile through the current config model (or simply relaunch so the profile is re-serialized through `WireGuardClientConfig`). 4. Connect to the same profile again. **Expected:** still obfuscated, still works. **Actual:** the tunnel now runs plain WireGuard — `awg showconf` shows no `Jc`/`I1`. On a network that blocks WireGuard by DPI, the handshake completes and a few packets flow, then the server's replies stop (~4 s in) and nothing behind the tunnel is reachable; `rx_bytes` freezes while `tx_bytes` keeps growing. Verified on Windows ARM64: an obfuscated profile that works on 4.8 stops passing traffic on 5.0 with the **same `tunnel.dll`, same routing, same keys**; a NIC packet capture showed plain-WireGuard frames after the model round-trip, and `awg showconf` on the still-working 4.8 tunnel showed the obfuscation params 5.0 had dropped. ## Fix Add the AmneziaWG obfuscation fields to `WireGuardClientConfig` and round-trip them in `fromJson`/`toJson`, mirroring `AwgClientConfig`. Values are written only when present, so a genuinely plain WireGuard config emits no obfuscation keys and is unaffected. ## Testing - Obfuscated native-WireGuard profile that regressed after the upgrade now keeps working **without re-import**: obfuscation params survive the round-trip, `awg showconf` shows `jc`/`jmin`/`jmax`/`i1`, `rx_bytes` grows steadily instead of freezing, RDP/traffic behind the tunnel works. - Plain WireGuard config (no obfuscation) unchanged — no obfuscation keys emitted, behaviour identical to before. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-client#3001
No description provided.