diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/apparmor/apparmor_linux.go | 13 | ||||
-rw-r--r-- | pkg/spec/spec.go | 34 | ||||
-rw-r--r-- | pkg/trust/trust.go | 122 | ||||
-rw-r--r-- | pkg/util/utils.go | 44 | ||||
-rw-r--r-- | pkg/varlinkapi/containers.go | 17 | ||||
-rw-r--r-- | pkg/varlinkapi/containers_create.go | 2 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 33 | ||||
-rw-r--r-- | pkg/varlinkapi/mount.go | 4 | ||||
-rw-r--r-- | pkg/varlinkapi/transfers.go | 75 |
9 files changed, 199 insertions, 145 deletions
diff --git a/pkg/apparmor/apparmor_linux.go b/pkg/apparmor/apparmor_linux.go index 0787b3fa5..2c5022c1f 100644 --- a/pkg/apparmor/apparmor_linux.go +++ b/pkg/apparmor/apparmor_linux.go @@ -214,8 +214,15 @@ func CheckProfileAndLoadDefault(name string) (string, error) { return name, nil } - if name != "" && rootless.IsRootless() { - return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name) + // AppArmor is not supported in rootless mode as it requires root + // privileges. Return an error in case a specific profile is specified. + if rootless.IsRootless() { + if name != "" { + return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name) + } else { + logrus.Debug("skipping loading default AppArmor profile (rootless mode)") + return "", nil + } } if name != "" && !runcaa.IsEnabled() { @@ -230,7 +237,7 @@ func CheckProfileAndLoadDefault(name string) (string, error) { return "", err } if !isLoaded { - return "", fmt.Errorf("AppArmor profile %q specified but not loaded") + return "", fmt.Errorf("AppArmor profile %q specified but not loaded", name) } return name, nil } diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index 46105af4a..76b8963ff 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -9,6 +9,7 @@ import ( "github.com/containers/storage/pkg/mount" "github.com/docker/docker/daemon/caps" "github.com/docker/go-units" + "github.com/opencontainers/runc/libcontainer/user" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" @@ -45,6 +46,18 @@ func supercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.M return configMount } +func getAvailableGids() (int64, error) { + idMap, err := user.ParseIDMapFile("/proc/self/gid_map") + if err != nil { + return 0, err + } + count := int64(0) + for _, r := range idMap { + count += r.Count + } + return count, nil +} + // CreateConfigToOCISpec parses information needed to create a container into an OCI runtime spec func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint cgroupPerm := "ro" @@ -91,14 +104,21 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint g.AddMount(sysMnt) } if isRootless { - g.RemoveMount("/dev/pts") - devPts := spec.Mount{ - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, + nGids, err := getAvailableGids() + if err != nil { + return nil, err + } + if nGids < 5 { + // If we have no GID mappings, the gid=5 default option would fail, so drop it. + g.RemoveMount("/dev/pts") + devPts := spec.Mount{ + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, + } + g.AddMount(devPts) } - g.AddMount(devPts) } if inUserNS && config.IpcMode.IsHost() { g.RemoveMount("/dev/mqueue") diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go index 31e41903e..9a75474ae 100644 --- a/pkg/trust/trust.go +++ b/pkg/trust/trust.go @@ -175,43 +175,30 @@ func CreateTmpFile(dir, pattern string, content []byte) (string, error) { return tmpfile.Name(), nil } -func getGPGIdFromKeyPath(path []string) []string { - var uids []string - for _, k := range path { - cmd := exec.Command("gpg2", "--with-colons", k) - results, err := cmd.Output() - if err != nil { - logrus.Warnf("error get key identity: %s", err) - continue - } - uids = append(uids, parseUids(results)...) +// GetGPGIdFromKeyPath return user keyring from key path +func GetGPGIdFromKeyPath(path string) []string { + cmd := exec.Command("gpg2", "--with-colons", path) + results, err := cmd.Output() + if err != nil { + logrus.Errorf("error getting key identity: %s", err) + return nil } - return uids + return parseUids(results) } -func getGPGIdFromKeyData(keys []string) []string { - var uids []string - for _, k := range keys { - decodeKey, err := base64.StdEncoding.DecodeString(k) - if err != nil { - logrus.Warnf("error decoding key data") - continue - } - tmpfileName, err := CreateTmpFile("", "", decodeKey) - if err != nil { - logrus.Warnf("error creating key date temp file %s", err) - } - defer os.Remove(tmpfileName) - k = tmpfileName - cmd := exec.Command("gpg2", "--with-colons", k) - results, err := cmd.Output() - if err != nil { - logrus.Warnf("error get key identity: %s", err) - continue - } - uids = append(uids, parseUids(results)...) +// GetGPGIdFromKeyData return user keyring from keydata +func GetGPGIdFromKeyData(key string) []string { + decodeKey, err := base64.StdEncoding.DecodeString(key) + if err != nil { + logrus.Errorf("%s, error decoding key data", err) + return nil } - return uids + tmpfileName, err := CreateTmpFile("", "", decodeKey) + if err != nil { + logrus.Errorf("error creating key date temp file %s", err) + } + defer os.Remove(tmpfileName) + return GetGPGIdFromKeyPath(tmpfileName) } func parseUids(colonDelimitKeys []byte) []string { @@ -234,68 +221,15 @@ func parseUids(colonDelimitKeys []byte) []string { return parseduids } -var typeDescription = map[string]string{"insecureAcceptAnything": "accept", "signedBy": "signed", "reject": "reject"} - -func trustTypeDescription(trustType string) string { - trustDescription, exist := typeDescription[trustType] - if !exist { - logrus.Warnf("invalid trust type %s", trustType) - } - return trustDescription -} - -// GetPolicy return the struct to show policy.json in json format and a map (reponame, ShowOutput) pair for image trust show command -func GetPolicy(policyContentStruct PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, map[string]ShowOutput, error) { - registryConfigs, err := LoadAndMergeConfig(systemRegistriesDirPath) +// GetPolicy parse policy.json into PolicyContent struct +func GetPolicy(policyPath string) (PolicyContent, error) { + var policyContentStruct PolicyContent + policyContent, err := ioutil.ReadFile(policyPath) if err != nil { - return nil, nil, err + return policyContentStruct, errors.Wrapf(err, "unable to read policy file %s", policyPath) } - - trustShowOutputMap := make(map[string]ShowOutput) - policyJSON := make(map[string]map[string]interface{}) - if len(policyContentStruct.Default) > 0 { - policyJSON["* (default)"] = make(map[string]interface{}) - policyJSON["* (default)"]["type"] = policyContentStruct.Default[0].Type - - var defaultPolicyStruct ShowOutput - defaultPolicyStruct.Repo = "default" - defaultPolicyStruct.Trusttype = trustTypeDescription(policyContentStruct.Default[0].Type) - trustShowOutputMap["* (default)"] = defaultPolicyStruct - } - for transname, transval := range policyContentStruct.Transports { - for repo, repoval := range transval { - tempTrustShowOutput := ShowOutput{ - Repo: repo, - Trusttype: repoval[0].Type, - } - policyJSON[repo] = make(map[string]interface{}) - policyJSON[repo]["type"] = repoval[0].Type - policyJSON[repo]["transport"] = transname - keyDataArr := []string{} - keyPathArr := []string{} - keyarr := []string{} - for _, repoele := range repoval { - if len(repoele.KeyPath) > 0 { - keyarr = append(keyarr, repoele.KeyPath) - keyPathArr = append(keyPathArr, repoele.KeyPath) - } - if len(repoele.KeyData) > 0 { - keyarr = append(keyarr, string(repoele.KeyData)) - keyDataArr = append(keyDataArr, string(repoele.KeyData)) - } - } - policyJSON[repo]["keys"] = keyarr - uids := append(getGPGIdFromKeyPath(keyPathArr), getGPGIdFromKeyData(keyDataArr)...) - tempTrustShowOutput.GPGid = strings.Join(uids, ",") - - policyJSON[repo]["sigstore"] = "" - registryNamespace := HaveMatchRegistry(repo, registryConfigs) - if registryNamespace != nil { - policyJSON[repo]["sigstore"] = registryNamespace.SigStore - tempTrustShowOutput.Sigstore = registryNamespace.SigStore - } - trustShowOutputMap[repo] = tempTrustShowOutput - } + if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { + return policyContentStruct, errors.Wrapf(err, "could not parse trust policies") } - return policyJSON, trustShowOutputMap, nil + return policyContentStruct, nil } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index e0b94b011..52f431881 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -301,36 +301,36 @@ func getTomlStorage(storeOptions *storage.StoreOptions) *tomlConfig { // for the volume API // It also returns the path where all named volumes will be created using the volume API func GetDefaultStoreOptions() (storage.StoreOptions, string, error) { + var ( + defaultRootlessRunRoot string + defaultRootlessGraphRoot string + err error + ) storageOpts := storage.DefaultStoreOptions volumePath := "/var/lib/containers/storage" + if rootless.IsRootless() { - var err error storageOpts, err = GetRootlessStorageOpts() if err != nil { return storageOpts, volumePath, err } + volumePath, err = GetRootlessVolumeInfo() if err != nil { return storageOpts, volumePath, err } + } - storageConf := StorageConfigFile() - if _, err := os.Stat(storageConf); err == nil { - defaultRootlessRunRoot := storageOpts.RunRoot - defaultRootlessGraphRoot := storageOpts.GraphRoot - storageOpts = storage.StoreOptions{} - storage.ReloadConfigurationFile(storageConf, &storageOpts) + storageConf := StorageConfigFile() + if _, err = os.Stat(storageConf); err == nil { + defaultRootlessRunRoot = storageOpts.RunRoot + defaultRootlessGraphRoot = storageOpts.GraphRoot + storageOpts = storage.StoreOptions{} + storage.ReloadConfigurationFile(storageConf, &storageOpts) + } - // If the file did not specify a graphroot or runroot, - // set sane defaults so we don't try and use root-owned - // directories - if storageOpts.RunRoot == "" { - storageOpts.RunRoot = defaultRootlessRunRoot - } - if storageOpts.GraphRoot == "" { - storageOpts.GraphRoot = defaultRootlessGraphRoot - } - } else if os.IsNotExist(err) { + if rootless.IsRootless() { + if os.IsNotExist(err) { os.MkdirAll(filepath.Dir(storageConf), 0755) file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) if err != nil { @@ -343,6 +343,16 @@ func GetDefaultStoreOptions() (storage.StoreOptions, string, error) { if err := enc.Encode(tomlConfiguration); err != nil { os.Remove(storageConf) } + } else if err == nil { + // If the file did not specify a graphroot or runroot, + // set sane defaults so we don't try and use root-owned + // directories + if storageOpts.RunRoot == "" { + storageOpts.RunRoot = defaultRootlessRunRoot + } + if storageOpts.GraphRoot == "" { + storageOpts.GraphRoot = defaultRootlessGraphRoot + } } } return storageOpts, volumePath, nil diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index a01e3cc2b..737e2dd96 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "os" "syscall" "time" @@ -194,15 +195,25 @@ func (i *LibpodAPI) ListContainerChanges(call iopodman.VarlinkCall, name string) } // ExportContainer ... -func (i *LibpodAPI) ExportContainer(call iopodman.VarlinkCall, name, path string) error { +func (i *LibpodAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name) } - if err := ctr.Export(path); err != nil { + outputFile, err := ioutil.TempFile("", "varlink_recv") + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + + defer outputFile.Close() + if outPath == "" { + outPath = outputFile.Name() + } + if err := ctr.Export(outPath); err != nil { return call.ReplyErrorOccurred(err.Error()) } - return call.ReplyExportContainer(path) + return call.ReplyExportContainer(outPath) + } // GetContainerStats ... diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go index d72eaeb18..cc707b11f 100644 --- a/pkg/varlinkapi/containers_create.go +++ b/pkg/varlinkapi/containers_create.go @@ -25,7 +25,7 @@ func (i *LibpodAPI) CreateContainer(call iopodman.VarlinkCall, config iopodman.C rtc := i.Runtime.GetConfig() ctx := getContext() - newImage, err := i.Runtime.ImageRuntime().New(ctx, config.Image, rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false) + newImage, err := i.Runtime.ImageRuntime().New(ctx, config.Image, rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false, nil) if err != nil { return call.ReplyErrorOccurred(err.Error()) } diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 744f031c0..5e0889645 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -500,7 +500,7 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch } // ImportImage imports an image from a tarball to the image store -func (i *LibpodAPI) ImportImage(call iopodman.VarlinkCall, source, reference, message string, changes []string) error { +func (i *LibpodAPI) ImportImage(call iopodman.VarlinkCall, source, reference, message string, changes []string, delete bool) error { configChanges, err := util.GetImageConfig(changes) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -516,6 +516,12 @@ func (i *LibpodAPI) ImportImage(call iopodman.VarlinkCall, source, reference, me if err != nil { return call.ReplyErrorOccurred(err.Error()) } + if delete { + if err := os.Remove(source); err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + } + return call.ReplyImportImage(newImage.ID()) } @@ -573,7 +579,7 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string, certDir, c } imageID = newImage[0].ID() } else { - newImage, err := i.Runtime.ImageRuntime().New(getContext(), name, signaturePolicy, "", nil, &dockerRegistryOptions, so, false) + newImage, err := i.Runtime.ImageRuntime().New(getContext(), name, signaturePolicy, "", nil, &dockerRegistryOptions, so, false, nil) if err != nil { return call.ReplyErrorOccurred(fmt.Sprintf("unable to pull %s: %s", name, err.Error())) } @@ -610,15 +616,15 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman. runLabel, imageName, err := shared.GetRunlabel(input.Label, input.Image, ctx, i.Runtime, input.Pull, input.Creds, dockerRegistryOptions, input.Authfile, input.SignaturePolicyPath, nil) if err != nil { - return err + return call.ReplyErrorOccurred(err.Error()) } if runLabel == "" { - return nil + return call.ReplyErrorOccurred(fmt.Sprintf("%s does not contain the label %s", input.Image, input.Label)) } cmd, env, err := shared.GenerateRunlabelCommand(runLabel, imageName, input.Name, input.Opts, input.ExtraArgs) if err != nil { - return err + return call.ReplyErrorOccurred(err.Error()) } if err := utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...); err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -627,19 +633,10 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman. } // ImagesPrune .... -func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall) error { - var ( - pruned []string - ) - pruneImages, err := i.Runtime.ImageRuntime().GetPruneImages() +func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool) error { + prunedImages, err := i.Runtime.ImageRuntime().PruneImages(all) if err != nil { - return err - } - for _, i := range pruneImages { - if err := i.Remove(true); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - pruned = append(pruned, i.ID()) + return call.ReplyErrorOccurred(err.Error()) } - return call.ReplyImagesPrune(pruned) + return call.ReplyImagesPrune(prunedImages) } diff --git a/pkg/varlinkapi/mount.go b/pkg/varlinkapi/mount.go index 84e6b2709..3b4fe87e3 100644 --- a/pkg/varlinkapi/mount.go +++ b/pkg/varlinkapi/mount.go @@ -6,7 +6,7 @@ import ( // ListContainerMounts ... func (i *LibpodAPI) ListContainerMounts(call iopodman.VarlinkCall) error { - var mounts []string + mounts := make(map[string]string) allContainers, err := i.Runtime.GetAllContainers() if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -17,7 +17,7 @@ func (i *LibpodAPI) ListContainerMounts(call iopodman.VarlinkCall) error { return call.ReplyErrorOccurred(err.Error()) } if mounted { - mounts = append(mounts, mountPoint) + mounts[container.ID()] = mountPoint } } return call.ReplyListContainerMounts(mounts) diff --git a/pkg/varlinkapi/transfers.go b/pkg/varlinkapi/transfers.go new file mode 100644 index 000000000..0cb7e5e2e --- /dev/null +++ b/pkg/varlinkapi/transfers.go @@ -0,0 +1,75 @@ +package varlinkapi + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + + "github.com/containers/libpod/cmd/podman/varlink" +) + +// SendFile allows a client to send a file to the varlink server +func (i *LibpodAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int64) error { + if !call.WantsUpgrade() { + return call.ReplyErrorOccurred("client must use upgraded connection to send files") + } + + outputFile, err := ioutil.TempFile("", "varlink_send") + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + defer outputFile.Close() + + if err = call.ReplySendFile(outputFile.Name()); err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + + writer := bufio.NewWriter(outputFile) + defer writer.Flush() + + reader := call.Call.Reader + if _, err := io.CopyN(writer, reader, length); err != nil { + return err + } + + // Send an ACK to the client + call.Call.Writer.WriteString(fmt.Sprintf("%s:", outputFile.Name())) + call.Call.Writer.Flush() + return nil + +} + +// ReceiveFile allows the varlink server to send a file to a client +func (i *LibpodAPI) ReceiveFile(call iopodman.VarlinkCall, filepath string, delete bool) error { + if !call.WantsUpgrade() { + return call.ReplyErrorOccurred("client must use upgraded connection to send files") + } + fs, err := os.Open(filepath) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + fileInfo, err := fs.Stat() + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + + // Send the file length down to client + // Varlink connection upraded + if err = call.ReplyReceiveFile(fileInfo.Size()); err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + + reader := bufio.NewReader(fs) + _, err = reader.WriteTo(call.Writer) + if err != nil { + return err + } + if delete { + if err := os.Remove(filepath); err != nil { + return err + } + } + return call.Writer.Flush() +} |