diff --git a/cmd/skopeo/login.go b/cmd/skopeo/login.go new file mode 100644 index 00000000..a9faaa58 --- /dev/null +++ b/cmd/skopeo/login.go @@ -0,0 +1,47 @@ +package main + +import ( + "io" + "os" + + "github.com/containers/common/pkg/auth" + "github.com/containers/image/v5/types" + "github.com/spf13/cobra" +) + +type loginOptions struct { + global *globalOptions + loginOpts auth.LoginOptions + getLogin optionalBool + tlsVerify optionalBool +} + +func loginCmd(global *globalOptions) *cobra.Command { + opts := loginOptions{ + global: global, + } + cmd := &cobra.Command{ + Use: "login", + Short: "Login to a container registry", + Long: "Login to a container registry on a specified server.", + RunE: commandAction(opts.run), + Example: `skopeo login quay.io`, + } + adjustUsage(cmd) + flags := cmd.Flags() + optionalBoolFlag(flags, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the registry") + flags.AddFlagSet(auth.GetLoginFlags(&opts.loginOpts)) + return cmd +} + +func (opts *loginOptions) run(args []string, stdout io.Writer) error { + ctx, cancel := opts.global.commandTimeoutContext() + defer cancel() + opts.loginOpts.Stdout = stdout + opts.loginOpts.Stdin = os.Stdin + sys := opts.global.newSystemContext() + if opts.tlsVerify.present { + sys.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.value) + } + return auth.Login(ctx, sys, &opts.loginOpts, args) +} diff --git a/cmd/skopeo/logout.go b/cmd/skopeo/logout.go new file mode 100644 index 00000000..92dc308f --- /dev/null +++ b/cmd/skopeo/logout.go @@ -0,0 +1,35 @@ +package main + +import ( + "io" + + "github.com/containers/common/pkg/auth" + "github.com/spf13/cobra" +) + +type logoutOptions struct { + global *globalOptions + logoutOpts auth.LogoutOptions +} + +func logoutCmd(global *globalOptions) *cobra.Command { + opts := logoutOptions{ + global: global, + } + cmd := &cobra.Command{ + Use: "logout", + Short: "Logout of a container registry", + Long: "Logout of a container registry on a specified server.", + RunE: commandAction(opts.run), + Example: `skopeo logout quay.io`, + } + adjustUsage(cmd) + cmd.Flags().AddFlagSet(auth.GetLogoutFlags(&opts.logoutOpts)) + return cmd +} + +func (opts *logoutOptions) run(args []string, stdout io.Writer) error { + opts.logoutOpts.Stdout = stdout + sys := opts.global.newSystemContext() + return auth.Logout(sys, &opts.logoutOpts, args) +} diff --git a/cmd/skopeo/main.go b/cmd/skopeo/main.go index 9cb12123..caca33b8 100644 --- a/cmd/skopeo/main.go +++ b/cmd/skopeo/main.go @@ -6,6 +6,7 @@ import ( "time" "github.com/containers/image/v5/signature" + "github.com/containers/image/v5/types" "github.com/containers/skopeo/version" "github.com/containers/storage/pkg/reexec" "github.com/sirupsen/logrus" @@ -71,6 +72,8 @@ func createApp() (*cobra.Command, *globalOptions) { deleteCmd(&opts), inspectCmd(&opts), layersCmd(&opts), + loginCmd(&opts), + logoutCmd(&opts), manifestDigestCmd(), syncCmd(&opts), standaloneSignCmd(), @@ -129,3 +132,21 @@ func (opts *globalOptions) commandTimeoutContext() (context.Context, context.Can } return ctx, cancel } + +// newSystemContext returns a *types.SystemContext corresponding to opts. +// It is guaranteed to return a fresh instance, so it is safe to make additional updates to it. +func (opts *globalOptions) newSystemContext() *types.SystemContext { + ctx := &types.SystemContext{ + RegistriesDirPath: opts.registriesDirPath, + ArchitectureChoice: opts.overrideArch, + OSChoice: opts.overrideOS, + VariantChoice: opts.overrideVariant, + SystemRegistriesConfPath: opts.registriesConfPath, + BigFilesTemporaryDir: opts.tmpDir, + } + // DEPRECATED: We support this for backward compatibility, but override it if a per-image flag is provided. + if opts.tlsVerify.present { + ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.value) + } + return ctx +} diff --git a/cmd/skopeo/main_test.go b/cmd/skopeo/main_test.go index 9fd2e275..33e4524f 100644 --- a/cmd/skopeo/main_test.go +++ b/cmd/skopeo/main_test.go @@ -2,6 +2,10 @@ package main import ( "bytes" + "testing" + + "github.com/containers/image/v5/types" + "github.com/stretchr/testify/assert" ) // runSkopeo creates an app object and runs it with args, with an implied first "skopeo". @@ -14,3 +18,30 @@ func runSkopeo(args ...string) (string, error) { err := app.Execute() return stdout.String(), err } + +func TestGlobalOptionsNewSystemContext(t *testing.T) { + // Default state + opts, _ := fakeGlobalOptions(t, []string{}) + res := opts.newSystemContext() + assert.Equal(t, &types.SystemContext{}, res) + // Set everything to non-default values. + opts, _ = fakeGlobalOptions(t, []string{ + "--registries.d", "/srv/registries.d", + "--override-arch", "overridden-arch", + "--override-os", "overridden-os", + "--override-variant", "overridden-variant", + "--tmpdir", "/srv", + "--registries-conf", "/srv/registries.conf", + "--tls-verify=false", + }) + res = opts.newSystemContext() + assert.Equal(t, &types.SystemContext{ + RegistriesDirPath: "/srv/registries.d", + ArchitectureChoice: "overridden-arch", + OSChoice: "overridden-os", + VariantChoice: "overridden-variant", + BigFilesTemporaryDir: "/srv", + SystemRegistriesConfPath: "/srv/registries.conf", + DockerInsecureSkipTLSVerify: types.OptionalBoolTrue, + }, res) +} diff --git a/cmd/skopeo/utils.go b/cmd/skopeo/utils.go index fd38aa43..1571f99b 100644 --- a/cmd/skopeo/utils.go +++ b/cmd/skopeo/utils.go @@ -111,29 +111,20 @@ func imageFlags(global *globalOptions, shared *sharedImageOptions, flagPrefix, c // newSystemContext returns a *types.SystemContext corresponding to opts. // It is guaranteed to return a fresh instance, so it is safe to make additional updates to it. func (opts *imageOptions) newSystemContext() (*types.SystemContext, error) { - ctx := &types.SystemContext{ - RegistriesDirPath: opts.global.registriesDirPath, - ArchitectureChoice: opts.global.overrideArch, - OSChoice: opts.global.overrideOS, - VariantChoice: opts.global.overrideVariant, - DockerCertPath: opts.dockerCertPath, - OCISharedBlobDirPath: opts.sharedBlobDir, - AuthFilePath: opts.shared.authFilePath, - DockerDaemonHost: opts.dockerDaemonHost, - DockerDaemonCertPath: opts.dockerCertPath, - SystemRegistriesConfPath: opts.global.registriesConfPath, - BigFilesTemporaryDir: opts.global.tmpDir, - } + // *types.SystemContext instance from globalOptions + // imageOptions option overrides the instance if both are present. + ctx := opts.global.newSystemContext() + ctx.DockerCertPath = opts.dockerCertPath + ctx.OCISharedBlobDirPath = opts.sharedBlobDir + ctx.AuthFilePath = opts.shared.authFilePath + ctx.DockerDaemonHost = opts.dockerDaemonHost + ctx.DockerDaemonCertPath = opts.dockerCertPath if opts.dockerImageOptions.authFilePath.present { ctx.AuthFilePath = opts.dockerImageOptions.authFilePath.value } if opts.tlsVerify.present { ctx.DockerDaemonInsecureSkipTLSVerify = !opts.tlsVerify.value } - // DEPRECATED: We support this for backward compatibility, but override it if a per-image flag is provided. - if opts.global.tlsVerify.present { - ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.global.tlsVerify.value) - } if opts.tlsVerify.present { ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.value) } @@ -167,7 +158,6 @@ type imageDestOptions struct { func imageDestFlags(global *globalOptions, shared *sharedImageOptions, flagPrefix, credsOptionAlias string) (pflag.FlagSet, *imageDestOptions) { genericFlags, genericOptions := imageFlags(global, shared, flagPrefix, credsOptionAlias) opts := imageDestOptions{imageOptions: genericOptions} - fs := pflag.FlagSet{} fs.AddFlagSet(&genericFlags) fs.BoolVar(&opts.dirForceCompression, flagPrefix+"compress", false, "Compress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source)") diff --git a/completions/bash/skopeo b/completions/bash/skopeo index 7ce610d2..46fae3b4 100644 --- a/completions/bash/skopeo +++ b/completions/bash/skopeo @@ -158,6 +158,33 @@ _skopeo_list_repository_tags() { _complete_ "$options_with_args" "$boolean_options" } +_skopeo_login() { + local options_with_args=" + --authfile + --cert-dir + --password -p + --username -u + " + + local boolean_options=" + --get-login + --tls-verify + --password-stdin + " + _complete_ "$options_with_args" "$boolean_options" +} + +_skopeo_logout() { + local options_with_args=" + --authfile + " + + local boolean_options=" + --all -a + " + _complete_ "$options_with_args" "$boolean_options" +} + _skopeo_skopeo() { # XXX: Changes here need to be refleceted in the manually expanded # string in the `case` statement below as well. @@ -182,6 +209,8 @@ _skopeo_skopeo() { delete inspect list-tags + login + logout manifest-digest standalone-sign standalone-verify diff --git a/docs/skopeo-login.1.md b/docs/skopeo-login.1.md new file mode 100644 index 00000000..706a5377 --- /dev/null +++ b/docs/skopeo-login.1.md @@ -0,0 +1,101 @@ +% skopeo-login(1) + +## NAME +skopeo\-login - Login to a container registry + +## SYNOPSIS +**skoepo login** [*options*] *registry* + +## DESCRIPTION +**skopeo login** logs into a specified registry server with the correct username +and password. **skopeo login** reads in the username and password from STDIN. +The username and password can also be set using the **username** and **password** flags. +The path of the authentication file can be specified by the user by setting the **authfile** +flag. The default path used is **${XDG\_RUNTIME\_DIR}/containers/auth.json**. + +## OPTIONS + +**--password**, **-p**=*password* + +Password for registry + +**--password-stdin** + +Take the password from stdin + +**--username**, **-u**=*username* + +Username for registry + +**--authfile**=*path* + +Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json + +Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE +environment variable. `export REGISTRY_AUTH_FILE=path` + +**--get-login** + +Return the logged-in user for the registry. Return error if no login is found. + +**--cert-dir**=*path* + +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Default certificates directory is _/etc/containers/certs.d_. + +**--tls-verify**=*true|false* + +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, +TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. + +**--help**, **-h** + +Print usage statement + +## EXAMPLES + +``` +$ skopeo login docker.io +Username: testuser +Password: +Login Succeeded! +``` + +``` +$ skopeo login -u testuser -p testpassword localhost:5000 +Login Succeeded! +``` + +``` +$ skopeo login --authfile authdir/myauths.json docker.io +Username: testuser +Password: +Login Succeeded! +``` + +``` +$ skopeo login --tls-verify=false -u test -p test localhost:5000 +Login Succeeded! +``` + +``` +$ skopeo login --cert-dir /etc/containers/certs.d/ -u foo -p bar localhost:5000 +Login Succeeded! +``` + +``` +$ skopeo login -u testuser --password-stdin < testpassword.txt docker.io +Login Succeeded! +``` + +``` +$ echo $testpassword | skopeo login -u testuser --password-stdin docker.io +Login Succeeded! +``` + +## SEE ALSO +skopeo(1), skopeo-logout(1) + +## HISTORY +May 2020, Originally compiled by Qi Wang diff --git a/docs/skopeo-logout.1.md b/docs/skopeo-logout.1.md new file mode 100644 index 00000000..cf27e29f --- /dev/null +++ b/docs/skopeo-logout.1.md @@ -0,0 +1,53 @@ +% skopeo-logout(1) + +## NAME +skopeo\-logout - Logout of a container registry + +## SYNOPSIS +**skopeo logout** [*options*] *registry* + +## DESCRIPTION +**skopeo logout** logs out of a specified registry server by deleting the cached credentials +stored in the **auth.json** file. The path of the authentication file can be overridden by the user by setting the **authfile** flag. +The default path used is **${XDG\_RUNTIME\_DIR}/containers/auth.json**. +All the cached credentials can be removed by setting the **all** flag. + +## OPTIONS + +**--authfile**=*path* + +Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json + +Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE +environment variable. `export REGISTRY_AUTH_FILE=path` + +**--all**, **-a** + +Remove the cached credentials for all registries in the auth file + +**--help**, **-h** + +Print usage statement + +## EXAMPLES + +``` +$ skopeo logout docker.io +Remove login credentials for docker.io +``` + +``` +$ skopeo logout --authfile authdir/myauths.json docker.io +Remove login credentials for docker.io +``` + +``` +$ skopeo logout --all +Remove login credentials for all registries +``` + +## SEE ALSO +skopeo(1), skopeo-login(1) + +## HISTORY +May 2020, Originally compiled by Qi Wang diff --git a/go.mod b/go.mod index 3b56b524..ad9a17a6 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/containerd/containerd v1.3.0 // indirect + github.com/containers/common v0.11.2 github.com/containers/image/v5 v5.4.4 github.com/containers/ocicrypt v1.0.2 github.com/containers/storage v1.19.1 diff --git a/go.sum b/go.sum index 5913f799..f0f01630 100644 --- a/go.sum +++ b/go.sum @@ -38,12 +38,16 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containers/common v0.11.2 h1:e4477fCE3qSA+Z2vT+uUMUTn8s8CyIM++qNm3PCSl68= +github.com/containers/common v0.11.2/go.mod h1:2w3QE6VUmhltGYW4wV00h4okq1Crs7hNI1ZD2I0QRUY= +github.com/containers/image/v5 v5.4.3/go.mod h1:pN0tvp3YbDd7BWavK2aE0mvJUqVd2HmhPjekyWSFm0U= github.com/containers/image/v5 v5.4.4 h1:JSanNn3v/BMd3o0MEvO4R4OKNuoJUSzVGQAI1+0FMXE= github.com/containers/image/v5 v5.4.4/go.mod h1:g7cxNXitiLi6pEr9/L9n/0wfazRuhDKXU15kV86N8h8= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.2 h1:Q0/IPs8ohfbXNxEfyJ2pFVmvJu5BhqJUAmc6ES9NKbo= github.com/containers/ocicrypt v1.0.2/go.mod h1:nsOhbP19flrX6rE7ieGFvBlr7modwmNjsqWarIUce4M= +github.com/containers/storage v1.18.2/go.mod h1:WTBMf+a9ZZ/LbmEVeLHH2TX4CikWbO1Bt+/m58ZHVPg= github.com/containers/storage v1.19.1 h1:YKIzOO12iaD5Ra0PKFS6emcygbHLmwmQOCQRU/19YAQ= github.com/containers/storage v1.19.1/go.mod h1:KbXjSwKnx17ejOsjFcCXSf78mCgZkQSLPBNTMRc3XrQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -119,6 +123,7 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -131,12 +136,14 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc= github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.3 h1:Ce2to9wvs/cuJ2b86/CKQoTYr9VHfpanYosZ0UBJqdw= github.com/klauspost/pgzip v1.2.3/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -164,6 +171,10 @@ github.com/mtrmac/gpgme v0.1.2 h1:dNOmvYmsrakgW7LcgiprD0yfRuQQe8/C8F6Z+zogO3s= github.com/mtrmac/gpgme v0.1.2/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgiVFNI= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -176,9 +187,11 @@ github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5X github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.0 h1:O6L965K88AilqnxeYPks/75HLpp4IG+FjeSCI3cVdRg= github.com/opencontainers/runtime-spec v1.0.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.4.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opencontainers/selinux v1.5.1 h1:jskKwSMFYqyTrHEuJgQoUlTcId0av64S6EWObrIfn5Y= github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= @@ -254,6 +267,7 @@ github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE= github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= +github.com/vbauerster/mpb/v5 v5.0.3/go.mod h1:h3YxU5CSr8rZP4Q3xZPVB3jJLhWPou63lHEdr9ytH4Y= github.com/vbauerster/mpb/v5 v5.0.4 h1:w7l/tJfHmtIOKZkU+bhbDZOUxj1kln9jy4DUOp3Tl14= github.com/vbauerster/mpb/v5 v5.0.4/go.mod h1:fvzasBUyuo35UyuA6sSOlVhpLoNQsp2nBdHw7OiSUU8= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -279,6 +293,8 @@ go4.org v0.0.0-20190218023631-ce4c26f7be8e/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -287,6 +303,7 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -308,6 +325,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03i golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -318,9 +336,12 @@ golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -336,6 +357,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -351,12 +373,15 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= diff --git a/integration/check_test.go b/integration/check_test.go index 202e18bb..7234e6a0 100644 --- a/integration/check_test.go +++ b/integration/check_test.go @@ -30,18 +30,11 @@ type SkopeoSuite struct { func (s *SkopeoSuite) SetUpSuite(c *check.C) { _, err := exec.LookPath(skopeoBinary) c.Assert(err, check.IsNil) -} - -func (s *SkopeoSuite) TearDownSuite(c *check.C) { - -} - -func (s *SkopeoSuite) SetUpTest(c *check.C) { s.regV2 = setupRegistryV2At(c, privateRegistryURL0, false, false) s.regV2WithAuth = setupRegistryV2At(c, privateRegistryURL1, true, false) } -func (s *SkopeoSuite) TearDownTest(c *check.C) { +func (s *SkopeoSuite) TearDownSuite(c *check.C) { if s.regV2 != nil { s.regV2.Close() } @@ -91,3 +84,30 @@ func (s *SkopeoSuite) TestNoNeedAuthToPrivateRegistryV2ImageNotFound(c *check.C) func (s *SkopeoSuite) TestInspectFailsWhenReferenceIsInvalid(c *check.C) { assertSkopeoFails(c, `.*Invalid image name.*`, "inspect", "unknown") } + +func (s *SkopeoSuite) TestLoginLogout(c *check.C) { + wanted := "^Login Succeeded!\n$" + assertSkopeoSucceeds(c, wanted, "login", "--tls-verify=false", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, s.regV2WithAuth.url) + // test --get-login returns username + wanted = fmt.Sprintf("^%s\n$", s.regV2WithAuth.username) + assertSkopeoSucceeds(c, wanted, "login", "--tls-verify=false", "--get-login", s.regV2WithAuth.url) + // test logout + wanted = fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url) + assertSkopeoSucceeds(c, wanted, "logout", s.regV2WithAuth.url) +} + +func (s *SkopeoSuite) TestCopyWithLocalAuth(c *check.C) { + wanted := "^Login Succeeded!\n$" + assertSkopeoSucceeds(c, wanted, "login", "--tls-verify=false", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, s.regV2WithAuth.url) + // copy to private registry using local authentication + imageName := fmt.Sprintf("docker://%s/busybox:mine", s.regV2WithAuth.url) + assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "docker://docker.io/library/busybox:latest", imageName) + // inspec from private registry + assertSkopeoSucceeds(c, "", "inspect", "--tls-verify=false", imageName) + // logout from the registry + wanted = fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url) + assertSkopeoSucceeds(c, wanted, "logout", s.regV2WithAuth.url) + // inspect from private registry should fail after logout + wanted = ".*unauthorized: authentication required.*" + assertSkopeoFails(c, wanted, "inspect", "--tls-verify=false", imageName) +} diff --git a/vendor/github.com/containers/common/LICENSE b/vendor/github.com/containers/common/LICENSE new file mode 100644 index 00000000..8dada3ed --- /dev/null +++ b/vendor/github.com/containers/common/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containers/common/pkg/auth/auth.go b/vendor/github.com/containers/common/pkg/auth/auth.go new file mode 100644 index 00000000..1aa9f8b3 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/auth/auth.go @@ -0,0 +1,257 @@ +package auth + +import ( + "bufio" + "context" + "fmt" + "os" + "strings" + + "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/pkg/docker/config" + "github.com/containers/image/v5/pkg/sysregistriesv2" + "github.com/containers/image/v5/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh/terminal" +) + +// GetDefaultAuthFile returns env value REGISTRY_AUTH_FILE as default --authfile path +// used in multiple --authfile flag definitions +func GetDefaultAuthFile() string { + return os.Getenv("REGISTRY_AUTH_FILE") +} + +// CheckAuthFile validates filepath given by --authfile +// used by command has --authfile flag +func CheckAuthFile(authfile string) error { + if authfile == "" { + return nil + } + if _, err := os.Stat(authfile); err != nil { + return errors.Wrapf(err, "error checking authfile path %s", authfile) + } + return nil +} + +// systemContextWithOptions returns a version of sys +// updated with authFile and certDir values (if they are not ""). +// NOTE: this is a shallow copy that can be used and updated, but may share +// data with the original parameter. +func systemContextWithOptions(sys *types.SystemContext, authFile, certDir string) *types.SystemContext { + if sys != nil { + copy := *sys + sys = © + } else { + sys = &types.SystemContext{} + } + + if authFile != "" { + sys.AuthFilePath = authFile + } + if certDir != "" { + sys.DockerCertPath = certDir + } + return sys +} + +// Login implements a “log in” command with the provided opts and args +// reading the password from opts.Stdin or the options in opts. +func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginOptions, args []string) error { + systemContext = systemContextWithOptions(systemContext, opts.AuthFile, opts.CertDir) + + var ( + server string + err error + ) + if len(args) > 1 { + return errors.Errorf("login accepts only one registry to login to") + } + if len(args) == 0 { + if !opts.AcceptUnspecifiedRegistry { + return errors.Errorf("please provide a registry to login to") + } + if server, err = defaultRegistryWhenUnspecified(systemContext); err != nil { + return err + } + logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", server) + } else { + server = getRegistryName(args[0]) + } + authConfig, err := config.GetCredentials(systemContext, server) + if err != nil { + return errors.Wrapf(err, "error reading auth file") + } + if opts.GetLoginSet { + if authConfig.Username == "" { + return errors.Errorf("not logged into %s", server) + } + fmt.Fprintf(opts.Stdout, "%s\n", authConfig.Username) + return nil + } + if authConfig.IdentityToken != "" { + return errors.Errorf("currently logged in, auth file contains an Identity token") + } + + password := opts.Password + if opts.StdinPassword { + var stdinPasswordStrBuilder strings.Builder + if opts.Password != "" { + return errors.Errorf("Can't specify both --password-stdin and --password") + } + if opts.Username == "" { + return errors.Errorf("Must provide --username with --password-stdin") + } + scanner := bufio.NewScanner(opts.Stdin) + for scanner.Scan() { + fmt.Fprint(&stdinPasswordStrBuilder, scanner.Text()) + } + password = stdinPasswordStrBuilder.String() + } + + // If no username and no password is specified, try to use existing ones. + if opts.Username == "" && password == "" && authConfig.Username != "" && authConfig.Password != "" { + fmt.Println("Authenticating with existing credentials...") + if err := docker.CheckAuth(ctx, systemContext, authConfig.Username, authConfig.Password, server); err == nil { + fmt.Fprintln(opts.Stdout, "Existing credentials are valid. Already logged in to", server) + return nil + } + fmt.Fprintln(opts.Stdout, "Existing credentials are invalid, please enter valid username and password") + } + + username, password, err := getUserAndPass(opts, password, authConfig.Username) + if err != nil { + return errors.Wrapf(err, "error getting username and password") + } + + if err = docker.CheckAuth(ctx, systemContext, username, password, server); err == nil { + // Write the new credentials to the authfile + if err = config.SetAuthentication(systemContext, server, username, password); err != nil { + return err + } + } + if err == nil { + fmt.Fprintln(opts.Stdout, "Login Succeeded!") + return nil + } + if unauthorized, ok := err.(docker.ErrUnauthorizedForCredentials); ok { + logrus.Debugf("error logging into %q: %v", server, unauthorized) + return errors.Errorf("error logging into %q: invalid username/password", server) + } + return errors.Wrapf(err, "error authenticating creds for %q", server) +} + +// getRegistryName scrubs and parses the input to get the server name +func getRegistryName(server string) string { + // removes 'http://' or 'https://' from the front of the + // server/registry string if either is there. This will be mostly used + // for user input from 'Buildah login' and 'Buildah logout'. + server = strings.TrimPrefix(strings.TrimPrefix(server, "https://"), "http://") + // gets the registry from the input. If the input is of the form + // quay.io/myuser/myimage, it will parse it and just return quay.io + split := strings.Split(server, "/") + if len(split) > 1 { + return split[0] + } + return split[0] +} + +// getUserAndPass gets the username and password from STDIN if not given +// using the -u and -p flags. If the username prompt is left empty, the +// displayed userFromAuthFile will be used instead. +func getUserAndPass(opts *LoginOptions, password, userFromAuthFile string) (string, string, error) { + var err error + reader := bufio.NewReader(opts.Stdin) + username := opts.Username + if username == "" { + if userFromAuthFile != "" { + fmt.Fprintf(opts.Stdout, "Username (%s): ", userFromAuthFile) + } else { + fmt.Fprint(opts.Stdout, "Username: ") + } + username, err = reader.ReadString('\n') + if err != nil { + return "", "", errors.Wrapf(err, "error reading username") + } + // If the user just hit enter, use the displayed user from the + // the authentication file. This allows to do a lazy + // `$ buildah login -p $NEW_PASSWORD` without specifying the + // user. + if strings.TrimSpace(username) == "" { + username = userFromAuthFile + } + } + if password == "" { + fmt.Fprint(opts.Stdout, "Password: ") + pass, err := terminal.ReadPassword(0) + if err != nil { + return "", "", errors.Wrapf(err, "error reading password") + } + password = string(pass) + fmt.Fprintln(opts.Stdout) + } + return strings.TrimSpace(username), password, err +} + +// Logout implements a “log out” command with the provided opts and args +func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []string) error { + if err := CheckAuthFile(opts.AuthFile); err != nil { + return err + } + systemContext = systemContextWithOptions(systemContext, opts.AuthFile, "") + + var ( + server string + err error + ) + if len(args) > 1 { + return errors.Errorf("logout accepts only one registry to logout from") + } + if len(args) == 0 && !opts.All { + if !opts.AcceptUnspecifiedRegistry { + return errors.Errorf("please provide a registry to logout from") + } + if server, err = defaultRegistryWhenUnspecified(systemContext); err != nil { + return err + } + logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", server) + } + if len(args) != 0 { + if opts.All { + return errors.Errorf("--all takes no arguments") + } + server = getRegistryName(args[0]) + } + + if opts.All { + if err := config.RemoveAllAuthentication(systemContext); err != nil { + return err + } + fmt.Fprintln(opts.Stdout, "Removed login credentials for all registries") + return nil + } + + err = config.RemoveAuthentication(systemContext, server) + switch err { + case nil: + fmt.Fprintf(opts.Stdout, "Removed login credentials for %s\n", server) + return nil + case config.ErrNotLoggedIn: + return errors.Errorf("Not logged into %s\n", server) + default: + return errors.Wrapf(err, "error logging out of %q", server) + } +} + +// defaultRegistryWhenUnspecified returns first registry from search list of registry.conf +// used by login/logout when registry argument is not specified +func defaultRegistryWhenUnspecified(systemContext *types.SystemContext) (string, error) { + registriesFromFile, err := sysregistriesv2.UnqualifiedSearchRegistries(systemContext) + if err != nil { + return "", errors.Wrapf(err, "error getting registry from registry.conf, please specify a registry") + } + if len(registriesFromFile) == 0 { + return "", errors.Errorf("no registries found in registries.conf, a registry must be provided") + } + return registriesFromFile[0], nil +} diff --git a/vendor/github.com/containers/common/pkg/auth/cli.go b/vendor/github.com/containers/common/pkg/auth/cli.go new file mode 100644 index 00000000..ab033681 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/auth/cli.go @@ -0,0 +1,58 @@ +package auth + +import ( + "io" + + "github.com/spf13/pflag" +) + +// LoginOptions represents common flags in login +// In addition, the caller should probably provide a --tls-verify flag (that affects the provided +// *types.SystemContest) +type LoginOptions struct { + // CLI flags managed by the FlagSet returned by GetLoginFlags + // Callers that use GetLoginFlags should not need to touch these values at all; callers that use + // other CLI frameworks should set them based on user input. + AuthFile string + CertDir string + Password string + Username string + StdinPassword bool + GetLoginSet bool + // Options caller can set + Stdin io.Reader // set to os.Stdin + Stdout io.Writer // set to os.Stdout + AcceptUnspecifiedRegistry bool // set to true if allows login with unspecified registry +} + +// LogoutOptions represents the results for flags in logout +type LogoutOptions struct { + // CLI flags managed by the FlagSet returned by GetLogoutFlags + // Callers that use GetLogoutFlags should not need to touch these values at all; callers that use + // other CLI frameworks should set them based on user input. + AuthFile string + All bool + // Options caller can set + Stdout io.Writer // set to os.Stdout + AcceptUnspecifiedRegistry bool // set to true if allows logout with unspecified registry +} + +// GetLoginFlags defines and returns login flags for containers tools +func GetLoginFlags(flags *LoginOptions) *pflag.FlagSet { + fs := pflag.FlagSet{} + fs.StringVar(&flags.AuthFile, "authfile", GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + fs.StringVar(&flags.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry") + fs.StringVarP(&flags.Password, "password", "p", "", "Password for registry") + fs.StringVarP(&flags.Username, "username", "u", "", "Username for registry") + fs.BoolVar(&flags.StdinPassword, "password-stdin", false, "Take the password from stdin") + fs.BoolVar(&flags.GetLoginSet, "get-login", false, "Return the current login user for the registry") + return &fs +} + +// GetLogoutFlags defines and returns logout flags for containers tools +func GetLogoutFlags(flags *LogoutOptions) *pflag.FlagSet { + fs := pflag.FlagSet{} + fs.StringVar(&flags.AuthFile, "authfile", GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + fs.BoolVarP(&flags.All, "all", "a", false, "Remove the cached credentials for all registries in the auth file") + return &fs +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 93829f10..ec761eac 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -36,6 +36,8 @@ github.com/containerd/cgroups/stats/v1 github.com/containerd/containerd/errdefs github.com/containerd/containerd/log github.com/containerd/containerd/platforms +# github.com/containers/common v0.11.2 +github.com/containers/common/pkg/auth # github.com/containers/image/v5 v5.4.4 github.com/containers/image/v5/copy github.com/containers/image/v5/directory