mwgp

package module
v0.0.0-...-307cec2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 21, 2023 License: MIT Imports: 23 Imported by: 0

README

Multiple WireGuard Proxy

mwgp is a proxy software for WireGuard traffic that supports port multiplexing and experimental traffic obfuscation (see below). It is compatible with the official WireGuard client.

A common use case is to run multiple WireGuard instances on a single UDP port, each configured with AllowedIPs=0.0.0.0/0, ::/0.

How it works

See PRINCIPLES.md.

Summary: mwgp-server decrypts WireGuard handshake messages using the configured server-side private key. It is able to identify the sender of the handshake message by its public key. Then it records the corresponding sender index, which is always unencrypted, and forwards all subsequent data messages to the desired destination, according to this sender index. There is no need to decrypt data messages. The sender index is generated locally, so there is a small chance of index conflict. mwgp resolves the conflict by a mechanism called WireGuard Index Translation.

Install

go install github.com/haruue-net/mwgp/cmd/mwgp@latest

For Arch Linux users, we also maintain a PKGBUILD in the AUR.

Usage

mwgp [server|client] config.json
Server config
{
  "listen": ":1000",  // Listen address
  "timeout": 60,      // Timeout before a forwarding entry expires, in seconds
  "servers": [
    {
      "privkey": "EFt3ELmZeM/M47qFkgF4RbSOijtdHS43BNIxvxstREI=", // The private key of the WireGuard server, which is required to decrypt the handshake_initiation message for the public_key of the client
      "address": "192.0.2.1", // The IP address of the WireGuard server, which would be combined with the peer."forward_to" for a completed UDP address
      "peers": [
        {
          "pubkey": "mCXTsTRyjQKV74eWR2Ka1LIdIptCG9K0FXlrG2NC4EQ=", // The public key of the client who would be connected to the WireGuard interface listening on the "forward_to" address
          "forward_to": ":1000" // The endpoint of the server WireGuard, will be combined with the server."address" if the IP address part gets omitted
        },
        {
          "pubkey": "WKn3Dtne0ZYj/BXa6uzqMVU+xrLIQRsPA/F/SkgFsVY=",
          "forward_to": "192.0.2.2:1002" // A complete UDP address will also be accepted, for forwarding to another host other than the server."address"
        },
        {
          // If the "pubkey" is not specified, it will define a "fallback" peer which matches any unmatched public keys, this is useful for edge nodes
          "forward_to": ":1003"
        }
      ]
    },
    {
      // Servers with different private keys can be defined in one mwgp-server and share the listen port
      "privkey_file": "/etc/wireguard/private/privkey", // As an alternative to the "privkey", you can also load it from a file, just like PrivateKeyFile= in the systemd.netdev(5)
      "address": "192.0.2.3",
      "peers": [
        {
          // A client can be defined again with the same public key for another server
          "pubkey": "mCXTsTRyjQKV74eWR2Ka1LIdIptCG9K0FXlrG2NC4EQ=",
          "forward_to": ":1000"
        },
        {
          "pubkey": "OPdP2G4hfQasp/+/AZ6LiHJXIY62UKQQY4iNHJVJwH4=",
          "forward_to": ":1001"
        }
      ]
    }
  ],
  "obfs": "kisekimo, mahoumo, muryoudewaarimasen" // Obfuscation password (optional)
}
Client config

Note

You can connect to a mwgp-server endpoint directly with the official WireGuard implementation.

The mwgp-client provides additional features: customized DNS server for resolving the server address, and traffic obfuscation.

If you do not need these features, mwgp-client is not required since mwgp-server is compatible with official WireGuard clients.

{
  "server": "192.0.2.1:1000", // The endpoint of mwgp-server
  "listen": "127.10.11.1:1000", // Listen address
  "timeout": 60,      // Timeout before a forwarding entry expired, in seconds
  "server_pubkey": "S6hPS4iuvUKmnH3fp1TssT95XsHY3E3L4hqMZ68TknA=", // The public key of the WireGuard server, required by MAC computation for the handshake messages
  "client_pubkey": "mCXTsTRyjQKV74eWR2Ka1LIdIptCG9K0FXlrG2NC4EQ=", // The public key of the WireGuard client, required by MAC computation for the handshake messages
  "dns": "8.8.8.8:53", // The DNS server for server address resolving (optional)
  "obfs": "kisekimo, mahoumo, muryoudewaarimasen" // Obfuscation password (optional)
}
Forwarding Table Cache File

mwgp stores the forwarding table in a disk file to keep the forwarding rules persistent. Otherwise, a restart of mwgp would cause all peers to disconnect for 1~2 minutes, until new handshake messages are exchanged.

The cache file is under the user cache directory by default, and can be specified with the --cache-file option or the MWGP_CACHE_FILE environment variable.

Some configurations, such as forwarding destination and obfuscation settings, are also stored in the same file. As a result, the modification of these settings will not take effect until new handshake messages are exchanged.

Typically, a WireGuard initiator sends a handshake message every 2 minutes. You can always restart the client manually to send a handshake initiation message immediately.

Traffic Obfuscation (experimental)

Note

Traffic obfuscation is still an experimental feature, if you want to try it, please make sure you are using exact the same version of mwgp-client and mwgp-server.

mwgp comes with a built-in traffic obfuscator which helps you bypass some DPI. Enable this feature by setting an obfuscation password on both ends.

Highlights of mwgp obfuscation:

  • Zero MTU overhead.
  • MessageInitiation, MessageResponse and MessageCookieReply messages are padded to a random length and then obfuscated.
  • First 16 bytes of MessageTransport are obfuscated. The remaining payload is already encrypted by chacha20-poly1305.
  • mwgp-server is still compatible with vanilla WireGuard clients even with the obfuscation setting enabled. This is very useful when some clients do not run mwgp-client.

Documentation

Index

Constants

View Source
const (
	PacketFlagDeobfuscatedAfterReceived = 1 << iota
	PacketFlagObfuscateBeforeSend
)
View Source
const (
	SourceValidateLevelDefault = iota

	// SourceValidateLevelNone (1):
	//   do not validate the source address.
	//   this allows client roaming but also comes with the risk of a kind of DoS attack.
	//   this is the default behavior for ClientSourceValidateLevel.
	SourceValidateLevelNone

	// SourceValidateLevelIP (2):
	//   validate the source address only by IP.
	//   disable client roaming across different hosts,
	//   maybe compatible with some kinds of NAT.
	SourceValidateLevelIP

	// SourceValidateLevelIPAndPort (3):
	//   validate the source address by IP and port.
	//   disabled the client roaming to defeat DoS attack,
	//   but client need to wait timeout and resend the MessageInitiation
	//   if they really got their IP address changed.
	//   this is the default behavior for ServerSourceValidateLevel.
	SourceValidateLevelIPAndPort
)

Variables

View Source
var (
	DebugAlwaysGenerateProxyIndex = false
)
View Source
var UDPAddrResolverCreators = map[string]UDPAddrResolverCreator{} // Type => Creator

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClientWithConfig

func NewClientWithConfig(config *ClientConfig) (outClient *Client, err error)

func (*Client) Start

func (c *Client) Start() (err error)

type ClientConfig

type ClientConfig struct {
	Server                    string         `json:"server"`
	Listen                    string         `json:"listen"`
	Timeout                   int            `json:"timeout,omitempty"`
	Resolver                  string         `json:"resolver,omitempty"`
	ClientSourceValidateLevel int            `json:"csvl,omitempty"`
	ServerSourceValidateLevel int            `json:"ssvl,omitempty"`
	MaxPacketSize             int            `json:"max_packet_size,omitempty"`
	ClientPublicKey           NoisePublicKey `json:"client_pubkey"`
	ServerPublicKey           NoisePublicKey `json:"server_pubkey"`
	ObfuscateKey              string         `json:"obfs"`
	WGITCacheConfig

	// Deprecated: use Resolver instead
	DNS string `json:"dns,omitempty"`
}

type NoisePrivateKey

type NoisePrivateKey struct {
	device.NoisePrivateKey
}

func (*NoisePrivateKey) Base64

func (sk *NoisePrivateKey) Base64() (s string)

func (*NoisePrivateKey) FromBase64

func (sk *NoisePrivateKey) FromBase64(s string) (err error)

func (*NoisePrivateKey) MarshalJSON

func (sk *NoisePrivateKey) MarshalJSON() (result []byte, err error)

func (*NoisePrivateKey) PublicKey

func (sk *NoisePrivateKey) PublicKey() (pk NoisePublicKey)

func (*NoisePrivateKey) ReadFromFile

func (sk *NoisePrivateKey) ReadFromFile(path string) (err error)

func (*NoisePrivateKey) SharedSecret

func (sk *NoisePrivateKey) SharedSecret(pk device.NoisePublicKey) (ss [blake2s.Size]byte)

func (*NoisePrivateKey) UnmarshalJSON

func (sk *NoisePrivateKey) UnmarshalJSON(bytes []byte) (err error)

type NoisePublicKey

type NoisePublicKey struct {
	device.NoisePublicKey
}

func (*NoisePublicKey) Base64

func (pk *NoisePublicKey) Base64() (s string)

func (*NoisePublicKey) FromBase64

func (pk *NoisePublicKey) FromBase64(s string) (err error)

func (*NoisePublicKey) MarshalJSON

func (pk *NoisePublicKey) MarshalJSON() (result []byte, err error)

func (*NoisePublicKey) UnmarshalJSON

func (pk *NoisePublicKey) UnmarshalJSON(bytes []byte) (err error)

type Packet

type Packet struct {
	Data        []byte
	Length      int
	Source      *net.UDPAddr
	Destination *net.UDPAddr
	Flags       uint64
}

func (*Packet) FixMACs

func (p *Packet) FixMACs(cg *device.CookieGenerator)

func (*Packet) MessageType

func (p *Packet) MessageType() int

func (*Packet) ReceiverIndex

func (p *Packet) ReceiverIndex() (index uint32, err error)

func (*Packet) Reset

func (p *Packet) Reset()

func (*Packet) SetReceiverIndex

func (p *Packet) SetReceiverIndex(index uint32) (err error)

func (*Packet) SetSenderIndex

func (p *Packet) SetSenderIndex(index uint32) (err error)

func (*Packet) Slice

func (p *Packet) Slice() []byte

type Peer

type Peer struct {
	// contains filtered or unexported fields
}

func (*Peer) IsServerReplied

func (p *Peer) IsServerReplied() bool

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServerWithConfig

func NewServerWithConfig(config *ServerConfig) (outServer *Server, err error)

func (*Server) Start

func (s *Server) Start() (err error)

type ServerConfig

type ServerConfig struct {
	Listen        string                `json:"listen"`
	Timeout       int                   `json:"timeout,omitempty"`
	MaxPacketSize int                   `json:"max_packet_size,omitempty"`
	Servers       []*ServerConfigServer `json:"servers"`
	ObfuscateKey  string                `json:"obfs"`
	WGITCacheConfig
}

type ServerConfigPeer

type ServerConfigPeer struct {
	ForwardTo string `json:"forward_to"`

	// ClientSourceValidateLevel is same config with the one in ServerConfigServer
	// but intended to be used as a per-peer override.
	ClientSourceValidateLevel int `json:"csvl,omitempty"`

	// ServerSourceValidateLevel is same config with the one in ServerConfigServer
	// but intended to be used as a per-peer override.
	ServerSourceValidateLevel int `json:"ssvl,omitempty"`

	ClientPublicKey *NoisePublicKey `json:"pubkey,omitempty"`
	// contains filtered or unexported fields
}

type ServerConfigServer

type ServerConfigServer struct {
	PrivateKey     *NoisePrivateKey `json:"privkey"`
	PrivateKeyFile string           `json:"privkey_file,omitempty"`

	Address string              `json:"address"`
	Peers   []*ServerConfigPeer `json:"peers"`

	// ClientSourceValidateLevel specified the way to handle a MessageTransport
	// packet that comes from a source address not matches to prior packets.
	ClientSourceValidateLevel int `json:"csvl,omitempty"`

	// ServerSourceValidateLevel specified the way to handle a MessageTransport
	// packet that comes from a source address not matches to prior packets.
	ServerSourceValidateLevel int `json:"ssvl,omitempty"`
}

func (*ServerConfigServer) Initialize

func (s *ServerConfigServer) Initialize() (err error)

type UDPAddrResolver

type UDPAddrResolver interface {
	ResolveUDPAddr(ctx context.Context, address string) (addr *net.UDPAddr, err error)
}

type UDPAddrResolverCreator

type UDPAddrResolverCreator = func(url string) (resolver UDPAddrResolver, err error)

type WGITCacheConfig

type WGITCacheConfig struct {
	CacheFilePath string `json:"cache_file_path,omitempty"`
	SkipLoadCache bool   `json:"-"`
}

type WGITCacheJar

type WGITCacheJar struct {
	WGITCacheConfig
}

func (*WGITCacheJar) LoadLocked

func (c *WGITCacheJar) LoadLocked(serverMap map[uint32]*Peer, clientMap map[uint32]*Peer) (err error)

func (*WGITCacheJar) SaveLocked

func (c *WGITCacheJar) SaveLocked(clientMap map[uint32]*Peer) (err error)

type WGITCachePeer

type WGITCachePeer struct {
	ClientOriginIndex         uint32         `json:"coidx"`
	ClientProxyIndex          uint32         `json:"cpidx"`
	ClientPublicKey           NoisePublicKey `json:"cpk"`
	ClientDestination         string         `json:"cdst"`
	ClientSourceValidateLevel int            `json:"csvl"`
	ServerOriginIndex         uint32         `json:"soidx"`
	ServerProxyIndex          uint32         `json:"spidx"`
	ServerPublicKey           NoisePublicKey `json:"spk"`
	ServerDestination         string         `json:"sdst"`
	ServerSourceValidateLevel int            `json:"ssvl"`
	ObfuscateEnabled          bool           `json:"obfe"`
}

func (*WGITCachePeer) FromWGITPeer

func (cp *WGITCachePeer) FromWGITPeer(peer *Peer) (err error)

func (*WGITCachePeer) WGITPeer

func (cp *WGITCachePeer) WGITPeer() (peer *Peer, err error)

type WGITCacheTable

type WGITCacheTable struct {
	ClientMap []WGITCachePeer `json:"client_map"`
}

type WireGuardIndexTranslationTable

type WireGuardIndexTranslationTable struct {
	ClientListen          *net.UDPAddr
	ClientReadFromUDPFunc func(conn *net.UDPConn, packet *Packet) (err error)
	ClientWriteToUDPFunc  func(conn *net.UDPConn, packet *Packet) (err error)

	ServerListen          *net.UDPAddr
	ServerReadFromUDPFunc func(conn *net.UDPConn, packet *Packet) (err error)
	ServerWriteToUDPFunc  func(conn *net.UDPConn, packet *Packet) (err error)

	Timeout         time.Duration
	ExtractPeerFunc func(msg *device.MessageInitiation) (fi *ServerConfigPeer, err error)
	CacheJar        WGITCacheJar

	// UpdateAllServerDestinationChan is used to set all server address for mwgp-client (in case of DNS update).
	// this channel is not intended to be used by mwgp-server.
	UpdateAllServerDestinationChan chan *net.UDPAddr

	// MaxPacketSize is the maximum size of a WireGuard packet.
	//
	// We use the default value of 65536, which is the maximum possible size of a UDP packet.
	//
	// However, in most cases, 1500 is sufficient because WireGuard will only send
	// large UDP packets when you configure a large MTU on the WireGuard interface.
	//
	// If you are running mwgp on a server with limited memory, you can adjust this to
	// reduce memory consumption.
	MaxPacketSize uint
	// contains filtered or unexported fields
}

func NewWireGuardIndexTranslationTable

func NewWireGuardIndexTranslationTable() (table *WireGuardIndexTranslationTable)

func (*WireGuardIndexTranslationTable) Serve

func (t *WireGuardIndexTranslationTable) Serve() (err error)

type WireGuardObfuscator

type WireGuardObfuscator struct {
	ReadFromUDPFunc func(conn *net.UDPConn, packet *Packet) (err error)
	WriteToUDPFunc  func(conn *net.UDPConn, packet *Packet) (err error)
	// contains filtered or unexported fields
}

func (*WireGuardObfuscator) Deobfuscate

func (o *WireGuardObfuscator) Deobfuscate(packet *Packet)

func (*WireGuardObfuscator) Initialize

func (o *WireGuardObfuscator) Initialize(userKey string)

func (*WireGuardObfuscator) Obfuscate

func (o *WireGuardObfuscator) Obfuscate(packet *Packet)

func (*WireGuardObfuscator) ReadFromUDPWithDeobfuscate

func (o *WireGuardObfuscator) ReadFromUDPWithDeobfuscate(conn *net.UDPConn, packet *Packet) (err error)

func (*WireGuardObfuscator) WriteToUDPWithObfuscate

func (o *WireGuardObfuscator) WriteToUDPWithObfuscate(conn *net.UDPConn, packet *Packet) (err error)

Directories

Path Synopsis
cmd
resolvers
dns

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL