mirror of
https://github.com/qdm12/gluetun.git
synced 2026-05-06 20:10:11 +02:00
feat(wireguard): amneziawg implementation (#3150)
This commit is contained in:
@@ -1 +1,2 @@
|
||||
scratch.txt
|
||||
.DS_Store
|
||||
|
||||
+17
@@ -112,6 +112,23 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
||||
WIREGUARD_ADDRESSES_SECRETFILE=/run/secrets/wireguard_addresses \
|
||||
WIREGUARD_MTU= \
|
||||
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_PORT_FORWARDING=off \
|
||||
VPN_PORT_FORWARDING_PROVIDER= \
|
||||
|
||||
@@ -4,6 +4,7 @@ go 1.25.0
|
||||
|
||||
require (
|
||||
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/fatih/color v1.18.0
|
||||
github.com/golang/mock v1.6.0
|
||||
@@ -59,7 +60,6 @@ require (
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/mod v0.32.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.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
|
||||
@@ -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-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
|
||||
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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
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/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/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
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/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
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.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
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.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
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-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 h1:ze1vwAdliUAr68RQ5NtufWaXaOg8WUO2OACzEV+TNdE=
|
||||
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/go.mod h1:/iBwcj9nbLejQitYvUm9caurITQ6WyNHibJk6Q9fiS4=
|
||||
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
|
||||
// settings object with any field set in the other
|
||||
// settings.
|
||||
//
|
||||
//nolint:dupl
|
||||
func (o *OpenVPN) overrideWith(other OpenVPN) {
|
||||
o.Version = gosettings.OverrideWithComparable(o.Version, other.Version)
|
||||
o.User = gosettings.OverrideWithPointer(o.User, other.User)
|
||||
|
||||
@@ -42,10 +42,12 @@ type Wireguard struct {
|
||||
// 0 indicating to use PMTUD.
|
||||
MTU *uint32 `json:"mtu"`
|
||||
// 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
|
||||
// in the internal state.
|
||||
Implementation string `json:"implementation"`
|
||||
// AmneziaWG contains obfuscation parameters
|
||||
AmneziaWG AmneziaWg `json:"amneziawg"`
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
validImplementations := []string{"auto", "userspace", "kernelspace"}
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -154,6 +161,7 @@ func (w *Wireguard) copy() (copied Wireguard) {
|
||||
Interface: w.Interface,
|
||||
MTU: w.MTU,
|
||||
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.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) {
|
||||
@@ -191,6 +200,7 @@ 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 {
|
||||
@@ -232,7 +242,11 @@ func (w Wireguard) toLinesNode() (node *gotree.Node) {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -245,6 +259,11 @@ func (w *Wireguard) read(r *reader.Reader) (err error) {
|
||||
reader.RetroKeys("WIREGUARD_INTERFACE"), reader.ForceLowercase(false))
|
||||
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"))
|
||||
// WARNING: do not initialize w.Addresses to an empty slice
|
||||
// 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)
|
||||
case "wireguard_endpoint_port":
|
||||
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)
|
||||
|
||||
@@ -25,13 +25,54 @@ func (s *Source) lazyLoadWireguardConf() WireguardConfig {
|
||||
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 {
|
||||
PrivateKey *string
|
||||
PreSharedKey *string
|
||||
Addresses *string
|
||||
PublicKey *string
|
||||
EndpointIP *string
|
||||
EndpointPort *string
|
||||
PrivateKey *string
|
||||
PreSharedKey *string
|
||||
Addresses *string
|
||||
PublicKey *string
|
||||
EndpointIP *string
|
||||
EndpointPort *string
|
||||
AmneziaParams amneziaWgConfig
|
||||
}
|
||||
|
||||
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")
|
||||
if err == nil {
|
||||
config.PrivateKey, config.Addresses = parseWireguardInterfaceSection(interfaceSection)
|
||||
config.AmneziaParams = parseWireguardAmneziaInterfaceSection(interfaceSection)
|
||||
} else if !regexINISectionNotExist.MatchString(err.Error()) {
|
||||
// can never happen
|
||||
return WireguardConfig{}, fmt.Errorf("getting interface section: %w", err)
|
||||
|
||||
@@ -97,9 +97,10 @@ func Test_parseWireguardInterfaceSection(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := map[string]struct {
|
||||
iniData string
|
||||
privateKey *string
|
||||
addresses *string
|
||||
iniData string
|
||||
privateKey *string
|
||||
addresses *string
|
||||
amneziaParams amneziaWgConfig
|
||||
}{
|
||||
"no_fields": {
|
||||
iniData: `[Interface]`,
|
||||
@@ -115,9 +116,17 @@ PrivateKey = x
|
||||
[Interface]
|
||||
PrivateKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=
|
||||
Address = 10.38.22.35/32
|
||||
Jc = 4
|
||||
H1 = 721391205
|
||||
I1 = <b 0x1234>
|
||||
`,
|
||||
privateKey: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="),
|
||||
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)
|
||||
|
||||
privateKey, addresses := parseWireguardInterfaceSection(iniSection)
|
||||
amneziaWgConfig := parseWireguardAmneziaInterfaceSection(iniSection)
|
||||
|
||||
assert.Equal(t, testCase.privateKey, privateKey)
|
||||
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)
|
||||
}
|
||||
|
||||
value, isSet, matched := s.getAmneziaWg(key)
|
||||
if matched {
|
||||
return value, isSet
|
||||
}
|
||||
|
||||
value, isSet, err := files.ReadFromFile(path)
|
||||
if err != nil {
|
||||
s.warner.Warnf("skipping %s: reading file: %s", path, err)
|
||||
@@ -104,3 +109,43 @@ func (s *Source) KeyTransform(key string) string {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ func BuildWireguardSettings(connection models.Connection,
|
||||
settings.PreSharedKey = *userSettings.PreSharedKey
|
||||
settings.InterfaceName = userSettings.Interface
|
||||
settings.Implementation = userSettings.Implementation
|
||||
settings.AmneziaWG = buildAmneziaWgSettings(userSettings.AmneziaWG)
|
||||
if *userSettings.MTU > 0 {
|
||||
settings.MTU = *userSettings.MTU
|
||||
} else {
|
||||
@@ -55,3 +56,24 @@ func BuildWireguardSettings(connection models.Connection,
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,24 @@ func Test_BuildWireguardSettings(t *testing.T) {
|
||||
PersistentKeepaliveInterval: ptrTo(time.Hour),
|
||||
Interface: "wg1",
|
||||
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,
|
||||
settings: wireguard.Settings{
|
||||
@@ -60,6 +78,10 @@ func Test_BuildWireguardSettings(t *testing.T) {
|
||||
RulePriority: 101,
|
||||
IPv6: boolPtr(false),
|
||||
MTU: 1000,
|
||||
AmneziaWG: wireguard.AmneziaSettings{
|
||||
JunkPacketCount: 1,
|
||||
HeaderH1: "x",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
package wireguard
|
||||
|
||||
import (
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
)
|
||||
|
||||
//go:generate mockgen -destination=log_mock_test.go -package wireguard . Logger
|
||||
|
||||
type Logger interface {
|
||||
@@ -13,10 +9,3 @@ type Logger interface {
|
||||
Error(s string)
|
||||
Errorf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
func makeDeviceLogger(logger Logger) (deviceLogger *device.Logger) {
|
||||
return &device.Logger{
|
||||
Verbosef: logger.Debugf,
|
||||
Errorf: logger.Errorf,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -7,9 +7,6 @@ import (
|
||||
"net"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -29,6 +26,7 @@ var (
|
||||
ErrRouteAdd = errors.New("cannot add route for interface")
|
||||
ErrDeviceWaited = errors.New("device waited for")
|
||||
ErrKernelSupport = errors.New("kernel does not support Wireguard")
|
||||
ErrAmneziaConfigure = errors.New("cannot configure AmneziaWG")
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
setupFunction := setupUserSpace
|
||||
userspaceBackend := defaultUserSpaceBackend()
|
||||
setupFunction := setupUserSpaceCommon
|
||||
switch w.settings.Implementation {
|
||||
case "auto": //nolint:goconst
|
||||
if !kernelSupported {
|
||||
@@ -55,6 +54,8 @@ func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan<
|
||||
return
|
||||
}
|
||||
setupFunction = setupKernelSpace
|
||||
case "amneziawg":
|
||||
userspaceBackend = amneziaUserSpaceBackend()
|
||||
default:
|
||||
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)
|
||||
|
||||
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 {
|
||||
waitError <- err
|
||||
return
|
||||
@@ -136,7 +137,7 @@ type waitAndCleanupFunc func() error
|
||||
|
||||
func setupKernelSpace(ctx context.Context,
|
||||
interfaceName string, netLinker NetLinker, mtu uint32,
|
||||
closers *closers, logger Logger) (
|
||||
closers *closers, logger Logger, _ Settings, _ userSpaceBackend) (
|
||||
linkIndex uint32, waitAndCleanup waitAndCleanupFunc, err error,
|
||||
) {
|
||||
links, err := netLinker.LinkList()
|
||||
@@ -178,12 +179,14 @@ func setupKernelSpace(ctx context.Context,
|
||||
return linkIndex, waitAndCleanup, nil
|
||||
}
|
||||
|
||||
func setupUserSpace(ctx context.Context,
|
||||
func setupUserSpaceCommon(ctx context.Context,
|
||||
interfaceName string, netLinker NetLinker, mtu uint32,
|
||||
closers *closers, logger Logger) (
|
||||
closers *closers, logger Logger,
|
||||
settings Settings, b userSpaceBackend,
|
||||
) (
|
||||
linkIndex uint32, waitAndCleanup waitAndCleanupFunc, err error,
|
||||
) {
|
||||
tun, err := tun.CreateTUN(interfaceName, int(mtu))
|
||||
tun, err := b.createTun(interfaceName, int(mtu))
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("%w: %s", ErrCreateTun, err)
|
||||
}
|
||||
@@ -206,12 +209,11 @@ func setupUserSpace(ctx context.Context,
|
||||
return netLinker.LinkDel(link.Index)
|
||||
})
|
||||
|
||||
bind := conn.NewDefaultBind()
|
||||
bind := b.createBind()
|
||||
|
||||
closers.add("closing bind", stepSeven, bind.Close)
|
||||
|
||||
deviceLogger := makeDeviceLogger(logger)
|
||||
device := device.NewDevice(tun, bind, deviceLogger)
|
||||
device := b.createDevice(tun, bind, logger)
|
||||
|
||||
closers.add("closing Wireguard device", stepSix, func() error {
|
||||
device.Close()
|
||||
@@ -232,6 +234,13 @@ func setupUserSpace(ctx context.Context,
|
||||
|
||||
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
|
||||
uapiAcceptErrorCh := make(chan error)
|
||||
go acceptAndHandle(uapiListener, device, uapiAcceptErrorCh)
|
||||
@@ -255,7 +264,7 @@ func setupUserSpace(ctx context.Context,
|
||||
return link.Index, waitAndCleanup, nil
|
||||
}
|
||||
|
||||
func acceptAndHandle(uapi net.Listener, device *device.Device,
|
||||
func acceptAndHandle(uapi net.Listener, device userspaceDevice,
|
||||
uapiAcceptErrorCh chan<- error,
|
||||
) {
|
||||
for { // stopped by uapiFile.Close()
|
||||
|
||||
@@ -46,8 +46,11 @@ type Settings struct {
|
||||
// It defaults to false if left unset.
|
||||
IPv6 *bool
|
||||
// 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
|
||||
// AmneziaWG settings are extra obfuscation parameters
|
||||
AmneziaWG AmneziaSettings
|
||||
}
|
||||
|
||||
func (s *Settings) SetDefaults() {
|
||||
@@ -178,7 +181,7 @@ func (s *Settings) Check() (err error) {
|
||||
}
|
||||
|
||||
switch s.Implementation {
|
||||
case "auto", "kernelspace", "userspace":
|
||||
case "auto", "kernelspace", "userspace", "amneziawg":
|
||||
default:
|
||||
return fmt.Errorf("%w: %s", ErrImplementationInvalid, s.Implementation)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user