Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WireGuard Inbound (User-space WireGuard server) #2477

Merged
merged 18 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ require (
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b
h12.io/socks v1.0.3
lukechampine.com/blake3 v1.2.1
)
Expand All @@ -48,7 +49,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
go.uber.org/mock v0.3.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/mod v0.14.0 // indirect
Expand All @@ -59,5 +60,4 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect
)
3 changes: 1 addition & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,8 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 h1:tkMT5pTye+1NlKIXETU78NXw0fyjnaNHmJyyLyzw8+U=
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3/go.mod h1:cAAsePK2e15YDAMJNyOpGYEWNe4sIghTY7gpz4cX/Ik=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI=
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
Expand Down
69 changes: 46 additions & 23 deletions infra/conf/wireguard.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,31 @@ type WireGuardPeerConfig struct {
PublicKey string `json:"publicKey"`
PreSharedKey string `json:"preSharedKey"`
Endpoint string `json:"endpoint"`
KeepAlive int `json:"keepAlive"`
KeepAlive uint32 `json:"keepAlive"`
AllowedIPs []string `json:"allowedIPs,omitempty"`
}

func (c *WireGuardPeerConfig) Build() (proto.Message, error) {
var err error
config := new(wireguard.PeerConfig)

config.PublicKey, err = parseWireGuardKey(c.PublicKey)
if err != nil {
return nil, err
if c.PublicKey != "" {
config.PublicKey, err = parseWireGuardKey(c.PublicKey)
if err != nil {
return nil, err
}
}

if c.PreSharedKey != "" {
config.PreSharedKey, err = parseWireGuardKey(c.PreSharedKey)
if err != nil {
return nil, err
}
} else {
config.PreSharedKey = "0000000000000000000000000000000000000000000000000000000000000000"
}

config.Endpoint = c.Endpoint
// default 0
config.KeepAlive = int32(c.KeepAlive)
config.KeepAlive = c.KeepAlive
if c.AllowedIPs == nil {
config.AllowedIps = []string{"0.0.0.0/0", "::0/0"}
} else {
Expand All @@ -48,11 +48,14 @@ func (c *WireGuardPeerConfig) Build() (proto.Message, error) {
}

type WireGuardConfig struct {
IsClient bool `json:""`

KernelMode *bool `json:"kernelMode"`
SecretKey string `json:"secretKey"`
Address []string `json:"address"`
Peers []*WireGuardPeerConfig `json:"peers"`
MTU int `json:"mtu"`
NumWorkers int `json:"workers"`
MTU int32 `json:"mtu"`
NumWorkers int32 `json:"workers"`
Reserved []byte `json:"reserved"`
DomainStrategy string `json:"domainStrategy"`
}
Expand Down Expand Up @@ -87,11 +90,11 @@ func (c *WireGuardConfig) Build() (proto.Message, error) {
if c.MTU == 0 {
config.Mtu = 1420
} else {
config.Mtu = int32(c.MTU)
config.Mtu = c.MTU
}
// these a fallback code exists in github.com/nanoda0523/wireguard-go code,
// these a fallback code exists in wireguard-go code,
// we don't need to process fallback manually
config.NumWorkers = int32(c.NumWorkers)
config.NumWorkers = c.NumWorkers

if len(c.Reserved) != 0 && len(c.Reserved) != 3 {
return nil, newError(`"reserved" should be empty or 3 bytes`)
Expand All @@ -113,22 +116,42 @@ func (c *WireGuardConfig) Build() (proto.Message, error) {
return nil, newError("unsupported domain strategy: ", c.DomainStrategy)
}

config.IsClient = c.IsClient
if c.KernelMode != nil {
config.KernelMode = *c.KernelMode
if config.KernelMode && !wireguard.KernelTunSupported() {
newError("kernel mode is not supported on your OS or permission is insufficient").AtWarning().WriteToLog()
}
} else {
config.KernelMode = wireguard.KernelTunSupported()
if config.KernelMode {
newError("kernel mode is enabled as it's supported and permission is sufficient").AtDebug().WriteToLog()
}
}

return config, nil
}

func parseWireGuardKey(str string) (string, error) {
if len(str) != 64 {
// may in base64 form
dat, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return "", err
}
if len(dat) != 32 {
return "", newError("key should be 32 bytes: " + str)
var err error

if len(str)%2 == 0 {
_, err = hex.DecodeString(str)
if err == nil {
return str, nil
}
return hex.EncodeToString(dat), err
}

var dat []byte
str = strings.TrimSuffix(str, "=")
if strings.ContainsRune(str, '+') || strings.ContainsRune(str, '/') {
dat, err = base64.RawStdEncoding.DecodeString(str)
} else {
// already hex form
return str, nil
dat, err = base64.RawURLEncoding.DecodeString(str)
}
if err == nil {
return hex.EncodeToString(dat), nil
}

return "", newError("failed to deserialize key").Base(err)
}
15 changes: 8 additions & 7 deletions infra/conf/wireguard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/xtls/xray-core/proxy/wireguard"
)

func TestWireGuardOutbound(t *testing.T) {
func TestWireGuardConfig(t *testing.T) {
creator := func() Buildable {
return new(WireGuardConfig)
}
Expand All @@ -25,7 +25,8 @@ func TestWireGuardOutbound(t *testing.T) {
],
"mtu": 1300,
"workers": 2,
"domainStrategy": "ForceIPv6v4"
"domainStrategy": "ForceIPv6v4",
"kernelMode": false
}`,
Parser: loadJSON(creator),
Output: &wireguard.DeviceConfig{
Expand All @@ -35,16 +36,16 @@ func TestWireGuardOutbound(t *testing.T) {
Peers: []*wireguard.PeerConfig{
{
// also can read from hex form directly
PublicKey: "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a",
PreSharedKey: "0000000000000000000000000000000000000000000000000000000000000000",
Endpoint: "127.0.0.1:1234",
KeepAlive: 0,
AllowedIps: []string{"0.0.0.0/0", "::0/0"},
PublicKey: "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a",
Endpoint: "127.0.0.1:1234",
KeepAlive: 0,
AllowedIps: []string{"0.0.0.0/0", "::0/0"},
},
},
Mtu: 1300,
NumWorkers: 2,
DomainStrategy: wireguard.DeviceConfig_FORCE_IP64,
KernelMode: false,
},
},
})
Expand Down
3 changes: 2 additions & 1 deletion infra/conf/xray.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var (
"vless": func() interface{} { return new(VLessInboundConfig) },
"vmess": func() interface{} { return new(VMessInboundConfig) },
"trojan": func() interface{} { return new(TrojanServerConfig) },
"wireguard": func() interface{} { return &WireGuardConfig{IsClient: false} },
}, "protocol", "settings")

outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
Expand All @@ -37,7 +38,7 @@ var (
"vmess": func() interface{} { return new(VMessOutboundConfig) },
"trojan": func() interface{} { return new(TrojanClientConfig) },
"dns": func() interface{} { return new(DNSOutboundConfig) },
"wireguard": func() interface{} { return new(WireGuardConfig) },
"wireguard": func() interface{} { return &WireGuardConfig{IsClient: true} },
}, "protocol", "settings")

ctllog = log.New(os.Stderr, "xctl> ", 0)
Expand Down
15 changes: 12 additions & 3 deletions main/commands/all/x25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import (
)

var cmdX25519 = &base.Command{
UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"]`,
UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"] [--std-encoding]`,
Short: `Generate key pair for x25519 key exchange`,
Long: `
Generate key pair for x25519 key exchange.

Random: {{.Exec}} x25519

From private key: {{.Exec}} x25519 -i "private key (base64.RawURLEncoding)"
For Std Encoding: {{.Exec}} x25519 --std-encoding
`,
}

Expand All @@ -26,12 +27,14 @@ func init() {
}

var input_base64 = cmdX25519.Flag.String("i", "", "")
var input_stdEncoding = cmdX25519.Flag.Bool("std-encoding", false, "")

func executeX25519(cmd *base.Command, args []string) {
var output string
var err error
var privateKey []byte
var publicKey []byte
var encoding *base64.Encoding
if len(*input_base64) > 0 {
privateKey, err = base64.RawURLEncoding.DecodeString(*input_base64)
if err != nil {
Expand Down Expand Up @@ -63,9 +66,15 @@ func executeX25519(cmd *base.Command, args []string) {
goto out
}

if *input_stdEncoding {
encoding = base64.StdEncoding
} else {
encoding = base64.RawURLEncoding
}

output = fmt.Sprintf("Private key: %v\nPublic key: %v",
base64.RawURLEncoding.EncodeToString(privateKey),
base64.RawURLEncoding.EncodeToString(publicKey))
encoding.EncodeToString(privateKey),
encoding.EncodeToString(publicKey))
out:
fmt.Println(output)
}
Loading