Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ body:
label: VPN service provider
options:
- AirVPN
- AzireVPN
- Custom
- Cyberghost
- ExpressVPN
Expand Down
2 changes: 2 additions & 0 deletions .github/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
# VPN providers
- name: "☁️ AirVPN"
color: "cfe8d4"
- name: "☁️ AzireVPN"
color: "cfe8d4"
- name: "☁️ Custom"
color: "cfe8d4"
- name: "☁️ Cyberghost"
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ ENV VPN_SERVICE_PROVIDER=pia \
OPENVPN_MSSFIX= \
OPENVPN_CUSTOM_CONFIG= \
# Wireguard
AZIREVPN_TOKEN= \
WIREGUARD_ENDPOINT_IP= \
WIREGUARD_ENDPOINT_PORT= \
WIREGUARD_CONF_SECRETFILE=/run/secrets/wg0.conf \
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ Lightweight swiss-army-knife-like VPN client to multiple VPN service providers
## Features

- Based on Alpine 3.23 for a small Docker image of 43.1MB
- Supports: **AirVPN**, **Cyberghost**, **ExpressVPN**, **FastestVPN**, **Giganews**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad** (Wireguard only), **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **SlickVPN**, **Surfshark**, **TorGuard**, **VPNSecure.me**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
- Supports: **AirVPN**, **AzireVPN** (Wireguard only), **Cyberghost**, **ExpressVPN**, **FastestVPN**, **Giganews**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad** (Wireguard only), **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **SlickVPN**, **Surfshark**, **TorGuard**, **VPNSecure.me**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
- Supports OpenVPN for all providers listed
- Supports Wireguard both kernelspace and userspace
- For **AirVPN**, **FastestVPN**, **Ivpn**, **Mullvad**, **NordVPN**, **Perfect privacy**, **ProtonVPN**, **Surfshark** and **Windscribe**
- For **AirVPN**, **AzireVPN**, **FastestVPN**, **Ivpn**, **Mullvad**, **NordVPN**, **Perfect privacy**, **ProtonVPN**, **Surfshark** and **Windscribe**
- For **Cyberghost**, **Private Internet Access**, **PrivateVPN**, **PureVPN**, **Torguard**, **VPN Unlimited** and **VyprVPN** using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md)
- For custom Wireguard configurations using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md)
- More in progress, see [#134](https://github.com/passteque/gluetun/issues/134)
Expand All @@ -78,7 +78,7 @@ Lightweight swiss-army-knife-like VPN client to multiple VPN service providers
- [Connect other containers to it](https://github.com/qdm12/gluetun-wiki/blob/main/setup/connect-a-container-to-gluetun.md)
- [Connect LAN devices to it](https://github.com/qdm12/gluetun-wiki/blob/main/setup/connect-a-lan-device-to-gluetun.md)
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
- Custom VPN server side port forwarding for [Perfect Privacy](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/perfect-privacy.md#vpn-server-port-forwarding), [Private Internet Access](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/private-internet-access.md#vpn-server-port-forwarding), [PrivateVPN](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/privatevpn.md#vpn-server-port-forwarding) and [ProtonVPN](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/protonvpn.md#vpn-server-port-forwarding)
- Custom VPN server side port forwarding for [AzireVPN](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/azirevpn.md#vpn-server-port-forwarding), [Perfect Privacy](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/perfect-privacy.md#vpn-server-port-forwarding), [Private Internet Access](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/private-internet-access.md#vpn-server-port-forwarding), [PrivateVPN](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/privatevpn.md#vpn-server-port-forwarding) and [ProtonVPN](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/protonvpn.md#vpn-server-port-forwarding)
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
- Can work as a Kubernetes sidecar container, thanks @rorph

Expand Down
1 change: 1 addition & 0 deletions internal/configuration/settings/openvpnselection.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) {

// Validate TCP
if o.Protocol == constants.TCP && helpers.IsOneOf(vpnProvider,
providers.Azirevpn,
providers.Giganews,
providers.Ipvanish,
providers.Perfectprivacy,
Expand Down
1 change: 1 addition & 0 deletions internal/configuration/settings/portforward.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func (p PortForwarding) Validate(vpnProvider string) (err error) {
providerSelected = *p.Provider
}
validProviders := []string{
providers.Azirevpn,
providers.Perfectprivacy,
providers.PrivateInternetAccess,
providers.Privatevpn,
Expand Down
19 changes: 19 additions & 0 deletions internal/configuration/settings/provider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package settings

import (
"errors"
"fmt"
"slices"
"sort"
Expand All @@ -24,6 +25,8 @@ type Provider struct {
ServerSelection ServerSelection `json:"server_selection"`
// PortForwarding is the settings about port forwarding.
PortForwarding PortForwarding `json:"port_forwarding"`
// AzirevpnToken is the API token used by AzireVPN.
AzirevpnToken string `json:"azirevpn_token"`
}

// TODO v4 remove pointer for receiver (because of Surfshark).
Expand All @@ -40,10 +43,15 @@ func (p *Provider) validate(vpnType string, filterChoicesGetter FilterChoicesGet
mullvadIndex := slices.Index(validNames, providers.Mullvad)
validNames[mullvadIndex], validNames[len(validNames)-1] = validNames[len(validNames)-1], validNames[mullvadIndex]
validNames = validNames[:len(validNames)-1]
// Remove AzireVPN since it is Wireguard only.
azirevpnIndex := slices.Index(validNames, providers.Azirevpn)
validNames[azirevpnIndex], validNames[len(validNames)-1] = validNames[len(validNames)-1], validNames[azirevpnIndex]
validNames = validNames[:len(validNames)-1]
sort.Strings(validNames)
case vpn.Wireguard:
validNames = []string{
providers.Airvpn,
providers.Azirevpn,
providers.Custom,
providers.Fastestvpn,
providers.Ivpn,
Expand All @@ -58,6 +66,10 @@ func (p *Provider) validate(vpnType string, filterChoicesGetter FilterChoicesGet
return fmt.Errorf("VPN provider name is not valid for %s: %w", vpnType, err)
}

if p.Name == providers.Azirevpn && *p.PortForwarding.Enabled && p.AzirevpnToken == "" {
return errors.New("azirevpn token is missing")
}

err = p.ServerSelection.validate(p.Name, filterChoicesGetter, warner)
if err != nil {
return fmt.Errorf("server selection: %w", err)
Expand All @@ -76,19 +88,22 @@ func (p *Provider) copy() (copied Provider) {
Name: p.Name,
ServerSelection: p.ServerSelection.copy(),
PortForwarding: p.PortForwarding.Copy(),
AzirevpnToken: p.AzirevpnToken,
}
}

func (p *Provider) overrideWith(other Provider) {
p.Name = gosettings.OverrideWithComparable(p.Name, other.Name)
p.ServerSelection.overrideWith(other.ServerSelection)
p.PortForwarding.OverrideWith(other.PortForwarding)
p.AzirevpnToken = gosettings.OverrideWithComparable(p.AzirevpnToken, other.AzirevpnToken)
}

func (p *Provider) setDefaults() {
p.Name = gosettings.DefaultComparable(p.Name, providers.PrivateInternetAccess)
p.PortForwarding.setDefaults()
p.ServerSelection.setDefaults(p.Name, *p.PortForwarding.Enabled)
p.AzirevpnToken = gosettings.DefaultComparable(p.AzirevpnToken, "")
}

func (p Provider) String() string {
Expand All @@ -98,13 +113,17 @@ func (p Provider) String() string {
func (p Provider) toLinesNode() (node *gotree.Node) {
node = gotree.New("VPN provider settings:")
node.Appendf("Name: %s", p.Name)
if p.AzirevpnToken != "" {
node.Appendf("AzireVPN token: %s", gosettings.ObfuscateKey(p.AzirevpnToken))
}
node.AppendNode(p.ServerSelection.toLinesNode())
node.AppendNode(p.PortForwarding.toLinesNode())
return node
}

func (p *Provider) read(r *reader.Reader, vpnType string) (err error) {
p.Name = readVPNServiceProvider(r, vpnType)
p.AzirevpnToken = r.String("AZIREVPN_TOKEN", reader.ForceLowercase(false))

err = p.ServerSelection.read(r, p.Name, vpnType)
if err != nil {
Expand Down
6 changes: 4 additions & 2 deletions internal/configuration/settings/serverselection.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ func validateSubscriptionTierFilters(settings ServerSelection, vpnServiceProvide

func validateFeatureFilters(settings ServerSelection, vpnServiceProvider string) error {
switch {
case *settings.OwnedOnly && vpnServiceProvider != providers.Mullvad:
case *settings.OwnedOnly &&
!helpers.IsOneOf(vpnServiceProvider, providers.Mullvad, providers.Azirevpn):
return errors.New("owned only filter is not supported")
case vpnServiceProvider == providers.Protonvpn && *settings.FreeOnly && *settings.PortForwardOnly:
return errors.New("port forwarding only filter is not supported: together with free only filter")
Expand All @@ -266,7 +267,8 @@ func validateFeatureFilters(settings ServerSelection, vpnServiceProvider string)
case *settings.MultiHopOnly && vpnServiceProvider != providers.Surfshark:
return errors.New("multi hop only filter is not supported")
case *settings.PortForwardOnly &&
!helpers.IsOneOf(vpnServiceProvider, providers.PrivateInternetAccess, providers.Protonvpn):
!helpers.IsOneOf(vpnServiceProvider, providers.Azirevpn,
providers.PrivateInternetAccess, providers.Protonvpn):
return errors.New("port forwarding only filter is not supported")
case *settings.SecureCoreOnly && vpnServiceProvider != providers.Protonvpn:
return errors.New("secure core only filter is not supported")
Expand Down
9 changes: 9 additions & 0 deletions internal/configuration/settings/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type Updater struct {
ProtonEmail *string
// ProtonPassword is the password to authenticate with the Proton API.
ProtonPassword *string
// AzirevpnToken is the token for AzireVPN API.
AzirevpnToken string
}

func (u Updater) Validate() (err error) {
Expand Down Expand Up @@ -81,6 +83,7 @@ func (u *Updater) copy() (copied Updater) {
PreferDirectDownload: gosettings.CopyPointer(u.PreferDirectDownload),
ProtonEmail: gosettings.CopyPointer(u.ProtonEmail),
ProtonPassword: gosettings.CopyPointer(u.ProtonPassword),
AzirevpnToken: u.AzirevpnToken,
}
}

Expand All @@ -94,6 +97,7 @@ func (u *Updater) overrideWith(other Updater) {
u.PreferDirectDownload = gosettings.OverrideWithPointer(u.PreferDirectDownload, other.PreferDirectDownload)
u.ProtonEmail = gosettings.OverrideWithPointer(u.ProtonEmail, other.ProtonEmail)
u.ProtonPassword = gosettings.OverrideWithPointer(u.ProtonPassword, other.ProtonPassword)
u.AzirevpnToken = gosettings.OverrideWithComparable(u.AzirevpnToken, other.AzirevpnToken)
}

func (u *Updater) SetDefaults(vpnProvider string) {
Expand All @@ -112,6 +116,7 @@ func (u *Updater) SetDefaults(vpnProvider string) {
u.PreferDirectDownload = gosettings.DefaultPointer(u.PreferDirectDownload, false)
u.ProtonEmail = gosettings.DefaultPointer(u.ProtonEmail, "")
u.ProtonPassword = gosettings.DefaultPointer(u.ProtonPassword, "")
u.AzirevpnToken = gosettings.DefaultComparable(u.AzirevpnToken, "")
}

func (u Updater) String() string {
Expand All @@ -132,6 +137,9 @@ func (u Updater) toLinesNode() (node *gotree.Node) {
node.Appendf("Proton API email: %s", *u.ProtonEmail)
node.Appendf("Proton API password: %s", gosettings.ObfuscateKey(*u.ProtonPassword))
}
if slices.Contains(u.Providers, providers.Azirevpn) && u.AzirevpnToken != "" {
node.Appendf("AzireVPN API token: %s", gosettings.ObfuscateKey(u.AzirevpnToken))
}

return node
}
Expand Down Expand Up @@ -163,6 +171,7 @@ func (u *Updater) read(r *reader.Reader) (err error) {
}
}
u.ProtonPassword = r.Get("UPDATER_PROTONVPN_PASSWORD")
u.AzirevpnToken = r.String("AZIREVPN_TOKEN", reader.ForceLowercase(false))

return nil
}
8 changes: 5 additions & 3 deletions internal/configuration/settings/wireguardselection.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ type WireguardSelection struct {
func (w WireguardSelection) validate(vpnProvider string) (err error) {
// Validate EndpointIP
switch vpnProvider {
case providers.Airvpn, providers.Fastestvpn, providers.Ivpn,
case providers.Airvpn, providers.Azirevpn,
providers.Fastestvpn, providers.Ivpn,
providers.Mullvad, providers.Nordvpn, providers.Protonvpn,
providers.Surfshark, providers.Windscribe:
// endpoint IP addresses are baked in
Expand All @@ -58,7 +59,7 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
return errors.New("endpoint port is not set")
}
// EndpointPort cannot be set
case providers.Fastestvpn, providers.Nordvpn,
case providers.Azirevpn, providers.Fastestvpn, providers.Nordvpn,
providers.Protonvpn, providers.Surfshark:
if *w.EndpointPort != 0 {
return errors.New("endpoint port is set")
Expand Down Expand Up @@ -91,7 +92,8 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {

// Validate PublicKey
switch vpnProvider {
case providers.Fastestvpn, providers.Ivpn, providers.Mullvad,
case providers.Azirevpn, providers.Fastestvpn,
providers.Ivpn, providers.Mullvad,
providers.Surfshark, providers.Windscribe:
// public keys are baked in
case providers.Custom:
Expand Down
2 changes: 2 additions & 0 deletions internal/constants/providers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const (
// Custom is the VPN provider name for custom
// VPN configurations.
Airvpn = "airvpn"
Azirevpn = "azirevpn"
Custom = "custom"
Cyberghost = "cyberghost"
Example = "example"
Expand Down Expand Up @@ -34,6 +35,7 @@ const (
func All() []string {
return []string{
Airvpn,
Azirevpn,
Cyberghost,
Expressvpn,
Fastestvpn,
Expand Down
2 changes: 2 additions & 0 deletions internal/models/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ func getMarkdownHeaders(vpnProvider string) (headers []string, err error) {
regionHeader, countryHeader, cityHeader, vpnHeader,
udpHeader, tcpHeader, hostnameHeader, nameHeader,
}, nil
case providers.Azirevpn:
return []string{countryHeader, cityHeader, hostnameHeader, vpnHeader, ownedHeader, portForwardHeader}, nil
case providers.Cyberghost:
return []string{countryHeader, hostnameHeader, tcpHeader, udpHeader}, nil
case providers.Expressvpn:
Expand Down
Loading