mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-08-29 03:37:54 +00:00
As discussed [1], the runlabel command should execute any command specified in a label. The reasoning behind is that we cannot restrict which options are passed to Podman which thereby has full access to the host (runlabels must be used with care). With the updated semantics, runlabel will substitute the commands with a basepath equal to "docker" or "podman" with "/proc/self/exe", and otherwise leave the command unchanged to execute any other command on the host. [1] https://github.com/containers/libpod/pull/1607#issuecomment-428321382 Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
239 lines
6 KiB
Go
239 lines
6 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/containers/image/types"
|
|
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
|
"github.com/containers/libpod/cmd/podman/shared"
|
|
"github.com/containers/libpod/libpod/image"
|
|
"github.com/containers/libpod/pkg/util"
|
|
"github.com/containers/libpod/utils"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var (
|
|
runlabelFlags = []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "authfile",
|
|
Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "display",
|
|
Usage: "preview the command that the label would run",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "cert-dir",
|
|
Usage: "`pathname` of a directory containing TLS certificates and keys",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "creds",
|
|
Usage: "`credentials` (USERNAME:PASSWORD) to use for authenticating to a registry",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "name",
|
|
Usage: "Assign a name to the container",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "opt1",
|
|
Usage: "Optional parameter to pass for install",
|
|
Hidden: true,
|
|
},
|
|
cli.StringFlag{
|
|
Name: "opt2",
|
|
Usage: "Optional parameter to pass for install",
|
|
Hidden: true,
|
|
},
|
|
cli.StringFlag{
|
|
Name: "opt3",
|
|
Usage: "Optional parameter to pass for install",
|
|
Hidden: true,
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "quiet, q",
|
|
Usage: "Suppress output information when installing images",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "pull, p",
|
|
Usage: "pull the image if it does not exist locally prior to executing the label contents",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "signature-policy",
|
|
Usage: "`pathname` of signature policy file (not usually used)",
|
|
},
|
|
cli.BoolTFlag{
|
|
Name: "tls-verify",
|
|
Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
|
|
},
|
|
}
|
|
|
|
runlabelDescription = `
|
|
Executes a command as described by a container image label.
|
|
`
|
|
runlabelCommand = cli.Command{
|
|
Name: "runlabel",
|
|
Usage: "Execute the command described by an image label",
|
|
Description: runlabelDescription,
|
|
Flags: sortFlags(runlabelFlags),
|
|
Action: runlabelCmd,
|
|
ArgsUsage: "",
|
|
SkipArgReorder: true,
|
|
OnUsageError: usageErrorHandler,
|
|
}
|
|
)
|
|
|
|
// installCmd gets the data from the command line and calls installImage
|
|
// to copy an image from a registry to a local machine
|
|
func runlabelCmd(c *cli.Context) error {
|
|
var (
|
|
imageName string
|
|
stdErr, stdOut io.Writer
|
|
stdIn io.Reader
|
|
newImage *image.Image
|
|
)
|
|
|
|
// Evil images could trick into recursively executing the runlabel
|
|
// command. Avoid this by setting the "PODMAN_RUNLABEL_NESTED" env
|
|
// variable when executing a label first.
|
|
nested := os.Getenv("PODMAN_RUNLABEL_NESTED")
|
|
if nested == "1" {
|
|
return fmt.Errorf("nested runlabel calls: runlabels cannot execute the runlabel command")
|
|
}
|
|
|
|
opts := make(map[string]string)
|
|
runtime, err := libpodruntime.GetRuntime(c)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "could not get runtime")
|
|
}
|
|
defer runtime.Shutdown(false)
|
|
|
|
args := c.Args()
|
|
if len(args) < 2 {
|
|
logrus.Errorf("the runlabel command requires at least 2 arguments: LABEL IMAGE")
|
|
return nil
|
|
}
|
|
if err := validateFlags(c, runlabelFlags); err != nil {
|
|
return err
|
|
}
|
|
if c.Bool("display") && c.Bool("quiet") {
|
|
return errors.Errorf("the display and quiet flags cannot be used together.")
|
|
}
|
|
|
|
pull := c.Bool("pull")
|
|
label := args[0]
|
|
|
|
runlabelImage := args[1]
|
|
|
|
if c.IsSet("opt1") {
|
|
opts["opt1"] = c.String("opt1")
|
|
}
|
|
if c.IsSet("opt2") {
|
|
opts["opt2"] = c.String("opt2")
|
|
}
|
|
if c.IsSet("opt3") {
|
|
opts["opt3"] = c.String("opt3")
|
|
}
|
|
|
|
ctx := getContext()
|
|
|
|
stdErr = os.Stderr
|
|
stdOut = os.Stdout
|
|
stdIn = os.Stdin
|
|
|
|
if c.Bool("quiet") {
|
|
stdErr = nil
|
|
stdOut = nil
|
|
stdIn = nil
|
|
}
|
|
|
|
if pull {
|
|
var registryCreds *types.DockerAuthConfig
|
|
if c.IsSet("creds") {
|
|
creds, err := util.ParseRegistryCreds(c.String("creds"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
registryCreds = creds
|
|
}
|
|
dockerRegistryOptions := image.DockerRegistryOptions{
|
|
DockerRegistryCreds: registryCreds,
|
|
DockerCertPath: c.String("cert-dir"),
|
|
DockerInsecureSkipTLSVerify: !c.BoolT("tls-verify"),
|
|
}
|
|
authfile := getAuthFile(c.String("authfile"))
|
|
|
|
newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, c.String("signature-policy"), authfile, stdOut, &dockerRegistryOptions, image.SigningOptions{}, false, false)
|
|
} else {
|
|
newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage)
|
|
}
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to find image")
|
|
}
|
|
|
|
if len(newImage.Names()) < 1 {
|
|
imageName = newImage.ID()
|
|
} else {
|
|
imageName = newImage.Names()[0]
|
|
}
|
|
|
|
runLabel, err := newImage.GetLabel(ctx, label)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If no label to execute, we return
|
|
if runLabel == "" {
|
|
return nil
|
|
}
|
|
|
|
// The user provided extra arguments that need to be tacked onto the label's command
|
|
if len(args) > 2 {
|
|
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(args[2:], " "))
|
|
}
|
|
|
|
cmd, err := shared.GenerateCommand(runLabel, imageName, c.String("name"))
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to generate command")
|
|
}
|
|
env := shared.GenerateRunEnvironment(c.String("name"), imageName, opts)
|
|
env = append(env, "PODMAN_RUNLABEL_NESTED=1")
|
|
|
|
envmap := envSliceToMap(env)
|
|
|
|
envmapper := func(k string) string {
|
|
switch k {
|
|
case "OPT1":
|
|
return envmap["OPT1"]
|
|
case "OPT2":
|
|
return envmap["OPT2"]
|
|
case "OPT3":
|
|
return envmap["OPT3"]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
newS := os.Expand(strings.Join(cmd, " "), envmapper)
|
|
cmd = strings.Split(newS, " ")
|
|
|
|
if !c.Bool("quiet") {
|
|
fmt.Printf("Command: %s\n", strings.Join(cmd, " "))
|
|
if c.Bool("display") {
|
|
return nil
|
|
}
|
|
}
|
|
return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...)
|
|
}
|
|
|
|
func envSliceToMap(env []string) map[string]string {
|
|
m := make(map[string]string)
|
|
for _, i := range env {
|
|
split := strings.Split(i, "=")
|
|
m[split[0]] = strings.Join(split[1:], " ")
|
|
}
|
|
return m
|
|
}
|