diff --git a/docker/Dockerfile-netclient-kernel b/docker/Dockerfile-netclient-kernel new file mode 100644 index 00000000..f562fb68 --- /dev/null +++ b/docker/Dockerfile-netclient-kernel @@ -0,0 +1,39 @@ +FROM debian:buster as builder +# add glib support daemon manager + +RUN apt update -y && apt install -y wget bash gcc musl-dev openssl golang git build-essential libmnl-dev iptables + +RUN wget -O go.tgz https://dl.google.com/go/go1.17.1.linux-amd64.tar.gz + +RUN tar -C /usr/local -xzf go.tgz + +WORKDIR /usr/local/go/src + +RUN chmod +x make.bash + +RUN ./make.bash + +ENV PATH="/usr/local/go/bin:$PATH" + +ENV GOPATH=/opt/go/ + +ENV PATH=$PATH:$GOPATH/bin + +WORKDIR /app + +COPY . . + +ENV GO111MODULE=auto + +RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-w -s" -o netclient-app netclient/main.go + +FROM debian:buster + +WORKDIR /root/ + +RUN apt update -y && apt install -y bash curl wget traceroute procps dnsutils iptables openresolv iproute2 +COPY --from=builder /app/netclient-app ./netclient +COPY --from=builder /app/scripts/netclient.sh . +RUN chmod 0755 netclient && chmod 0755 netclient.sh + +ENTRYPOINT ["/bin/sh", "./netclient.sh"] diff --git a/netclient/ncutils/netclientutils.go b/netclient/ncutils/netclientutils.go index ae20367c..84fbb29e 100644 --- a/netclient/ncutils/netclientutils.go +++ b/netclient/ncutils/netclientutils.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "io/ioutil" "log" "math/rand" "net" @@ -318,6 +319,38 @@ func GetNetclientPathSpecific() string { } } +func GetNewIface(dir string) (string, error) { + files, _ := ioutil.ReadDir(dir) + var newestFile string + var newestTime int64 = 0 + var err error + for _, f := range files { + fi, err := os.Stat(dir + f.Name()) + if err != nil { + return "", err + } + currTime := fi.ModTime().Unix() + if currTime > newestTime && strings.Contains(f.Name(), ".sock") { + newestTime = currTime + newestFile = f.Name() + } + } + resultArr := strings.Split(newestFile, ".") + if resultArr[0] == "" { + err = errors.New("sock file does not exist") + } + return resultArr[0], err +} + +func GetFileAsString(path string) (string, error) { + content, err := os.ReadFile(path) + if err != nil { + return "", err + } + text := string(content) + return text, err +} + // GetNetclientPathSpecific - gets specific netclient config path func GetWGPathSpecific() string { if IsWindows() { diff --git a/netclient/netclient-darwin b/netclient/netclient-darwin new file mode 100755 index 00000000..ede60372 Binary files /dev/null and b/netclient/netclient-darwin differ diff --git a/netclient/wireguard/common.go b/netclient/wireguard/common.go index e264cab6..fdf79a39 100644 --- a/netclient/wireguard/common.go +++ b/netclient/wireguard/common.go @@ -159,22 +159,28 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig // spin up userspace / windows interface + apply the conf file var deviceiface string if ncutils.IsMac() { + log.Println("DELETE ME: check for local iface") deviceiface, err = local.GetMacIface(node.Address) if err != nil || deviceiface == "" { deviceiface = ifacename } } if syncconf { + log.Println("DELETE ME: syncconf") err = SyncWGQuickConf(ifacename, confPath) } else { - d, _ := wgclient.Device(deviceiface) - for d != nil && d.Name == deviceiface { - RemoveConf(ifacename, false) // remove interface first - time.Sleep(time.Second >> 2) - d, _ = wgclient.Device(deviceiface) + if !ncutils.IsMac() { + log.Println("DELETE ME: get device") + d, _ := wgclient.Device(deviceiface) + for d != nil && d.Name == deviceiface { + RemoveConf(ifacename, false) // remove interface first + time.Sleep(time.Second >> 2) + d, _ = wgclient.Device(deviceiface) + } } if !ncutils.IsWindows() { - err = ApplyConf(confPath) + log.Println("DELETE ME: apply conf") + err = ApplyConf(*node, ifacename, confPath) if err != nil { ncutils.PrintLog("failed to create wireguard interface", 1) return err @@ -187,7 +193,7 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig ncutils.PrintLog("waiting for interface...", 1) for !strings.Contains(output, ifacename) && !(time.Now().After(starttime.Add(time.Duration(10) * time.Second))) { output, _ = ncutils.RunCmd("wg", false) - err = ApplyConf(confPath) + err = ApplyConf(*node, ifacename, confPath) time.Sleep(time.Second) } if !strings.Contains(output, ifacename) { @@ -259,8 +265,9 @@ func RemoveConf(iface string, printlog bool) error { var err error switch os { case "windows": - err = RemoveWindowsConf(iface, printlog) + case "darwin": + err = WgQuickDownShortMac(iface) default: confPath := ncutils.GetNetclientPathSpecific() + iface + ".conf" err = RemoveWGQuickConf(confPath, printlog) @@ -269,12 +276,14 @@ func RemoveConf(iface string, printlog bool) error { } // ApplyConf - applys a conf on disk to WireGuard interface -func ApplyConf(confPath string) error { +func ApplyConf(node models.Node, ifacename string, confPath string) error { os := runtime.GOOS var err error switch os { case "windows": _ = ApplyWindowsConf(confPath) + case "darwin": + _ = ApplyMacOSConf(node, ifacename, confPath) default: err = ApplyWGQuickConf(confPath) } diff --git a/netclient/wireguard/mac.go b/netclient/wireguard/mac.go new file mode 100644 index 00000000..df88bf45 --- /dev/null +++ b/netclient/wireguard/mac.go @@ -0,0 +1,205 @@ +package wireguard + +import ( + "errors" + "os" + "strconv" + "strings" + "time" + + "github.com/gravitl/netmaker/models" + "github.com/gravitl/netmaker/netclient/ncutils" +) + +func AddInterface(iface string) (string, error) { + ncutils.RunCmd("mkdir -p /var/run/wireguard/", true) + ncutils.RunCmd("wireguard-go utun", true) + return ncutils.GetNewIface("/var/run/wireguard/") +} + +func GetRealIface(iface string) (string, error) { + ncutils.RunCmd("wg show interfaces", false) + ifacePath := "/var/run/wireguard/" + iface + ".name" + if !(ncutils.FileExists(ifacePath)) { + return "", errors.New(ifacePath + " does not exist") + } + realIfaceName, err := ncutils.GetFileAsString(ifacePath) + if err != nil { + return "", err + } + if !(ncutils.FileExists("/var/run/wireguard/" + realIfaceName + ".sock")) { + return "", errors.New("interface file does not exist") + } + return realIfaceName, nil +} + +func DeleteRoutes(iface string) error { + realIface, err := GetRealIface(iface) + if err != nil { + return err + } + var inets = [2]string{"inet", "inet6"} + for _, inet := range inets { + ifaceList, err := ncutils.RunCmd("netstat -nr -f "+inet+" | grep -e "+realIface+" | awk '{print $1}'", true) + if err != nil { + return err + } + destinations := strings.Split(ifaceList, "\n") + + for _, i := range destinations { + ncutils.RunCmd("route -q -n delete -"+inet+" "+i, true) + } + } + // wg-quick deletes ENDPOINTS here (runs 'route -q delete' for each peer endpoint on the interface.) + // We don't believe this is necessary. + return nil +} + +func DeleteInterface(iface string) error { + var err error + if iface != "" { + ncutils.RunCmd("rm -f /var/run/wireguard/"+iface+".sock", true) + } + _, err = ncutils.RunCmd("ifconfig "+iface+" down", false) + if strings.Contains(err.Error(), "does not exist") { + err = nil + } + return err +} + +func UpInterface(iface string) error { + var err error + _, err = ncutils.RunCmd("ifconfig "+iface+" up", true) + return err +} + +func AddAddress(iface string, addr string) error { + var err error + if strings.Contains(addr, ":") { + _, err = ncutils.RunCmd("ifconfig "+iface+" inet6 "+addr+" alias", true) + } else { + _, err = ncutils.RunCmd("ifconfig "+iface+" inet "+addr+" 255.255.255.0 alias", true) + } + return err +} + +func SetMTU(iface string, mtu int) error { + var err error + if mtu == 0 { + mtu = 1280 + } + _, err = ncutils.RunCmd("ifconfig "+iface+" mtu "+strconv.Itoa(mtu), true) + return err +} + +func AddRoute(addr string, iface string) error { + var err error + var out string + var inetx = "inet" + if strings.Contains(addr, ":") { + inetx = "inet6" + } + out, err = ncutils.RunCmd("route -n get -"+inetx+" "+addr, true) + if err != nil { + return err + } + if out == "" { + _, err = ncutils.RunCmd("route -q -n add -"+inetx+" "+addr+" -interface "+iface, true) + } + return err +} + +func SetConfig(realIface string, confPath string) error { + confString := GetConfig(confPath) + err := os.WriteFile(confPath+".tmp", []byte(confString), 0644) + if err != nil { + return err + } + _, err = ncutils.RunCmd("wg setconf "+realIface+" "+confPath+".tmp", true) + os.Remove(confPath + ".tmp") + return err +} + +func GetConfig(path string) string { + var confCmd = "grep -v -e Address -e MTU -e PostUp -e PostDown " + confRaw, _ := ncutils.RunCmd(confCmd+path, false) + return confRaw +} + +func WgQuickUpMac(node models.Node, iface string, confPath string) error { + var err error + var realIface string + DeleteInterface(iface) + DeleteRoutes(iface) + + realIface, err = AddInterface(iface) + if err != nil { + ncutils.PrintLog("error creating wg interface", 1) + return err + } + time.Sleep(1) + err = SetConfig(realIface, confPath) + if err != nil { + ncutils.PrintLog("error setting config for "+realIface, 1) + return err + } + var ips []string + ips = append(node.AllowedIPs, node.Address) + ips = append(ips, node.Address6) + for _, i := range ips { + if i != "" { + err = AddAddress(realIface, i) + if err != nil { + ncutils.PrintLog("error adding address "+i+" on interface "+realIface, 1) + return err + } + } + } + SetMTU(realIface, int(node.MTU)) + err = UpInterface(realIface) + if err != nil { + ncutils.PrintLog("error turning on interface "+iface, 1) + return err + } + for _, i := range ips { + if i != "" { + err = AddRoute(i, realIface) + if err != nil { + ncutils.PrintLog("error adding route to "+realIface+" for "+i, 1) + return err + } + } + } + //next, wg-quick runs set_endpoint_direct_route + //next, wg-quick runs monitor_daemon + time.Sleep(1) + if node.PostUp != "" { + runcmds := strings.Split(node.PostUp, "; ") + ncutils.RunCmds(runcmds, true) + } + return err +} + +func WgQuickDownShortMac(iface string) error { + var err error + realIface, err := GetRealIface(iface) + if realIface != "" { + err = DeleteInterface(iface) + } + return err +} + +func WgQuickDownMac(node models.Node, iface string) error { + var err error + realIface, err := GetRealIface(iface) + if realIface != "" { + err = DeleteInterface(iface) + } else if err != nil { + return err + } + if node.PostDown != "" { + runcmds := strings.Split(node.PostDown, "; ") + ncutils.RunCmds(runcmds, true) + } + return err +} diff --git a/netclient/wireguard/unix.go b/netclient/wireguard/unix.go index 254f6482..586f16c8 100644 --- a/netclient/wireguard/unix.go +++ b/netclient/wireguard/unix.go @@ -58,10 +58,22 @@ func ApplyWGQuickConf(confPath string) error { return err } +// ApplyMacOSConf - applies system commands similar to wg-quick using golang for MacOS +func ApplyMacOSConf(node models.Node, ifacename string, confPath string) error { + var err error + err = WgQuickDownMac(node, ifacename) + err = WgQuickUpMac(node, ifacename, confPath) + return err +} + // SyncWGQuickConf - formats config file and runs sync command func SyncWGQuickConf(iface string, confPath string) error { var tmpConf = confPath + ".sync.tmp" - confRaw, err := ncutils.RunCmd("wg-quick strip "+confPath, false) + var confCmd = "wg-quick strip " + if ncutils.IsMac() { + confCmd = "grep -v -e Address -e MTU -e PostUp -e PostDown " + } + confRaw, err := ncutils.RunCmd(confCmd+confPath, false) if err != nil { return err }