2020-03-12 22:26:38 -04:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
2020-04-24 09:05:13 -04:00
|
|
|
"context"
|
2020-04-29 14:58:36 -04:00
|
|
|
"encoding/json"
|
2020-03-12 22:26:38 -04:00
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os/exec"
|
|
|
|
|
2020-05-06 15:21:08 -04:00
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
2020-04-29 14:58:36 -04:00
|
|
|
"github.com/aws/aws-sdk-go/service/ssm"
|
2020-05-06 15:21:08 -04:00
|
|
|
"github.com/mitchellh/iochan"
|
2020-03-12 22:26:38 -04:00
|
|
|
)
|
|
|
|
|
2020-04-29 14:58:36 -04:00
|
|
|
const sessionManagerPluginName string = "session-manager-plugin"
|
|
|
|
|
|
|
|
//sessionCommand is the AWS-SDK equivalent to the command you would specify to `aws ssm ...`
|
|
|
|
const sessionCommand string = "StartSession"
|
2020-03-12 22:26:38 -04:00
|
|
|
|
2020-04-22 07:52:47 -04:00
|
|
|
type SSMDriver struct {
|
2020-04-29 14:58:36 -04:00
|
|
|
Region string
|
|
|
|
ProfileName string
|
|
|
|
Session *ssm.StartSessionOutput
|
|
|
|
SessionParams ssm.StartSessionInput
|
|
|
|
SessionEndpoint string
|
2020-05-06 15:21:08 -04:00
|
|
|
PluginName string
|
2020-03-12 22:26:38 -04:00
|
|
|
}
|
|
|
|
|
2020-04-22 07:52:47 -04:00
|
|
|
// StartSession starts an interactive Systems Manager session with a remote instance via the AWS session-manager-plugin
|
2020-05-06 15:21:08 -04:00
|
|
|
func (d *SSMDriver) StartSession(ctx context.Context) error {
|
|
|
|
if d.PluginName == "" {
|
|
|
|
d.PluginName = sessionManagerPluginName
|
2020-04-22 07:52:47 -04:00
|
|
|
}
|
|
|
|
|
2020-05-06 15:21:08 -04:00
|
|
|
args, err := d.Args()
|
2020-04-29 14:58:36 -04:00
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("error encountered validating session details: %s", err)
|
2020-04-22 07:52:47 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-05-06 15:21:08 -04:00
|
|
|
cmd := exec.CommandContext(ctx, d.PluginName, args...)
|
2020-03-12 22:26:38 -04:00
|
|
|
|
2020-05-06 15:21:08 -04:00
|
|
|
// Let's build up our logging
|
|
|
|
stdout, err := cmd.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
stderr, err := cmd.StderrPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the channels we'll use for data
|
|
|
|
stdoutCh := iochan.DelimReader(stdout, '\n')
|
|
|
|
stderrCh := iochan.DelimReader(stderr, '\n')
|
|
|
|
|
|
|
|
// Loop and get all our output
|
|
|
|
go func(ctx context.Context, prefix string) {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
case output := <-stderrCh:
|
|
|
|
if output != "" {
|
|
|
|
log.Printf("[ERROR] %s: %s", prefix, output)
|
|
|
|
}
|
|
|
|
case output := <-stdoutCh:
|
|
|
|
if output != "" {
|
|
|
|
log.Printf("[DEBUG] %s: %s", prefix, output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(ctx, d.PluginName)
|
|
|
|
|
|
|
|
log.Printf("[DEBUG %s] opening session tunnel to instance %q for session %q", d.PluginName, aws.StringValue(d.SessionParams.Target), aws.StringValue(d.Session.SessionId))
|
2020-03-12 22:26:38 -04:00
|
|
|
if err := cmd.Start(); err != nil {
|
2020-05-06 15:21:08 -04:00
|
|
|
err = fmt.Errorf("error encountered when calling %s: %s\n", d.PluginName, err)
|
2020-03-12 22:26:38 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2020-05-06 15:21:08 -04:00
|
|
|
|
|
|
|
func (d *SSMDriver) Args() ([]string, error) {
|
|
|
|
if d.Session == nil {
|
2020-04-29 14:58:36 -04:00
|
|
|
return nil, fmt.Errorf("an active Amazon SSM Session is required before trying to open a session tunnel")
|
|
|
|
}
|
|
|
|
|
|
|
|
// AWS session-manager-plugin requires a valid session be passed in JSON.
|
2020-05-06 15:21:08 -04:00
|
|
|
sessionDetails, err := json.Marshal(d.Session)
|
2020-04-29 14:58:36 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error encountered in reading session details %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AWS session-manager-plugin requires the parameters used in the session to be passed in JSON as well.
|
2020-05-06 15:21:08 -04:00
|
|
|
sessionParameters, err := json.Marshal(d.SessionParams)
|
2020-04-29 14:58:36 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error encountered in reading session parameter details %s", err)
|
|
|
|
}
|
|
|
|
|
2020-05-06 15:21:08 -04:00
|
|
|
// Args must be in this order
|
2020-04-29 14:58:36 -04:00
|
|
|
args := []string{
|
|
|
|
string(sessionDetails),
|
2020-05-06 15:21:08 -04:00
|
|
|
d.Region,
|
2020-04-29 14:58:36 -04:00
|
|
|
sessionCommand,
|
2020-05-06 15:21:08 -04:00
|
|
|
d.ProfileName,
|
2020-04-29 14:58:36 -04:00
|
|
|
string(sessionParameters),
|
2020-05-06 15:21:08 -04:00
|
|
|
d.SessionEndpoint,
|
2020-04-29 14:58:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return args, nil
|
|
|
|
}
|