diff --git a/.gitignore b/.gitignore index effc9c31b..13b0e9250 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -entrypoint/out/ vendor/ *~ *# diff --git a/Makefile b/Makefile index a887c4029..f4f696b81 100644 --- a/Makefile +++ b/Makefile @@ -11,18 +11,18 @@ rwildcard = $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subs GOPATH ?= $(HOME)/go BIN = $(GOPATH)/bin/pathwar.pw SOURCES = $(call rwildcard, ./, *.go) -ENTRYPOINT_SOURCES = ./entrypoint/main.go +PWCTL_SOURCES = $(call rwildcard,pwctl//,*.go) OUR_SOURCES = $(filter-out $(call rwildcard,vendor//,*.go),$(SOURCES)) PROTOS = $(call rwildcard, ./, *.proto) OUR_PROTOS = $(filter-out $(call rwildcard,vendor//,*.proto),$(PROTOS)) GENERATED_PB_FILES = \ $(patsubst %.proto,%.pb.go,$(PROTOS)) \ $(call rwildcard ./, *.gen.go) -ENTRYPOINT_OUT_FILES = \ - ./entrypoint/out/entrypoint-linux-amd64 +PWCTL_OUT_FILES = \ + ./pwctl/out/pwctl-linux-amd64 GENERATED_FILES = \ $(GENERATED_PB_FILES) \ - $(ENTRYPOINT_OUT_FILES) + $(PWCTL_OUT_FILES) PROTOC_OPTS = -I/protobuf:vendor:. RUN_OPTS ?= @@ -44,7 +44,7 @@ run: $(BIN) .PHONY: install install: $(BIN) -$(BIN): .proto.generated $(ENTRYPOINT_OUT_FILES) $(OUR_SOURCES) +$(BIN): .proto.generated $(PWCTL_OUT_FILES) $(OUR_SOURCES) go install -v .PHONY: clean @@ -67,7 +67,7 @@ generate: .proto.generated --user="$(shell id -u)" \ --volume="$(PWD):/go/src/pathwar.pw" \ --workdir="/go/src/pathwar.pw" \ - --entrypoint="sh" \ + --pwctl="sh" \ --rm \ pathwar/protoc:v1 \ -xec "make _proto_generate" @@ -76,9 +76,9 @@ generate: .proto.generated .PHONY: _generate _proto_generate: $(GENERATED_PB_FILES) -$(ENTRYPOINT_OUT_FILES): $(ENTRYPOINT_SOURCES) - mkdir -p ./entrypoint/out - GOOS=linux GOARCH=amd64 go build -o ./entrypoint/out/entrypoint-linux-amd64 $< +$(PWCTL_OUT_FILES): $(PWCTL_SOURCES) + mkdir -p ./pwctl/out + GOOS=linux GOARCH=amd64 go build -o ./pwctl/out/pwctl-linux-amd64 ./pwctl/ .PHONY: test test: .proto.generated diff --git a/entrypoint/main.go b/entrypoint/main.go deleted file mode 100644 index b57933ab6..000000000 --- a/entrypoint/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "log" - "os" - "os/exec" - "syscall" -) - -func main() { - log.Println("Hello world from entrypoint, os.Args:", os.Args) - binary, err := exec.LookPath(os.Args[1]) - if err != nil { - panic(err) - } - args := os.Args[1:] - env := os.Environ() - if err := syscall.Exec(binary, args, env); err != nil { - panic(err) - } -} diff --git a/hypervisor/cmd_hypervisor_run.go b/hypervisor/cmd_hypervisor_run.go index b56667389..21ecc209d 100644 --- a/hypervisor/cmd_hypervisor_run.go +++ b/hypervisor/cmd_hypervisor_run.go @@ -4,6 +4,7 @@ import ( "archive/tar" "bytes" "context" + "encoding/json" "fmt" "io" "os" @@ -22,6 +23,7 @@ import ( "go.uber.org/zap" "pathwar.pw/pkg/cli" + pwctlconfig "pathwar.pw/pwctl/config" ) type runOptions struct { @@ -88,7 +90,7 @@ func runRun(opts runOptions) error { nat.Port("80/tcp"): {}, }, // Hostname: "" - Entrypoint: strslice.StrSlice{"/pathwar"}, + Entrypoint: strslice.StrSlice{"/bin/pwctl", "entrypoint"}, Cmd: append(imageInspect.Config.Entrypoint, imageInspect.Config.Cmd...), Labels: map[string]string{createdByPathwarLabel: "true"}, } @@ -108,7 +110,6 @@ func runRun(opts runOptions) error { // FIXME: create a limited network config // FIXME: restrict resources (cgroups, etc.) // FIXME: configure env - // FIXME: copy tokens somewhere safe in the image cont, err := cli.ContainerCreate(ctx, containerConfig, hostConfig, nil, "") if err != nil { @@ -120,17 +121,27 @@ func runRun(opts runOptions) error { } } - // inject tools in container + pwctlConfig := pwctlconfig.Config{ + Passphrases: []string{ + randString(10), + randString(10), + randString(10), + }, + } + // if !pwctlConfig.Validate() ... + pwctlConfigJSON, _ := json.Marshal(pwctlConfig) + + // inject tools & config in container // FIXME: support alternative architectures -> using copyFromContainer with a dedicated image? var buf bytes.Buffer - var entrypointBox = packr.New("entrypoint-binaries", "../entrypoint/out") - binary, err := entrypointBox.Find("entrypoint-linux-amd64") + var pwctlBox = packr.New("pwctl-binaries", "../pwctl/out") + binary, err := pwctlBox.Find("pwctl-linux-amd64") if err != nil { return err } tw := tar.NewWriter(&buf) if err := tw.WriteHeader(&tar.Header{ - Name: "pathwar", + Name: "/bin/pwctl", Mode: 0755, Size: int64(len(binary)), }); err != nil { @@ -139,6 +150,17 @@ func runRun(opts runOptions) error { if _, err := tw.Write(binary); err != nil { return err } + if err := tw.WriteHeader(&tar.Header{ + Name: "/pwctl.json", + Mode: 0755, + Size: int64(len(pwctlConfigJSON)), + // FIXME: chown it to container's default user + }); err != nil { + return err + } + if _, err := tw.Write(pwctlConfigJSON); err != nil { + return err + } if err := tw.Close(); err != nil { return err } diff --git a/hypervisor/util.go b/hypervisor/util.go new file mode 100644 index 000000000..a017bb023 --- /dev/null +++ b/hypervisor/util.go @@ -0,0 +1,30 @@ +package hypervisor + +import "math/rand" + +// from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = rand.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letterBytes) { + b[i] = letterBytes[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + + return string(b) +} diff --git a/pwctl/.gitignore b/pwctl/.gitignore new file mode 100644 index 000000000..6a3417b8d --- /dev/null +++ b/pwctl/.gitignore @@ -0,0 +1 @@ +/out/ diff --git a/pwctl/config.go b/pwctl/config.go new file mode 100644 index 000000000..cb8505125 --- /dev/null +++ b/pwctl/config.go @@ -0,0 +1,21 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + + pwctlconfig "pathwar.pw/pwctl/config" +) + +func getConfig() (*pwctlconfig.Config, error) { + // load config file + configJSON, err := ioutil.ReadFile("/pwctl.json") + if err != nil { + return nil, err + } + var config pwctlconfig.Config + if err := json.Unmarshal(configJSON, &config); err != nil { + return nil, err + } + return &config, nil +} diff --git a/pwctl/config/config.go b/pwctl/config/config.go new file mode 100644 index 000000000..79910f832 --- /dev/null +++ b/pwctl/config/config.go @@ -0,0 +1,12 @@ +package config + +import "encoding/json" + +type Config struct { + Passphrases []string +} + +func (c Config) String() string { + out, _ := json.Marshal(c) + return string(out) +} diff --git a/pwctl/main.go b/pwctl/main.go new file mode 100644 index 000000000..d1b74760c --- /dev/null +++ b/pwctl/main.go @@ -0,0 +1,85 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "strings" + "syscall" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func main() { + rootCmd := &cobra.Command{ + Use: os.Args[0], + } + rootCmd.PersistentFlags().BoolP("help", "h", false, "print usage") + rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + return nil + } + rootCmd.AddCommand(&cobra.Command{ + Use: "entrypoint", + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + // FIXME: prepare the level + // FIXME: add a self-destruct mode that allow having root access only at runtime + // FIXME: lock to block other commands + binary, err := exec.LookPath(args[0]) + if err != nil { + return err + } + env := os.Environ() + if err := syscall.Exec(binary, args, env); err != nil { + return err + } + return nil + }, + }) + rootCmd.AddCommand(&cobra.Command{ + Use: "env", + RunE: func(cmd *cobra.Command, args []string) error { + for _, line := range os.Environ() { + fmt.Println(line) + } + return nil + }, + }) + rootCmd.AddCommand(&cobra.Command{ + Use: "config", + RunE: func(cmd *cobra.Command, args []string) error { + config, err := getConfig() + if err != nil { + return err + } + out, _ := json.Marshal(config) + fmt.Println(string(out)) + return nil + }, + }) + rootCmd.AddCommand(&cobra.Command{ + Use: "passphrase", + RunE: func(cmd *cobra.Command, args []string) error { + config, err := getConfig() + if err != nil { + return err + } + // FIXME: parse index from CLI + fmt.Println(config.Passphrases[0]) + return nil + }, + }) + args := os.Args[1:] + if args[0] == "entrypoint" && len(args) > 1 && args[1] != "--" { + args = append([]string{"entrypoint", "--"}, args[1:]...) + } + rootCmd.SetArgs(args) + viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + if err := rootCmd.Execute(); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } +}