Added --tls-verify functionality to podman search, with tests

Signed-off-by: haircommander <pehunt@redhat.com>

Closes: #932
Approved by: baude
This commit is contained in:
haircommander 2018-06-08 17:56:25 -04:00 committed by Atomic Bot
parent 894ae2bf76
commit b43677c9fd
4 changed files with 241 additions and 15 deletions

View File

@ -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
}

View File

@ -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
```

View File

@ -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 {

View File

@ -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", "")
})
})