Merge pull request #28665 from hashicorp/alisdair/communicator-bastion-port-type

communicator/ssh: Fix crash with SSH bastion port
This commit is contained in:
Alisdair McDiarmid 2021-05-11 08:54:11 -04:00 committed by GitHub
commit 4a93399c60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 62 additions and 24 deletions

View File

@ -36,7 +36,7 @@ var ConnectionBlockSupersetSchema = &configschema.Block{
Optional: true, Optional: true,
}, },
"port": { "port": {
Type: cty.String, Type: cty.Number,
Optional: true, Optional: true,
}, },
"timeout": { "timeout": {

View File

@ -331,7 +331,7 @@ func TestHostKey(t *testing.T) {
Password: "pass", Password: "pass",
Host: host, Host: host,
HostKey: pubKey, HostKey: pubKey,
Port: port, Port: uint16(port),
Timeout: "30s", Timeout: "30s",
} }
@ -363,7 +363,7 @@ func TestHostKey(t *testing.T) {
port, _ = strconv.Atoi(p) port, _ = strconv.Atoi(p)
connInfo.HostKey = testClientPublicKey connInfo.HostKey = testClientPublicKey
connInfo.Port = port connInfo.Port = uint16(port)
cfg, err = prepareSSHConfig(connInfo) cfg, err = prepareSSHConfig(connInfo)
if err != nil { if err != nil {
@ -406,7 +406,7 @@ func TestHostCert(t *testing.T) {
Password: "pass", Password: "pass",
Host: host, Host: host,
HostKey: testCAPublicKey, HostKey: testCAPublicKey,
Port: port, Port: uint16(port),
Timeout: "30s", Timeout: "30s",
} }
@ -438,7 +438,7 @@ func TestHostCert(t *testing.T) {
port, _ = strconv.Atoi(p) port, _ = strconv.Atoi(p)
connInfo.HostKey = testClientPublicKey connInfo.HostKey = testClientPublicKey
connInfo.Port = port connInfo.Port = uint16(port)
cfg, err = prepareSSHConfig(connInfo) cfg, err = prepareSSHConfig(connInfo)
if err != nil { if err != nil {
@ -528,7 +528,7 @@ func TestCertificateBasedAuth(t *testing.T) {
Host: host, Host: host,
PrivateKey: CLIENT_PEM, PrivateKey: CLIENT_PEM,
Certificate: CLIENT_CERT_SIGNED_BY_SERVER, Certificate: CLIENT_CERT_SIGNED_BY_SERVER,
Port: port, Port: uint16(port),
Timeout: "30s", Timeout: "30s",
} }

View File

@ -10,13 +10,13 @@ import (
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
"github.com/hashicorp/terraform/communicator/shared" "github.com/hashicorp/terraform/communicator/shared"
sshagent "github.com/xanzy/ssh-agent" sshagent "github.com/xanzy/ssh-agent"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent" "golang.org/x/crypto/ssh/agent"
"golang.org/x/crypto/ssh/knownhosts" "golang.org/x/crypto/ssh/knownhosts"
@ -55,7 +55,7 @@ type connectionInfo struct {
Certificate string Certificate string
Host string Host string
HostKey string HostKey string
Port int Port uint16
Agent bool Agent bool
ScriptPath string ScriptPath string
TargetPlatform string TargetPlatform string
@ -68,7 +68,7 @@ type connectionInfo struct {
BastionCertificate string BastionCertificate string
BastionHost string BastionHost string
BastionHostKey string BastionHostKey string
BastionPort int BastionPort uint16
AgentIdentity string AgentIdentity string
} }
@ -101,11 +101,9 @@ func decodeConnInfo(v cty.Value) (*connectionInfo, error) {
case "host_key": case "host_key":
connInfo.HostKey = v.AsString() connInfo.HostKey = v.AsString()
case "port": case "port":
p, err := strconv.Atoi(v.AsString()) if err := gocty.FromCtyValue(v, &connInfo.Port); err != nil {
if err != nil {
return nil, err return nil, err
} }
connInfo.Port = p
case "agent": case "agent":
connInfo.Agent = v.True() connInfo.Agent = v.True()
case "script_path": case "script_path":
@ -127,11 +125,9 @@ func decodeConnInfo(v cty.Value) (*connectionInfo, error) {
case "bastion_host_key": case "bastion_host_key":
connInfo.BastionHostKey = v.AsString() connInfo.BastionHostKey = v.AsString()
case "bastion_port": case "bastion_port":
p, err := strconv.Atoi(v.AsString()) if err := gocty.FromCtyValue(v, &connInfo.BastionPort); err != nil {
if err != nil {
return nil, err return nil, err
} }
connInfo.BastionPort = p
case "agent_identity": case "agent_identity":
connInfo.AgentIdentity = v.AsString() connInfo.AgentIdentity = v.AsString()
} }

View File

@ -17,6 +17,7 @@ func TestProvisioner_connInfo(t *testing.T) {
"port": cty.StringVal("22"), "port": cty.StringVal("22"),
"timeout": cty.StringVal("30s"), "timeout": cty.StringVal("30s"),
"bastion_host": cty.StringVal("127.0.1.1"), "bastion_host": cty.StringVal("127.0.1.1"),
"bastion_port": cty.NumberIntVal(20022),
}) })
conf, err := parseConnectionInfo(v) conf, err := parseConnectionInfo(v)
@ -54,7 +55,7 @@ func TestProvisioner_connInfo(t *testing.T) {
if conf.BastionHost != "127.0.1.1" { if conf.BastionHost != "127.0.1.1" {
t.Fatalf("bad: %v", conf) t.Fatalf("bad: %v", conf)
} }
if conf.BastionPort != 22 { if conf.BastionPort != 20022 {
t.Fatalf("bad: %v", conf) t.Fatalf("bad: %v", conf)
} }
if conf.BastionUser != "root" { if conf.BastionUser != "root" {
@ -135,3 +136,45 @@ func TestProvisioner_connInfoEmptyHostname(t *testing.T) {
t.Fatalf("bad: should not allow empty host") t.Fatalf("bad: should not allow empty host")
} }
} }
func TestProvisioner_stringBastionPort(t *testing.T) {
v := cty.ObjectVal(map[string]cty.Value{
"type": cty.StringVal("ssh"),
"user": cty.StringVal("root"),
"password": cty.StringVal("supersecret"),
"private_key": cty.StringVal("someprivatekeycontents"),
"host": cty.StringVal("example.com"),
"port": cty.StringVal("22"),
"timeout": cty.StringVal("30s"),
"bastion_host": cty.StringVal("example.com"),
"bastion_port": cty.StringVal("12345"),
})
conf, err := parseConnectionInfo(v)
if err != nil {
t.Fatalf("err: %v", err)
}
if conf.BastionPort != 12345 {
t.Fatalf("bad %v", conf)
}
}
func TestProvisioner_invalidPortNumber(t *testing.T) {
v := cty.ObjectVal(map[string]cty.Value{
"type": cty.StringVal("ssh"),
"user": cty.StringVal("root"),
"password": cty.StringVal("supersecret"),
"private_key": cty.StringVal("someprivatekeycontents"),
"host": cty.StringVal("example.com"),
"port": cty.NumberIntVal(123456789),
})
_, err := parseConnectionInfo(v)
if err == nil {
t.Fatalf("bad: should not allow invalid port number")
}
if got, want := err.Error(), "value must be a whole number, between 0 and 65535 inclusive"; got != want {
t.Errorf("unexpected error\n got: %s\nwant: %s", got, want)
}
}

View File

@ -33,7 +33,7 @@ func New(v cty.Value) (*Communicator, error) {
endpoint := &winrm.Endpoint{ endpoint := &winrm.Endpoint{
Host: connInfo.Host, Host: connInfo.Host,
Port: connInfo.Port, Port: int(connInfo.Port),
HTTPS: connInfo.HTTPS, HTTPS: connInfo.HTTPS,
Insecure: connInfo.Insecure, Insecure: connInfo.Insecure,
Timeout: connInfo.TimeoutVal, Timeout: connInfo.TimeoutVal,

View File

@ -4,12 +4,12 @@ import (
"fmt" "fmt"
"log" "log"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
"github.com/hashicorp/terraform/communicator/shared" "github.com/hashicorp/terraform/communicator/shared"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
) )
const ( const (
@ -37,7 +37,7 @@ type connectionInfo struct {
User string User string
Password string Password string
Host string Host string
Port int Port uint16
HTTPS bool HTTPS bool
Insecure bool Insecure bool
NTLM bool `mapstructure:"use_ntlm"` NTLM bool `mapstructure:"use_ntlm"`
@ -69,11 +69,9 @@ func decodeConnInfo(v cty.Value) (*connectionInfo, error) {
case "host": case "host":
connInfo.Host = v.AsString() connInfo.Host = v.AsString()
case "port": case "port":
p, err := strconv.Atoi(v.AsString()) if err := gocty.FromCtyValue(v, &connInfo.Port); err != nil {
if err != nil {
return nil, err return nil, err
} }
connInfo.Port = p
case "https": case "https":
connInfo.HTTPS = v.True() connInfo.HTTPS = v.True()
case "insecure": case "insecure":

View File

@ -177,7 +177,7 @@ var connectionBlockSupersetSchema = &configschema.Block{
Optional: true, Optional: true,
}, },
"port": { "port": {
Type: cty.String, Type: cty.Number,
Optional: true, Optional: true,
}, },
"timeout": { "timeout": {

View File

@ -31,6 +31,7 @@ func TestNodeValidatableResource_ValidateProvisioner_valid(t *testing.T) {
Config: configs.SynthBody("", map[string]cty.Value{ Config: configs.SynthBody("", map[string]cty.Value{
"host": cty.StringVal("localhost"), "host": cty.StringVal("localhost"),
"type": cty.StringVal("ssh"), "type": cty.StringVal("ssh"),
"port": cty.NumberIntVal(10022),
}), }),
}, },
} }
@ -104,7 +105,7 @@ func TestNodeValidatableResource_ValidateProvisioner__warning(t *testing.T) {
} }
} }
func TestNodeValidatableResource_ValidateProvisioner__conntectionInvalid(t *testing.T) { func TestNodeValidatableResource_ValidateProvisioner__connectionInvalid(t *testing.T) {
ctx := &MockEvalContext{} ctx := &MockEvalContext{}
ctx.installSimpleEval() ctx.installSimpleEval()
mp := &MockProvisioner{} mp := &MockProvisioner{}