diff options
-rw-r--r-- | cmd/podman/image.go | 1 | ||||
-rw-r--r-- | cmd/podman/trust.go | 293 | ||||
-rw-r--r-- | docs/podman-image-trust.1.md | 81 | ||||
-rw-r--r-- | docs/podman-image.1.md | 1 | ||||
-rw-r--r-- | libpod/image/utils.go | 22 | ||||
-rw-r--r-- | libpod/runtime.go | 5 | ||||
-rw-r--r-- | pkg/trust/trust.go | 250 | ||||
-rw-r--r-- | test/e2e/trust_test.go | 72 |
8 files changed, 725 insertions, 0 deletions
diff --git a/cmd/podman/image.go b/cmd/podman/image.go index 95af36df5..e978b9cf5 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -19,6 +19,7 @@ var ( rmImageCommand, saveCommand, tagCommand, + trustCommand, } imageDescription = "Manage images" diff --git a/cmd/podman/trust.go b/cmd/podman/trust.go new file mode 100644 index 000000000..7c404cd3f --- /dev/null +++ b/cmd/podman/trust.go @@ -0,0 +1,293 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "sort" + + "github.com/containers/image/types" + "github.com/containers/libpod/cmd/podman/formats" + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/trust" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var ( + setTrustFlags = []cli.Flag{ + cli.StringFlag{ + Name: "type, t", + Usage: "Trust type, accept values: signedBy(default), accept, reject.", + Value: "signedBy", + }, + cli.StringSliceFlag{ + Name: "pubkeysfile, f", + Usage: `Path of installed public key(s) to trust for TARGET. + Absolute path to keys is added to policy.json. May + used multiple times to define multiple public keys. + File(s) must exist before using this command.`, + }, + cli.StringFlag{ + Name: "policypath", + Hidden: true, + }, + } + showTrustFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "raw", + Usage: "Output raw policy file", + }, + cli.BoolFlag{ + Name: "json, j", + Usage: "Output as json", + }, + cli.StringFlag{ + Name: "policypath", + Hidden: true, + }, + cli.StringFlag{ + Name: "registrypath", + Hidden: true, + }, + } + + setTrustDescription = "Set default trust policy or add a new trust policy for a registry" + setTrustCommand = cli.Command{ + Name: "set", + Usage: "Set default trust policy or a new trust policy for a registry", + Description: setTrustDescription, + Flags: sortFlags(setTrustFlags), + ArgsUsage: "default | REGISTRY[/REPOSITORY]", + Action: setTrustCmd, + OnUsageError: usageErrorHandler, + } + + showTrustDescription = "Display trust policy for the system" + showTrustCommand = cli.Command{ + Name: "show", + Usage: "Display trust policy for the system", + Description: showTrustDescription, + Flags: sortFlags(showTrustFlags), + Action: showTrustCmd, + ArgsUsage: "", + UseShortOptionHandling: true, + OnUsageError: usageErrorHandler, + } + + trustSubCommands = []cli.Command{ + setTrustCommand, + showTrustCommand, + } + + trustDescription = fmt.Sprintf(`Manages the trust policy of the host system. (%s) + Trust policy describes a registry scope that must be signed by public keys.`, getDefaultPolicyPath()) + trustCommand = cli.Command{ + Name: "trust", + Usage: "Manage container image trust policy", + Description: trustDescription, + ArgsUsage: "{set,show} ...", + Subcommands: trustSubCommands, + OnUsageError: usageErrorHandler, + } +) + +func showTrustCmd(c *cli.Context) error { + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not create runtime") + } + + var ( + policyPath string + systemRegistriesDirPath string + ) + if c.IsSet("policypath") { + policyPath = c.String("policypath") + } else { + policyPath = trust.DefaultPolicyPath(runtime.SystemContext()) + } + policyContent, err := ioutil.ReadFile(policyPath) + if err != nil { + return errors.Wrapf(err, "unable to read %s", policyPath) + } + if c.IsSet("registrypath") { + systemRegistriesDirPath = c.String("registrypath") + } else { + systemRegistriesDirPath = trust.RegistriesDirPath(runtime.SystemContext()) + } + + if c.Bool("raw") { + _, err := os.Stdout.Write(policyContent) + if err != nil { + return errors.Wrap(err, "could not read trust policies") + } + return nil + } + + var policyContentStruct trust.PolicyContent + if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { + return errors.Errorf("could not read trust policies") + } + policyJSON, err := trust.GetPolicyJSON(policyContentStruct, systemRegistriesDirPath) + if err != nil { + return errors.Wrapf(err, "error reading registry config file") + } + if c.Bool("json") { + var outjson interface{} + outjson = policyJSON + out := formats.JSONStruct{Output: outjson} + return formats.Writer(out).Out() + } + + sortedRepos := sortPolicyJSONKey(policyJSON) + type policydefault struct { + Repo string + Trusttype string + GPGid string + Sigstore string + } + var policyoutput []policydefault + for _, repo := range sortedRepos { + repoval := policyJSON[repo] + var defaultstruct policydefault + defaultstruct.Repo = repo + if repoval["type"] != nil { + defaultstruct.Trusttype = trustTypeDescription(repoval["type"].(string)) + } + if repoval["keys"] != nil && len(repoval["keys"].([]string)) > 0 { + defaultstruct.GPGid = trust.GetGPGId(repoval["keys"].([]string)) + } + if repoval["sigstore"] != nil { + defaultstruct.Sigstore = repoval["sigstore"].(string) + } + policyoutput = append(policyoutput, defaultstruct) + } + var output []interface{} + for _, ele := range policyoutput { + output = append(output, interface{}(ele)) + } + out := formats.StdoutTemplateArray{Output: output, Template: "{{.Repo}}\t{{.Trusttype}}\t{{.GPGid}}\t{{.Sigstore}}"} + return formats.Writer(out).Out() +} + +func setTrustCmd(c *cli.Context) error { + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not create runtime") + } + + args := c.Args() + if len(args) != 1 { + return errors.Errorf("default or a registry name must be specified") + } + valid, err := image.IsValidImageURI(args[0]) + if err != nil || !valid { + return errors.Wrapf(err, "invalid image uri %s", args[0]) + } + + trusttype := c.String("type") + if !isValidTrustType(trusttype) { + return errors.Errorf("invalid choice: %s (choose from 'accept', 'reject', 'signedBy')", trusttype) + } + if trusttype == "accept" { + trusttype = "insecureAcceptAnything" + } + + pubkeysfile := c.StringSlice("pubkeysfile") + if len(pubkeysfile) == 0 && trusttype == "signedBy" { + return errors.Errorf("At least one public key must be defined for type 'signedBy'") + } + + var policyPath string + if c.IsSet("policypath") { + policyPath = c.String("policypath") + } else { + policyPath = trust.DefaultPolicyPath(runtime.SystemContext()) + } + var policyContentStruct trust.PolicyContent + _, err = os.Stat(policyPath) + if !os.IsNotExist(err) { + policyContent, err := ioutil.ReadFile(policyPath) + if err != nil { + return errors.Wrapf(err, "unable to read %s", policyPath) + } + if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { + return errors.Errorf("could not read trust policies") + } + } + var newReposContent []trust.RepoContent + if len(pubkeysfile) != 0 { + for _, filepath := range pubkeysfile { + newReposContent = append(newReposContent, trust.RepoContent{Type: trusttype, KeyType: "GPGKeys", KeyPath: filepath}) + } + } else { + newReposContent = append(newReposContent, trust.RepoContent{Type: trusttype}) + } + if args[0] == "default" { + policyContentStruct.Default = newReposContent + } else { + exists := false + for transport, transportval := range policyContentStruct.Transports { + _, exists = transportval[args[0]] + if exists { + policyContentStruct.Transports[transport][args[0]] = newReposContent + break + } + } + if !exists { + if policyContentStruct.Transports == nil { + policyContentStruct.Transports = make(map[string]trust.RepoMap) + } + if policyContentStruct.Transports["docker"] == nil { + policyContentStruct.Transports["docker"] = make(map[string][]trust.RepoContent) + } + policyContentStruct.Transports["docker"][args[0]] = append(policyContentStruct.Transports["docker"][args[0]], newReposContent...) + } + } + + data, err := json.MarshalIndent(policyContentStruct, "", " ") + if err != nil { + return errors.Wrapf(err, "error setting trust policy") + } + err = ioutil.WriteFile(policyPath, data, 0644) + if err != nil { + return errors.Wrapf(err, "error setting trust policy") + } + return nil +} + +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 +} + +func sortPolicyJSONKey(m map[string]map[string]interface{}) []string { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +func isValidTrustType(t string) bool { + if t == "accept" || t == "insecureAcceptAnything" || t == "reject" || t == "signedBy" { + return true + } + return false +} + +func getDefaultPolicyPath() string { + return trust.DefaultPolicyPath(&types.SystemContext{}) +} diff --git a/docs/podman-image-trust.1.md b/docs/podman-image-trust.1.md new file mode 100644 index 000000000..24209698c --- /dev/null +++ b/docs/podman-image-trust.1.md @@ -0,0 +1,81 @@ +% podman-image-trust "1" + +# NAME +podman\-trust - Manage container image trust policy + + +# SYNOPSIS +**podman image trust set|show** +[**-h**|**--help**] +[**-j**|**--json**] +[**--raw**] +[**-f**|**--pubkeysfile** KEY1 [**f**|**--pubkeysfile** KEY2,...]] +[**-t**|**--type** signedBy|accept|reject] +REGISTRY[/REPOSITORY] + +# DESCRIPTION +Manages the trust policy of the host system. Trust policy describes +a registry scope (registry and/or repository) that must be signed by public keys. Trust +is defined in **/etc/containers/policy.json**. Trust is enforced when a user attempts to pull +an image from a registry. + +Trust scope is evaluated by most specific to least specific. In other words, policy may +be defined for an entire registry, but refined for a particular repository in that +registry. See below for examples. + +Trust **type** provides a way to whitelist ("accept") or blacklist +("reject") registries. + +Trust may be updated using the command **podman image trust set** for an existing trust scope. + +# OPTIONS +**-h** **--help** + Print usage statement. + +**-f** **--pubkeysfile** + A path to an exported public key on the local system. Key paths + will be referenced in policy.json. Any path may be used but path + **/etc/pki/containers** is recommended. Option may be used multiple times to + require an image be sigend by multiple keys. One of **--pubkeys** or + **--pubkeysfile** is required for **signedBy** type. + +**-t** **--type** + The trust type for this policy entry. Accepted values: + **signedBy** (default): Require signatures with corresponding list of + public keys + **accept**: do not require any signatures for this + registry scope + **reject**: do not accept images for this registry scope + +# show OPTIONS + +**--raw** + Output trust policy file as raw JSON + +**-j** **--json** + Output trust as JSON for machine parsing + +# EXAMPLES + +Accept all unsigned images from a registry + + podman image trust set --type accept docker.io + +Modify default trust policy + + podman image trust set -t reject default + +Display system trust policy + + podman image trust show + +Display trust policy file + + podman image trust show --raw + +Display trust as JSON + + podman image trust show --json + +# HISTORY +December 2018, originally compiled by Qi Wang (qiwan at redhat dot com) diff --git a/docs/podman-image.1.md b/docs/podman-image.1.md index 8b812af11..8aa7cee64 100644 --- a/docs/podman-image.1.md +++ b/docs/podman-image.1.md @@ -26,6 +26,7 @@ The image command allows you to manage images | rm | [podman-rm(1)](podman-rmi.1.md) | Removes one or more locally stored images. | | save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. | | tag | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. | +| trust | [podman-image-trust(1)](podman-image-trust.1.md) | Manage container image trust policy. ## SEE ALSO podman diff --git a/libpod/image/utils.go b/libpod/image/utils.go index 9a75ca6dc..b944de1bb 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -2,6 +2,8 @@ package image import ( "io" + "net/url" + "regexp" "strings" cp "github.com/containers/image/copy" @@ -117,3 +119,23 @@ func GetAdditionalTags(images []string) ([]reference.NamedTagged, error) { } return allTags, nil } + +// IsValidImageURI checks if image name has valid format +func IsValidImageURI(imguri string) (bool, error) { + uri := "http://" + imguri + u, err := url.Parse(uri) + if err != nil { + return false, errors.Wrapf(err, "invalid image uri: %s", imguri) + } + reg := regexp.MustCompile(`^[a-zA-Z0-9-_\.]+\/?:?[0-9]*[a-z0-9-\/:]*$`) + ret := reg.FindAllString(u.Host, -1) + if len(ret) == 0 { + return false, errors.Wrapf(err, "invalid image uri: %s", imguri) + } + reg = regexp.MustCompile(`^[a-z0-9-:\./]*$`) + ret = reg.FindAllString(u.Fragment, -1) + if len(ret) == 0 { + return false, errors.Wrapf(err, "invalid image uri: %s", imguri) + } + return true, nil +} diff --git a/libpod/runtime.go b/libpod/runtime.go index 82473aae9..2dfebf565 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -877,3 +877,8 @@ func (r *Runtime) generateName() (string, error) { func (r *Runtime) ImageRuntime() *image.Runtime { return r.imageRuntime } + +// SystemContext returns the imagecontext +func (r *Runtime) SystemContext() *types.SystemContext { + return r.imageContext +} diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go new file mode 100644 index 000000000..efc760364 --- /dev/null +++ b/pkg/trust/trust.go @@ -0,0 +1,250 @@ +package trust + +import ( + "bufio" + "encoding/base64" + "encoding/json" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "unsafe" + + "github.com/containers/image/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + yaml "gopkg.in/yaml.v2" +) + +// PolicyContent struct for policy.json file +type PolicyContent struct { + Default []RepoContent `json:"default"` + Transports TransportsContent `json:"transports"` +} + +// RepoContent struct used under each repo +type RepoContent struct { + Type string `json:"type"` + KeyType string `json:"keyType,omitempty"` + KeyPath string `json:"keyPath,omitempty"` + KeyData string `json:"keyData,omitempty"` + SignedIdentity json.RawMessage `json:"signedIdentity,omitempty"` +} + +// RepoMap map repo name to policycontent for each repo +type RepoMap map[string][]RepoContent + +// TransportsContent struct for content under "transports" +type TransportsContent map[string]RepoMap + +// RegistryConfiguration is one of the files in registriesDirPath configuring lookaside locations, or the result of merging them all. +// NOTE: Keep this in sync with docs/registries.d.md! +type RegistryConfiguration struct { + DefaultDocker *RegistryNamespace `json:"default-docker"` + // The key is a namespace, using fully-expanded Docker reference format or parent namespaces (per dockerReference.PolicyConfiguration*), + Docker map[string]RegistryNamespace `json:"docker"` +} + +// RegistryNamespace defines lookaside locations for a single namespace. +type RegistryNamespace struct { + SigStore string `json:"sigstore"` // For reading, and if SigStoreStaging is not present, for writing. + SigStoreStaging string `json:"sigstore-staging"` // For writing only. +} + +// DefaultPolicyPath returns a path to the default policy of the system. +func DefaultPolicyPath(sys *types.SystemContext) string { + systemDefaultPolicyPath := "/etc/containers/policy.json" + if sys != nil { + if sys.SignaturePolicyPath != "" { + return sys.SignaturePolicyPath + } + if sys.RootForImplicitAbsolutePaths != "" { + return filepath.Join(sys.RootForImplicitAbsolutePaths, systemDefaultPolicyPath) + } + } + return systemDefaultPolicyPath +} + +// RegistriesDirPath returns a path to registries.d +func RegistriesDirPath(sys *types.SystemContext) string { + systemRegistriesDirPath := "/etc/containers/registries.d" + if sys != nil { + if sys.RegistriesDirPath != "" { + return sys.RegistriesDirPath + } + if sys.RootForImplicitAbsolutePaths != "" { + return filepath.Join(sys.RootForImplicitAbsolutePaths, systemRegistriesDirPath) + } + } + return systemRegistriesDirPath +} + +// LoadAndMergeConfig loads configuration files in dirPath +func LoadAndMergeConfig(dirPath string) (*RegistryConfiguration, error) { + mergedConfig := RegistryConfiguration{Docker: map[string]RegistryNamespace{}} + dockerDefaultMergedFrom := "" + nsMergedFrom := map[string]string{} + + dir, err := os.Open(dirPath) + if err != nil { + if os.IsNotExist(err) { + return &mergedConfig, nil + } + return nil, err + } + configNames, err := dir.Readdirnames(0) + if err != nil { + return nil, err + } + for _, configName := range configNames { + if !strings.HasSuffix(configName, ".yaml") { + continue + } + configPath := filepath.Join(dirPath, configName) + configBytes, err := ioutil.ReadFile(configPath) + if err != nil { + return nil, err + } + var config RegistryConfiguration + err = yaml.Unmarshal(configBytes, &config) + if err != nil { + return nil, errors.Wrapf(err, "Error parsing %s", configPath) + } + if config.DefaultDocker != nil { + if mergedConfig.DefaultDocker != nil { + return nil, errors.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in "%s" and "%s"`, + dockerDefaultMergedFrom, configPath) + } + mergedConfig.DefaultDocker = config.DefaultDocker + dockerDefaultMergedFrom = configPath + } + for nsName, nsConfig := range config.Docker { // includes config.Docker == nil + if _, ok := mergedConfig.Docker[nsName]; ok { + return nil, errors.Errorf(`Error parsing signature storage configuration: "docker" namespace "%s" defined both in "%s" and "%s"`, + nsName, nsMergedFrom[nsName], configPath) + } + mergedConfig.Docker[nsName] = nsConfig + nsMergedFrom[nsName] = configPath + } + } + return &mergedConfig, nil +} + +// HaveMatchRegistry checks if trust settings for the registry have been configed in yaml file +func HaveMatchRegistry(key string, registryConfigs *RegistryConfiguration) *RegistryNamespace { + searchKey := key + if !strings.Contains(searchKey, "/") { + val, exists := registryConfigs.Docker[searchKey] + if exists { + return &val + } + } + for range strings.Split(key, "/") { + val, exists := registryConfigs.Docker[searchKey] + if exists { + return &val + } + if strings.Contains(searchKey, "/") { + searchKey = searchKey[:strings.LastIndex(searchKey, "/")] + } + } + return nil +} + +// CreateTmpFile creates a temp file under dir and writes the content into it +func CreateTmpFile(dir, pattern string, content []byte) (string, error) { + tmpfile, err := ioutil.TempFile(dir, pattern) + if err != nil { + return "", err + } + defer tmpfile.Close() + + if _, err := tmpfile.Write(content); err != nil { + return "", err + + } + return tmpfile.Name(), nil +} + +// GetGPGId return GPG identity, either bracketed <email> or ID string +// comma separated if more than one key +func GetGPGId(keys []string) string { + for _, k := range keys { + if _, err := os.Stat(k); err != nil { + decodeKey, err := base64.StdEncoding.DecodeString(k) + if err != nil { + logrus.Warnf("error decoding key data") + continue + } + tmpfileName, err := CreateTmpFile("/run/", "", 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 + } + resultsStr := *(*string)(unsafe.Pointer(&results)) + scanner := bufio.NewScanner(strings.NewReader(resultsStr)) + var parseduids []string + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "uid:") || strings.HasPrefix(line, "pub:") { + uid := strings.Split(line, ":")[9] + if uid == "" { + continue + } + parseduid := uid + if strings.Contains(uid, "<") && strings.Contains(uid, ">") { + parseduid = strings.SplitN(strings.SplitAfterN(uid, "<", 2)[1], ">", 2)[0] + } + parseduids = append(parseduids, parseduid) + } + } + return strings.Join(parseduids, ",") + } + return "" +} + +// GetPolicyJSON return the struct to show policy.json in json format +func GetPolicyJSON(policyContentStruct PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, error) { + registryConfigs, err := LoadAndMergeConfig(systemRegistriesDirPath) + if err != nil { + return nil, err + } + + 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 + } + for transname, transval := range policyContentStruct.Transports { + for repo, repoval := range transval { + policyJSON[repo] = make(map[string]interface{}) + policyJSON[repo]["type"] = repoval[0].Type + policyJSON[repo]["transport"] = transname + for _, repoele := range repoval { + keyarr := []string{} + if len(repoele.KeyPath) > 0 { + keyarr = append(keyarr, repoele.KeyPath) + } + if len(repoele.KeyData) > 0 { + keyarr = append(keyarr, string(repoele.KeyData)) + } + policyJSON[repo]["keys"] = keyarr + } + policyJSON[repo]["sigstore"] = "" + registryNamespace := HaveMatchRegistry(repo, registryConfigs) + if registryNamespace != nil { + policyJSON[repo]["sigstore"] = registryNamespace.SigStore + } + } + } + return policyJSON, nil +} diff --git a/test/e2e/trust_test.go b/test/e2e/trust_test.go new file mode 100644 index 000000000..bbf09eca4 --- /dev/null +++ b/test/e2e/trust_test.go @@ -0,0 +1,72 @@ +package integration + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + . "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman trust", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.RestoreAllArtifacts() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) + GinkgoWriter.Write([]byte(timedResult)) + }) + + It("podman image trust show", func() { + path, err := os.Getwd() + if err != nil { + os.Exit(1) + } + session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Dir(path), "--policypath", filepath.Join(filepath.Dir(path), "policy.json")}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + outArray := session.OutputToStringArray() + Expect(len(outArray)).To(Equal(3)) + Expect(outArray[0]).Should(ContainSubstring("accept")) + Expect(outArray[1]).Should(ContainSubstring("reject")) + Expect(outArray[2]).Should(ContainSubstring("signed")) + }) + + It("podman image trust set", func() { + path, err := os.Getwd() + if err != nil { + os.Exit(1) + } + session := podmanTest.Podman([]string{"image", "trust", "set", "--policypath", filepath.Join(filepath.Dir(path), "trust_set_test.json"), "-t", "accept", "default"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + var teststruct map[string][]map[string]string + policyContent, err := ioutil.ReadFile(filepath.Join(filepath.Dir(path), "trust_set_test.json")) + if err != nil { + os.Exit(1) + } + err = json.Unmarshal(policyContent, &teststruct) + if err != nil { + os.Exit(1) + } + Expect(teststruct["default"][0]["type"]).To(Equal("insecureAcceptAnything")) + }) +}) |