diff options
Diffstat (limited to 'cmd/podman')
-rw-r--r-- | cmd/podman/common.go | 9 | ||||
-rw-r--r-- | cmd/podman/create.go | 82 | ||||
-rw-r--r-- | cmd/podman/exec.go | 14 | ||||
-rw-r--r-- | cmd/podman/image.go | 1 | ||||
-rw-r--r-- | cmd/podman/info.go | 12 | ||||
-rw-r--r-- | cmd/podman/shared/container.go | 4 | ||||
-rw-r--r-- | cmd/podman/sign.go | 197 | ||||
-rw-r--r-- | cmd/podman/start.go | 16 |
8 files changed, 227 insertions, 108 deletions
diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 0fc9a6acc..d934c8699 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -28,6 +28,10 @@ var ( Name: "latest, l", Usage: "act on the latest pod podman is aware of", } + WorkDirFlag = cli.StringFlag{ + Name: "workdir, w", + Usage: "Working directory inside the container", + } ) const ( @@ -522,10 +526,7 @@ var createFlags = []cli.Flag{ Name: "volumes-from", Usage: "Mount volumes from the specified container(s) (default [])", }, - cli.StringFlag{ - Name: "workdir, w", - Usage: "Working `directory inside the container", - }, + WorkDirFlag, } func getFormat(c *cli.Context) (string, error) { diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 395a64b3b..d98b78bd4 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -15,13 +15,11 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" ann "github.com/containers/libpod/pkg/annotations" - "github.com/containers/libpod/pkg/apparmor" "github.com/containers/libpod/pkg/inspect" ns "github.com/containers/libpod/pkg/namespaces" "github.com/containers/libpod/pkg/rootless" cc "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/util" - libpodVersion "github.com/containers/libpod/version" "github.com/docker/docker/pkg/signal" "github.com/docker/go-connections/nat" "github.com/docker/go-units" @@ -162,83 +160,9 @@ func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container return ctr, createConfig, nil } -// Checks if a user-specified AppArmor profile is loaded, or loads the default profile if -// AppArmor is enabled. -// Any interaction with AppArmor requires root permissions. -func loadAppArmor(config *cc.CreateConfig) error { - if rootless.IsRootless() { - noAAMsg := "AppArmor security is not available in rootless mode" - switch config.ApparmorProfile { - case "": - logrus.Warn(noAAMsg) - case "unconfined": - default: - return fmt.Errorf(noAAMsg) - } - return nil - } - - if config.ApparmorProfile == "" && apparmor.IsEnabled() { - // Unless specified otherwise, make sure that the default AppArmor - // profile is installed. To avoid redundantly loading the profile - // on each invocation, check if it's loaded before installing it. - // Suffix the profile with the current libpod version to allow - // loading the new, potentially updated profile after an update. - profile := fmt.Sprintf("%s-%s", apparmor.DefaultLibpodProfile, libpodVersion.Version) - - loadProfile := func() error { - isLoaded, err := apparmor.IsLoaded(profile) - if err != nil { - return err - } - if !isLoaded { - err = apparmor.InstallDefault(profile) - if err != nil { - return err - } - - } - return nil - } - - if err := loadProfile(); err != nil { - switch err { - case apparmor.ErrApparmorUnsupported: - // do not set the profile when AppArmor isn't supported - logrus.Debugf("AppArmor is not supported: setting empty profile") - default: - return err - } - } else { - logrus.Infof("Sucessfully loaded AppAmor profile '%s'", profile) - config.ApparmorProfile = profile - } - } else if config.ApparmorProfile != "" && config.ApparmorProfile != "unconfined" { - if !apparmor.IsEnabled() { - return fmt.Errorf("Profile specified but AppArmor is disabled on the host") - } - - isLoaded, err := apparmor.IsLoaded(config.ApparmorProfile) - if err != nil { - switch err { - case apparmor.ErrApparmorUnsupported: - return fmt.Errorf("Profile specified but AppArmor is not supported") - default: - return fmt.Errorf("Error checking if AppArmor profile is loaded: %v", err) - } - } - if !isLoaded { - return fmt.Errorf("The specified AppArmor profile '%s' is not loaded", config.ApparmorProfile) - } - } - - return nil -} - func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error { var ( labelOpts []string - err error ) if config.PidMode.IsHost() { @@ -283,10 +207,6 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error { } } - if err := loadAppArmor(config); err != nil { - return err - } - if config.SeccompProfilePath == "" { if _, err := os.Stat(libpod.SeccompOverridePath); err == nil { config.SeccompProfilePath = libpod.SeccompOverridePath @@ -304,7 +224,7 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error { } } config.LabelOpts = labelOpts - return err + return nil } // isPortInPortBindings determines if an exposed host port is in user diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index 1dcb88dbd..073e72e64 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -3,7 +3,6 @@ package main import ( "fmt" "os" - "strings" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" @@ -35,6 +34,7 @@ var ( Usage: "Sets the username or UID used and optionally the groupname or GID for the specified command", }, LatestFlag, + WorkDirFlag, } execDescription = ` podman exec @@ -99,15 +99,7 @@ func execCmd(c *cli.Context) error { } // ENVIRONMENT VARIABLES - env := defaultEnvVariables - for _, e := range c.StringSlice("env") { - split := strings.SplitN(e, "=", 2) - if len(split) > 1 { - env[split[0]] = split[1] - } else { - env[split[0]] = "" - } - } + env := map[string]string{} if err := readKVStrings(env, []string{}, c.StringSlice("env")); err != nil { return errors.Wrapf(err, "unable to process environment variables") @@ -117,5 +109,5 @@ func execCmd(c *cli.Context) error { envs = append(envs, fmt.Sprintf("%s=%s", k, v)) } - return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user")) + return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"), c.String("workdir")) } diff --git a/cmd/podman/image.go b/cmd/podman/image.go index e978b9cf5..557fc1056 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -20,6 +20,7 @@ var ( saveCommand, tagCommand, trustCommand, + signCommand, } imageDescription = "Manage images" diff --git a/cmd/podman/info.go b/cmd/podman/info.go index c0639725e..4b80f94db 100644 --- a/cmd/podman/info.go +++ b/cmd/podman/info.go @@ -1,10 +1,10 @@ package main import ( + "github.com/containers/libpod/libpod/adapter" "runtime" "github.com/containers/libpod/cmd/podman/formats" - "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/pkg/errors" "github.com/urfave/cli" @@ -39,18 +39,20 @@ func infoCmd(c *cli.Context) error { } info := map[string]interface{}{} - runtime, err := libpodruntime.GetRuntime(c) + localRuntime, err := adapter.GetRuntime(c) if err != nil { return errors.Wrapf(err, "could not get runtime") } - defer runtime.Shutdown(false) + defer localRuntime.Runtime.Shutdown(false) - infoArr, err := runtime.Info() + infoArr, err := localRuntime.Runtime.Info() if err != nil { return errors.Wrapf(err, "error getting info") } - if c.Bool("debug") { + // TODO This is no a problem child because we don't know if we should add information + // TODO about the client or the backend. Only do for traditional podman for now. + if !localRuntime.Remote && c.Bool("debug") { debugInfo := debugInfo(c) infoArr = append(infoArr, libpod.InfoData{Type: "debug", Data: debugInfo}) } diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index 6c7d8eb52..a904ef75a 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -52,7 +52,7 @@ type PsOptions struct { // BatchContainerStruct is the return obkect from BatchContainer and contains // container related information type BatchContainerStruct struct { - ConConfig *libpod.Config + ConConfig *libpod.ContainerConfig ConState libpod.ContainerStatus ExitCode int32 Exited bool @@ -329,7 +329,7 @@ func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsCon // locks. func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) { var ( - conConfig *libpod.Config + conConfig *libpod.ContainerConfig conState libpod.ContainerStatus err error exitCode int32 diff --git a/cmd/podman/sign.go b/cmd/podman/sign.go new file mode 100644 index 000000000..e7367a311 --- /dev/null +++ b/cmd/podman/sign.go @@ -0,0 +1,197 @@ +package main + +import ( + "io/ioutil" + "net/url" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/containers/image/signature" + "github.com/containers/image/transports" + "github.com/containers/image/transports/alltransports" + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/trust" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var ( + signFlags = []cli.Flag{ + cli.StringFlag{ + Name: "sign-by", + Usage: "Name of the signing key", + }, + cli.StringFlag{ + Name: "directory, d", + Usage: "Define an alternate directory to store signatures", + }, + } + + signDescription = "Create a signature file that can be used later to verify the image" + signCommand = cli.Command{ + Name: "sign", + Usage: "Sign an image", + Description: signDescription, + Flags: sortFlags(signFlags), + Action: signCmd, + ArgsUsage: "IMAGE-NAME [IMAGE-NAME ...]", + OnUsageError: usageErrorHandler, + } +) + +// SignatureStoreDir defines default directory to store signatures +const SignatureStoreDir = "/var/lib/containers/sigstore" + +func signCmd(c *cli.Context) error { + args := c.Args() + if len(args) < 1 { + return errors.Errorf("at least one image name must be specified") + } + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not create runtime") + } + defer runtime.Shutdown(false) + + signby := c.String("sign-by") + if signby == "" { + return errors.Errorf("please provide an identity") + } + + var sigStoreDir string + if c.IsSet("directory") { + sigStoreDir = c.String("directory") + if _, err := os.Stat(sigStoreDir); err != nil { + return errors.Wrapf(err, "invalid directory %s", sigStoreDir) + } + } + + mech, err := signature.NewGPGSigningMechanism() + if err != nil { + return errors.Wrap(err, "error initializing GPG") + } + defer mech.Close() + if err := mech.SupportsSigning(); err != nil { + return errors.Wrap(err, "signing is not supported") + } + + systemRegistriesDirPath := trust.RegistriesDirPath(runtime.SystemContext()) + registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) + if err != nil { + return errors.Wrapf(err, "error reading registry configuration") + } + + for _, signimage := range args { + srcRef, err := alltransports.ParseImageName(signimage) + if err != nil { + return errors.Wrapf(err, "error parsing image name") + } + rawSource, err := srcRef.NewImageSource(getContext(), runtime.SystemContext()) + if err != nil { + return errors.Wrapf(err, "error getting image source") + } + manifest, _, err := rawSource.GetManifest(getContext(), nil) + if err != nil { + return errors.Wrapf(err, "error getting manifest") + } + dockerReference := rawSource.Reference().DockerReference() + if dockerReference == nil { + return errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference())) + } + + // create the signstore file + newImage, err := runtime.ImageRuntime().New(getContext(), signimage, runtime.GetConfig().SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{SignBy: signby}, false) + if err != nil { + return errors.Wrapf(err, "error pulling image %s", signimage) + } + + registryInfo := trust.HaveMatchRegistry(rawSource.Reference().DockerReference().String(), registryConfigs) + if registryInfo != nil { + if sigStoreDir == "" { + sigStoreDir = registryInfo.SigStoreStaging + if sigStoreDir == "" { + sigStoreDir = registryInfo.SigStore + } + } + sigStoreDir, err = isValidSigStoreDir(sigStoreDir) + if err != nil { + return errors.Wrapf(err, "invalid signature storage %s", sigStoreDir) + } + } + if sigStoreDir == "" { + sigStoreDir = SignatureStoreDir + } + + repos, err := newImage.RepoDigests() + if err != nil { + return errors.Wrapf(err, "error calculating repo digests for %s", signimage) + } + if len(repos) == 0 { + logrus.Errorf("no repodigests associated with the image %s", signimage) + continue + } + + // create signature + newSig, err := signature.SignDockerManifest(manifest, dockerReference.String(), mech, signby) + if err != nil { + return errors.Wrapf(err, "error creating new signature") + } + + sigStoreDir = filepath.Join(sigStoreDir, strings.Replace(repos[0][strings.Index(repos[0], "/")+1:len(repos[0])], ":", "=", 1)) + if err := os.MkdirAll(sigStoreDir, 0751); err != nil { + // The directory is allowed to exist + if !os.IsExist(err) { + logrus.Errorf("error creating directory %s: %s", sigStoreDir, err) + continue + } + } + sigFilename, err := getSigFilename(sigStoreDir) + if err != nil { + logrus.Errorf("error creating sigstore file: %v", err) + continue + } + err = ioutil.WriteFile(filepath.Join(sigStoreDir, sigFilename), newSig, 0644) + if err != nil { + logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String()) + continue + } + } + return nil +} + +func getSigFilename(sigStoreDirPath string) (string, error) { + sigFileSuffix := 1 + sigFiles, err := ioutil.ReadDir(sigStoreDirPath) + if err != nil { + return "", err + } + sigFilenames := make(map[string]bool) + for _, file := range sigFiles { + sigFilenames[file.Name()] = true + } + for { + sigFilename := "signature-" + strconv.Itoa(sigFileSuffix) + if _, exists := sigFilenames[sigFilename]; !exists { + return sigFilename, nil + } + sigFileSuffix++ + } +} + +func isValidSigStoreDir(sigStoreDir string) (string, error) { + writeURIs := map[string]bool{"file": true} + url, err := url.Parse(sigStoreDir) + if err != nil { + return sigStoreDir, errors.Wrapf(err, "invalid directory %s", sigStoreDir) + } + _, exists := writeURIs[url.Scheme] + if !exists { + return sigStoreDir, errors.Errorf("writing to %s is not supported. Use a supported scheme", sigStoreDir) + } + sigStoreDir = url.Path + return sigStoreDir, nil +} diff --git a/cmd/podman/start.go b/cmd/podman/start.go index 8bb386c68..df34deec2 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -27,9 +27,9 @@ var ( Name: "interactive, i", Usage: "Keep STDIN open even if not attached", }, - cli.BoolFlag{ + cli.BoolTFlag{ Name: "sig-proxy", - Usage: "proxy received signals to the process", + Usage: "proxy received signals to the process (default true if attaching, false otherwise)", }, LatestFlag, } @@ -67,8 +67,14 @@ func startCmd(c *cli.Context) error { return err } - if c.Bool("sig-proxy") && !attach { - return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach") + sigProxy := c.BoolT("sig-proxy") + + if sigProxy && !attach { + if c.IsSet("sig-proxy") { + return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach") + } else { + sigProxy = false + } } runtime, err := libpodruntime.GetRuntime(c) @@ -111,7 +117,7 @@ func startCmd(c *cli.Context) error { } // attach to the container and also start it not already running - err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.String("detach-keys"), c.Bool("sig-proxy"), !ctrRunning) + err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.String("detach-keys"), sigProxy, !ctrRunning) if ctrRunning { return err } |