summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2020-04-21 14:28:09 -0400
committerGitHub <noreply@github.com>2020-04-21 14:28:09 -0400
commit2ed4a0e35f6182b79976979ecf3f3ab9010417bd (patch)
tree5c720fc48147789d21d798acf04ee0905f4f7be4
parent01349990341515b588a42bb8e0b0e765ad98f481 (diff)
parent0138ecfba047d95466b4dd66295d7800412ff2b6 (diff)
downloadpodman-2ed4a0e35f6182b79976979ecf3f3ab9010417bd.tar.gz
podman-2ed4a0e35f6182b79976979ecf3f3ab9010417bd.tar.bz2
podman-2ed4a0e35f6182b79976979ecf3f3ab9010417bd.zip
Merge pull request #5917 from vrothberg/v2-login-logout
v2: implement log{in,out}
-rw-r--r--cmd/podman/login.go68
-rw-r--r--cmd/podman/logout.go57
-rw-r--r--vendor/github.com/containers/common/pkg/auth/auth.go182
-rw-r--r--vendor/github.com/containers/common/pkg/auth/cli.go47
-rw-r--r--vendor/modules.txt1
5 files changed, 355 insertions, 0 deletions
diff --git a/cmd/podman/login.go b/cmd/podman/login.go
new file mode 100644
index 000000000..1843a764d
--- /dev/null
+++ b/cmd/podman/login.go
@@ -0,0 +1,68 @@
+package main
+
+import (
+ "context"
+ "os"
+
+ "github.com/containers/common/pkg/auth"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+type loginOptionsWrapper struct {
+ auth.LoginOptions
+ tlsVerify bool
+}
+
+var (
+ loginOptions = loginOptionsWrapper{}
+ loginCommand = &cobra.Command{
+ Use: "login [flags] REGISTRY",
+ Short: "Login to a container registry",
+ Long: "Login to a container registry on a specified server.",
+ RunE: login,
+ Args: cobra.ExactArgs(1),
+ Example: `podman login quay.io
+ podman login --username ... --password ... quay.io
+ podman login --authfile dir/auth.json quay.io`,
+ }
+)
+
+func init() {
+ // Note that the local and the remote client behave the same: both
+ // store credentials locally while the remote client will pass them
+ // over the wire to the endpoint.
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: loginCommand,
+ })
+ flags := loginCommand.Flags()
+
+ // Flags from the auth package.
+ flags.AddFlagSet(auth.GetLoginFlags(&loginOptions.LoginOptions))
+
+ // Podman flags.
+ flags.BoolVarP(&loginOptions.tlsVerify, "tls-verify", "", false, "Require HTTPS and verify certificates when contacting registries")
+ flags.BoolVarP(&loginOptions.GetLoginSet, "get-login", "", false, "Return the current login user for the registry")
+ loginOptions.Stdin = os.Stdin
+ loginOptions.Stdout = os.Stdout
+}
+
+// Implementation of podman-login.
+func login(cmd *cobra.Command, args []string) error {
+ var skipTLS types.OptionalBool
+
+ if cmd.Flags().Changed("tls-verify") {
+ skipTLS = types.NewOptionalBool(!loginOptions.tlsVerify)
+ }
+
+ sysCtx := types.SystemContext{
+ AuthFilePath: loginOptions.AuthFile,
+ DockerCertPath: loginOptions.CertDir,
+ DockerInsecureSkipTLSVerify: skipTLS,
+ }
+
+ return auth.Login(context.Background(), &sysCtx, &loginOptions.LoginOptions, args[0])
+}
diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go
new file mode 100644
index 000000000..77bdc92b4
--- /dev/null
+++ b/cmd/podman/logout.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ "os"
+
+ "github.com/containers/common/pkg/auth"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ logoutOptions = auth.LogoutOptions{}
+ logoutCommand = &cobra.Command{
+ Use: "logout [flags] REGISTRY",
+ Short: "Logout of a container registry",
+ Long: "Remove the cached username and password for the registry.",
+ RunE: logout,
+ Args: cobra.MaximumNArgs(1),
+ Example: `podman logout quay.io
+ podman logout --authfile dir/auth.json quay.io
+ podman logout --all`,
+ }
+)
+
+func init() {
+ // Note that the local and the remote client behave the same: both
+ // store credentials locally while the remote client will pass them
+ // over the wire to the endpoint.
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: logoutCommand,
+ })
+ flags := logoutCommand.Flags()
+
+ // Flags from the auth package.
+ flags.AddFlagSet(auth.GetLogoutFlags(&logoutOptions))
+ logoutOptions.Stdin = os.Stdin
+ logoutOptions.Stdout = os.Stdout
+}
+
+// Implementation of podman-logout.
+func logout(cmd *cobra.Command, args []string) error {
+ sysCtx := types.SystemContext{AuthFilePath: logoutOptions.AuthFile}
+
+ registry := ""
+ if len(args) > 0 {
+ if logoutOptions.All {
+ return errors.New("--all takes no arguments")
+ }
+ registry = args[0]
+ }
+
+ return auth.Logout(&sysCtx, &logoutOptions, registry)
+}
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 000000000..769e5a9fa
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/auth/auth.go
@@ -0,0 +1,182 @@
+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/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
+}
+
+// Login login to the server with creds from Stdin or CLI
+func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginOptions, registry string) error {
+ server := getRegistryName(registry)
+ 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 removes the authentication of server from authfile
+// removes all authtication if specifies all in the options
+func Logout(systemContext *types.SystemContext, opts *LogoutOptions, server string) error {
+ if server != "" {
+ server = getRegistryName(server)
+ }
+ if err := CheckAuthFile(opts.AuthFile); err != nil {
+ return err
+ }
+
+ 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)
+ }
+}
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 000000000..dffd06718
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/auth/cli.go
@@ -0,0 +1,47 @@
+package auth
+
+import (
+ "io"
+
+ "github.com/spf13/pflag"
+)
+
+// LoginOptions represents common flags in login
+// caller should define bool or optionalBool fields for flags --get-login and --tls-verify
+type LoginOptions struct {
+ AuthFile string
+ CertDir string
+ GetLoginSet bool
+ Password string
+ Username string
+ StdinPassword bool
+ Stdin io.Reader
+ Stdout io.Writer
+}
+
+// LogoutOptions represents the results for flags in logout
+type LogoutOptions struct {
+ AuthFile string
+ All bool
+ Stdin io.Reader
+ Stdout io.Writer
+}
+
+// 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")
+ 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 3b45161da..fc2179675 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -84,6 +84,7 @@ github.com/containers/buildah/pkg/umask
github.com/containers/buildah/util
# github.com/containers/common v0.9.1
github.com/containers/common/pkg/apparmor
+github.com/containers/common/pkg/auth
github.com/containers/common/pkg/capabilities
github.com/containers/common/pkg/cgroupv2
github.com/containers/common/pkg/config