Skip to content

Commit

Permalink
vpn refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-hajek authored and jansaidl committed Mar 31, 2024
1 parent a4e803b commit 39123a1
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 58 deletions.
2 changes: 1 addition & 1 deletion src/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func rootCmd() *cmdBuilder.Cmd {
}
}

if isVpnUp(ctx) {
if isVpnUp(ctx, cmdData.UxBlocks, 1) {
body.AddStringsRow(i18n.T(i18n.StatusInfoVpnStatus), i18n.T(i18n.VpnCheckingConnectionIsActive))
} else {
body.AddStringsRow(i18n.T(i18n.StatusInfoVpnStatus), i18n.T(i18n.VpnCheckingConnectionIsNotActive))
Expand Down
10 changes: 4 additions & 6 deletions src/cmd/vpnDown.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package cmd
import (
"context"
"os"
"os/exec"

"github.com/pkg/errors"

"github.com/zeropsio/zcli/src/cmdBuilder"
"github.com/zeropsio/zcli/src/cmdRunner"
Expand All @@ -14,6 +11,7 @@ import (
"github.com/zeropsio/zcli/src/i18n"
"github.com/zeropsio/zcli/src/uxBlock"
"github.com/zeropsio/zcli/src/uxBlock/styles"
"github.com/zeropsio/zcli/src/wg"
)

func vpnDownCmd() *cmdBuilder.Cmd {
Expand All @@ -27,9 +25,9 @@ func vpnDownCmd() *cmdBuilder.Cmd {
}

func disconnectVpn(ctx context.Context, uxBlocks uxBlock.UxBlocks) error {
_, err := exec.LookPath("wg-quick")
err := wg.CheckWgInstallation()
if err != nil {
return errors.New(i18n.T(i18n.VpnWgQuickIsNotInstalled))
return err
}

filePath, fileMode, err := constants.WgConfigFilePath()
Expand All @@ -44,7 +42,7 @@ func disconnectVpn(ctx context.Context, uxBlocks uxBlock.UxBlocks) error {
}
defer f.Close()

c := exec.CommandContext(ctx, "wg-quick", "down", filePath)
c := wg.DownCmd(ctx, filePath)
_, err = cmdRunner.Run(c)
if err != nil {
return err
Expand Down
79 changes: 32 additions & 47 deletions src/cmd/vpnUp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ package cmd
import (
"context"
"os"
"os/exec"
"text/template"
"time"

"github.com/pkg/errors"
"github.com/zeropsio/zcli/src/uxBlock"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"

"github.com/zeropsio/zcli/src/cliStorage"
Expand All @@ -21,6 +20,7 @@ import (
"github.com/zeropsio/zcli/src/nettools"
"github.com/zeropsio/zcli/src/uxBlock/styles"
"github.com/zeropsio/zcli/src/uxHelpers"
"github.com/zeropsio/zcli/src/wg"
"github.com/zeropsio/zerops-go/dto/input/body"
"github.com/zeropsio/zerops-go/dto/input/path"
"github.com/zeropsio/zerops-go/types"
Expand All @@ -40,7 +40,7 @@ func vpnUpCmd() *cmdBuilder.Cmd {
LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error {
uxBlocks := cmdData.UxBlocks

if isVpnUp(ctx) {
if isVpnUp(ctx, uxBlocks, 1) {
if cmdData.Params.GetBool("auto-disconnect") {
err := disconnectVpn(ctx, uxBlocks)
if err != nil {
Expand Down Expand Up @@ -92,28 +92,12 @@ func vpnUpCmd() *cmdBuilder.Cmd {
return err
}

f, err := file.Open(filePath, os.O_RDWR|os.O_CREATE, fileMode)
f, err := file.Open(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileMode)
if err != nil {
return err
}
err = func() error {
defer f.Close()

templ := template.Must(template.New("wg template").Parse(vpnTmpl))

return templ.Execute(f, map[string]interface{}{
"PrivateKey": privateKey.String(),
"PublicKey": vpnSettings.Project.PublicKey,
"AssignedIpv4Address": vpnSettings.Peer.Ipv4.AssignedIpAddress,
"AssignedIpv6Address": vpnSettings.Peer.Ipv6.AssignedIpAddress,
"Ipv4NetworkGateway": vpnSettings.Project.Ipv4.Network.Gateway,
"ProjectIpv4Network": vpnSettings.Project.Ipv4.Network.Network,
"ProjectIpv6Network": vpnSettings.Project.Ipv6.Network.Network,
"Ipv4Network": vpnSettings.Peer.Ipv4.Network.Network,
"Ipv6Network": vpnSettings.Peer.Ipv6.Network.Network,
"ProjectIpv4SharedEndpoint": vpnSettings.Project.Ipv4.SharedEndpoint,
})
}()

err = wg.GenerateConfig(f, privateKey, vpnSettings)
if err != nil {
return err
}
Expand All @@ -136,18 +120,19 @@ func vpnUpCmd() *cmdBuilder.Cmd {
return err
}

_, err = exec.LookPath("wg-quick")
err = wg.CheckWgInstallation()
if err != nil {
return errors.New(i18n.T(i18n.VpnWgQuickIsNotInstalled))
return err
}

c := exec.CommandContext(ctx, "wg-quick", "up", filePath)
c := wg.UpCmd(ctx, filePath)
_, err = cmdRunner.Run(c)
if err != nil {
return err
}

if isVpnUp(ctx) {
// wait for the vpn to be up
if isVpnUp(ctx, uxBlocks, 6) {
uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.VpnUp)))
} else {
uxBlocks.PrintWarning(styles.WarningLine(i18n.T(i18n.VpnPingFailed)))
Expand All @@ -157,11 +142,28 @@ func vpnUpCmd() *cmdBuilder.Cmd {
})
}

func isVpnUp(ctx context.Context) bool {
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
func isVpnUp(ctx context.Context, uxBlocks uxBlock.UxBlocks, attempts int) bool {
p := []uxHelpers.Process{
{
F: func(ctx context.Context) error {
for i := 0; i < attempts; i++ {
err := nettools.Ping(ctx, vpnCheckAddress)
if err == nil {
return nil
}

time.Sleep(time.Millisecond * 500)
}
return errors.New(i18n.T(i18n.VpnPingFailed))
},
RunningMessage: i18n.T(i18n.VpnCheckingConnection),
ErrorMessageMessage: "",
SuccessMessage: "",
},
}

err := uxHelpers.ProcessCheckWithSpinner(ctx, uxBlocks, p)

err := nettools.Ping(ctx, vpnCheckAddress)
return err == nil
}

Expand All @@ -186,20 +188,3 @@ func getOrCreatePrivateVpnKey(cmdData *cmdBuilder.LoggedUserCmdData) (wgtypes.Ke

return vpnKey, nil
}

var vpnTmpl = `
[Interface]
PrivateKey = {{.PrivateKey}}
Address = {{if .AssignedIpv4Address}}{{.AssignedIpv4Address}}/32{{end}}, {{.AssignedIpv6Address}}/128
DNS = {{.Ipv4NetworkGateway}}, zerops
[Peer]
PublicKey = {{.PublicKey}}
AllowedIPs = {{if .ProjectIpv4Network}}{{.ProjectIpv4Network}},{{end}} {{.ProjectIpv6Network}}, {{if .Ipv4Network}}{{.Ipv4Network}}, {{end}}{{.Ipv6Network}}
Endpoint = {{.ProjectIpv4SharedEndpoint}}
PersistentKeepalive = 5
`
4 changes: 3 additions & 1 deletion src/i18n/en.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ var en = map[string]string{
VpnPrivateKeyCreated: "VPN private key created",
VpnDisconnectionPrompt: "VPN is active, do you want to disconnect?",
VpnDisconnectionPromptNo: "VPN is active, you can disconnect using the 'zcli vpn down' command",
VpnCheckingConnection: "Checking VPN connection",
VpnPingFailed: fmt.Sprintf("Wireguard adapter was created, but we are not able to establish a connection,"+
"this could indicate a problem on our side. Please contact our support team %s.", CustomerSupportLink),

Expand All @@ -180,7 +181,8 @@ var en = map[string]string{
VpnDown: "VPN disconnected",

// vpn shared
VpnWgQuickIsNotInstalled: "wg-quick is not installed, please install it and try again",
VpnWgQuickIsNotInstalled: "wg-quick is not installed, please visit https://www.wireguard.com/install/",
VpnWgQuickIsNotInstalledWindows: "wireguard is not installed, please visit https://www.wireguard.com/install/",

// flags description
RegionFlag: "Choose one of Zerops regions. Use the \"zcli region list\" command to list all Zerops regions.",
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/i18n.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ const (
VpnPrivateKeyCreated = "VpnPrivateKeyCreated"
VpnDisconnectionPrompt = "VpnDisconnectionPrompt"
VpnDisconnectionPromptNo = "VpnDisconnectionPromptNo"
VpnCheckingConnection = "VpnCheckingConnection"
VpnPingFailed = "VpnPingFailed"

// vpn down
Expand All @@ -174,7 +175,8 @@ const (
VpnDown = "VpnDown"

// vpn shared
VpnWgQuickIsNotInstalled = "VpnWgQuickIsNotInstalled"
VpnWgQuickIsNotInstalled = "VpnWgQuickIsNotInstalled"
VpnWgQuickIsNotInstalledWindows = "VpnWgQuickIsNotInstalledWindows"

// flags description
RegionFlag = "RegionFlag"
Expand Down
5 changes: 4 additions & 1 deletion src/uxBlock/spinner.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ func (m *spinnerModel) View() string {
if m.canceled {
s += "canceled\n"
} else {
s += spinner.view() + spinner.line.String() + "\n"
line := spinner.line.String()
if line != "" {
s += spinner.view() + spinner.line.String() + "\n"
}
}
}

Expand Down
10 changes: 9 additions & 1 deletion src/uxHelpers/spinner.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,20 @@ func ProcessCheckWithSpinner(
defer wg.Done()
err := process.F(ctx)
if err != nil {
spinner.Finish(styles.ErrorLine(process.ErrorMessageMessage))
if process.ErrorMessageMessage == "" {
spinner.Finish(styles.NewLine())
} else {
spinner.Finish(styles.ErrorLine(process.ErrorMessageMessage))
}
once.Do(func() {
returnErr = err
})
return
}
if process.SuccessMessage == "" {
spinner.Finish(styles.NewLine())
return
}
spinner.Finish(styles.SuccessLine(process.SuccessMessage))
}(processList[i], spinners[i])
}
Expand Down
60 changes: 60 additions & 0 deletions src/wg/darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//go:build darwin
// +build darwin

package wg

import (
"context"
"io"
"os/exec"
"text/template"

"github.com/pkg/errors"
"github.com/zeropsio/zcli/src/i18n"
"github.com/zeropsio/zerops-go/dto/output"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)

func CheckWgInstallation() error {
_, err := exec.LookPath("wg-quick")
if err != nil {
return errors.New(i18n.T(i18n.VpnWgQuickIsNotInstalled))
}

return nil
}

func GenerateConfig(f io.Writer, privateKey wgtypes.Key, vpnSettings output.ProjectVpnItem) error {
data, err := defaultTemplateData(privateKey, vpnSettings)
if err != nil {
return err
}

return template.Must(template.New("wg template").Parse(vpnTmpl)).Execute(f, data)
}

func UpCmd(ctx context.Context, filePath string) (err *exec.Cmd) {
return exec.CommandContext(ctx, "wg-quick", "up", filePath)
}

func DownCmd(ctx context.Context, filePath string) (err *exec.Cmd) {
return exec.CommandContext(ctx, "wg-quick", "down", filePath)
}

var vpnTmpl = `
[Interface]
PrivateKey = {{.PrivateKey}}
Address = {{if .AssignedIpv4Address}}{{.AssignedIpv4Address}}/32{{end}}, {{.AssignedIpv6Address}}/128
PostUp = echo "nameserver {{.Ipv4NetworkGateway}}" > /etc/resolver/zerops
PostDown = rm /etc/resolver/zerops
[Peer]
PublicKey = {{.PublicKey}}
AllowedIPs = {{if .ProjectIpv4Network}}{{.ProjectIpv4Network}},{{end}} {{.ProjectIpv6Network}}, {{if .Ipv4Network}}{{.Ipv4Network}}, {{end}}{{.Ipv6Network}}
Endpoint = {{.ProjectIpv4SharedEndpoint}}
PersistentKeepalive = 5
`
60 changes: 60 additions & 0 deletions src/wg/linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//go:build linux
// +build linux

package wg

import (
"context"
"io"
"os/exec"
"text/template"

"github.com/pkg/errors"
"github.com/zeropsio/zcli/src/i18n"
"github.com/zeropsio/zerops-go/dto/output"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)

func CheckWgInstallation() error {
_, err := exec.LookPath("wg-quick")
if err != nil {
return errors.New(i18n.T(i18n.VpnWgQuickIsNotInstalled))
}

return nil
}

func GenerateConfig(f io.Writer, privateKey wgtypes.Key, vpnSettings output.ProjectVpnItem) error {
data, err := defaultTemplateData(privateKey, vpnSettings)
if err != nil {
return err
}

return template.Must(template.New("wg template").Parse(vpnTmpl)).Execute(f, data)
}

func UpCmd(ctx context.Context, filePath string) (err *exec.Cmd) {
return exec.CommandContext(ctx, "wg-quick", "up", filePath)
}

func DownCmd(ctx context.Context, filePath string) (err *exec.Cmd) {
return exec.CommandContext(ctx, "wg-quick", "down", filePath)
}

var vpnTmpl = `
[Interface]
PrivateKey = {{.PrivateKey}}
Address = {{if .AssignedIpv4Address}}{{.AssignedIpv4Address}}/32{{end}}, {{.AssignedIpv6Address}}/128
PostUp = resolvectl dns %i {{.Ipv4NetworkGateway}}
PostUp = resolvectl domain %i zerops
[Peer]
PublicKey = {{.PublicKey}}
AllowedIPs = {{if .ProjectIpv4Network}}{{.ProjectIpv4Network}},{{end}} {{.ProjectIpv6Network}}, {{if .Ipv4Network}}{{.Ipv4Network}}, {{end}}{{.Ipv6Network}}
Endpoint = {{.ProjectIpv4SharedEndpoint}}
PersistentKeepalive = 5
`
Loading

0 comments on commit 39123a1

Please sign in to comment.