Build openvpn configuration from scratch

This commit is contained in:
Quentin McGaw (desktop)
2020-02-07 13:55:24 +00:00
parent 6a9cd7ed9c
commit 69796e1ff9
11 changed files with 204 additions and 439 deletions

View File

@@ -85,12 +85,7 @@ func main() {
e.FatalOnError(err) e.FatalOnError(err)
} }
lines, err := piaConf.DownloadOvpnConfig(allSettings.PIA.Encryption, allSettings.OpenVPN.NetworkProtocol, allSettings.PIA.Region) VPNIPs, port, err := piaConf.BuildConf(allSettings.PIA.Region, allSettings.OpenVPN.NetworkProtocol, allSettings.PIA.Encryption, uid, gid)
e.FatalOnError(err)
VPNIPs, port, VPNDevice, err := piaConf.ParseConfig(lines)
e.FatalOnError(err)
lines = piaConf.ModifyLines(lines, VPNIPs, port)
fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines)
e.FatalOnError(err) e.FatalOnError(err)
defaultInterface, defaultGateway, defaultSubnet, err := firewallConf.GetDefaultRoute() defaultInterface, defaultGateway, defaultSubnet, err := firewallConf.GetDefaultRoute()
@@ -103,7 +98,7 @@ func main() {
e.FatalOnError(err) e.FatalOnError(err)
err = firewallConf.CreateGeneralRules() err = firewallConf.CreateGeneralRules()
e.FatalOnError(err) e.FatalOnError(err)
err = firewallConf.CreateVPNRules(VPNDevice, VPNIPs, defaultInterface, port, allSettings.OpenVPN.NetworkProtocol) err = firewallConf.CreateVPNRules(constants.TUN, VPNIPs, defaultInterface, port, allSettings.OpenVPN.NetworkProtocol)
e.FatalOnError(err) e.FatalOnError(err)
err = firewallConf.CreateLocalSubnetsRules(defaultSubnet, allSettings.Firewall.AllowedSubnets, defaultInterface) err = firewallConf.CreateLocalSubnetsRules(defaultSubnet, allSettings.Firewall.AllowedSubnets, defaultInterface)
e.FatalOnError(err) e.FatalOnError(err)
@@ -133,7 +128,7 @@ func main() {
if err := piaConf.WritePortForward(allSettings.PIA.PortForwarding.Filepath, port); err != nil { if err := piaConf.WritePortForward(allSettings.PIA.PortForwarding.Filepath, port); err != nil {
logger.Error("port forwarding:", err) logger.Error("port forwarding:", err)
} }
if err := piaConf.AllowPortForwardFirewall(VPNDevice, port); err != nil { if err := piaConf.AllowPortForwardFirewall(constants.TUN, port); err != nil {
logger.Error("port forwarding:", err) logger.Error("port forwarding:", err)
} }
}) })

View File

@@ -11,6 +11,13 @@ const (
PIAEncryptionStrong models.PIAEncryption = "strong" PIAEncryptionStrong models.PIAEncryption = "strong"
) )
const (
PIAX509CRL_NORMAL = "MIICWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZaMCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG9w0BAQ0FAAOCAQEAQZo9X97ci8EcPYu/uK2HB152OZbeZCINmYyluLDOdcSvg6B5jI+ffKN3laDvczsG6CxmY3jNyc79XVpEYUnq4rT3FfveW1+Ralf+Vf38HdpwB8EWB4hZlQ205+21CALLvZvR8HcPxC9KEnev1mU46wkTiov0EKc+EdRxkj5yMgv0V2Reze7AP+NQ9ykvDScH4eYCsmufNpIjBLhpLE2cuZZXBLcPhuRzVoU3l7A9lvzG9mjA5YijHJGHNjlWFqyrn1CfYS6koa4TGEPngBoAziWRbDGdhEgJABHrpoaFYaL61zqyMR6jC0K2ps9qyZAN74LEBedEfK7tBOzWMwr58A=="
PIAX509CRL_STRONG = "MIIDWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZaMCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG9w0BAQ0FAAOCAgEAppFfEpGsasjB1QgJcosGpzbf2kfRhM84o2TlqY1ua+Gi5TMdKydA3LJcNTjlI9a0TYAJfeRX5IkpoglSUuHuJgXhP3nEvX10mjXDpcu/YvM8TdE5JV2+EGqZ80kFtBeOq94WcpiVKFTR4fO+VkOK9zwspFfb1cNs9rHvgJ1QMkRUF8PpLN6AkntHY0+6DnigtSaKqldqjKTDTv2OeH3nPoh80SGrt0oCOmYKfWTJGpggMGKvIdvU3vH9+EuILZKKIskt+1dwdfA5Bkz1GLmiQG7+9ZZBQUjBG9Dos4hfX/rwJ3eU8oUIm4WoTz9rb71SOEuUUjP5NPy9HNx2vx+cVvLsTF4ZDZaUztW9o9JmIURDtbeyqxuHN3prlPWB6aj73IIm2dsDQvs3XXwRIxs8NwLbJ6CyEuvEOVCskdM8rdADWx1J0lRNlOJ0Z8ieLLEmYAA834VN1SboB6wJIAPxQU3rcBhXqO9y8aa2oRMg8NxZ5gr+PnKVMqag1x0IxbIgLxtkXQvxXxQHEMSODzvcOfK/nBRBsqTj30P+R87sU8titOoxNeRnBDRNhdEy/QGAqGh62ShPpQUCJdnKRiRTjnil9hMQHevoSuFKeEMO30FQL7BZyo37GFU+q1WPCplVZgCP9hC8Rn5K2+f6KLFo5bhtowSmu+GY1yZtg+RTtsA="
PIACertificate_NORMAL = "MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXDL1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzXlH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWpcdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RCOfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tLy8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZOsqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpMb3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2VzczEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5jb22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAna5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xUryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK37pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyCGohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUtYDQ8z9v+DMO6iwyIDRiU"
PIACertificate_STRONG = "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"
)
const ( const (
AUMelbourne models.PIARegion = "AU Melbourne" AUMelbourne models.PIARegion = "AU Melbourne"
AUPerth models.PIARegion = "AU Perth" AUPerth models.PIARegion = "AU Perth"
@@ -65,6 +72,111 @@ const (
) )
const ( const (
PIAOpenVPNURL models.URL = "https://www.privateinternetaccess.com/openvpn" PIASubdomainAUMelbourne string = "au-melbourne"
PIASubdomainAUPerth string = "au-perth"
PIASubdomainAUSydney string = "au-sydney"
PIASubdomainAustria string = "austria"
PIASubdomainBelgium string = "belgium"
PIASubdomainCAMontreal string = "ca-montreal"
PIASubdomainCAToronto string = "ca-toronto"
PIASubdomainCAVancouver string = "ca-vancouver"
PIASubdomainCzechRepublic string = "czech"
PIASubdomainDEBerlin string = "de-berlin"
PIASubdomainDEFrankfurt string = "de-frankfurt"
PIASubdomainDenmark string = "denmark"
PIASubdomainFinland string = "fi"
PIASubdomainFrance string = "france"
PIASubdomainHongKong string = "hk"
PIASubdomainHungary string = "hungary"
PIASubdomainIndia string = "in"
PIASubdomainIreland string = "ireland"
PIASubdomainIsrael string = "israel"
PIASubdomainItaly string = "italy"
PIASubdomainJapan string = "japan"
PIASubdomainLuxembourg string = "lu"
PIASubdomainMexico string = "mexico"
PIASubdomainNetherlands string = "nl"
PIASubdomainNewZealand string = "nz"
PIASubdomainNorway string = "no"
PIASubdomainPoland string = "poland"
PIASubdomainRomania string = "ro"
PIASubdomainSingapore string = "sg"
PIASubdomainSpain string = "spain"
PIASubdomainSweden string = "sweden"
PIASubdomainSwitzerland string = "swiss"
PIASubdomainUAE string = "ae"
PIASubdomainUKLondon string = "uk-london"
PIASubdomainUKManchester string = "uk-manchester"
PIASubdomainUKSouthampton string = "uk-southampton"
PIASubdomainUSAtlanta string = "us-atlanta"
PIASubdomainUSCalifornia string = "us-california"
PIASubdomainUSChicago string = "us-chicago"
PIASubdomainUSDenver string = "us-denver"
PIASubdomainUSEast string = "us-east"
PIASubdomainUSFlorida string = "us-florida"
PIASubdomainUSHouston string = "us-houston"
PIASubdomainUSLasVegas string = "us-lasvegas"
PIASubdomainUSNewYorkCity string = "us-newyorkcity"
PIASubdomainUSSeattle string = "us-seattle"
PIASubdomainUSSiliconValley string = "us-siliconvalley"
PIASubdomainUSTexas string = "us-texas"
PIASubdomainUSWashingtonDC string = "us-washingtondc"
PIASubdomainUSWest string = "us-west"
)
var PIARegionToSubdomainMapping = map[models.PIARegion]string{
AUMelbourne: PIASubdomainAUMelbourne,
AUPerth: PIASubdomainAUPerth,
AUSydney: PIASubdomainAUSydney,
Austria: PIASubdomainAustria,
Belgium: PIASubdomainBelgium,
CAMontreal: PIASubdomainCAMontreal,
CAToronto: PIASubdomainCAToronto,
CAVancouver: PIASubdomainCAVancouver,
CzechRepublic: PIASubdomainCzechRepublic,
DEBerlin: PIASubdomainDEBerlin,
DEFrankfurt: PIASubdomainDEFrankfurt,
Denmark: PIASubdomainDenmark,
Finland: PIASubdomainFinland,
France: PIASubdomainFrance,
HongKong: PIASubdomainHongKong,
Hungary: PIASubdomainHungary,
India: PIASubdomainIndia,
Ireland: PIASubdomainIreland,
Israel: PIASubdomainIsrael,
Italy: PIASubdomainItaly,
Japan: PIASubdomainJapan,
Luxembourg: PIASubdomainLuxembourg,
Mexico: PIASubdomainMexico,
Netherlands: PIASubdomainNetherlands,
NewZealand: PIASubdomainNewZealand,
Norway: PIASubdomainNorway,
Poland: PIASubdomainPoland,
Romania: PIASubdomainRomania,
Singapore: PIASubdomainSingapore,
Spain: PIASubdomainSpain,
Sweden: PIASubdomainSweden,
Switzerland: PIASubdomainSwitzerland,
UAE: PIASubdomainUAE,
UKLondon: PIASubdomainUKLondon,
UKManchester: PIASubdomainUKManchester,
UKSouthampton: PIASubdomainUKSouthampton,
USAtlanta: PIASubdomainUSAtlanta,
USCalifornia: PIASubdomainUSCalifornia,
USChicago: PIASubdomainUSChicago,
USDenver: PIASubdomainUSDenver,
USEast: PIASubdomainUSEast,
USFlorida: PIASubdomainUSFlorida,
USHouston: PIASubdomainUSHouston,
USLasVegas: PIASubdomainUSLasVegas,
USNewYorkCity: PIASubdomainUSNewYorkCity,
USSeattle: PIASubdomainUSSeattle,
USSiliconValley: PIASubdomainUSSiliconValley,
USTexas: PIASubdomainUSTexas,
USWashingtonDC: PIASubdomainUSWashingtonDC,
USWest: PIASubdomainUSWest,
}
const (
PIAPortForwardURL models.URL = "http://209.222.18.222:2000" PIAPortForwardURL models.URL = "http://209.222.18.222:2000"
) )

86
internal/pia/conf.go Normal file
View File

@@ -0,0 +1,86 @@
package pia
import (
"fmt"
"net"
"github.com/qdm12/golibs/files"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
func (c *configurator) BuildConf(region models.PIARegion, protocol models.NetworkProtocol,
encryption models.PIAEncryption, uid, gid int) (IPs []net.IP, port uint16, err error) {
var X509CRL, certificate string // depends on encryption
var cipherAlgo, authAlgo string // depends on encryption
if encryption == constants.PIAEncryptionNormal {
cipherAlgo = "aes-128-cbc"
authAlgo = "sha1"
X509CRL = constants.PIAX509CRL_NORMAL
certificate = constants.PIACertificate_NORMAL
if protocol == constants.UDP {
port = 1198
} else {
port = 502
}
} else { // strong
cipherAlgo = "aes-256-cbc"
authAlgo = "sha256"
X509CRL = constants.PIAX509CRL_STRONG
certificate = constants.PIACertificate_STRONG
if protocol == constants.UDP {
port = 1197
} else {
port = 501
}
}
subdomain := constants.PIARegionToSubdomainMapping[region]
IPs, err = c.lookupIP(subdomain + ".privateinternetaccess.com")
if err != nil {
return nil, 0, err
}
lines := []string{
"client",
"dev tun",
"nobind",
"persist-key",
"persist-tun",
"tls-client",
"remote-cert-tls server",
"compress",
"verb 1", // TODO env variable
"reneg-sec 0",
// Added constant values
"mute-replay-warnings",
"user nonrootuser",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"disable-occ",
// Modified variables
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", string(protocol)),
fmt.Sprintf("cipher %s", cipherAlgo),
fmt.Sprintf("auth %s", authAlgo),
}
for _, IP := range IPs {
lines = append(lines, fmt.Sprintf("remote %s %d", IP.String(), port))
}
lines = append(lines, []string{
"<crl-verify>",
"-----BEGIN X509 CRL-----",
X509CRL,
"-----END X509 CRL-----",
"</crl-verify>",
}...)
lines = append(lines, []string{
"<ca>",
"-----BEGIN CERTIFICATE-----",
certificate,
"-----END CERTIFICATE-----",
"</ca>",
"",
}...)
err = c.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.FileOwnership(uid, gid), files.FilePermissions(0400))
return IPs, port, err
}

View File

@@ -1,61 +0,0 @@
package pia
import (
"archive/zip"
"bytes"
"fmt"
"io/ioutil"
"strings"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
func (c *configurator) DownloadOvpnConfig(encryption models.PIAEncryption,
protocol models.NetworkProtocol, region models.PIARegion) (lines []string, err error) {
c.logger.Info("%s: downloading openvpn configuration files", logPrefix)
URL := buildZipURL(encryption, protocol)
content, status, err := c.client.GetContent(URL)
if err != nil {
return nil, err
} else if status != 200 {
return nil, fmt.Errorf("HTTP Get %s resulted in HTTP status code %d", URL, status)
}
filename := fmt.Sprintf("%s.ovpn", region)
fileContent, err := getFileInZip(content, filename)
if err != nil {
return nil, fmt.Errorf("%s: %w", URL, err)
}
lines = strings.Split(string(fileContent), "\n")
return lines, nil
}
func buildZipURL(encryption models.PIAEncryption, protocol models.NetworkProtocol) (URL string) {
URL = string(constants.PIAOpenVPNURL) + "/openvpn"
if encryption == constants.PIAEncryptionStrong {
URL += "-strong"
}
if protocol == constants.TCP {
URL += "-tcp"
}
return URL + ".zip"
}
func getFileInZip(zipContent []byte, filename string) (fileContent []byte, err error) {
contentLength := int64(len(zipContent))
r, err := zip.NewReader(bytes.NewReader(zipContent), contentLength)
if err != nil {
return nil, err
}
for _, f := range r.File {
if f.Name == filename {
readCloser, err := f.Open()
if err != nil {
return nil, err
}
defer readCloser.Close()
return ioutil.ReadAll(readCloser)
}
}
return nil, fmt.Errorf("%s not found in zip archive file", filename)
}

View File

@@ -1,31 +0,0 @@
package pia
import (
"fmt"
"net"
"strings"
"github.com/qdm12/private-internet-access-docker/internal/constants"
)
func (c *configurator) ModifyLines(lines []string, IPs []net.IP, port uint16) (modifiedLines []string) {
c.logger.Info("%s: adapting openvpn configuration for server IP addresses and port %d", logPrefix, port)
// Remove lines
for _, line := range lines {
if strings.Contains(line, "privateinternetaccess.com") ||
strings.Contains(line, "resolv-retry") {
continue
}
modifiedLines = append(modifiedLines, line)
}
// Add lines
for _, IP := range IPs {
modifiedLines = append(modifiedLines, fmt.Sprintf("remote %s %d", IP.String(), port))
}
modifiedLines = append(modifiedLines, "auth-user-pass "+string(constants.OpenVPNAuthConf))
modifiedLines = append(modifiedLines, "auth-retry nointeract")
modifiedLines = append(modifiedLines, "pull-filter ignore \"auth-token\"") // prevent auth failed loops
modifiedLines = append(modifiedLines, "user nonrootuser")
modifiedLines = append(modifiedLines, "mute-replay-warnings")
return modifiedLines
}

View File

@@ -1,31 +0,0 @@
package pia
import (
"io/ioutil"
"net"
"strings"
"testing"
loggingMocks "github.com/qdm12/golibs/logging/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_ModifyLines(t *testing.T) {
t.Parallel()
original, err := ioutil.ReadFile("testdata/ovpn.golden")
require.NoError(t, err)
originalLines := strings.Split(string(original), "\n")
expected, err := ioutil.ReadFile("testdata/ovpn.modified.golden")
require.NoError(t, err)
expectedLines := strings.Split(string(expected), "\n")
var port uint16 = 3000
IPs := []net.IP{net.IP{100, 10, 10, 10}, net.IP{100, 20, 20, 20}}
logger := &loggingMocks.Logger{}
logger.On("Info", "%s: adapting openvpn configuration for server IP addresses and port %d", logPrefix, port).Once()
c := &configurator{logger: logger}
modifiedLines := c.ModifyLines(originalLines, IPs, port)
assert.Equal(t, expectedLines, modifiedLines)
logger.AssertExpectations(t)
}

View File

@@ -1,54 +0,0 @@
package pia
import (
"fmt"
"net"
"strconv"
"strings"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
func (c *configurator) ParseConfig(lines []string) (IPs []net.IP, port uint16, device models.VPNDevice, err error) {
c.logger.Info("%s: parsing openvpn configuration", logPrefix)
remoteLineFound := false
deviceLineFound := false
for _, line := range lines {
if strings.HasPrefix(line, "remote ") {
remoteLineFound = true
words := strings.Fields(line)
if len(words) != 3 {
return nil, 0, "", fmt.Errorf("line %q misses information", line)
}
host := words[1]
if err := c.verifyPort(words[2]); err != nil {
return nil, 0, "", fmt.Errorf("line %q has an invalid port: %w", line, err)
}
portUint64, _ := strconv.ParseUint(words[2], 10, 16)
port = uint16(portUint64)
IPs, err = c.lookupIP(host)
if err != nil {
return nil, 0, "", err
}
} else if strings.HasPrefix(line, "dev ") {
deviceLineFound = true
fields := strings.Fields(line)
if len(fields) != 2 {
return nil, 0, "", fmt.Errorf("line %q misses information", line)
}
device = models.VPNDevice(fields[1] + "0")
if device != constants.TUN && device != constants.TAP {
return nil, 0, "", fmt.Errorf("device %q is not valid", device)
}
}
}
if remoteLineFound && deviceLineFound {
c.logger.Info("%s: Found %d PIA server IP addresses, port %d and device %s", logPrefix, len(IPs), port, device)
return IPs, port, device, nil
} else if !remoteLineFound {
return nil, 0, "", fmt.Errorf("remote line not found in Openvpn configuration")
} else {
return nil, 0, "", fmt.Errorf("device line not found in Openvpn configuration")
}
}

View File

@@ -1,99 +0,0 @@
package pia
import (
"fmt"
"io/ioutil"
"net"
"strings"
"testing"
loggingMocks "github.com/qdm12/golibs/logging/mocks"
"github.com/qdm12/golibs/verification"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_ParseConfig(t *testing.T) {
t.Parallel()
original, err := ioutil.ReadFile("testdata/ovpn.golden")
require.NoError(t, err)
exampleLines := strings.Split(string(original), "\n")
tests := map[string]struct {
lines []string
lookupIPs []net.IP
lookupIPErr error
IPs []net.IP
port uint16
device models.VPNDevice
err error
}{
"no data": {
err: fmt.Errorf("remote line not found in Openvpn configuration"),
},
"bad remote line": {
lines: []string{"remote field2"},
err: fmt.Errorf("line \"remote field2\" misses information"),
},
"bad remote port": {
lines: []string{"remote field2 port"},
err: fmt.Errorf("line \"remote field2 port\" has an invalid port: port \"port\" is not a valid integer"),
},
"lookupIP error": {
lines: []string{"remote host 1000"},
lookupIPErr: fmt.Errorf("lookup error"),
err: fmt.Errorf("lookup error"),
},
"missing dev line": {
lines: []string{"remote host 1994"},
err: fmt.Errorf("device line not found in Openvpn configuration"),
},
"bad dev line": {
lines: []string{"dev field2 field3"},
err: fmt.Errorf("line \"dev field2 field3\" misses information"),
},
"bad device": {
lines: []string{"dev xx"},
err: fmt.Errorf("device \"xx0\" is not valid"),
},
"valid lines": {
lines: []string{"remote host 1194", "dev tap", "blabla"},
port: 1194,
device: constants.TAP,
},
"real data": {
lines: exampleLines,
lookupIPs: []net.IP{{100, 100, 100, 100}, {100, 100, 200, 200}},
IPs: []net.IP{{100, 100, 100, 100}, {100, 100, 200, 200}},
port: 1198,
device: constants.TUN,
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
logger := &loggingMocks.Logger{}
logger.On("Info", "%s: parsing openvpn configuration", logPrefix).Once()
if tc.err == nil {
logger.On("Info", "%s: Found %d PIA server IP addresses, port %d and device %s", logPrefix, len(tc.IPs), tc.port, tc.device).Once()
}
lookupIP := func(host string) ([]net.IP, error) {
return tc.lookupIPs, tc.lookupIPErr
}
c := &configurator{logger: logger, verifyPort: verification.NewVerifier().VerifyPort, lookupIP: lookupIP}
IPs, port, device, err := c.ParseConfig(tc.lines)
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, tc.IPs, IPs)
assert.Equal(t, tc.port, port)
assert.Equal(t, tc.device, device)
logger.AssertExpectations(t)
})
}
}

View File

@@ -16,10 +16,8 @@ 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 {
DownloadOvpnConfig(encryption models.PIAEncryption, BuildConf(region models.PIARegion, protocol models.NetworkProtocol,
protocol models.NetworkProtocol, region models.PIARegion) (lines []string, err error) encryption models.PIAEncryption, uid, gid int) (IPs []net.IP, port uint16, err error)
ParseConfig(lines []string) (IPs []net.IP, port uint16, device models.VPNDevice, err error)
ModifyLines(lines []string, IPs []net.IP, port uint16) (modifiedLines []string)
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)
AllowPortForwardFirewall(device models.VPNDevice, port uint16) (err error) AllowPortForwardFirewall(device models.VPNDevice, port uint16) (err error)

View File

@@ -1,72 +0,0 @@
client
dev tun
proto udp
remote belgium.privateinternetaccess.com 1198
resolv-retry infinite
nobind
persist-key
persist-tun
cipher aes-128-cbc
auth sha1
tls-client
remote-cert-tls server
auth-user-pass
compress
verb 1
reneg-sec 0
<crl-verify>
-----BEGIN X509 CRL-----
MIICWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQI
EwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRl
cm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAw
HgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0
ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRl
aW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZa
MCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG
9w0BAQ0FAAOCAQEAQZo9X97ci8EcPYu/uK2HB152OZbeZCINmYyluLDOdcSvg6B5
jI+ffKN3laDvczsG6CxmY3jNyc79XVpEYUnq4rT3FfveW1+Ralf+Vf38HdpwB8EW
B4hZlQ205+21CALLvZvR8HcPxC9KEnev1mU46wkTiov0EKc+EdRxkj5yMgv0V2Re
ze7AP+NQ9ykvDScH4eYCsmufNpIjBLhpLE2cuZZXBLcPhuRzVoU3l7A9lvzG9mjA
5YijHJGHNjlWFqyrn1CfYS6koa4TGEPngBoAziWRbDGdhEgJABHrpoaFYaL61zqy
MR6jC0K2ps9qyZAN74LEBedEfK7tBOzWMwr58A==
-----END X509 CRL-----
</crl-verify>
<ca>
-----BEGIN CERTIFICATE-----
MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu
dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx
IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB
FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1
MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg
QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE
AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50
ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy
bmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXD
L1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzX
lH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWp
cdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/
8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB
/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RC
OfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tL
y8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZO
sqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpM
b3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4G
A1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUg
SW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2Vz
czEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5j
b22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAn
a5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xU
ryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK3
7pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyC
GohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz
1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUt
YDQ8z9v+DMO6iwyIDRiU
-----END CERTIFICATE-----
</ca>
disable-occ

View File

@@ -1,78 +0,0 @@
client
dev tun
proto udp
nobind
persist-key
persist-tun
cipher aes-128-cbc
auth sha1
tls-client
remote-cert-tls server
auth-user-pass
compress
verb 1
reneg-sec 0
<crl-verify>
-----BEGIN X509 CRL-----
MIICWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQI
EwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRl
cm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAw
HgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0
ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRl
aW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZa
MCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG
9w0BAQ0FAAOCAQEAQZo9X97ci8EcPYu/uK2HB152OZbeZCINmYyluLDOdcSvg6B5
jI+ffKN3laDvczsG6CxmY3jNyc79XVpEYUnq4rT3FfveW1+Ralf+Vf38HdpwB8EW
B4hZlQ205+21CALLvZvR8HcPxC9KEnev1mU46wkTiov0EKc+EdRxkj5yMgv0V2Re
ze7AP+NQ9ykvDScH4eYCsmufNpIjBLhpLE2cuZZXBLcPhuRzVoU3l7A9lvzG9mjA
5YijHJGHNjlWFqyrn1CfYS6koa4TGEPngBoAziWRbDGdhEgJABHrpoaFYaL61zqy
MR6jC0K2ps9qyZAN74LEBedEfK7tBOzWMwr58A==
-----END X509 CRL-----
</crl-verify>
<ca>
-----BEGIN CERTIFICATE-----
MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu
dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx
IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB
FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1
MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg
QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE
AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50
ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy
bmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXD
L1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzX
lH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWp
cdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/
8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB
/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RC
OfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tL
y8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZO
sqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpM
b3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4G
A1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUg
SW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2Vz
czEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5j
b22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAn
a5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xU
ryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK3
7pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyC
GohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz
1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUt
YDQ8z9v+DMO6iwyIDRiU
-----END CERTIFICATE-----
</ca>
disable-occ
remote 100.10.10.10 3000
remote 100.20.20.20 3000
auth-user-pass /etc/openvpn/auth.conf
auth-retry nointeract
pull-filter ignore "auth-token"
user nonrootuser
mute-replay-warnings