feat: use pointers for connkeywords keys

This makes it easier for the ssh keywords to be overridden.
This commit is contained in:
Sylvia Crowe 2024-12-11 10:53:28 -08:00
parent ab5e7b5bd9
commit ef99e95d42
3 changed files with 59 additions and 40 deletions

View File

@ -139,6 +139,7 @@ declare global {
blockdef: BlockDef; blockdef: BlockDef;
rtopts?: RuntimeOpts; rtopts?: RuntimeOpts;
magnified?: boolean; magnified?: boolean;
ephemeral?: boolean;
}; };
// wshrpc.CommandCreateSubBlockData // wshrpc.CommandCreateSubBlockData

View File

@ -152,7 +152,7 @@ func createPublicKeyCallback(connCtx context.Context, sshKeywords *wshrpc.ConnKe
if err == nil { if err == nil {
signer, err := ssh.NewSignerFromKey(unencryptedPrivateKey) signer, err := ssh.NewSignerFromKey(unencryptedPrivateKey)
if err == nil { if err == nil {
if sshKeywords.SshAddKeysToAgent && agentClient != nil { if safeDeref(sshKeywords.SshAddKeysToAgent) && agentClient != nil {
agentClient.Add(agent.AddedKey{ agentClient.Add(agent.AddedKey{
PrivateKey: unencryptedPrivateKey, PrivateKey: unencryptedPrivateKey,
}) })
@ -166,7 +166,7 @@ func createPublicKeyCallback(connCtx context.Context, sshKeywords *wshrpc.ConnKe
} }
// batch mode deactivates user input // batch mode deactivates user input
if sshKeywords.SshBatchMode { if safeDeref(sshKeywords.SshBatchMode) {
// skip this key and try with the next // skip this key and try with the next
return createDummySigner() return createDummySigner()
} }
@ -195,7 +195,7 @@ func createPublicKeyCallback(connCtx context.Context, sshKeywords *wshrpc.ConnKe
// skip this key and try with the next // skip this key and try with the next
return createDummySigner() return createDummySigner()
} }
if sshKeywords.SshAddKeysToAgent && agentClient != nil { if safeDeref(sshKeywords.SshAddKeysToAgent) && agentClient != nil {
agentClient.Add(agent.AddedKey{ agentClient.Add(agent.AddedKey{
PrivateKey: unencryptedPrivateKey, PrivateKey: unencryptedPrivateKey,
}) })
@ -551,12 +551,27 @@ func createHostKeyCallback(sshKeywords *wshrpc.ConnKeywords) (ssh.HostKeyCallbac
return waveHostKeyCallback, hostKeyAlgorithms, nil return waveHostKeyCallback, hostKeyAlgorithms, nil
} }
func safeDeref[T any](x *T) T {
if x == nil {
var safeOut T
return safeOut
}
return *x
}
func ptr[T any](x T) *T {
return &x
}
func createClientConfig(connCtx context.Context, sshKeywords *wshrpc.ConnKeywords, debugInfo *ConnectionDebugInfo) (*ssh.ClientConfig, error) { func createClientConfig(connCtx context.Context, sshKeywords *wshrpc.ConnKeywords, debugInfo *ConnectionDebugInfo) (*ssh.ClientConfig, error) {
remoteName := sshKeywords.SshUser + "@" + xknownhosts.Normalize(sshKeywords.SshHostName+":"+sshKeywords.SshPort) chosenUser := safeDeref(sshKeywords.SshUser)
chosenHostName := safeDeref(sshKeywords.SshHostName)
chosenPort := safeDeref(sshKeywords.SshPort)
remoteName := chosenUser + xknownhosts.Normalize(chosenHostName+":"+chosenPort)
var authSockSigners []ssh.Signer var authSockSigners []ssh.Signer
var agentClient agent.ExtendedAgent var agentClient agent.ExtendedAgent
conn, err := net.Dial("unix", sshKeywords.SshIdentityAgent) conn, err := net.Dial("unix", safeDeref(sshKeywords.SshIdentityAgent))
if err != nil { if err != nil {
log.Printf("Failed to open Identity Agent Socket: %v", err) log.Printf("Failed to open Identity Agent Socket: %v", err)
} else { } else {
@ -577,9 +592,9 @@ func createClientConfig(connCtx context.Context, sshKeywords *wshrpc.ConnKeyword
// note: batch mode turns off interactive input // note: batch mode turns off interactive input
authMethodActiveMap := map[string]bool{ authMethodActiveMap := map[string]bool{
"publickey": sshKeywords.SshPubkeyAuthentication, "publickey": safeDeref(sshKeywords.SshPubkeyAuthentication),
"keyboard-interactive": sshKeywords.SshKbdInteractiveAuthentication && !sshKeywords.SshBatchMode, "keyboard-interactive": safeDeref(sshKeywords.SshKbdInteractiveAuthentication) && !safeDeref(sshKeywords.SshBatchMode),
"password": sshKeywords.SshPasswordAuthentication && !sshKeywords.SshBatchMode, "password": safeDeref(sshKeywords.SshPasswordAuthentication) && !safeDeref(sshKeywords.SshBatchMode),
} }
var authMethods []ssh.AuthMethod var authMethods []ssh.AuthMethod
@ -600,9 +615,9 @@ func createClientConfig(connCtx context.Context, sshKeywords *wshrpc.ConnKeyword
return nil, err return nil, err
} }
networkAddr := sshKeywords.SshHostName + ":" + sshKeywords.SshPort networkAddr := chosenHostName + ":" + chosenPort
return &ssh.ClientConfig{ return &ssh.ClientConfig{
User: sshKeywords.SshUser, User: chosenUser,
Auth: authMethods, Auth: authMethods,
HostKeyCallback: hostKeyCallback, HostKeyCallback: hostKeyCallback,
HostKeyAlgorithms: hostKeyAlgorithms(networkAddr), HostKeyAlgorithms: hostKeyAlgorithms(networkAddr),
@ -646,9 +661,10 @@ func ConnectToClient(connCtx context.Context, opts *SSHOpts, currentClient *ssh.
return nil, debugInfo.JumpNum, ConnectionError{ConnectionDebugInfo: debugInfo, Err: err} return nil, debugInfo.JumpNum, ConnectionError{ConnectionDebugInfo: debugInfo, Err: err}
} }
connFlags.SshUser = opts.SSHUser connFlags.SshUser = &opts.SSHUser
connFlags.SshHostName = opts.SSHHost connFlags.SshHostName = &opts.SSHHost
connFlags.SshPort = fmt.Sprintf("%d", opts.SSHPort) portStr := fmt.Sprintf("%d", opts.SSHPort)
connFlags.SshPort = &portStr
rawName := opts.String() rawName := opts.String()
savedKeywords, ok := wconfig.ReadFullConfig().Connections[rawName] savedKeywords, ok := wconfig.ReadFullConfig().Connections[rawName]
@ -684,7 +700,7 @@ func ConnectToClient(connCtx context.Context, opts *SSHOpts, currentClient *ssh.
if err != nil { if err != nil {
return nil, debugInfo.JumpNum, ConnectionError{ConnectionDebugInfo: debugInfo, Err: err} return nil, debugInfo.JumpNum, ConnectionError{ConnectionDebugInfo: debugInfo, Err: err}
} }
networkAddr := sshKeywords.SshHostName + ":" + sshKeywords.SshPort networkAddr := safeDeref(sshKeywords.SshHostName) + ":" + safeDeref(sshKeywords.SshPort)
client, err := connectInternal(connCtx, networkAddr, clientConfig, debugInfo.CurrentClient) client, err := connectInternal(connCtx, networkAddr, clientConfig, debugInfo.CurrentClient)
if err != nil { if err != nil {
return client, debugInfo.JumpNum, ConnectionError{ConnectionDebugInfo: debugInfo, Err: err} return client, debugInfo.JumpNum, ConnectionError{ConnectionDebugInfo: debugInfo, Err: err}
@ -695,32 +711,33 @@ func ConnectToClient(connCtx context.Context, opts *SSHOpts, currentClient *ssh.
func combineSshKeywords(userProvidedOpts *wshrpc.ConnKeywords, configKeywords *wshrpc.ConnKeywords, savedKeywords *wshrpc.ConnKeywords) (*wshrpc.ConnKeywords, error) { func combineSshKeywords(userProvidedOpts *wshrpc.ConnKeywords, configKeywords *wshrpc.ConnKeywords, savedKeywords *wshrpc.ConnKeywords) (*wshrpc.ConnKeywords, error) {
sshKeywords := &wshrpc.ConnKeywords{} sshKeywords := &wshrpc.ConnKeywords{}
if userProvidedOpts.SshUser != "" { if safeDeref(userProvidedOpts.SshUser) != "" {
sshKeywords.SshUser = userProvidedOpts.SshUser sshKeywords.SshUser = userProvidedOpts.SshUser
} else if configKeywords.SshUser != "" { } else if safeDeref(configKeywords.SshUser) != "" {
sshKeywords.SshUser = configKeywords.SshUser sshKeywords.SshUser = configKeywords.SshUser
} else { } else {
user, err := user.Current() user, err := user.Current()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get user for ssh: %+v", err) return nil, fmt.Errorf("failed to get user for ssh: %+v", err)
} }
sshKeywords.SshUser = user.Username sshKeywords.SshUser = ptr(user.Username)
} }
// we have to check the host value because of the weird way // we have to check the host value because of the weird way
// we store the pattern as the hostname for imported remotes // we store the pattern as the hostname for imported remotes
if configKeywords.SshHostName != "" { if safeDeref(configKeywords.SshHostName) != "" {
sshKeywords.SshHostName = configKeywords.SshHostName sshKeywords.SshHostName = configKeywords.SshHostName
} else { } else {
sshKeywords.SshHostName = userProvidedOpts.SshHostName sshKeywords.SshHostName = userProvidedOpts.SshHostName
} }
if userProvidedOpts.SshPort != "0" && userProvidedOpts.SshPort != "22" { userPort := safeDeref(userProvidedOpts.SshPort)
if userPort != "" && userPort != "0" && userPort != "22" {
sshKeywords.SshPort = userProvidedOpts.SshPort sshKeywords.SshPort = userProvidedOpts.SshPort
} else if configKeywords.SshPort != "" && configKeywords.SshPort != "22" { } else if userPort != "" && userPort != "22" {
sshKeywords.SshPort = configKeywords.SshPort sshKeywords.SshPort = configKeywords.SshPort
} else { } else {
sshKeywords.SshPort = "22" sshKeywords.SshPort = ptr("22")
} }
// use internal config ones // use internal config ones
@ -760,19 +777,19 @@ func findSshConfigKeywords(hostPattern string) (*wshrpc.ConnKeywords, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshUser = trimquotes.TryTrimQuotes(userRaw) sshKeywords.SshUser = ptr(trimquotes.TryTrimQuotes(userRaw))
hostNameRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "HostName") hostNameRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "HostName")
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshHostName = trimquotes.TryTrimQuotes(hostNameRaw) sshKeywords.SshHostName = ptr(trimquotes.TryTrimQuotes(hostNameRaw))
portRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "Port") portRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "Port")
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshPort = trimquotes.TryTrimQuotes(portRaw) sshKeywords.SshPort = ptr(trimquotes.TryTrimQuotes(portRaw))
identityFileRaw := WaveSshConfigUserSettings().GetAll(hostPattern, "IdentityFile") identityFileRaw := WaveSshConfigUserSettings().GetAll(hostPattern, "IdentityFile")
for i := 0; i < len(identityFileRaw); i++ { for i := 0; i < len(identityFileRaw); i++ {
@ -784,26 +801,26 @@ func findSshConfigKeywords(hostPattern string) (*wshrpc.ConnKeywords, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshBatchMode = (strings.ToLower(trimquotes.TryTrimQuotes(batchModeRaw)) == "yes") sshKeywords.SshBatchMode = ptr(strings.ToLower(trimquotes.TryTrimQuotes(batchModeRaw)) == "yes")
// we currently do not support host-bound or unbound but will use yes when they are selected // we currently do not support host-bound or unbound but will use yes when they are selected
pubkeyAuthenticationRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "PubkeyAuthentication") pubkeyAuthenticationRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "PubkeyAuthentication")
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshPubkeyAuthentication = (strings.ToLower(trimquotes.TryTrimQuotes(pubkeyAuthenticationRaw)) != "no") sshKeywords.SshPubkeyAuthentication = ptr(strings.ToLower(trimquotes.TryTrimQuotes(pubkeyAuthenticationRaw)) != "no")
passwordAuthenticationRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "PasswordAuthentication") passwordAuthenticationRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "PasswordAuthentication")
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshPasswordAuthentication = (strings.ToLower(trimquotes.TryTrimQuotes(passwordAuthenticationRaw)) != "no") sshKeywords.SshPasswordAuthentication = ptr(strings.ToLower(trimquotes.TryTrimQuotes(passwordAuthenticationRaw)) != "no")
kbdInteractiveAuthenticationRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "KbdInteractiveAuthentication") kbdInteractiveAuthenticationRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "KbdInteractiveAuthentication")
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshKbdInteractiveAuthentication = (strings.ToLower(trimquotes.TryTrimQuotes(kbdInteractiveAuthenticationRaw)) != "no") sshKeywords.SshKbdInteractiveAuthentication = ptr(strings.ToLower(trimquotes.TryTrimQuotes(kbdInteractiveAuthenticationRaw)) != "no")
// these are parsed as a single string and must be separated // these are parsed as a single string and must be separated
// these are case sensitive in openssh so they are here too // these are case sensitive in openssh so they are here too
@ -816,7 +833,7 @@ func findSshConfigKeywords(hostPattern string) (*wshrpc.ConnKeywords, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshAddKeysToAgent = (strings.ToLower(trimquotes.TryTrimQuotes(addKeysToAgentRaw)) == "yes") sshKeywords.SshAddKeysToAgent = ptr(strings.ToLower(trimquotes.TryTrimQuotes(addKeysToAgentRaw)) == "yes")
identityAgentRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "IdentityAgent") identityAgentRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "IdentityAgent")
if err != nil { if err != nil {
@ -831,7 +848,7 @@ func findSshConfigKeywords(hostPattern string) (*wshrpc.ConnKeywords, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshIdentityAgent = agentPath sshKeywords.SshIdentityAgent = ptr(agentPath)
} else { } else {
log.Printf("unable to find SSH_AUTH_SOCK: %v\n", err) log.Printf("unable to find SSH_AUTH_SOCK: %v\n", err)
} }
@ -840,7 +857,7 @@ func findSshConfigKeywords(hostPattern string) (*wshrpc.ConnKeywords, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sshKeywords.SshIdentityAgent = agentPath sshKeywords.SshIdentityAgent = ptr(agentPath)
} }
proxyJumpRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "ProxyJump") proxyJumpRaw, err := WaveSshConfigUserSettings().GetStrict(hostPattern, "ProxyJump")

View File

@ -290,6 +290,7 @@ type CommandCreateBlockData struct {
BlockDef *waveobj.BlockDef `json:"blockdef"` BlockDef *waveobj.BlockDef `json:"blockdef"`
RtOpts *waveobj.RuntimeOpts `json:"rtopts,omitempty"` RtOpts *waveobj.RuntimeOpts `json:"rtopts,omitempty"`
Magnified bool `json:"magnified,omitempty"` Magnified bool `json:"magnified,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"`
} }
type CommandCreateSubBlockData struct { type CommandCreateSubBlockData struct {
@ -468,17 +469,17 @@ type ConnKeywords struct {
TermFontFamily string `json:"term:fontfamily,omitempty"` TermFontFamily string `json:"term:fontfamily,omitempty"`
TermTheme string `json:"term:theme,omitempty"` TermTheme string `json:"term:theme,omitempty"`
SshUser string `json:"ssh:user,omitempty"` SshUser *string `json:"ssh:user,omitempty"`
SshHostName string `json:"ssh:hostname,omitempty"` SshHostName *string `json:"ssh:hostname,omitempty"`
SshPort string `json:"ssh:port,omitempty"` SshPort *string `json:"ssh:port,omitempty"`
SshIdentityFile []string `json:"ssh:identityfile,omitempty"` SshIdentityFile []string `json:"ssh:identityfile,omitempty"`
SshBatchMode bool `json:"ssh:batchmode,omitempty"` SshBatchMode *bool `json:"ssh:batchmode,omitempty"`
SshPubkeyAuthentication bool `json:"ssh:pubkeyauthentication,omitempty"` SshPubkeyAuthentication *bool `json:"ssh:pubkeyauthentication,omitempty"`
SshPasswordAuthentication bool `json:"ssh:passwordauthentication,omitempty"` SshPasswordAuthentication *bool `json:"ssh:passwordauthentication,omitempty"`
SshKbdInteractiveAuthentication bool `json:"ssh:kbdinteractiveauthentication,omitempty"` SshKbdInteractiveAuthentication *bool `json:"ssh:kbdinteractiveauthentication,omitempty"`
SshPreferredAuthentications []string `json:"ssh:preferredauthentications,omitempty"` SshPreferredAuthentications []string `json:"ssh:preferredauthentications,omitempty"`
SshAddKeysToAgent bool `json:"ssh:addkeystoagent,omitempty"` SshAddKeysToAgent *bool `json:"ssh:addkeystoagent,omitempty"`
SshIdentityAgent string `json:"ssh:identityagent,omitempty"` SshIdentityAgent *string `json:"ssh:identityagent,omitempty"`
SshProxyJump []string `json:"ssh:proxyjump,omitempty"` SshProxyJump []string `json:"ssh:proxyjump,omitempty"`
SshUserKnownHostsFile []string `json:"ssh:userknownhostsfile,omitempty"` SshUserKnownHostsFile []string `json:"ssh:userknownhostsfile,omitempty"`
SshGlobalKnownHostsFile []string `json:"ssh:globalknownhostsfile,omitempty"` SshGlobalKnownHostsFile []string `json:"ssh:globalknownhostsfile,omitempty"`