diff options
36 files changed, 346 insertions, 121 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 9856e46ef..7fed15e5e 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -861,10 +861,10 @@ func AutocompletePsFilters(cmd *cobra.Command, args []string, toComplete string) "status=": func(_ string) ([]string, cobra.ShellCompDirective) { return containerStatuses, cobra.ShellCompDirectiveNoFileComp }, - "ancestor": func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) }, - "before=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, - "since=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, - "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, + "ancestor=": func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) }, + "before=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, + "since=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, + "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, "health=": func(_ string) ([]string, cobra.ShellCompDirective) { return []string{define.HealthCheckHealthy, define.HealthCheckUnhealthy}, cobra.ShellCompDirectiveNoFileComp diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index af53a3b67..4b0e40df2 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -204,10 +204,10 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup for _, m := range cc.HostConfig.Mounts { mount := fmt.Sprintf("type=%s", m.Type) if len(m.Source) > 0 { - mount += fmt.Sprintf("source=%s", m.Source) + mount += fmt.Sprintf(",source=%s", m.Source) } if len(m.Target) > 0 { - mount += fmt.Sprintf("dest=%s", m.Target) + mount += fmt.Sprintf(",dst=%s", m.Target) } mounts = append(mounts, mount) } diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 0bb6e79e5..e0da142ad 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -517,18 +517,22 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } switch con[0] { - case "proc-opts": - s.ProcOpts = strings.Split(con[1], ",") + case "apparmor": + s.ContainerSecurityConfig.ApparmorProfile = con[1] + s.Annotations[define.InspectAnnotationApparmor] = con[1] case "label": // TODO selinux opts and label opts are the same thing s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1]) s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=") - case "apparmor": - s.ContainerSecurityConfig.ApparmorProfile = con[1] - s.Annotations[define.InspectAnnotationApparmor] = con[1] + case "mask": + s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...) + case "proc-opts": + s.ProcOpts = strings.Split(con[1], ",") case "seccomp": s.SeccompProfilePath = con[1] s.Annotations[define.InspectAnnotationSeccomp] = con[1] + case "unmask": + s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, strings.Split(con[1], ":")...) default: return fmt.Errorf("invalid --security-opt 2: %q", opt) } diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index bcb31e6ee..8a7951923 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -126,8 +126,8 @@ func images(cmd *cobra.Command, args []string) error { case listFlag.quiet: return writeID(imgs) default: - if cmd.Flag("format").Changed { - listFlag.noHeading = true // V1 compatibility + if cmd.Flags().Changed("format") && !parse.HasTable(listFlag.format) { + listFlag.noHeading = true } return writeTemplate(imgs) } diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index 6e6bbb07d..16ae980dc 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -37,6 +37,7 @@ var ( var ( networkListOptions entities.NetworkListOptions filters []string + noTrunc bool ) func networkListFlags(flags *pflag.FlagSet) { @@ -45,6 +46,7 @@ func networkListFlags(flags *pflag.FlagSet) { _ = networklistCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names") + flags.BoolVar(&noTrunc, "no-trunc", false, "Do not truncate the network ID") filterFlagName := "filter" flags.StringArrayVarP(&filters, filterFlagName, "f", nil, "Provide filter values (e.g. 'name=podman')") @@ -96,6 +98,7 @@ func networkList(cmd *cobra.Command, args []string) error { "Version": "version", "Plugins": "plugins", "Labels": "labels", + "ID": "network id", }) renderHeaders := true row := "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n" @@ -155,3 +158,11 @@ func (n ListPrintReports) Labels() string { } return strings.Join(list, ",") } + +func (n ListPrintReports) ID() string { + length := 12 + if noTrunc { + length = 64 + } + return network.GetNetworkID(n.Name)[:length] +} diff --git a/docs/source/markdown/podman-load.1.md b/docs/source/markdown/podman-load.1.md index 177709a43..dc2a632e5 100644 --- a/docs/source/markdown/podman-load.1.md +++ b/docs/source/markdown/podman-load.1.md @@ -10,7 +10,7 @@ podman\-load - Load image(s) from a tar archive into container storage ## DESCRIPTION **podman load** loads an image from either an **oci-archive** or a **docker-archive** stored on the local machine into container storage. **podman load** reads from stdin by default or a file if the **input** option is set. -You can also specify a name for the image if the archive does not contain a named reference, of if you want an additional name for the local image. +You can also specify a name for the image if the archive is of single image and load will tag an additional image with the name:tag. **podman load** is used for loading from the archive generated by **podman save**, that includes the image parent layers. To load the archive of container's filesystem created by **podman export**, use **podman import**. The local client further supports loading an **oci-dir** or a **docker-dir** as created with **podman save** (1). diff --git a/docs/source/markdown/podman-network-ls.1.md b/docs/source/markdown/podman-network-ls.1.md index fcba51190..a964c97e8 100644 --- a/docs/source/markdown/podman-network-ls.1.md +++ b/docs/source/markdown/podman-network-ls.1.md @@ -10,14 +10,6 @@ podman\-network\-ls - Display a summary of CNI networks Displays a list of existing podman networks. This command is not available for rootless users. ## OPTIONS -#### **--quiet**, **-q** - -The `quiet` option will restrict the output to only the network names. - -#### **--format** - -Pretty-print networks to JSON or using a Go template. - #### **--filter**, **-f** Filter output based on conditions given. @@ -30,10 +22,33 @@ Valid filters are listed below: | **Filter** | **Description** | | ---------- | ------------------------------------------------------------------------------------- | | name | [Name] Network name (accepts regex) | +| id | [ID] Full or partial network ID | | label | [Key] or [Key=Value] Label assigned to a network | | plugin | [Plugin] CNI plugins included in a network (e.g `bridge`,`portmap`,`firewall`,`tuning`,`dnsname`,`macvlan`) | | driver | [Driver] Only `bridge` is supported | +#### **--format** + +Change the default output format. This can be of a supported type like 'json' +or a Go template. +Valid placeholders for the Go template are listed below: + +| **Placeholder** | **Description** | +| --------------- | --------------------------------| +| .ID | Network ID | +| .Name | Network name | +| .Plugins | Network Plugins | +| .Labels | Network labels | +| .Version | CNI Version of the config file | + +#### **--no-trunc** + +Do not truncate the network ID. The network ID is not displayed by default and must be specified with **--format**. + +#### **--quiet**, **-q** + +The `quiet` option will restrict the output to only the network names. + ## EXAMPLE Display networks diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 83aaa33e8..1038906c0 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -885,11 +885,16 @@ Security Options - **label=level:**_LEVEL_: Set the label level for the container processes - **label=filetype:**TYPE_: Set the label file type for the container files - **label=disable**: Turn off label separation for the container +- **mask**=_/path/1:/path/2_: The paths to mask separated by a colon. A masked path + cannot be accessed inside the container. - **no-new-privileges**: Disable container processes from gaining additional privileges - **seccomp=unconfined**: Turn off seccomp confinement for the container - **seccomp**=_profile.json_: Allowed syscall list seccomp JSON file to be used as a seccomp filter - **proc-opts**=_OPTIONS_ : Comma separated list of options to use for the /proc mount. More details for the possible mount options are specified at **proc(5)** man page. +- **unmask**=_ALL_ or _/path/1:/path/2_: Paths to unmask separated by a colon. If set to **ALL**, it will + unmask all the paths that are masked by default. + The default masked paths are **/proc/acpi, /proc/kcore, /proc/keys, /proc/latency_stats, /proc/sched_debug, /proc/scsi, /proc/timer_list, /proc/timer_stats, /sys/firmware, and /sys/fs/selinux.** Note: Labeling can be disabled for all containers by setting **label=false** in the **containers.conf**(5) file. @@ -1479,6 +1484,26 @@ $ podman run --security-opt label=type:svirt_apache_t -i -t centos bash Note you would have to write policy defining a **svirt_apache_t** type. +To mask additional specific paths in the container, specify the paths +separated by a colon using the **mask** option with the **--security-opt** +flag. + +``` +$ podman run --security-opt mask=/foo/bar:/second/path fedora bash +``` + +To unmask all the paths that are masked by default, set the **unmask** option to +**ALL**. Or to only unmask specific paths, specify the paths as shown above with +the **mask** option. + +``` +$ podman run --security-opt unmask=ALL fedora bash +``` + +``` +$ podman run --security-opt unmask=/foo/bar:/sys/firmware fedora bash +``` + ### Setting device weight If you want to set _/dev/sda_ device weight to **200**, you can specify the device diff --git a/libpod/network/files.go b/libpod/network/files.go index 83cb1c23a..33cf01064 100644 --- a/libpod/network/files.go +++ b/libpod/network/files.go @@ -50,13 +50,15 @@ func LoadCNIConfsFromDir(dir string) ([]*libcni.NetworkConfigList, error) { return configs, nil } -// GetCNIConfigPathByName finds a CNI network by name and +// GetCNIConfigPathByNameOrID finds a CNI network by name and // returns its configuration file path -func GetCNIConfigPathByName(config *config.Config, name string) (string, error) { +func GetCNIConfigPathByNameOrID(config *config.Config, name string) (string, error) { files, err := libcni.ConfFiles(GetCNIConfDir(config), []string{".conflist"}) if err != nil { return "", err } + idMatch := 0 + file := "" for _, confFile := range files { conf, err := libcni.ConfListFromFile(confFile) if err != nil { @@ -65,6 +67,16 @@ func GetCNIConfigPathByName(config *config.Config, name string) (string, error) if conf.Name == name { return confFile, nil } + if strings.HasPrefix(GetNetworkID(conf.Name), name) { + idMatch++ + file = confFile + } + } + if idMatch == 1 { + return file, nil + } + if idMatch > 1 { + return "", errors.Errorf("more than one result for network ID %s", name) } return "", errors.Wrap(define.ErrNoSuchNetwork, fmt.Sprintf("unable to find network configuration for %s", name)) } @@ -72,7 +84,7 @@ func GetCNIConfigPathByName(config *config.Config, name string) (string, error) // ReadRawCNIConfByName reads the raw CNI configuration for a CNI // network by name func ReadRawCNIConfByName(config *config.Config, name string) ([]byte, error) { - confFile, err := GetCNIConfigPathByName(config, name) + confFile, err := GetCNIConfigPathByNameOrID(config, name) if err != nil { return nil, err } diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go index a5fec5e80..d61b96ecb 100644 --- a/libpod/network/netconflist.go +++ b/libpod/network/netconflist.go @@ -230,8 +230,16 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri } } + case "id": + // matches part of one id + for _, filterValue := range filterValues { + if strings.Contains(GetNetworkID(netconf.Name), filterValue) { + result = true + break + } + } + // TODO: add dangling filter - // TODO TODO: add id filter if we support ids default: return false, errors.Errorf("invalid filter %q", key) diff --git a/libpod/network/network.go b/libpod/network/network.go index 0febb52f6..89f0b67ac 100644 --- a/libpod/network/network.go +++ b/libpod/network/network.go @@ -1,6 +1,8 @@ package network import ( + "crypto/sha256" + "encoding/hex" "encoding/json" "net" "os" @@ -175,7 +177,7 @@ func RemoveNetwork(config *config.Config, name string) error { return err } defer l.releaseCNILock() - cniPath, err := GetCNIConfigPathByName(config, name) + cniPath, err := GetCNIConfigPathByNameOrID(config, name) if err != nil { return err } @@ -229,3 +231,10 @@ func Exists(config *config.Config, name string) (bool, error) { } return true, nil } + +// GetNetworkID return the network ID for a given name. +// It is just the sha256 hash but this should be good enough. +func GetNetworkID(name string) string { + hash := sha256.Sum256([]byte(name)) + return hex.EncodeToString(hash[:]) +} diff --git a/libpod/reset.go b/libpod/reset.go index f8828fed4..6d2842723 100644 --- a/libpod/reset.go +++ b/libpod/reset.go @@ -46,7 +46,7 @@ func (r *Runtime) Reset(ctx context.Context) error { } } - if err := stopPauseProcess(); err != nil { + if err := r.stopPauseProcess(); err != nil { logrus.Errorf("Error stopping pause process: %v", err) } diff --git a/libpod/runtime.go b/libpod/runtime.go index df3dfae2b..cdf66a4d0 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -472,7 +472,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { // we will need to access the storage. if os.Geteuid() != 0 { aliveLock.Unlock() // Unlock to avoid deadlock as BecomeRootInUserNS will reexec. - pausePid, err := util.GetRootlessPauseProcessPidPath() + pausePid, err := util.GetRootlessPauseProcessPidPathGivenDir(runtime.config.Engine.TmpDir) if err != nil { return errors.Wrapf(err, "could not get pause process pid file path") } @@ -538,6 +538,15 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { return nil } +// TmpDir gets the current Libpod temporary files directory. +func (r *Runtime) TmpDir() (string, error) { + if !r.valid { + return "", define.ErrRuntimeStopped + } + + return r.config.Engine.TmpDir, nil +} + // GetConfig returns a copy of the configuration used by the runtime func (r *Runtime) GetConfig() (*config.Config, error) { r.lock.RLock() diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index e57890fa2..a2d9a875e 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -8,7 +8,6 @@ import ( "net/http" "net/url" "os" - "strings" "github.com/containers/buildah/imagebuildah" "github.com/containers/image/v5/directory" @@ -276,56 +275,47 @@ func DownloadFromFile(reader *os.File) (string, error) { } // LoadImage loads a container image into local storage -func (r *Runtime) LoadImage(ctx context.Context, name, inputFile string, writer io.Writer, signaturePolicy string) (string, error) { - var ( - newImages []*image.Image - err error - src types.ImageReference - ) +func (r *Runtime) LoadImage(ctx context.Context, inputFile string, writer io.Writer, signaturePolicy string) (string, error) { + if newImages, err := r.LoadAllImageFromArchive(ctx, writer, inputFile, signaturePolicy); err == nil { + return newImages, nil + } + return r.LoadImageFromSingleImageArchive(ctx, writer, inputFile, signaturePolicy) +} - if name == "" { - newImages, err = r.ImageRuntime().LoadAllImagesFromDockerArchive(ctx, inputFile, signaturePolicy, writer) - if err == nil { - return getImageNames(newImages), nil - } +// LoadAllImageFromArchive loads all images from the archive of multi-image that inputFile points to. +func (r *Runtime) LoadAllImageFromArchive(ctx context.Context, writer io.Writer, inputFile, signaturePolicy string) (string, error) { + newImages, err := r.ImageRuntime().LoadAllImagesFromDockerArchive(ctx, inputFile, signaturePolicy, writer) + if err == nil { + return getImageNames(newImages), nil } + return "", err +} +// LoadImageFromSingleImageArchive load image from the archive of single image that inputFile points to. +func (r *Runtime) LoadImageFromSingleImageArchive(ctx context.Context, writer io.Writer, inputFile, signaturePolicy string) (string, error) { + var err error for _, referenceFn := range []func() (types.ImageReference, error){ func() (types.ImageReference, error) { return dockerarchive.ParseReference(inputFile) }, func() (types.ImageReference, error) { - return ociarchive.NewReference(inputFile, name) // name may be "" - }, - func() (types.ImageReference, error) { - // prepend "localhost/" to support local image saved with this semantics - if !strings.Contains(name, "/") { - return ociarchive.NewReference(inputFile, fmt.Sprintf("%s/%s", image.DefaultLocalRegistry, name)) - } - return nil, nil + return ociarchive.NewReference(inputFile, "") }, func() (types.ImageReference, error) { return directory.NewReference(inputFile) }, func() (types.ImageReference, error) { - return layout.NewReference(inputFile, name) - }, - func() (types.ImageReference, error) { - // prepend "localhost/" to support local image saved with this semantics - if !strings.Contains(name, "/") { - return layout.NewReference(inputFile, fmt.Sprintf("%s/%s", image.DefaultLocalRegistry, name)) - } - return nil, nil + return layout.NewReference(inputFile, "") }, } { - src, err = referenceFn() + src, err := referenceFn() if err == nil && src != nil { - if newImages, err = r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer); err == nil { + if newImages, err := r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer); err == nil { return getImageNames(newImages), nil } } } - return "", errors.Wrapf(err, "error pulling %q", name) + return "", errors.Wrapf(err, "error pulling image") } func getImageNames(images []*image.Image) string { diff --git a/libpod/runtime_migrate.go b/libpod/runtime_migrate.go index 1ad32fe9c..f0f800ef0 100644 --- a/libpod/runtime_migrate.go +++ b/libpod/runtime_migrate.go @@ -18,9 +18,9 @@ import ( "github.com/sirupsen/logrus" ) -func stopPauseProcess() error { +func (r *Runtime) stopPauseProcess() error { if rootless.IsRootless() { - pausePidPath, err := util.GetRootlessPauseProcessPidPath() + pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(r.config.Engine.TmpDir) if err != nil { return errors.Wrapf(err, "could not get pause process pid file path") } @@ -98,5 +98,5 @@ func (r *Runtime) migrate(ctx context.Context) error { } } - return stopPauseProcess() + return r.stopPauseProcess() } diff --git a/libpod/runtime_migrate_unsupported.go b/libpod/runtime_migrate_unsupported.go index e362cca63..a9d351318 100644 --- a/libpod/runtime_migrate_unsupported.go +++ b/libpod/runtime_migrate_unsupported.go @@ -10,6 +10,6 @@ func (r *Runtime) migrate(ctx context.Context) error { return nil } -func stopPauseProcess() error { +func (r *Runtime) stopPauseProcess() error { return nil } diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index d177b2335..a51dd8ed3 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -390,7 +390,7 @@ func LoadImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file")) return } - id, err := runtime.LoadImage(r.Context(), "", f.Name(), writer, "") + id, err := runtime.LoadImage(r.Context(), f.Name(), writer, "") if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to load image")) return diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index a4bb72140..149050209 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -104,9 +104,6 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { if len(query.Tag) > 0 { output = query.Tag[0] } - if _, found := r.URL.Query()["target"]; found { - output = query.Target - } var additionalNames []string if len(query.Tag) > 1 { @@ -162,7 +159,6 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { reporter := channel.NewWriter(make(chan []byte, 1)) defer reporter.Close() - buildOptions := imagebuildah.BuildOptions{ ContextDirectory: contextDirectory, PullPolicy: pullPolicy, diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index 762f88a68..b4f3aa2f1 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -50,7 +50,7 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { utils.NetworkNotFound(w, name, err) return } - report, err := getNetworkResourceByName(name, runtime, nil) + report, err := getNetworkResourceByNameOrID(name, runtime, nil) if err != nil { utils.InternalServerError(w, err) return @@ -58,7 +58,7 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, report) } -func getNetworkResourceByName(name string, runtime *libpod.Runtime, filters map[string][]string) (*types.NetworkResource, error) { +func getNetworkResourceByNameOrID(nameOrID string, runtime *libpod.Runtime, filters map[string][]string) (*types.NetworkResource, error) { var ( ipamConfigs []dockerNetwork.IPAMConfig ) @@ -68,7 +68,7 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime, filters map[ } containerEndpoints := map[string]types.EndpointResource{} // Get the network path so we can get created time - networkConfigPath, err := network.GetCNIConfigPathByName(config, name) + networkConfigPath, err := network.GetCNIConfigPathByNameOrID(config, nameOrID) if err != nil { return nil, err } @@ -116,7 +116,7 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime, filters map[ if err != nil { return nil, err } - if netData, ok := data.NetworkSettings.Networks[name]; ok { + if netData, ok := data.NetworkSettings.Networks[conf.Name]; ok { containerEndpoint := types.EndpointResource{ Name: netData.NetworkID, EndpointID: netData.EndpointID, @@ -128,8 +128,8 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime, filters map[ } } report := types.NetworkResource{ - Name: name, - ID: name, + Name: conf.Name, + ID: network.GetNetworkID(conf.Name), Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert Scope: "", Driver: network.DefaultNetworkDriver, @@ -199,7 +199,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) { var reports []*types.NetworkResource logrus.Errorf("netNames: %q", strings.Join(netNames, ", ")) for _, name := range netNames { - report, err := getNetworkResourceByName(name, runtime, query.Filters) + report, err := getNetworkResourceByNameOrID(name, runtime, query.Filters) if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index be5a394de..6145207ca 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -336,7 +336,7 @@ func ImagesLoad(w http.ResponseWriter, r *http.Request) { } tmpfile.Close() - loadedImage, err := runtime.LoadImage(context.Background(), query.Reference, tmpfile.Name(), os.Stderr, "") + loadedImage, err := runtime.LoadImage(context.Background(), tmpfile.Name(), os.Stderr, "") if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to load image")) return diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go index 193b05e6d..e6c85d244 100644 --- a/pkg/api/server/register_networks.go +++ b/pkg/api/server/register_networks.go @@ -68,6 +68,7 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { // description: | // JSON encoded value of the filters (a map[string][]string) to process on the network list. Currently available filters: // - name=[name] Matches network name (accepts regex). + // - id=[id] Matches for full or partial ID. // - driver=[driver] Only bridge is supported. // - label=[key] or label=[key=value] Matches networks based on the presence of a label alone or a label and a value. // produces: @@ -225,6 +226,7 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { // description: | // JSON encoded value of the filters (a map[string][]string) to process on the network list. Available filters: // - name=[name] Matches network name (accepts regex). + // - id=[id] Matches for full or partial ID. // - driver=[driver] Only bridge is supported. // - label=[key] or label=[key=value] Matches networks based on the presence of a label alone or a label and a value. // - plugin=[plugin] Matches CNI plugins included in a network (e.g `bridge`,`portmap`,`firewall`,`tuning`,`dnsname`,`macvlan`) diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 31435ae91..a5683796a 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -152,7 +152,7 @@ func pingNewConnection(ctx context.Context) error { return err } // the ping endpoint sits at / in this case - response, err := client.DoRequest(nil, http.MethodGet, "../../../_ping", nil, nil) + response, err := client.DoRequest(nil, http.MethodGet, "/_ping", nil, nil) if err != nil { return err } diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index ef0e15264..1b523f06a 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -458,7 +458,7 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if !opts.Quiet { writer = os.Stderr } - name, err := ir.Libpod.LoadImage(ctx, opts.Name, opts.Input, writer, opts.SignaturePolicy) + name, err := ir.Libpod.LoadImage(ctx, opts.Input, writer, opts.SignaturePolicy) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 72fd98ac1..ec2532bea 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" @@ -86,7 +87,11 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) return nil } - pausePidPath, err := util.GetRootlessPauseProcessPidPath() + tmpDir, err := ic.Libpod.TmpDir() + if err != nil { + return err + } + pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(tmpDir) if err != nil { return errors.Wrapf(err, "could not get pause process pid file path") } @@ -112,7 +117,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) } became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths) - if err := movePauseProcessToScope(); err != nil { + if err := movePauseProcessToScope(ic.Libpod); err != nil { conf, err := ic.Config(context.Background()) if err != nil { return err @@ -133,8 +138,12 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) return nil } -func movePauseProcessToScope() error { - pausePidPath, err := util.GetRootlessPauseProcessPidPath() +func movePauseProcessToScope(r *libpod.Runtime) error { + tmpDir, err := r.TmpDir() + if err != nil { + return err + } + pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(tmpDir) if err != nil { return errors.Wrapf(err, "could not get pause process pid file path") } diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index 2d40dba8f..1808f99b8 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -4,13 +4,16 @@ import ( "fmt" "io/ioutil" "os" + "path" "path/filepath" "strings" "github.com/containers/podman/v2/pkg/rootless" + "github.com/containers/podman/v2/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -137,22 +140,33 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error { return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":")) } -func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.Generator) { +func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask []string, g *generate.Generator) { + defaultMaskPaths := []string{"/proc/acpi", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/proc/scsi", + "/sys/firmware", + "/sys/fs/selinux", + "/sys/dev/block", + } + + unmaskAll := false + if unmask != nil && unmask[0] == "ALL" { + unmaskAll = true + } + if !privileged { - for _, mp := range []string{ - "/proc/acpi", - "/proc/kcore", - "/proc/keys", - "/proc/latency_stats", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug", - "/proc/scsi", - "/sys/firmware", - "/sys/fs/selinux", - "/sys/dev", - } { - g.AddLinuxMaskedPaths(mp) + if !unmaskAll { + for _, mp := range defaultMaskPaths { + // check that the path to mask is not in the list of paths to unmask + if !util.StringInSlice(mp, unmask) { + g.AddLinuxMaskedPaths(mp) + } + } } if pidModeIsHost && rootless.IsRootless() { @@ -170,6 +184,15 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate. g.AddLinuxReadonlyPaths(rp) } } + + // mask the paths provided by the user + for _, mp := range mask { + if !path.IsAbs(mp) && mp != "" { + logrus.Errorf("Path %q is not an absolute path, skipping...", mp) + continue + } + g.AddLinuxMaskedPaths(mp) + } } // based on getDevices from runc (libcontainer/devices/devices.go) diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 8454458a8..0368ab205 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -298,7 +298,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt } } - BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), &g) + BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) for name, val := range s.Env { g.AddProcessEnv(name, val) diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index fad2406e5..964b89fa4 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -307,6 +307,13 @@ type ContainerSecurityConfig struct { Umask string `json:"umask,omitempty"` // ProcOpts are the options used for the proc mount. ProcOpts []string `json:"procfs_opts,omitempty"` + // Mask is the path we want to mask in the container. This masks the paths + // given in addition to the default list. + // Optional + Mask []string `json:"mask,omitempty"` + // Unmask is the path we want to unmask in the container. To override + // all the default paths that are masked, set unmask=ALL. + Unmask []string `json:"unmask,omitempty"` } // ContainerCgroupConfig contains configuration information about a container's diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index 2d636a7cb..a63c76415 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -99,7 +99,8 @@ func GetRootlessConfigHomeDir() (string, error) { } // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for -// the pause process +// the pause process. +// DEPRECATED - switch to GetRootlessPauseProcessPidPathGivenDir func GetRootlessPauseProcessPidPath() (string, error) { runtimeDir, err := GetRuntimeDir() if err != nil { @@ -107,3 +108,13 @@ func GetRootlessPauseProcessPidPath() (string, error) { } return filepath.Join(runtimeDir, "libpod", "pause.pid"), nil } + +// GetRootlessPauseProcessPidPathGivenDir returns the path to the file that +// holds the PID of the pause process, given the location of Libpod's temporary +// files. +func GetRootlessPauseProcessPidPathGivenDir(libpodTmpDir string) (string, error) { + if libpodTmpDir == "" { + return "", errors.Errorf("must provide non-empty tmporary directory") + } + return filepath.Join(libpodTmpDir, "pause.pid"), nil +} diff --git a/pkg/util/utils_windows.go b/pkg/util/utils_windows.go index 9bba2d1ee..46ca5e7f1 100644 --- a/pkg/util/utils_windows.go +++ b/pkg/util/utils_windows.go @@ -25,6 +25,12 @@ func GetRootlessPauseProcessPidPath() (string, error) { return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") } +// GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for +// the pause process +func GetRootlessPauseProcessPidPathGivenDir(unused string) (string, error) { + return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") +} + // GetRuntimeDir returns the runtime directory func GetRuntimeDir() (string, error) { return "", errors.New("this function is not implemented for windows") diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at index d9556d59f..0ce56ee3c 100644 --- a/test/apiv2/35-networks.at +++ b/test/apiv2/35-networks.at @@ -38,9 +38,19 @@ length=2 # filters={"label":["abc"]} t GET networks?filters=%7B%22label%22%3A%5B%22abc%22%5D%7D 200 \ length=1 -# invalid filter filters={"id":["abc"]} -t GET networks?filters=%7B%22id%22%3A%5B%22abc%22%5D%7D 500 \ -.cause='invalid filter "id"' +# id filter filters={"id":["a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1"]} +t GET networks?filters=%7B%22id%22%3A%5B%22a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1%22%5D%7D 200 \ +length=1 \ +.[0].Name=network1 \ +.[0].Id=a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 +# invalid filter filters={"dangling":["1"]} +t GET networks?filters=%7B%22dangling%22%3A%5B%221%22%5D%7D 500 \ +.cause='invalid filter "dangling"' + +# network inspect docker +t GET networks/a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 200 \ +.Name=network1 \ +.Id=a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 # clean the network t DELETE libpod/networks/network1 200 \ diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index b69d2597e..281b2c313 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -278,7 +278,7 @@ WORKDIR /test It("podman images sort by values", func() { sortValueTest := func(value string, result int, format string) []string { f := fmt.Sprintf("{{.%s}}", format) - session := podmanTest.Podman([]string{"images", "--sort", value, "--format", f}) + session := podmanTest.Podman([]string{"images", "--noheading", "--sort", value, "--format", f}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(result)) diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index ad6af61c7..ffc914bc2 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -135,6 +135,40 @@ var _ = Describe("Podman network", func() { Expect(session.LineInOutputContains(name)).To(BeFalse()) }) + It("podman network ID test", func() { + net := "networkIDTest" + // the network id should be the sha256 hash of the network name + netID := "6073aefe03cdf8f29be5b23ea9795c431868a3a22066a6290b187691614fee84" + session := podmanTest.Podman([]string{"network", "create", net}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net) + Expect(session.ExitCode()).To(BeZero()) + + session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}} {{.ID}}", "--filter", "id=" + netID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(net + " " + netID[:12])) + + session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}} {{.ID}}", "--filter", "id=" + netID[10:50], "--no-trunc"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(net + " " + netID)) + + session = podmanTest.Podman([]string{"network", "inspect", netID[:40]}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(net)) + + session = podmanTest.Podman([]string{"network", "inspect", netID[1:]}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).ToNot(BeZero()) + Expect(session.ErrorToString()).To(ContainSubstring("no such network")) + + session = podmanTest.Podman([]string{"network", "rm", netID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + }) + rm_func := func(rm string) { It(fmt.Sprintf("podman network %s no args", rm), func() { session := podmanTest.Podman([]string{"network", rm}) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 0d65a3e59..efc125d2b 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -233,6 +233,39 @@ var _ = Describe("Podman run", func() { return jsonFile } + It("podman run mask and unmask path test", func() { + session := podmanTest.Podman([]string{"run", "-d", "--name=maskCtr1", "--security-opt", "unmask=ALL", "--security-opt", "mask=/proc/acpi", ALPINE, "sleep", "200"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/sys/firmware"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(Not(BeEmpty())) + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/proc/acpi"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(BeEmpty()) + + session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr2", "--security-opt", "unmask=/proc/acpi:/sys/firmware", ALPINE, "sleep", "200"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/sys/firmware"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(Not(BeEmpty())) + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/proc/acpi"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(Not(BeEmpty())) + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr3", "--security-opt", "mask=/sys/power/disk", ALPINE, "sleep", "200"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr3", "cat", "/sys/power/disk"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(BeEmpty()) + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman run seccomp test", func() { session := podmanTest.Podman([]string{"run", "-it", "--security-opt", strings.Join([]string{"seccomp=", forbidGetCWDSeccompProfile()}, ""), ALPINE, "pwd"}) session.WaitWithDefaultTimeout() diff --git a/test/system/010-images.bats b/test/system/010-images.bats index ee6da30ec..76caf282b 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -199,9 +199,16 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z local format=$2 run_podman images --sort repository --format "$format" - _check_line 0 ${aaa_name} ${aaa_tag} - _check_line 1 "${PODMAN_TEST_IMAGE_REGISTRY}/${PODMAN_TEST_IMAGE_USER}/${PODMAN_TEST_IMAGE_NAME}" "${PODMAN_TEST_IMAGE_TAG}" - _check_line 2 ${zzz_name} ${zzz_tag} + + line_no=0 + if [[ $format == table* ]]; then + # skip headers from table command + line_no=1 + fi + + _check_line $line_no ${aaa_name} ${aaa_tag} + _check_line $((line_no+1)) "${PODMAN_TEST_IMAGE_REGISTRY}/${PODMAN_TEST_IMAGE_USER}/${PODMAN_TEST_IMAGE_NAME}" "${PODMAN_TEST_IMAGE_TAG}" + _check_line $((line_no+2)) ${zzz_name} ${zzz_tag} } # Begin the test: tag $IMAGE with both the given names diff --git a/test/system/120-load.bats b/test/system/120-load.bats index 8ea9b1c69..272e2ae93 100644 --- a/test/system/120-load.bats +++ b/test/system/120-load.bats @@ -28,12 +28,15 @@ verify_iid_and_name() { @test "podman save to pipe and load" { # Generate a random name and tag (must be lower-case) - local random_name=x$(random_string 12 | tr A-Z a-z) - local random_tag=t$(random_string 7 | tr A-Z a-z) + local random_name=x0$(random_string 12 | tr A-Z a-z) + local random_tag=t0$(random_string 7 | tr A-Z a-z) local fqin=localhost/$random_name:$random_tag run_podman tag $IMAGE $fqin - archive=$PODMAN_TMPDIR/myimage-$(random_string 8).tar + # Believe it or not, 'podman load' would barf if any path element + # included a capital letter + archive=$PODMAN_TMPDIR/MySubDirWithCaps/MyImage-$(random_string 8).tar + mkdir -p $(dirname $archive) # We can't use run_podman because that uses the BATS 'run' function # which redirects stdout and stderr. Here we need to guarantee @@ -51,19 +54,20 @@ verify_iid_and_name() { run_podman images $fqin --format '{{.Repository}}:{{.Tag}}' is "$output" "$fqin" "image preserves name across save/load" - # FIXME: when/if 7337 gets fixed, load with a new tag - if false; then - local new_name=x$(random_string 14 | tr A-Z a-z) - local new_tag=t$(random_string 6 | tr A-Z a-z) + # Load with a new tag + local new_name=x1$(random_string 14 | tr A-Z a-z) + local new_tag=t1$(random_string 6 | tr A-Z a-z) run_podman rmi $fqin - fqin=localhost/$new_name:$new_tag - run_podman load -i $archive $fqin - run_podman images $fqin --format '{{.Repository}}:{{.Tag}}' - is "$output" "$fqin" "image can be loaded with new name:tag" - fi + + new_fqin=localhost/$new_name:$new_tag + run_podman load -i $archive $new_fqin + run_podman images --format '{{.Repository}}:{{.Tag}}' --sort tag + is "${lines[0]}" "$IMAGE" "image is preserved" + is "${lines[1]}" "$fqin" "image is reloaded with old fqin" + is "${lines[2]}" "$new_fqin" "image is reloaded with new fqin too" # Clean up - run_podman rmi $fqin + run_podman rmi $fqin $new_fqin } diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats index 142d7dcd9..20fdd068f 100644 --- a/test/system/400-unprivileged-access.bats +++ b/test/system/400-unprivileged-access.bats @@ -118,7 +118,7 @@ EOF /proc/scsi /sys/firmware /sys/fs/selinux - /sys/dev + /sys/dev/block ) # Some of the above may not exist on our host. Find only the ones that do. |