diff --git a/docs/packages.md b/docs/packages.md index 07b50d3ba..e316c6ca3 100644 --- a/docs/packages.md +++ b/docs/packages.md @@ -94,6 +94,11 @@ invoke the build like this (for LastPass): ``` DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$(lpass show --password) linuxkit pkg push «path-to-package» ``` +or alternatively you may add the command to `~/.moby/linuxkit/config.yml` e.g.: +``` +pkg: + content-trust-passphrase-command: "lpass show --password" +``` ### Build packages as a developer diff --git a/src/cmd/linuxkit/main.go b/src/cmd/linuxkit/main.go index f64646352..6baceb6cb 100644 --- a/src/cmd/linuxkit/main.go +++ b/src/cmd/linuxkit/main.go @@ -3,12 +3,27 @@ package main import ( "flag" "fmt" + "io/ioutil" "os" "path/filepath" log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v2" ) +// GlobalConfig is the global tool configuration +type GlobalConfig struct { + Pkg PkgConfig `yaml:"pkg"` +} + +// PkgConfig is the config specific to the `pkg` subcommand +type PkgConfig struct { + // ContentTrustCommand is passed to `sh -c` and the stdout + // (including whitespace and \n) is set as the content trust + // passphrase. Can be used to execute a password manager. + ContentTrustCommand string `yaml:"content-trust-passphrase-command"` +} + var ( defaultLogFormatter = &log.TextFormatter{} @@ -17,6 +32,9 @@ var ( // GitCommit hash, set at compile time GitCommit = "unknown" + + // Config is the global tool configuration + Config = GlobalConfig{} ) // infoFormatter overrides the default format for Info() log events to @@ -37,6 +55,22 @@ func version() { os.Exit(0) } +func readConfig() { + cfgPath := filepath.Join(os.Getenv("HOME"), ".moby", "linuxkit", "config.yml") + cfgBytes, err := ioutil.ReadFile(cfgPath) + if err != nil { + if os.IsNotExist(err) { + return + } + fmt.Printf("Failed to read %q\n", cfgPath) + os.Exit(1) + } + if err := yaml.Unmarshal(cfgBytes, &Config); err != nil { + fmt.Printf("Failed to parse %q\n", cfgPath) + os.Exit(1) + } +} + func main() { flag.Usage = func() { fmt.Printf("USAGE: %s [options] COMMAND\n\n", filepath.Base(os.Args[0])) @@ -56,6 +90,8 @@ func main() { flagQuiet := flag.Bool("q", false, "Quiet execution") flagVerbose := flag.Bool("v", false, "Verbose execution") + readConfig() + // Set up logging log.SetFormatter(new(infoFormatter)) log.SetLevel(log.InfoLevel) diff --git a/src/cmd/linuxkit/pkg.go b/src/cmd/linuxkit/pkg.go index eb023811f..57777f6e1 100644 --- a/src/cmd/linuxkit/pkg.go +++ b/src/cmd/linuxkit/pkg.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "os/exec" "path/filepath" ) @@ -19,12 +20,37 @@ func pkgUsage() { fmt.Printf("See '%s pkg [command] --help' for details.\n\n", invoked) } +func setupContentTrust() { + // If it is already set there is nothing to do. + if _, ok := os.LookupEnv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"); ok { + return + } + // If it is not set but it is needed this is checked at time + // of use, not all commands need it. + if Config.Pkg.ContentTrustCommand == "" { + return + } + + // Run the command and set the output as the passphrase + cmd := exec.Command("/bin/sh", "-c", Config.Pkg.ContentTrustCommand) + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + v, err := cmd.Output() + if err != nil { + fmt.Printf("Failed to run ContentTrustCommand: %s\n", err) + os.Exit(1) + } + os.Setenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE", string(v)) +} + func pkg(args []string) { if len(args) < 1 { pkgUsage() os.Exit(1) } + setupContentTrust() + switch args[0] { case "build": pkgBuild(args[1:])