summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/search.go73
-rw-r--r--docs/podman-search.1.md6
-rw-r--r--test/e2e/libpod_suite_test.go33
-rw-r--r--test/e2e/search_test.go144
4 files changed, 241 insertions, 15 deletions
diff --git a/cmd/podman/search.go b/cmd/podman/search.go
index 803661753..a5eb580cd 100644
--- a/cmd/podman/search.go
+++ b/cmd/podman/search.go
@@ -2,16 +2,19 @@ package main
import (
"context"
+ "fmt"
"reflect"
"strconv"
"strings"
"github.com/containers/image/docker"
+ "github.com/containers/image/types"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/cmd/podman/formats"
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
"github.com/projectatomic/libpod/libpod/common"
sysreg "github.com/projectatomic/libpod/pkg/registries"
+ "github.com/projectatomic/libpod/pkg/util"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -43,6 +46,10 @@ var (
Name: "registry",
Usage: "specific registry to search",
},
+ cli.BoolTFlag{
+ Name: "tls-verify",
+ Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
+ },
}
searchDescription = `
Search registries for a given image. Can search all the default registries or a specific registry.
@@ -106,15 +113,9 @@ func searchCmd(c *cli.Context) error {
limit: c.Int("limit"),
filter: c.StringSlice("filter"),
}
-
- var registries []string
- if len(c.StringSlice("registry")) > 0 {
- registries = c.StringSlice("registry")
- } else {
- registries, err = sysreg.GetRegistries()
- if err != nil {
- return errors.Wrapf(err, "error getting registries to search")
- }
+ registries, sc, err := getSystemContextAndRegistries(c)
+ if err != nil {
+ return err
}
filter, err := parseSearchFilter(&opts)
@@ -122,7 +123,7 @@ func searchCmd(c *cli.Context) error {
return err
}
- return generateSearchOutput(term, registries, opts, *filter)
+ return generateSearchOutput(term, registries, opts, *filter, sc)
}
func genSearchFormat(format string) string {
@@ -153,8 +154,54 @@ func (s *searchParams) headerMap() map[string]string {
return values
}
-func getSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams) ([]searchParams, error) {
+// A wrapper for GetSystemContext and GetInsecureRegistries
+// Sets up system context and active list of registries to search with
+func getSystemContextAndRegistries(c *cli.Context) ([]string, *types.SystemContext, error) {
sc := common.GetSystemContext("", "", false)
+
+ // Variables for setting up Registry and TLSVerify
+ tlsVerify := c.BoolT("tls-verify")
+ forceSecure := false
+
+ if c.IsSet("tls-verify") {
+ forceSecure = c.BoolT("tls-verify")
+ }
+
+ var registries []string
+ if len(c.StringSlice("registry")) > 0 {
+ registries = c.StringSlice("registry")
+ } else {
+ var err error
+ registries, err = sysreg.GetRegistries()
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "error getting registries to search")
+ }
+ }
+
+ // If user flagged to skip verify for HTTP connections, set System Context as such
+ if !tlsVerify {
+ // If tls-verify is set to false, allow insecure always.
+ sc.DockerInsecureSkipTLSVerify = true
+ } else if !forceSecure {
+ // if the user didn't allow nor disallow insecure registries, check to see if the registry is insecure
+ insecureRegistries, err := sysreg.GetInsecureRegistries()
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "error getting insecure registries to search")
+ }
+
+ for _, reg := range insecureRegistries {
+ // if there are any insecure registries in registries, allow for HTTP
+ if util.StringInSlice(reg, registries) {
+ sc.DockerInsecureSkipTLSVerify = true
+ logrus.Info(fmt.Sprintf("%s is an insecure registry; searching with tls-verify=false", reg))
+ break
+ }
+ }
+ }
+ return registries, sc, nil
+}
+
+func getSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams, sc *types.SystemContext) ([]searchParams, error) {
// Max number of queries by default is 25
limit := maxQueries
if opts.limit != 0 {
@@ -222,8 +269,8 @@ func getSearchOutput(term string, registries []string, opts searchOpts, filter s
return paramsArr, nil
}
-func generateSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams) error {
- searchOutput, err := getSearchOutput(term, registries, opts, filter)
+func generateSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams, sc *types.SystemContext) error {
+ searchOutput, err := getSearchOutput(term, registries, opts, filter, sc)
if err != nil {
return err
}
diff --git a/docs/podman-search.1.md b/docs/podman-search.1.md
index 84a134200..432906821 100644
--- a/docs/podman-search.1.md
+++ b/docs/podman-search.1.md
@@ -70,6 +70,12 @@ Do not truncate the output
Specific registry to search (only the given registry will be searched, not the default registries)
+**--tls-verify**
+
+Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true,
+then tls verification will be used, If set to false then tls verification will not be used. If not specified
+both insecured and default registries will be searched through, and tls will be used when possible.
+
## EXAMPLES
```
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 9f59eb4a6..d06818fd3 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -2,6 +2,7 @@ package integration
import (
"context"
+ "encoding/json"
"fmt"
"io/ioutil"
"os"
@@ -12,8 +13,6 @@ import (
"testing"
"time"
- "encoding/json"
-
"github.com/containers/image/copy"
"github.com/containers/image/signature"
"github.com/containers/image/storage"
@@ -268,6 +267,36 @@ func (s *PodmanSession) OutputToStringArray() []string {
return strings.Split(output, "\n")
}
+// ErrorGrepString takes session stderr output and behaves like grep. it returns a bool
+// if successful and an array of strings on positive matches
+func (s *PodmanSession) ErrorGrepString(term string) (bool, []string) {
+ var (
+ greps []string
+ matches bool
+ )
+
+ for _, line := range strings.Split(s.ErrorToString(), "\n") {
+ if strings.Contains(line, term) {
+ matches = true
+ greps = append(greps, line)
+ }
+ }
+ return matches, greps
+}
+
+// ErrorToString formats session stderr to string
+func (s *PodmanSession) ErrorToString() string {
+ fields := strings.Fields(fmt.Sprintf("%s", s.Err.Contents()))
+ return strings.Join(fields, " ")
+}
+
+// ErrorToStringArray returns the stderr output as a []string
+// where each array item is a line split by newline
+func (s *PodmanSession) ErrorToStringArray() []string {
+ output := fmt.Sprintf("%s", s.Err.Contents())
+ return strings.Split(output, "\n")
+}
+
// IsJSONOutputValid attempts to unmarshal the session buffer
// and if successful, returns true, else false
func (s *PodmanSession) IsJSONOutputValid() bool {
diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go
index 5a814b139..96e1422ed 100644
--- a/test/e2e/search_test.go
+++ b/test/e2e/search_test.go
@@ -1,7 +1,9 @@
package integration
import (
+ "io/ioutil"
"os"
+ "path/filepath"
"strconv"
. "github.com/onsi/ginkgo"
@@ -14,13 +16,26 @@ var _ = Describe("Podman search", func() {
err error
podmanTest PodmanTest
)
+ const regFileContents = `
+ [registries.search]
+ registries = ['localhost:5000']
+ [registries.insecure]
+ registries = ['localhost:5000']`
+
+ const badRegFileContents = `
+ [registries.search]
+ registries = ['localhost:5000']
+ # empty
+ [registries.insecure]
+ registries = []`
BeforeEach(func() {
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
})
AfterEach(func() {
@@ -96,4 +111,133 @@ var _ = Describe("Podman search", func() {
Expect(output[i]).To(Equal(""))
}
})
+
+ It("podman search attempts HTTP if tls-verify flag is set false", func() {
+ fakereg := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", "5000:5000", "docker.io/library/registry:2", "/entrypoint.sh", "/etc/docker/registry/config.yml"})
+ fakereg.WaitWithDefaultTimeout()
+ Expect(fakereg.ExitCode()).To(Equal(0))
+
+ if !WaitContainerReady(&podmanTest, "registry", "listening on", 20, 1) {
+ Skip("Can not start docker registry.")
+ }
+
+ search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "fake/image:andtag", "--tls-verify=false"})
+ search.WaitWithDefaultTimeout()
+
+ // if this test succeeded, there will be no output (there is no entry named fake/image:andtag in an empty registry)
+ // and the exit code will be 0
+ Expect(search.ExitCode()).To(Equal(0))
+ Expect(search.OutputToString()).Should(BeEmpty())
+ Expect(search.ErrorToString()).Should(BeEmpty())
+ })
+
+ It("podman search in local registry", func() {
+ registry := podmanTest.Podman([]string{"run", "-d", "--name", "registry3", "-p", "5000:5000", "docker.io/library/registry:2", "/entrypoint.sh", "/etc/docker/registry/config.yml"})
+ registry.WaitWithDefaultTimeout()
+ Expect(registry.ExitCode()).To(Equal(0))
+
+ if !WaitContainerReady(&podmanTest, "registry3", "listening on", 20, 1) {
+ Skip("Can not start docker registry.")
+ }
+
+ push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
+ push.WaitWithDefaultTimeout()
+ Expect(push.ExitCode()).To(Equal(0))
+ search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "my-alpine", "--tls-verify=false"})
+ search.WaitWithDefaultTimeout()
+
+ Expect(search.ExitCode()).To(Equal(0))
+ Expect(search.OutputToString()).ShouldNot(BeEmpty())
+ })
+
+ It("podman search attempts HTTP if registry is in registries.insecure and force secure is false", func() {
+ registry := podmanTest.Podman([]string{"run", "-d", "--name", "registry4", "-p", "5000:5000", "docker.io/library/registry:2", "/entrypoint.sh", "/etc/docker/registry/config.yml"})
+ registry.WaitWithDefaultTimeout()
+ Expect(registry.ExitCode()).To(Equal(0))
+
+ if !WaitContainerReady(&podmanTest, "registry4", "listening on", 20, 1) {
+ Skip("Can not start docker registry.")
+ }
+
+ push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
+ push.WaitWithDefaultTimeout()
+ Expect(push.ExitCode()).To(Equal(0))
+
+ // registries.conf set up
+ regFileBytes := []byte(regFileContents)
+ outfile := filepath.Join(podmanTest.TempDir, "registries.conf")
+ os.Setenv("REGISTRIES_CONFIG_PATH", outfile)
+ ioutil.WriteFile(outfile, regFileBytes, 0644)
+
+ search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "my-alpine"})
+ search.WaitWithDefaultTimeout()
+
+ Expect(search.ExitCode()).To(Equal(0))
+ match, _ := search.GrepString("my-alpine")
+ Expect(match).Should(BeTrue())
+ Expect(search.ErrorToString()).Should(BeEmpty())
+
+ // cleanup
+ os.Setenv("REGISTRIES_CONFIG_PATH", "")
+ })
+
+ It("podman search doesn't attempt HTTP if force secure is true", func() {
+ registry := podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry5", "registry:2"})
+ registry.WaitWithDefaultTimeout()
+ Expect(registry.ExitCode()).To(Equal(0))
+
+ if !WaitContainerReady(&podmanTest, "registry5", "listening on", 20, 1) {
+ Skip("Can not start docker registry.")
+ }
+ push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
+ push.WaitWithDefaultTimeout()
+ Expect(push.ExitCode()).To(Equal(0))
+
+ // registries.conf set up
+ regFileBytes := []byte(regFileContents)
+ outfile := filepath.Join(podmanTest.TempDir, "registries.conf")
+ os.Setenv("REGISTRIES_CONFIG_PATH", outfile)
+ ioutil.WriteFile(outfile, regFileBytes, 0644)
+
+ search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "my-alpine", "--tls-verify=true"})
+ search.WaitWithDefaultTimeout()
+
+ Expect(search.ExitCode()).To(Equal(0))
+ Expect(search.OutputToString()).Should(BeEmpty())
+ match, _ := search.ErrorGrepString("error")
+ Expect(match).Should(BeTrue())
+
+ // cleanup
+ os.Setenv("REGISTRIES_CONFIG_PATH", "")
+ })
+
+ It("podman search doesn't attempt HTTP if registry is not listed as insecure", func() {
+ registry := podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry6", "registry:2"})
+ registry.WaitWithDefaultTimeout()
+ Expect(registry.ExitCode()).To(Equal(0))
+
+ if !WaitContainerReady(&podmanTest, "registry6", "listening on", 20, 1) {
+ Skip("Can not start docker registry.")
+ }
+ push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
+ push.WaitWithDefaultTimeout()
+ Expect(push.ExitCode()).To(Equal(0))
+
+ // registries.conf set up
+ regFileBytes := []byte(badRegFileContents)
+ outfile := filepath.Join(podmanTest.TempDir, "registries.conf")
+ os.Setenv("REGISTRIES_CONFIG_PATH", outfile)
+ ioutil.WriteFile(outfile, regFileBytes, 0644)
+
+ search := podmanTest.Podman([]string{"search", "--registry", "localhost:5000", "my-alpine"})
+ search.WaitWithDefaultTimeout()
+
+ Expect(search.ExitCode()).To(Equal(0))
+ Expect(search.OutputToString()).Should(BeEmpty())
+ match, _ := search.ErrorGrepString("error")
+ Expect(match).Should(BeTrue())
+
+ // cleanup
+ os.Setenv("REGISTRIES_CONFIG_PATH", "")
+ })
})