Skip to content

Commit

Permalink
config parser
Browse files Browse the repository at this point in the history
  • Loading branch information
honsiorovskyi committed May 10, 2022
1 parent 3416e55 commit 5badc67
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 27 deletions.
106 changes: 95 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,26 @@ import (
"awsvpn/awsvpn"
"awsvpn/openvpn"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"regexp"
"runtime"
"strconv"
)

const (
defaultPort = 443
defaultProto = "udp"
)

var (
confRemoteRegexp = regexp.MustCompile(`(?m)^remote\s+(\S+)`)
confPortRegexp = regexp.MustCompile(`(?m)(^remote\s+\S+\s+|port\s+)([0-9]+)`)
confProtoRegexp = regexp.MustCompile(`(?m)(^remote\s+\S+\s+[0-9]+\s+|proto\s+)(tcp[46]?|udp[46]?)`)
caSectionRegexp = regexp.MustCompile(`(?s)<ca>(.*)</ca>`)
)

func defaultOpenVPNConfig() string {
Expand All @@ -18,25 +35,92 @@ func defaultOpenVPNConfig() string {
return path.Join(home, ".awsvpn.ovpn")
}

func defaultBrowser() string {
switch runtime.GOOS {
case "windows": // no idea what else to use here :)
return "explorer.exe"
case "darwin": // better override it with "open -g -a YOUR_BROWSER_NAME"
return "open"
default: // let's try good old xdg
return "xdg-open"
}
}

func defaultOpenVPNClient() string {
switch runtime.GOOS {
case "windows": // no idea how this thing works :)
return "C:\\Program Files\\OpenVPN\\OpenVPN.exe"
case "darwin": // using native AWS client
return "/Applications/AWS VPN Client/AWS VPN Client.app/Contents/Resources/openvpn/acvc-openvpn"
default: // assuming it's a *nix OS and we have a customly build OpenVPN client
return "/opt/aws-openvpn/bin/openvpn"
}
}

var (
openVPNCommand = flag.String("openvpn", "/opt/openvpn/sbin/openvpn", "Absolute path to the OpenVPN binary")
openVPNConfig = flag.String("openvpn-config", defaultOpenVPNConfig(), "[TEMP] Absolute path to the OpenVPN configuration file")
openVPNRemote = flag.String("openvpn-remote", "cvpn-endpoint-YOUR-ID.prod.clientvpn.eu-west-1.amazonaws.com", "[TEMP] AWS OpenVPN server endpoint")
browserCmd = flag.String("browser", "open", "Command used to open the authorization URL")
configPath = flag.String("config", defaultOpenVPNConfig(), "Absolute path to the config file (OpenVPN-compatible)")
browserCmd = flag.String("browser", defaultBrowser(), "Command used to open the authorization URL")
openVPNCommand = flag.String("openvpn", defaultOpenVPNClient(), "Absolute path to the OpenVPN binary")
)

func parseOpenVPNConfig(cmd string, cfgPath string) (*openvpn.Config, error) {
f, err := os.Open(cfgPath)
if err != nil {
return nil, fmt.Errorf("config: %w", err)
}
defer f.Close()

b, err := ioutil.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("config: %w", err)
}

// ca
ca := caSectionRegexp.FindSubmatch(b)
if ca == nil {
return nil, fmt.Errorf("config: <ca> section not found in the config")
}

// remote
remote := confRemoteRegexp.FindSubmatch(b)
if remote == nil {
return nil, fmt.Errorf("config: remote not found in the config")
}

cfg := &openvpn.Config{
Command: cmd,
CACertificate: string(ca[1]),
Remote: string(remote[1]),
Port: defaultPort,
Proto: defaultProto,
}

// port
if p := confPortRegexp.FindSubmatch(b); p != nil {
port, err := strconv.Atoi(string(p[2]))
if err != nil {
return nil, fmt.Errorf("config: invalid port: %s", string(p[2]))
}
cfg.Port = port
}

// proto
if p := confProtoRegexp.FindSubmatch(b); p != nil {
cfg.Proto = string(p[2])
}

return cfg, nil
}

func main() {
flag.Parse()

cfg := openvpn.Config{
Command: *openVPNCommand,
ConfigFile: *openVPNConfig,
Proto: "udp",
Remote: *openVPNRemote,
Port: "443",
cfg, err := parseOpenVPNConfig(*openVPNCommand, *configPath)
if err != nil {
log.Fatal(err)
}

app, err := awsvpn.NewApp(cfg, *browserCmd)
app, err := awsvpn.NewApp(*cfg, *browserCmd)
if err != nil {
log.Fatalln(err)
}
Expand Down
33 changes: 27 additions & 6 deletions openvpn/config.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
package openvpn

import "time"
import (
"fmt"
"time"
)

type Config struct {
Command string
ConfigFile string
Proto string
Remote string
Port string
Command string
CACertificate string
Proto string
Remote string
Port int

TerminationTimeout time.Duration
}

func (c Config) pipe() (*pipe, error) {
return newTextSourcePipe(fmt.Sprintf(`
client
dev tun
proto tcp
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
<ca>
%s
</ca>
auth-nocache
reneg-sec 0`, c.CACertificate))
}
16 changes: 11 additions & 5 deletions openvpn/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
"time"
Expand All @@ -18,6 +19,12 @@ const (
)

func Connect(ctx context.Context, c Config, sid string, samlResponse string, notifyCh chan int) error {
conf, err := c.pipe()
if err != nil {
return fmt.Errorf("openvpn: %w", err)
}
defer conf.Close()

auth, err := newTextSourcePipe(fmt.Sprintf("N/A\nCRV1::%s::%s\n", sid, samlResponse))
if err != nil {
return fmt.Errorf("openvpn: %w", err)
Expand All @@ -28,17 +35,16 @@ func Connect(ctx context.Context, c Config, sid string, samlResponse string, not
defer killProcess()

cmd := exec.CommandContext(cmdCtx, c.Command,
"--config", c.ConfigFile,
"--config", "/dev/fd/3",
"--verb", "3",
"--auth-nocache",
"--inactive", "3600",
"--proto", c.Proto,
"--remote", c.Remote,
c.Port,
"--auth-user-pass", "/dev/fd/3",
"--remote", c.Remote, strconv.Itoa(c.Port),
"--auth-user-pass", "/dev/fd/4",
)

cmd.ExtraFiles = []*os.File{auth.source}
cmd.ExtraFiles = []*os.File{conf.source, auth.source}
cmd.Stderr = os.Stderr

stdout, err := cmd.StdoutPipe()
Expand Down
16 changes: 11 additions & 5 deletions openvpn/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"regexp"
"strconv"
"syscall"
"time"
)
Expand Down Expand Up @@ -36,6 +37,12 @@ func parseHandshakeResponse(b []byte) (string, string, error) {
}

func Handshake(ctx context.Context, c Config) (string, string, error) {
conf, err := c.pipe()
if err != nil {
return "", "", fmt.Errorf("handshake: %w", err)
}
defer conf.Close()

auth, err := newTextSourcePipe("N/A\nACS::35001\n")
if err != nil {
return "", "", fmt.Errorf("handshake: %w", err)
Expand All @@ -46,18 +53,17 @@ func Handshake(ctx context.Context, c Config) (string, string, error) {
defer killProcess()

cmd := exec.CommandContext(cmdCtx, c.Command,
"--config", c.ConfigFile,
"--config", "/dev/fd/3",
"--verb", "3",
"--proto", c.Proto,
"--remote", c.Remote,
c.Port,
"--auth-user-pass", "/dev/fd/3",
"--remote", c.Remote, strconv.Itoa(c.Port),
"--auth-user-pass", "/dev/fd/4",
"--connect-retry-max", "1",
)

// cmd.Stderr = os.Stderr
// cmd.Stdout = os.Stdout
cmd.ExtraFiles = []*os.File{auth.source}
cmd.ExtraFiles = []*os.File{conf.source, auth.source}

go func() {
<-ctx.Done()
Expand Down

0 comments on commit 5badc67

Please sign in to comment.