fix ValueString (wtf?) and add ssh bits
This commit is contained in:
parent
02c675c796
commit
07d47aba28
4
go.mod
4
go.mod
|
@ -8,6 +8,7 @@ require (
|
||||||
github.com/hashicorp/terraform-plugin-go v0.14.3
|
github.com/hashicorp/terraform-plugin-go v0.14.3
|
||||||
github.com/hashicorp/terraform-plugin-log v0.7.0
|
github.com/hashicorp/terraform-plugin-log v0.7.0
|
||||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.1
|
github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.1
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -59,7 +60,6 @@ require (
|
||||||
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
|
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
|
||||||
github.com/vmihailenco/tagparser v0.1.1 // indirect
|
github.com/vmihailenco/tagparser v0.1.1 // indirect
|
||||||
github.com/zclconf/go-cty v1.12.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/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||||
golang.org/x/text v0.4.0 // 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/genproto v0.0.0-20200711021454-869866162049 // indirect
|
||||||
google.golang.org/grpc v1.51.0 // indirect
|
google.golang.org/grpc v1.51.0 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
@ -47,12 +47,11 @@ func (p *SystemProvider) Configure(ctx context.Context, req provider.ConfigureRe
|
||||||
var data SystemProviderModel
|
var data SystemProviderModel
|
||||||
|
|
||||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||||
|
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := system.NewSystemManagerFromUri(data.ConnectionString.String())
|
client, err := system.NewSystemManagerFromUri(data.ConnectionString.ValueString())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics.AddError(
|
resp.Diagnostics.AddError(
|
||||||
"Unable to create System Manager API client",
|
"Unable to create System Manager API client",
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -1,5 +1,11 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
// SystemManager providers the functionality to interrogate / interact with target systems
|
// SystemManager providers the functionality to interrogate / interact with target systems
|
||||||
type SystemManager struct {
|
type SystemManager struct {
|
||||||
connection *SystemConnection
|
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
|
// SystemConnection provides an unopinionated interface for interacting with remote systems on a low level
|
||||||
type SystemConnection struct {
|
type SystemConnection struct {
|
||||||
|
client *ssh.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSystemConnectionFromUri(uri string) (*SystemConnection, error) {
|
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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue