mirror of
https://github.com/davegallant/vpngate.git
synced 2025-11-25 19:04:17 +00:00
Compare commits
73 Commits
v0.1.3
...
1c980b4209
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c980b4209 | ||
| 9545c9fd85 | |||
|
|
fb9c4800ae | ||
| 903f93895a | |||
|
|
1de072bbbf | ||
|
|
486fc180dd | ||
|
|
52cadc8433 | ||
|
|
106f8b39d2 | ||
|
|
79c742f274 | ||
| 0167804252 | |||
|
|
acb938dfdd | ||
|
|
5c069cb289 | ||
|
|
98bb23ee28 | ||
| 9185801b91 | |||
|
|
4cb7af39c6 | ||
|
|
552a6e3543 | ||
|
|
8811763d7e | ||
|
|
4bd470b95e | ||
|
|
9fb2a3f171 | ||
|
|
4e8cc6b9b7 | ||
|
|
7afc0dd235 | ||
|
|
a4391ba6ee | ||
|
|
4c66b19a7d | ||
|
|
3a03348840 | ||
|
|
361d54c96f | ||
|
|
f5d788a271 | ||
|
|
4b202ae94c | ||
|
|
a72c298292 | ||
|
|
3e819c5c55 | ||
|
|
885f73db1c | ||
|
|
c225d77696 | ||
|
|
c1548a3378 | ||
|
|
86a0869bb5 | ||
|
|
0ec8aa1977 | ||
|
|
667783899e | ||
|
|
ef99491fa6 | ||
|
|
4d458e4d47 | ||
|
|
16aa16c66e | ||
|
|
5850876efa | ||
|
|
98426b012d | ||
|
|
679a63cc14 | ||
|
|
a6e6fc992f | ||
|
|
830b40f945 | ||
|
|
950ef75855 | ||
|
|
9768124578 | ||
|
|
88c77db4c2 | ||
|
|
12e76d6443 | ||
|
|
710b69750f | ||
|
|
039793cbd6 | ||
|
|
660f20758c | ||
|
|
a01305cccd | ||
|
|
b0b36599ed | ||
|
|
e7c2c61335 | ||
|
|
41b629c45d | ||
|
|
8fdb73df50 | ||
|
|
bde340e5fb | ||
|
|
86b054fd7a | ||
|
|
47b434020c | ||
|
|
4391374bcd | ||
|
|
2609e8b969 | ||
|
|
48c551ef9c | ||
|
|
f69bdce04d | ||
|
|
0074b2c6c6 | ||
|
|
23d020f9ff | ||
|
|
015727c6c7 | ||
|
|
d27bcdbc4a | ||
|
|
000d5762e8 | ||
|
|
d93b835130 | ||
|
|
7ef409d307 | ||
|
|
b2b9900a90 | ||
|
|
3510f02036 | ||
|
|
3044ecef5f | ||
|
|
0acfaef158 |
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@@ -1,11 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: gomod
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
10
.github/workflows/golangci-lint.yml
vendored
10
.github/workflows/golangci-lint.yml
vendored
@@ -13,13 +13,13 @@ jobs:
|
|||||||
name: validate
|
name: validate
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.25
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v9
|
||||||
with:
|
with:
|
||||||
version: v1.49
|
version: v2.6.2
|
||||||
- name: test
|
- name: test
|
||||||
run: make test
|
run: make test
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
-
|
-
|
||||||
name: Set up Go
|
name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: "1.25"
|
||||||
-
|
-
|
||||||
name: Run GoReleaser
|
name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v4
|
uses: goreleaser/goreleaser-action@v6
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: '~> v2'
|
||||||
args: release --rm-dist
|
args: release --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_TOKEN }}
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
@@ -1,36 +1,40 @@
|
|||||||
|
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||||
|
# vim: set ts=2 sw=2 tw=0 fo=jcroql
|
||||||
|
version: 2
|
||||||
|
|
||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
- go get -v
|
- go get -v
|
||||||
- rm -rf dist
|
- rm -rf dist
|
||||||
builds:
|
builds:
|
||||||
-
|
- env:
|
||||||
env:
|
- CGO_ENABLED=0
|
||||||
- CGO_ENABLED=0
|
goos:
|
||||||
ldflags:
|
- linux
|
||||||
- -s -w
|
- darwin
|
||||||
goos:
|
- windows
|
||||||
- darwin
|
goarch:
|
||||||
- linux
|
- "386"
|
||||||
goarch:
|
- amd64
|
||||||
- amd64
|
- arm
|
||||||
- arm
|
- arm64
|
||||||
- arm64
|
goarm:
|
||||||
archives:
|
- "7"
|
||||||
- replacements:
|
ldflags:
|
||||||
darwin: Darwin
|
- -s -w
|
||||||
linux: Linux
|
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||||
amd64: x86_64
|
|
||||||
checksum:
|
checksum:
|
||||||
name_template: 'checksums.txt'
|
name_template: "checksums.txt"
|
||||||
snapshot:
|
snapshot:
|
||||||
name_template: "{{ .Tag }}"
|
name_template: "{{ .Tag }}"
|
||||||
changelog:
|
changelog:
|
||||||
sort: asc
|
sort: asc
|
||||||
|
use: github
|
||||||
filters:
|
filters:
|
||||||
exclude:
|
exclude:
|
||||||
- '^docs:'
|
- "^docs:"
|
||||||
- '^test:'
|
- "^test:"
|
||||||
release:
|
release:
|
||||||
github:
|
github:
|
||||||
owner: davegallant
|
owner: davegallant
|
||||||
@@ -38,12 +42,12 @@ release:
|
|||||||
|
|
||||||
# Check https://goreleaser.com/customization/homebrew/
|
# Check https://goreleaser.com/customization/homebrew/
|
||||||
brews:
|
brews:
|
||||||
- homepage: 'https://github.com/davegallant/homebrew-public'
|
- homepage: "https://github.com/davegallant/homebrew-public"
|
||||||
description: 'a client for vpngate.net'
|
description: "a client for vpngate.net"
|
||||||
folder: Formula
|
directory: Formula
|
||||||
commit_author:
|
commit_author:
|
||||||
name: davegallant
|
name: davegallant
|
||||||
email: davegallant@gmail.com
|
email: davegallant@gmail.com
|
||||||
tap:
|
repository:
|
||||||
owner: davegallant
|
owner: davegallant
|
||||||
name: homebrew-public
|
name: homebrew-public
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -11,5 +11,5 @@ test: ## Run unit tests
|
|||||||
.PHONY: test
|
.PHONY: test
|
||||||
|
|
||||||
lint: ## Run lint
|
lint: ## Run lint
|
||||||
@go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.51.0
|
@go get github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.6.2
|
||||||
golangci-lint run
|
golangci-lint run
|
||||||
|
|||||||
51
README.md
51
README.md
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
This is a client for [vpngate.net](https://www.vpngate.net/).
|
This is a client for [vpngate.net](https://www.vpngate.net/).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
This client fetches the list of available relay servers provided by vpngate.net, and allows you to filter and connect to a server of your liking.
|
This client fetches the list of available relay servers provided by vpngate.net, and allows you to filter and connect to a server of your liking.
|
||||||
|
|
||||||
You can check out your current IP address and region at https://nordvpn.com/what-is-my-ip/, or simply run the following command in a terminal:
|
You can check out your current IP address and region at <https://ipinfo.io/json>, or run the following:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl ipinfo.io
|
curl ipinfo.io
|
||||||
@@ -14,23 +14,34 @@ curl ipinfo.io
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- [openvpn](https://github.com/OpenVPN/openvpn)
|
- OpenVPN
|
||||||
- macOS or Linux
|
- macOS, Linux, or Windows
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
The simplest method of installation is using homebrew. You can also build from source.
|
You can install vpngate in a few different ways, and it will differ slightly depending on your OS.
|
||||||
|
|
||||||
### from homebrew
|
### Homebrew (macOS and linux)
|
||||||
|
|
||||||
vpngate can be installed with [homebrew](https://brew.sh/) (ensure that xcode is installed before installing homebrew by running `xcode-select --install`).
|
vpngate can be installed with [homebrew](https://brew.sh/) (if on macOS, ensure that xcode is installed before installing homebrew by running `xcode-select --install`).
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|
||||||
brew install openvpn davegallant/public/vpngate
|
brew install openvpn davegallant/public/vpngate
|
||||||
```
|
```
|
||||||
|
|
||||||
### from source
|
### Windows
|
||||||
|
|
||||||
|
On Windows, install OpenVPN from the [official website](https://openvpn.net/community-downloads/).
|
||||||
|
|
||||||
|
As there is no installer at the moment, you will need to download and extract the Windows release from the relevant Github release.
|
||||||
|
|
||||||
|
Once the release is extracted, open Command Prompt *as Administrator*, and run vpngate.exe from the location where it was extracted.
|
||||||
|
|
||||||
|
<img width="278" alt="image" src="https://github.com/user-attachments/assets/fb47270d-82bb-4790-833a-377b874c8104">
|
||||||
|
|
||||||
|
<img width="565" alt="image" src="https://github.com/user-attachments/assets/42287904-6c00-48d1-bff3-9757cf250519">
|
||||||
|
|
||||||
|
### Build from source
|
||||||
|
|
||||||
Ensure that [go](https://golang.org/doc/install) is installed.
|
Ensure that [go](https://golang.org/doc/install) is installed.
|
||||||
|
|
||||||
@@ -79,6 +90,28 @@ If the country doesn't matter, a random server can be selected:
|
|||||||
sudo vpngate connect --random
|
sudo vpngate connect --random
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Proxy
|
||||||
|
|
||||||
|
In some cases, anonymity is necessary to populate the list of available VPN servers.
|
||||||
|
|
||||||
|
A proxy is a way to bypass restrictions and in some cases, internet censorship.
|
||||||
|
|
||||||
|
##### HTTP/HTTPS
|
||||||
|
|
||||||
|
Use the specified HTTP/HTTPS proxy to fetch the server list.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo vpngate connect --proxy "http://localhost:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
##### SOCKS5
|
||||||
|
|
||||||
|
Use the specified SOCKS5 proxy to fetch the server list.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo vpngate connect --socks5 "127.0.0.1:1080"
|
||||||
|
```
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- I do not maintain any of the servers on vpngate.net (connect to these servers at your own discretion)
|
- I do not maintain any of the servers on vpngate.net (connect to these servers at your own discretion)
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@@ -17,25 +15,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagRandom bool
|
flagRandom bool
|
||||||
flagReconnect bool
|
flagReconnect bool
|
||||||
|
flagProxy string
|
||||||
|
flagSocks5Proxy string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
connectCmd.Flags().BoolVarP(&flagRandom, "random", "r", false, "connect to a random server")
|
connectCmd.Flags().BoolVarP(&flagRandom, "random", "r", false, "connect to a random server")
|
||||||
connectCmd.Flags().BoolVarP(&flagReconnect, "reconnect", "t", false, "continually attempt to connect to the server")
|
connectCmd.Flags().BoolVarP(&flagReconnect, "reconnect", "t", false, "continually attempt to connect to the server")
|
||||||
|
connectCmd.Flags().StringVarP(&flagProxy, "proxy", "p", "", "provide a http/https proxy server to make requests through (i.e. http://127.0.0.1:8080)")
|
||||||
|
connectCmd.Flags().StringVarP(&flagSocks5Proxy, "socks5", "s", "", "provide a socks5 proxy server to make requests through (i.e. 127.0.0.1:1080)")
|
||||||
rootCmd.AddCommand(connectCmd)
|
rootCmd.AddCommand(connectCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
var connectCmd = &cobra.Command{
|
var connectCmd = &cobra.Command{
|
||||||
Use: "connect",
|
Use: "connect",
|
||||||
|
|
||||||
Short: "Connect to a vpn server (survey selection appears if hostname is not provided)",
|
Short: "Connect to a vpn server (survey selection appears if hostname is not provided)",
|
||||||
Long: `Connect to a vpn from a list of relay servers`,
|
Long: `Connect to a vpn from a list of relay servers`,
|
||||||
Args: cobra.RangeArgs(0, 1),
|
Args: cobra.RangeArgs(0, 1),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
vpnServers, err := vpn.GetList()
|
vpnServers, err := vpn.GetList(flagProxy, flagSocks5Proxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Msgf(err.Error())
|
log.Fatal().Msg(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,29 +84,28 @@ var connectCmd = &cobra.Command{
|
|||||||
|
|
||||||
if flagRandom {
|
if flagRandom {
|
||||||
// Select a random server
|
// Select a random server
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
serverSelected = (*vpnServers)[rand.Intn(len(*vpnServers))]
|
serverSelected = (*vpnServers)[rand.Intn(len(*vpnServers))]
|
||||||
}
|
}
|
||||||
|
|
||||||
decodedConfig, err := base64.StdEncoding.DecodeString(serverSelected.OpenVpnConfigData)
|
decodedConfig, err := base64.StdEncoding.DecodeString(serverSelected.OpenVpnConfigData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Msgf(err.Error())
|
log.Fatal().Msg(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpfile, err := ioutil.TempFile("", "vpngate-openvpn-config-")
|
tmpfile, err := os.CreateTemp("", "vpngate-openvpn-config-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Msgf(err.Error())
|
log.Fatal().Msg(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := tmpfile.Write(decodedConfig); err != nil {
|
if _, err := tmpfile.Write(decodedConfig); err != nil {
|
||||||
log.Fatal().Msgf(err.Error())
|
log.Fatal().Msg(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tmpfile.Close(); err != nil {
|
if err := tmpfile.Close(); err != nil {
|
||||||
log.Fatal().Msgf(err.Error())
|
log.Fatal().Msg(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,10 +114,14 @@ var connectCmd = &cobra.Command{
|
|||||||
err = vpn.Connect(tmpfile.Name())
|
err = vpn.Connect(tmpfile.Name())
|
||||||
|
|
||||||
if err != nil && !flagReconnect {
|
if err != nil && !flagReconnect {
|
||||||
log.Fatal().Msgf(err.Error())
|
log.Fatal().Msg(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
} else {
|
} else {
|
||||||
os.Remove(tmpfile.Name())
|
err = os.Remove(tmpfile.Name())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Msg(err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"github.com/davegallant/vpngate/pkg/vpn"
|
"github.com/davegallant/vpngate/pkg/vpn"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(listCmd)
|
rootCmd.AddCommand(listCmd)
|
||||||
|
listCmd.Flags().StringVarP(&flagProxy, "proxy", "p", "", "provide a http/https proxy server to make requests through (i.e. http://127.0.0.1:8080)")
|
||||||
|
listCmd.Flags().StringVarP(&flagSocks5Proxy, "socks5", "s", "", "provide a socks5 proxy server to make requests through (i.e. 127.0.0.1:1080)")
|
||||||
}
|
}
|
||||||
|
|
||||||
var listCmd = &cobra.Command{
|
var listCmd = &cobra.Command{
|
||||||
@@ -20,9 +23,10 @@ var listCmd = &cobra.Command{
|
|||||||
Short: "List all available vpn servers",
|
Short: "List all available vpn servers",
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
vpnServers, err := vpn.GetList()
|
|
||||||
|
vpnServers, err := vpn.GetList(flagProxy, flagSocks5Proxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Msgf(err.Error())
|
log.Fatal().Msg(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1743814133,
|
||||||
|
"narHash": "sha256-drDyYyUmjeYGiHmwB9eOPTQRjmrq3Yz26knwmMPLZFk=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "250b695f41e0e2f5afbf15c6b12480de1fe0001b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
46
flake.nix
Normal file
46
flake.nix
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
flake-utils,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
vpngate =
|
||||||
|
pkgs:
|
||||||
|
pkgs.buildGo123Module rec {
|
||||||
|
name = "vpngate";
|
||||||
|
src = ./.;
|
||||||
|
vendorHash = "sha256-CP2sFJdIde88WFJlAq29GlE7c1c0xJ6tHzrrasMzJo8=";
|
||||||
|
nativeBuildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.makeWrapper ];
|
||||||
|
env.CGO_ENABLED = 0;
|
||||||
|
doCheck = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
flakeForSystem =
|
||||||
|
nixpkgs: system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
vg = vpngate pkgs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = {
|
||||||
|
default = vg;
|
||||||
|
vpngate = vg;
|
||||||
|
};
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
gopls
|
||||||
|
gotools
|
||||||
|
go_1_25
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
flake-utils.lib.eachDefaultSystem (system: flakeForSystem nixpkgs system);
|
||||||
|
}
|
||||||
239
go.mod
239
go.mod
@@ -1,36 +1,233 @@
|
|||||||
module github.com/davegallant/vpngate
|
module github.com/davegallant/vpngate
|
||||||
|
|
||||||
go 1.19
|
go 1.24.0
|
||||||
|
|
||||||
|
toolchain go1.24.9
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.6
|
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||||
github.com/jszwec/csvutil v1.7.1
|
github.com/jszwec/csvutil v1.10.0
|
||||||
github.com/juju/errors v1.0.0
|
github.com/juju/errors v1.0.0
|
||||||
github.com/nxadm/tail v1.4.8
|
github.com/olekukonko/tablewriter v1.1.1
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/rs/zerolog v1.34.0
|
||||||
github.com/rs/zerolog v1.29.0
|
github.com/spf13/afero v1.14.0
|
||||||
github.com/spf13/afero v1.9.3
|
github.com/spf13/cobra v1.10.1
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/stretchr/testify v1.8.1
|
golang.org/x/net v0.46.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
|
||||||
|
4d63.com/gochecknoglobals v0.2.2 // indirect
|
||||||
|
codeberg.org/chavacava/garif v0.2.0 // indirect
|
||||||
|
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect
|
||||||
|
dev.gaijin.team/go/golib v0.6.0 // indirect
|
||||||
|
github.com/4meepo/tagalign v1.4.3 // indirect
|
||||||
|
github.com/Abirdcfly/dupword v0.1.7 // indirect
|
||||||
|
github.com/AdminBenni/iota-mixing v1.0.0 // indirect
|
||||||
|
github.com/AlwxSin/noinlineerr v1.0.5 // indirect
|
||||||
|
github.com/Antonboom/errname v1.1.1 // indirect
|
||||||
|
github.com/Antonboom/nilnil v1.1.1 // indirect
|
||||||
|
github.com/Antonboom/testifylint v1.6.4 // indirect
|
||||||
|
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||||
|
github.com/Djarvur/go-err113 v0.1.1 // indirect
|
||||||
|
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||||
|
github.com/MirrexOne/unqueryvet v1.2.1 // indirect
|
||||||
|
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
|
||||||
|
github.com/alecthomas/chroma/v2 v2.20.0 // indirect
|
||||||
|
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
|
||||||
|
github.com/alexkohler/nakedret/v2 v2.0.6 // indirect
|
||||||
|
github.com/alexkohler/prealloc v1.0.0 // indirect
|
||||||
|
github.com/alfatraining/structtag v1.0.0 // indirect
|
||||||
|
github.com/alingse/asasalint v0.0.11 // indirect
|
||||||
|
github.com/alingse/nilnesserr v0.2.0 // indirect
|
||||||
|
github.com/ashanbrown/forbidigo/v2 v2.3.0 // indirect
|
||||||
|
github.com/ashanbrown/makezero/v2 v2.1.0 // indirect
|
||||||
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/bkielbasa/cyclop v1.2.3 // indirect
|
||||||
|
github.com/blizzy78/varnamelen v0.8.0 // indirect
|
||||||
|
github.com/bombsimon/wsl/v4 v4.7.0 // indirect
|
||||||
|
github.com/bombsimon/wsl/v5 v5.3.0 // indirect
|
||||||
|
github.com/breml/bidichk v0.3.3 // indirect
|
||||||
|
github.com/breml/errchkjson v0.4.1 // indirect
|
||||||
|
github.com/butuzov/ireturn v0.4.0 // indirect
|
||||||
|
github.com/butuzov/mirror v1.3.0 // indirect
|
||||||
|
github.com/catenacyber/perfsprint v0.10.0 // indirect
|
||||||
|
github.com/ccojocar/zxcvbn-go v1.0.4 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/charithe/durationcheck v0.0.11 // indirect
|
||||||
|
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||||
|
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||||
|
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||||
|
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||||
|
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||||
|
github.com/ckaznocha/intrange v0.3.1 // indirect
|
||||||
|
github.com/clipperhouse/displaywidth v0.3.1 // indirect
|
||||||
|
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||||
|
github.com/curioswitch/go-reassign v0.3.0 // indirect
|
||||||
|
github.com/daixiang0/gci v0.13.7 // indirect
|
||||||
|
github.com/dave/dst v0.27.3 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/denis-tingaikin/go-header v0.5.0 // indirect
|
||||||
|
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||||
|
github.com/ettle/strcase v0.2.0 // indirect
|
||||||
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
|
github.com/fatih/structtag v1.2.0 // indirect
|
||||||
|
github.com/firefart/nonamedreturns v1.0.6 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
github.com/fzipp/gocyclo v0.6.0 // indirect
|
||||||
|
github.com/ghostiam/protogetter v0.3.17 // indirect
|
||||||
|
github.com/go-critic/go-critic v0.14.2 // indirect
|
||||||
|
github.com/go-toolsmith/astcast v1.1.0 // indirect
|
||||||
|
github.com/go-toolsmith/astcopy v1.1.0 // indirect
|
||||||
|
github.com/go-toolsmith/astequal v1.2.0 // indirect
|
||||||
|
github.com/go-toolsmith/astfmt v1.1.0 // indirect
|
||||||
|
github.com/go-toolsmith/astp v1.1.0 // indirect
|
||||||
|
github.com/go-toolsmith/strparse v1.1.0 // indirect
|
||||||
|
github.com/go-toolsmith/typep v1.1.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
|
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
|
||||||
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
|
github.com/godoc-lint/godoc-lint v0.10.1 // indirect
|
||||||
|
github.com/gofrs/flock v0.13.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/golangci/asciicheck v0.5.0 // indirect
|
||||||
|
github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect
|
||||||
|
github.com/golangci/go-printf-func-name v0.1.1 // indirect
|
||||||
|
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect
|
||||||
|
github.com/golangci/golangci-lint/v2 v2.6.2 // indirect
|
||||||
|
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 // indirect
|
||||||
|
github.com/golangci/misspell v0.7.0 // indirect
|
||||||
|
github.com/golangci/plugin-module-register v0.1.2 // indirect
|
||||||
|
github.com/golangci/revgrep v0.8.0 // indirect
|
||||||
|
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect
|
||||||
|
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect
|
||||||
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
|
github.com/gordonklaus/ineffassign v0.2.0 // indirect
|
||||||
|
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
|
||||||
|
github.com/gostaticanalysis/comment v1.5.0 // indirect
|
||||||
|
github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect
|
||||||
|
github.com/gostaticanalysis/nilerr v0.1.2 // indirect
|
||||||
|
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/hexops/gotextdiff v1.0.3 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/jgautheron/goconst v1.8.2 // indirect
|
||||||
|
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
|
||||||
|
github.com/jjti/go-spancheck v0.6.5 // indirect
|
||||||
|
github.com/julz/importas v0.2.0 // indirect
|
||||||
|
github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/kr/pretty v0.3.0 // indirect
|
github.com/kisielk/errcheck v1.9.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/kulti/thelper v0.7.1 // indirect
|
||||||
|
github.com/kunwardeep/paralleltest v1.0.15 // indirect
|
||||||
|
github.com/lasiar/canonicalheader v1.1.2 // indirect
|
||||||
|
github.com/ldez/exptostd v0.4.5 // indirect
|
||||||
|
github.com/ldez/gomoddirectives v0.7.1 // indirect
|
||||||
|
github.com/ldez/grignotin v0.10.1 // indirect
|
||||||
|
github.com/ldez/tagliatelle v0.7.2 // indirect
|
||||||
|
github.com/ldez/usetesting v0.5.0 // indirect
|
||||||
|
github.com/leonklingele/grouper v1.1.2 // indirect
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
|
github.com/macabu/inamedparam v0.2.0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.6 // indirect
|
||||||
|
github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect
|
||||||
|
github.com/manuelarte/funcorder v0.5.0 // indirect
|
||||||
|
github.com/maratori/testableexamples v1.0.1 // indirect
|
||||||
|
github.com/maratori/testpackage v1.1.2 // indirect
|
||||||
|
github.com/matoous/godox v1.1.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
|
github.com/mgechev/revive v1.12.0 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/moricho/tparallel v0.3.2 // indirect
|
||||||
|
github.com/muesli/termenv v0.16.0 // indirect
|
||||||
|
github.com/nakabonne/nestif v0.3.1 // indirect
|
||||||
|
github.com/nishanths/exhaustive v0.12.0 // indirect
|
||||||
|
github.com/nishanths/predeclared v0.2.2 // indirect
|
||||||
|
github.com/nunnatsa/ginkgolinter v0.21.2 // indirect
|
||||||
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||||
|
github.com/olekukonko/errors v1.1.0 // indirect
|
||||||
|
github.com/olekukonko/ll v0.1.2 // indirect
|
||||||
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.2 // indirect
|
github.com/polyfloyd/go-errorlint v1.8.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
golang.org/x/sys v0.4.0 // indirect
|
github.com/prometheus/common v0.32.1 // indirect
|
||||||
golang.org/x/term v0.4.0 // indirect
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
golang.org/x/text v0.6.0 // indirect
|
github.com/quasilyte/go-ruleguard v0.4.5 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
github.com/quasilyte/go-ruleguard/dsl v0.3.23 // indirect
|
||||||
|
github.com/quasilyte/gogrep v0.5.0 // indirect
|
||||||
|
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
|
||||||
|
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
|
||||||
|
github.com/raeperd/recvcheck v0.2.0 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
|
github.com/ryancurrah/gomodguard v1.4.1 // indirect
|
||||||
|
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
|
||||||
|
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||||
|
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
|
||||||
|
github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect
|
||||||
|
github.com/securego/gosec/v2 v2.22.10 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/sivchari/containedctx v1.0.3 // indirect
|
||||||
|
github.com/sonatard/noctx v0.4.0 // indirect
|
||||||
|
github.com/sourcegraph/go-diff v0.7.0 // indirect
|
||||||
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/spf13/viper v1.12.0 // indirect
|
||||||
|
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
|
||||||
|
github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
|
github.com/subosito/gotenv v1.4.1 // indirect
|
||||||
|
github.com/tetafro/godot v1.5.4 // indirect
|
||||||
|
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect
|
||||||
|
github.com/timonwong/loggercheck v0.11.0 // indirect
|
||||||
|
github.com/tomarrell/wrapcheck/v2 v2.11.0 // indirect
|
||||||
|
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
|
||||||
|
github.com/ultraware/funlen v0.2.0 // indirect
|
||||||
|
github.com/ultraware/whitespace v0.2.0 // indirect
|
||||||
|
github.com/uudashr/gocognit v1.2.0 // indirect
|
||||||
|
github.com/uudashr/iface v1.4.1 // indirect
|
||||||
|
github.com/xen0n/gosmopolitan v1.3.0 // indirect
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
|
github.com/yagipy/maintidx v1.0.0 // indirect
|
||||||
|
github.com/yeya24/promlinter v0.3.0 // indirect
|
||||||
|
github.com/ykadowak/zerologlint v0.1.5 // indirect
|
||||||
|
gitlab.com/bosi/decorder v0.4.2 // indirect
|
||||||
|
go-simpler.org/musttag v0.14.0 // indirect
|
||||||
|
go-simpler.org/sloglint v0.11.1 // indirect
|
||||||
|
go.augendre.info/arangolint v0.3.1 // indirect
|
||||||
|
go.augendre.info/fatcontext v0.9.0 // indirect
|
||||||
|
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||||
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
|
golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
|
golang.org/x/mod v0.29.0 // indirect
|
||||||
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
|
golang.org/x/sys v0.37.0 // indirect
|
||||||
|
golang.org/x/term v0.36.0 // indirect
|
||||||
|
golang.org/x/text v0.30.0 // indirect
|
||||||
|
golang.org/x/tools v0.38.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.8 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
honnef.co/go/tools v0.6.1 // indirect
|
||||||
|
mvdan.cc/gofumpt v0.9.2 // indirect
|
||||||
|
mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
package exec
|
package exec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bufio"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run executes a command in workDir and returns stdout and error.
|
// Run executes a command in workDir and returns stdout and error.
|
||||||
// The spawned process will exit upon termination of this application
|
// The spawned process will exit upon termination of this application
|
||||||
// to ensure a clean exit
|
// to ensure a clean exit
|
||||||
func Run(path string, workDir string, args ...string) (string, error) {
|
func Run(path string, workDir string, args ...string) error {
|
||||||
_, err := exec.LookPath(path)
|
_, err := exec.LookPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Msgf("%s is required, please install it", path)
|
log.Error().Msgf("%s is required, please install it", path)
|
||||||
@@ -21,25 +20,28 @@ func Run(path string, workDir string, args ...string) (string, error) {
|
|||||||
}
|
}
|
||||||
cmd := exec.Command(path, args...)
|
cmd := exec.Command(path, args...)
|
||||||
cmd.Dir = workDir
|
cmd.Dir = workDir
|
||||||
stdout := &bytes.Buffer{}
|
log.Debug().Msg("Executing " + strings.Join(cmd.Args, " "))
|
||||||
stderr := &bytes.Buffer{}
|
stdout, err := cmd.StdoutPipe()
|
||||||
cmd.Stdout = stdout
|
|
||||||
cmd.Stderr = stderr
|
|
||||||
log.Debug().Msgf("Executing " + strings.Join(cmd.Args, " "))
|
|
||||||
err = cmd.Run()
|
|
||||||
output := strings.TrimSpace(stdout.String())
|
|
||||||
errOut := strings.TrimSpace(stderr.String())
|
|
||||||
if output != "" {
|
|
||||||
log.Debug().Msgf(output)
|
|
||||||
}
|
|
||||||
if errOut != "" {
|
|
||||||
log.Debug().Msgf(errOut)
|
|
||||||
}
|
|
||||||
if _, ok := err.(*exec.ExitError); !ok {
|
|
||||||
return output, errors.Trace(err)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return output, errors.Annotatef(err, path, cmd.Args, errOut)
|
log.Fatal().Msgf("Failed to get stdout pipe: %v", err)
|
||||||
}
|
}
|
||||||
return output, nil
|
if err := cmd.Start(); err != nil {
|
||||||
|
log.Fatal().Msgf("Failed to start command: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(stdout)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
log.Debug().Msg(scanner.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
log.Fatal().Msgf("Error reading stdout: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
log.Fatal().Msgf("Command finished with error: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
18
pkg/util/retry.go
Normal file
18
pkg/util/retry.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Retry(attempts int, delay time.Duration,fn func() error) error {
|
||||||
|
var err error
|
||||||
|
for i := 0; i < attempts; i++ {
|
||||||
|
if err = fn(); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Error().Msgf("Retrying after %d seconds. An error occured: %s", delay, err)
|
||||||
|
time.Sleep(delay)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ package vpn
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
@@ -36,7 +36,7 @@ func getVpnListCache() (*[]Server, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
byteValue, err := ioutil.ReadAll(serversFile)
|
byteValue, err := io.ReadAll(serversFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ func writeVpnListToCache(servers []Server) error {
|
|||||||
|
|
||||||
cacheFile := path.Join(getCacheDir(), serverCachefile)
|
cacheFile := path.Join(getCacheDir(), serverCachefile)
|
||||||
|
|
||||||
err = ioutil.WriteFile(cacheFile, f, 0o644)
|
err = os.WriteFile(cacheFile, f, 0o644)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,28 @@
|
|||||||
package vpn
|
package vpn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/davegallant/vpngate/pkg/exec"
|
"github.com/davegallant/vpngate/pkg/exec"
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
"github.com/nxadm/tail"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Connect to a specified OpenVPN configuration
|
// Connect to a specified OpenVPN configuration
|
||||||
func Connect(configPath string) error {
|
func Connect(configPath string) error {
|
||||||
tmpLogFile, err := ioutil.TempFile("", "vpngate-openvpn-log-")
|
tmpLogFile, err := os.CreateTemp("", "vpngate-openvpn-log-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "Unable to create a temporary log file")
|
return errors.Annotate(err, "Unable to create a temporary log file")
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpLogFile.Name())
|
defer func() {
|
||||||
|
_ = os.Remove(tmpLogFile.Name())
|
||||||
go func() {
|
|
||||||
// Tail the temporary openvpn log file
|
|
||||||
t, err := tail.TailFile(tmpLogFile.Name(), tail.Config{Follow: true})
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Msgf("%s", err)
|
|
||||||
}
|
|
||||||
for line := range t.Lines {
|
|
||||||
log.Debug().Msg(line.Text)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
_, err = exec.Run("openvpn", ".", "--verb", "4", "--log", tmpLogFile.Name(), "--config", configPath)
|
executable := "openvpn"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
executable = "C:\\Program Files\\OpenVPN\\bin\\openvpn.exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
err = exec.Run(executable, ".", "--verb", "4", "--config", configPath, "--data-ciphers", "AES-128-CBC")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,14 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/jszwec/csvutil"
|
"github.com/jszwec/csvutil"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
|
|
||||||
|
"github.com/davegallant/vpngate/pkg/util"
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,6 +48,7 @@ func parseVpnList(r io.Reader) (*[]Server, error) {
|
|||||||
// Trim known invalid rows
|
// Trim known invalid rows
|
||||||
serverList = bytes.TrimPrefix(serverList, []byte("*vpn_servers\r\n"))
|
serverList = bytes.TrimPrefix(serverList, []byte("*vpn_servers\r\n"))
|
||||||
serverList = bytes.TrimSuffix(serverList, []byte("*\r\n"))
|
serverList = bytes.TrimSuffix(serverList, []byte("*\r\n"))
|
||||||
|
serverList = bytes.ReplaceAll(serverList, []byte(`"`), []byte{})
|
||||||
|
|
||||||
if err := csvutil.Unmarshal(serverList, &servers); err != nil {
|
if err := csvutil.Unmarshal(serverList, &servers); err != nil {
|
||||||
return nil, errors.Annotatef(err, "Unable to parse CSV")
|
return nil, errors.Annotatef(err, "Unable to parse CSV")
|
||||||
@@ -53,10 +58,11 @@ func parseVpnList(r io.Reader) (*[]Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetList returns a list of vpn servers
|
// GetList returns a list of vpn servers
|
||||||
func GetList() (*[]Server, error) {
|
func GetList(httpProxy string, socks5Proxy string) (*[]Server, error) {
|
||||||
cacheExpired := vpnListCacheIsExpired()
|
cacheExpired := vpnListCacheIsExpired()
|
||||||
|
|
||||||
var servers *[]Server
|
var servers *[]Server
|
||||||
|
var client *http.Client
|
||||||
|
|
||||||
if !cacheExpired {
|
if !cacheExpired {
|
||||||
servers, err := getVpnListCache()
|
servers, err := getVpnListCache()
|
||||||
@@ -73,27 +79,70 @@ func GetList() (*[]Server, error) {
|
|||||||
|
|
||||||
log.Info().Msg("Fetching the latest server list")
|
log.Info().Msg("Fetching the latest server list")
|
||||||
|
|
||||||
r, err := http.Get(vpnList)
|
if httpProxy != "" {
|
||||||
if err != nil {
|
proxyURL, err := url.Parse(httpProxy)
|
||||||
return nil, errors.Annotate(err, "Unable to retrieve vpn list")
|
if err != nil {
|
||||||
|
log.Error().Msgf("Error parsing proxy: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
transport := &http.Transport{
|
||||||
|
Proxy: http.ProxyURL(proxyURL),
|
||||||
|
}
|
||||||
|
|
||||||
|
client = &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if socks5Proxy != "" {
|
||||||
|
dialer, err := proxy.SOCKS5("tcp", socks5Proxy, nil, proxy.Direct)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msgf("Error creating SOCKS5 dialer: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpTransport := &http.Transport{
|
||||||
|
Dial: dialer.Dial,
|
||||||
|
}
|
||||||
|
|
||||||
|
client = &http.Client{
|
||||||
|
Transport: httpTransport,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client = &http.Client{}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer r.Body.Close()
|
var r *http.Response
|
||||||
|
|
||||||
if r.StatusCode != 200 {
|
err := util.Retry(5, 1, func() error {
|
||||||
return nil, errors.Annotatef(err, "Unexpected status code when retrieving vpn list: %d", r.StatusCode)
|
var err error
|
||||||
}
|
r, err = client.Get(vpnList)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = r.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
servers, err = parseVpnList(r.Body)
|
if r.StatusCode != 200 {
|
||||||
|
return errors.Annotatef(err, "Unexpected status code when retrieving vpn list: %d", r.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err = parseVpnList(r.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeVpnListToCache(*servers)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Msgf("Unable to write servers to cache: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Annotate(err, "unable to parse vpn list")
|
return nil, err
|
||||||
}
|
|
||||||
|
|
||||||
err = writeVpnListToCache(*servers)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Warn().Msgf("Unable to write servers to cache: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return servers, nil
|
return servers, nil
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// TestGetListReal tests getting the real list of vpn servers
|
// TestGetListReal tests getting the real list of vpn servers
|
||||||
func TestGetListReal(t *testing.T) {
|
func TestGetListReal(t *testing.T) {
|
||||||
_, err := GetList()
|
_, err := GetList("", "")
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
9
renovate.json
Normal file
9
renovate.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"schedule": [
|
||||||
|
"every weekend"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"config:recommended"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user