mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
The Nomad clients store their Nomad identity in memory and within their state store. While active, it is not possible to dump the state to view the stored identity token, so having a way to view the current claims while running aids debugging and operations. This change adds a client identity workflow, allowing operators to view the current claims of the nodes identity. It does not return any of the signing key material.
162 lines
3.8 KiB
Go
162 lines
3.8 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package command
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/nomad/api"
|
|
"github.com/posener/complete"
|
|
)
|
|
|
|
type NodeIdentityGetCommand struct {
|
|
Meta
|
|
|
|
// Command flags are stored below for use across the command.
|
|
json bool
|
|
tmpl string
|
|
}
|
|
|
|
func (n *NodeIdentityGetCommand) Help() string {
|
|
helpText := `
|
|
Usage: nomad node identity get [options] <node_id>
|
|
|
|
Get the identity claims for a node. This command only applies to client
|
|
agents.
|
|
|
|
If ACLs are enabled, this command requires a token with the 'node:read'
|
|
capability.
|
|
|
|
General Options:
|
|
|
|
` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + `
|
|
|
|
Get Options:
|
|
|
|
-json
|
|
Output the node identity claims in a JSON format.
|
|
|
|
-t
|
|
Format and display the node identity claims using a Go template.
|
|
`
|
|
|
|
return strings.TrimSpace(helpText)
|
|
}
|
|
|
|
func (n *NodeIdentityGetCommand) Synopsis() string { return "Detail a node's identity claims" }
|
|
|
|
func (n *NodeIdentityGetCommand) Name() string { return "node identity get" }
|
|
|
|
func (n *NodeIdentityGetCommand) Run(args []string) int {
|
|
|
|
flags := n.Meta.FlagSet(n.Name(), FlagSetClient)
|
|
flags.BoolVar(&n.json, "json", false, "")
|
|
flags.StringVar(&n.tmpl, "t", "", "")
|
|
flags.Usage = func() { n.Ui.Output(n.Help()) }
|
|
|
|
if err := flags.Parse(args); err != nil {
|
|
return 1
|
|
}
|
|
args = flags.Args()
|
|
|
|
if len(args) != 1 {
|
|
n.Ui.Error("This command takes one argument: <node_id>")
|
|
n.Ui.Error(commandErrorText(n))
|
|
return 1
|
|
}
|
|
|
|
// Get the HTTP client
|
|
client, err := n.Meta.Client()
|
|
if err != nil {
|
|
n.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
|
return 1
|
|
}
|
|
|
|
nodeID, err := lookupNodeID(client.Nodes(), args[0])
|
|
if err != nil {
|
|
n.Ui.Error(err.Error())
|
|
return 1
|
|
}
|
|
|
|
req := api.NodeIdentityGetRequest{NodeID: nodeID}
|
|
|
|
resp, err := client.Nodes().Identity().Get(&req, nil)
|
|
if err != nil {
|
|
n.Ui.Error(fmt.Sprintf("Error requesting node identity: %s", err))
|
|
return 1
|
|
}
|
|
|
|
return n.ouputClaims(resp.Claims)
|
|
}
|
|
|
|
func (n *NodeIdentityGetCommand) ouputClaims(claims map[string]any) int {
|
|
|
|
// If the user has requested JSON output or a template, format the claims
|
|
// accordingly.
|
|
if n.json || len(n.tmpl) > 0 {
|
|
out, err := Format(n.json, n.tmpl, claims)
|
|
if err != nil {
|
|
n.Ui.Error(err.Error())
|
|
return 1
|
|
}
|
|
|
|
n.Ui.Output(out)
|
|
return 0
|
|
}
|
|
|
|
var genericClaims, nomadClaims []string
|
|
|
|
// Iterate through the claims and separate the generic and Nomad-specific
|
|
// claims. This will allow us to group them in the output.
|
|
for key := range claims {
|
|
if strings.HasPrefix(key, "nomad") {
|
|
nomadClaims = append(nomadClaims, key)
|
|
} else {
|
|
genericClaims = append(genericClaims, key)
|
|
}
|
|
}
|
|
|
|
// Sort the claims alphabetically for consistent output.
|
|
sort.Strings(genericClaims)
|
|
sort.Strings(nomadClaims)
|
|
|
|
output := make([]string, len(genericClaims)+len(nomadClaims)+1)
|
|
output[0] = "Claim Key|Claim Value"
|
|
|
|
for i, key := range genericClaims {
|
|
|
|
// The generic claims currently include timestamps which come to the CLI
|
|
// as float64 values. We need to correctly convert these into a
|
|
// human-readable format. All other claims are string values.
|
|
switch valT := claims[key].(type) {
|
|
case float64:
|
|
output[i+1] = fmt.Sprintf("%s | %v", key, formatTime(time.Unix(int64(valT), 0)))
|
|
default:
|
|
output[i+1] = fmt.Sprintf("%s | %s", key, valT)
|
|
}
|
|
}
|
|
|
|
for i, key := range nomadClaims {
|
|
output[i+1+len(genericClaims)] = fmt.Sprintf("%s | %s", key, claims[key])
|
|
}
|
|
|
|
n.Ui.Output(formatList(output))
|
|
return 0
|
|
}
|
|
|
|
func (n *NodeIdentityGetCommand) AutocompleteFlags() complete.Flags {
|
|
return mergeAutocompleteFlags(n.Meta.AutocompleteFlags(FlagSetClient),
|
|
complete.Flags{
|
|
"-json": complete.PredictNothing,
|
|
"-t": complete.PredictAnything,
|
|
})
|
|
}
|
|
|
|
func (n *NodeIdentityGetCommand) AutocompleteArgs() complete.Predictor {
|
|
return nodePredictor(n.Client, nil)
|
|
}
|