2019-04-26 17:49:28 +00:00
|
|
|
package authmethodcreate
|
|
|
|
|
|
|
|
import (
|
2020-05-04 21:21:28 +00:00
|
|
|
"encoding/json"
|
2019-04-26 17:49:28 +00:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2020-02-15 15:06:05 +00:00
|
|
|
"strings"
|
2020-05-04 22:02:57 +00:00
|
|
|
"time"
|
2019-04-26 17:49:28 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/consul/api"
|
2020-02-15 15:06:05 +00:00
|
|
|
"github.com/hashicorp/consul/command/acl/authmethod"
|
2019-04-26 17:49:28 +00:00
|
|
|
"github.com/hashicorp/consul/command/flags"
|
|
|
|
"github.com/hashicorp/consul/command/helpers"
|
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
)
|
|
|
|
|
|
|
|
func New(ui cli.Ui) *cmd {
|
|
|
|
c := &cmd{UI: ui}
|
|
|
|
c.init()
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
type cmd struct {
|
|
|
|
UI cli.Ui
|
|
|
|
flags *flag.FlagSet
|
|
|
|
http *flags.HTTPFlags
|
|
|
|
help string
|
|
|
|
|
|
|
|
authMethodType string
|
|
|
|
name string
|
2020-05-04 20:18:25 +00:00
|
|
|
displayName string
|
2019-04-26 17:49:28 +00:00
|
|
|
description string
|
2020-05-04 22:02:57 +00:00
|
|
|
maxTokenTTL time.Duration
|
2020-06-01 16:44:47 +00:00
|
|
|
tokenLocality string
|
2020-05-04 22:02:57 +00:00
|
|
|
config string
|
2019-04-26 17:49:28 +00:00
|
|
|
|
|
|
|
k8sHost string
|
|
|
|
k8sCACert string
|
|
|
|
k8sServiceAccountJWT string
|
|
|
|
|
|
|
|
showMeta bool
|
2020-02-15 15:06:05 +00:00
|
|
|
format string
|
2019-04-26 17:49:28 +00:00
|
|
|
|
|
|
|
testStdin io.Reader
|
2020-05-07 22:08:42 +00:00
|
|
|
|
|
|
|
enterpriseCmd
|
2019-04-26 17:49:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cmd) init() {
|
|
|
|
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
|
|
|
|
|
|
|
c.flags.BoolVar(
|
|
|
|
&c.showMeta,
|
|
|
|
"meta",
|
|
|
|
false,
|
|
|
|
"Indicates that auth method metadata such "+
|
2019-05-01 21:11:23 +00:00
|
|
|
"as the raft indices should be shown for each entry.",
|
2019-04-26 17:49:28 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
c.flags.StringVar(
|
|
|
|
&c.authMethodType,
|
|
|
|
"type",
|
|
|
|
"",
|
|
|
|
"The new auth method's type. This flag is required.",
|
|
|
|
)
|
|
|
|
c.flags.StringVar(
|
|
|
|
&c.name,
|
|
|
|
"name",
|
|
|
|
"",
|
|
|
|
"The new auth method's name. This flag is required.",
|
|
|
|
)
|
2020-05-04 20:18:25 +00:00
|
|
|
c.flags.StringVar(
|
|
|
|
&c.displayName,
|
|
|
|
"display-name",
|
|
|
|
"",
|
|
|
|
"An optional name to use instead of the name when displaying this auth method in a UI.",
|
|
|
|
)
|
2019-04-26 17:49:28 +00:00
|
|
|
c.flags.StringVar(
|
|
|
|
&c.description,
|
|
|
|
"description",
|
|
|
|
"",
|
|
|
|
"A description of the auth method.",
|
|
|
|
)
|
2020-05-04 22:02:57 +00:00
|
|
|
c.flags.DurationVar(
|
|
|
|
&c.maxTokenTTL,
|
|
|
|
"max-token-ttl",
|
|
|
|
0,
|
|
|
|
"Duration of time all tokens created by this auth method should be valid for",
|
|
|
|
)
|
2020-06-01 16:44:47 +00:00
|
|
|
c.flags.StringVar(
|
|
|
|
&c.tokenLocality,
|
|
|
|
"token-locality",
|
|
|
|
"",
|
|
|
|
"Defines the kind of token that this auth method should produce. "+
|
|
|
|
"This can be either 'local' or 'global'. If empty the value of 'local' is assumed.",
|
|
|
|
)
|
2019-04-26 17:49:28 +00:00
|
|
|
|
|
|
|
c.flags.StringVar(
|
|
|
|
&c.k8sHost,
|
|
|
|
"kubernetes-host",
|
|
|
|
"",
|
|
|
|
"Address of the Kubernetes API server. This flag is required for type=kubernetes.",
|
|
|
|
)
|
|
|
|
c.flags.StringVar(
|
|
|
|
&c.k8sCACert,
|
|
|
|
"kubernetes-ca-cert",
|
|
|
|
"",
|
|
|
|
"PEM encoded CA cert for use by the TLS client used to talk with the "+
|
|
|
|
"Kubernetes API. May be prefixed with '@' to indicate that the "+
|
|
|
|
"value is a file path to load the cert from. "+
|
|
|
|
"This flag is required for type=kubernetes.",
|
|
|
|
)
|
|
|
|
c.flags.StringVar(
|
|
|
|
&c.k8sServiceAccountJWT,
|
|
|
|
"kubernetes-service-account-jwt",
|
|
|
|
"",
|
2019-05-01 21:11:23 +00:00
|
|
|
"A Kubernetes service account JWT used to access the TokenReview API to "+
|
2019-04-26 17:49:28 +00:00
|
|
|
"validate other JWTs during login. "+
|
|
|
|
"This flag is required for type=kubernetes.",
|
|
|
|
)
|
2020-02-15 15:06:05 +00:00
|
|
|
c.flags.StringVar(
|
|
|
|
&c.format,
|
|
|
|
"format",
|
|
|
|
authmethod.PrettyFormat,
|
|
|
|
fmt.Sprintf("Output format {%s}", strings.Join(authmethod.GetSupportedFormats(), "|")),
|
|
|
|
)
|
2020-05-04 21:21:28 +00:00
|
|
|
c.flags.StringVar(
|
|
|
|
&c.config,
|
|
|
|
"config",
|
|
|
|
"",
|
|
|
|
"The configuration for the auth method. Must be JSON. May be prefixed with '@' "+
|
|
|
|
"to indicate that the value is a file path to load the config from. '-' may also be "+
|
|
|
|
"given to indicate that the config is available on stdin",
|
|
|
|
)
|
2019-04-26 17:49:28 +00:00
|
|
|
|
2020-05-07 22:08:42 +00:00
|
|
|
c.initEnterpriseFlags()
|
|
|
|
|
2019-04-26 17:49:28 +00:00
|
|
|
c.http = &flags.HTTPFlags{}
|
|
|
|
flags.Merge(c.flags, c.http.ClientFlags())
|
|
|
|
flags.Merge(c.flags, c.http.ServerFlags())
|
2021-07-21 19:45:24 +00:00
|
|
|
flags.Merge(c.flags, c.http.MultiTenancyFlags())
|
2019-04-26 17:49:28 +00:00
|
|
|
c.help = flags.Usage(help, c.flags)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cmd) Run(args []string) int {
|
|
|
|
if err := c.flags.Parse(args); err != nil {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.authMethodType == "" {
|
|
|
|
c.UI.Error(fmt.Sprintf("Missing required '-type' flag"))
|
|
|
|
c.UI.Error(c.Help())
|
|
|
|
return 1
|
|
|
|
} else if c.name == "" {
|
|
|
|
c.UI.Error(fmt.Sprintf("Missing required '-name' flag"))
|
|
|
|
c.UI.Error(c.Help())
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
client, err := c.http.APIClient()
|
|
|
|
if err != nil {
|
|
|
|
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
newAuthMethod := &api.ACLAuthMethod{
|
2020-06-01 16:44:47 +00:00
|
|
|
Type: c.authMethodType,
|
|
|
|
Name: c.name,
|
|
|
|
DisplayName: c.displayName,
|
|
|
|
Description: c.description,
|
|
|
|
TokenLocality: c.tokenLocality,
|
2019-04-26 17:49:28 +00:00
|
|
|
}
|
2020-05-04 22:02:57 +00:00
|
|
|
if c.maxTokenTTL > 0 {
|
|
|
|
newAuthMethod.MaxTokenTTL = c.maxTokenTTL
|
|
|
|
}
|
2019-04-26 17:49:28 +00:00
|
|
|
|
2020-05-07 22:08:42 +00:00
|
|
|
if err := c.enterprisePopulateAuthMethod(newAuthMethod); err != nil {
|
|
|
|
c.UI.Error(err.Error())
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2020-05-04 21:21:28 +00:00
|
|
|
if c.config != "" {
|
|
|
|
if c.k8sHost != "" || c.k8sCACert != "" || c.k8sServiceAccountJWT != "" {
|
|
|
|
c.UI.Error(fmt.Sprintf("Cannot use command line arguments with '-config' flags"))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
data, err := helpers.LoadDataSource(c.config, c.testStdin)
|
|
|
|
if err != nil {
|
|
|
|
c.UI.Error(fmt.Sprintf("Error loading configuration file: %v", err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
err = json.Unmarshal([]byte(data), &newAuthMethod.Config)
|
|
|
|
if err != nil {
|
|
|
|
c.UI.Error(fmt.Sprintf("Error parsing JSON configuration file: %v", err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-04-26 17:49:28 +00:00
|
|
|
if c.authMethodType == "kubernetes" {
|
|
|
|
if c.k8sHost == "" {
|
|
|
|
c.UI.Error(fmt.Sprintf("Missing required '-kubernetes-host' flag"))
|
|
|
|
return 1
|
|
|
|
} else if c.k8sCACert == "" {
|
|
|
|
c.UI.Error(fmt.Sprintf("Missing required '-kubernetes-ca-cert' flag"))
|
|
|
|
return 1
|
|
|
|
} else if c.k8sServiceAccountJWT == "" {
|
|
|
|
c.UI.Error(fmt.Sprintf("Missing required '-kubernetes-service-account-jwt' flag"))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
c.k8sCACert, err = helpers.LoadDataSource(c.k8sCACert, c.testStdin)
|
|
|
|
if err != nil {
|
|
|
|
c.UI.Error(fmt.Sprintf("Invalid '-kubernetes-ca-cert' value: %v", err))
|
|
|
|
return 1
|
|
|
|
} else if c.k8sCACert == "" {
|
|
|
|
c.UI.Error(fmt.Sprintf("Kubernetes CA Cert is empty"))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
newAuthMethod.Config = map[string]interface{}{
|
|
|
|
"Host": c.k8sHost,
|
|
|
|
"CACert": c.k8sCACert,
|
|
|
|
"ServiceAccountJWT": c.k8sServiceAccountJWT,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
method, _, err := client.ACL().AuthMethodCreate(newAuthMethod, nil)
|
|
|
|
if err != nil {
|
|
|
|
c.UI.Error(fmt.Sprintf("Failed to create new auth method: %v", err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2020-02-15 15:06:05 +00:00
|
|
|
formatter, err := authmethod.NewFormatter(c.format, c.showMeta)
|
|
|
|
if err != nil {
|
|
|
|
c.UI.Error(err.Error())
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
out, err := formatter.FormatAuthMethod(method)
|
|
|
|
if err != nil {
|
|
|
|
c.UI.Error(err.Error())
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
if out != "" {
|
|
|
|
c.UI.Info(out)
|
|
|
|
}
|
|
|
|
|
2019-04-26 17:49:28 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cmd) Synopsis() string {
|
|
|
|
return synopsis
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cmd) Help() string {
|
|
|
|
return flags.Usage(c.help, nil)
|
|
|
|
}
|
|
|
|
|
2019-05-01 21:11:23 +00:00
|
|
|
const synopsis = "Create an ACL auth method"
|
2019-04-26 17:49:28 +00:00
|
|
|
|
|
|
|
const help = `
|
|
|
|
Usage: consul acl auth-method create -name NAME -type TYPE [options]
|
|
|
|
|
|
|
|
Create a new auth method:
|
|
|
|
|
|
|
|
$ consul acl auth-method create -type "kubernetes" \
|
|
|
|
-name "my-k8s" \
|
|
|
|
-description "This is an example kube method" \
|
|
|
|
-kubernetes-host "https://apiserver.example.com:8443" \
|
2020-10-06 22:44:24 +00:00
|
|
|
-kubernetes-ca-cert @/path/to/kube.ca.crt \
|
2019-04-26 17:49:28 +00:00
|
|
|
-kubernetes-service-account-jwt "JWT_CONTENTS"
|
|
|
|
`
|