mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 18:35:44 +03:00
acl: JWT auth CLI (#16532)
This commit is contained in:
@@ -84,14 +84,21 @@ func formatAuthMethod(authMethod *api.ACLAuthMethod) string {
|
||||
|
||||
func formatAuthMethodConfig(config *api.ACLAuthMethodConfig) string {
|
||||
out := []string{
|
||||
fmt.Sprintf("JWT Validation Public Keys|%s", strings.Join(config.JWTValidationPubKeys, ",")),
|
||||
fmt.Sprintf("JWKS URL|%s", config.JWKSURL),
|
||||
fmt.Sprintf("OIDC Discovery URL|%s", config.OIDCDiscoveryURL),
|
||||
fmt.Sprintf("OIDC Client ID|%s", config.OIDCClientID),
|
||||
fmt.Sprintf("OIDC Client Secret|%s", config.OIDCClientSecret),
|
||||
fmt.Sprintf("OIDC Scopes|%s", strings.Join(config.OIDCScopes, ",")),
|
||||
fmt.Sprintf("Bound audiences|%s", strings.Join(config.BoundAudiences, ",")),
|
||||
fmt.Sprintf("Bound issuer|%s", strings.Join(config.BoundIssuer, ",")),
|
||||
fmt.Sprintf("Allowed redirects URIs|%s", strings.Join(config.AllowedRedirectURIs, ",")),
|
||||
fmt.Sprintf("Discovery CA pem|%s", strings.Join(config.DiscoveryCaPem, ",")),
|
||||
fmt.Sprintf("JWKS CA cert|%s", config.JWKSCACert),
|
||||
fmt.Sprintf("Signing algorithms|%s", strings.Join(config.SigningAlgs, ",")),
|
||||
fmt.Sprintf("Expiration Leeway|%s", config.ExpirationLeeway.String()),
|
||||
fmt.Sprintf("NotBefore Leeway|%s", config.NotBeforeLeeway.String()),
|
||||
fmt.Sprintf("ClockSkew Leeway|%s", config.ClockSkewLeeway.String()),
|
||||
fmt.Sprintf("Claim mappings|%s", strings.Join(formatMap(config.ClaimMappings), "; ")),
|
||||
fmt.Sprintf("List claim mappings|%s", strings.Join(formatMap(config.ListClaimMappings), "; ")),
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@ ACL Auth Method Create Options:
|
||||
between 1-128 characters and is a required parameter.
|
||||
|
||||
-type
|
||||
Sets the type of the auth method. Currently the only supported type is
|
||||
'OIDC'.
|
||||
Sets the type of the auth method. Supported types are 'OIDC' and 'JWT'.
|
||||
|
||||
-max-token-ttl
|
||||
Sets the duration of time all tokens created by this auth method should be
|
||||
@@ -83,7 +82,7 @@ func (a *ACLAuthMethodCreateCommand) AutocompleteFlags() complete.Flags {
|
||||
return mergeAutocompleteFlags(a.Meta.AutocompleteFlags(FlagSetClient),
|
||||
complete.Flags{
|
||||
"-name": complete.PredictAnything,
|
||||
"-type": complete.PredictSet("OIDC"),
|
||||
"-type": complete.PredictSet("OIDC", "JWT"),
|
||||
"-max-token-ttl": complete.PredictAnything,
|
||||
"-token-locality": complete.PredictSet("local", "global"),
|
||||
"-default": complete.PredictSet("true", "false"),
|
||||
@@ -140,8 +139,8 @@ func (a *ACLAuthMethodCreateCommand) Run(args []string) int {
|
||||
a.Ui.Error("Max token TTL must be set to a value between min and max TTL configured for the server.")
|
||||
return 1
|
||||
}
|
||||
if strings.ToUpper(a.methodType) != "OIDC" {
|
||||
a.Ui.Error("ACL auth method type must be set to 'OIDC'")
|
||||
if !slices.Contains([]string{"OIDC", "JWT"}, strings.ToUpper(a.methodType)) {
|
||||
a.Ui.Error("ACL auth method type must be set to 'OIDC' or 'JWT'")
|
||||
return 1
|
||||
}
|
||||
if len(a.config) == 0 {
|
||||
|
||||
@@ -64,7 +64,7 @@ func TestACLAuthMethodCreateCommand_Run(t *testing.T) {
|
||||
args := []string{
|
||||
"-address=" + url, "-token=" + rootACLToken.SecretID, "-name=acl-auth-method-cli-test",
|
||||
"-type=OIDC", "-token-locality=global", "-default=true", "-max-token-ttl=3600s",
|
||||
"-config={\"OIDCDiscoveryURL\":\"http://example.com\"}",
|
||||
"-config={\"OIDCDiscoveryURL\":\"http://example.com\", \"ExpirationLeeway\": \"1h\"}",
|
||||
}
|
||||
must.Eq(t, 0, cmd.Run(args))
|
||||
s := ui.OutputWriter.String()
|
||||
|
||||
@@ -46,8 +46,7 @@ General Options:
|
||||
ACL Auth Method Update Options:
|
||||
|
||||
-type
|
||||
Updates the type of the auth method. Currently the only supported type is
|
||||
'OIDC'.
|
||||
Updates the type of the auth method. Supported types are 'OIDC' and 'JWT'.
|
||||
|
||||
-max-token-ttl
|
||||
Updates the duration of time all tokens created by this auth method should be
|
||||
@@ -79,7 +78,7 @@ ACL Auth Method Update Options:
|
||||
func (a *ACLAuthMethodUpdateCommand) AutocompleteFlags() complete.Flags {
|
||||
return mergeAutocompleteFlags(a.Meta.AutocompleteFlags(FlagSetClient),
|
||||
complete.Flags{
|
||||
"-type": complete.PredictSet("OIDC"),
|
||||
"-type": complete.PredictSet("OIDC", "JWT"),
|
||||
"-max-token-ttl": complete.PredictAnything,
|
||||
"-token-locality": complete.PredictSet("local", "global"),
|
||||
"-default": complete.PredictSet("true", "false"),
|
||||
@@ -161,8 +160,8 @@ func (a *ACLAuthMethodUpdateCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
if slices.Contains(setFlags, "type") {
|
||||
if strings.ToLower(a.methodType) != "oidc" {
|
||||
a.Ui.Error("ACL auth method type must be set to 'OIDC'")
|
||||
if !slices.Contains([]string{"OIDC", "JWT"}, strings.ToUpper(a.methodType)) {
|
||||
a.Ui.Error("ACL auth method type must be set to 'OIDC' or 'JWT'")
|
||||
return 1
|
||||
}
|
||||
updatedMethod.Type = a.methodType
|
||||
|
||||
@@ -25,6 +25,7 @@ type LoginCommand struct {
|
||||
authMethodType string // deprecated in 1.5.2, left for backwards compat
|
||||
authMethodName string
|
||||
callbackAddr string
|
||||
loginToken string
|
||||
|
||||
template string
|
||||
json bool
|
||||
@@ -52,6 +53,10 @@ Login Options:
|
||||
The address to use for the local OIDC callback server. This should be given
|
||||
in the form of <IP>:<PORT> and defaults to "localhost:4649".
|
||||
|
||||
-login-token
|
||||
Login token used for authentication that will be exchanged for a Nomad ACL
|
||||
Token. It is only required if using auth method type other than OIDC.
|
||||
|
||||
-json
|
||||
Output the ACL token in JSON format.
|
||||
|
||||
@@ -71,6 +76,7 @@ func (l *LoginCommand) AutocompleteFlags() complete.Flags {
|
||||
complete.Flags{
|
||||
"-method": complete.PredictAnything,
|
||||
"-oidc-callback-addr": complete.PredictAnything,
|
||||
"-login-token": complete.PredictAnything,
|
||||
"-json": complete.PredictNothing,
|
||||
"-t": complete.PredictAnything,
|
||||
})
|
||||
@@ -86,6 +92,7 @@ func (l *LoginCommand) Run(args []string) int {
|
||||
flags.Usage = func() { l.Ui.Output(l.Help()) }
|
||||
flags.StringVar(&l.authMethodName, "method", "", "")
|
||||
flags.StringVar(&l.authMethodType, "type", "", "")
|
||||
flags.StringVar(&l.loginToken, "login-token", "", "")
|
||||
flags.StringVar(&l.callbackAddr, "oidc-callback-addr", "localhost:4649", "")
|
||||
flags.BoolVar(&l.json, "json", false, "")
|
||||
flags.StringVar(&l.template, "t", "", "")
|
||||
@@ -154,6 +161,12 @@ func (l *LoginCommand) Run(args []string) int {
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we got the login token if we're not using OIDC
|
||||
if methodType != api.ACLAuthMethodTypeOIDC && l.loginToken == "" {
|
||||
l.Ui.Error("You need to provide a login token.")
|
||||
return 1
|
||||
}
|
||||
|
||||
// Each login type should implement a function which matches this signature
|
||||
// for the specific login implementation. This allows the command to have
|
||||
// reusable and generic handling of errors and outputs.
|
||||
@@ -162,6 +175,8 @@ func (l *LoginCommand) Run(args []string) int {
|
||||
switch methodType {
|
||||
case api.ACLAuthMethodTypeOIDC:
|
||||
authFn = l.loginOIDC
|
||||
case api.ACLAuthMethodTypeJWT:
|
||||
authFn = l.loginJWT
|
||||
default:
|
||||
l.Ui.Error(fmt.Sprintf("Unsupported authentication type %q", methodType))
|
||||
return 1
|
||||
@@ -244,6 +259,15 @@ func (l *LoginCommand) loginOIDC(ctx context.Context, client *api.Client) (*api.
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (l *LoginCommand) loginJWT(ctx context.Context, client *api.Client) (*api.ACLToken, error) {
|
||||
authArgs := api.ACLLoginRequest{
|
||||
AuthMethodName: l.authMethodName,
|
||||
LoginToken: l.loginToken,
|
||||
}
|
||||
token, _, err := client.ACLAuth().Login(&authArgs, nil)
|
||||
return token, err
|
||||
}
|
||||
|
||||
const (
|
||||
// oidcErrorVisitURLMsg is a message to show users when opening the OIDC
|
||||
// provider URL automatically fails. This type of message is otherwise not
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/nomad/ci"
|
||||
"github.com/hashicorp/nomad/command/agent"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/testutil"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/shoenig/test/must"
|
||||
@@ -51,6 +52,25 @@ func TestLoginCommand_Run(t *testing.T) {
|
||||
ui.OutputWriter.Reset()
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
// Store a default auth method
|
||||
state := srv.Agent.Server().State()
|
||||
method := &structs.ACLAuthMethod{
|
||||
Name: "test-auth-method",
|
||||
Default: true,
|
||||
Type: "JWT",
|
||||
Config: &structs.ACLAuthMethodConfig{
|
||||
OIDCDiscoveryURL: "http://example.com",
|
||||
},
|
||||
}
|
||||
method.SetHash()
|
||||
must.NoError(t, state.UpsertACLAuthMethods(1000, []*structs.ACLAuthMethod{method}))
|
||||
|
||||
// Try logging in with non-OIDC method and no token (expected error)
|
||||
must.Eq(t, 1, cmd.Run([]string{"-address=" + agentURL}))
|
||||
must.StrContains(t, ui.ErrorWriter.String(), "You need to provide a login token.")
|
||||
ui.OutputWriter.Reset()
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
// TODO(jrasell) find a way to test the full login flow from the CLI
|
||||
// perspective.
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@ via flags detailed below.
|
||||
- `-description`: A free form text description of the auth-method that must not exceed
|
||||
256 characters.
|
||||
|
||||
- `-type`: Sets the type of the auth method. Currently the only supported type
|
||||
is `OIDC`.
|
||||
- `-type`: Sets the type of the auth method. Supported types are `OIDC` and `JWT`.
|
||||
|
||||
- `-max-token-ttl`: Sets the duration of time all tokens created by this auth
|
||||
method should be valid for.
|
||||
|
||||
@@ -37,8 +37,7 @@ The `acl auth-method update` command requires an existing method's name.
|
||||
to the command. Instead, overwrite all fields with the exception of the role
|
||||
ID which is immutable.
|
||||
|
||||
- `-type`: Updates the type of the auth method. Currently the only supported
|
||||
type is `OIDC`.
|
||||
- `-type`: Updates the type of the auth method. Supported types are `OIDC` and `JWT`.
|
||||
|
||||
- `-max-token-ttl`: Updates the duration of time all tokens created by this auth
|
||||
method should be valid for.
|
||||
|
||||
Reference in New Issue
Block a user