diff options
30 files changed, 455 insertions, 395 deletions
@@ -36,7 +36,7 @@ PKG_MANAGER ?= $(shell command -v dnf yum|head -n1) # ~/.local/bin is not in PATH on all systems PRE_COMMIT = $(shell command -v bin/venv/bin/pre-commit ~/.local/bin/pre-commit pre-commit | head -n1) -SOURCES = $(shell find . -name "*.go") +SOURCES = $(shell find . -path './.*' -prune -o -name "*.go") GO_BUILD=$(GO) build # Go module support: set `-mod=vendor` to use the vendored sources diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 85b344b3c..7550bf784 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -47,6 +47,12 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string if err != nil { return err } + if s.ResourceLimits == nil { + s.ResourceLimits = &specs.LinuxResources{} + } + if s.ResourceLimits.Memory == nil { + s.ResourceLimits.Memory = &specs.LinuxMemory{} + } if m := c.Memory; len(m) > 0 { ml, err := units.RAMInBytes(m) if err != nil { @@ -81,6 +87,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } s.ResourceLimits.Memory.Kernel = &mk } + if s.ResourceLimits.BlockIO == nil { + s.ResourceLimits.BlockIO = &specs.LinuxBlockIO{} + } if b := c.BlkIOWeight; len(b) > 0 { u, err := strconv.ParseUint(b, 10, 16) if err != nil { @@ -313,14 +322,16 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.StopSignal = &stopSignal } } - swappiness := uint64(c.MemorySwappiness) if s.ResourceLimits == nil { s.ResourceLimits = &specs.LinuxResources{} } if s.ResourceLimits.Memory == nil { s.ResourceLimits.Memory = &specs.LinuxMemory{} } - s.ResourceLimits.Memory.Swappiness = &swappiness + if c.MemorySwappiness >= 0 { + swappiness := uint64(c.MemorySwappiness) + s.ResourceLimits.Memory.Swappiness = &swappiness + } if s.LogConfiguration == nil { s.LogConfiguration = &specgen.LogConfig{} @@ -332,7 +343,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string if s.ResourceLimits.Pids == nil { s.ResourceLimits.Pids = &specs.LinuxPids{} } - s.ResourceLimits.Pids.Limit = c.PIDsLimit + if c.PIDsLimit > 0 { + s.ResourceLimits.Pids.Limit = c.PIDsLimit + } if c.CGroups == "disabled" && c.PIDsLimit > 0 { s.ResourceLimits.Pids.Limit = -1 } @@ -411,6 +424,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } } + s.SeccompPolicy = c.SeccompPolicy // TODO any idea why this was done // storage.go from spec/ // grab it @@ -507,18 +521,28 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string if s.ResourceLimits.CPU == nil { s.ResourceLimits.CPU = &specs.LinuxCPU{} } - s.ResourceLimits.CPU.Shares = &c.CPUShares - s.ResourceLimits.CPU.Period = &c.CPUPeriod - - // TODO research these - //s.ResourceLimits.CPU.Cpus = c.CPUS - //s.ResourceLimits.CPU.Cpus = c.CPUSetCPUs + if c.CPUShares > 0 { + s.ResourceLimits.CPU.Shares = &c.CPUShares + } + if c.CPUPeriod > 0 { + s.ResourceLimits.CPU.Period = &c.CPUPeriod + } - //s.ResourceLimits.CPU. = c.CPUSetCPUs - s.ResourceLimits.CPU.Mems = c.CPUSetMems - s.ResourceLimits.CPU.Quota = &c.CPUQuota - s.ResourceLimits.CPU.RealtimePeriod = &c.CPURTPeriod - s.ResourceLimits.CPU.RealtimeRuntime = &c.CPURTRuntime + if c.CPUSetCPUs != "" { + s.ResourceLimits.CPU.Cpus = c.CPUSetCPUs + } + if c.CPUSetMems != "" { + s.ResourceLimits.CPU.Mems = c.CPUSetMems + } + if c.CPUQuota > 0 { + s.ResourceLimits.CPU.Quota = &c.CPUQuota + } + if c.CPURTPeriod > 0 { + s.ResourceLimits.CPU.RealtimePeriod = &c.CPURTPeriod + } + if c.CPURTRuntime > 0 { + s.ResourceLimits.CPU.RealtimeRuntime = &c.CPURTRuntime + } s.OOMScoreAdj = &c.OOMScoreAdj s.RestartPolicy = c.Restart s.Remove = c.Rm diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 292d5c1ad..0843789eb 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -3,6 +3,7 @@ package containers import ( "fmt" + "github.com/containers/common/pkg/config" "github.com/containers/libpod/cmd/podman/common" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/pkg/domain/entities" @@ -61,6 +62,11 @@ func create(cmd *cobra.Command, args []string) error { if err := createInit(cmd); err != nil { return err } + + if err := pullImage(args[0]); err != nil { + return err + } + //TODO rootfs still s := specgen.NewSpecGenerator(rawImageInput) if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { @@ -100,3 +106,27 @@ func createInit(c *cobra.Command) error { return nil } + +func pullImage(imageName string) error { + br, err := registry.ImageEngine().Exists(registry.GetContext(), imageName) + if err != nil { + return err + } + pullPolicy, err := config.ValidatePullPolicy(cliVals.Pull) + if err != nil { + return err + } + if !br.Value || pullPolicy == config.PullImageAlways { + if pullPolicy == config.PullImageNever { + return errors.New("unable to find a name and tag match for busybox in repotags: no such image") + } + _, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{ + Authfile: cliVals.Authfile, + Quiet: cliVals.Quiet, + }) + if pullErr != nil { + return pullErr + } + } + return nil +} diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go index ebc0d8ea1..046dac53e 100644 --- a/cmd/podman/containers/diff.go +++ b/cmd/podman/containers/diff.go @@ -45,7 +45,11 @@ func diff(cmd *cobra.Command, args []string) error { return errors.New("container must be specified: podman container diff [options [...]] ID-NAME") } - results, err := registry.ContainerEngine().ContainerDiff(registry.GetContext(), args[0], entities.DiffOptions{}) + var id string + if len(args) > 0 { + id = args[0] + } + results, err := registry.ContainerEngine().ContainerDiff(registry.GetContext(), id, *diffOpts) if err != nil { return err } diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 151f71885..9d222e44d 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -5,7 +5,6 @@ import ( "os" "strings" - "github.com/containers/common/pkg/config" "github.com/containers/libpod/cmd/podman/common" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/libpod/define" @@ -72,26 +71,10 @@ func run(cmd *cobra.Command, args []string) error { return err } - br, err := registry.ImageEngine().Exists(registry.GetContext(), args[0]) - if err != nil { - return err - } - pullPolicy, err := config.ValidatePullPolicy(cliVals.Pull) - if err != nil { + if err := pullImage(args[0]); err != nil { return err } - if !br.Value || pullPolicy == config.PullImageAlways { - if pullPolicy == config.PullImageNever { - return errors.New("unable to find a name and tag match for busybox in repotags: no such image") - } - _, pullErr := registry.ImageEngine().Pull(registry.GetContext(), args[0], entities.ImagePullOptions{ - Authfile: cliVals.Authfile, - Quiet: cliVals.Quiet, - }) - if pullErr != nil { - return pullErr - } - } + // If -i is not set, clear stdin if !cliVals.Interactive { runOpts.InputStream = nil diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 8db76e8af..ec94c0918 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -46,10 +46,9 @@ func init() { } func diff(cmd *cobra.Command, args []string) error { - if found, err := registry.ImageEngine().Exists(registry.GetContext(), args[0]); err != nil { - return err - } else if found.Value { - return images.Diff(cmd, args, diffOpts) + // Latest implies looking for a container + if diffOpts.Latest { + return containers.Diff(cmd, args, diffOpts) } if found, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), args[0]); err != nil { @@ -57,5 +56,12 @@ func diff(cmd *cobra.Command, args []string) error { } else if found.Value { return containers.Diff(cmd, args, diffOpts) } + + if found, err := registry.ImageEngine().Exists(registry.GetContext(), args[0]); err != nil { + return err + } else if found.Value { + return images.Diff(cmd, args, diffOpts) + } + return fmt.Errorf("%s not found on system", args[0]) } diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go index dd98dc4d6..7cfacfc6c 100644 --- a/cmd/podman/images/diff.go +++ b/cmd/podman/images/diff.go @@ -11,8 +11,8 @@ import ( var ( // podman container _inspect_ diffCmd = &cobra.Command{ - Use: "diff [flags] CONTAINER", - Args: registry.IdOrLatestArgs, + Use: "diff [flags] IMAGE", + Args: cobra.ExactArgs(1), Short: "Inspect changes on image's file systems", Long: `Displays changes on a image's filesystem. The image will be compared to its parent layer.`, RunE: diff, @@ -32,16 +32,16 @@ func init() { diffOpts = &entities.DiffOptions{} flags := diffCmd.Flags() flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") - _ = flags.MarkHidden("archive") + _ = flags.MarkDeprecated("archive", "Provided for backwards compatibility, has no impact on output.") flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") } func diff(cmd *cobra.Command, args []string) error { - if len(args) == 0 && !diffOpts.Latest { - return errors.New("image must be specified: podman image diff [options [...]] ID-NAME") + if diffOpts.Latest { + return errors.New("image diff does not support --latest") } - results, err := registry.ImageEngine().Diff(registry.GetContext(), args[0], entities.DiffOptions{}) + results, err := registry.ImageEngine().Diff(registry.GetContext(), args[0], *diffOpts) if err != nil { return err } diff --git a/cmd/podman/images/inspect.go b/cmd/podman/images/inspect.go index 4482ceee5..11bef02ba 100644 --- a/cmd/podman/images/inspect.go +++ b/cmd/podman/images/inspect.go @@ -20,11 +20,13 @@ import ( var ( // Command: podman image _inspect_ inspectCmd = &cobra.Command{ - Use: "inspect [flags] IMAGE", - Short: "Display the configuration of an image", - Long: `Displays the low-level information on an image identified by name or ID.`, - RunE: inspect, - Example: `podman image inspect alpine`, + Use: "inspect [flags] IMAGE", + Short: "Display the configuration of an image", + Long: `Displays the low-level information on an image identified by name or ID.`, + RunE: inspect, + Example: `podman inspect alpine + podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine + podman inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`, } inspectOpts *entities.InspectOptions ) @@ -39,14 +41,14 @@ func init() { } func inspect(cmd *cobra.Command, args []string) error { - latestContainer := inspectOpts.Latest - - if len(args) == 0 && !latestContainer { - return errors.Errorf("container or image name must be specified: podman inspect [options [...]] name") + if inspectOpts.Size { + return fmt.Errorf("--size can only be used for containers") } - - if len(args) > 0 && latestContainer { - return errors.Errorf("you cannot provide additional arguments with --latest") + if inspectOpts.Latest { + return fmt.Errorf("--latest can only be used for containers") + } + if len(args) == 0 { + return errors.Errorf("image name must be specified: podman image inspect [options [...]] name") } results, err := registry.ImageEngine().Inspect(context.Background(), args, *inspectOpts) diff --git a/cmd/podman/images/tree.go b/cmd/podman/images/tree.go new file mode 100644 index 000000000..5e82e9dea --- /dev/null +++ b/cmd/podman/images/tree.go @@ -0,0 +1,40 @@ +package images + +import ( + "fmt" + + "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + treeDescription = "Prints layer hierarchy of an image in a tree format" + treeCmd = &cobra.Command{ + Use: "tree [flags] IMAGE", + Args: cobra.ExactArgs(1), + Short: treeDescription, + Long: treeDescription, + RunE: tree, + Example: "podman image tree alpine:latest", + } + treeOpts entities.ImageTreeOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: treeCmd, + Parent: imageCmd, + }) + treeCmd.Flags().BoolVar(&treeOpts.WhatRequires, "whatrequires", false, "Show all child images and layers of the specified image") +} + +func tree(_ *cobra.Command, args []string) error { + results, err := registry.ImageEngine().Tree(registry.Context(), args[0], treeOpts) + if err != nil { + return err + } + fmt.Println(results.Tree) + return nil +} diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index e67bc326b..93bf58bdd 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -1,10 +1,8 @@ package main import ( - "context" "fmt" - "github.com/containers/image/v5/docker/reference" "github.com/containers/libpod/cmd/podman/common" "github.com/containers/libpod/cmd/podman/containers" "github.com/containers/libpod/cmd/podman/images" @@ -21,11 +19,12 @@ var ( // Command: podman _inspect_ Object_ID inspectCmd = &cobra.Command{ Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}", - Args: cobra.ExactArgs(1), Short: "Display the configuration of object denoted by ID", Long: "Displays the low-level information on an object identified by name or ID", TraverseChildren: true, RunE: inspect, + Example: `podman inspect alpine + podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine`, } ) @@ -35,21 +34,25 @@ func init() { Command: inspectCmd, }) inspectOpts = common.AddInspectFlagSet(inspectCmd) + flags := inspectCmd.Flags() + flags.StringVarP(&inspectOpts.Type, "type", "t", "", "Return JSON for specified type, (image or container) (default \"all\")") + if !registry.IsRemote() { + flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of (containers only)") + } } func inspect(cmd *cobra.Command, args []string) error { - // First check if the input is even valid for an image - if _, err := reference.Parse(args[0]); err == nil { - if found, err := registry.ImageEngine().Exists(context.Background(), args[0]); err != nil { - return err - } else if found.Value { - return images.Inspect(cmd, args, inspectOpts) + switch inspectOpts.Type { + case "image": + return images.Inspect(cmd, args, inspectOpts) + case "container": + return containers.Inspect(cmd, args, inspectOpts) + case "": + if err := images.Inspect(cmd, args, inspectOpts); err == nil { + return nil } - } - if found, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0]); err != nil { - return err - } else if found.Value { return containers.Inspect(cmd, args, inspectOpts) + default: + return fmt.Errorf("invalid type %q is must be 'container' or 'image'", inspectOpts.Type) } - return fmt.Errorf("%s not found on system", args[0]) } diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index fa1a33faa..e09107b53 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -2,8 +2,10 @@ package system import ( "fmt" + "net/url" "os" "path/filepath" + "syscall" "time" "github.com/containers/libpod/cmd/podman/registry" @@ -57,7 +59,24 @@ func service(cmd *cobra.Command, args []string) error { if err != nil { return err } - logrus.Infof("using API endpoint: \"%s\"", apiURI) + logrus.Infof("using API endpoint: '%s'", apiURI) + + // Clean up any old existing unix domain socket + if len(apiURI) > 0 { + uri, err := url.Parse(apiURI) + if err != nil { + return err + } + + // socket activation uses a unix:// socket in the shipped unit files but apiURI is coded as "" at this layer. + if "unix" == uri.Scheme && !registry.IsRemote() { + if err := syscall.Unlink(uri.Path); err != nil && !os.IsNotExist(err) { + return err + } + mask := syscall.Umask(0177) + defer syscall.Umask(mask) + } + } opts := entities.ServiceOptions{ URI: apiURI, @@ -75,7 +94,6 @@ func service(cmd *cobra.Command, args []string) error { } func resolveApiURI(_url []string) (string, error) { - // When determining _*THE*_ listening endpoint -- // 1) User input wins always // 2) systemd socket activation @@ -83,14 +101,15 @@ func resolveApiURI(_url []string) (string, error) { // 4) if varlink -- adapter.DefaultVarlinkAddress // 5) lastly adapter.DefaultAPIAddress - if _url == nil { + if len(_url) == 0 { if v, found := os.LookupEnv("PODMAN_SOCKET"); found { + logrus.Debugf("PODMAN_SOCKET='%s' used to determine API endpoint", v) _url = []string{v} } } switch { - case len(_url) > 0: + case len(_url) > 0 && _url[0] != "": return _url[0], nil case systemd.SocketActivated(): logrus.Info("using systemd socket activation to determine API endpoint") diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go index 8558c149b..26fd2cab4 100644 --- a/libpod/define/pod_inspect.go +++ b/libpod/define/pod_inspect.go @@ -18,6 +18,8 @@ type InspectPodData struct { Namespace string `json:"Namespace,omitempty"` // Created is the time when the pod was created. Created time.Time + // State represents the current state of the pod. + State string `json:"State"` // Hostname is the hostname that the pod will set. Hostname string // Labels is a set of key-value labels that have been applied to the diff --git a/libpod/image/image.go b/libpod/image/image.go index 7198a42a3..bbf803056 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -867,22 +867,77 @@ func (i *Image) Intermediate(ctx context.Context) (bool, error) { return false, nil } +// User returns the image's user +func (i *Image) User(ctx context.Context) (string, error) { + imgInspect, err := i.inspect(ctx, false) + if err != nil { + return "", err + } + return imgInspect.Config.User, nil +} + +// StopSignal returns the image's StopSignal +func (i *Image) StopSignal(ctx context.Context) (string, error) { + imgInspect, err := i.inspect(ctx, false) + if err != nil { + return "", err + } + return imgInspect.Config.StopSignal, nil +} + +// WorkingDir returns the image's WorkingDir +func (i *Image) WorkingDir(ctx context.Context) (string, error) { + imgInspect, err := i.inspect(ctx, false) + if err != nil { + return "", err + } + return imgInspect.Config.WorkingDir, nil +} + +// Cmd returns the image's cmd +func (i *Image) Cmd(ctx context.Context) ([]string, error) { + imgInspect, err := i.inspect(ctx, false) + if err != nil { + return nil, err + } + return imgInspect.Config.Cmd, nil +} + +// Entrypoint returns the image's entrypoint +func (i *Image) Entrypoint(ctx context.Context) ([]string, error) { + imgInspect, err := i.inspect(ctx, false) + if err != nil { + return nil, err + } + return imgInspect.Config.Entrypoint, nil +} + +// Env returns the image's env +func (i *Image) Env(ctx context.Context) ([]string, error) { + imgInspect, err := i.imageInspectInfo(ctx) + if err != nil { + return nil, err + } + return imgInspect.Env, nil +} + // Labels returns the image's labels func (i *Image) Labels(ctx context.Context) (map[string]string, error) { imgInspect, err := i.imageInspectInfo(ctx) if err != nil { - return nil, nil + return nil, err } return imgInspect.Labels, nil } // GetLabel Returns a case-insensitive match of a given label func (i *Image) GetLabel(ctx context.Context, label string) (string, error) { - imageLabels, err := i.Labels(ctx) + labels, err := i.Labels(ctx) if err != nil { return "", err } - for k, v := range imageLabels { + + for k, v := range labels { if strings.ToLower(k) == strings.ToLower(label) { return v, nil } diff --git a/libpod/pod_api.go b/libpod/pod_api.go index ed4dc0727..45aa5cb8d 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -446,6 +446,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { if err != nil { return nil, err } + ctrStatuses := make(map[string]define.ContainerStatus, len(containers)) for _, c := range containers { containerStatus := "unknown" // Ignoring possible errors here because we don't want this to be @@ -459,12 +460,18 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { Name: c.Name(), State: containerStatus, }) + ctrStatuses[c.ID()] = c.state.State + } + podState, err := CreatePodStatusResults(ctrStatuses) + if err != nil { + return nil, err } inspectData := define.InspectPodData{ ID: p.ID(), Name: p.Name(), Namespace: p.Namespace(), Created: p.CreatedTime(), + State: podState, Hostname: "", Labels: p.Labels(), CreateCgroup: false, diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 5f1a86183..9576fd437 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -51,7 +51,7 @@ func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, list func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { // If listener not provided try socket activation protocol if listener == nil { - if _, found := os.LookupEnv("LISTEN_FDS"); !found { + if _, found := os.LookupEnv("LISTEN_PID"); !found { return nil, errors.Errorf("Cannot create API Server, no listener provided and socket activation protocol is not active.") } @@ -125,7 +125,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li if err != nil { methods = []string{"<N/A>"} } - logrus.Debugf("Methods: %s Path: %s", strings.Join(methods, ", "), path) + logrus.Debugf("Methods: %6s Path: %s", strings.Join(methods, ", "), path) return nil }) } @@ -179,6 +179,7 @@ func (s *APIServer) Shutdown() error { } // Gracefully shutdown server, duration of wait same as idle window + // TODO: Should we really wait the idle window for shutdown? ctx, cancel := context.WithTimeout(context.Background(), s.idleTracker.Duration) defer cancel() go func() { diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 4fe4dd72d..29b6f04ec 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -126,7 +126,7 @@ func tcpClient(_url *url.URL) (*http.Client, error) { return &http.Client{ Transport: &http.Transport{ DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { - return net.Dial("tcp", _url.Path) + return net.Dial("tcp", _url.Host) }, DisableCompression: true, }, diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 3550c3968..e0f523ebd 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -74,7 +74,7 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.Image return &inspectedData, response.Process(&inspectedData) } -func ImageTree(ctx context.Context, nameOrId string) error { +func Tree(ctx context.Context, nameOrId string) error { return bindings.ErrNotImplemented } diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index 4d682a522..8a0b9c7a6 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -174,8 +174,7 @@ var _ = Describe("Podman pods", func() { Expect(err).To(BeNil()) response, err := pods.Inspect(bt.conn, newpod) Expect(err).To(BeNil()) - // FIXME sujil please fix this - //Expect(response.Status).To(Equal(define.PodStatePaused)) + Expect(response.State).To(Equal(define.PodStatePaused)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStatePaused)) @@ -186,8 +185,7 @@ var _ = Describe("Podman pods", func() { Expect(err).To(BeNil()) response, err = pods.Inspect(bt.conn, newpod) Expect(err).To(BeNil()) - // FIXME sujil please fix this - //Expect(response.State.Status).To(Equal(define.PodStateRunning)) + Expect(response.State).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStateRunning)) @@ -219,8 +217,7 @@ var _ = Describe("Podman pods", func() { response, err := pods.Inspect(bt.conn, newpod) Expect(err).To(BeNil()) - // FIXME sujil please fix this - //Expect(response.State.Status).To(Equal(define.PodStateRunning)) + Expect(response.State).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStateRunning)) @@ -234,8 +231,7 @@ var _ = Describe("Podman pods", func() { _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) response, _ = pods.Inspect(bt.conn, newpod) - // FIXME sujil please fix this - //Expect(response.State.Status).To(Equal(define.PodStateExited)) + Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStateStopped)) @@ -248,8 +244,7 @@ var _ = Describe("Podman pods", func() { _, err = pods.Restart(bt.conn, newpod) Expect(err).To(BeNil()) response, _ = pods.Inspect(bt.conn, newpod) - // FIXME sujil please fix this - //Expect(response.State.Status).To(Equal(define.PodStateRunning)) + Expect(response.State).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStateRunning)) @@ -277,15 +272,15 @@ var _ = Describe("Podman pods", func() { Expect(err).To(BeNil()) response, err := pods.Inspect(bt.conn, newpod) Expect(err).To(BeNil()) - // FIXME sujil please fix this - //Expect(response.State.Status).To(Equal(define.PodStateExited)) + Expect(response.State).To(Equal(define.PodStateExited)) pruneResponse, err = pods.Prune(bt.conn) Expect(err).To(BeNil()) // Validate status and record pod id of pod to be pruned - //Expect(response.State.Status).To(Equal(define.PodStateExited)) - //podID := response.Config.ID + Expect(response.State).To(Equal(define.PodStateExited)) + podID := response.ID // Check if right pod was pruned Expect(len(pruneResponse)).To(Equal(1)) + Expect(pruneResponse[0].Id).To(Equal(podID)) // One pod is pruned hence only one pod should be active. podSummary, err = pods.List(bt.conn, nil) Expect(err).To(BeNil()) @@ -301,8 +296,7 @@ var _ = Describe("Podman pods", func() { Expect(err).To(BeNil()) response, err = pods.Inspect(bt.conn, newpod) Expect(err).To(BeNil()) - // FIXME sujil please fix this - //Expect(response.State.Status).To(Equal(define.PodStateExited)) + Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStateStopped)) @@ -311,8 +305,7 @@ var _ = Describe("Podman pods", func() { Expect(err).To(BeNil()) response, err = pods.Inspect(bt.conn, newpod2) Expect(err).To(BeNil()) - // FIXME sujil please fix this - //Expect(response.State.Status).To(Equal(define.PodStateExited)) + Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStateStopped)) diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index 052e7bee5..b6283b6ad 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -23,5 +23,6 @@ type ImageEngine interface { Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error) Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error + Tree(ctx context.Context, nameOrId string, options ImageTreeOptions) (*ImageTreeReport, error) Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 3a6d159e4..56c4c0ac5 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -273,3 +273,13 @@ type ImageSaveOptions struct { Output string Quiet bool } + +// ImageTreeOptions provides options for ImageEngine.Tree() +type ImageTreeOptions struct { + WhatRequires bool // Show all child images and layers of the specified image +} + +// ImageTreeReport provides results from ImageEngine.Tree() +type ImageTreeReport struct { + Tree string // TODO: Refactor move presentation work out of server +} diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index b89aa869a..096af2df2 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -50,6 +50,7 @@ type InspectOptions struct { Format string `json:",omitempty"` Latest bool `json:",omitempty"` Size bool `json:",omitempty"` + Type string `json:",omitempty"` } // All API and CLI diff commands and diff sub-commands use the same options diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 0f710ad28..4353e0798 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -476,3 +476,15 @@ func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts } return &entities.BuildReport{ID: id}, nil } + +func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { + img, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) + if err != nil { + return nil, err + } + results, err := img.GenerateTree(opts.WhatRequires) + if err != nil { + return nil, err + } + return &entities.ImageTreeReport{Tree: results}, nil +} diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 67593b2dd..078f5404d 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -35,7 +35,7 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { func (ic *ContainerEngine) RestService(_ context.Context, opts entities.ServiceOptions) error { var ( - listener net.Listener + listener *net.Listener err error ) @@ -45,13 +45,14 @@ func (ic *ContainerEngine) RestService(_ context.Context, opts entities.ServiceO return errors.Errorf("%s is an invalid socket destination", opts.URI) } address := strings.Join(fields[1:], ":") - listener, err = net.Listen(fields[0], address) + l, err := net.Listen(fields[0], address) if err != nil { return errors.Wrapf(err, "unable to create socket %s", opts.URI) } + listener = &l } - server, err := api.NewServerWithSettings(ic.Libpod, opts.Timeout, &listener) + server, err := api.NewServerWithSettings(ic.Libpod, opts.Timeout, listener) if err != nil { return err } @@ -62,7 +63,9 @@ func (ic *ContainerEngine) RestService(_ context.Context, opts entities.ServiceO }() err = server.Serve() - _ = listener.Close() + if listener != nil { + _ = (*listener).Close() + } return err } diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 6ea2bd9f2..2decd605d 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -263,3 +263,7 @@ func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) { return nil, errors.New("not implemented yet") } + +func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { + return nil, errors.New("not implemented yet") +} diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index da52a7217..72d461cdc 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -535,8 +535,36 @@ create_pause_process (const char *pause_pid_file_path, char **argv) } } +static void +join_namespace_or_die (int pid_to_join, const char *ns_file) +{ + char ns_path[PATH_MAX]; + int ret; + int fd; + + ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file); + if (ret == PATH_MAX) + { + fprintf (stderr, "internal error: namespace path too long\n"); + _exit (EXIT_FAILURE); + } + + fd = open (ns_path, O_CLOEXEC | O_RDONLY); + if (fd < 0) + { + fprintf (stderr, "cannot open: %s\n", ns_path); + _exit (EXIT_FAILURE); + } + if (setns (fd, 0) < 0) + { + fprintf (stderr, "cannot set namespace to %s: %s\n", ns_path, strerror (errno)); + _exit (EXIT_FAILURE); + } + close (fd); +} + int -reexec_userns_join (int userns, int mountns, char *pause_pid_file_path) +reexec_userns_join (int pid_to_join, char *pause_pid_file_path) { char uid[16]; char gid[16]; @@ -606,19 +634,8 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path) _exit (EXIT_FAILURE); } - if (setns (userns, 0) < 0) - { - fprintf (stderr, "cannot setns: %s\n", strerror (errno)); - _exit (EXIT_FAILURE); - } - close (userns); - - if (mountns >= 0 && setns (mountns, 0) < 0) - { - fprintf (stderr, "cannot setns: %s\n", strerror (errno)); - _exit (EXIT_FAILURE); - } - close (mountns); + join_namespace_or_die (pid_to_join, "user"); + join_namespace_or_die (pid_to_join, "mnt"); if (syscall_setresgid (0, 0, 0) < 0) { diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 5ddfab7ad..3de136f12 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -31,7 +31,7 @@ extern uid_t rootless_uid(); extern uid_t rootless_gid(); extern int reexec_in_user_namespace(int ready, char *pause_pid_file_path, char *file_to_read, int fd); extern int reexec_in_user_namespace_wait(int pid, int options); -extern int reexec_userns_join(int userns, int mountns, char *pause_pid_file_path); +extern int reexec_userns_join(int pid, char *pause_pid_file_path); */ import "C" @@ -124,91 +124,6 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) return nil } -func readUserNs(path string) (string, error) { - b := make([]byte, 256) - _, err := unix.Readlink(path, b) - if err != nil { - return "", err - } - return string(b), nil -} - -func readUserNsFd(fd uintptr) (string, error) { - return readUserNs(fmt.Sprintf("/proc/self/fd/%d", fd)) -} - -func getParentUserNs(fd uintptr) (uintptr, error) { - const nsGetParent = 0xb702 - ret, _, errno := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(nsGetParent), 0) - if errno != 0 { - return 0, errno - } - return (uintptr)(unsafe.Pointer(ret)), nil -} - -// getUserNSFirstChild returns an open FD for the first direct child user namespace that created the process -// Each container creates a new user namespace where the runtime runs. The current process in the container -// might have created new user namespaces that are child of the initial namespace we created. -// This function finds the initial namespace created for the container that is a child of the current namespace. -// -// current ns -// / \ -// TARGET -> a [other containers] -// / -// b -// / -// NS READ USING THE PID -> c -func getUserNSFirstChild(fd uintptr) (*os.File, error) { - currentNS, err := readUserNs("/proc/self/ns/user") - if err != nil { - return nil, err - } - - ns, err := readUserNsFd(fd) - if err != nil { - return nil, errors.Wrapf(err, "cannot read user namespace") - } - if ns == currentNS { - return nil, errors.New("process running in the same user namespace") - } - - for { - nextFd, err := getParentUserNs(fd) - if err != nil { - if err == unix.ENOTTY { - return os.NewFile(fd, "userns child"), nil - } - return nil, errors.Wrapf(err, "cannot get parent user namespace") - } - - ns, err = readUserNsFd(nextFd) - if err != nil { - return nil, errors.Wrapf(err, "cannot read user namespace") - } - - if ns == currentNS { - if err := unix.Close(int(nextFd)); err != nil { - return nil, err - } - - // Drop O_CLOEXEC for the fd. - _, _, errno := unix.Syscall(unix.SYS_FCNTL, fd, unix.F_SETFD, 0) - if errno != 0 { - if err := unix.Close(int(fd)); err != nil { - logrus.Errorf("failed to close file descriptor %d", fd) - } - return nil, errno - } - - return os.NewFile(fd, "userns child"), nil - } - if err := unix.Close(int(fd)); err != nil { - return nil, err - } - fd = nextFd - } -} - // joinUserAndMountNS re-exec podman in a new userNS and join the user and mount // namespace of the specified PID without looking up its parent. Useful to join directly // the conmon process. @@ -220,31 +135,7 @@ func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) { cPausePid := C.CString(pausePid) defer C.free(unsafe.Pointer(cPausePid)) - userNS, err := os.Open(fmt.Sprintf("/proc/%d/ns/user", pid)) - if err != nil { - return false, -1, err - } - defer func() { - if err := userNS.Close(); err != nil { - logrus.Errorf("unable to close namespace: %q", err) - } - }() - - mountNS, err := os.Open(fmt.Sprintf("/proc/%d/ns/mnt", pid)) - if err != nil { - return false, -1, err - } - defer func() { - if err := mountNS.Close(); err != nil { - logrus.Errorf("unable to close namespace: %q", err) - } - }() - - fd, err := getUserNSFirstChild(userNS.Fd()) - if err != nil { - return false, -1, err - } - pidC := C.reexec_userns_join(C.int(fd.Fd()), C.int(mountNS.Fd()), cPausePid) + pidC := C.reexec_userns_join(C.int(pid), cPausePid) if int(pidC) < 0 { return false, -1, errors.Errorf("cannot re-exec process") } diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 7233acb8a..31465d8bf 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -20,17 +20,27 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat } // Image stop signal - if s.StopSignal == nil && newImage.Config != nil { - sig, err := signal.ParseSignalNameOrNumber(newImage.Config.StopSignal) + if s.StopSignal == nil { + stopSignal, err := newImage.StopSignal(ctx) + if err != nil { + return err + } + sig, err := signal.ParseSignalNameOrNumber(stopSignal) if err != nil { return err } s.StopSignal = &sig } + // Image envs from the image if they don't exist // already - if newImage.Config != nil && len(newImage.Config.Env) > 0 { - envs, err := envLib.ParseSlice(newImage.Config.Env) + env, err := newImage.Env(ctx) + if err != nil { + return err + } + + if len(env) > 0 { + envs, err := envLib.ParseSlice(env) if err != nil { return err } @@ -41,12 +51,15 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat } } + labels, err := newImage.Labels(ctx) + if err != nil { + return err + } + // labels from the image that dont exist already - if config := newImage.Config; config != nil { - for k, v := range config.Labels { - if _, exists := s.Labels[k]; !exists { - s.Labels[k] = v - } + for k, v := range labels { + if _, exists := s.Labels[k]; !exists { + s.Labels[k] = v } } @@ -75,20 +88,30 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat } // entrypoint - if config := newImage.Config; config != nil { - if len(s.Entrypoint) < 1 && len(config.Entrypoint) > 0 { - s.Entrypoint = config.Entrypoint - } - if len(s.Command) < 1 && len(config.Cmd) > 0 { - s.Command = config.Cmd - } - if len(s.Command) < 1 && len(s.Entrypoint) < 1 { - return errors.Errorf("No command provided or as CMD or ENTRYPOINT in this image") - } - // workdir - if len(s.WorkDir) < 1 && len(config.WorkingDir) > 1 { - s.WorkDir = config.WorkingDir - } + entrypoint, err := newImage.Entrypoint(ctx) + if err != nil { + return err + } + if len(s.Entrypoint) < 1 && len(entrypoint) > 0 { + s.Entrypoint = entrypoint + } + command, err := newImage.Cmd(ctx) + if err != nil { + return err + } + if len(s.Command) < 1 && len(command) > 0 { + s.Command = command + } + if len(s.Command) < 1 && len(s.Entrypoint) < 1 { + return errors.Errorf("No command provided or as CMD or ENTRYPOINT in this image") + } + // workdir + workingDir, err := newImage.WorkingDir(ctx) + if err != nil { + return err + } + if len(s.WorkDir) < 1 && len(workingDir) > 1 { + s.WorkDir = workingDir } if len(s.SeccompProfilePath) < 1 { @@ -99,15 +122,17 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat s.SeccompProfilePath = p } - if user := s.User; len(user) == 0 { - switch { + if len(s.User) == 0 { + s.User, err = newImage.User(ctx) + if err != nil { + return err + } + // TODO This should be enabled when namespaces actually work //case usernsMode.IsKeepID(): // user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID()) - case newImage.Config == nil || (newImage.Config != nil && len(newImage.Config.User) == 0): + if len(s.User) == 0 { s.User = "0" - default: - s.User = newImage.Config.User } } if err := finishThrottleDevices(s); err != nil { @@ -116,7 +141,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // Unless already set via the CLI, check if we need to disable process // labels or set the defaults. if len(s.SelinuxOpts) == 0 { - if err := SetLabelOpts(s, r, s.PidNS, s.IpcNS); err != nil { + if err := setLabelOpts(s, r, s.PidNS, s.IpcNS); err != nil { return err } } diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index cdd7d86da..53ae335c3 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -3,9 +3,7 @@ package generate import ( "os" - "github.com/containers/common/pkg/capabilities" "github.com/containers/libpod/libpod" - "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/specgen" "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -324,66 +322,6 @@ func userConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) err return nil } -func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *image.Image) error { - // HANDLE CAPABILITIES - // NOTE: Must happen before SECCOMP - if s.Privileged { - g.SetupPrivileged(true) - } - - useNotRoot := func(user string) bool { - if user == "" || user == "root" || user == "0" { - return false - } - return true - } - configSpec := g.Config - var err error - var caplist []string - bounding := configSpec.Process.Capabilities.Bounding - if useNotRoot(s.User) { - configSpec.Process.Capabilities.Bounding = caplist - } - caplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, s.CapAdd, s.CapDrop) - if err != nil { - return err - } - - configSpec.Process.Capabilities.Bounding = caplist - configSpec.Process.Capabilities.Permitted = caplist - configSpec.Process.Capabilities.Inheritable = caplist - configSpec.Process.Capabilities.Effective = caplist - configSpec.Process.Capabilities.Ambient = caplist - if useNotRoot(s.User) { - caplist, err = capabilities.MergeCapabilities(bounding, s.CapAdd, s.CapDrop) - if err != nil { - return err - } - } - configSpec.Process.Capabilities.Bounding = caplist - - // HANDLE SECCOMP - if s.SeccompProfilePath != "unconfined" { - seccompConfig, err := getSeccompConfig(s, configSpec, newImage) - if err != nil { - return err - } - configSpec.Linux.Seccomp = seccompConfig - } - - // Clear default Seccomp profile from Generator for privileged containers - if s.SeccompProfilePath == "unconfined" || s.Privileged { - configSpec.Linux.Seccomp = nil - } - - g.SetRootReadonly(s.ReadOnlyFilesystem) - for sysctlKey, sysctlVal := range s.Sysctl { - g.AddLinuxSysctl(sysctlKey, sysctlVal) - } - - return nil -} - // GetNamespaceOptions transforms a slice of kernel namespaces // into a slice of pod create options. Currently, not all // kernel namespaces are supported, and they will be returned in an error diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index ef4b3b47a..e2da9e976 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -1,15 +1,22 @@ package generate import ( + "strings" + + "github.com/containers/common/pkg/capabilities" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/specgen" + "github.com/containers/libpod/pkg/util" + "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) -// SetLabelOpts sets the label options of the SecurityConfig according to the +// setLabelOpts sets the label options of the SecurityConfig according to the // input. -func SetLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { +func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { if !runtime.EnableLabeling() || s.Privileged { s.SelinuxOpts = label.DisableSecOpt() return nil @@ -48,12 +55,10 @@ func SetLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig s return nil } -// ConfigureGenerator configures the generator according to the input. -/* -func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error { +func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *image.Image) error { // HANDLE CAPABILITIES // NOTE: Must happen before SECCOMP - if c.Privileged { + if s.Privileged { g.SetupPrivileged(true) } @@ -63,56 +68,66 @@ func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserCon } return true } - configSpec := g.Config var err error - var defaultCaplist []string + var caplist []string bounding := configSpec.Process.Capabilities.Bounding - if useNotRoot(user.User) { - configSpec.Process.Capabilities.Bounding = defaultCaplist + if useNotRoot(s.User) { + configSpec.Process.Capabilities.Bounding = caplist } - defaultCaplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop) + caplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, s.CapAdd, s.CapDrop) if err != nil { return err } + privCapsRequired := []string{} + + // If the container image specifies an label with a + // capabilities.ContainerImageLabel then split the comma separated list + // of capabilities and record them. This list indicates the only + // capabilities, required to run the container. + var capsRequiredRequested []string + for key, val := range s.Labels { + if util.StringInSlice(key, capabilities.ContainerImageLabels) { + capsRequiredRequested = strings.Split(val, ",") + } + } + if !s.Privileged && len(capsRequiredRequested) > 0 { - privCapRequired := []string{} - - if !c.Privileged && len(c.CapRequired) > 0 { - // Pass CapRequired in CapAdd field to normalize capabilities names - capRequired, err := capabilities.MergeCapabilities(nil, c.CapRequired, nil) + // Pass capRequiredRequested in CapAdd field to normalize capabilities names + capsRequired, err := capabilities.MergeCapabilities(nil, capsRequiredRequested, nil) if err != nil { - logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(c.CapRequired, ",")) + logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(capsRequired, ",")) } else { - // Verify all capRequiered are in the defaultCapList - for _, cap := range capRequired { - if !util.StringInSlice(cap, defaultCaplist) { - privCapRequired = append(privCapRequired, cap) + // Verify all capRequiered are in the capList + for _, cap := range capsRequired { + if !util.StringInSlice(cap, caplist) { + privCapsRequired = append(privCapsRequired, cap) } } } - if len(privCapRequired) == 0 { - defaultCaplist = capRequired + if len(privCapsRequired) == 0 { + caplist = capsRequired } else { - logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapRequired, ",")) + logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapsRequired, ",")) } } - configSpec.Process.Capabilities.Bounding = defaultCaplist - configSpec.Process.Capabilities.Permitted = defaultCaplist - configSpec.Process.Capabilities.Inheritable = defaultCaplist - configSpec.Process.Capabilities.Effective = defaultCaplist - configSpec.Process.Capabilities.Ambient = defaultCaplist - if useNotRoot(user.User) { - defaultCaplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop) + + configSpec.Process.Capabilities.Bounding = caplist + configSpec.Process.Capabilities.Permitted = caplist + configSpec.Process.Capabilities.Inheritable = caplist + configSpec.Process.Capabilities.Effective = caplist + configSpec.Process.Capabilities.Ambient = caplist + if useNotRoot(s.User) { + caplist, err = capabilities.MergeCapabilities(bounding, s.CapAdd, s.CapDrop) if err != nil { return err } } - configSpec.Process.Capabilities.Bounding = defaultCaplist + configSpec.Process.Capabilities.Bounding = caplist // HANDLE SECCOMP - if c.SeccompProfilePath != "unconfined" { - seccompConfig, err := getSeccompConfig(c, configSpec) + if s.SeccompProfilePath != "unconfined" { + seccompConfig, err := getSeccompConfig(s, configSpec, newImage) if err != nil { return err } @@ -120,35 +135,14 @@ func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserCon } // Clear default Seccomp profile from Generator for privileged containers - if c.SeccompProfilePath == "unconfined" || c.Privileged { + if s.SeccompProfilePath == "unconfined" || s.Privileged { configSpec.Linux.Seccomp = nil } - for _, opt := range c.SecurityOpts { - // Split on both : and = - splitOpt := strings.Split(opt, "=") - if len(splitOpt) == 1 { - splitOpt = strings.Split(opt, ":") - } - if len(splitOpt) < 2 { - continue - } - switch splitOpt[0] { - case "label": - configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1] - case "seccomp": - configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1] - case "apparmor": - configSpec.Annotations[libpod.InspectAnnotationApparmor] = splitOpt[1] - } - } - - g.SetRootReadonly(c.ReadOnlyRootfs) - for sysctlKey, sysctlVal := range c.Sysctl { + g.SetRootReadonly(s.ReadOnlyFilesystem) + for sysctlKey, sysctlVal := range s.Sysctl { g.AddLinuxSysctl(sysctlKey, sysctlVal) } return nil } - -*/ diff --git a/pkg/systemd/activation.go b/pkg/systemd/activation.go index c8b2389dc..8f75f9cca 100644 --- a/pkg/systemd/activation.go +++ b/pkg/systemd/activation.go @@ -3,38 +3,33 @@ package systemd import ( "os" "strconv" - "strings" ) // SocketActivated determine if podman is running under the socket activation protocol +// Criteria is based on the expectations of "github.com/coreos/go-systemd/v22/activation" func SocketActivated() bool { - pid, pid_found := os.LookupEnv("LISTEN_PID") - fds, fds_found := os.LookupEnv("LISTEN_FDS") - fdnames, fdnames_found := os.LookupEnv("LISTEN_FDNAMES") - - if !(pid_found && fds_found && fdnames_found) { + pid, found := os.LookupEnv("LISTEN_PID") + if !found { return false } - p, err := strconv.Atoi(pid) if err != nil || p != os.Getpid() { return false } + fds, found := os.LookupEnv("LISTEN_FDS") + if !found { + return false + } nfds, err := strconv.Atoi(fds) - if err != nil || nfds < 1 { + if err != nil || nfds == 0 { return false } - // First available file descriptor is always 3. - if nfds > 1 { - names := strings.Split(fdnames, ":") - for _, n := range names { - if strings.Contains(n, "podman") { - return true - } - } + // "github.com/coreos/go-systemd/v22/activation" will use and validate this variable's + // value. We're just providing a fast fail + if _, found = os.LookupEnv("LISTEN_FDNAMES"); !found { + return false } - return true } |