From 2bebb8236e49e0979bb36b20afd90290d0d49670 Mon Sep 17 00:00:00 2001 From: cezmunsta Date: Thu, 26 Feb 2026 21:20:19 +0000 Subject: [PATCH 1/3] Added pre-connect VPN check Initial logic to permit checking for undesired network interfaces ahead of making an SSH connection --- Makefile | 5 +++-- config/main.go | 22 ++++++++++++---------- config/versions.go | 2 +- ssh/main.go | 13 +++++++++++++ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index f69ea75..08ff589 100644 --- a/Makefile +++ b/Makefile @@ -18,15 +18,16 @@ SSH_MS_SECRET_PATH?=secret/ssh_ms SSH_MS_SYNC_HOST?=localhost SSH_MS_SYNC_PATH?=/usr/share/nginx/html/downloads/ssh_ms/ SSH_MS_USERNAME?=SSH_MS_USERNAME +SSH_MS_VPN_CHECK_NAMES?= DEBUG_BUILD=$(shell test "${DEBUG}" = "1" && echo 1 || echo 0) COMPRESS_BINARY=$(shell test "${XZ_COMPRESS}" = "1" && echo 1 || echo 0) PACKAGE=github.com/cezmunsta/ssh_ms ifeq ($(DEBUG_BUILD), 1) -LDFLAGS=-ldflags "-X ${PACKAGE}/config.EnvBasePath=${SSH_MS_BASEPATH} -X ${PACKAGE}/cmd.Version=${RELEASE_VER} -X ${PACKAGE}/config.EnvSSHUsername=${SSH_MS_USERNAME} -X ${PACKAGE}/config.EnvSSHIdentityFile=${SSH_MS_ID_FILE} -X ${PACKAGE}/config.EnvSSHDefaultUsername=${SSH_MS_DEFAULT_USERNAME} -X ${PACKAGE}/config.EnvVaultAddr=${SSH_MS_DEFAULT_VAULT_ADDR} -X ${PACKAGE}/config.SecretPath=${SSH_MS_SECRET_PATH} -X ${PACKAGE}/vault.RenewThreshold=${SSH_MS_RENEW_THRESHOLD} -X ${PACKAGE}/config.portServiceMappings=${SSH_MS_SECRET_MAP}" +LDFLAGS=-ldflags "-X ${PACKAGE}/config.EnvBasePath=${SSH_MS_BASEPATH} -X ${PACKAGE}/cmd.Version=${RELEASE_VER} -X ${PACKAGE}/config.EnvSSHUsername=${SSH_MS_USERNAME} -X ${PACKAGE}/config.EnvSSHIdentityFile=${SSH_MS_ID_FILE} -X ${PACKAGE}/config.EnvSSHDefaultUsername=${SSH_MS_DEFAULT_USERNAME} -X ${PACKAGE}/config.EnvVaultAddr=${SSH_MS_DEFAULT_VAULT_ADDR} -X ${PACKAGE}/config.SecretPath=${SSH_MS_SECRET_PATH} -X ${PACKAGE}/vault.RenewThreshold=${SSH_MS_RENEW_THRESHOLD} -X ${PACKAGE}/config.portServiceMappings=${SSH_MS_SECRET_MAP} -X ${PACKAGE}/config.undesiredInterfaces=${SSH_MS_VPN_CHECK_NAMES}" else -LDFLAGS=-ldflags "-w -X ${PACKAGE}/config.EnvBasePath=${SSH_MS_BASEPATH} -X ${PACKAGE}/cmd.Version=${RELEASE_VER} -X ${PACKAGE}/config.EnvSSHUsername=${SSH_MS_USERNAME} -X ${PACKAGE}/config.EnvSSHIdentityFile=${SSH_MS_ID_FILE} -X ${PACKAGE}/config.EnvSSHDefaultUsername=${SSH_MS_DEFAULT_USERNAME} -X ${PACKAGE}/config.EnvVaultAddr=${SSH_MS_DEFAULT_VAULT_ADDR} -X ${PACKAGE}/config.SecretPath=${SSH_MS_SECRET_PATH} -X ${PACKAGE}/vault.RenewThreshold=${SSH_MS_RENEW_THRESHOLD} -X ${PACKAGE}/config.portServiceMappings=${SSH_MS_SECRET_MAP}" +LDFLAGS=-ldflags "-w -X ${PACKAGE}/config.EnvBasePath=${SSH_MS_BASEPATH} -X ${PACKAGE}/cmd.Version=${RELEASE_VER} -X ${PACKAGE}/config.EnvSSHUsername=${SSH_MS_USERNAME} -X ${PACKAGE}/config.EnvSSHIdentityFile=${SSH_MS_ID_FILE} -X ${PACKAGE}/config.EnvSSHDefaultUsername=${SSH_MS_DEFAULT_USERNAME} -X ${PACKAGE}/config.EnvVaultAddr=${SSH_MS_DEFAULT_VAULT_ADDR} -X ${PACKAGE}/config.SecretPath=${SSH_MS_SECRET_PATH} -X ${PACKAGE}/vault.RenewThreshold=${SSH_MS_RENEW_THRESHOLD} -X ${PACKAGE}/config.portServiceMappings=${SSH_MS_SECRET_MAP} -X ${PACKAGE}/config.undesiredInterfaces=${SSH_MS_VPN_CHECK_NAMES}" endif VETFLAGS?=( -unusedresult -bools -copylocks -framepointer -httpresponse -json -stdmethods -printf -stringintconv -unmarshal -unsafeptr ) diff --git a/config/main.go b/config/main.go index 87b9d10..cbcf738 100644 --- a/config/main.go +++ b/config/main.go @@ -18,21 +18,14 @@ type Settings struct { Debug, RenewWarningOptOut, Simulate, StoredToken, Verbose, Version, VersionCheck bool ConfigComment, ConfigMotd, EnvSSHDefaultUsername, EnvSSHIdentityFile, CustomLocalForward, EnvSSHUsername, EnvVaultAddr, NameSpace, SecretPath, Show, StoragePath, User, VaultAddr, VaultToken, VaultAPIVersion, VaultSDKVersion string - ServiceMap map[string]string + ServiceMap map[string]string + UndesiredInterfaces []string } var ( once sync.Once settings Settings - /* - The following support overrides during builds, which can be done - by setting ldflags, e.g. - - `-ldflags "-X github.com/cezmunsta/ssh_ms/config.EnvSSHUserName=xxx"` - - */ - // EnvBasePath is the parent location used to prefix storage paths, // default value is filepath.Join(os.Getenv("HOME"), ".ssh", "cache") EnvBasePath string @@ -60,7 +53,10 @@ var ( SecretPath = "secret/ssh_ms" portServiceMappings string - serviceMap = make(map[string]string) + undesiredInterfaces string + + serviceMap = make(map[string]string) + undesiredInterfaceNames = []string{} ) func init() { @@ -83,6 +79,10 @@ func init() { serviceMap[p[0]] = p[1] } } + + if len(undesiredInterfaces) > 0 { + undesiredInterfaceNames = strings.Split(undesiredInterfaces, ",") + } } // ToJSON returns the config in JSON format @@ -99,6 +99,7 @@ func (s *Settings) ToJSON() string { func GetConfig() *Settings { once.Do(func() { renewWarningOptOut := false + if EnvBasePath == "" { EnvBasePath = filepath.Join(os.Getenv("HOME"), ".ssh", "cache") } @@ -138,6 +139,7 @@ func GetConfig() *Settings { Simulate: false, StoragePath: EnvBasePath, StoredToken: false, + UndesiredInterfaces: undesiredInterfaceNames, VaultAPIVersion: vaultAPIVersion, VaultSDKVersion: vaultSDKVersion, } diff --git a/config/versions.go b/config/versions.go index f7fb3e3..1bbee1d 100644 --- a/config/versions.go +++ b/config/versions.go @@ -3,5 +3,5 @@ package config const ( vaultAPIVersion = "v1.22.0" - vaultSDKVersion = "v0.20.0" + vaultSDKVersion = "v0.23.0" ) diff --git a/ssh/main.go b/ssh/main.go index 1410ba1..af49c9a 100644 --- a/ssh/main.go +++ b/ssh/main.go @@ -111,6 +111,11 @@ func acquirePort(min uint16, max uint16) (uint16, error) { return 0, errNoFreePort } +// check network interfaces +func checkNetworkInterfaces(denyList []string) ([]string, error) { + return []string{}, nil +} + // exists checks to see if a value is already present func (c Connection) exists(lf LocalForward) bool { for _, item := range c.LocalForward { @@ -541,6 +546,14 @@ func Connect(args []string, e UserEnv) { if e.Simulate { log.Println("cmd: ssh", strings.Join(args, " ")) } else { + if len(cfg.UndesiredInterfaces) > 0 { + log.Debug("VPN check enabled, checking before connecting") + + if detectedInterfaces, err := checkNetworkInterfaces(cfg.UndesiredInterfaces); err != nil { + log.Fatalf("detected undesired interfaces: %v", detectedInterfaces) + } + } + cmd := exec.Command("ssh", args...) cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin From 0f8f770f489b4bb5cd6811ee5299e0052c5cda7d Mon Sep 17 00:00:00 2001 From: cezmunsta Date: Mon, 2 Mar 2026 19:08:56 +0000 Subject: [PATCH 2/3] Added logic to handleUndesiredInterfacePresent --- config/main.go | 5 ++++ ssh/main.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/config/main.go b/config/main.go index cbcf738..095d99c 100644 --- a/config/main.go +++ b/config/main.go @@ -80,6 +80,11 @@ func init() { } } + userByPass := os.Getenv("SSH_MS_BYPASS_INTERFACE_CHECK") + if userByPass == "1" || userByPass == "yes" || userByPass == "true" { + undesiredInterfaces = "" + } + if len(undesiredInterfaces) > 0 { undesiredInterfaceNames = strings.Split(undesiredInterfaces, ",") } diff --git a/ssh/main.go b/ssh/main.go index af49c9a..879f2e1 100644 --- a/ssh/main.go +++ b/ssh/main.go @@ -1,17 +1,20 @@ package ssh import ( + "bufio" "bytes" "crypto/sha1" "encoding/json" "errors" "fmt" + "io" "io/ioutil" "maps" "net" "os" "os/exec" "reflect" + "regexp" "slices" "strconv" "strings" @@ -113,7 +116,63 @@ func acquirePort(min uint16, max uint16) (uint16, error) { // check network interfaces func checkNetworkInterfaces(denyList []string) ([]string, error) { - return []string{}, nil + var blockedInterfaces []string + var regexPatterns []*regexp.Regexp + + interfaces, err := net.Interfaces() + if err != nil { + return nil, fmt.Errorf("failed to get network interfaces: %w", err) + } + + for _, pattern := range denyList { + re, err := regexp.Compile(pattern) + if err != nil { + return nil, fmt.Errorf("invalid regex pattern '%s': %w", pattern, err) + } + regexPatterns = append(regexPatterns, re) + } + + for _, iface := range interfaces { + allowed := false + for _, re := range regexPatterns { + if !re.MatchString(iface.Name) { + allowed = true + break + } + } + if !allowed { + blockedInterfaces = append(blockedInterfaces, iface.Name) + } + } + + return blockedInterfaces, nil +} + +// action for when undesired network interfaces are required +func handleUndesiredInterfacePresent() { + fmt.Println("Please confirm that you wish to proceed? y/n") + + reader := bufio.NewReader(os.Stdin) + answer, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + fmt.Println("\nInput cancelled") + os.Exit(1) + } + fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err) + os.Exit(1) + } + + switch strings.ToLower(strings.TrimSpace(answer)) { + case "y", "yes", "1": + fmt.Println("Proceeding...") + case "n", "no", "0": + fmt.Println("Cancelled") + os.Exit(0) + default: + fmt.Println("Invalid input. Please enter y/n") + os.Exit(1) + } } // exists checks to see if a value is already present @@ -547,10 +606,11 @@ func Connect(args []string, e UserEnv) { log.Println("cmd: ssh", strings.Join(args, " ")) } else { if len(cfg.UndesiredInterfaces) > 0 { - log.Debug("VPN check enabled, checking before connecting") + log.Debug("Interface check enabled, checking before connecting") - if detectedInterfaces, err := checkNetworkInterfaces(cfg.UndesiredInterfaces); err != nil { - log.Fatalf("detected undesired interfaces: %v", detectedInterfaces) + if detectedInterfaces, err := checkNetworkInterfaces(cfg.UndesiredInterfaces); err != nil || len(detectedInterfaces) > 0 { + log.Warningf("detected undesired interfaces: %v", detectedInterfaces) + handleUndesiredInterfacePresent() } } From dffd5950e042743ebad2f4c3c5dbae4160894a27 Mon Sep 17 00:00:00 2001 From: cezmunsta Date: Tue, 3 Mar 2026 11:31:43 +0000 Subject: [PATCH 3/3] Updated prompt to include a reason --- ssh/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ssh/main.go b/ssh/main.go index 879f2e1..c9caab1 100644 --- a/ssh/main.go +++ b/ssh/main.go @@ -610,6 +610,7 @@ func Connect(args []string, e UserEnv) { if detectedInterfaces, err := checkNetworkInterfaces(cfg.UndesiredInterfaces); err != nil || len(detectedInterfaces) > 0 { log.Warningf("detected undesired interfaces: %v", detectedInterfaces) + fmt.Println("Your data may be transferred through undesired interfaces:", detectedInterfaces) handleUndesiredInterfacePresent() } }