Support Mullvad over openvpn (#85)

Additional changes:
- Allow empty value for PIA region
- Most settings are lowercased
- `OPENVPN_VERBOSITY` environment variable
- openvpn also tunnels IPv6, and unbound supports ipv6
- auth kept only on disk, not in memory
- readme reworked
- CI script fixed and improved
- Added v2 Docker tag
- Shadowsocks log defaults to `off`
This commit is contained in:
Quentin McGaw
2020-02-29 21:05:20 -05:00
committed by GitHub
33 changed files with 1351 additions and 245 deletions

View File

@@ -0,0 +1,667 @@
package constants
import (
"net"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
const (
MullvadCertificate = "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"
)
func MullvadCountryChoices() (choices []string) {
uniqueChoices := map[string]struct{}{}
for _, server := range mullvadServers() {
uniqueChoices[string(server.Country)] = struct{}{}
}
for choice := range uniqueChoices {
choices = append(choices, choice)
}
return choices
}
func MullvadCityChoices() (choices []string) {
uniqueChoices := map[string]struct{}{}
for _, server := range mullvadServers() {
uniqueChoices[string(server.City)] = struct{}{}
}
for choice := range uniqueChoices {
choices = append(choices, choice)
}
return choices
}
func MullvadProviderChoices() (choices []string) {
uniqueChoices := map[string]struct{}{}
for _, server := range mullvadServers() {
uniqueChoices[string(server.Provider)] = struct{}{}
}
for choice := range uniqueChoices {
choices = append(choices, choice)
}
return choices
}
func MullvadServerFilter(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider) (servers []models.MullvadServer) {
for _, server := range mullvadServers() {
if len(country) == 0 {
server.Country = ""
}
if len(city) == 0 {
server.City = ""
}
if len(provider) == 0 {
server.Provider = ""
}
if server.Country == country && server.City == city && server.Provider == provider {
servers = append(servers, server)
}
}
return servers
}
func mullvadServers() []models.MullvadServer {
return []models.MullvadServer{
{
Country: models.MullvadCountry("united arab emirates"),
City: models.MullvadCity("dubai"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{45, 9, 249, 34}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("albania"),
City: models.MullvadCity("tirana"),
Provider: models.MullvadProvider("iregister"),
IPs: []net.IP{{31, 171, 154, 210}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("austria"),
City: models.MullvadCity("wien"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 155, 250}, {217, 64, 127, 138}, {217, 64, 127, 202}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("adelaide"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{116, 206, 231, 58}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("brisbane"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{43, 245, 160, 162}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("canberra"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{116, 206, 229, 98}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("melbourne"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{116, 206, 228, 202}, {116, 206, 228, 242}, {116, 206, 230, 98}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("perth"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{103, 77, 235, 66}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("sydney"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{43, 245, 162, 130}, {103, 77, 232, 130}, {103, 77, 232, 146}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("sydney"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{217, 138, 204, 82}, {217, 138, 204, 98}, {217, 138, 204, 66}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("belgium"),
City: models.MullvadCity("brussels"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 218, 146}, {37, 120, 218, 138}, {91, 207, 57, 50}, {37, 120, 143, 138}, {185, 104, 186, 202}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("bulgaria"),
City: models.MullvadCity("sofia"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 94, 192, 42}, {185, 94, 192, 66}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("brazil"),
City: models.MullvadCity("sao paulo"),
Provider: models.MullvadProvider("qnax"),
IPs: []net.IP{{191, 101, 62, 178}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("brazil"),
City: models.MullvadCity("sao paulo"),
Provider: models.MullvadProvider("heficed"),
IPs: []net.IP{{177, 67, 80, 186}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("canada"),
City: models.MullvadCity("montreal"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{139, 28, 218, 114}, {217, 138, 200, 194}, {217, 138, 200, 186}, {87, 101, 92, 146}, {176, 113, 74, 178}, {37, 120, 205, 114}, {87, 101, 92, 138}, {37, 120, 205, 122}, {217, 138, 200, 210}, {217, 138, 200, 202}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("canada"),
City: models.MullvadCity("toronto"),
Provider: models.MullvadProvider("amanah"),
IPs: []net.IP{{184, 75, 214, 130}, {162, 219, 176, 250}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("canada"),
City: models.MullvadCity("vancouver"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{172, 83, 40, 34}, {172, 83, 40, 38}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("canada"),
City: models.MullvadCity("vancouver"),
Provider: models.MullvadProvider("esecuredata"),
IPs: []net.IP{{71, 19, 249, 81}, {176, 113, 74, 186}, {71, 19, 248, 240}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("switzerland"),
City: models.MullvadCity("zurich"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{193, 32, 127, 82}, {193, 32, 127, 81}, {193, 32, 127, 83}, {193, 32, 127, 84}},
Owned: true,
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("zwitzerland"),
City: models.MullvadCity("zurich"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 212, 170, 50}, {185, 183, 104, 82}, {185, 9, 18, 98}, {82, 102, 24, 130}, {82, 102, 24, 186}, {185, 212, 170, 162}, {185, 9, 18, 114}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("switzerland"),
City: models.MullvadCity("zurich"),
Provider: models.MullvadProvider("privateLayer"),
IPs: []net.IP{{179, 43, 128, 170}, {81, 17, 20, 34}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("czech republic"),
City: models.MullvadCity("prague"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{217, 138, 199, 82}, {217, 138, 199, 74}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("germany"),
City: models.MullvadCity("frankfurt"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 213, 155, 132}, {185, 213, 155, 140}, {185, 213, 155, 136}, {185, 213, 155, 133}, {185, 213, 155, 144}, {185, 213, 155, 143}, {185, 213, 155, 138}, {185, 213, 155, 142}, {185, 213, 155, 139}, {185, 213, 155, 135}, {185, 213, 155, 145}, {185, 213, 155, 137}, {185, 213, 155, 131}, {185, 213, 155, 134}, {185, 213, 155, 141}},
Owned: true,
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("germany"),
City: models.MullvadCity("frankfurt"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{82, 102, 16, 90}, {185, 104, 184, 186}, {77, 243, 183, 202}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("denmark"),
City: models.MullvadCity("copenhagen"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{141, 98, 254, 71}, {141, 98, 254, 72}},
Owned: true,
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("denmark"),
City: models.MullvadCity("copenhagen"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 206, 224, 114}, {185, 206, 224, 119}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("denmark"),
City: models.MullvadCity("copenhagen"),
Provider: models.MullvadProvider("blix"),
IPs: []net.IP{{134, 90, 149, 138}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("denmark"),
City: models.MullvadCity("copenhagen"),
Provider: models.MullvadProvider("asergo"),
IPs: []net.IP{{82, 103, 140, 213}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("spain"),
City: models.MullvadCity("madrid"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{195, 206, 107, 146}, {45, 152, 183, 42}, {89, 238, 178, 74}, {45, 152, 183, 26}, {89, 238, 178, 34}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("finland"),
City: models.MullvadCity("helsinki"),
Provider: models.MullvadProvider("creanova"),
IPs: []net.IP{{185, 204, 1, 174}, {185, 204, 1, 176}, {185, 212, 149, 201}, {185, 204, 1, 175}, {185, 204, 1, 173}, {185, 204, 1, 172}, {185, 204, 1, 171}},
Owned: true,
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("france"),
City: models.MullvadCity("paris"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{193, 32, 126, 83}, {193, 32, 126, 82}, {193, 32, 126, 81}, {193, 32, 126, 84}},
Owned: true,
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("france"),
City: models.MullvadCity("paris"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 189, 113, 82}, {185, 156, 173, 218}, {185, 128, 25, 162}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("uk"),
City: models.MullvadCity("london"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{141, 98, 252, 133}, {141, 98, 252, 139}, {141, 98, 252, 137}, {141, 98, 252, 143}, {141, 98, 252, 142}, {141, 98, 252, 132}, {141, 98, 252, 134}, {141, 98, 252, 140}, {141, 98, 252, 141}, {141, 98, 252, 136}, {141, 98, 252, 144}, {141, 98, 252, 131}, {141, 98, 252, 135}, {141, 98, 252, 138}},
Owned: true,
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("uk"),
City: models.MullvadCity("london"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 200, 118, 105}, {185, 212, 168, 244}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("uk"),
City: models.MullvadCity("manchester"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{89, 238, 130, 66}, {81, 92, 205, 10}, {89, 238, 130, 74}, {81, 92, 205, 18}, {81, 92, 205, 26}, {89, 238, 183, 244}, {89, 238, 132, 36}, {217, 151, 98, 68}, {37, 120, 159, 164}, {89, 238, 183, 60}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("greece"),
City: models.MullvadCity("athens"),
Provider: models.MullvadProvider("aweb"),
IPs: []net.IP{{185, 226, 67, 168}},
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("hong kong"),
City: models.MullvadCity("hong kong"),
Provider: models.MullvadProvider("leaseweb"),
IPs: []net.IP{{209, 58, 185, 53}, {209, 58, 184, 146}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("hungary"),
City: models.MullvadCity("budapest"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 94, 190, 138}, {185, 189, 114, 10}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("ireland"),
City: models.MullvadCity("dublin"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{217, 138, 222, 90}, {217, 138, 222, 82}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("israel"),
City: models.MullvadCity("tel aviv"),
Provider: models.MullvadProvider("hqserv"),
IPs: []net.IP{{185, 191, 207, 210}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("italy"),
City: models.MullvadCity("milan"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{217, 138, 197, 106}, {217, 64, 113, 180}, {217, 138, 197, 98}, {217, 138, 197, 114}, {217, 64, 113, 183}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("japan"),
City: models.MullvadCity("tokyo"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 210, 138}, {193, 148, 16, 218}, {37, 120, 210, 146}, {185, 242, 4, 50}, {37, 120, 210, 122}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("luxembourg"),
City: models.MullvadCity("luxembourg"),
Provider: models.MullvadProvider("evoluso"),
IPs: []net.IP{{92, 223, 89, 160}, {92, 223, 89, 182}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("latvia"),
City: models.MullvadCity("riga"),
Provider: models.MullvadProvider("makonix"),
IPs: []net.IP{{31, 170, 22, 2}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("moldova"),
City: models.MullvadCity("chisinau"),
Provider: models.MullvadProvider("trabia"),
IPs: []net.IP{{178, 175, 142, 194}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("netherlands"),
City: models.MullvadCity("amsterdam"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 65, 134, 139}, {185, 65, 134, 133}, {185, 65, 134, 148}, {185, 65, 134, 147}, {185, 65, 134, 141}, {185, 65, 134, 140}, {185, 65, 134, 145}, {185, 65, 134, 132}, {185, 65, 134, 146}, {185, 65, 134, 143}, {185, 65, 134, 134}, {185, 65, 134, 136}, {185, 65, 134, 135}, {185, 65, 134, 142}, {185, 65, 134, 144}},
Owned: true,
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("norway"),
City: models.MullvadCity("oslo"),
Provider: models.MullvadProvider("blix"),
IPs: []net.IP{{91, 90, 44, 13}, {91, 90, 44, 18}, {91, 90, 44, 12}, {91, 90, 44, 15}, {91, 90, 44, 16}, {91, 90, 44, 17}, {91, 90, 44, 14}, {91, 90, 44, 11}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("new zealand"),
City: models.MullvadCity("auckland"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{103, 231, 91, 114}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("poland"),
City: models.MullvadCity("warsaw"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 211, 202}, {37, 120, 156, 162}, {185, 244, 214, 210}, {37, 120, 211, 186}, {185, 244, 214, 215}, {37, 120, 211, 194}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("portugal"),
City: models.MullvadCity("lisbon"),
Provider: models.MullvadProvider("dotsi"),
IPs: []net.IP{{5, 206, 231, 214}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("romania"),
City: models.MullvadCity("bucharest"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{89, 40, 181, 146}, {185, 181, 100, 202}, {89, 40, 181, 82}, {185, 45, 13, 10}, {89, 40, 181, 210}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("serbia"),
City: models.MullvadCity("belgrade"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{141, 98, 103, 50}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("serbia"),
City: models.MullvadCity("nis"),
Provider: models.MullvadProvider("ninet"),
IPs: []net.IP{{176, 104, 107, 118}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("sweden"),
City: models.MullvadCity("gothenburg"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 213, 154, 139}, {185, 213, 154, 141}, {185, 213, 154, 140}, {185, 213, 154, 132}, {185, 213, 154, 135}, {185, 213, 154, 138}, {185, 213, 154, 133}, {185, 213, 154, 131}, {185, 213, 154, 134}, {185, 213, 154, 142}, {185, 213, 154, 137}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("sweden"),
City: models.MullvadCity("helsingborg"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 213, 152, 133}, {185, 213, 152, 132}, {185, 213, 152, 138}, {185, 213, 152, 131}, {185, 213, 152, 137}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("sweden"),
City: models.MullvadCity("malmo"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{193, 138, 218, 138}, {45, 83, 220, 87}, {141, 98, 255, 94}, {141, 98, 255, 85}, {141, 98, 255, 87}, {141, 98, 255, 92}, {45, 83, 220, 84}, {141, 98, 255, 86}, {45, 83, 220, 81}, {193, 138, 218, 135}, {193, 138, 218, 131}, {193, 138, 218, 136}, {141, 98, 255, 88}, {141, 98, 255, 91}, {193, 138, 218, 133}, {45, 83, 220, 89}, {45, 83, 220, 88}, {141, 98, 255, 84}, {141, 98, 255, 89}, {193, 138, 218, 134}, {45, 83, 220, 86}, {141, 98, 255, 83}, {45, 83, 220, 85}, {141, 98, 255, 90}, {141, 98, 255, 93}, {193, 138, 218, 132}, {193, 138, 218, 137}, {45, 83, 220, 91}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("sweden"),
City: models.MullvadCity("stockholm"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 65, 135, 150}, {185, 65, 135, 153}, {185, 65, 135, 151}, {185, 65, 135, 149}, {185, 65, 135, 141}, {185, 65, 135, 144}, {185, 65, 135, 145}, {185, 65, 135, 140}, {185, 65, 135, 134}, {185, 65, 135, 139}, {185, 65, 135, 131}, {185, 65, 135, 152}, {185, 65, 135, 146}, {185, 65, 135, 138}, {185, 65, 135, 143}, {185, 65, 135, 135}, {185, 65, 135, 154}, {185, 65, 135, 136}, {185, 65, 135, 133}, {185, 65, 135, 132}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("singapore"),
City: models.MullvadCity("singapore"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 208, 218}, {37, 120, 208, 234}, {37, 120, 208, 226}, {185, 128, 24, 50}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("singapore"),
City: models.MullvadCity("singapore"),
Provider: models.MullvadProvider("leaseweb"),
IPs: []net.IP{{103, 254, 153, 82}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("atlanta"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{208, 84, 153, 142}, {107, 152, 108, 62}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("atlanta"),
Provider: models.MullvadProvider("quadranet"),
IPs: []net.IP{{104, 129, 24, 242}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("atlanta"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{{155, 254, 96, 2}, {155, 254, 96, 18}, {155, 254, 96, 34}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("chicago"),
Provider: models.MullvadProvider("tzulo"),
IPs: []net.IP{{68, 235, 43, 18}, {68, 235, 43, 26}, {68, 235, 43, 42}, {68, 235, 43, 50}, {68, 235, 43, 58}, {68, 235, 43, 66}, {68, 235, 43, 74}}, // 3 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("chicago"),
Provider: models.MullvadProvider("quadranet"),
IPs: []net.IP{}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("dallas"),
Provider: models.MullvadProvider("quadranet"),
IPs: []net.IP{{96, 44, 145, 18}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("dallas"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{104, 200, 142, 50}, {107, 152, 102, 106}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("denver"),
Provider: models.MullvadProvider("tzulo"),
IPs: []net.IP{{198, 54, 128, 74}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("los angeles"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{45, 152, 182, 66}, {45, 152, 182, 74}, {45, 83, 89, 162}, {185, 230, 126, 146}}, // 7 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("los angeles"),
Provider: models.MullvadProvider("tzulo"),
IPs: []net.IP{{198, 54, 129, 74}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("los angeles"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{104, 200, 152, 66}, {107, 181, 168, 130}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("los angeles"),
Provider: models.MullvadProvider("choopa"),
IPs: []net.IP{{104, 238, 143, 58}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("miami"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 215, 130}, {193, 37, 252, 138}, {193, 37, 252, 154}, {37, 120, 215, 138}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("miami"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{172, 98, 76, 114}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("miami"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("new york"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 232, 22, 66}, {185, 232, 22, 98}, {193, 148, 18, 250}, {185, 232, 22, 10}, {217, 138, 206, 10}, {193, 148, 18, 218}, {193, 148, 18, 226}, {193, 148, 18, 194}, {87, 101, 95, 98}, {87, 101, 95, 114}, {87, 101, 95, 122}, {212, 103, 48, 226}, {176, 113, 72, 226}, {217, 138, 198, 250}, {217, 138, 206, 58}}, // 5 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("new york"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{107, 182, 226, 206}, {107, 182, 226, 218}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("phoenix"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("phoenix"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{{192, 200, 24, 82}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("piscataway"),
Provider: models.MullvadProvider("choopa"),
IPs: []net.IP{{108, 61, 78, 138}, {108, 61, 48, 115}, {66, 55, 147, 59}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("seattle"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{104, 200, 129, 202}, {104, 200, 129, 150}, {104, 200, 129, 110}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("seattle"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{{104, 128, 136, 146}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("san francisco"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{{209, 209, 238, 34}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("salt lake city"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{107, 182, 238, 229}, {107, 182, 235, 233}, {67, 212, 238, 236}, {67, 212, 238, 237}, {67, 212, 238, 239}, {107, 182, 239, 185}, {107, 182, 239, 170}}, // 2 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("secaucus"),
Provider: models.MullvadProvider("quadranet"),
IPs: []net.IP{{23, 226, 131, 154}}, // 1 missing
DefaultPort: 1194,
},
}
}

View File

@@ -1,9 +1,6 @@
package constants
import (
"fmt"
"strings"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
@@ -21,12 +18,15 @@ const (
PIACertificate_STRONG = "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"
)
func PIAGeoChoices() []string {
return []string{"AU Melbourne", "AU Perth", "AU Sydney", "Austria", "Belgium", "CA Montreal", "CA Toronto", "CA Vancouver", "Czech Republic", "DE Berlin", "DE Frankfurt", "Denmark", "Finland", "France", "Hong Kong", "Hungary", "India", "Ireland", "Israel", "Italy", "Japan", "Luxembourg", "Mexico", "Netherlands", "New Zealand", "Norway", "Poland", "Romania", "Singapore", "Spain", "Sweden", "Switzerland", "UAE", "UK London", "UK Manchester", "UK Southampton", "US Atlanta", "US California", "US Chicago", "US Denver", "US East", "US Florida", "US Houston", "US Las Vegas", "US New York City", "US Seattle", "US Silicon Valley", "US Texas", "US Washington DC", "US West"}
func PIAGeoChoices() (regions []string) {
for region := range PIAGeoToSubdomainMapping() {
regions = append(regions, string(region))
}
return regions
}
func PIAGeoToSubdomainMapping(region models.PIARegion) (subdomain string, err error) {
mapping := map[models.PIARegion]string{
func PIAGeoToSubdomainMapping() map[models.PIARegion]string {
return map[models.PIARegion]string{
models.PIARegion("AU Melbourne"): "au-melbourne",
models.PIARegion("AU Perth"): "au-perth",
models.PIARegion("AU Sydney"): "au-sydney",
@@ -78,11 +78,6 @@ func PIAGeoToSubdomainMapping(region models.PIARegion) (subdomain string, err er
models.PIARegion("US Washington DC"): "us-washingtondc",
models.PIARegion("US West"): "us-west",
}
subdomain, ok := mapping[region]
if !ok {
return "", fmt.Errorf("PIA region %q does not exist and can only be one of ", strings.Join(PIAGeoChoices(), ","))
}
return subdomain, nil
}
const (

View File

@@ -2,9 +2,9 @@ package constants
const (
// Annoucement is a message annoucement
Annoucement = "Total rewrite in Go with many new features"
// AnnoucementExpiration is the expiration time of the annoucement in unix timestamp
AnnoucementExpiration = 1582761600
Annoucement = "Support for Mullvad"
// AnnoucementExpiration is the expiration date of the annoucement in format yyyy-mm-dd
AnnoucementExpiration = "2020-03-15"
)
const (

View File

@@ -0,0 +1,17 @@
package constants
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func Test_AnnoucementExpiration(t *testing.T) {
t.Parallel()
if len(AnnoucementExpiration) == 0 {
return
}
_, err := time.Parse("2006-01-02", AnnoucementExpiration)
assert.NoError(t, err)
}

View File

@@ -60,7 +60,7 @@ func generateUnboundConf(settings settings.DNS, client network.Client, logger lo
"harden-algo-downgrade": "yes",
// Network
"do-ip4": "yes",
"do-ip6": "no",
"do-ip6": "yes",
"interface": "127.0.0.1",
"port": "53",
// Other

View File

@@ -43,7 +43,7 @@ server:
cache-max-ttl: 9000
cache-min-ttl: 3600
do-ip4: yes
do-ip6: no
do-ip6: yes
harden-algo-downgrade: yes
harden-below-nxdomain: yes
harden-referral-path: yes

View File

@@ -18,8 +18,7 @@ type Configurator interface {
Clear() error
BlockAll() error
CreateGeneralRules() error
CreateVPNRules(dev models.VPNDevice, serverIPs []net.IP, defaultInterface string,
port uint16, protocol models.NetworkProtocol) error
CreateVPNRules(dev models.VPNDevice, defaultInterface string, connections []models.OpenVPNConnection) error
CreateLocalSubnetsRules(subnet net.IPNet, extraSubnets []net.IPNet, defaultInterface string) error
AddRoutesVia(subnets []net.IPNet, defaultGateway net.IP, defaultInterface string) error
GetDefaultRoute() (defaultInterface string, defaultGateway net.IP, defaultSubnet net.IPNet, err error)

View File

@@ -77,14 +77,13 @@ func (c *configurator) CreateGeneralRules() error {
})
}
func (c *configurator) CreateVPNRules(dev models.VPNDevice, serverIPs []net.IP,
defaultInterface string, port uint16, protocol models.NetworkProtocol) error {
for _, serverIP := range serverIPs {
func (c *configurator) CreateVPNRules(dev models.VPNDevice, defaultInterface string, connections []models.OpenVPNConnection) error {
for _, connection := range connections {
c.logger.Info("%s: allowing output traffic to VPN server %s through %s on port %s %d",
logPrefix, serverIP, defaultInterface, protocol, port)
logPrefix, connection.IP, defaultInterface, connection.Protocol, connection.Port)
if err := c.runIptablesInstruction(
fmt.Sprintf("-A OUTPUT -d %s -o %s -p %s -m %s --dport %d -j ACCEPT",
serverIP, defaultInterface, protocol, protocol, port)); err != nil {
connection.IP, defaultInterface, connection.Protocol, connection.Protocol, connection.Port)); err != nil {
return err
}
}

View File

@@ -11,6 +11,12 @@ type (
PIAEncryption string
// PIARegion is used to define the list of regions available for PIA
PIARegion string
// MullvadCountry is used as the country for a Mullvad server
MullvadCountry string
// MullvadCity is used as the city for a Mullvad server
MullvadCity string
// MullvadProvider is used as the Internet service provider for a Mullvad server
MullvadProvider string
// URL is an HTTP(s) URL address
URL string
// Filepath is a local filesytem file path

View File

@@ -0,0 +1,12 @@
package models
import "net"
type MullvadServer struct {
Country MullvadCountry
City MullvadCity
Provider MullvadProvider
Owned bool
IPs []net.IP
DefaultPort uint16
}

View File

@@ -0,0 +1,9 @@
package models
import "net"
type OpenVPNConnection struct {
IP net.IP
Port uint16
Protocol NetworkProtocol
}

74
internal/mullvad/conf.go Normal file
View File

@@ -0,0 +1,74 @@
package mullvad
import (
"fmt"
"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) GetOpenVPNConnections(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider, protocol models.NetworkProtocol, customPort uint16) (connections []models.OpenVPNConnection, err error) {
servers := constants.MullvadServerFilter(country, city, provider)
if len(servers) == 0 {
return nil, fmt.Errorf("no server found for country %q, city %q and ISP %q", country, city, provider)
}
for _, server := range servers {
port := server.DefaultPort
if customPort > 0 {
port = customPort
}
for _, IP := range server.IPs {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: protocol})
}
}
return connections, nil
}
func (c *configurator) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int) (err error) {
if len(connections) == 0 {
return fmt.Errorf("at least one connection string is expected")
}
lines := []string{
"client",
"dev tun",
"nobind",
"persist-key",
"persist-tun",
"tls-client",
"remote-cert-tls server",
"ping 300",
// Mullvad specific
// "sndbuf 524288"
// "rcvbuf 524288"
"cipher AES-256-CBC",
"tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA",
"tun-ipv6",
// Added constant values
"mute-replay-warnings",
"auth-nocache",
"user nonrootuser",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
// Modified variables
fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", string(connections[0].Protocol)),
}
for _, connection := range connections {
lines = append(lines, fmt.Sprintf("remote %s %d", connection.IP.String(), connection.Port))
}
lines = append(lines, []string{
"<ca>",
"-----BEGIN CERTIFICATE-----",
constants.MullvadCertificate,
"-----END CERTIFICATE-----",
"</ca>",
"",
}...)
return c.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(uid, gid), files.Permissions(0400))
}

View File

@@ -0,0 +1,27 @@
package mullvad
import (
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
const logPrefix = "Mullvad configurator"
// Configurator contains methods to download, read and modify the openvpn configuration to connect as a client
type Configurator interface {
GetOpenVPNConnections(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider, protocol models.NetworkProtocol, customPort uint16) (connections []models.OpenVPNConnection, err error)
BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int) (err error)
}
type configurator struct {
client network.Client
fileManager files.FileManager
logger logging.Logger
}
// NewConfigurator returns a new Configurator object
func NewConfigurator(client network.Client, fileManager files.FileManager, logger logging.Logger) Configurator {
return &configurator{client, fileManager, logger}
}

View File

@@ -0,0 +1,40 @@
package params
import (
"strings"
libparams "github.com/qdm12/golibs/params"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
// GetMullvadCountry obtains the country for the Mullvad server from the
// environment variable COUNTRY
func (p *paramsReader) GetMullvadCountry() (country models.MullvadCountry, err error) {
choices := append(constants.MullvadCountryChoices(), "")
s, err := p.envParams.GetValueIfInside("COUNTRY", choices)
return models.MullvadCountry(strings.ToLower(s)), err
}
// GetMullvadCity obtains the city for the Mullvad server from the
// environment variable CITY
func (p *paramsReader) GetMullvadCity() (country models.MullvadCity, err error) {
choices := append(constants.MullvadCityChoices(), "")
s, err := p.envParams.GetValueIfInside("CITY", choices)
return models.MullvadCity(strings.ToLower(s)), err
}
// GetMullvadISP obtains the ISP for the Mullvad server from the
// environment variable ISP
func (p *paramsReader) GetMullvadISP() (country models.MullvadProvider, err error) {
choices := append(constants.MullvadProviderChoices(), "")
s, err := p.envParams.GetValueIfInside("ISP", choices)
return models.MullvadProvider(strings.ToLower(s)), err
}
// GetMullvadPort obtains the port to reach the Mullvad server on from the
// environment variable PORT
func (p *paramsReader) GetMullvadPort() (port uint16, err error) {
n, err := p.envParams.GetEnvIntRange("PORT", 0, 65535, libparams.Default("0"))
return uint16(n), err
}

View File

@@ -5,9 +5,37 @@ import (
"github.com/qdm12/private-internet-access-docker/internal/models"
)
// GetUser obtains the user to use to connect to the VPN servers
func (p *paramsReader) GetUser() (s string, err error) {
defer func() {
unsetenvErr := p.unsetEnv("USER")
if err == nil {
err = unsetenvErr
}
}()
return p.envParams.GetEnv("USER", libparams.CaseSensitiveValue(), libparams.Compulsory())
}
// GetPassword obtains the password to use to connect to the VPN servers
func (p *paramsReader) GetPassword() (s string, err error) {
defer func() {
unsetenvErr := p.unsetEnv("PASSWORD")
if err == nil {
err = unsetenvErr
}
}()
return p.envParams.GetEnv("PASSWORD", libparams.CaseSensitiveValue(), libparams.Compulsory())
}
// GetNetworkProtocol obtains the network protocol to use to connect to the
// VPN servers from the environment variable PROTOCOL
func (p *paramsReader) GetNetworkProtocol() (protocol models.NetworkProtocol, err error) {
s, err := p.envParams.GetValueIfInside("PROTOCOL", []string{"tcp", "udp"}, libparams.Default("udp"))
return models.NetworkProtocol(s), err
}
// GetOpenVPNVerbosity obtains the verbosity level for verbosity between 0 and 6
// from the environment variable OPENVPN_VERBOSITY
func (p *paramsReader) GetOpenVPNVerbosity() (verbosity int, err error) {
return p.envParams.GetEnvIntRange("OPENVPN_VERBOSITY", 0, 6, libparams.Default("1"))
}

View File

@@ -12,6 +12,8 @@ import (
// ParamsReader contains methods to obtain parameters
type ParamsReader interface {
GetVPNSP() (vpnServiceProvider string, err error)
// DNS over TLS getters
GetDNSOverTLS() (DNSOverTLS bool, err error)
GetDNSOverTLSProviders() (providers []models.DNSProvider, err error)
@@ -29,16 +31,23 @@ type ParamsReader interface {
GetExtraSubnets() (extraSubnets []net.IPNet, err error)
// VPN getters
GetNetworkProtocol() (protocol models.NetworkProtocol, err error)
// PIA getters
GetUser() (s string, err error)
GetPassword() (s string, err error)
GetNetworkProtocol() (protocol models.NetworkProtocol, err error)
GetOpenVPNVerbosity() (verbosity int, err error)
// PIA getters
GetPortForwarding() (activated bool, err error)
GetPortForwardingStatusFilepath() (filepath models.Filepath, err error)
GetPIAEncryption() (models.PIAEncryption, error)
GetPIARegion() (models.PIARegion, error)
// Mullvad getters
GetMullvadCountry() (country models.MullvadCountry, err error)
GetMullvadCity() (country models.MullvadCity, err error)
GetMullvadISP() (country models.MullvadProvider, err error)
GetMullvadPort() (port uint16, err error)
// Shadowsocks getters
GetShadowSocks() (activated bool, err error)
GetShadowSocksLog() (activated bool, err error)
@@ -75,3 +84,9 @@ func NewParamsReader(logger logging.Logger) ParamsReader {
unsetEnv: os.Unsetenv,
}
}
// GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP
func (p *paramsReader) GetVPNSP() (vpnServiceProvider string, err error) {
s, err := p.envParams.GetValueIfInside("VPNSP", []string{"pia", "mullvad"})
return s, err
}

View File

@@ -9,40 +9,6 @@ import (
"github.com/qdm12/private-internet-access-docker/internal/models"
)
// GetUser obtains the user to use to connect to the VPN servers
func (p *paramsReader) GetUser() (s string, err error) {
defer func() {
unsetenvErr := p.unsetEnv("USER")
if err == nil {
err = unsetenvErr
}
}()
s, err = p.envParams.GetEnv("USER")
if err != nil {
return "", err
} else if len(s) == 0 {
return s, fmt.Errorf("USER environment variable cannot be empty")
}
return s, nil
}
// GetPassword obtains the password to use to connect to the VPN servers
func (p *paramsReader) GetPassword() (s string, err error) {
defer func() {
unsetenvErr := p.unsetEnv("PASSWORD")
if err == nil {
err = unsetenvErr
}
}()
s, err = p.envParams.GetEnv("PASSWORD")
if err != nil {
return "", err
} else if len(s) == 0 {
return s, fmt.Errorf("PASSWORD environment variable cannot be empty")
}
return s, nil
}
// GetPortForwarding obtains if port forwarding on the VPN provider server
// side is enabled or not from the environment variable PORT_FORWARDING
func (p *paramsReader) GetPortForwarding() (activated bool, err error) {
@@ -62,7 +28,7 @@ func (p *paramsReader) GetPortForwarding() (activated bool, err error) {
// GetPortForwardingStatusFilepath obtains the port forwarding status file path
// from the environment variable PORT_FORWARDING_STATUS_FILE
func (p *paramsReader) GetPortForwardingStatusFilepath() (filepath models.Filepath, err error) {
filepathStr, err := p.envParams.GetPath("PORT_FORWARDING_STATUS_FILE", libparams.Default("/forwarded_port"))
filepathStr, err := p.envParams.GetPath("PORT_FORWARDING_STATUS_FILE", libparams.Default("/forwarded_port"), libparams.CaseSensitiveValue())
return models.Filepath(filepathStr), err
}
@@ -76,7 +42,7 @@ func (p *paramsReader) GetPIAEncryption() (models.PIAEncryption, error) {
// GetPIARegion obtains the region for the PIA server from the
// environment variable REGION
func (p *paramsReader) GetPIARegion() (region models.PIARegion, err error) {
choices := constants.PIAGeoChoices()
choices := append(constants.PIAGeoChoices(), "")
s, err := p.envParams.GetValueIfInside("REGION", choices)
if len(s) == 0 { // Suggestion by @rorph https://github.com/rorph
s = choices[rand.Int()%len(choices)]

View File

@@ -36,5 +36,5 @@ func (p *paramsReader) GetShadowSocksPort() (port uint16, err error) {
// SHADOWSOCKS_PASSWORD
func (p *paramsReader) GetShadowSocksPassword() (password string, err error) {
defer p.unsetEnv("SHADOWSOCKS_PASSWORD")
return p.envParams.GetEnv("SHADOWSOCKS_PASSWORD")
return p.envParams.GetEnv("SHADOWSOCKS_PASSWORD", libparams.CaseSensitiveValue())
}

View File

@@ -65,7 +65,7 @@ func (p *paramsReader) GetTinyProxyUser() (user string, err error) {
defer p.unsetEnv("PROXY_USER")
defer p.unsetEnv("TINYPROXY_USER")
// Retro-compatibility
user, err = p.envParams.GetEnv("PROXY_USER")
user, err = p.envParams.GetEnv("PROXY_USER", libparams.CaseSensitiveValue())
if err != nil {
return user, err
}
@@ -73,7 +73,7 @@ func (p *paramsReader) GetTinyProxyUser() (user string, err error) {
p.logger.Warn("You are using the old environment variable PROXY_USER, please consider changing it to TINYPROXY_USER")
return user, nil
}
return p.envParams.GetEnv("TINYPROXY_USER")
return p.envParams.GetEnv("TINYPROXY_USER", libparams.CaseSensitiveValue())
}
// GetTinyProxyPassword obtains the TinyProxy server password from the environment variable
@@ -82,7 +82,7 @@ func (p *paramsReader) GetTinyProxyPassword() (password string, err error) {
defer p.unsetEnv("PROXY_PASSWORD")
defer p.unsetEnv("TINYPROXY_PASSWORD")
// Retro-compatibility
password, err = p.envParams.GetEnv("PROXY_PASSWORD")
password, err = p.envParams.GetEnv("PROXY_PASSWORD", libparams.CaseSensitiveValue())
if err != nil {
return password, err
}
@@ -90,5 +90,5 @@ func (p *paramsReader) GetTinyProxyPassword() (password string, err error) {
p.logger.Warn("You are using the old environment variable PROXY_PASSWORD, please consider changing it to TINYPROXY_PASSWORD")
return password, nil
}
return p.envParams.GetEnv("TINYPROXY_PASSWORD")
return p.envParams.GetEnv("TINYPROXY_PASSWORD", libparams.CaseSensitiveValue())
}

View File

@@ -1,20 +1,20 @@
package params
import (
"github.com/qdm12/golibs/params"
libparams "github.com/qdm12/golibs/params"
)
func (p *paramsReader) GetVersion() string {
version, _ := p.envParams.GetEnv("VERSION", params.Default("?"))
version, _ := p.envParams.GetEnv("VERSION", libparams.Default("?"), libparams.CaseSensitiveValue())
return version
}
func (p *paramsReader) GetBuildDate() string {
buildDate, _ := p.envParams.GetEnv("BUILD_DATE", params.Default("?"))
buildDate, _ := p.envParams.GetEnv("BUILD_DATE", libparams.Default("?"), libparams.CaseSensitiveValue())
return buildDate
}
func (p *paramsReader) GetVcsRef() string {
buildDate, _ := p.envParams.GetEnv("VCS_REF", params.Default("?"))
buildDate, _ := p.envParams.GetEnv("VCS_REF", libparams.Default("?"), libparams.CaseSensitiveValue())
return buildDate
}

View File

@@ -2,45 +2,70 @@ package pia
import (
"fmt"
"net"
"strings"
"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
func (c *configurator) GetOpenVPNConnections(region models.PIARegion, protocol models.NetworkProtocol, encryption models.PIAEncryption) (connections []models.OpenVPNConnection, err error) {
geoMapping := constants.PIAGeoToSubdomainMapping()
var subdomain string
for r, s := range geoMapping {
if strings.ToLower(string(region)) == strings.ToLower(string(r)) {
subdomain = s
break
}
}
if len(subdomain) == 0 {
return nil, fmt.Errorf("region %q has no associated PIA subdomain", region)
}
if err != nil {
return nil, err
}
IPs, err := c.lookupIP(subdomain + ".privateinternetaccess.com")
if err != nil {
return nil, err
}
var port uint16
switch protocol {
case constants.TCP:
switch encryption {
case constants.PIAEncryptionNormal:
port = 502
case constants.PIAEncryptionStrong:
port = 501
}
case constants.UDP:
switch encryption {
case constants.PIAEncryptionNormal:
port = 1198
case constants.PIAEncryptionStrong:
port = 1197
}
}
if port == 0 {
return nil, fmt.Errorf("combination of protocol %q and encryption %q does not yield any port number", protocol, encryption)
}
for _, IP := range IPs {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: protocol})
}
return connections, nil
}
func (c *configurator) BuildConf(connections []models.OpenVPNConnection, encryption models.PIAEncryption, verbosity, uid, gid int) (err error) {
var X509CRL, certificate, cipherAlgo, authAlgo string
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
} else { // strong encryption
cipherAlgo = "aes-256-cbc"
authAlgo = "sha256"
X509CRL = constants.PIAX509CRL_STRONG
certificate = constants.PIACertificate_STRONG
if protocol == constants.UDP {
port = 1197
} else {
port = 501
}
}
subdomain, err := constants.PIAGeoToSubdomainMapping(region)
if err != nil {
return nil, 0, err
}
IPs, err = c.lookupIP(subdomain + ".privateinternetaccess.com")
if err != nil {
return nil, 0, err
}
lines := []string{
"client",
@@ -50,25 +75,30 @@ func (c *configurator) BuildConf(region models.PIARegion, protocol models.Networ
"persist-tun",
"tls-client",
"remote-cert-tls server",
"compress",
"verb 1", // TODO env variable
"ping 300", // Ping every 5 minutes to prevent a timeout error
// PIA specific
"reneg-sec 0",
"compress", // allow PIA server to choose the compression to use
// Added constant values
"tun-ipv6",
"auth-nocache",
"mute-replay-warnings",
"user nonrootuser",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"disable-occ",
"remote-random",
// Modified variables
fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", string(protocol)),
fmt.Sprintf("proto %s", string(connections[0].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))
for _, connection := range connections {
lines = append(lines, fmt.Sprintf("remote %s %d", connection.IP.String(), connection.Port))
}
lines = append(lines, []string{
"<crl-verify>",
@@ -85,6 +115,5 @@ func (c *configurator) BuildConf(region models.PIARegion, protocol models.Networ
"</ca>",
"",
}...)
err = c.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(uid, gid), files.Permissions(0400))
return IPs, port, err
return c.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(uid, gid), files.Permissions(0400))
}

View File

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

View File

@@ -0,0 +1,57 @@
package settings
import (
"strings"
"github.com/qdm12/private-internet-access-docker/internal/models"
"github.com/qdm12/private-internet-access-docker/internal/params"
)
// Mullvad contains the settings to connect to a Mullvad server
type Mullvad struct {
User string
Country models.MullvadCountry
City models.MullvadCity
ISP models.MullvadProvider
Port uint16
}
func (m *Mullvad) String() string {
settingsList := []string{
"Mullvad settings:",
"User: [redacted]",
"Country: " + string(m.Country),
"City: " + string(m.City),
"ISP: " + string(m.ISP),
"Port: " + string(m.Port),
}
return strings.Join(settingsList, "\n |--")
}
// GetMullvadSettings obtains Mullvad settings from environment variables using the params package.
func GetMullvadSettings(params params.ParamsReader) (settings Mullvad, err error) {
settings.User, err = params.GetUser()
if err != nil {
return settings, err
}
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
settings.User = strings.ReplaceAll(settings.User, " ", "")
settings.Country, err = params.GetMullvadCountry()
if err != nil {
return settings, err
}
settings.City, err = params.GetMullvadCity()
if err != nil {
return settings, err
}
settings.ISP, err = params.GetMullvadISP()
if err != nil {
return settings, err
}
settings.Port, err = params.GetMullvadPort()
if err != nil {
return settings, err
}
return settings, nil
}

View File

@@ -1,6 +1,7 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/private-internet-access-docker/internal/models"
@@ -10,6 +11,7 @@ import (
// OpenVPN contains settings to configure the OpenVPN client
type OpenVPN struct {
NetworkProtocol models.NetworkProtocol
Verbosity int
}
// GetOpenVPNSettings obtains the OpenVPN settings using the params functions
@@ -18,6 +20,10 @@ func GetOpenVPNSettings(params params.ParamsReader) (settings OpenVPN, err error
if err != nil {
return settings, err
}
settings.Verbosity, err = params.GetOpenVPNVerbosity()
if err != nil {
return settings, err
}
return settings, nil
}
@@ -25,6 +31,7 @@ func (o *OpenVPN) String() string {
settingsList := []string{
"OpenVPN settings:",
"Network protocol: " + string(o.NetworkProtocol),
"Verbosity level: " + fmt.Sprintf("%d", o.Verbosity),
}
return strings.Join(settingsList, "\n|--")
}

View File

@@ -1,6 +1,7 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/private-internet-access-docker/internal/params"
@@ -8,8 +9,10 @@ import (
// Settings contains all settings for the program to run
type Settings struct {
VPNSP string
OpenVPN OpenVPN
PIA PIA
Mullvad Mullvad
DNS DNS
Firewall Firewall
TinyProxy TinyProxy
@@ -17,10 +20,17 @@ type Settings struct {
}
func (s *Settings) String() string {
var vpnServiceProvider string
switch s.VPNSP {
case "pia":
vpnServiceProvider = s.PIA.String()
case "mullvad":
vpnServiceProvider = s.Mullvad.String()
}
return strings.Join([]string{
"Settings summary below:",
s.OpenVPN.String(),
s.PIA.String(),
vpnServiceProvider,
s.DNS.String(),
s.Firewall.String(),
s.TinyProxy.String(),
@@ -32,13 +42,27 @@ func (s *Settings) String() string {
// GetAllSettings obtains all settings for the program and returns an error as soon
// as an error is encountered reading them.
func GetAllSettings(params params.ParamsReader) (settings Settings, err error) {
settings.VPNSP, err = params.GetVPNSP()
if err != nil {
return settings, err
}
settings.OpenVPN, err = GetOpenVPNSettings(params)
if err != nil {
return settings, err
}
settings.PIA, err = GetPIASettings(params)
if err != nil {
return settings, err
switch settings.VPNSP {
case "pia":
settings.PIA, err = GetPIASettings(params)
if err != nil {
return settings, err
}
case "mullvad":
settings.Mullvad, err = GetMullvadSettings(params)
if err != nil {
return settings, err
}
default:
return settings, fmt.Errorf("VPN service provider %q is not valid", settings.VPNSP)
}
settings.DNS, err = GetDNSSettings(params)
if err != nil {

View File

@@ -39,11 +39,14 @@ func title() []string {
}
func annoucement() []string {
timestamp := time.Now().UnixNano() / 1000000000
if timestamp < constants.AnnoucementExpiration {
return []string{emoji.Sprint(":mega: ") + constants.Annoucement}
if len(constants.Annoucement) == 0 {
return nil
}
return nil
expirationDate, _ := time.Parse("2006-01-02", constants.AnnoucementExpiration) // error covered by a unit test
if time.Now().After(expirationDate) {
return nil
}
return []string{emoji.Sprint(":mega: ") + constants.Annoucement}
}
func links() []string {