chore!(amneziawg): refactor to be separate from wireguard

- amneziawg is now a VPN protocol and no longer a Wireguard implementation
- Use it with VPN_TYPE=amneziawg
- document AMNEZIAWG_* options in Dockerfile
- document amneziawg support in readme
- separate amneziawg settings and code from wireguard
- re-use code from wireguard whenever possible
This commit is contained in:
Quentin McGaw
2026-03-11 16:35:18 +00:00
parent efea169495
commit b04529c380
54 changed files with 1608 additions and 741 deletions
+23 -49
View File
@@ -7,7 +7,6 @@ import (
"strings"
"time"
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
"github.com/qdm12/gluetun/internal/constants/providers"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
@@ -42,34 +41,17 @@ type Wireguard struct {
// 0 indicating to use PMTUD.
MTU *uint32 `json:"mtu"`
// Implementation is the Wireguard implementation to use.
// It can be "auto", "userspace", "kernelspace" or "amneziawg".
// It can be "auto", "userspace" or "kernelspace".
// It defaults to "auto" and cannot be the empty string
// in the internal state.
Implementation string `json:"implementation"`
// AmneziaWG contains obfuscation parameters
AmneziaWG AmneziaWg `json:"amneziawg"`
}
var regexpInterfaceName = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
// Validate validates Wireguard settings.
// It should only be ran if the VPN type chosen is Wireguard.
func (w Wireguard) validate(vpnProvider string, ipv6Supported bool) (err error) {
if !helpers.IsOneOf(vpnProvider,
providers.Airvpn,
providers.Custom,
providers.Fastestvpn,
providers.Ivpn,
providers.Mullvad,
providers.Nordvpn,
providers.Protonvpn,
providers.Surfshark,
providers.Windscribe,
) {
// do not validate for VPN provider not supporting Wireguard
return nil
}
// It should only be ran if the VPN type chosen is Wireguard or AmneziaWg.
func (w Wireguard) validate(vpnProvider string, ipv6Supported, amneziawg bool) (err error) {
// Validate PrivateKey
if *w.PrivateKey == "" {
return fmt.Errorf("%w", ErrWireguardPrivateKeyNotSet)
@@ -138,14 +120,11 @@ func (w Wireguard) validate(vpnProvider string, ipv6Supported bool) (err error)
ErrWireguardInterfaceNotValid, w.Interface, regexpInterfaceName)
}
validImplementations := []string{"auto", "userspace", "kernelspace", "amneziawg"}
if err := validate.IsOneOf(w.Implementation, validImplementations...); err != nil {
return fmt.Errorf("%w: %w", ErrWireguardImplementationNotValid, err)
}
err = w.AmneziaWG.validate()
if err != nil {
return fmt.Errorf("amneziawg settings: %w", err)
if !amneziawg { // amneziawg should have its own Implementation field and ignore this one
validImplementations := []string{"auto", "userspace", "kernelspace"}
if err := validate.IsOneOf(w.Implementation, validImplementations...); err != nil {
return fmt.Errorf("%w: %w", ErrWireguardImplementationNotValid, err)
}
}
return nil
@@ -161,7 +140,6 @@ func (w *Wireguard) copy() (copied Wireguard) {
Interface: w.Interface,
MTU: w.MTU,
Implementation: w.Implementation,
AmneziaWG: w.AmneziaWG.copy(),
}
}
@@ -175,7 +153,6 @@ func (w *Wireguard) overrideWith(other Wireguard) {
w.Interface = gosettings.OverrideWithComparable(w.Interface, other.Interface)
w.MTU = gosettings.OverrideWithComparable(w.MTU, other.MTU)
w.Implementation = gosettings.OverrideWithComparable(w.Implementation, other.Implementation)
w.AmneziaWG.overrideWith(other.AmneziaWG)
}
func (w *Wireguard) setDefaults(vpnProvider string) {
@@ -200,7 +177,6 @@ func (w *Wireguard) setDefaults(vpnProvider string) {
w.Interface = gosettings.DefaultComparable(w.Interface, "wg0")
w.MTU = gosettings.DefaultPointer(w.MTU, 0)
w.Implementation = gosettings.DefaultComparable(w.Implementation, "auto")
w.AmneziaWG.setDefaults()
}
func (w Wireguard) String() string {
@@ -242,29 +218,27 @@ func (w Wireguard) toLinesNode() (node *gotree.Node) {
}
if w.Implementation != "auto" {
implNode := node.Appendf("Implementation: %s", w.Implementation)
if w.Implementation == "amneziawg" {
implNode.AppendNode(w.AmneziaWG.toLinesNode())
}
node.Appendf("Implementation: %s", w.Implementation)
}
return node
}
func (w *Wireguard) read(r *reader.Reader) (err error) {
w.PrivateKey = r.Get("WIREGUARD_PRIVATE_KEY", reader.ForceLowercase(false))
w.PreSharedKey = r.Get("WIREGUARD_PRESHARED_KEY", reader.ForceLowercase(false))
func (w *Wireguard) read(r *reader.Reader, amneziaWG bool) (err error) {
prefix := "WIREGUARD"
if amneziaWG {
prefix = "AMNEZIAWG"
}
w.PrivateKey = r.Get(prefix+"_PRIVATE_KEY", reader.ForceLowercase(false))
w.PreSharedKey = r.Get(prefix+"_PRESHARED_KEY", reader.ForceLowercase(false))
w.Interface = r.String("VPN_INTERFACE",
reader.RetroKeys("WIREGUARD_INTERFACE"), reader.ForceLowercase(false))
w.Implementation = r.String("WIREGUARD_IMPLEMENTATION")
reader.RetroKeys(prefix+"_INTERFACE"), reader.ForceLowercase(false))
err = w.AmneziaWG.read(r)
if err != nil {
return err
if !amneziaWG {
w.Implementation = r.String("WIREGUARD_IMPLEMENTATION")
}
addressStrings := r.CSV("WIREGUARD_ADDRESSES", reader.RetroKeys("WIREGUARD_ADDRESS"))
addressStrings := r.CSV(prefix+"_ADDRESSES", reader.RetroKeys(prefix+"_ADDRESS"))
// WARNING: do not initialize w.Addresses to an empty slice
// or the defaults for nordvpn will not work.
for _, addressString := range addressStrings {
@@ -279,17 +253,17 @@ func (w *Wireguard) read(r *reader.Reader) (err error) {
w.Addresses = append(w.Addresses, address)
}
w.AllowedIPs, err = r.CSVNetipPrefixes("WIREGUARD_ALLOWED_IPS")
w.AllowedIPs, err = r.CSVNetipPrefixes(prefix + "_ALLOWED_IPS")
if err != nil {
return err // already wrapped
}
w.PersistentKeepaliveInterval, err = r.DurationPtr("WIREGUARD_PERSISTENT_KEEPALIVE_INTERVAL")
w.PersistentKeepaliveInterval, err = r.DurationPtr(prefix + "_PERSISTENT_KEEPALIVE_INTERVAL")
if err != nil {
return err
}
w.MTU, err = r.Uint32Ptr("WIREGUARD_MTU")
w.MTU, err = r.Uint32Ptr(prefix + "_MTU")
if err != nil {
return err
}