Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add PORT_FORWARD_ONLY env to filter for servers with port forwarding #2070

Merged
merged 1 commit into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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",
komachi marked this conversation as resolved.
Show resolved Hide resolved
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": {
qdm12 marked this conversation as resolved.
Show resolved Hide resolved
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