From 2b9a5f62679684fb9b14e449e9ae575eac47ccbf Mon Sep 17 00:00:00 2001 From: Erwin Pakpahan Date: Mon, 16 Oct 2023 07:44:19 +0800 Subject: [PATCH] accept port ranges for config --- README.md | 5 +++-- internal/config/config.go | 20 ++++++++++++++++---- internal/config/test_in_memory_db.json | 6 +++++- internal/router/init.go | 4 ++-- internal/router/iptables.go | 2 +- 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 34db2b22..45ddd0fd 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ The web interface itself cannot add administrative users. `HelpMail`: The email address that is shown on the prompt page `Lockout`: Number of times a person can attempt mfa authentication before their account locks `NAT`: Turn on or off masquerading -`ExposePorts`: Expose ports on the VPN server to the client (adds rules to IPtables) example: [ "443/tcp" ] +`ExposePorts`: Expose ports on the VPN server to the client (adds rules to IPtables) example: [ "443/tcp", "100-200/udp" ] `CheckUpdates`: If enabled (off by default) the management UI will show an alert if a new version of wag is available. This talks to api.github.com `MFATemplatesDirectory`: A string path option, when set templates will be queried from disk rather than the embedded copies. Allows you to customise the MFA registration, entry, and success pages, allows custom `js` and `css` in the `MFATemplatesDirectory /custom/` directory `DownloadConfigFileName`: The filename of the wireguard config that is downloaded, defaults to `wg0.conf` @@ -286,7 +286,8 @@ Full config example { "Proxied": true, "ExposePorts": [ - "443/tcp" + "443/tcp", + "100-200/udp" ], "CheckUpdates": true, "Lockout": 5, diff --git a/internal/config/config.go b/internal/config/config.go index aaebae6b..9997e14c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -547,7 +547,7 @@ func load(path string) (c Config, err error) { for _, port := range c.ExposePorts { parts := strings.Split(port, "/") if len(parts) < 2 { - return c, errors.New(port + " is not in a valid port format. E.g 80/tcp") + return c, errors.New(port + " is not in a valid port format. E.g 80/tcp, 100-200/udp") } if c.Proxied { @@ -559,9 +559,21 @@ func load(path string) (c Config, err error) { switch strings.ToLower(parts[1]) { case "tcp", "udp": - _, err := strconv.Atoi(parts[0]) - if err != nil { - return c, errors.New(parts[0] + " is not in a valid port number. E.g 80/tcp") + scope := strings.Split(parts[0], "-") + if len(scope) == 2 { + start, errStart := strconv.Atoi(scope[0]) + end, errEnd := strconv.Atoi(scope[1]) + if (errStart != nil) || ( errEnd != nil) { + return c, errors.New(parts[0] + " Could not convert lower port or upper port to number. E.g 100:200/udp") + } + if (end < start) { + return c, errors.New(parts[0] + " port have to be smaller than end port . E.g 100-200/udp") + } + } else { + _, err := strconv.Atoi(parts[0]) + if err != nil { + return c, errors.New(parts[0] + " is not in a valid port number. E.g 80/tcp, 100-200/udp") + } } default: return c, errors.New(port + " invalid protocol (supports tcp/udp)") diff --git a/internal/config/test_in_memory_db.json b/internal/config/test_in_memory_db.json index 38ca9549..98701d85 100644 --- a/internal/config/test_in_memory_db.json +++ b/internal/config/test_in_memory_db.json @@ -23,6 +23,10 @@ "Address": "10.2.43.1/24", "MTU": 1420 }, + "ExposePorts": [ + "443/tcp", + "100-200/udp" + ], "Acls": { "Groups": { "group:nerds": [ @@ -71,4 +75,4 @@ } } } -} \ No newline at end of file +} diff --git a/internal/router/init.go b/internal/router/init.go index 17a106b3..45055e62 100644 --- a/internal/router/init.go +++ b/internal/router/init.go @@ -156,10 +156,10 @@ func TearDown() { for _, port := range config.Values().ExposePorts { parts := strings.Split(port, "/") if len(parts) < 2 { - log.Println(port + " is not in a valid port format. E.g 80/tcp") + log.Println(port + " is not in a valid port format. E.g 80/tcp, 100-200/tcp") } - err = ipt.Delete("filter", "INPUT", "-m", parts[1], "-p", parts[1], "-i", config.Values().Wireguard.DevName, "--dport", parts[0], "-j", "ACCEPT") + err = ipt.Delete("filter", "INPUT", "-m", parts[1], "-p", parts[1], "-i", config.Values().Wireguard.DevName, "--dport", strings.Replace(parts[0], "-", ":", 1), "-j", "ACCEPT") if err != nil { log.Println("unable to cleanup custom defined port", port, ":", err) } diff --git a/internal/router/iptables.go b/internal/router/iptables.go index 1ed63d0e..7afe51a1 100644 --- a/internal/router/iptables.go +++ b/internal/router/iptables.go @@ -73,7 +73,7 @@ func setupIptables() error { return errors.New(port + " is not in a valid port format. E.g 80/tcp") } - err = ipt.Append("filter", "INPUT", "-m", parts[1], "-p", parts[1], "-i", devName, "--dport", parts[0], "-j", "ACCEPT") + err = ipt.Append("filter", "INPUT", "-m", parts[1], "-p", parts[1], "-i", devName, "--dport", strings.Replace(parts[0], "-", ":", 1), "-j", "ACCEPT") if err != nil { return err }