//go:build search // +build search package client_test import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "os" "path" "path/filepath" "testing" . "github.com/smartystreets/goconvey/convey" "gopkg.in/resty.v1" "zotregistry.dev/zot/pkg/api" "zotregistry.dev/zot/pkg/api/config" "zotregistry.dev/zot/pkg/api/constants" "zotregistry.dev/zot/pkg/cli/client" extConf "zotregistry.dev/zot/pkg/extensions/config" test "zotregistry.dev/zot/pkg/test/common" ) const ( BaseSecureURL1 = "https://127.0.0.1:8088" HOST1 = "127.0.0.1:8088" SecurePort1 = "8088" BaseSecureURL2 = "https://127.0.0.1:8089" SecurePort2 = "8089" BaseSecureURL3 = "https://127.0.0.1:8090" SecurePort3 = "8090" ServerCert = "../../../test/data/server.cert" ServerKey = "../../../test/data/server.key" CACert = "../../../test/data/ca.crt" sourceCertsDir = "../../../test/data" certsDir1 = "/.config/containers/certs.d/127.0.0.1:8088/" ) func TestTLSWithAuth(t *testing.T) { Convey("Make a new controller", t, func() { caCert, err := os.ReadFile(CACert) So(err, ShouldBeNil) caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) resty.SetTLSClientConfig(&tls.Config{RootCAs: caCertPool, MinVersion: tls.VersionTLS12}) defer func() { resty.SetTLSClientConfig(nil) }() conf := config.New() conf.HTTP.Port = SecurePort1 username, seedUser := test.GenerateRandomString() password, seedPass := test.GenerateRandomString() htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password)) defer os.Remove(htpasswdPath) conf.HTTP.Auth = &config.AuthConfig{ HTPasswd: config.AuthHTPasswd{ Path: htpasswdPath, }, } conf.HTTP.TLS = &config.TLSConfig{ Cert: ServerCert, Key: ServerKey, CACert: CACert, } enable := true conf.Extensions = &extConf.ExtensionConfig{ Search: &extConf.SearchConfig{BaseConfig: extConf.BaseConfig{Enable: &enable}}, } ctlr := api.NewController(conf) ctlr.Log.Info().Int64("seedUser", seedUser).Int64("seedPass", seedPass).Msg("random seed for username & password") ctlr.Config.Storage.RootDirectory = t.TempDir() cm := test.NewControllerManager(ctlr) cm.StartAndWait(conf.HTTP.Port) defer cm.StopServer() Convey("Test with htpassw auth", func() { configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`) defer os.Remove(configPath) home := os.Getenv("HOME") destCertsDir := filepath.Join(home, certsDir1) err := test.CopyTestKeysAndCerts(destCertsDir) So(err, ShouldBeNil) defer os.RemoveAll(destCertsDir) args := []string{"name", "dummyImageName", "--url", HOST1} imageCmd := client.NewImageCommand(client.NewSearchService()) imageBuff := bytes.NewBufferString("") imageCmd.SetOut(imageBuff) imageCmd.SetErr(imageBuff) imageCmd.SetArgs(args) err = imageCmd.Execute() So(err, ShouldNotBeNil) So(imageBuff.String(), ShouldContainSubstring, "scheme not provided") args = []string{"list", "--config", "imagetest"} configPath = makeConfigFile( fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`, BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix)) defer os.Remove(configPath) imageCmd = client.NewImageCommand(client.NewSearchService()) imageBuff = bytes.NewBufferString("") imageCmd.SetOut(imageBuff) imageCmd.SetErr(imageBuff) imageCmd.SetArgs(args) err = imageCmd.Execute() So(err, ShouldNotBeNil) So(imageBuff.String(), ShouldContainSubstring, "check credentials") user := fmt.Sprintf("%s:%s", username, password) args = []string{"-u", user, "--config", "imagetest"} configPath = makeConfigFile( fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`, BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix)) defer os.Remove(configPath) imageCmd = client.NewImageCommand(client.NewSearchService()) imageBuff = bytes.NewBufferString("") imageCmd.SetOut(imageBuff) imageCmd.SetErr(imageBuff) imageCmd.SetArgs(args) err = imageCmd.Execute() So(err, ShouldBeNil) }) }) } func TestTLSWithoutAuth(t *testing.T) { Convey("Home certs - Make a new controller", t, func() { caCert, err := os.ReadFile(CACert) So(err, ShouldBeNil) caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) resty.SetTLSClientConfig(&tls.Config{RootCAs: caCertPool, MinVersion: tls.VersionTLS12}) defer func() { resty.SetTLSClientConfig(nil) }() conf := config.New() conf.HTTP.Port = SecurePort1 conf.HTTP.TLS = &config.TLSConfig{ Cert: ServerCert, Key: ServerKey, CACert: CACert, } enable := true conf.Extensions = &extConf.ExtensionConfig{ Search: &extConf.SearchConfig{BaseConfig: extConf.BaseConfig{Enable: &enable}}, } ctlr := api.NewController(conf) ctlr.Config.Storage.RootDirectory = t.TempDir() cm := test.NewControllerManager(ctlr) cm.StartAndWait(conf.HTTP.Port) defer cm.StopServer() Convey("Certs in user's home", func() { configPath := makeConfigFile( fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`, BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix)) defer os.Remove(configPath) home := os.Getenv("HOME") destCertsDir := filepath.Join(home, certsDir1) err := test.CopyFiles(sourceCertsDir, destCertsDir) So(err, ShouldBeNil) defer os.RemoveAll(destCertsDir) args := []string{"list", "--config", "imagetest"} imageCmd := client.NewImageCommand(client.NewSearchService()) imageBuff := bytes.NewBufferString("") imageCmd.SetOut(imageBuff) imageCmd.SetErr(imageBuff) imageCmd.SetArgs(args) err = imageCmd.Execute() So(err, ShouldBeNil) }) }) } func TestTLSBadCerts(t *testing.T) { Convey("Make a new controller", t, func() { caCert, err := os.ReadFile(CACert) So(err, ShouldBeNil) caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) resty.SetTLSClientConfig(&tls.Config{RootCAs: caCertPool, MinVersion: tls.VersionTLS12}) defer func() { resty.SetTLSClientConfig(nil) }() conf := config.New() conf.HTTP.Port = SecurePort3 conf.HTTP.TLS = &config.TLSConfig{ Cert: ServerCert, Key: ServerKey, CACert: CACert, } ctlr := api.NewController(conf) ctlr.Config.Storage.RootDirectory = t.TempDir() cm := test.NewControllerManager(ctlr) cm.StartAndWait(conf.HTTP.Port) defer cm.StopServer() Convey("Test with system certs", func() { configPath := makeConfigFile( fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`, BaseSecureURL3, constants.RoutePrefix, constants.ExtCatalogPrefix)) defer os.Remove(configPath) args := []string{"list", "--config", "imagetest"} imageCmd := client.NewImageCommand(client.NewSearchService()) imageBuff := bytes.NewBufferString("") imageCmd.SetOut(imageBuff) imageCmd.SetErr(imageBuff) imageCmd.SetArgs(args) err := imageCmd.Execute() So(err, ShouldNotBeNil) So(imageBuff.String(), ShouldContainSubstring, "certificate signed by unknown authority") }) }) } func makeConfigFile(content string) string { os.Setenv("HOME", os.TempDir()) home, err := os.UserHomeDir() if err != nil { panic(err) } configPath := path.Join(home, "/.zot") if err := os.WriteFile(configPath, []byte(content), 0o600); err != nil { panic(err) } return configPath }