open-vault/command/auth.go

330 lines
8.6 KiB
Go
Raw Normal View History

2015-03-04 07:34:32 +00:00
package command
import (
2015-05-21 02:43:47 +00:00
"bufio"
2015-03-30 17:55:41 +00:00
"fmt"
2015-05-21 02:43:47 +00:00
"io"
2015-03-30 17:55:41 +00:00
"os"
2015-04-02 00:01:10 +00:00
"sort"
2015-03-04 07:34:32 +00:00
"strings"
2015-03-30 17:55:41 +00:00
2015-04-06 16:53:43 +00:00
"github.com/hashicorp/vault/api"
2015-04-08 06:29:49 +00:00
"github.com/hashicorp/vault/helper/kv-builder"
2015-03-30 17:55:41 +00:00
"github.com/hashicorp/vault/helper/password"
2015-04-08 06:29:49 +00:00
"github.com/mitchellh/mapstructure"
2015-04-02 00:01:10 +00:00
"github.com/ryanuber/columnize"
2015-03-04 07:34:32 +00:00
)
// AuthHandler is the interface that any auth handlers must implement
// to enable auth via the CLI.
type AuthHandler interface {
2015-04-06 16:53:43 +00:00
Auth(*api.Client, map[string]string) (string, error)
Help() string
}
2015-03-04 07:34:32 +00:00
// AuthCommand is a Command that handles authentication.
type AuthCommand struct {
Meta
Handlers map[string]AuthHandler
// The fields below can be overwritten for tests
testStdin io.Reader
2015-03-04 07:34:32 +00:00
}
func (c *AuthCommand) Run(args []string) int {
var method string
2015-04-06 16:38:16 +00:00
var methods, methodHelp bool
2015-03-04 07:34:32 +00:00
flags := c.Meta.FlagSet("auth", FlagSetDefault)
2015-04-02 00:01:10 +00:00
flags.BoolVar(&methods, "methods", false, "")
2015-04-06 16:38:16 +00:00
flags.BoolVar(&methodHelp, "method-help", false, "")
2015-03-04 07:34:32 +00:00
flags.StringVar(&method, "method", "", "method")
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
return 1
}
2015-04-02 00:01:10 +00:00
if methods {
return c.listMethods()
}
2015-03-29 23:42:45 +00:00
args = flags.Args()
2015-03-30 17:55:41 +00:00
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
}
// token is where the final token will go
handler := c.Handlers[method]
2015-05-21 02:43:47 +00:00
// Read token from stdin if first arg is exactly "-"
var stdin io.Reader = os.Stdin
if c.testStdin != nil {
stdin = c.testStdin
}
2015-05-21 02:43:47 +00:00
if len(args) > 0 && args[0] == "-" {
stdinR := bufio.NewReader(stdin)
args[0], err = stdinR.ReadString('\n')
2015-05-21 02:43:47 +00:00
if err != nil && err != io.EOF {
c.Ui.Error(fmt.Sprintf("Error reading from stdin: %s", err))
return 1
}
args[0] = strings.TrimSpace(args[0])
}
2015-03-30 17:55:41 +00:00
if method == "" {
token := ""
2015-03-30 17:55:41 +00:00
if len(args) > 0 {
token = args[0]
}
handler = &tokenAuthHandler{Token: token}
2015-04-08 06:29:49 +00:00
args = nil
}
if handler == nil {
methods := make([]string, 0, len(c.Handlers))
for k, _ := range c.Handlers {
methods = append(methods, k)
2015-03-30 17:55:41 +00:00
}
sort.Strings(methods)
c.Ui.Error(fmt.Sprintf(
"Unknown authentication method: %s\n\n"+
"Please use a supported authentication method. The list of supported\n"+
"authentication methods is shown below. Note that this list may not\n"+
"be exhaustive: Vault may support other auth methods. For auth methods\n"+
"unsupported by the CLI, please use the HTTP API.\n\n"+
"%s",
method,
strings.Join(methods, ", ")))
return 1
}
2015-04-06 16:38:16 +00:00
if methodHelp {
c.Ui.Output(handler.Help())
return 0
}
2015-04-08 06:29:49 +00:00
var vars map[string]string
if len(args) > 0 {
builder := kvbuilder.Builder{Stdin: os.Stdin}
if err := builder.Add(args...); err != nil {
c.Ui.Error(err.Error())
return 1
}
if err := mapstructure.Decode(builder.Map(), &vars); err != nil {
c.Ui.Error(fmt.Sprintf("Error parsing options: %s", err))
return 1
}
}
2015-04-06 16:53:43 +00:00
// Build the client so we can auth
client, err := c.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing client to auth: %s", err))
return 1
}
// Authenticate
token, err := handler.Auth(client, vars)
if err != nil {
c.Ui.Error(err.Error())
return 1
2015-03-30 17:55:41 +00:00
}
// Store the token!
if err := tokenHelper.Store(token); err != nil {
c.Ui.Error(fmt.Sprintf(
"Error storing token: %s\n\n"+
"Authentication was not successful and did not persist.\n"+
"Please reauthenticate, or fix the issue above if possible.",
err))
return 1
}
2015-04-06 16:53:43 +00:00
// Build the client again so it can read the token we just wrote
client, err = c.Client()
2015-03-31 22:15:08 +00:00
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing client to verify the token: %s", err))
return 1
}
// Verify the token
secret, err := client.Logical().Read("auth/token/lookup-self")
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error validating token: %s", err))
return 1
}
if secret == nil {
c.Ui.Error(fmt.Sprintf("Error: Invalid token"))
return 1
}
2015-03-31 22:15:08 +00:00
// Get the policies we have
policiesRaw, ok := secret.Data["policies"]
if !ok {
policiesRaw = []string{"unknown"}
}
var policies []string
for _, v := range policiesRaw.([]interface{}) {
policies = append(policies, v.(string))
}
c.Ui.Output(fmt.Sprintf(
2015-03-31 22:15:08 +00:00
"Successfully authenticated! The policies that are associated\n"+
"with this token are listed below:\n\n%s",
strings.Join(policies, ", "),
))
2015-03-04 07:34:32 +00:00
return 0
}
2015-04-02 00:01:10 +00:00
func (c *AuthCommand) listMethods() int {
client, err := c.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing client: %s", err))
return 1
}
auth, err := client.Sys().ListAuth()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error reading auth table: %s", err))
return 1
}
paths := make([]string, 0, len(auth))
for path, _ := range auth {
paths = append(paths, path)
}
sort.Strings(paths)
columns := []string{"Path | Type | Description"}
for _, k := range paths {
a := auth[k]
columns = append(columns, fmt.Sprintf(
"%s | %s | %s", k, a.Type, a.Description))
}
c.Ui.Output(columnize.SimpleFormat(columns))
return 0
}
2015-03-04 07:34:32 +00:00
func (c *AuthCommand) Synopsis() string {
return "Prints information about how to authenticate with Vault"
}
func (c *AuthCommand) Help() string {
helpText := `
2015-04-08 06:29:49 +00:00
Usage: vault auth [options] [token or config...]
2015-03-29 23:42:45 +00:00
Authenticate with Vault with the given token or via any supported
authentication backend.
2015-03-04 07:34:32 +00:00
2015-03-29 23:42:45 +00:00
If no -method is specified, then the token is expected. If it is not
given on the command-line, it will be asked via user input. If the
token is "-", it will be read from stdin.
2015-03-04 07:34:32 +00:00
2015-03-29 23:42:45 +00:00
By specifying -method, alternate authentication methods can be used
2015-04-08 06:29:49 +00:00
such as OAuth or TLS certificates. For these, additional values for
configuration can be specified with "key=value" pairs just like
"vault write". Specify the "-method-help" flag to get help for a specific
method.
If you've mounted a credential backend to a different path, such
as mounting "github" to "github-private", the "method" flag should
still be "github." Most credential providers support the "mount" option
to specify the mount point. See the "-method-help" for more info.
2015-03-04 07:34:32 +00:00
General Options:
2015-04-28 16:15:21 +00:00
-address=addr The address of the Vault server.
-ca-cert=path Path to a PEM encoded CA cert file to use to
verify the Vault server SSL certificate.
-ca-path=path Path to a directory of PEM encoded CA cert files
to verify the Vault server SSL certificate. If both
-ca-cert and -ca-path are specified, -ca-path is used.
2015-05-11 18:01:20 +00:00
-tls-skip-verify Do not verify TLS certificate. This is highly
not recommended.
Auth Options:
2015-03-04 07:34:32 +00:00
-method=name Outputs help for the authentication method with the given
name for the remote server. If this authentication method
is not available, exit with code 1.
2015-04-02 00:01:10 +00:00
2015-04-06 16:38:16 +00:00
-method-help If set, the help for the selected method will be shown.
-methods List the available auth methods.
2015-03-04 07:34:32 +00:00
`
return strings.TrimSpace(helpText)
}
// tokenAuthHandler handles retrieving the token from the command-line.
type tokenAuthHandler struct {
Token string
}
2015-04-06 16:53:43 +00:00
func (h *tokenAuthHandler) Auth(*api.Client, map[string]string) (string, error) {
token := h.Token
if token == "" {
var err error
// No arguments given, read the token from user input
fmt.Printf("Token (will be hidden): ")
token, err = password.Read(os.Stdin)
fmt.Printf("\n")
if err != nil {
return "", fmt.Errorf(
"Error attempting to ask for token. The raw error message\n"+
"is shown below, but the most common reason for this error is\n"+
"that you attempted to pipe a value into auth. If you want to\n"+
"pipe the token, please pass '-' as the token argument.\n\n"+
"Raw error: %s", err)
}
}
if token == "" {
return "", fmt.Errorf(
"A token must be passed to auth. Please view the help\n" +
"for more information.")
}
return token, nil
}
func (h *tokenAuthHandler) Help() string {
2015-04-06 16:38:16 +00:00
help := `
No method selected with the "-method" flag, so the "auth" command assumes
you'll be using raw token authentication. For this, specify the token to
authenticate as as the parameter to "vault auth". Example:
vault auth 123456
The token used to authenticate must come from some other source. A root
token is created when Vault is first initialized. After that, subsequent
tokens are created via the API or command line interface (with the
"token"-prefixed commands).
`
return strings.TrimSpace(help)
}