feat(wireguard): amneziawg implementation (#3150)

This commit is contained in:
Zhurik
2026-03-11 16:55:28 +03:00
committed by GitHub
parent f4eeffe79a
commit e6fc792f4f
20 changed files with 635 additions and 68 deletions
+1
View File
@@ -1 +1,2 @@
scratch.txt scratch.txt
.DS_Store
+17
View File
@@ -112,6 +112,23 @@ ENV VPN_SERVICE_PROVIDER=pia \
WIREGUARD_ADDRESSES_SECRETFILE=/run/secrets/wireguard_addresses \ WIREGUARD_ADDRESSES_SECRETFILE=/run/secrets/wireguard_addresses \
WIREGUARD_MTU= \ WIREGUARD_MTU= \
WIREGUARD_IMPLEMENTATION=auto \ WIREGUARD_IMPLEMENTATION=auto \
# Wireguard AmneziaWG userspace obfuscation (requires WIREGUARD_IMPLEMENTATION=amneziawg)
AMNEZIAWG_JC=0 \
AMNEZIAWG_JMIN=0 \
AMNEZIAWG_JMAX=0 \
AMNEZIAWG_S1=0 \
AMNEZIAWG_S2=0 \
AMNEZIAWG_S3=0 \
AMNEZIAWG_S4=0 \
AMNEZIAWG_H1= \
AMNEZIAWG_H2= \
AMNEZIAWG_H3= \
AMNEZIAWG_H4= \
AMNEZIAWG_I1= \
AMNEZIAWG_I2= \
AMNEZIAWG_I3= \
AMNEZIAWG_I4= \
AMNEZIAWG_I5= \
# VPN server port forwarding # VPN server port forwarding
VPN_PORT_FORWARDING=off \ VPN_PORT_FORWARDING=off \
VPN_PORT_FORWARDING_PROVIDER= \ VPN_PORT_FORWARDING_PROVIDER= \
+1 -1
View File
@@ -4,6 +4,7 @@ go 1.25.0
require ( require (
github.com/ProtonMail/go-srp v0.0.7 github.com/ProtonMail/go-srp v0.0.7
github.com/amnezia-vpn/amneziawg-go v0.2.16
github.com/breml/rootcerts v0.3.4 github.com/breml/rootcerts v0.3.4
github.com/fatih/color v1.18.0 github.com/fatih/color v1.18.0
github.com/golang/mock v1.6.0 github.com/golang/mock v1.6.0
@@ -59,7 +60,6 @@ require (
golang.org/x/crypto v0.48.0 // indirect golang.org/x/crypto v0.48.0 // indirect
golang.org/x/mod v0.32.0 // indirect golang.org/x/mod v0.32.0 // indirect
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.41.0 // indirect golang.org/x/tools v0.41.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/protobuf v1.35.1 // indirect google.golang.org/protobuf v1.35.1 // indirect
+10 -6
View File
@@ -6,6 +6,8 @@ github.com/ProtonMail/go-crypto v1.3.0-proton h1:tAQKQRZX/73VmzK6yHSCaRUOvS/3OYS
github.com/ProtonMail/go-crypto v1.3.0-proton/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/ProtonMail/go-crypto v1.3.0-proton/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI= github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk= github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk=
github.com/amnezia-vpn/amneziawg-go v0.2.16 h1:XY6HOq/xtqH8ZXMncRWkjFs85EKdN10NLNnw23kTpE0=
github.com/amnezia-vpn/amneziawg-go v0.2.16/go.mod h1:nRkPpIzjCxMW8pZKXTRkpqAQVlmFJdVOGkeQSC7wbms=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/breml/rootcerts v0.3.4 h1:9i7WNl/ctd9OEAOaTfLy//Wrlfxq/tRQ7v4okYFN9Ys= github.com/breml/rootcerts v0.3.4 h1:9i7WNl/ctd9OEAOaTfLy//Wrlfxq/tRQ7v4okYFN9Ys=
@@ -27,8 +29,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
@@ -111,6 +113,8 @@ github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqTosly
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -165,8 +169,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@@ -193,8 +197,8 @@ gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 h1:ze1vwAdliUAr68RQ5NtufWaXaOg8WUO2OACzEV+TNdE=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.70 h1:QnLPkuDWWbD5C+3DUA2IUXai5TK6w2zff+MAGccqdsw= kernel.org/pub/linux/libs/security/libcap/cap v1.2.70 h1:QnLPkuDWWbD5C+3DUA2IUXai5TK6w2zff+MAGccqdsw=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.70/go.mod h1:/iBwcj9nbLejQitYvUm9caurITQ6WyNHibJk6Q9fiS4= kernel.org/pub/linux/libs/security/libcap/cap v1.2.70/go.mod h1:/iBwcj9nbLejQitYvUm9caurITQ6WyNHibJk6Q9fiS4=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 h1:HsB2G/rEQiYyo1bGoQqHZ/Bvd6x1rERQTNdPr1FyWjI= kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 h1:HsB2G/rEQiYyo1bGoQqHZ/Bvd6x1rERQTNdPr1FyWjI=
@@ -0,0 +1,228 @@
package settings
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type AmneziaWg struct {
JunkPacketCount *uint16 `json:"junk_packet_count"`
JunkPacketMin *uint16 `json:"junk_packet_min"`
JunkPacketMax *uint16 `json:"junk_packet_max"`
PaddingS1 *uint16 `json:"padding_s1"`
PaddingS2 *uint16 `json:"padding_s2"`
PaddingS3 *uint16 `json:"padding_s3"`
PaddingS4 *uint16 `json:"padding_s4"`
HeaderH1 *string `json:"header_h1"`
HeaderH2 *string `json:"header_h2"`
HeaderH3 *string `json:"header_h3"`
HeaderH4 *string `json:"header_h4"`
InitPacketI1 *string `json:"init_packet_i1"`
InitPacketI2 *string `json:"init_packet_i2"`
InitPacketI3 *string `json:"init_packet_i3"`
InitPacketI4 *string `json:"init_packet_i4"`
InitPacketI5 *string `json:"init_packet_i5"`
}
func (s *AmneziaWg) read(r *reader.Reader) error {
uint16Fields := map[string]*uint16{
"AMNEZIAWG_JC": s.JunkPacketCount,
"AMNEZIAWG_JMIN": s.JunkPacketMin,
"AMNEZIAWG_JMAX": s.JunkPacketMax,
"AMNEZIAWG_S1": s.PaddingS1,
"AMNEZIAWG_S2": s.PaddingS2,
"AMNEZIAWG_S3": s.PaddingS3,
"AMNEZIAWG_S4": s.PaddingS4,
}
for key, dst := range uint16Fields {
v, err := r.Uint16Ptr(key)
if err != nil {
return err
} else if v != nil {
*dst = *v
}
}
stringFields := map[string]*string{
"AMNEZIAWG_H1": s.HeaderH1,
"AMNEZIAWG_H2": s.HeaderH2,
"AMNEZIAWG_H3": s.HeaderH3,
"AMNEZIAWG_H4": s.HeaderH4,
"AMNEZIAWG_I1": s.InitPacketI1,
"AMNEZIAWG_I2": s.InitPacketI2,
"AMNEZIAWG_I3": s.InitPacketI3,
"AMNEZIAWG_I4": s.InitPacketI4,
"AMNEZIAWG_I5": s.InitPacketI5,
}
opt := reader.ForceLowercase(false)
for key, dst := range stringFields {
v := r.Get(key, opt)
if v != nil {
*dst = *v
}
}
return nil
}
func (s AmneziaWg) copy() (copied AmneziaWg) {
return AmneziaWg{
JunkPacketCount: gosettings.CopyPointer(s.JunkPacketCount),
JunkPacketMin: gosettings.CopyPointer(s.JunkPacketMin),
JunkPacketMax: gosettings.CopyPointer(s.JunkPacketMax),
PaddingS1: gosettings.CopyPointer(s.PaddingS1),
PaddingS2: gosettings.CopyPointer(s.PaddingS2),
PaddingS3: gosettings.CopyPointer(s.PaddingS3),
PaddingS4: gosettings.CopyPointer(s.PaddingS4),
HeaderH1: gosettings.CopyPointer(s.HeaderH1),
HeaderH2: gosettings.CopyPointer(s.HeaderH2),
HeaderH3: gosettings.CopyPointer(s.HeaderH3),
HeaderH4: gosettings.CopyPointer(s.HeaderH4),
InitPacketI1: gosettings.CopyPointer(s.InitPacketI1),
InitPacketI2: gosettings.CopyPointer(s.InitPacketI2),
InitPacketI3: gosettings.CopyPointer(s.InitPacketI3),
InitPacketI4: gosettings.CopyPointer(s.InitPacketI4),
InitPacketI5: gosettings.CopyPointer(s.InitPacketI5),
}
}
//nolint:dupl
func (s *AmneziaWg) overrideWith(other AmneziaWg) {
s.JunkPacketCount = gosettings.OverrideWithPointer(s.JunkPacketCount, other.JunkPacketCount)
s.JunkPacketMin = gosettings.OverrideWithPointer(s.JunkPacketMin, other.JunkPacketMin)
s.JunkPacketMax = gosettings.OverrideWithPointer(s.JunkPacketMax, other.JunkPacketMax)
s.PaddingS1 = gosettings.OverrideWithPointer(s.PaddingS1, other.PaddingS1)
s.PaddingS2 = gosettings.OverrideWithPointer(s.PaddingS2, other.PaddingS2)
s.PaddingS3 = gosettings.OverrideWithPointer(s.PaddingS3, other.PaddingS3)
s.PaddingS4 = gosettings.OverrideWithPointer(s.PaddingS4, other.PaddingS4)
s.HeaderH1 = gosettings.OverrideWithPointer(s.HeaderH1, other.HeaderH1)
s.HeaderH2 = gosettings.OverrideWithPointer(s.HeaderH2, other.HeaderH2)
s.HeaderH3 = gosettings.OverrideWithPointer(s.HeaderH3, other.HeaderH3)
s.HeaderH4 = gosettings.OverrideWithPointer(s.HeaderH4, other.HeaderH4)
s.InitPacketI1 = gosettings.OverrideWithPointer(s.InitPacketI1, other.InitPacketI1)
s.InitPacketI2 = gosettings.OverrideWithPointer(s.InitPacketI2, other.InitPacketI2)
s.InitPacketI3 = gosettings.OverrideWithPointer(s.InitPacketI3, other.InitPacketI3)
s.InitPacketI4 = gosettings.OverrideWithPointer(s.InitPacketI4, other.InitPacketI4)
s.InitPacketI5 = gosettings.OverrideWithPointer(s.InitPacketI5, other.InitPacketI5)
}
func (s *AmneziaWg) setDefaults() {
s.JunkPacketCount = gosettings.DefaultPointer(s.JunkPacketCount, 0)
s.JunkPacketMin = gosettings.DefaultPointer(s.JunkPacketMin, 0)
s.JunkPacketMax = gosettings.DefaultPointer(s.JunkPacketMax, 0)
s.PaddingS1 = gosettings.DefaultPointer(s.PaddingS1, 0)
s.PaddingS2 = gosettings.DefaultPointer(s.PaddingS2, 0)
s.PaddingS3 = gosettings.DefaultPointer(s.PaddingS3, 0)
s.PaddingS4 = gosettings.DefaultPointer(s.PaddingS4, 0)
s.HeaderH1 = gosettings.DefaultPointer(s.HeaderH1, "")
s.HeaderH2 = gosettings.DefaultPointer(s.HeaderH2, "")
s.HeaderH3 = gosettings.DefaultPointer(s.HeaderH3, "")
s.HeaderH4 = gosettings.DefaultPointer(s.HeaderH4, "")
s.InitPacketI1 = gosettings.DefaultPointer(s.InitPacketI1, "")
s.InitPacketI2 = gosettings.DefaultPointer(s.InitPacketI2, "")
s.InitPacketI3 = gosettings.DefaultPointer(s.InitPacketI3, "")
s.InitPacketI4 = gosettings.DefaultPointer(s.InitPacketI4, "")
s.InitPacketI5 = gosettings.DefaultPointer(s.InitPacketI5, "")
}
func (s AmneziaWg) toLinesNode() (node *gotree.Node) {
node = gotree.New("Amneziawg parameters:")
uintFields := []struct {
key string
val *uint16
}{
{"jc", s.JunkPacketCount},
{"jmin", s.JunkPacketMin},
{"jmax", s.JunkPacketMax},
{"s1", s.PaddingS1},
{"s2", s.PaddingS2},
{"s3", s.PaddingS3},
{"s4", s.PaddingS4},
}
for _, f := range uintFields {
node.Appendf("%s: %d", f.key, *f.val)
}
stringFields := []struct {
key string
val *string
}{
{"h1", s.HeaderH1},
{"h2", s.HeaderH2},
{"h3", s.HeaderH3},
{"h4", s.HeaderH4},
{"i1", s.InitPacketI1},
{"i2", s.InitPacketI2},
{"i3", s.InitPacketI3},
{"i4", s.InitPacketI4},
{"i5", s.InitPacketI5},
}
for _, f := range stringFields {
node.Appendf("%s: %s", f.key, *f.val)
}
return node
}
var (
ErrJunkPacketBounds = errors.New("junk packet minimum must be lower than or equal to maximum")
ErrJunkPacketMinMaxNotSet = errors.New("junk packet min and max must be set when junk packet count is set")
ErrJunkPacketCountNotSet = errors.New("junk packet count must be set when junk packet min or max is set")
ErrHeaderRangeMalformed = errors.New("header range is malformed")
)
func (s AmneziaWg) validate() error {
if *s.JunkPacketCount == 0 {
if *s.JunkPacketMin != 0 || *s.JunkPacketMax != 0 {
return fmt.Errorf("%w: jc=%d and jmin=%d and jmax=%d",
ErrJunkPacketCountNotSet, s.JunkPacketCount, *s.JunkPacketMin, *s.JunkPacketMax)
}
} else {
if *s.JunkPacketMin == 0 || *s.JunkPacketMax == 0 {
return fmt.Errorf("%w: jc=%d and jmin=%d and jmax=%d",
ErrJunkPacketMinMaxNotSet, s.JunkPacketCount, *s.JunkPacketMin, *s.JunkPacketMax)
} else if *s.JunkPacketMin > *s.JunkPacketMax {
return fmt.Errorf("%w: jmin=%d and jmax=%d",
ErrJunkPacketBounds, *s.JunkPacketMin, *s.JunkPacketMax)
}
}
nameToHeaderRange := map[string]string{
"h1": *s.HeaderH1,
"h2": *s.HeaderH2,
"h3": *s.HeaderH3,
"h4": *s.HeaderH4,
}
for name, headerRange := range nameToHeaderRange {
if headerRange == "" {
continue
}
fields := strings.Split(headerRange, "-")
switch len(fields) {
case 1:
_, err := strconv.Atoi(fields[0])
if err != nil {
return fmt.Errorf("%w: %s value %s is not a number",
ErrHeaderRangeMalformed, name, headerRange)
}
case 2: //nolint:mnd
for _, field := range fields {
_, err := strconv.Atoi(field)
if err != nil {
return fmt.Errorf("%w: %s value %s is not a valid range",
ErrHeaderRangeMalformed, name, headerRange)
}
}
default:
return fmt.Errorf("%w: %s value %s must be in the form n or n-m",
ErrHeaderRangeMalformed, name, headerRange)
}
}
return nil
}
@@ -268,6 +268,8 @@ func (o *OpenVPN) copy() (copied OpenVPN) {
// overrideWith overrides fields of the receiver // overrideWith overrides fields of the receiver
// settings object with any field set in the other // settings object with any field set in the other
// settings. // settings.
//
//nolint:dupl
func (o *OpenVPN) overrideWith(other OpenVPN) { func (o *OpenVPN) overrideWith(other OpenVPN) {
o.Version = gosettings.OverrideWithComparable(o.Version, other.Version) o.Version = gosettings.OverrideWithComparable(o.Version, other.Version)
o.User = gosettings.OverrideWithPointer(o.User, other.User) o.User = gosettings.OverrideWithPointer(o.User, other.User)
+22 -3
View File
@@ -42,10 +42,12 @@ type Wireguard struct {
// 0 indicating to use PMTUD. // 0 indicating to use PMTUD.
MTU *uint32 `json:"mtu"` MTU *uint32 `json:"mtu"`
// Implementation is the Wireguard implementation to use. // Implementation is the Wireguard implementation to use.
// It can be "auto", "userspace" or "kernelspace". // It can be "auto", "userspace", "kernelspace" or "amneziawg".
// It defaults to "auto" and cannot be the empty string // It defaults to "auto" and cannot be the empty string
// in the internal state. // in the internal state.
Implementation string `json:"implementation"` Implementation string `json:"implementation"`
// AmneziaWG contains obfuscation parameters
AmneziaWG AmneziaWg `json:"amneziawg"`
} }
var regexpInterfaceName = regexp.MustCompile(`^[a-zA-Z0-9_]+$`) var regexpInterfaceName = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
@@ -136,11 +138,16 @@ func (w Wireguard) validate(vpnProvider string, ipv6Supported bool) (err error)
ErrWireguardInterfaceNotValid, w.Interface, regexpInterfaceName) ErrWireguardInterfaceNotValid, w.Interface, regexpInterfaceName)
} }
validImplementations := []string{"auto", "userspace", "kernelspace"} validImplementations := []string{"auto", "userspace", "kernelspace", "amneziawg"}
if err := validate.IsOneOf(w.Implementation, validImplementations...); err != nil { if err := validate.IsOneOf(w.Implementation, validImplementations...); err != nil {
return fmt.Errorf("%w: %w", ErrWireguardImplementationNotValid, err) return fmt.Errorf("%w: %w", ErrWireguardImplementationNotValid, err)
} }
err = w.AmneziaWG.validate()
if err != nil {
return fmt.Errorf("amneziawg settings: %w", err)
}
return nil return nil
} }
@@ -154,6 +161,7 @@ func (w *Wireguard) copy() (copied Wireguard) {
Interface: w.Interface, Interface: w.Interface,
MTU: w.MTU, MTU: w.MTU,
Implementation: w.Implementation, Implementation: w.Implementation,
AmneziaWG: w.AmneziaWG.copy(),
} }
} }
@@ -167,6 +175,7 @@ func (w *Wireguard) overrideWith(other Wireguard) {
w.Interface = gosettings.OverrideWithComparable(w.Interface, other.Interface) w.Interface = gosettings.OverrideWithComparable(w.Interface, other.Interface)
w.MTU = gosettings.OverrideWithComparable(w.MTU, other.MTU) w.MTU = gosettings.OverrideWithComparable(w.MTU, other.MTU)
w.Implementation = gosettings.OverrideWithComparable(w.Implementation, other.Implementation) w.Implementation = gosettings.OverrideWithComparable(w.Implementation, other.Implementation)
w.AmneziaWG.overrideWith(other.AmneziaWG)
} }
func (w *Wireguard) setDefaults(vpnProvider string) { func (w *Wireguard) setDefaults(vpnProvider string) {
@@ -191,6 +200,7 @@ func (w *Wireguard) setDefaults(vpnProvider string) {
w.Interface = gosettings.DefaultComparable(w.Interface, "wg0") w.Interface = gosettings.DefaultComparable(w.Interface, "wg0")
w.MTU = gosettings.DefaultPointer(w.MTU, 0) w.MTU = gosettings.DefaultPointer(w.MTU, 0)
w.Implementation = gosettings.DefaultComparable(w.Implementation, "auto") w.Implementation = gosettings.DefaultComparable(w.Implementation, "auto")
w.AmneziaWG.setDefaults()
} }
func (w Wireguard) String() string { func (w Wireguard) String() string {
@@ -232,7 +242,11 @@ func (w Wireguard) toLinesNode() (node *gotree.Node) {
} }
if w.Implementation != "auto" { if w.Implementation != "auto" {
node.Appendf("Implementation: %s", w.Implementation) implNode := node.Appendf("Implementation: %s", w.Implementation)
if w.Implementation == "amneziawg" {
implNode.AppendNode(w.AmneziaWG.toLinesNode())
}
} }
return node return node
@@ -245,6 +259,11 @@ func (w *Wireguard) read(r *reader.Reader) (err error) {
reader.RetroKeys("WIREGUARD_INTERFACE"), reader.ForceLowercase(false)) reader.RetroKeys("WIREGUARD_INTERFACE"), reader.ForceLowercase(false))
w.Implementation = r.String("WIREGUARD_IMPLEMENTATION") w.Implementation = r.String("WIREGUARD_IMPLEMENTATION")
err = w.AmneziaWG.read(r)
if err != nil {
return err
}
addressStrings := r.CSV("WIREGUARD_ADDRESSES", reader.RetroKeys("WIREGUARD_ADDRESS")) addressStrings := r.CSV("WIREGUARD_ADDRESSES", reader.RetroKeys("WIREGUARD_ADDRESS"))
// WARNING: do not initialize w.Addresses to an empty slice // WARNING: do not initialize w.Addresses to an empty slice
// or the defaults for nordvpn will not work. // or the defaults for nordvpn will not work.
@@ -69,6 +69,38 @@ func (s *Source) Get(key string) (value string, isSet bool) {
return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointIP) return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointIP)
case "wireguard_endpoint_port": case "wireguard_endpoint_port":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointPort) return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointPort)
case "wireguard_jc":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.Jc)
case "wireguard_jmin":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.Jmin)
case "wireguard_jmax":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.Jmax)
case "wireguard_s1":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.S1)
case "wireguard_s2":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.S2)
case "wireguard_s3":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.S3)
case "wireguard_s4":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.S4)
case "wireguard_h1":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.H1)
case "wireguard_h2":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.H2)
case "wireguard_h3":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.H3)
case "wireguard_h4":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.H4)
case "wireguard_i1":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I1)
case "wireguard_i2":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I2)
case "wireguard_i3":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I3)
case "wireguard_i4":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I4)
case "wireguard_i5":
return strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I5)
} }
value, isSet, err := ReadFromFile(path) value, isSet, err := ReadFromFile(path)
@@ -25,13 +25,54 @@ func (s *Source) lazyLoadWireguardConf() WireguardConfig {
return s.cached.wireguardConf return s.cached.wireguardConf
} }
type amneziaWgConfig struct {
Jc *string
Jmin *string
Jmax *string
S1 *string
S2 *string
S3 *string
S4 *string
H1 *string
H2 *string
H3 *string
H4 *string
I1 *string
I2 *string
I3 *string
I4 *string
I5 *string
}
func parseWireguardAmneziaInterfaceSection(interfaceSection *ini.Section) amneziaWgConfig {
return amneziaWgConfig{
Jc: getINIKeyFromSection(interfaceSection, "Jc"),
Jmin: getINIKeyFromSection(interfaceSection, "Jmin"),
Jmax: getINIKeyFromSection(interfaceSection, "Jmax"),
S1: getINIKeyFromSection(interfaceSection, "S1"),
S2: getINIKeyFromSection(interfaceSection, "S2"),
S3: getINIKeyFromSection(interfaceSection, "S3"),
S4: getINIKeyFromSection(interfaceSection, "S4"),
H1: getINIKeyFromSection(interfaceSection, "H1"),
H2: getINIKeyFromSection(interfaceSection, "H2"),
H3: getINIKeyFromSection(interfaceSection, "H3"),
H4: getINIKeyFromSection(interfaceSection, "H4"),
I1: getINIKeyFromSection(interfaceSection, "I1"),
I2: getINIKeyFromSection(interfaceSection, "I2"),
I3: getINIKeyFromSection(interfaceSection, "I3"),
I4: getINIKeyFromSection(interfaceSection, "I4"),
I5: getINIKeyFromSection(interfaceSection, "I5"),
}
}
type WireguardConfig struct { type WireguardConfig struct {
PrivateKey *string PrivateKey *string
PreSharedKey *string PreSharedKey *string
Addresses *string Addresses *string
PublicKey *string PublicKey *string
EndpointIP *string EndpointIP *string
EndpointPort *string EndpointPort *string
AmneziaParams amneziaWgConfig
} }
var regexINISectionNotExist = regexp.MustCompile(`^section ".+" does not exist$`) var regexINISectionNotExist = regexp.MustCompile(`^section ".+" does not exist$`)
@@ -48,6 +89,7 @@ func ParseWireguardConf(path string) (config WireguardConfig, err error) {
interfaceSection, err := iniFile.GetSection("Interface") interfaceSection, err := iniFile.GetSection("Interface")
if err == nil { if err == nil {
config.PrivateKey, config.Addresses = parseWireguardInterfaceSection(interfaceSection) config.PrivateKey, config.Addresses = parseWireguardInterfaceSection(interfaceSection)
config.AmneziaParams = parseWireguardAmneziaInterfaceSection(interfaceSection)
} else if !regexINISectionNotExist.MatchString(err.Error()) { } else if !regexINISectionNotExist.MatchString(err.Error()) {
// can never happen // can never happen
return WireguardConfig{}, fmt.Errorf("getting interface section: %w", err) return WireguardConfig{}, fmt.Errorf("getting interface section: %w", err)
@@ -97,9 +97,10 @@ func Test_parseWireguardInterfaceSection(t *testing.T) {
t.Parallel() t.Parallel()
testCases := map[string]struct { testCases := map[string]struct {
iniData string iniData string
privateKey *string privateKey *string
addresses *string addresses *string
amneziaParams amneziaWgConfig
}{ }{
"no_fields": { "no_fields": {
iniData: `[Interface]`, iniData: `[Interface]`,
@@ -115,9 +116,17 @@ PrivateKey = x
[Interface] [Interface]
PrivateKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8= PrivateKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=
Address = 10.38.22.35/32 Address = 10.38.22.35/32
Jc = 4
H1 = 721391205
I1 = <b 0x1234>
`, `,
privateKey: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="), privateKey: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="),
addresses: ptrTo("10.38.22.35/32"), addresses: ptrTo("10.38.22.35/32"),
amneziaParams: amneziaWgConfig{
Jc: ptrTo("4"),
H1: ptrTo("721391205"),
I1: ptrTo("<b 0x1234>"),
},
}, },
} }
@@ -131,9 +140,11 @@ Address = 10.38.22.35/32
require.NoError(t, err) require.NoError(t, err)
privateKey, addresses := parseWireguardInterfaceSection(iniSection) privateKey, addresses := parseWireguardInterfaceSection(iniSection)
amneziaWgConfig := parseWireguardAmneziaInterfaceSection(iniSection)
assert.Equal(t, testCase.privateKey, privateKey) assert.Equal(t, testCase.privateKey, privateKey)
assert.Equal(t, testCase.addresses, addresses) assert.Equal(t, testCase.addresses, addresses)
assert.Equal(t, testCase.amneziaParams, amneziaWgConfig)
}) })
} }
} }
@@ -83,6 +83,11 @@ func (s *Source) Get(key string) (value string, isSet bool) {
return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointPort) return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointPort)
} }
value, isSet, matched := s.getAmneziaWg(key)
if matched {
return value, isSet
}
value, isSet, err := files.ReadFromFile(path) value, isSet, err := files.ReadFromFile(path)
if err != nil { if err != nil {
s.warner.Warnf("skipping %s: reading file: %s", path, err) s.warner.Warnf("skipping %s: reading file: %s", path, err)
@@ -104,3 +109,43 @@ func (s *Source) KeyTransform(key string) string {
return key return key
} }
} }
func (s *Source) getAmneziaWg(key string) (value string, isSet, matched bool) {
switch key {
case "wireguard_jc":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.Jc)
case "wireguard_jmin":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.Jmin)
case "wireguard_jmax":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.Jmax)
case "wireguard_s1":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.S1)
case "wireguard_s2":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.S2)
case "wireguard_s3":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.S3)
case "wireguard_s4":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.S4)
case "wireguard_h1":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.H1)
case "wireguard_h2":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.H2)
case "wireguard_h3":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.H3)
case "wireguard_h4":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.H4)
case "wireguard_i1":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I1)
case "wireguard_i2":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I2)
case "wireguard_i3":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I3)
case "wireguard_i4":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I4)
case "wireguard_i5":
value, isSet = strPtrToStringIsSet(s.lazyLoadWireguardConf().AmneziaParams.I5)
default:
return "", false, false
}
return value, isSet, true
}
+22
View File
@@ -16,6 +16,7 @@ func BuildWireguardSettings(connection models.Connection,
settings.PreSharedKey = *userSettings.PreSharedKey settings.PreSharedKey = *userSettings.PreSharedKey
settings.InterfaceName = userSettings.Interface settings.InterfaceName = userSettings.Interface
settings.Implementation = userSettings.Implementation settings.Implementation = userSettings.Implementation
settings.AmneziaWG = buildAmneziaWgSettings(userSettings.AmneziaWG)
if *userSettings.MTU > 0 { if *userSettings.MTU > 0 {
settings.MTU = *userSettings.MTU settings.MTU = *userSettings.MTU
} else { } else {
@@ -55,3 +56,24 @@ func BuildWireguardSettings(connection models.Connection,
return settings return settings
} }
func buildAmneziaWgSettings(s settings.AmneziaWg) wireguard.AmneziaSettings {
return wireguard.AmneziaSettings{
JunkPacketCount: *s.JunkPacketCount,
JunkPacketMin: *s.JunkPacketMin,
JunkPacketMax: *s.JunkPacketMax,
PaddingS1: *s.PaddingS1,
PaddingS2: *s.PaddingS2,
PaddingS3: *s.PaddingS3,
PaddingS4: *s.PaddingS4,
HeaderH1: *s.HeaderH1,
HeaderH2: *s.HeaderH2,
HeaderH3: *s.HeaderH3,
HeaderH4: *s.HeaderH4,
InitPacketI1: *s.InitPacketI1,
InitPacketI2: *s.InitPacketI2,
InitPacketI3: *s.InitPacketI3,
InitPacketI4: *s.InitPacketI4,
InitPacketI5: *s.InitPacketI5,
}
}
+22
View File
@@ -42,6 +42,24 @@ func Test_BuildWireguardSettings(t *testing.T) {
PersistentKeepaliveInterval: ptrTo(time.Hour), PersistentKeepaliveInterval: ptrTo(time.Hour),
Interface: "wg1", Interface: "wg1",
MTU: ptrTo(uint32(1000)), MTU: ptrTo(uint32(1000)),
AmneziaWG: settings.AmneziaWg{
JunkPacketCount: ptrTo(uint16(1)),
JunkPacketMin: ptrTo(uint16(0)),
JunkPacketMax: ptrTo(uint16(0)),
PaddingS1: ptrTo(uint16(0)),
PaddingS2: ptrTo(uint16(0)),
PaddingS3: ptrTo(uint16(0)),
PaddingS4: ptrTo(uint16(0)),
HeaderH1: ptrTo("x"),
HeaderH2: ptrTo(""),
HeaderH3: ptrTo(""),
HeaderH4: ptrTo(""),
InitPacketI1: ptrTo(""),
InitPacketI2: ptrTo(""),
InitPacketI3: ptrTo(""),
InitPacketI4: ptrTo(""),
InitPacketI5: ptrTo(""),
},
}, },
ipv6Supported: false, ipv6Supported: false,
settings: wireguard.Settings{ settings: wireguard.Settings{
@@ -60,6 +78,10 @@ func Test_BuildWireguardSettings(t *testing.T) {
RulePriority: 101, RulePriority: 101,
IPv6: boolPtr(false), IPv6: boolPtr(false),
MTU: 1000, MTU: 1000,
AmneziaWG: wireguard.AmneziaSettings{
JunkPacketCount: 1,
HeaderH1: "x",
},
}, },
}, },
} }
+58
View File
@@ -0,0 +1,58 @@
package wireguard
import (
"fmt"
"strings"
)
type AmneziaSettings struct {
JunkPacketCount uint16
JunkPacketMin uint16
JunkPacketMax uint16
PaddingS1 uint16
PaddingS2 uint16
PaddingS3 uint16
PaddingS4 uint16
HeaderH1 string
HeaderH2 string
HeaderH3 string
HeaderH4 string
InitPacketI1 string
InitPacketI2 string
InitPacketI3 string
InitPacketI4 string
InitPacketI5 string
}
func (s AmneziaSettings) uapiConfig() string {
uintFields := map[string]uint16{
"jc": s.JunkPacketCount,
"jmin": s.JunkPacketMin,
"jmax": s.JunkPacketMax,
"s1": s.PaddingS1,
"s2": s.PaddingS2,
"s3": s.PaddingS3,
"s4": s.PaddingS4,
}
stringFields := map[string]string{
"h1": s.HeaderH1,
"h2": s.HeaderH2,
"h3": s.HeaderH3,
"h4": s.HeaderH4,
"i1": s.InitPacketI1,
"i2": s.InitPacketI2,
"i3": s.InitPacketI3,
"i4": s.InitPacketI4,
"i5": s.InitPacketI5,
}
lines := make([]string, 0, len(uintFields)+len(stringFields))
for key, val := range uintFields {
lines = append(lines, fmt.Sprintf("%s=%d", key, val))
}
for key, val := range stringFields {
lines = append(lines, key+"="+val)
}
return strings.Join(lines, "\n")
}
+28
View File
@@ -0,0 +1,28 @@
package wireguard
import (
"net"
)
type tunDevice interface {
Close() error
Name() (string, error)
}
type bind interface {
Close() error
}
type userspaceDevice interface {
Close()
Wait() chan struct{}
IpcHandle(net.Conn)
IpcSet(string) error
}
type userSpaceBackend struct {
createTun func(string, int) (tunDevice, error)
createBind func() bind
createDevice func(tunDevice, bind, Logger) userspaceDevice
preStart func(userspaceDevice, Settings) error
}
-11
View File
@@ -1,9 +1,5 @@
package wireguard package wireguard
import (
"golang.zx2c4.com/wireguard/device"
)
//go:generate mockgen -destination=log_mock_test.go -package wireguard . Logger //go:generate mockgen -destination=log_mock_test.go -package wireguard . Logger
type Logger interface { type Logger interface {
@@ -13,10 +9,3 @@ type Logger interface {
Error(s string) Error(s string)
Errorf(format string, args ...interface{}) Errorf(format string, args ...interface{})
} }
func makeDeviceLogger(logger Logger) (deviceLogger *device.Logger) {
return &device.Logger{
Verbosef: logger.Debugf,
Errorf: logger.Errorf,
}
}
-23
View File
@@ -1,23 +0,0 @@
package wireguard
import (
"testing"
"github.com/golang/mock/gomock"
)
func Test_makeDeviceLogger(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
logger := NewMockLogger(ctrl)
deviceLogger := makeDeviceLogger(logger)
logger.EXPECT().Debugf("test %d", 1)
deviceLogger.Verbosef("test %d", 1)
logger.EXPECT().Errorf("test %d", 2)
deviceLogger.Errorf("test %d", 2)
}
+22 -13
View File
@@ -7,9 +7,6 @@ import (
"net" "net"
"github.com/qdm12/gluetun/internal/netlink" "github.com/qdm12/gluetun/internal/netlink"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl"
) )
@@ -29,6 +26,7 @@ var (
ErrRouteAdd = errors.New("cannot add route for interface") ErrRouteAdd = errors.New("cannot add route for interface")
ErrDeviceWaited = errors.New("device waited for") ErrDeviceWaited = errors.New("device waited for")
ErrKernelSupport = errors.New("kernel does not support Wireguard") ErrKernelSupport = errors.New("kernel does not support Wireguard")
ErrAmneziaConfigure = errors.New("cannot configure AmneziaWG")
) )
// See https://git.zx2c4.com/wireguard-go/tree/main.go // See https://git.zx2c4.com/wireguard-go/tree/main.go
@@ -39,7 +37,8 @@ func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan<
return return
} }
setupFunction := setupUserSpace userspaceBackend := defaultUserSpaceBackend()
setupFunction := setupUserSpaceCommon
switch w.settings.Implementation { switch w.settings.Implementation {
case "auto": //nolint:goconst case "auto": //nolint:goconst
if !kernelSupported { if !kernelSupported {
@@ -55,6 +54,8 @@ func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan<
return return
} }
setupFunction = setupKernelSpace setupFunction = setupKernelSpace
case "amneziawg":
userspaceBackend = amneziaUserSpaceBackend()
default: default:
panic(fmt.Sprintf("unknown implementation %q", w.settings.Implementation)) panic(fmt.Sprintf("unknown implementation %q", w.settings.Implementation))
} }
@@ -71,7 +72,7 @@ func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan<
defer closers.cleanup(w.logger) defer closers.cleanup(w.logger)
linkIndex, waitAndCleanup, err := setupFunction(ctx, linkIndex, waitAndCleanup, err := setupFunction(ctx,
w.settings.InterfaceName, w.netlink, w.settings.MTU, &closers, w.logger) w.settings.InterfaceName, w.netlink, w.settings.MTU, &closers, w.logger, w.settings, userspaceBackend)
if err != nil { if err != nil {
waitError <- err waitError <- err
return return
@@ -136,7 +137,7 @@ type waitAndCleanupFunc func() error
func setupKernelSpace(ctx context.Context, func setupKernelSpace(ctx context.Context,
interfaceName string, netLinker NetLinker, mtu uint32, interfaceName string, netLinker NetLinker, mtu uint32,
closers *closers, logger Logger) ( closers *closers, logger Logger, _ Settings, _ userSpaceBackend) (
linkIndex uint32, waitAndCleanup waitAndCleanupFunc, err error, linkIndex uint32, waitAndCleanup waitAndCleanupFunc, err error,
) { ) {
links, err := netLinker.LinkList() links, err := netLinker.LinkList()
@@ -178,12 +179,14 @@ func setupKernelSpace(ctx context.Context,
return linkIndex, waitAndCleanup, nil return linkIndex, waitAndCleanup, nil
} }
func setupUserSpace(ctx context.Context, func setupUserSpaceCommon(ctx context.Context,
interfaceName string, netLinker NetLinker, mtu uint32, interfaceName string, netLinker NetLinker, mtu uint32,
closers *closers, logger Logger) ( closers *closers, logger Logger,
settings Settings, b userSpaceBackend,
) (
linkIndex uint32, waitAndCleanup waitAndCleanupFunc, err error, linkIndex uint32, waitAndCleanup waitAndCleanupFunc, err error,
) { ) {
tun, err := tun.CreateTUN(interfaceName, int(mtu)) tun, err := b.createTun(interfaceName, int(mtu))
if err != nil { if err != nil {
return 0, nil, fmt.Errorf("%w: %s", ErrCreateTun, err) return 0, nil, fmt.Errorf("%w: %s", ErrCreateTun, err)
} }
@@ -206,12 +209,11 @@ func setupUserSpace(ctx context.Context,
return netLinker.LinkDel(link.Index) return netLinker.LinkDel(link.Index)
}) })
bind := conn.NewDefaultBind() bind := b.createBind()
closers.add("closing bind", stepSeven, bind.Close) closers.add("closing bind", stepSeven, bind.Close)
deviceLogger := makeDeviceLogger(logger) device := b.createDevice(tun, bind, logger)
device := device.NewDevice(tun, bind, deviceLogger)
closers.add("closing Wireguard device", stepSix, func() error { closers.add("closing Wireguard device", stepSix, func() error {
device.Close() device.Close()
@@ -232,6 +234,13 @@ func setupUserSpace(ctx context.Context,
closers.add("closing UAPI listener", stepTwo, uapiListener.Close) closers.add("closing UAPI listener", stepTwo, uapiListener.Close)
if b.preStart != nil {
err = b.preStart(device, settings)
if err != nil {
return 0, nil, err
}
}
// acceptAndHandle exits when uapiListener is closed // acceptAndHandle exits when uapiListener is closed
uapiAcceptErrorCh := make(chan error) uapiAcceptErrorCh := make(chan error)
go acceptAndHandle(uapiListener, device, uapiAcceptErrorCh) go acceptAndHandle(uapiListener, device, uapiAcceptErrorCh)
@@ -255,7 +264,7 @@ func setupUserSpace(ctx context.Context,
return link.Index, waitAndCleanup, nil return link.Index, waitAndCleanup, nil
} }
func acceptAndHandle(uapi net.Listener, device *device.Device, func acceptAndHandle(uapi net.Listener, device userspaceDevice,
uapiAcceptErrorCh chan<- error, uapiAcceptErrorCh chan<- error,
) { ) {
for { // stopped by uapiFile.Close() for { // stopped by uapiFile.Close()
+5 -2
View File
@@ -46,8 +46,11 @@ type Settings struct {
// It defaults to false if left unset. // It defaults to false if left unset.
IPv6 *bool IPv6 *bool
// Implementation is the implementation to use. // Implementation is the implementation to use.
// It can be auto, kernelspace or userspace, and defaults to auto. // It can be auto, kernelspace, userspace or amneziawg,
// and defaults to auto.
Implementation string Implementation string
// AmneziaWG settings are extra obfuscation parameters
AmneziaWG AmneziaSettings
} }
func (s *Settings) SetDefaults() { func (s *Settings) SetDefaults() {
@@ -178,7 +181,7 @@ func (s *Settings) Check() (err error) {
} }
switch s.Implementation { switch s.Implementation {
case "auto", "kernelspace", "userspace": case "auto", "kernelspace", "userspace", "amneziawg":
default: default:
return fmt.Errorf("%w: %s", ErrImplementationInvalid, s.Implementation) return fmt.Errorf("%w: %s", ErrImplementationInvalid, s.Implementation)
} }
+58
View File
@@ -0,0 +1,58 @@
package wireguard
import (
amneziaconn "github.com/amnezia-vpn/amneziawg-go/conn"
amneziadevice "github.com/amnezia-vpn/amneziawg-go/device"
amneziatun "github.com/amnezia-vpn/amneziawg-go/tun"
wgconn "golang.zx2c4.com/wireguard/conn"
wgdevice "golang.zx2c4.com/wireguard/device"
wgtun "golang.zx2c4.com/wireguard/tun"
)
func defaultUserSpaceBackend() userSpaceBackend {
return userSpaceBackend{
createTun: func(name string, mtu int) (tunDevice, error) {
return wgtun.CreateTUN(name, mtu)
},
createBind: func() bind {
return wgconn.NewDefaultBind()
},
createDevice: func(td tunDevice, b bind, logger Logger) userspaceDevice {
wgtun, _ := td.(wgtun.Device)
wgBind, _ := b.(wgconn.Bind)
wgLogger := wgdevice.Logger{
Verbosef: logger.Debugf,
Errorf: logger.Errorf,
}
device := wgdevice.NewDevice(wgtun, wgBind, &wgLogger)
return device
},
preStart: nil,
}
}
func amneziaUserSpaceBackend() userSpaceBackend {
return userSpaceBackend{
createTun: func(name string, mtu int) (tunDevice, error) {
return amneziatun.CreateTUN(name, mtu)
},
createBind: func() bind {
return amneziaconn.NewDefaultBind()
},
createDevice: func(td tunDevice, b bind, logger Logger) userspaceDevice {
wgamneziaTun, _ := td.(amneziatun.Device)
wgamneziaBind, _ := b.(amneziaconn.Bind)
wgamneziaLogger := amneziadevice.Logger{
Verbosef: logger.Debugf,
Errorf: logger.Errorf,
}
device := amneziadevice.NewDevice(wgamneziaTun, wgamneziaBind, &wgamneziaLogger)
return device
},
preStart: func(ud userspaceDevice, s Settings) error {
uapiConfig := s.AmneziaWG.uapiConfig()
err := ud.IpcSet(uapiConfig)
return err
},
}
}