Magnus Åhall
cfb5e0f9e0
Some checks failed
Wireguard MFA Windows Agent build / release (push) Failing after 15s
142 lines
3.2 KiB
Go
142 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
// External
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
|
|
// Standard
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io/fs"
|
|
"net/url"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"time"
|
|
)
|
|
|
|
const VERSION = "v3"
|
|
const SPOOLDIR = "C:\\Program Files\\Gibon\\wg-spool"
|
|
const DOMAIN = "https://vpn.gibonuddevalla.se"
|
|
|
|
var (
|
|
publicKeyPath string
|
|
runInteractiveAuth bool
|
|
connected bool
|
|
printVersion bool
|
|
)
|
|
|
|
func init() {
|
|
flag.BoolVar(&printVersion, "version", false, "print version and exit")
|
|
flag.BoolVar(&runInteractiveAuth, "interactive", false, "run interactive authentication")
|
|
flag.BoolVar(&connected, "connected", false, "postop, is connected")
|
|
flag.Parse()
|
|
|
|
if printVersion {
|
|
fmt.Println(VERSION)
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
|
|
func logError(where, e string) {
|
|
fmt.Printf("ERROR: %s\n", e)
|
|
fname := fmt.Sprintf("%s\\log.txt", SPOOLDIR)
|
|
file, err := os.OpenFile(fname, os.O_CREATE|os.O_APPEND, 0644)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
defer file.Close()
|
|
|
|
now := time.Now().String()
|
|
file.Write([]byte(now))
|
|
file.Write([]byte("\t"))
|
|
file.Write([]byte(where))
|
|
file.Write([]byte("\t"))
|
|
file.Write([]byte(e))
|
|
file.Write([]byte("\n"))
|
|
}
|
|
|
|
func main() {
|
|
if _, disabledDoesntExist := os.Stat(SPOOLDIR+`\disabled`); disabledDoesntExist == nil {
|
|
return
|
|
}
|
|
|
|
publicKeyPath = SPOOLDIR + "\\wireguard-mfa.url"
|
|
iface := os.Getenv("WIREGUARD_TUNNEL_NAME")
|
|
|
|
// Run from PreUp, step 1.
|
|
// Starts an interactive process, dropping privileges,
|
|
// in order to have permission to open a browser in step 2.
|
|
// Doesn't have permission to get public key.
|
|
if !runInteractiveAuth && !connected {
|
|
ex, _ := os.Executable()
|
|
if err := StartProcessAsCurrentUser("", ex+" -interactive", ""); err != nil {
|
|
logError("start_interactive", err.Error())
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Started from PreUp, step 2.
|
|
// Doesn't have permission to get public key.
|
|
if runInteractiveAuth {
|
|
var err error
|
|
start := time.Now()
|
|
for {
|
|
// Abort after 60 seconds to not have an eternal loop.
|
|
if time.Since(start).Seconds() > 60 {
|
|
os.Exit(1)
|
|
}
|
|
|
|
_, err = os.ReadFile(publicKeyPath)
|
|
if err != nil {
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
time.Sleep(time.Millisecond * 200)
|
|
continue
|
|
}
|
|
|
|
if !errors.Is(err, fs.ErrNotExist) {
|
|
logError("read_publickey_file", err.Error())
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
break
|
|
}
|
|
|
|
cmd := exec.Command("rundll32.exe", "url.dll,OpenURL", publicKeyPath)
|
|
cmd.Output()
|
|
os.Remove(publicKeyPath)
|
|
}
|
|
|
|
// Run from PostUp, step 3.
|
|
// Has only permission to get public key, can't run browser.
|
|
if connected {
|
|
getPublicKey(iface)
|
|
}
|
|
}
|
|
|
|
func getPublicKey(iface string) {
|
|
rxpPrivateKey := regexp.MustCompile("PrivateKey\\s*=\\s*(.*)")
|
|
cmd := exec.Command("wg", "showconf", iface)
|
|
out, _ := cmd.Output()
|
|
privateKey := rxpPrivateKey.FindStringSubmatch(string(out))
|
|
if len(privateKey) == 2 {
|
|
pubkey, err := wgtypes.ParseKey(privateKey[1])
|
|
if err != nil {
|
|
logError("parse_privatekey", fmt.Sprintf("%s [%s]", err.Error(), privateKey[1]))
|
|
return
|
|
}
|
|
|
|
urlData := fmt.Sprintf(
|
|
"[InternetShortcut]\r\nURL=%s/?key=%s\r\n",
|
|
DOMAIN,
|
|
url.QueryEscape(pubkey.PublicKey().String()),
|
|
)
|
|
os.WriteFile(publicKeyPath, []byte(urlData), 0644)
|
|
logError("publickey", pubkey.PublicKey().String())
|
|
} else {
|
|
logError("get_publickey", string(out))
|
|
}
|
|
}
|