Skip to content

Commit

Permalink
feat: add PORT_FORWARD_ONLY env to filter for servers with port forwa…
Browse files Browse the repository at this point in the history
…rding
  • Loading branch information
komachi committed Feb 24, 2024
1 parent 4bca4ca commit 1ff9824
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 7 deletions.
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ ENV VPN_SERVICE_PROVIDER=pia \
MULTIHOP_ONLY= \
# # VPN Secure only:
PREMIUM_ONLY= \
# # PIA only:
PORT_FORWARD_ONLY= \
# Firewall
FIREWALL=on \
FIREWALL_VPN_INPUT_PORTS= \
Expand Down
29 changes: 22 additions & 7 deletions internal/configuration/settings/serverselection.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ type ServerSelection struct { //nolint:maligned
// MultiHopOnly is true if VPN servers that are not multihop
// should be filtered. This is used with Surfshark.
MultiHopOnly *bool `json:"multi_hop_only"`

// PortForwardOnly is true if VPN servers that don't support
// port forwarding should be filtered. This is used with PIA.
PortForwardOnly *bool `json:"port_forward_only"`
// OpenVPN contains settings to select OpenVPN servers
// and the final connection.
OpenVPN OpenVPNSelection `json:"openvpn"`
Expand All @@ -67,12 +69,13 @@ type ServerSelection struct { //nolint:maligned
}

var (
ErrOwnedOnlyNotSupported = errors.New("owned only filter is not supported")
ErrFreeOnlyNotSupported = errors.New("free only filter is not supported")
ErrPremiumOnlyNotSupported = errors.New("premium only filter is not supported")
ErrStreamOnlyNotSupported = errors.New("stream only filter is not supported")
ErrMultiHopOnlyNotSupported = errors.New("multi hop only filter is not supported")
ErrFreePremiumBothSet = errors.New("free only and premium only filters are both set")
ErrOwnedOnlyNotSupported = errors.New("owned only filter is not supported")
ErrFreeOnlyNotSupported = errors.New("free only filter is not supported")
ErrPremiumOnlyNotSupported = errors.New("premium only filter is not supported")
ErrStreamOnlyNotSupported = errors.New("stream only filter is not supported")
ErrMultiHopOnlyNotSupported = errors.New("multi hop only filter is not supported")
ErrPortForwardOnlyNotSupported = errors.New("port forwarding only filter is not supported")
ErrFreePremiumBothSet = errors.New("free only and premium only filters are both set")
)

func (ss *ServerSelection) validate(vpnServiceProvider string,
Expand Down Expand Up @@ -143,6 +146,15 @@ func (ss *ServerSelection) validate(vpnServiceProvider string,
ErrMultiHopOnlyNotSupported, vpnServiceProvider)
}

if *ss.PortForwardOnly &&
vpnServiceProvider != providers.PrivateInternetAccess {
// ProtonVPN also supports port forwarding, but on all their servers, so these
// don't have the port forwarding boolean field. As a consequence, we only allow
// the use of PortForwardOnly for Private Internet Access.
return fmt.Errorf("%w: for VPN service provider %s",
ErrPortForwardOnlyNotSupported, vpnServiceProvider)
}

if ss.VPN == vpn.OpenVPN {
err = ss.OpenVPN.validate(vpnServiceProvider)
if err != nil {
Expand Down Expand Up @@ -251,6 +263,7 @@ func (ss *ServerSelection) mergeWith(other ServerSelection) {
ss.PremiumOnly = gosettings.MergeWithPointer(ss.PremiumOnly, other.PremiumOnly)
ss.StreamOnly = gosettings.MergeWithPointer(ss.StreamOnly, other.StreamOnly)
ss.MultiHopOnly = gosettings.MergeWithPointer(ss.MultiHopOnly, other.MultiHopOnly)
ss.PortForwardOnly = gosettings.MergeWithPointer(ss.PortForwardOnly, other.PortForwardOnly)

ss.OpenVPN.mergeWith(other.OpenVPN)
ss.Wireguard.mergeWith(other.Wireguard)
Expand All @@ -271,6 +284,7 @@ func (ss *ServerSelection) overrideWith(other ServerSelection) {
ss.PremiumOnly = gosettings.OverrideWithPointer(ss.PremiumOnly, other.PremiumOnly)
ss.StreamOnly = gosettings.OverrideWithPointer(ss.StreamOnly, other.StreamOnly)
ss.MultiHopOnly = gosettings.OverrideWithPointer(ss.MultiHopOnly, other.MultiHopOnly)
ss.PortForwardOnly = gosettings.OverrideWithPointer(ss.PortForwardOnly, other.PortForwardOnly)
ss.OpenVPN.overrideWith(other.OpenVPN)
ss.Wireguard.overrideWith(other.Wireguard)
}
Expand All @@ -283,6 +297,7 @@ func (ss *ServerSelection) setDefaults(vpnProvider string) {
ss.PremiumOnly = gosettings.DefaultPointer(ss.PremiumOnly, false)
ss.StreamOnly = gosettings.DefaultPointer(ss.StreamOnly, false)
ss.MultiHopOnly = gosettings.DefaultPointer(ss.MultiHopOnly, false)
ss.PortForwardOnly = gosettings.DefaultPointer(ss.PortForwardOnly, false)
ss.OpenVPN.setDefaults(vpnProvider)
ss.Wireguard.setDefaults()
}
Expand Down
6 changes: 6 additions & 0 deletions internal/configuration/sources/env/serverselection.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ func (s *Source) readServerSelection(vpnProvider, vpnType string) (
return ss, err
}

// PIA only
ss.PortForwardOnly, err = s.env.BoolPtr("PORT_FORWARD_ONLY")
if err != nil {
return ss, err
}

ss.OpenVPN, err = s.readOpenVPNSelection()
if err != nil {
return ss, err
Expand Down
4 changes: 4 additions & 0 deletions internal/provider/utils/filtering.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ func filterServer(server models.Server,
return true
}

if *selection.PortForwardOnly && !server.PortForward {
return true
}

if filterByPossibilities(server.Country, selection.Countries) {
return true
}
Expand Down
13 changes: 13 additions & 0 deletions internal/provider/utils/filtering_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,19 @@ func Test_FilterServers(t *testing.T) {
{Owned: true, VPN: vpn.OpenVPN, UDP: true},
},
},
"filter by port forwarding only": {
selection: settings.ServerSelection{
PortForwardOnly: boolPtr(true),
}.WithDefaults(providers.PrivateInternetAccess),
servers: []models.Server{
{PortForward: false, VPN: vpn.OpenVPN, UDP: true},
{PortForward: true, VPN: vpn.OpenVPN, UDP: true},
{PortForward: false, VPN: vpn.OpenVPN, UDP: true},
},
filtered: []models.Server{
{PortForward: true, VPN: vpn.OpenVPN, UDP: true},
},
},
"filter by country": {
selection: settings.ServerSelection{
Countries: []string{"b"},
Expand Down
4 changes: 4 additions & 0 deletions internal/storage/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ func filterServer(server models.Server,
return true
}

if *selection.PortForwardOnly && !server.PortForward {
return true
}

if filterByPossibilities(server.Country, selection.Countries) {
return true
}
Expand Down

0 comments on commit 1ff9824

Please sign in to comment.