From 3dd46eb5f496403e30706dfd27788b578ae8506c Mon Sep 17 00:00:00 2001 From: Wilken Rivera Date: Thu, 12 Mar 2020 22:26:38 -0400 Subject: [PATCH] Add PoC of connectivity using session-manager-plugin * Add a bunch of hard coded values for testing on port 8081; ssh configs are set to localhost and 8081 * Add a base drive for communicating with the session manager plugin * Update step for creating tunnel to actually create SSM session tunnel via driver --- builder/amazon/common/ssm_driver.go | 50 +++++++++++++ .../amazon/common/step_create_ssm_tunnel.go | 73 ++++++++++++++++++- builder/amazon/ebs/builder.go | 6 ++ 3 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 builder/amazon/common/ssm_driver.go diff --git a/builder/amazon/common/ssm_driver.go b/builder/amazon/common/ssm_driver.go new file mode 100644 index 000000000..270cc525f --- /dev/null +++ b/builder/amazon/common/ssm_driver.go @@ -0,0 +1,50 @@ +package common + +import ( + "bytes" + "fmt" + "log" + "os/exec" + "sync" + + "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/template/interpolate" +) + +type SSMDriver struct { + Ui packer.Ui + Ctx *interpolate.Context + + l sync.Mutex +} + +// sessJson, region, "StartSession", profile, paramJson, endpoint +func (s *SSMDriver) StartSession(sessionData, region, profile, params, endpoint string) error { + var stdout bytes.Buffer + var stderr bytes.Buffer + + args := []string{ + sessionData, + region, + "StartSession", + profile, + params, + endpoint, + } + + // Remove log statement + log.Printf("Attempting to start session with the following args: %v", args) + cmd := exec.Command("session-manager-plugin", args...) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + if err := cmd.Start(); err != nil { + err = fmt.Errorf("Error committing container: %s\nStderr: %s", err, stderr.String()) + s.Ui.Error(err.Error()) + return err + } + log.Println(stdout.String()) + log.Println(stderr.String()) + + return nil +} diff --git a/builder/amazon/common/step_create_ssm_tunnel.go b/builder/amazon/common/step_create_ssm_tunnel.go index 1eb8721e0..9cdc754a5 100644 --- a/builder/amazon/common/step_create_ssm_tunnel.go +++ b/builder/amazon/common/step_create_ssm_tunnel.go @@ -2,11 +2,17 @@ package common import ( "context" + "encoding/json" "fmt" + "net" + "strconv" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ssm" + "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -20,31 +26,91 @@ type StepCreateSSMTunnel struct { SrcPort string ssmSession *ssm.StartSessionOutput + tunnel net.Listener } func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) + /* + p, _ := strconv.Atoi(s.SrcPort) + //TODO dynamically setup local port + // Find an available TCP port for our HTTP server + l, err := packernet.ListenRangeConfig{ + Min: p, + Max: p, + Addr: "0.0.0.0", + Network: "tcp", + }.Listen(ctx) + if err != nil { + 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(s.SrcPort)}, + "localPortNumber": []*string{aws.String(strconv.Itoa(8081))}, } + instance, ok := state.Get("instance").(*ec2.Instance) + if !ok { + err := fmt.Errorf("error encountered in obtaining target instance id for SSM tunnel") + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + + s.InstanceID = aws.StringValue(instance.InstanceId) + ssmconn := ssm.New(s.AWSSession) input := ssm.StartSessionInput{ DocumentName: aws.String("AWS-StartPortForwardingSession"), Parameters: params, Target: aws.String(s.InstanceID), } + var output *ssm.StartSessionOutput + var err error + err = retry.Config{ + Tries: 11, + ShouldRetry: func(err error) bool { return isAWSErr(err, "TargetNotConnected", "") }, + RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear, + }.Run(ctx, func(ctx context.Context) error { + output, err = ssmconn.StartSessionWithContext(ctx, &input) + return err + }) - output, err := ssmconn.StartSession(&input) if err != nil { - err = fmt.Errorf("error encountered in creating a connection to the SSM agent: %s", err) + err = fmt.Errorf("error encountered in starting session: %s", err) ui.Error(err.Error()) state.Put("error", err) return multistep.ActionHalt } s.ssmSession = output + sessJson, err := json.Marshal(s.ssmSession) + if err != nil { + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + + paramsJson, err := json.Marshal(input) + if err != nil { + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + + driver := SSMDriver{Ui: ui} + // sessJson, region, "StartSession", profile, paramJson, endpoint + if err := driver.StartSession(string(sessJson), "us-east-1", "packer", string(paramsJson), ssmconn.Endpoint); err != nil { + err = fmt.Errorf("error encountered in creating a connection to the SSM agent: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + return multistep.ActionContinue } @@ -61,4 +127,5 @@ func (s *StepCreateSSMTunnel) Cleanup(state multistep.StateBag) { aws.StringValue(s.ssmSession.SessionId), err) ui.Error(msg) } + } diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index a268ea022..859ecd4f4 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -11,6 +11,7 @@ package ebs import ( "context" "fmt" + "strconv" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" @@ -255,6 +256,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack Timeout: b.config.WindowsPasswordTimeout, BuildName: b.config.PackerBuildName, }, + &awscommon.StepCreateSSMTunnel{ + AWSSession: session, + DstPort: strconv.Itoa(22), + SrcPort: "8081", + }, &communicator.StepConnect{ Config: &b.config.RunConfig.Comm, Host: awscommon.SSHHost(