Fix #90 add env variable OPENVPN_TARGET_IP

This commit is contained in:
Quentin McGaw
2020-03-18 23:49:40 +00:00
parent d2b361b998
commit 9435db8e1e
11 changed files with 67 additions and 8 deletions

View File

@@ -34,6 +34,7 @@ ENV VPNSP=pia \
PROTOCOL=udp \ PROTOCOL=udp \
OPENVPN_VERBOSITY=1 \ OPENVPN_VERBOSITY=1 \
OPENVPN_ROOT=no \ OPENVPN_ROOT=no \
OPENVPN_TARGET_IP= \
TZ= \ TZ= \
# PIA only # PIA only
PASSWORD= \ PASSWORD= \

View File

@@ -161,6 +161,7 @@ docker run --rm --network=container:pia alpine:3.11 wget -qO- https://ipinfo.io
| `TZ` | | Specify a timezone to use i.e. `Europe/London` | | `TZ` | | Specify a timezone to use i.e. `Europe/London` |
| `OPENVPN_VERBOSITY` | `1` | Openvpn verbosity level from 0 to 6 | | `OPENVPN_VERBOSITY` | `1` | Openvpn verbosity level from 0 to 6 |
| `OPENVPN_ROOT` | `no` | Run OpenVPN as root, `yes` or `no` | | `OPENVPN_ROOT` | `no` | Run OpenVPN as root, `yes` or `no` |
| `OPENVPN_TARGET_IP` | | Specify a target VPN server IP address to use, valid for Mullvad and Private Internet Access |
## Connect to it ## Connect to it

View File

@@ -130,12 +130,12 @@ func main() {
var connections []models.OpenVPNConnection var connections []models.OpenVPNConnection
switch allSettings.VPNSP { switch allSettings.VPNSP {
case "pia": case "pia":
connections, err = piaConf.GetOpenVPNConnections(allSettings.PIA.Region, allSettings.OpenVPN.NetworkProtocol, allSettings.PIA.Encryption) connections, err = piaConf.GetOpenVPNConnections(allSettings.PIA.Region, allSettings.OpenVPN.NetworkProtocol, allSettings.PIA.Encryption, allSettings.OpenVPN.TargetIP)
e.FatalOnError(err) e.FatalOnError(err)
err = piaConf.BuildConf(connections, allSettings.PIA.Encryption, allSettings.OpenVPN.Verbosity, uid, gid, allSettings.OpenVPN.Root) err = piaConf.BuildConf(connections, allSettings.PIA.Encryption, allSettings.OpenVPN.Verbosity, uid, gid, allSettings.OpenVPN.Root)
e.FatalOnError(err) e.FatalOnError(err)
case "mullvad": case "mullvad":
connections, err = mullvadConf.GetOpenVPNConnections(allSettings.Mullvad.Country, allSettings.Mullvad.City, allSettings.Mullvad.ISP, allSettings.OpenVPN.NetworkProtocol, allSettings.Mullvad.Port) connections, err = mullvadConf.GetOpenVPNConnections(allSettings.Mullvad.Country, allSettings.Mullvad.City, allSettings.Mullvad.ISP, allSettings.OpenVPN.NetworkProtocol, allSettings.Mullvad.Port, allSettings.OpenVPN.TargetIP)
e.FatalOnError(err) e.FatalOnError(err)
err = mullvadConf.BuildConf(connections, allSettings.OpenVPN.Verbosity, uid, gid, allSettings.OpenVPN.Root) err = mullvadConf.BuildConf(connections, allSettings.OpenVPN.Verbosity, uid, gid, allSettings.OpenVPN.Root)
e.FatalOnError(err) e.FatalOnError(err)

View File

@@ -19,6 +19,7 @@ services:
- PROTOCOL=udp - PROTOCOL=udp
- OPENVPN_VERBOSITY=1 - OPENVPN_VERBOSITY=1
- OPENVPN_ROOT=no - OPENVPN_ROOT=no
- OPENVPN_TARGET_IP=
- TZ= - TZ=
# PIA only # PIA only

View File

@@ -2,13 +2,14 @@ package mullvad
import ( import (
"fmt" "fmt"
"net"
"github.com/qdm12/golibs/files" "github.com/qdm12/golibs/files"
"github.com/qdm12/private-internet-access-docker/internal/constants" "github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models" "github.com/qdm12/private-internet-access-docker/internal/models"
) )
func (c *configurator) GetOpenVPNConnections(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider, protocol models.NetworkProtocol, customPort uint16) (connections []models.OpenVPNConnection, err error) { func (c *configurator) GetOpenVPNConnections(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider, protocol models.NetworkProtocol, customPort uint16, targetIP net.IP) (connections []models.OpenVPNConnection, err error) {
servers := constants.MullvadServerFilter(country, city, provider) servers := constants.MullvadServerFilter(country, city, provider)
if len(servers) == 0 { if len(servers) == 0 {
return nil, fmt.Errorf("no server found for country %q, city %q and ISP %q", country, city, provider) return nil, fmt.Errorf("no server found for country %q, city %q and ISP %q", country, city, provider)
@@ -19,9 +20,18 @@ func (c *configurator) GetOpenVPNConnections(country models.MullvadCountry, city
port = customPort port = customPort
} }
for _, IP := range server.IPs { for _, IP := range server.IPs {
if targetIP != nil {
if targetIP.Equal(IP) {
return []models.OpenVPNConnection{{IP: IP, Port: port, Protocol: protocol}}, nil
}
} else {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: protocol}) connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: protocol})
} }
} }
}
if targetIP != nil {
return nil, fmt.Errorf("target IP address %q not found in IP addresses", targetIP)
}
return connections, nil return connections, nil
} }

View File

@@ -1,6 +1,8 @@
package mullvad package mullvad
import ( import (
"net"
"github.com/qdm12/golibs/files" "github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network" "github.com/qdm12/golibs/network"
@@ -11,7 +13,7 @@ const logPrefix = "Mullvad configurator"
// Configurator contains methods to download, read and modify the openvpn configuration to connect as a client // Configurator contains methods to download, read and modify the openvpn configuration to connect as a client
type Configurator interface { type Configurator interface {
GetOpenVPNConnections(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider, protocol models.NetworkProtocol, customPort uint16) (connections []models.OpenVPNConnection, err error) GetOpenVPNConnections(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider, protocol models.NetworkProtocol, customPort uint16, targetIP net.IP) (connections []models.OpenVPNConnection, err error)
BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool) (err error) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool) (err error)
} }

View File

@@ -1,6 +1,9 @@
package params package params
import ( import (
"fmt"
"net"
libparams "github.com/qdm12/golibs/params" libparams "github.com/qdm12/golibs/params"
"github.com/qdm12/private-internet-access-docker/internal/models" "github.com/qdm12/private-internet-access-docker/internal/models"
) )
@@ -45,3 +48,18 @@ func (p *paramsReader) GetOpenVPNVerbosity() (verbosity int, err error) {
func (p *paramsReader) GetOpenVPNRoot() (root bool, err error) { func (p *paramsReader) GetOpenVPNRoot() (root bool, err error) {
return p.envParams.GetYesNo("OPENVPN_ROOT", libparams.Default("no")) return p.envParams.GetYesNo("OPENVPN_ROOT", libparams.Default("no"))
} }
// GetTargetIP obtains the IP address to choose from the list of IP addresses
// available for a particular region, from the environment variable
// OPENVPN_TARGET_IP
func (p *paramsReader) GetTargetIP() (ip net.IP, err error) {
s, err := p.envParams.GetEnv("OPENVPN_TARGET_IP")
if len(s) == 0 {
return nil, nil
}
ip = net.ParseIP(s)
if ip == nil {
return nil, fmt.Errorf("target IP address %q is not valid", s)
}
return ip, nil
}

View File

@@ -37,6 +37,7 @@ type ParamsReader interface {
GetNetworkProtocol() (protocol models.NetworkProtocol, err error) GetNetworkProtocol() (protocol models.NetworkProtocol, err error)
GetOpenVPNVerbosity() (verbosity int, err error) GetOpenVPNVerbosity() (verbosity int, err error)
GetOpenVPNRoot() (root bool, err error) GetOpenVPNRoot() (root bool, err error)
GetTargetIP() (ip net.IP, err error)
// PIA getters // PIA getters
GetPortForwarding() (activated bool, err error) GetPortForwarding() (activated bool, err error)

View File

@@ -2,6 +2,7 @@ package pia
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/qdm12/golibs/files" "github.com/qdm12/golibs/files"
@@ -9,7 +10,7 @@ import (
"github.com/qdm12/private-internet-access-docker/internal/models" "github.com/qdm12/private-internet-access-docker/internal/models"
) )
func (c *configurator) GetOpenVPNConnections(region models.PIARegion, protocol models.NetworkProtocol, encryption models.PIAEncryption) (connections []models.OpenVPNConnection, err error) { func (c *configurator) GetOpenVPNConnections(region models.PIARegion, protocol models.NetworkProtocol, encryption models.PIAEncryption, targetIP net.IP) (connections []models.OpenVPNConnection, err error) {
geoMapping := constants.PIAGeoToSubdomainMapping() geoMapping := constants.PIAGeoToSubdomainMapping()
var subdomain string var subdomain string
for r, s := range geoMapping { for r, s := range geoMapping {
@@ -24,10 +25,24 @@ func (c *configurator) GetOpenVPNConnections(region models.PIARegion, protocol m
if err != nil { if err != nil {
return nil, err return nil, err
} }
IPs, err := c.lookupIP(subdomain + ".privateinternetaccess.com") hostname := subdomain + ".privateinternetaccess.com"
IPs, err := c.lookupIP(hostname)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if targetIP != nil {
found := false
for i := range IPs {
if IPs[i].Equal(targetIP) {
found = true
break
}
}
if !found {
return nil, fmt.Errorf("target IP address %q not found from IP addresses resolved from %s", targetIP, hostname)
}
IPs = []net.IP{targetIP}
}
var port uint16 var port uint16
switch protocol { switch protocol {
case constants.TCP: case constants.TCP:

View File

@@ -17,7 +17,7 @@ const logPrefix = "PIA configurator"
// Configurator contains methods to download, read and modify the openvpn configuration to connect as a client // Configurator contains methods to download, read and modify the openvpn configuration to connect as a client
type Configurator interface { type Configurator interface {
GetOpenVPNConnections(region models.PIARegion, protocol models.NetworkProtocol, GetOpenVPNConnections(region models.PIARegion, protocol models.NetworkProtocol,
encryption models.PIAEncryption) (connections []models.OpenVPNConnection, err error) encryption models.PIAEncryption, targetIP net.IP) (connections []models.OpenVPNConnection, err error)
BuildConf(connections []models.OpenVPNConnection, encryption models.PIAEncryption, verbosity, uid, gid int, root bool) (err error) BuildConf(connections []models.OpenVPNConnection, encryption models.PIAEncryption, verbosity, uid, gid int, root bool) (err error)
GetPortForward() (port uint16, err error) GetPortForward() (port uint16, err error)
WritePortForward(filepath models.Filepath, port uint16) (err error) WritePortForward(filepath models.Filepath, port uint16) (err error)

View File

@@ -2,6 +2,7 @@ package settings
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/qdm12/private-internet-access-docker/internal/models" "github.com/qdm12/private-internet-access-docker/internal/models"
@@ -13,6 +14,7 @@ type OpenVPN struct {
NetworkProtocol models.NetworkProtocol NetworkProtocol models.NetworkProtocol
Verbosity int Verbosity int
Root bool Root bool
TargetIP net.IP
} }
// GetOpenVPNSettings obtains the OpenVPN settings using the params functions // GetOpenVPNSettings obtains the OpenVPN settings using the params functions
@@ -26,6 +28,13 @@ func GetOpenVPNSettings(params params.ParamsReader) (settings OpenVPN, err error
return settings, err return settings, err
} }
settings.Root, err = params.GetOpenVPNRoot() settings.Root, err = params.GetOpenVPNRoot()
if err != nil {
return settings, err
}
settings.TargetIP, err = params.GetTargetIP()
if err != nil {
return settings, err
}
return settings, nil return settings, nil
} }
@@ -39,6 +48,7 @@ func (o *OpenVPN) String() string {
"Network protocol: " + string(o.NetworkProtocol), "Network protocol: " + string(o.NetworkProtocol),
"Verbosity level: " + fmt.Sprintf("%d", o.Verbosity), "Verbosity level: " + fmt.Sprintf("%d", o.Verbosity),
"Run as root: " + runAsRoot, "Run as root: " + runAsRoot,
"Target IP address: " + o.TargetIP.String(),
} }
return strings.Join(settingsList, "\n|--") return strings.Join(settingsList, "\n|--")
} }