Skip to content

Instantly share code, notes, and snippets.

@mix3

mix3/recv.go Secret

Last active December 9, 2021 07:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mix3/e3690a5124c576d34209fcad9e9f31b6 to your computer and use it in GitHub Desktop.
Save mix3/e3690a5124c576d34209fcad9e9f31b6 to your computer and use it in GitHub Desktop.
ECS Exec と scp で送受信
package main
import (
"bufio"
"context"
"encoding/json"
"flag"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"syscall"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ecs"
)
const SessionManagerPluginBinary = "session-manager-plugin"
func main() {
if err := run(context.Background()); err != nil {
log.Fatal(err)
}
}
func run(ctx context.Context) error {
var (
cluster = flag.String("cluster", "", "cluster")
containerName = flag.String("container-name", "", "container name")
taskID = flag.String("task-id", "", "task id")
)
flag.Parse()
if flag.NArg() != 1 {
fmt.Printf("Usage: %s [options] [TARGET_FILE]\n", os.Args[0])
flag.PrintDefaults()
return nil
}
conf, err := config.LoadDefaultConfig(ctx)
if err != nil {
return err
}
svc := ecs.NewFromConfig(conf)
task, err := getTask(ctx, svc, *cluster, *containerName, *taskID)
if err != nil {
return err
}
out, err := svc.ExecuteCommand(ctx, &ecs.ExecuteCommandInput{
Cluster: aws.String(task.clusterArn),
Interactive: true,
Task: aws.String(task.taskArn),
Command: aws.String(fmt.Sprintf(`scpf -q %s`, flag.Arg(0))),
Container: aws.String(*containerName),
})
if err != nil {
return err
}
sess, _ := json.Marshal(out.Session)
cmd := exec.Command(SessionManagerPluginBinary, string(sess), conf.Region, "StartSession", "", task.targetJson, fmt.Sprintf("ecs.%s.amazonaws.com", conf.Region))
r, _ := cmd.StdoutPipe()
defer r.Close()
signal.Ignore(os.Interrupt, syscall.SIGPIPE)
defer signal.Reset(os.Interrupt, syscall.SIGPIPE)
cmd.Start()
// なぜか 改行が \r\n になってやってくるので bufio.Scanner で \n にしつつ読む
scanner := bufio.NewScanner(r)
// session-manager-plugin が標準出力でログ吐くので読み飛ばす
// 標準出力を読んでゴニョるツールを作るのに邪魔なので session-manager-plugin のログは標準エラーにしないか?というPRは作られているが無視されていて悲しい
// https://github.com/aws/session-manager-plugin/pull/20
scanner.Scan() // \n
scanner.Scan() // Starting session with SessionId: ecs-execute-command-XXXXXXXXXXXXXXXXX
for scanner.Scan() {
fmt.Println(scanner.Text())
}
return cmd.Wait()
}
type task struct {
clusterArn string
taskArn string
target string
targetJson string
}
func getTask(ctx context.Context, svc *ecs.Client, cluster, containerName, taskID string) (*task, error) {
res, err := svc.DescribeTasks(ctx, &ecs.DescribeTasksInput{
Cluster: aws.String(cluster),
Tasks: []string{taskID},
})
if err != nil {
return nil, fmt.Errorf("error describe tasks: %w", err)
}
var clusterArn, taskArn, runtimeID *string
for _, t := range res.Tasks {
for _, c := range t.Containers {
if aws.ToString(c.Name) == containerName {
runtimeID = c.RuntimeId
taskArn = c.TaskArn
clusterArn = t.ClusterArn
}
}
}
if runtimeID == nil {
return nil, fmt.Errorf("Could not identify runtimeId")
}
target := fmt.Sprintf("ecs:%s_%s_%s", cluster, taskID, aws.ToString(runtimeID))
targetJson, _ := json.Marshal(struct {
Target string
}{
Target: target,
})
return &task{
clusterArn: aws.ToString(clusterArn),
taskArn: aws.ToString(taskArn),
target: target,
targetJson: string(targetJson),
}, nil
}
package main
import (
"fmt"
"log"
"os"
"os/exec"
"strings"
)
func main() {
args := append([]string{"-rf"}, os.Args[1:]...)
cmd := exec.Command("scp", args...)
w, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
fmt.Fprint(w, strings.Repeat("\x00", 7))
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
}
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ecs"
)
const SessionManagerPluginBinary = "session-manager-plugin"
func main() {
if err := run(context.Background()); err != nil {
log.Fatal(err)
}
}
func run(ctx context.Context) error {
var (
cluster = flag.String("cluster", "", "cluster")
containerName = flag.String("container-name", "", "container name")
taskID = flag.String("task-id", "", "task id")
)
flag.Parse()
if flag.NArg() != 2 {
fmt.Printf("Usage: %s [options] [SRC] [DST]\n", os.Args[0])
flag.PrintDefaults()
return nil
}
stat, err := os.Stat(flag.Arg(0))
if err != nil {
return err
}
f, err := os.ReadFile(flag.Arg(0))
if err != nil {
return err
}
conf, err := config.LoadDefaultConfig(ctx)
if err != nil {
return err
}
svc := ecs.NewFromConfig(conf)
task, err := getTask(ctx, svc, *cluster, *containerName, *taskID)
if err != nil {
return err
}
out, err := svc.ExecuteCommand(ctx, &ecs.ExecuteCommandInput{
Cluster: aws.String(task.clusterArn),
Interactive: true,
Task: aws.String(task.taskArn),
Command: aws.String(fmt.Sprintf(`scp -qrt %s`, flag.Arg(1))),
Container: aws.String(*containerName),
})
if err != nil {
return err
}
sess, _ := json.Marshal(out.Session)
cmd := exec.Command(SessionManagerPluginBinary, string(sess), conf.Region, "StartSession", "", task.targetJson, fmt.Sprintf("ecs.%s.amazonaws.com", conf.Region))
w, err := cmd.StdinPipe()
if err != nil {
return err
}
signal.Ignore(os.Interrupt)
defer signal.Reset(os.Interrupt)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Start()
fmt.Fprintln(w, fmt.Sprintf("C%04o", stat.Mode()), len(f), stat.Name())
w.Write(f)
fmt.Fprint(w, "\x00")
fmt.Fprint(w, "\n") // -t で待ち受けてる scp を直接 kill することが出来ないので改行ぶち込んで自死してもらう
return cmd.Wait()
}
type task struct {
clusterArn string
taskArn string
target string
targetJson string
}
func getTask(ctx context.Context, svc *ecs.Client, cluster, containerName, taskID string) (*task, error) {
res, err := svc.DescribeTasks(ctx, &ecs.DescribeTasksInput{
Cluster: aws.String(cluster),
Tasks: []string{taskID},
})
if err != nil {
return nil, fmt.Errorf("error describe tasks: %w", err)
}
var clusterArn, taskArn, runtimeID *string
for _, t := range res.Tasks {
for _, c := range t.Containers {
if aws.ToString(c.Name) == containerName {
runtimeID = c.RuntimeId
taskArn = c.TaskArn
clusterArn = t.ClusterArn
}
}
}
if runtimeID == nil {
return nil, fmt.Errorf("Could not identify runtimeId")
}
target := fmt.Sprintf("ecs:%s_%s_%s", cluster, taskID, aws.ToString(runtimeID))
targetJson, _ := json.Marshal(struct {
Target string
}{
Target: target,
})
return &task{
clusterArn: aws.ToString(clusterArn),
taskArn: aws.ToString(taskArn),
target: target,
targetJson: string(targetJson),
}, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment