Wireguard support for Mullvad and Windscribe (#565)
- `internal/wireguard` client package with unit tests - Implementation works with kernel space or user space if unavailable - `WIREGUARD_PRIVATE_KEY` - `WIREGUARD_ADDRESS` - `WIREGUARD_PRESHARED_KEY` - `WIREGUARD_PORT` - `internal/netlink` package used by `internal/wireguard`
This commit is contained in:
@@ -22,10 +22,12 @@ type serverData struct {
|
||||
Provider string `json:"provider"`
|
||||
IPv4 string `json:"ipv4_addr_in"`
|
||||
IPv6 string `json:"ipv6_addr_in"`
|
||||
Type string `json:"type"`
|
||||
PubKey string `json:"pubkey"` // Wireguard public key
|
||||
}
|
||||
|
||||
func fetchAPI(ctx context.Context, client *http.Client) (data []serverData, err error) {
|
||||
const url = "https://api.mullvad.net/www/relays/openvpn/"
|
||||
const url = "https://api.mullvad.net/www/relays/all/"
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,14 +6,17 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
type hostToServer map[string]models.MullvadServer
|
||||
|
||||
var (
|
||||
ErrParseIPv4 = errors.New("cannot parse IPv4 address")
|
||||
ErrParseIPv6 = errors.New("cannot parse IPv6 address")
|
||||
ErrNoIP = errors.New("no IP address for VPN server")
|
||||
ErrParseIPv4 = errors.New("cannot parse IPv4 address")
|
||||
ErrParseIPv6 = errors.New("cannot parse IPv6 address")
|
||||
ErrVPNTypeNotSupported = errors.New("VPN type not supported")
|
||||
)
|
||||
|
||||
func (hts hostToServer) add(data serverData) (err error) {
|
||||
@@ -21,14 +24,8 @@ func (hts hostToServer) add(data serverData) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
ipv4 := net.ParseIP(data.IPv4)
|
||||
if ipv4 == nil || ipv4.To4() == nil {
|
||||
return fmt.Errorf("%w: %s", ErrParseIPv4, data.IPv4)
|
||||
}
|
||||
|
||||
ipv6 := net.ParseIP(data.IPv6)
|
||||
if ipv6 == nil || ipv6.To4() != nil {
|
||||
return fmt.Errorf("%w: %s", ErrParseIPv6, data.IPv6)
|
||||
if data.IPv4 == "" && data.IPv6 == "" {
|
||||
return ErrNoIP
|
||||
}
|
||||
|
||||
server, ok := hts[data.Hostname]
|
||||
@@ -36,13 +33,40 @@ func (hts hostToServer) add(data serverData) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch data.Type {
|
||||
case "openvpn":
|
||||
server.VPN = constants.OpenVPN
|
||||
case "wireguard":
|
||||
server.VPN = constants.Wireguard
|
||||
case "bridge":
|
||||
// ignore bridge servers
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("%w: %s", ErrVPNTypeNotSupported, data.Type)
|
||||
}
|
||||
|
||||
if data.IPv4 != "" {
|
||||
ipv4 := net.ParseIP(data.IPv4)
|
||||
if ipv4 == nil || ipv4.To4() == nil {
|
||||
return fmt.Errorf("%w: %s", ErrParseIPv4, data.IPv4)
|
||||
}
|
||||
server.IPs = []net.IP{ipv4}
|
||||
}
|
||||
|
||||
if data.IPv6 != "" {
|
||||
ipv6 := net.ParseIP(data.IPv6)
|
||||
if ipv6 == nil || ipv6.To4() != nil {
|
||||
return fmt.Errorf("%w: %s", ErrParseIPv6, data.IPv6)
|
||||
}
|
||||
server.IPsV6 = []net.IP{ipv6}
|
||||
}
|
||||
|
||||
server.Country = data.Country
|
||||
server.City = strings.ReplaceAll(data.City, ",", "")
|
||||
server.Hostname = data.Hostname
|
||||
server.ISP = data.Provider
|
||||
server.Owned = data.Owned
|
||||
server.IPs = []net.IP{ipv4}
|
||||
server.IPsV6 = []net.IP{ipv6}
|
||||
server.WgPubKey = data.PubKey
|
||||
|
||||
hts[data.Hostname] = server
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ type groupData struct {
|
||||
City string `json:"city"`
|
||||
Nodes []serverData `json:"nodes"`
|
||||
OvpnX509 string `json:"ovpn_x509"`
|
||||
WgPubKey string `json:"wg_pubkey"`
|
||||
}
|
||||
|
||||
type serverData struct {
|
||||
|
||||
@@ -9,10 +9,14 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
var ErrNotEnoughServers = errors.New("not enough servers found")
|
||||
var (
|
||||
ErrNotEnoughServers = errors.New("not enough servers found")
|
||||
ErrNoWireguardKey = errors.New("no wireguard public key found")
|
||||
)
|
||||
|
||||
func GetServers(ctx context.Context, client *http.Client, minServers int) (
|
||||
servers []models.WindscribeServer, err error) {
|
||||
@@ -26,19 +30,17 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
|
||||
for _, group := range regionData.Groups {
|
||||
city := group.City
|
||||
x5090Name := group.OvpnX509
|
||||
wgPubKey := group.WgPubKey
|
||||
for _, node := range group.Nodes {
|
||||
const maxIPsPerNode = 3
|
||||
ips := make([]net.IP, 0, maxIPsPerNode)
|
||||
ips := make([]net.IP, 0, 2) // nolint:gomnd
|
||||
if node.IP != nil {
|
||||
ips = append(ips, node.IP)
|
||||
}
|
||||
if node.IP2 != nil {
|
||||
ips = append(ips, node.IP2)
|
||||
}
|
||||
// if node.IP3 != nil { // Wireguard + Stealth
|
||||
// ips = append(ips, node.IP3)
|
||||
// }
|
||||
server := models.WindscribeServer{
|
||||
VPN: constants.OpenVPN,
|
||||
Region: region,
|
||||
City: city,
|
||||
Hostname: node.Hostname,
|
||||
@@ -46,6 +48,18 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
|
||||
IPs: ips,
|
||||
}
|
||||
servers = append(servers, server)
|
||||
|
||||
if node.IP3 == nil { // Wireguard + Stealth
|
||||
continue
|
||||
} else if wgPubKey == "" {
|
||||
return nil, fmt.Errorf("%w: for node %s", ErrNoWireguardKey, node.Hostname)
|
||||
}
|
||||
|
||||
server.VPN = constants.Wireguard
|
||||
server.OvpnX509 = ""
|
||||
server.WgPubKey = wgPubKey
|
||||
server.IPs = []net.IP{node.IP3}
|
||||
servers = append(servers, server)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ func sortServers(servers []models.WindscribeServer) {
|
||||
sort.Slice(servers, func(i, j int) bool {
|
||||
if servers[i].Region == servers[j].Region {
|
||||
if servers[i].City == servers[j].City {
|
||||
if servers[i].Hostname == servers[j].Hostname {
|
||||
return servers[i].VPN < servers[j].VPN
|
||||
}
|
||||
return servers[i].Hostname < servers[j].Hostname
|
||||
}
|
||||
return servers[i].City < servers[j].City
|
||||
|
||||
Reference in New Issue
Block a user