Merge pull request #21 from cedws/identityfile

feat: add ssh_key_file field to connections config
This commit is contained in:
Pavel Sviderski
2025-02-10 09:03:13 +10:00
committed by GitHub
5 changed files with 62 additions and 12 deletions
+14 -6
View File
@@ -4,15 +4,17 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/charmbracelet/huh"
"google.golang.org/protobuf/types/known/emptypb"
"net/netip" "net/netip"
"uncloud/internal/cli/client" "uncloud/internal/cli/client"
"uncloud/internal/cli/client/connector" "uncloud/internal/cli/client/connector"
"uncloud/internal/cli/config" "uncloud/internal/cli/config"
"uncloud/internal/fs"
"uncloud/internal/machine" "uncloud/internal/machine"
"uncloud/internal/machine/api/pb" "uncloud/internal/machine/api/pb"
"uncloud/internal/sshexec" "uncloud/internal/sshexec"
"github.com/charmbracelet/huh"
"google.golang.org/protobuf/types/known/emptypb"
) )
const defaultClusterName = "default" const defaultClusterName = "default"
@@ -89,10 +91,14 @@ func (cli *CLI) ConnectCluster(ctx context.Context, clusterName string) (*client
if err != nil { if err != nil {
return nil, fmt.Errorf("parse SSH connection %q: %w", conn.SSH, err) return nil, fmt.Errorf("parse SSH connection %q: %w", conn.SSH, err)
} }
keyPath := fs.ExpandHomeDir(conn.SSHKeyFile)
sshConfig := &connector.SSHConnectorConfig{ sshConfig := &connector.SSHConnectorConfig{
User: user, User: user,
Host: host, Host: host,
Port: port, Port: port,
KeyPath: keyPath,
} }
return client.New(ctx, connector.NewSSHConnector(sshConfig)) return client.New(ctx, connector.NewSSHConnector(sshConfig))
} else if conn.TCP.IsValid() { } else if conn.TCP.IsValid() {
@@ -157,9 +163,11 @@ func (cli *CLI) initRemoteMachine(
return fmt.Errorf("set current cluster: %w", err) return fmt.Errorf("set current cluster: %w", err)
} }
} }
// Save the machine's SSH connection details in the cluster config. // Save the machine's SSH connection details in the cluster config.
connCfg := config.MachineConnection{ connCfg := config.MachineConnection{
SSH: config.NewSSHDestination(remoteMachine.User, remoteMachine.Host, remoteMachine.Port), SSH: config.NewSSHDestination(remoteMachine.User, remoteMachine.Host, remoteMachine.Port),
SSHKeyFile: remoteMachine.KeyPath,
} }
cli.config.Clusters[clusterName].Connections = append(cli.config.Clusters[clusterName].Connections, connCfg) cli.config.Clusters[clusterName].Connections = append(cli.config.Clusters[clusterName].Connections, connCfg)
if err = cli.config.Save(); err != nil { if err = cli.config.Save(); err != nil {
+5 -4
View File
@@ -14,10 +14,11 @@ const (
) )
type MachineConnection struct { type MachineConnection struct {
SSH SSHDestination `toml:"ssh,omitempty"` SSH SSHDestination `toml:"ssh,omitempty"`
TCP netip.AddrPort `toml:"tcp,omitempty"` TCP netip.AddrPort `toml:"tcp,omitempty"`
Host string `toml:"host,omitempty"` Host string `toml:"host,omitempty"`
PublicKey secret.Secret `toml:"public_key,omitempty"` PublicKey secret.Secret `toml:"public_key,omitempty"`
SSHKeyFile string `toml:"ssh_key_file,omitempty"`
} }
// SSHDestination represents an SSH destination string in the canonical form of "user@host:port". // SSHDestination represents an SSH destination string in the canonical form of "user@host:port".
+15
View File
@@ -5,8 +5,23 @@ import (
"os" "os"
"os/user" "os/user"
"strconv" "strconv"
"strings"
) )
func ExpandHomeDir(path string) string {
if len(path) == 0 {
return path
}
if path[0] == '~' {
home, err := os.UserHomeDir()
if err != nil {
return path
}
return strings.Replace(path, "~", home, 1)
}
return path
}
// LookupUIDGID returns the user and group IDs for the given username. // LookupUIDGID returns the user and group IDs for the given username.
func LookupUIDGID(username string) (uid, gid int, err error) { func LookupUIDGID(username string) (uid, gid int, err error) {
usr, err := user.Lookup(username) usr, err := user.Lookup(username)
+22
View File
@@ -0,0 +1,22 @@
package fs
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestExpandHomeDir(t *testing.T) {
t.Run("empty", func(t *testing.T) {
assert.Equal(t, "", ExpandHomeDir(""))
})
t.Run("no home", func(t *testing.T) {
assert.Equal(t, "/path", ExpandHomeDir("/path"))
})
t.Run("home", func(t *testing.T) {
t.Setenv("HOME", "/home/user")
assert.Equal(t, "/home/user/path", ExpandHomeDir("~/path"))
})
}
+6 -2
View File
@@ -2,12 +2,13 @@ package sshexec
import ( import (
"fmt" "fmt"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"net" "net"
"os" "os"
"strconv" "strconv"
"time" "time"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
) )
func Connect(user, host string, port int, sshKeyPath string) (*ssh.Client, error) { func Connect(user, host string, port int, sshKeyPath string) (*ssh.Client, error) {
@@ -34,6 +35,9 @@ func Connect(user, host string, port int, sshKeyPath string) (*ssh.Client, error
} }
keyAuth, err := privateKeyAuth(sshKeyPath) keyAuth, err := privateKeyAuth(sshKeyPath)
if err != nil {
return nil, err
}
config := &ssh.ClientConfig{ config := &ssh.ClientConfig{
User: user, User: user,
Auth: []ssh.AuthMethod{keyAuth}, Auth: []ssh.AuthMethod{keyAuth},