mirror of
https://github.com/qdm12/gluetun.git
synced 2025-12-10 10:45:38 -06:00
169 lines
5.1 KiB
Go
169 lines
5.1 KiB
Go
package settings
|
|
|
|
import (
|
|
"fmt"
|
|
"net/netip"
|
|
|
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
|
"github.com/qdm12/gosettings"
|
|
"github.com/qdm12/gosettings/reader"
|
|
"github.com/qdm12/gosettings/validate"
|
|
"github.com/qdm12/gotree"
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
)
|
|
|
|
type WireguardSelection struct {
|
|
// EndpointIP is the server endpoint IP address.
|
|
// It is only used with VPN providers generating Wireguard
|
|
// configurations specific to each server and user.
|
|
// To indicate it should not be used, it should be set
|
|
// to netip.IPv4Unspecified(). It can never be the zero value
|
|
// in the internal state.
|
|
EndpointIP netip.Addr `json:"endpoint_ip"`
|
|
// EndpointPort is a the server port to use for the VPN server.
|
|
// It is optional for VPN providers IVPN, Mullvad, Surfshark
|
|
// and Windscribe, and compulsory for the others.
|
|
// When optional, it can be set to 0 to indicate not use
|
|
// a custom endpoint port. It cannot be nil in the internal
|
|
// state.
|
|
EndpointPort *uint16 `json:"endpoint_port"`
|
|
// PublicKey is the server public key.
|
|
// It is only used with VPN providers generating Wireguard
|
|
// configurations specific to each server and user.
|
|
PublicKey string `json:"public_key"`
|
|
}
|
|
|
|
// Validate validates WireguardSelection settings.
|
|
// It should only be ran if the VPN type chosen is Wireguard.
|
|
func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|
// Validate EndpointIP
|
|
switch vpnProvider {
|
|
case providers.Airvpn, providers.Fastestvpn, providers.Ivpn,
|
|
providers.Mullvad, providers.Nordvpn, providers.Protonvpn,
|
|
providers.Surfshark, providers.Windscribe:
|
|
// endpoint IP addresses are baked in
|
|
case providers.Custom:
|
|
if !w.EndpointIP.IsValid() || w.EndpointIP.IsUnspecified() {
|
|
return fmt.Errorf("%w", ErrWireguardEndpointIPNotSet)
|
|
}
|
|
default: // Providers not supporting Wireguard
|
|
}
|
|
|
|
// Validate EndpointPort
|
|
switch vpnProvider {
|
|
// EndpointPort is required
|
|
case providers.Custom:
|
|
if *w.EndpointPort == 0 {
|
|
return fmt.Errorf("%w", ErrWireguardEndpointPortNotSet)
|
|
}
|
|
// EndpointPort cannot be set
|
|
case providers.Fastestvpn, providers.Nordvpn,
|
|
providers.Protonvpn, providers.Surfshark:
|
|
if *w.EndpointPort != 0 {
|
|
return fmt.Errorf("%w", ErrWireguardEndpointPortSet)
|
|
}
|
|
case providers.Airvpn, providers.Ivpn, providers.Mullvad, providers.Windscribe:
|
|
// EndpointPort is optional and can be 0
|
|
if *w.EndpointPort == 0 {
|
|
break // no custom endpoint port set
|
|
}
|
|
if vpnProvider == providers.Mullvad {
|
|
break // no restriction on custom endpoint port value
|
|
}
|
|
var allowed []uint16
|
|
switch vpnProvider {
|
|
case providers.Airvpn:
|
|
allowed = []uint16{1637, 47107}
|
|
case providers.Ivpn:
|
|
allowed = []uint16{2049, 2050, 53, 30587, 41893, 48574, 58237}
|
|
case providers.Windscribe:
|
|
allowed = []uint16{53, 80, 123, 443, 1194, 65142}
|
|
}
|
|
|
|
err = validate.IsOneOf(*w.EndpointPort, allowed...)
|
|
if err == nil {
|
|
break
|
|
}
|
|
return fmt.Errorf("%w: for VPN service provider %s: %w",
|
|
ErrWireguardEndpointPortNotAllowed, vpnProvider, err)
|
|
default: // Providers not supporting Wireguard
|
|
}
|
|
|
|
// Validate PublicKey
|
|
switch vpnProvider {
|
|
case providers.Fastestvpn, providers.Ivpn, providers.Mullvad,
|
|
providers.Surfshark, providers.Windscribe:
|
|
// public keys are baked in
|
|
case providers.Custom:
|
|
if w.PublicKey == "" {
|
|
return fmt.Errorf("%w", ErrWireguardPublicKeyNotSet)
|
|
}
|
|
default: // Providers not supporting Wireguard
|
|
}
|
|
if w.PublicKey != "" {
|
|
_, err := wgtypes.ParseKey(w.PublicKey)
|
|
if err != nil {
|
|
return fmt.Errorf("%w: %s: %s",
|
|
ErrWireguardPublicKeyNotValid, w.PublicKey, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (w *WireguardSelection) copy() (copied WireguardSelection) {
|
|
return WireguardSelection{
|
|
EndpointIP: w.EndpointIP,
|
|
EndpointPort: gosettings.CopyPointer(w.EndpointPort),
|
|
PublicKey: w.PublicKey,
|
|
}
|
|
}
|
|
|
|
func (w *WireguardSelection) overrideWith(other WireguardSelection) {
|
|
w.EndpointIP = gosettings.OverrideWithValidator(w.EndpointIP, other.EndpointIP)
|
|
w.EndpointPort = gosettings.OverrideWithPointer(w.EndpointPort, other.EndpointPort)
|
|
w.PublicKey = gosettings.OverrideWithComparable(w.PublicKey, other.PublicKey)
|
|
}
|
|
|
|
func (w *WireguardSelection) setDefaults() {
|
|
w.EndpointIP = gosettings.DefaultValidator(w.EndpointIP, netip.IPv4Unspecified())
|
|
w.EndpointPort = gosettings.DefaultPointer(w.EndpointPort, 0)
|
|
}
|
|
|
|
func (w WireguardSelection) String() string {
|
|
return w.toLinesNode().String()
|
|
}
|
|
|
|
func (w WireguardSelection) toLinesNode() (node *gotree.Node) {
|
|
node = gotree.New("Wireguard selection settings:")
|
|
|
|
if !w.EndpointIP.IsUnspecified() {
|
|
node.Appendf("Endpoint IP address: %s", w.EndpointIP)
|
|
}
|
|
|
|
if *w.EndpointPort != 0 {
|
|
node.Appendf("Endpoint port: %d", *w.EndpointPort)
|
|
}
|
|
|
|
if w.PublicKey != "" {
|
|
node.Appendf("Server public key: %s", w.PublicKey)
|
|
}
|
|
|
|
return node
|
|
}
|
|
|
|
func (w *WireguardSelection) read(r *reader.Reader) (err error) {
|
|
w.EndpointIP, err = r.NetipAddr("WIREGUARD_ENDPOINT_IP", reader.RetroKeys("VPN_ENDPOINT_IP"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
w.EndpointPort, err = r.Uint16Ptr("WIREGUARD_ENDPOINT_PORT", reader.RetroKeys("VPN_ENDPOINT_PORT"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
w.PublicKey = r.String("WIREGUARD_PUBLIC_KEY", reader.ForceLowercase(false))
|
|
return nil
|
|
}
|