summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorjwijenbergh <jeroenwijenbergh@protonmail.com>2024-02-07 13:43:52 +0100
committerJeroen Wijenbergh <46386452+jwijenbergh@users.noreply.github.com>2024-02-19 14:15:07 +0100
commitc8e7424f0b9ca963c7454e3297a8d001d67d729d (patch)
tree979341ca6b67badc451aa58d3790704b3bf04386 /internal
parenta912257ad6d4260fbea9c0e3e3fb9bbefa92bb6e (diff)
WireGuard: TCP support using proxyguard
Diffstat (limited to 'internal')
-rw-r--r--internal/wireguard/wireguard.go118
-rw-r--r--internal/wireguard/wireguard_test.go93
2 files changed, 169 insertions, 42 deletions
diff --git a/internal/wireguard/wireguard.go b/internal/wireguard/wireguard.go
index cc6c577..af290ea 100644
--- a/internal/wireguard/wireguard.go
+++ b/internal/wireguard/wireguard.go
@@ -2,34 +2,110 @@
package wireguard
import (
+ "errors"
"fmt"
- "regexp"
+ "net"
- "github.com/go-errors/errors"
+ "github.com/eduvpn/eduvpn-common/internal/wireguard/ini"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
-// GenerateKey generates a WireGuard private key using wgctrl
-// It returns an error if key generation failed.
-func GenerateKey() (wgtypes.Key, error) {
- key, err := wgtypes.GeneratePrivateKey()
+func availableTCPPort() (int, error) {
+ tcpaddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
if err != nil {
- return key, errors.WrapPrefix(err, "failed generating WireGuard key", 0)
+ return -1, err
}
- return key, nil
+ ltcp, err := net.ListenTCP("tcp", tcpaddr)
+ if err != nil {
+ return -1, err
+ }
+ defer ltcp.Close()
+ return ltcp.Addr().(*net.TCPAddr).Port, nil
+}
+
+func availableUDPPort() (int, error) {
+ udpaddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
+ if err != nil {
+ return -1, err
+ }
+ ludp, err := net.ListenUDP("udp", udpaddr)
+ if err != nil {
+ return -1, err
+ }
+ defer ludp.Close()
+ return ludp.LocalAddr().(*net.UDPAddr).Port, nil
+}
+
+type Proxy struct {
+ SourcePort int
+ Listen string
+ Peer string
+}
+
+func Config(cfg string, key *wgtypes.Key, tcp bool) (string, *Proxy, error) {
+ // the key is nil if the client does not accept WireGuard
+ if key == nil {
+ return "", nil, errors.New("the server sent us a WireGuard profile but the client does not accept WireGuard")
+ }
+
+ var tcpp int
+ var proxy string
+ var err error
+
+ if tcp {
+ tcpp, err = availableTCPPort()
+ if err != nil {
+ return "", nil, err
+ }
+ udpp, err := availableUDPPort()
+ if err != nil {
+ return "", nil, err
+ }
+ proxy = fmt.Sprintf("127.0.0.1:%d", udpp)
+ }
+
+ rcfg, peer, err := configReplace(cfg, *key, proxy)
+ if err != nil {
+ return "", nil, err
+ }
+ var retP *Proxy
+ if tcp {
+ retP = &Proxy{
+ SourcePort: tcpp,
+ Listen: proxy,
+ Peer: peer,
+ }
+ }
+ return rcfg, retP, nil
}
-// ConfigAddKey takes the WireGuard configuration and adds the PrivateKey to the right section
-// FIXME: Instead of doing a regex replace, decide if we should use a parser.
-func ConfigAddKey(config string, key wgtypes.Key) string {
- interfaceSection := "[Interface]"
- InterfaceSectionEscaped := regexp.QuoteMeta(interfaceSection)
-
- // (?m) enables multi line mode
- // ^ match from beginning of line
- // $ match till end of line
- // So it matches [Interface] section exactly
- InterfaceRe := regexp.MustCompile(fmt.Sprintf("(?m)^%s$", InterfaceSectionEscaped))
- toReplace := fmt.Sprintf("%s\nPrivateKey = %s", interfaceSection, key.String())
- return InterfaceRe.ReplaceAllString(config, toReplace)
+// ConfigReplace replaces the wireguard config with our private key and proxy in case of TCP
+func configReplace(cfg string, key wgtypes.Key, proxy string) (string, string, error) {
+ // first parse the config
+ secs := ini.Parse(cfg)
+ if secs.Empty() {
+ return "", "", errors.New("parsed ini is empty")
+ }
+
+ // find the interface section
+ // and set the private key
+ is, err := secs.Section("Interface")
+ if err != nil {
+ return "", "", err
+ }
+ is.AddOrReplaceKeyValue("PrivateKey", key.String())
+ peer := ""
+ if proxy != "" {
+ ps, err := secs.Section("Peer")
+ if err != nil {
+ return "", "", err
+ }
+ peer, err = ps.RemoveKey("TCPEndpoint")
+ if err != nil {
+ return "", "", err
+ }
+ ps.AddOrReplaceKeyValue("Endpoint", proxy)
+ }
+
+ return secs.String(), peer, nil
}
diff --git a/internal/wireguard/wireguard_test.go b/internal/wireguard/wireguard_test.go
index 026658e..ddb9d56 100644
--- a/internal/wireguard/wireguard_test.go
+++ b/internal/wireguard/wireguard_test.go
@@ -3,12 +3,36 @@ package wireguard
import (
"fmt"
"testing"
+
+ "github.com/eduvpn/eduvpn-common/internal/test"
+ "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
-func Test_ConfigAddKey(t *testing.T) {
- config := `
-[Interface]
+func TestConfigReplace(t *testing.T) {
+ k, err := wgtypes.GeneratePrivateKey()
+ if err != nil {
+ t.Fatalf("Failed to generate key for wg config replace: %v", err)
+ }
+ cases := []struct {
+ config string
+ proxy string
+ want string
+ wantep string
+ werr string
+ }{
+ {
+ config: `
+`,
+ want: "",
+ wantep: "",
+ proxy: "",
+ werr: "parsed ini is empty",
+ },
+ {
+ config: `
+[Interface]
+PrivateKey = bla
[interface]
[interface2]
@@ -18,29 +42,56 @@ interface
[Interface]
[Interface]test
-`
- wgKey, wgKeyErr := GenerateKey()
-
- if wgKeyErr != nil {
- t.Fatalf("WireGuard config add key, generate key error: %v", wgKeyErr)
- }
- expectedConfig := fmt.Sprintf(`
-[Interface]
+`,
+ want: fmt.Sprintf(`[Interface]
PrivateKey = %s
-
[interface]
-
[interface2]
+`, k.String()),
+ wantep: "",
+ proxy: "",
+ werr: "",
+ },
+ {
+ config: `
+[Interface]
+MTU = 1392
+PrivateKey =
+Address = 10.146.176.5/24,fdee:1ead:29e8:22a2::5/64
+DNS = 9.9.9.9,2620:fe::fe
-interface
-
- [Interface]
+[Peer]
+PublicKey =
+AllowedIPs = 0.0.0.0/0,::/0
+# TCPEndpoint is a proprietary eduVPN / Let's Connect! extension
+# See https://docs.eduvpn.org/server/v3/proxyguard.html#client on how to use the TCP proxy
+TCPEndpoint = vpn.example.org:51820
+`,
+ want: fmt.Sprintf(`[Interface]
+MTU = 1392
+PrivateKey = %s
+Address = 10.146.176.5/24,fdee:1ead:29e8:22a2::5/64
+DNS = 9.9.9.9,2620:fe::fe
+[Peer]
+PublicKey =
+AllowedIPs = 0.0.0.0/0,::/0
+Endpoint = 127.0.0.1:1337
+`, k.String()),
+ wantep: "vpn.example.org:51820",
+ proxy: "127.0.0.1:1337",
+ werr: "",
+ },
+ }
-[Interface]test
-`, wgKey.String())
- gotConfig := ConfigAddKey(config, wgKey)
+ for _, c := range cases {
+ gcfg, gep, err := configReplace(c.config, k, c.proxy)
+ test.AssertError(t, err, c.werr)
+ if gcfg != c.want {
+ t.Fatalf("Got config: %s, not equal to config: %s", gcfg, c.want)
+ }
- if gotConfig != expectedConfig {
- t.Fatalf("Got: %s, Want: %s", gotConfig, expectedConfig)
+ if gep != c.wantep {
+ t.Fatalf("Got endpoint: %s, not equal to endpoint: %s", gep, c.wantep)
+ }
}
}