mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			453 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			453 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package command
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/hashicorp/vault/api"
 | |
| 	"github.com/posener/complete"
 | |
| )
 | |
| 
 | |
| // AuthHandler is the interface that any auth handlers must implement
 | |
| // to enable auth via the CLI.
 | |
| type AuthHandler interface {
 | |
| 	Auth(*api.Client, map[string]string) (*api.Secret, error)
 | |
| 	Help() string
 | |
| }
 | |
| 
 | |
| // AuthCommand is a Command that handles authentication.
 | |
| type AuthCommand struct {
 | |
| 	*BaseCommand
 | |
| 
 | |
| 	Handlers map[string]AuthHandler
 | |
| 
 | |
| 	flagMethod    string
 | |
| 	flagPath      string
 | |
| 	flagNoVerify  bool
 | |
| 	flagNoStore   bool
 | |
| 	flagOnlyToken bool
 | |
| 
 | |
| 	// Deprecations
 | |
| 	// TODO: remove in 0.9.0
 | |
| 	flagTokenOnly  bool
 | |
| 	flagMethods    bool
 | |
| 	flagMethodHelp bool
 | |
| 
 | |
| 	testStdin io.Reader // for tests
 | |
| }
 | |
| 
 | |
| func (c *AuthCommand) Synopsis() string {
 | |
| 	return "Authenticates users or machines"
 | |
| }
 | |
| 
 | |
| func (c *AuthCommand) Help() string {
 | |
| 	helpText := `
 | |
| Usage: vault auth [options] [AUTH K=V...]
 | |
| 
 | |
|   Authenticates users or machines to Vault using the provided arguments. By
 | |
|   default, the authentication method is "token". If not supplied via the CLI,
 | |
|   Vault will prompt for input. If argument is "-", the configuration options
 | |
|   are read from stdin.
 | |
| 
 | |
|   The -method flag allows alternative authentication providers to be used,
 | |
|   such as userpass, github, or cert. For these, additional "key=value" pairs
 | |
|   may be required. For example, to authenticate to the userpass auth backend:
 | |
| 
 | |
|       $ vault auth -method=userpass username=my-username
 | |
| 
 | |
|   Use "vault auth-help TYPE" for more information about the list of
 | |
|   configuration parameters and examples for a particular provider. Use the
 | |
|   "vault auth-list" command to see a list of enabled authentication providers.
 | |
| 
 | |
|   If an authentication provider is mounted at a different path, the -method
 | |
|   flag should by the canonical type, and the -path flag should be set to the
 | |
|   mount path. If a github authentication provider was mounted at "github-ent",
 | |
|   you would authenticate to this backend like this:
 | |
| 
 | |
|       $ vault auth -method=github -path=github-prod
 | |
| 
 | |
|   If the authentication is requested with response wrapping (via -wrap-ttl),
 | |
|   the returned token is automatically unwrapped unless:
 | |
| 
 | |
|     - The -only-token flag is used, in which case this command will output
 | |
|       the wrapping token
 | |
| 
 | |
|     - The -no-store flag is used, in which case this command will output
 | |
|       the details of the wrapping token.
 | |
| 
 | |
|   For a full list of examples, please see the documentation.
 | |
| 
 | |
| ` + c.Flags().Help()
 | |
| 
 | |
| 	return strings.TrimSpace(helpText)
 | |
| }
 | |
| 
 | |
| func (c *AuthCommand) Flags() *FlagSets {
 | |
| 	set := c.flagSet(FlagSetHTTP | FlagSetOutputField | FlagSetOutputFormat)
 | |
| 
 | |
| 	f := set.NewFlagSet("Command Options")
 | |
| 
 | |
| 	f.StringVar(&StringVar{
 | |
| 		Name:       "method",
 | |
| 		Target:     &c.flagMethod,
 | |
| 		Default:    "token",
 | |
| 		Completion: c.PredictVaultAvailableAuths(),
 | |
| 		Usage: "Type of authentication to use such as \"userpass\" or " +
 | |
| 			"\"ldap\". Note this corresponds to the TYPE, not the mount path. Use " +
 | |
| 			"-path to specify the path where the authentication is mounted.",
 | |
| 	})
 | |
| 
 | |
| 	f.StringVar(&StringVar{
 | |
| 		Name:       "path",
 | |
| 		Target:     &c.flagPath,
 | |
| 		Default:    "",
 | |
| 		Completion: c.PredictVaultAuths(),
 | |
| 		Usage: "Mount point where the auth backend is enabled. This defaults to " +
 | |
| 			"the TYPE of backend (e.g. userpass -> userpass/).",
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "no-verify",
 | |
| 		Target:  &c.flagNoVerify,
 | |
| 		Default: false,
 | |
| 		Usage: "Do not verify the token after authentication. By default, Vault " +
 | |
| 			"issue a request to get more metdata about the token. This request " +
 | |
| 			"against the use-limit of the token. Set this to true to disable the " +
 | |
| 			"post-authenication lookup.",
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "no-store",
 | |
| 		Target:  &c.flagNoStore,
 | |
| 		Default: false,
 | |
| 		Usage: "Do not persist the token to the token helper (usually the " +
 | |
| 			"local filesystem) after authentication for use in future requests. " +
 | |
| 			"The token will only be displayed in the command output.",
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "only-token",
 | |
| 		Target:  &c.flagOnlyToken,
 | |
| 		Default: false,
 | |
| 		Usage: "Output only the token with no verification. This flag is a " +
 | |
| 			"shortcut for \"-field=token -no-store -no-verify\". Setting those " +
 | |
| 			"flags to other values will have no affect.",
 | |
| 	})
 | |
| 
 | |
| 	// Deprecations
 | |
| 	// TODO: remove in Vault 0.9.0
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "token-only", // Prefer only-token
 | |
| 		Target:  &c.flagTokenOnly,
 | |
| 		Default: false,
 | |
| 		Hidden:  true,
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "methods", // Prefer auth-list
 | |
| 		Target:  &c.flagMethods,
 | |
| 		Default: false,
 | |
| 		Hidden:  true,
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "method-help", // Prefer auth-help
 | |
| 		Target:  &c.flagMethodHelp,
 | |
| 		Default: false,
 | |
| 		Hidden:  true,
 | |
| 	})
 | |
| 
 | |
| 	return set
 | |
| }
 | |
| 
 | |
| func (c *AuthCommand) AutocompleteArgs() complete.Predictor {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *AuthCommand) AutocompleteFlags() complete.Flags {
 | |
| 	return c.Flags().Completions()
 | |
| }
 | |
| 
 | |
| func (c *AuthCommand) Run(args []string) int {
 | |
| 	f := c.Flags()
 | |
| 
 | |
| 	if err := f.Parse(args); err != nil {
 | |
| 		c.UI.Error(err.Error())
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	args = f.Args()
 | |
| 
 | |
| 	// Deprecations - do this before any argument validations
 | |
| 	// TODO: remove in 0.9.0
 | |
| 	switch {
 | |
| 	case c.flagMethods:
 | |
| 		c.UI.Warn(wrapAtLength(
 | |
| 			"WARNING! The -methods flag is deprecated. Please use " +
 | |
| 				"\"vault auth-list\". This flag will be removed in the next major " +
 | |
| 				"release of Vault."))
 | |
| 		cmd := &AuthListCommand{
 | |
| 			BaseCommand: &BaseCommand{
 | |
| 				UI:     c.UI,
 | |
| 				client: c.client,
 | |
| 			},
 | |
| 		}
 | |
| 		return cmd.Run(nil)
 | |
| 	case c.flagMethodHelp:
 | |
| 		c.UI.Warn(wrapAtLength(
 | |
| 			"WARNING! The -method-help flag is deprecated. Please use " +
 | |
| 				"\"vault auth-help\". This flag will be removed in the next major " +
 | |
| 				"release of Vault."))
 | |
| 		cmd := &AuthHelpCommand{
 | |
| 			BaseCommand: &BaseCommand{
 | |
| 				UI:     c.UI,
 | |
| 				client: c.client,
 | |
| 			},
 | |
| 			Handlers: c.Handlers,
 | |
| 		}
 | |
| 		return cmd.Run([]string{c.flagMethod})
 | |
| 	}
 | |
| 
 | |
| 	// TODO: remove in 0.9.0
 | |
| 	if c.flagTokenOnly {
 | |
| 		c.UI.Warn(wrapAtLength(
 | |
| 			"WARNING! The -token-only flag is deprecated. Plase use -only-token " +
 | |
| 				"instead. This flag will be removed in the next major release of " +
 | |
| 				"Vault."))
 | |
| 		c.flagOnlyToken = c.flagTokenOnly
 | |
| 	}
 | |
| 
 | |
| 	// Set the right flags if the user requested only-token - this overrides
 | |
| 	// any previously configured values, as documented.
 | |
| 	if c.flagOnlyToken {
 | |
| 		c.flagNoStore = true
 | |
| 		c.flagNoVerify = true
 | |
| 		c.flagField = "token"
 | |
| 	}
 | |
| 
 | |
| 	// Get the auth method
 | |
| 	authMethod := sanitizePath(c.flagMethod)
 | |
| 	if authMethod == "" {
 | |
| 		authMethod = "token"
 | |
| 	}
 | |
| 
 | |
| 	// If no path is specified, we default the path to the backend type
 | |
| 	// or use the plugin name if it's a plugin backend
 | |
| 	authPath := c.flagPath
 | |
| 	if authPath == "" {
 | |
| 		authPath = ensureTrailingSlash(authMethod)
 | |
| 	}
 | |
| 
 | |
| 	// Get the handler function
 | |
| 	authHandler, ok := c.Handlers[authMethod]
 | |
| 	if !ok {
 | |
| 		c.UI.Error(wrapAtLength(fmt.Sprintf(
 | |
| 			"Unknown authentication method: %s. Use \"vault auth-list\" to see the "+
 | |
| 				"complete list of authentication providers. Additionally, some "+
 | |
| 				"authentication providers are only available via the HTTP API.",
 | |
| 			authMethod)))
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	// Pull our fake stdin if needed
 | |
| 	stdin := (io.Reader)(os.Stdin)
 | |
| 	if c.testStdin != nil {
 | |
| 		stdin = c.testStdin
 | |
| 	}
 | |
| 
 | |
| 	// If the user provided a token, pass it along to the auth provier.
 | |
| 	if authMethod == "token" && len(args) == 1 {
 | |
| 		args = []string{"token=" + args[0]}
 | |
| 	}
 | |
| 
 | |
| 	config, err := parseArgsDataString(stdin, args)
 | |
| 	if err != nil {
 | |
| 		c.UI.Error(fmt.Sprintf("Error parsing configuration: %s", err))
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	// If the user did not specify a mount path, use the provided mount path.
 | |
| 	if config["mount"] == "" && authPath != "" {
 | |
| 		config["mount"] = authPath
 | |
| 	}
 | |
| 
 | |
| 	// Create the client
 | |
| 	client, err := c.Client()
 | |
| 	if err != nil {
 | |
| 		c.UI.Error(err.Error())
 | |
| 		return 2
 | |
| 	}
 | |
| 
 | |
| 	// Authenticate delegation to the auth handler
 | |
| 	secret, err := authHandler.Auth(client, config)
 | |
| 	if err != nil {
 | |
| 		c.UI.Error(wrapAtLength(fmt.Sprintf(
 | |
| 			"Error authenticating: %s", err)))
 | |
| 		return 2
 | |
| 	}
 | |
| 
 | |
| 	// Unset any previous token wrapping functionality. If the original request
 | |
| 	// was for a wrapped token, we don't want future requests to be wrapped.
 | |
| 	client.SetWrappingLookupFunc(func(string, string) string { return "" })
 | |
| 
 | |
| 	// Recursively extract the token, handling wrapping
 | |
| 	unwrap := !c.flagOnlyToken && !c.flagNoStore
 | |
| 	secret, isWrapped, err := c.extractToken(client, secret, unwrap)
 | |
| 	if err != nil {
 | |
| 		c.UI.Error(fmt.Sprintf("Error extracting token: %s", err))
 | |
| 		return 2
 | |
| 	}
 | |
| 	if secret == nil {
 | |
| 		c.UI.Error("Vault returned an empty secret")
 | |
| 		return 2
 | |
| 	}
 | |
| 
 | |
| 	// Handle special cases if the token was wrapped
 | |
| 	if isWrapped {
 | |
| 		if c.flagOnlyToken {
 | |
| 			return PrintRawField(c.UI, secret, "wrapping_token")
 | |
| 		}
 | |
| 		if c.flagNoStore {
 | |
| 			return OutputSecret(c.UI, c.flagFormat, secret)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// If we got this far, verify we have authentication data before continuing
 | |
| 	if secret.Auth == nil {
 | |
| 		c.UI.Error(wrapAtLength(
 | |
| 			"Vault returned a secret, but the secret has no authentication " +
 | |
| 				"information attached. This should never happen and is likely a " +
 | |
| 				"bug."))
 | |
| 		return 2
 | |
| 	}
 | |
| 
 | |
| 	// Pull the token itself out, since we don't need the rest of the auth
 | |
| 	// information anymore/.
 | |
| 	token := secret.Auth.ClientToken
 | |
| 
 | |
| 	tokenHelper, err := c.TokenHelper()
 | |
| 	if err != nil {
 | |
| 		c.UI.Error(fmt.Sprintf(
 | |
| 			"Error initializing token helper: %s\n\n"+
 | |
| 				"Please verify that the token helper is available and properly\n"+
 | |
| 				"configured for your system. Please refer to the documentation\n"+
 | |
| 				"on token helpers for more information.",
 | |
| 			err))
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	if !c.flagNoVerify {
 | |
| 		// Verify the token and pull it's list of policies
 | |
| 		client.SetToken(token)
 | |
| 		client.SetWrappingLookupFunc(func(string, string) string { return "" })
 | |
| 
 | |
| 		secret, err = client.Auth().Token().LookupSelf()
 | |
| 		if err != nil {
 | |
| 			c.UI.Error(fmt.Sprintf("Error verifying token: %s", err))
 | |
| 			return 2
 | |
| 		}
 | |
| 		if secret == nil {
 | |
| 			c.UI.Error("Empty response from lookup-self")
 | |
| 			return 2
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if !c.flagNoStore {
 | |
| 		// Store the token in the local client
 | |
| 		if err := tokenHelper.Store(token); err != nil {
 | |
| 			c.UI.Error(fmt.Sprintf("Error storing token: %s", err))
 | |
| 			c.UI.Error(wrapAtLength(
 | |
| 				"Authentication was successful, but the token was not persisted. The " +
 | |
| 					"resulting token is shown below for your records."))
 | |
| 			OutputSecret(c.UI, c.flagFormat, secret)
 | |
| 			return 2
 | |
| 		}
 | |
| 
 | |
| 		// Warn if the VAULT_TOKEN environment variable is set, as that will take
 | |
| 		// precedence. Don't output on token-only since we're likely piping output.
 | |
| 		if c.flagField == "" && c.flagFormat == "table" {
 | |
| 			if os.Getenv("VAULT_TOKEN") != "" {
 | |
| 				c.UI.Warn(wrapAtLength("WARNING! The VAULT_TOKEN environment variable " +
 | |
| 					"is set! This takes precedence over the value set by this command. To " +
 | |
| 					"use the value set by this command, unset the VAULT_TOKEN environment " +
 | |
| 					"variable or set it to the token displayed below."))
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// If the user requested a particular field, print that out now since we
 | |
| 	// are likely piping to another process.
 | |
| 	if c.flagField != "" {
 | |
| 		return PrintRawField(c.UI, secret, c.flagField)
 | |
| 	}
 | |
| 
 | |
| 	// Output the secret as json or yaml if requested. We have to maintain
 | |
| 	// backwards compatiability
 | |
| 	if c.flagFormat != "table" {
 | |
| 		return OutputSecret(c.UI, c.flagFormat, secret)
 | |
| 	}
 | |
| 
 | |
| 	output := "Success! You are now authenticated. "
 | |
| 	if c.flagNoVerify {
 | |
| 		output += "The token was not verified for validity. "
 | |
| 	}
 | |
| 	if c.flagNoStore {
 | |
| 		output += "The token was not stored in the token helper. "
 | |
| 	} else {
 | |
| 		output += "The token information displayed below is already stored in " +
 | |
| 			"the token helper. You do NOT need to run \"vault auth\" again."
 | |
| 	}
 | |
| 	c.UI.Output(wrapAtLength(output) + "\n")
 | |
| 
 | |
| 	// TODO make this consistent with other printed token secrets.
 | |
| 	c.UI.Output(fmt.Sprintf("token: %s", secret.TokenID()))
 | |
| 	c.UI.Output(fmt.Sprintf("accessor: %s", secret.TokenAccessor()))
 | |
| 
 | |
| 	if ttl := secret.TokenTTL(); ttl != 0 {
 | |
| 		c.UI.Output(fmt.Sprintf("duration: %s", ttl))
 | |
| 	}
 | |
| 
 | |
| 	c.UI.Output(fmt.Sprintf("renewable: %t", secret.TokenIsRenewable()))
 | |
| 
 | |
| 	if policies := secret.TokenPolicies(); len(policies) > 0 {
 | |
| 		c.UI.Output(fmt.Sprintf("policies: %s", policies))
 | |
| 	}
 | |
| 
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| // extractToken extracts the token from the given secret, automatically
 | |
| // unwrapping responses and handling error conditions if unwrap is true. The
 | |
| // result also returns whether it was a wrapped resonse that was not unwrapped.
 | |
| func (c *AuthCommand) extractToken(client *api.Client, secret *api.Secret, unwrap bool) (*api.Secret, bool, error) {
 | |
| 	switch {
 | |
| 	case secret == nil:
 | |
| 		return nil, false, fmt.Errorf("empty response from auth helper")
 | |
| 
 | |
| 	case secret.Auth != nil:
 | |
| 		return secret, false, nil
 | |
| 
 | |
| 	case secret.WrapInfo != nil:
 | |
| 		if secret.WrapInfo.WrappedAccessor == "" {
 | |
| 			return nil, false, fmt.Errorf("wrapped response does not contain a token")
 | |
| 		}
 | |
| 
 | |
| 		if !unwrap {
 | |
| 			return secret, true, nil
 | |
| 		}
 | |
| 
 | |
| 		client.SetToken(secret.WrapInfo.Token)
 | |
| 		secret, err := client.Logical().Unwrap("")
 | |
| 		if err != nil {
 | |
| 			return nil, false, err
 | |
| 		}
 | |
| 		return c.extractToken(client, secret, unwrap)
 | |
| 
 | |
| 	default:
 | |
| 		return nil, false, fmt.Errorf("no auth or wrapping info in response")
 | |
| 	}
 | |
| }
 | 
