diff --git a/cmd/cli.go b/cmd/cli.go index 2dc7396..f2b3599 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -1,11 +1,15 @@ package purevpnwg +import "github.com/Rikpat/purevpnwg/pkg/util" + type Context struct { - Debug bool + Debug bool + Config *util.Config } var CLI struct { - Debug bool `help:"Enable debug mode."` + Debug bool `help:"Enable debug mode."` + Config *util.Config `embed:"" envprefix:"PUREVPN_"` Login LoginCmd `cmd:"" help:"Login and store cookies."` Update UpdateCmd `cmd:"" help:"Updates wireguard config file."` diff --git a/cmd/full.go b/cmd/full.go index eb57cfe..8fc531c 100644 --- a/cmd/full.go +++ b/cmd/full.go @@ -9,11 +9,10 @@ import ( ) type FullCmd struct { - Config *util.Config `embed:"" envprefix:"PUREVPN_"` } func (r *FullCmd) Run(ctx *Context) error { - page, cookies := purevpn.Login(r.Config.Username, r.Config.Password) + page, cookies := purevpn.Login(ctx.Config.Username, ctx.Config.Password) defer page.MustClose() if ctx.Debug { @@ -34,33 +33,34 @@ func (r *FullCmd) Run(ctx *Context) error { fmt.Println("Successfully parsed user data") } - if r.Config.Subscription.Username == "" { - if r.Config.Subscription, err = userData.SelectSubscription(); err != nil { + if ctx.Config.Subscription.Username == "" { + if ctx.Config.Subscription, err = userData.SelectSubscription(); err != nil { return err } } if ctx.Debug { - fmt.Printf("Selected subscription %v\n", r.Config.Subscription.Username) + fmt.Printf("Selected subscription %v\n", ctx.Config.Subscription.Username) } - if err := r.Config.Subscription.GetEncryptPassword(page, token[0].Value); err != nil { + if err := ctx.Config.Subscription.GetEncryptPassword(page, token[0].Value); err != nil { return err } if ctx.Debug { fmt.Println("Successfully got subscription password") + fmt.Printf("ctx.Config: %v\n", ctx.Config) } - server, err := purevpn.GetWireguardServer(page, r.Config, token[0].Value) + server, err := purevpn.GetWireguardServer(page, ctx.Config, token[0].Value) if err == nil { if ctx.Debug { fmt.Printf("Got wireguard server: %v\n", server) } - err = wireguard.UpdateConfig([]byte(server), r.Config) + err = wireguard.UpdateConfig([]byte(server), ctx.Config) if ctx.Debug && err == nil { - fmt.Printf("Created wireguard file at %v\n", r.Config.WireguardFile) + fmt.Printf("Created wireguard file at %v\n", ctx.Config.WireguardFile) } } return err diff --git a/cmd/login.go b/cmd/login.go index 989c4d6..2e91c33 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -8,12 +8,10 @@ import ( ) type LoginCmd struct { - Username string `flag:"" help:"PureVPN username (email)."` - Password string `flag:"" help:"PureVPN password."` } func (r *LoginCmd) Run(ctx *Context) error { - page, cookies := purevpn.Login(r.Username, r.Password) + page, cookies := purevpn.Login(ctx.Config.Username, ctx.Config.Password) defer page.MustClose() token := util.FilterCookies(cookies, "fa_token") diff --git a/cmd/update.go b/cmd/update.go index 1e2acb4..92b4d26 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -2,7 +2,6 @@ package purevpnwg import ( "github.com/Rikpat/purevpnwg/pkg/purevpn" - "github.com/Rikpat/purevpnwg/pkg/util" "github.com/Rikpat/purevpnwg/pkg/wireguard" ) @@ -19,19 +18,15 @@ func (r *UpdateCmd) Run(ctx *Context) error { } defer page.MustClose() - config, err := util.ReadConfig() - if err != nil { - return err - } - token, err := purevpn.GetToken(page, config.UUID) + token, err := purevpn.GetToken(page, ctx.Config.UUID) if err != nil { return err } - server, err := purevpn.GetWireguardServer(page, config, token) + server, err := purevpn.GetWireguardServer(page, ctx.Config, token) if err == nil { - err = wireguard.UpdateConfig([]byte(server), config) + err = wireguard.UpdateConfig([]byte(server), ctx.Config) } return err } diff --git a/go.mod b/go.mod index 219bd59..79aecd0 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,21 @@ module github.com/Rikpat/purevpnwg -go 1.22.1 +go 1.22.2 require ( github.com/alecthomas/kong v0.8.1 + github.com/alecthomas/kong-yaml v0.2.0 github.com/go-rod/rod v0.114.4 + github.com/go-rod/stealth v0.4.9 github.com/manifoldco/promptui v0.9.0 + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 + gopkg.in/ini.v1 v1.67.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/alecthomas/kong-yaml v0.2.0 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect - github.com/go-rod/stealth v0.4.9 // indirect - github.com/golang-jwt/jwt/v5 v5.0.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/kr/text v0.2.0 // indirect github.com/ysmood/fetchup v0.2.3 // indirect github.com/ysmood/goob v0.4.0 // indirect github.com/ysmood/got v0.34.1 // indirect @@ -22,7 +23,4 @@ require ( github.com/ysmood/leakless v0.8.0 // indirect golang.org/x/crypto v0.8.0 // indirect golang.org/x/sys v0.12.0 // indirect - golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fd3e1a6..01b72b2 100644 --- a/go.sum +++ b/go.sum @@ -12,25 +12,28 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-rod/rod v0.113.0/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw= github.com/go-rod/rod v0.114.4 h1:FpkNFukjCuZLwnoLs+S9aCL95o/EMec6M+41UmvQay8= github.com/go-rod/rod v0.114.4/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw= github.com/go-rod/stealth v0.4.9 h1:X2PmQk4DUF2wzw6GOsWjW/glb8K5ebnftbEvLh7MlZ4= github.com/go-rod/stealth v0.4.9/go.mod h1:eAzyvw8c0iAd5nJJsSWeh0fQ5z94vCIfdi1hUmYDimc= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= @@ -53,6 +56,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go index 030c821..bf563d8 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,6 @@ import ( func main() { ctx := kong.Parse(&purevpnwg.CLI, kong.Configuration(kongyaml.Loader, "/etc/purevpnwg/config.yml", "~/.purevpnwg.yml", util.CONFIG_FILE)) - // Call the Run() method of the selected parsed command. - err := ctx.Run(&purevpnwg.Context{Debug: purevpnwg.CLI.Debug}) + err := ctx.Run(&purevpnwg.Context{Debug: purevpnwg.CLI.Debug, Config: purevpnwg.CLI.Config}) ctx.FatalIfErrorf(err) } diff --git a/pkg/purevpn/browser.go b/pkg/purevpn/browser.go index eefec9d..873f3db 100644 --- a/pkg/purevpn/browser.go +++ b/pkg/purevpn/browser.go @@ -117,7 +117,7 @@ func GetWireguardServer(page *rod.Page, config *util.Config, token string) (stri } const json = await res.json() if (!json.status) { - throw Error(json) + throw Error(JSON.stringify(json)) } return json.body[0].wireguard_configuration.replace("{clientPrivateKey}", privateKey) }`, params.Encode(), "Bearer "+token, privateKey) diff --git a/pkg/util/config.go b/pkg/util/config.go index 8d930de..39e4f56 100644 --- a/pkg/util/config.go +++ b/pkg/util/config.go @@ -14,19 +14,20 @@ type SubscriptionAuth struct { Password string `flag:"" yaml:"password" env:"PASSWORD" help:"PureVPN subscription password (not necessary)"` } -type Server struct { +type ServerConfig struct { Country string `flag:"" yaml:"country" env:"COUNTRY" help:"PureVPN server country (example: DE)."` City string `flag:"" yaml:"city" env:"CITY" help:"PureVPN server city (example: 2762)."` } type Config struct { - Username string `flag:"" yaml:"username" env:"USERNAME" help:"PureVPN username (email)."` - Password string `flag:"" yaml:"password" env:"PASSWORD" help:"PureVPN password."` - UUID string `yaml:"uuid" hidden:""` - Server *Server `yaml:"server" embed:"" prefix:"server." envprefix:"SERVER_"` + Username string `flag:"" yaml:"username" required:"" env:"USERNAME" help:"PureVPN username (email)."` + Password string `flag:"" yaml:"password" required:"" env:"PASSWORD" help:"PureVPN password."` + UUID string `yaml:"uuid" kong:"-"` + Server *ServerConfig `yaml:"server" embed:"" prefix:"server." envprefix:"SERVER_"` Subscription *SubscriptionAuth `embed:"" prefix:"subscription." envprefix:"SUBSCRIPTION_"` Device string `yaml:"device" env:"DEVICE" default:"linux"` WireguardFile string `flag:"" yaml:"wireguardFile" env:"WIREGUARD_FILE" default:"wg0.conf"` + ResolveIP bool `flag:"" yaml:"resolveIP" env:"RESOLVE_IP" default:"false" help:"Resolves hostname to IP (for use with gluetun)"` } func (sub *SubscriptionAuth) GetEncryptPassword(page *rod.Page, token string) error { diff --git a/pkg/wireguard/config.go b/pkg/wireguard/config.go index f7ec454..6286425 100644 --- a/pkg/wireguard/config.go +++ b/pkg/wireguard/config.go @@ -2,7 +2,9 @@ package wireguard import ( "errors" + "net" "os" + "strings" "github.com/Rikpat/purevpnwg/pkg/util" "gopkg.in/ini.v1" @@ -21,10 +23,28 @@ type Peer struct { PublicKey, AllowedIPs, Endpoint, PersistentKeepalive string } +func resolveIpFromHostname(conf *WireguardConfig) { + // [0]: endpoint [1]: port + endpointSlice := strings.Split(conf.Peer.Endpoint, ":") + ips, _ := net.LookupIP(endpointSlice[0]) + conf.Peer.Endpoint = ips[0].To4().String() + ":" + endpointSlice[1] +} + func UpdateConfig(newConfig []byte, config *util.Config) error { + wgConf := new(WireguardConfig) + + err := ini.MapTo(wgConf, newConfig) + if err != nil { + return err + } + + if config.ResolveIP { + resolveIpFromHostname(wgConf) + } + if _, err := os.Stat(config.WireguardFile); errors.Is(err, os.ErrNotExist) { - wgConfFile, err := ini.Load(newConfig) - if err != nil { + wgConfFile := ini.Empty() + if err := wgConfFile.ReflectFrom(wgConf); err != nil { return err } return wgConfFile.SaveTo(config.WireguardFile) @@ -32,13 +52,6 @@ func UpdateConfig(newConfig []byte, config *util.Config) error { return err } - wgConf := new(WireguardConfig) - - err := ini.MapTo(wgConf, newConfig) - if err != nil { - return err - } - wgConfFile, err := ini.Load(config.WireguardFile) if err != nil { return err