diff --git a/Dockerfile b/Dockerfile index e548cec2..88453c85 100644 --- a/Dockerfile +++ b/Dockerfile @@ -82,6 +82,7 @@ ENV VPNSP=pia \ # Firewall FIREWALL=on \ EXTRA_SUBNETS= \ + FIREWALL_VPN_INPUT_PORTS= \ FIREWALL_DEBUG=off \ # Tinyproxy TINYPROXY=off \ diff --git a/README.md b/README.md index 5e5919d3..c4474af5 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy* - **Windscribe**: Pick the [region](https://windscribe.com/status), and optionally a custom port to use - **Surfshark**: Pick the [region](https://github.com/qdm12/private-internet-access-docker/wiki/Surfshark) or a multi hop region name - **Cyberghost**: Pick the [region](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost) and server group. -- **VyprVPN**: Pick the [region](https://www.vyprvpn.com/server-locations), port forwarding works by default +- **VyprVPN**: Pick the [region](https://www.vyprvpn.com/server-locations), port forwarding works by default (see `FIREWALL_VPN_INPUT_PORTS` though) - **NordVPN**: Pick the region and optionally the server number ### Extra niche features @@ -240,6 +240,7 @@ That one is important if you want to connect to the container from your LAN for | --- | --- | --- | --- | | `FIREWALL` | `on` | `on` or `off` | Turn on or off the container built-in firewall. You should use it for **debugging purposes** only. | | `EXTRA_SUBNETS` | | i.e. `192.168.1.0/24,192.168.10.121,10.0.0.5/28` | Comma separated subnets allowed in the container firewall | +| `FIREWALL_VPN_INPUT_PORTS` | | i.e. `1000,8080` | Comma separated list of ports to allow from the VPN server side (useful for **vyprvpn** port forwarding) | | `FIREWALL_DEBUG` | `off` | `on` or `off` | Prints every firewall related command. You should use it for **debugging purposes** only. | ### Shadowsocks diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index 53a8e2e6..e080bddb 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -16,6 +16,7 @@ import ( "github.com/qdm12/golibs/network" "github.com/qdm12/private-internet-access-docker/internal/alpine" "github.com/qdm12/private-internet-access-docker/internal/cli" + "github.com/qdm12/private-internet-access-docker/internal/constants" "github.com/qdm12/private-internet-access-docker/internal/dns" "github.com/qdm12/private-internet-access-docker/internal/firewall" gluetunLogging "github.com/qdm12/private-internet-access-docker/internal/logging" @@ -137,6 +138,11 @@ func _main(background context.Context, args []string) int { err = firewallConf.SetAllowedSubnets(ctx, allSettings.Firewall.AllowedSubnets) fatalOnError(err) + for _, vpnPort := range allSettings.Firewall.VPNInputPorts { + err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN)) + fatalOnError(err) + } + openvpnLooper := openvpn.NewLooper(allSettings.VPNSP, allSettings.OpenVPN, uid, gid, ovpnConf, firewallConf, logger, client, fileManager, streamMerger, fatalOnError) restartOpenvpn := openvpnLooper.Restart diff --git a/internal/params/firewall.go b/internal/params/firewall.go index e2ead976..665c1124 100644 --- a/internal/params/firewall.go +++ b/internal/params/firewall.go @@ -3,6 +3,7 @@ package params import ( "fmt" "net" + "strconv" "strings" libparams "github.com/qdm12/golibs/params" @@ -35,6 +36,30 @@ func (r *reader) GetExtraSubnets() (extraSubnets []net.IPNet, err error) { return extraSubnets, nil } +// GetAllowedVPNInputPorts obtains a list of input ports to allow from the +// VPN server side in the firewall, from the environment variable FIREWALL_VPN_INPUT_PORTS +func (r *reader) GetVPNInputPorts() (ports []uint16, err error) { + s, err := r.envParams.GetEnv("FIREWALL_VPN_INPUT_PORTS", libparams.Default("")) + if err != nil { + return nil, err + } + if len(s) == 0 { + return nil, nil + } + portsStr := strings.Split(s, ",") + ports = make([]uint16, len(portsStr)) + for i := range portsStr { + portInt, err := strconv.Atoi(portsStr[i]) + if err != nil { + return nil, fmt.Errorf("VPN input port %q is not valid (%s)", portInt, err) + } else if portInt <= 0 || portInt > 65535 { + return nil, fmt.Errorf("VPN input port %d must be between 1 and 65535", portInt) + } + ports[i] = uint16(portInt) + } + return ports, nil +} + // GetFirewallDebug obtains if the firewall should run in debug verbose mode from the environment variable FIREWALL_DEBUG func (r *reader) GetFirewallDebug() (debug bool, err error) { return r.envParams.GetOnOff("FIREWALL_DEBUG", libparams.Default("off")) diff --git a/internal/params/params.go b/internal/params/params.go index 03290cf9..85c1df03 100644 --- a/internal/params/params.go +++ b/internal/params/params.go @@ -42,6 +42,7 @@ type Reader interface { // Firewall getters GetFirewall() (enabled bool, err error) GetExtraSubnets() (extraSubnets []net.IPNet, err error) + GetVPNInputPorts() (ports []uint16, err error) GetFirewallDebug() (debug bool, err error) // VPN getters diff --git a/internal/settings/firewall.go b/internal/settings/firewall.go index fc0affbf..14c7203d 100644 --- a/internal/settings/firewall.go +++ b/internal/settings/firewall.go @@ -1,6 +1,7 @@ package settings import ( + "fmt" "net" "strings" @@ -10,6 +11,7 @@ import ( // Firewall contains settings to customize the firewall operation type Firewall struct { AllowedSubnets []net.IPNet + VPNInputPorts []uint16 Enabled bool Debug bool } @@ -22,9 +24,15 @@ func (f *Firewall) String() string { if !f.Enabled { return "Firewall settings: disabled" } + vpnInputPorts := make([]string, len(f.VPNInputPorts)) + for i, port := range f.VPNInputPorts { + vpnInputPorts[i] = fmt.Sprintf("%d", port) + } + settingsList := []string{ "Firewall settings:", "Allowed subnets: " + strings.Join(allowedSubnets, ", "), + "VPN input ports: " + strings.Join(vpnInputPorts, ", "), } if f.Debug { settingsList = append(settingsList, "Debug: on") @@ -38,6 +46,10 @@ func GetFirewallSettings(paramsReader params.Reader) (settings Firewall, err err if err != nil { return settings, err } + settings.VPNInputPorts, err = paramsReader.GetVPNInputPorts() + if err != nil { + return settings, err + } settings.Enabled, err = paramsReader.GetFirewall() if err != nil { return settings, err