fix ValueString (wtf?) and add ssh bits

This commit is contained in:
dave 2023-02-07 17:29:49 -08:00
parent 02c675c796
commit 07d47aba28
4 changed files with 127 additions and 5 deletions

4
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/hashicorp/terraform-plugin-go v0.14.3
github.com/hashicorp/terraform-plugin-log v0.7.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.1
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
)
require (
@ -59,7 +60,6 @@ require (
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
github.com/vmihailenco/tagparser v0.1.1 // indirect
github.com/zclconf/go-cty v1.12.1 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.4.0 // indirect
@ -67,4 +67,4 @@ require (
google.golang.org/genproto v0.0.0-20200711021454-869866162049 // indirect
google.golang.org/grpc v1.51.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)
)

View File

@ -47,12 +47,11 @@ func (p *SystemProvider) Configure(ctx context.Context, req provider.ConfigureRe
var data SystemProviderModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
client, err := system.NewSystemManagerFromUri(data.ConnectionString.String())
client, err := system.NewSystemManagerFromUri(data.ConnectionString.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Unable to create System Manager API client",

98
system/ssh.go Normal file
View File

@ -0,0 +1,98 @@
package system
import (
"fmt"
"io/ioutil"
"log"
"net"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"golang.org/x/crypto/ssh/knownhosts"
)
func getKnownHosts() (ssh.HostKeyCallback, error) {
homedir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("need homedir: %s", err)
}
return knownhosts.New(fmt.Sprintf("%s/.ssh/known_hosts", homedir))
}
func expandHome(path string) string {
if strings.HasPrefix(path, "~/") {
dirname, _ := os.UserHomeDir()
path = filepath.Join(dirname, path[2:])
}
return path
}
func NewSSHConfigFromUrl(u *url.URL) (string, int, *ssh.ClientConfig, error) {
port := 22
portStr := u.Port()
if portStr != "" {
port, _ = strconv.Atoi(portStr)
}
knownHostsCallback, err := getKnownHosts()
if err != nil {
return "", -1, nil, fmt.Errorf("couldn't load known hosts: %s", err)
}
username := u.User.Username()
password, _ := u.User.Password()
sshConfig := &ssh.ClientConfig{
User: username,
HostKeyCallback: knownHostsCallback,
}
extraArguments, err := url.ParseQuery(u.RawQuery)
if err != nil {
return "", -1, nil, err
}
sshKeyPath, hasSshKeyPath := extraArguments["ssh_key_path"]
agentValue, hasAgent := extraArguments["agent"]
if username != "" && hasSshKeyPath {
log.Printf("using ssh key: %s", sshKeyPath)
key, err := ioutil.ReadFile(expandHome(sshKeyPath[0]))
if err != nil {
return "", -1, nil, fmt.Errorf("read private key: %s", err)
}
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
return "", -1, nil, fmt.Errorf("parse private key: %v", err)
}
sshConfig.Auth = []ssh.AuthMethod{
ssh.PublicKeys(signer),
}
} else if hasAgent && agentValue[0] == "yes" {
socket := os.Getenv("SSH_AUTH_SOCK")
if socket == "" {
return "", -1, nil, fmt.Errorf("requested ssh-agent but $SSH_AUTH_SOCK is unset")
}
conn, err := net.Dial("unix", socket)
if err != nil {
return "", -1, nil, fmt.Errorf("Failed to open SSH_AUTH_SOCK: %v", err)
}
agentClient := agent.NewClient(conn)
sshConfig.Auth = []ssh.AuthMethod{
ssh.PublicKeysCallback(agentClient.Signers),
}
} else if username != "" && password != "" {
log.Printf("using ssh password")
sshConfig.Auth = []ssh.AuthMethod{
ssh.Password(password),
}
} else {
return "", -1, nil, fmt.Errorf("no credential supplied, need user with password or ?ssh_key_path=<path> or ?agent=yes")
}
return u.Host, port, sshConfig, nil
}

View File

@ -1,5 +1,11 @@
package system
import (
"fmt"
"golang.org/x/crypto/ssh"
"net/url"
)
// SystemManager providers the functionality to interrogate / interact with target systems
type SystemManager struct {
connection *SystemConnection
@ -21,8 +27,27 @@ func (s *SystemManager) Test() error {
// SystemConnection provides an unopinionated interface for interacting with remote systems on a low level
type SystemConnection struct {
client *ssh.Client
}
func NewSystemConnectionFromUri(uri string) (*SystemConnection, error) {
return nil, nil
//return nil, fmt.Errorf("using uri: '%s'", uri)
sshUrl, err := url.Parse(uri)
if err != nil {
return nil, fmt.Errorf("invalid ssh uri: %s", err)
}
sshHost, sshPort, sshConfig, err := NewSSHConfigFromUrl(sshUrl)
if err != nil {
return nil, fmt.Errorf("ssh failed: %s", err)
}
sshClient, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", sshHost, sshPort), sshConfig)
if err != nil {
return nil, fmt.Errorf("dial failed: %s", err)
}
return &SystemConnection{
client: sshClient,
}, nil
}