builder/amazon: Update Session Manger connectivity
* Update security group creation step skip ingress rules if using session manager * Update create ssm tunnel step to dynamically set a session port * Add SSHPort function to common to return session-manager tunnel port * Update SSHHost to return proper host for session-manager
This commit is contained in:
parent
3dd46eb5f4
commit
520061dee6
|
@ -441,6 +441,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||||
c.SSHInterface != "private_ip" &&
|
c.SSHInterface != "private_ip" &&
|
||||||
c.SSHInterface != "public_dns" &&
|
c.SSHInterface != "public_dns" &&
|
||||||
c.SSHInterface != "private_dns" &&
|
c.SSHInterface != "private_dns" &&
|
||||||
|
c.SSHInterface != "session_manager" &&
|
||||||
c.SSHInterface != "" {
|
c.SSHInterface != "" {
|
||||||
errs = append(errs, fmt.Errorf("Unknown interface type: %s", c.SSHInterface))
|
errs = append(errs, fmt.Errorf("Unknown interface type: %s", c.SSHInterface))
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ func SSHHost(e ec2Describer, sshInterface string, host string) func(multistep.St
|
||||||
return host, nil
|
return host, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sshInterface == "session_manager" {
|
||||||
|
return "127.0.0.1", nil
|
||||||
|
}
|
||||||
|
|
||||||
const tries = 2
|
const tries = 2
|
||||||
// <= with current structure to check result of describing `tries` times
|
// <= with current structure to check result of describing `tries` times
|
||||||
for j := 0; j <= tries; j++ {
|
for j := 0; j <= tries; j++ {
|
||||||
|
@ -85,3 +89,20 @@ func SSHHost(e ec2Describer, sshInterface string, host string) func(multistep.St
|
||||||
return "", errors.New("couldn't determine address for instance")
|
return "", errors.New("couldn't determine address for instance")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSHPort returns a function that can be given to the SSH communicator
|
||||||
|
// for determining the SSH port to use when connecting to an instance.
|
||||||
|
func SSHPort(sshInterface string, port int) func(multistep.StateBag) (int, error) {
|
||||||
|
return func(state multistep.StateBag) (int, error) {
|
||||||
|
if sshInterface != "session_manager" {
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
port, ok := state.GetOk("sessionPort")
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("no local port defined for session-manager")
|
||||||
|
}
|
||||||
|
return port.(int), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -12,6 +11,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/ssm"
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
|
"github.com/hashicorp/packer/common/net"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
@ -22,37 +22,34 @@ type StepCreateSSMTunnel struct {
|
||||||
CommConfig *communicator.Config
|
CommConfig *communicator.Config
|
||||||
AWSSession *session.Session
|
AWSSession *session.Session
|
||||||
InstanceID string
|
InstanceID string
|
||||||
DstPort string
|
DstPort int
|
||||||
SrcPort string
|
|
||||||
|
|
||||||
ssmSession *ssm.StartSessionOutput
|
ssmSession *ssm.StartSessionOutput
|
||||||
tunnel net.Listener
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
/*
|
// Find an available TCP port for our HTTP server
|
||||||
p, _ := strconv.Atoi(s.SrcPort)
|
l, err := net.ListenRangeConfig{
|
||||||
//TODO dynamically setup local port
|
Min: 8000,
|
||||||
// Find an available TCP port for our HTTP server
|
Max: 9000,
|
||||||
l, err := packernet.ListenRangeConfig{
|
Addr: "0.0.0.0",
|
||||||
Min: p,
|
Network: "tcp",
|
||||||
Max: p,
|
}.Listen(ctx)
|
||||||
Addr: "0.0.0.0",
|
if err != nil {
|
||||||
Network: "tcp",
|
err := fmt.Errorf("error finding an available port to initiate a session tunnel: %s", err)
|
||||||
}.Listen(ctx)
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
if err != nil {
|
return multistep.ActionHalt
|
||||||
err := fmt.Errorf("Error finding port: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
params := map[string][]*string{
|
|
||||||
"portNumber": []*string{aws.String(s.DstPort)},
|
|
||||||
"localPortNumber": []*string{aws.String(strconv.Itoa(8081))},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dst, src := strconv.Itoa(s.DstPort), strconv.Itoa(l.Port)
|
||||||
|
params := map[string][]*string{
|
||||||
|
"portNumber": []*string{aws.String(dst)},
|
||||||
|
"localPortNumber": []*string{aws.String(src)},
|
||||||
|
}
|
||||||
|
l.Close()
|
||||||
|
|
||||||
instance, ok := state.Get("instance").(*ec2.Instance)
|
instance, ok := state.Get("instance").(*ec2.Instance)
|
||||||
if !ok {
|
if !ok {
|
||||||
err := fmt.Errorf("error encountered in obtaining target instance id for SSM tunnel")
|
err := fmt.Errorf("error encountered in obtaining target instance id for SSM tunnel")
|
||||||
|
@ -62,15 +59,14 @@ func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.InstanceID = aws.StringValue(instance.InstanceId)
|
s.InstanceID = aws.StringValue(instance.InstanceId)
|
||||||
|
|
||||||
ssmconn := ssm.New(s.AWSSession)
|
ssmconn := ssm.New(s.AWSSession)
|
||||||
input := ssm.StartSessionInput{
|
input := ssm.StartSessionInput{
|
||||||
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
||||||
Parameters: params,
|
Parameters: params,
|
||||||
Target: aws.String(s.InstanceID),
|
Target: aws.String(s.InstanceID),
|
||||||
}
|
}
|
||||||
|
|
||||||
var output *ssm.StartSessionOutput
|
var output *ssm.StartSessionOutput
|
||||||
var err error
|
|
||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 11,
|
Tries: 11,
|
||||||
ShouldRetry: func(err error) bool { return isAWSErr(err, "TargetNotConnected", "") },
|
ShouldRetry: func(err error) bool { return isAWSErr(err, "TargetNotConnected", "") },
|
||||||
|
@ -81,36 +77,41 @@ func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag)
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("error encountered in starting session: %s", err)
|
err = fmt.Errorf("error encountered in starting session for instance %q: %s", s.InstanceID, err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
s.ssmSession = output
|
s.ssmSession = output
|
||||||
|
|
||||||
sessJson, err := json.Marshal(s.ssmSession)
|
// AWS session-manager-plugin requires a valid session be passed in JSON
|
||||||
|
sessionDetails, err := json.Marshal(s.ssmSession)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
state.Put("error", err)
|
state.Put("error encountered in reading session details", err)
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
paramsJson, err := json.Marshal(input)
|
sessionParameters, err := json.Marshal(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
state.Put("error", err)
|
state.Put("error encountered in reading session parameter details", err)
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
driver := SSMDriver{Ui: ui}
|
driver := SSMDriver{Ui: ui}
|
||||||
// sessJson, region, "StartSession", profile, paramJson, endpoint
|
// sessionDetails, region, "StartSession", profile, paramJson, endpoint
|
||||||
if err := driver.StartSession(string(sessJson), "us-east-1", "packer", string(paramsJson), ssmconn.Endpoint); err != nil {
|
region := aws.StringValue(s.AWSSession.Config.Region)
|
||||||
err = fmt.Errorf("error encountered in creating a connection to the SSM agent: %s", err)
|
// how to best get Profile name
|
||||||
|
if err := driver.StartSession(string(sessionDetails), region, "default", string(sessionParameters), ssmconn.Endpoint); err != nil {
|
||||||
|
err = fmt.Errorf("error encountered in establishing a tunnel with the session-manager-plugin: %s", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.Put("sessionPort", l.Port)
|
||||||
|
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ type StepSecurityGroup struct {
|
||||||
SecurityGroupFilter SecurityGroupFilterOptions
|
SecurityGroupFilter SecurityGroupFilterOptions
|
||||||
SecurityGroupIds []string
|
SecurityGroupIds []string
|
||||||
TemporarySGSourceCidrs []string
|
TemporarySGSourceCidrs []string
|
||||||
SkipSSHGroupCreation bool
|
SkipSSHRuleCreation bool
|
||||||
|
|
||||||
createdGroupId string
|
createdGroupId string
|
||||||
}
|
}
|
||||||
|
@ -77,10 +77,7 @@ func (s *StepSecurityGroup) Run(ctx context.Context, state multistep.StateBag) m
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.SkipSSHGroupCreation {
|
// TODO move to some prevalidation step for
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
port := s.CommConfig.Port()
|
port := s.CommConfig.Port()
|
||||||
if port == 0 {
|
if port == 0 {
|
||||||
if s.CommConfig.Type != "none" {
|
if s.CommConfig.Type != "none" {
|
||||||
|
@ -115,15 +112,15 @@ func (s *StepSecurityGroup) Run(ctx context.Context, state multistep.StateBag) m
|
||||||
GroupIds: []*string{aws.String(s.createdGroupId)},
|
GroupIds: []*string{aws.String(s.createdGroupId)},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
log.Printf("[DEBUG] Found security group %s", s.createdGroupId)
|
|
||||||
} else {
|
|
||||||
err := fmt.Errorf("Timed out waiting for security group %s: %s", s.createdGroupId, err)
|
err := fmt.Errorf("Timed out waiting for security group %s: %s", s.createdGroupId, err)
|
||||||
log.Printf("[DEBUG] %s", err.Error())
|
log.Printf("[DEBUG] %s", err.Error())
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Found security group %s", s.createdGroupId)
|
||||||
|
|
||||||
// map the list of temporary security group CIDRs bundled with config to
|
// map the list of temporary security group CIDRs bundled with config to
|
||||||
// types expected by EC2.
|
// types expected by EC2.
|
||||||
groupIpRanges := []*ec2.IpRange{}
|
groupIpRanges := []*ec2.IpRange{}
|
||||||
|
@ -134,6 +131,13 @@ func (s *StepSecurityGroup) Run(ctx context.Context, state multistep.StateBag) m
|
||||||
groupIpRanges = append(groupIpRanges, &ipRange)
|
groupIpRanges = append(groupIpRanges, &ipRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set some state data for use in future steps
|
||||||
|
state.Put("securityGroupIds", []string{s.createdGroupId})
|
||||||
|
|
||||||
|
if s.SkipSSHRuleCreation {
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
// Authorize the SSH access for the security group
|
// Authorize the SSH access for the security group
|
||||||
groupRules := &ec2.AuthorizeSecurityGroupIngressInput{
|
groupRules := &ec2.AuthorizeSecurityGroupIngressInput{
|
||||||
GroupId: groupResp.GroupId,
|
GroupId: groupResp.GroupId,
|
||||||
|
@ -159,9 +163,6 @@ func (s *StepSecurityGroup) Run(ctx context.Context, state multistep.StateBag) m
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set some state data for use in future steps
|
|
||||||
state.Put("securityGroupIds", []string{s.createdGroupId})
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ package ebs
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
|
@ -240,6 +239,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||||
SecurityGroupIds: b.config.SecurityGroupIds,
|
SecurityGroupIds: b.config.SecurityGroupIds,
|
||||||
CommConfig: &b.config.RunConfig.Comm,
|
CommConfig: &b.config.RunConfig.Comm,
|
||||||
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
TemporarySGSourceCidrs: b.config.TemporarySGSourceCidrs,
|
||||||
|
SkipSSHRuleCreation: b.config.SSHInterface == "session_manager",
|
||||||
},
|
},
|
||||||
&awscommon.StepIamInstanceProfile{
|
&awscommon.StepIamInstanceProfile{
|
||||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
|
@ -258,8 +258,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||||
},
|
},
|
||||||
&awscommon.StepCreateSSMTunnel{
|
&awscommon.StepCreateSSMTunnel{
|
||||||
AWSSession: session,
|
AWSSession: session,
|
||||||
DstPort: strconv.Itoa(22),
|
DstPort: b.config.Comm.SSHPort,
|
||||||
SrcPort: "8081",
|
|
||||||
},
|
},
|
||||||
&communicator.StepConnect{
|
&communicator.StepConnect{
|
||||||
Config: &b.config.RunConfig.Comm,
|
Config: &b.config.RunConfig.Comm,
|
||||||
|
@ -268,6 +267,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||||
b.config.SSHInterface,
|
b.config.SSHInterface,
|
||||||
b.config.Comm.Host(),
|
b.config.Comm.Host(),
|
||||||
),
|
),
|
||||||
|
SSHPort: awscommon.SSHPort(
|
||||||
|
b.config.SSHInterface,
|
||||||
|
b.config.Comm.Port(),
|
||||||
|
),
|
||||||
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
|
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
|
||||||
},
|
},
|
||||||
&common.StepProvision{},
|
&common.StepProvision{},
|
||||||
|
|
Loading…
Reference in New Issue