diff --git a/.changelog/25588.txt b/.changelog/25588.txt new file mode 100644 index 000000000..c03b9c464 --- /dev/null +++ b/.changelog/25588.txt @@ -0,0 +1,3 @@ +```release-note:improvement +wi: new API endpoint for listing workload-attached ACL policies +``` diff --git a/api/acl.go b/api/acl.go index dd61541aa..74f71f605 100644 --- a/api/acl.go +++ b/api/acl.go @@ -67,6 +67,16 @@ func (a *ACLPolicies) Info(policyName string, q *QueryOptions) (*ACLPolicy, *Que return &resp, wm, nil } +// Self is used to query policies attached to a workload identity +func (a *ACLPolicies) Self(q *QueryOptions) ([]*ACLPolicyListStub, *QueryMeta, error) { + var resp []*ACLPolicyListStub + wm, err := a.client.query("/v1/acl/policy/self", &resp, q) + if err != nil { + return nil, nil, err + } + return resp, wm, nil +} + // ACLTokens is used to query the ACL token endpoints. type ACLTokens struct { client *Client @@ -509,6 +519,7 @@ func (a *ACLAuth) Login(req *ACLLoginRequest, q *WriteOptions) (*ACLToken, *Writ type ACLPolicyListStub struct { Name string Description string + JobACL *JobACL CreateIndex uint64 ModifyIndex uint64 } diff --git a/command/acl_policy_self.go b/command/acl_policy_self.go new file mode 100644 index 000000000..c574fa244 --- /dev/null +++ b/command/acl_policy_self.go @@ -0,0 +1,129 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package command + +import ( + "fmt" + "strings" + + "github.com/posener/complete" +) + +type ACLPolicySelfCommand struct { + Meta + + json bool + tmpl string +} + +func (c *ACLPolicySelfCommand) Help() string { + helpText := ` +Usage: nomad acl policy self + + Self is used to fetch information about the policy assigned to the current + workload identity or ACL token. + +General Options: + + ` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + ` + +ACL List Options: + + -json + Output the ACL policies in a JSON format. + + -t + Format and display the ACL policies using a Go template. +` + return strings.TrimSpace(helpText) +} + +func (c *ACLPolicySelfCommand) AutocompleteFlags() complete.Flags { + return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), + complete.Flags{ + "-json": complete.PredictNothing, + "-t": complete.PredictAnything, + }) +} + +func (c *ACLPolicySelfCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +func (c *ACLPolicySelfCommand) Synopsis() string { + return "Lookup self ACL policy assigned to the workload identity or ACL token" +} + +func (c *ACLPolicySelfCommand) Name() string { return "acl policy self" } + +func (c *ACLPolicySelfCommand) Run(args []string) int { + flags := c.Meta.FlagSet(c.Name(), FlagSetClient) + flags.Usage = func() { c.Ui.Output(c.Help()) } + flags.BoolVar(&c.json, "json", false, "") + flags.StringVar(&c.tmpl, "t", "", "") + if err := flags.Parse(args); err != nil { + return 1 + } + + // Check that we have no arguments + args = flags.Args() + if l := len(args); l != 0 { + c.Ui.Error("This command takes no arguments") + c.Ui.Error(commandErrorText(c)) + return 1 + } + + // Get the HTTP client + client, err := c.Meta.Client() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) + return 1 + } + + policies, _, err := client.ACLPolicies().Self(nil) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error fetching WI policies: %s", err)) + return 1 + } + + if len(policies) == 0 { + c.Ui.Output("No policies found.") + } else { + if c.json || len(c.tmpl) > 0 { + out, err := Format(c.json, c.tmpl, policies) + if err != nil { + c.Ui.Error(err.Error()) + return 1 + } + + c.Ui.Output(out) + return 0 + } + + output := make([]string, 0, len(policies)+1) + output = append(output, "Name|Job ID|Group Name|Task Name") + for _, p := range policies { + var outputString string + if p.JobACL == nil { + outputString = fmt.Sprintf("%s|%s|%s|%s", p.Name, "", "", "") + } else { + outputString = fmt.Sprintf( + "%s|%s|%s|%s", + p.Name, formatJobACL(p.JobACL.JobID), formatJobACL(p.JobACL.Group), formatJobACL(p.JobACL.Task), + ) + } + output = append(output, outputString) + } + + c.Ui.Output(formatList(output)) + } + return 0 +} + +func formatJobACL(jobACL string) string { + if jobACL == "" { + return "" + } + return jobACL +} diff --git a/command/acl_policy_self_test.go b/command/acl_policy_self_test.go new file mode 100644 index 000000000..21fd17e7c --- /dev/null +++ b/command/acl_policy_self_test.go @@ -0,0 +1,79 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package command + +import ( + "testing" + + "github.com/hashicorp/cli" + "github.com/hashicorp/nomad/command/agent" + "github.com/hashicorp/nomad/nomad/mock" + "github.com/hashicorp/nomad/nomad/structs" + "github.com/hashicorp/nomad/testutil" + "github.com/shoenig/test/must" +) + +func TestACLPolicySelfCommand_ViaEnvVar(t *testing.T) { + config := func(c *agent.Config) { + c.ACL.Enabled = true + } + + srv, _, url := testServer(t, true, config) + defer srv.Shutdown() + + state := srv.Agent.Server().State() + + // Bootstrap an initial ACL token + token := srv.RootToken + must.NotNil(t, token) + + // Create a minimal job + job := mock.MinJob() + + // Add a job policy + polArgs := structs.ACLPolicyUpsertRequest{ + Policies: []*structs.ACLPolicy{ + { + Name: "nw", + Description: "test job can write to nodes", + Rules: `node { policy = "write" }`, + JobACL: &structs.JobACL{ + Namespace: job.Namespace, + JobID: job.ID, + }, + }, + }, + WriteRequest: structs.WriteRequest{ + Region: job.Region, + AuthToken: token.SecretID, + Namespace: job.Namespace, + }, + } + polReply := structs.GenericResponse{} + must.NoError(t, srv.RPC("ACL.UpsertPolicies", &polArgs, &polReply)) + must.NonZero(t, polReply.WriteMeta.Index) + + ui := cli.NewMockUi() + cmd := &ACLPolicySelfCommand{Meta: Meta{Ui: ui, flagAddress: url}} + + allocs := testutil.WaitForRunningWithToken(t, srv.RPC, job, token.SecretID) + must.Len(t, 1, allocs) + + alloc, err := state.AllocByID(nil, allocs[0].ID) + must.NoError(t, err) + must.MapContainsKey(t, alloc.SignedIdentities, "t") + wid := alloc.SignedIdentities["t"] + + // Fetch info on policies with a JWT + t.Setenv("NOMAD_TOKEN", wid) + code := cmd.Run([]string{"-address=" + url}) + must.Zero(t, code) + + // Check the output + out := ui.OutputWriter.String() + must.StrContains(t, out, polArgs.Policies[0].Name) + + // make sure we put the job ACLs in there, too + must.StrContains(t, out, polArgs.Policies[0].JobACL.JobID) +} diff --git a/command/acl_token_self.go b/command/acl_token_self.go index 805dde91f..deaa717f7 100644 --- a/command/acl_token_self.go +++ b/command/acl_token_self.go @@ -5,8 +5,10 @@ package command import ( "fmt" + "os" "strings" + "github.com/hashicorp/nomad/helper" "github.com/posener/complete" ) @@ -63,14 +65,30 @@ func (c *ACLTokenSelfCommand) Run(args []string) int { return 1 } - // Get the specified token information - token, _, err := client.ACLTokens().Self(nil) - if err != nil { - c.Ui.Error(fmt.Sprintf("Error fetching self token: %s", err)) + // Check what kind of token we have available + envToken := os.Getenv("NOMAD_TOKEN") + if envToken == "" { + c.Ui.Error("No token present in the environment") return 1 } - // Format the output - outputACLToken(c.Ui, token) - return 0 + // Does this look like a Nomad ACL token? + if helper.IsUUID(envToken) { + token, _, err := client.ACLTokens().Self(nil) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error fetching self token: %s", err)) + return 1 + } + // Format the output + outputACLToken(c.Ui, token) + return 0 + } + + policies, _, err := client.ACLPolicies().Self(nil) + if err == nil && len(policies) > 0 { + c.Ui.Info("No ACL token found but there are ACL policies attached to this workload identity. You can query them with acl policy self command.") + return 0 + } + c.Ui.Error("No ACL tokens, nor ACL policies attached to a workload identity found.") + return 1 } diff --git a/command/agent/acl_endpoint.go b/command/agent/acl_endpoint.go index 2971b46c3..baa793612 100644 --- a/command/agent/acl_endpoint.go +++ b/command/agent/acl_endpoint.go @@ -8,6 +8,7 @@ import ( "net/http" "strings" + "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/nomad/structs" ) @@ -34,6 +35,11 @@ func (s *HTTPServer) ACLPoliciesRequest(resp http.ResponseWriter, req *http.Requ } func (s *HTTPServer) ACLPolicySpecificRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + // handle the special case for "self" call + if req.URL.Path == "/v1/acl/policy/self" { + return s.aclSelfPolicy(resp, req) + } + name := strings.TrimPrefix(req.URL.Path, "/v1/acl/policy/") if len(name) == 0 { return nil, CodedError(400, "Missing Policy Name") @@ -46,7 +52,7 @@ func (s *HTTPServer) ACLPolicySpecificRequest(resp http.ResponseWriter, req *htt case http.MethodDelete: return s.aclPolicyDelete(resp, req, name) default: - return nil, CodedError(405, ErrInvalidMethod) + return nil, CodedError(http.StatusMethodNotAllowed, ErrInvalidMethod) } } @@ -180,6 +186,51 @@ func (s *HTTPServer) ACLTokenSpecificRequest(resp http.ResponseWriter, req *http return s.aclTokenCrud(resp, req, accessor) } +func (s *HTTPServer) aclSelfPolicy(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + if req.Method != http.MethodGet { + return nil, CodedError(http.StatusMethodNotAllowed, ErrInvalidMethod) + } + + wiPolicyReq := structs.GenericRequest{} + if s.parse(resp, req, &wiPolicyReq.Region, &wiPolicyReq.QueryOptions) { + return nil, nil + } + + // is it a JWT or a Nomad ACL token? + if !helper.IsUUID(wiPolicyReq.AuthToken) { + + // Resolve policies for workload identities + wiPolicyReply := structs.ACLPolicySetResponse{} + if err := s.agent.RPC("ACL.GetClaimPolicies", &wiPolicyReq, &wiPolicyReply); err != nil { + return nil, err + } + + if wiPolicyReply.Policies == nil { + wiPolicyReply.Policies = make(map[string]*structs.ACLPolicy, 0) + } + + // convert the output into ACLPolicyListResponse to get consistent API + // output + reply := structs.ACLPolicyListResponse{} + for _, wiReply := range wiPolicyReply.Policies { + reply.Policies = append(reply.Policies, wiReply.Stub()) + } + + setMeta(resp, &reply.QueryMeta) + return reply.Policies, nil + } + + // Resolve any authenticated policies + policiesListReq := &structs.ACLPolicyListRequest{QueryOptions: wiPolicyReq.QueryOptions} + policiesListReply := structs.ACLPolicyListResponse{} + if err := s.agent.RPC("ACL.ListPolicies", policiesListReq, &policiesListReply); err != nil { + return nil, err + } + + setMeta(resp, &policiesListReply.QueryMeta) + return policiesListReply.Policies, nil +} + func (s *HTTPServer) aclTokenCrud(resp http.ResponseWriter, req *http.Request, tokenAccessor string) (interface{}, error) { if tokenAccessor == "" { diff --git a/command/agent/acl_endpoint_test.go b/command/agent/acl_endpoint_test.go index 4e80c79eb..4e4f764ff 100644 --- a/command/agent/acl_endpoint_test.go +++ b/command/agent/acl_endpoint_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/nomad/helper/uuid" "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" + "github.com/hashicorp/nomad/testutil" "github.com/shoenig/test/must" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -123,6 +124,69 @@ func TestHTTP_ACLPolicyQuery(t *testing.T) { }) } +func TestHTTP_ACLPolicySelfQuery(t *testing.T) { + ci.Parallel(t) + httpACLTest(t, nil, func(s *TestAgent) { + job := mock.MinJob() + p1 := &structs.ACLPolicy{ + Name: "nw", + Description: "test job can write to nodes", + Rules: `node { policy = "write" }`, + JobACL: &structs.JobACL{ + Namespace: job.Namespace, + JobID: job.ID, + }, + } + p2 := mock.ACLPolicy() + p3 := mock.ACLPolicy() + args := structs.ACLPolicyUpsertRequest{ + Policies: []*structs.ACLPolicy{p1, p2, p3}, + WriteRequest: structs.WriteRequest{ + Region: "global", + AuthToken: s.RootToken.SecretID, + }, + } + var resp structs.GenericResponse + must.NoError(t, s.Agent.RPC("ACL.UpsertPolicies", &args, &resp)) + + // get the WI JWT from state + allocs := testutil.WaitForRunningWithToken(t, s.RPC, job, s.RootToken.SecretID) + must.Len(t, 1, allocs) + + state := s.Agent.server.State() + + alloc, err := state.AllocByID(nil, allocs[0].ID) + must.NoError(t, err) + must.MapContainsKey(t, alloc.SignedIdentities, "t") + wid := alloc.SignedIdentities["t"] + + // Make the HTTP request + req, err := http.NewRequest(http.MethodGet, "/v1/acl/policy/self", nil) + must.NoError(t, err) + + respW := httptest.NewRecorder() + req.Header.Set("X-Nomad-Token", wid) + + // Make the request + obj, err := s.Server.aclSelfPolicy(respW, req) + must.NoError(t, err) + + // Check the output + n := obj.([]*structs.ACLPolicyListStub) + must.Eq(t, n[0].JobACL, p1.JobACL) + must.SliceLen(t, 1, n) // only 1 policy is assigned to the WID + + // Make the without JWT + req.Header.Set("X-Nomad-Token", s.RootToken.SecretID) + obj, err = s.Server.aclSelfPolicy(respW, req) + must.NoError(t, err) + + // We should get all the policies now + nn := obj.([]*structs.ACLPolicyListStub) + must.SliceLen(t, 3, nn) + }) +} + func TestHTTP_ACLPolicyCreate(t *testing.T) { ci.Parallel(t) httpACLTest(t, nil, func(s *TestAgent) { diff --git a/command/commands.go b/command/commands.go index aeb4b301c..0766ea43f 100644 --- a/command/commands.go +++ b/command/commands.go @@ -173,6 +173,11 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory { Meta: meta, }, nil }, + "acl policy self": func() (cli.Command, error) { + return &ACLPolicySelfCommand{ + Meta: meta, + }, nil + }, "acl role": func() (cli.Command, error) { return &ACLRoleCommand{ Meta: meta, diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 39bb0b245..f781d506a 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -13242,6 +13242,7 @@ func (a *ACLPolicy) Stub() *ACLPolicyListStub { return &ACLPolicyListStub{ Name: a.Name, Description: a.Description, + JobACL: a.JobACL, Hash: a.Hash, CreateIndex: a.CreateIndex, ModifyIndex: a.ModifyIndex, @@ -13284,6 +13285,7 @@ func (a *ACLPolicy) Validate() error { type ACLPolicyListStub struct { Name string Description string + JobACL *JobACL Hash []byte CreateIndex uint64 ModifyIndex uint64 diff --git a/website/content/api-docs/acl/policies.mdx b/website/content/api-docs/acl/policies.mdx index 942deea0c..5788d6bed 100644 --- a/website/content/api-docs/acl/policies.mdx +++ b/website/content/api-docs/acl/policies.mdx @@ -155,6 +155,50 @@ $ curl \ } ``` +## Read Self Policy + +This endpoint reads the ACL policies associated with the current workload +identity or any authenticated request. + +| Method | Path | Produces | +|--------|-----------------------|--------------------| +| `GET` | `/v1/acl/policy/self` | `application/json` | + +This table shows this endpoint's support for +[blocking queries](/nomad/api-docs#blocking-queries), [consistency modes](/nomad/api-docs#consistency-modes) and +[required ACLs](/nomad/api-docs#acls). + +| Blocking Queries | Consistency Modes | ACL Required | +|------------------|-------------------|---------------------------------| +| `YES` | `all` | Any valid Workload Identity JWT | + +### Sample Request + +```shell-session +$ curl --header "X-Nomad-Token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImJiMmUwYjI5LTIyZTYtYjk0My0yN2M1LThkYmNmMjc5ODM0MCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJub21hZHByb2plY3QuaW8iLCJpYXQiOjE3NDM2OTI5NzksImp0aSI6IjJmOWI3ZGRmLWU1M2ItZGMxNS1kMzU4LTAyMDk5NjE1NThlMyIsIm5iZiI6MTc0MzY5Mjk3OSwibm9tYWRfYWxsb2NhdGlvbl9pZCI6IjhjMGJjMWFjLWRlMzMtYTNlYi03YWUwLTZiZjM3OGY5YzcxYiIsIm5vbWFkX2pvYl9pZCI6Im5vbWFkIiwibm9tYWRfbmFtZXNwYWNlIjoiZGVmYXVsdCIsIm5vbWFkX3Rhc2siOiJzZXJ2ZXIiLCJzdWIiOiJnbG9iYWw6ZGVmYXVsdDpub21hZDpub21hZDpzZXJ2ZXI6ZGVmYXVsdCJ9.IDZWTqGWRURDwI5OvO3LLjCsU1qzg6LEG4q5S7CfZawUXzMqAOoYajI_nynOGJp2aU77MqUyJmdFtrIBMoQnIxclEwNl9DkhfOrgjBsWefn9JqKEpORGD-0OLzaoYUgbu0k6aXCNktfpvHZN5uUsfL6nLOG-osQvHn9ZdboT31tjp1v6d-MfP96ZLG0NrXgLWMfwan2AAzuqMabIS9iO6OrZDNp2-TeeY_sqM-7sNEgfDo33GAeyhqTi8CAZhsDOv4wtJuFfMhrsbb33wHdAiltjXlafBtncMMaLHr07gbLvOMfty2_193i4Yi3H2PgPr7c4BYHoXyQJhFchDyYmFA" \ + https://localhost:4646/v1/acl/policy/self +``` + +### Sample Response + +```json +[ + { + "CreateIndex": 22, + "Description": "", + "Hash": "SmmKR0rW0WA0Bfpk7m3D8wqgrL0dtkY3DwAucF9YN4Y=", + "JobACL": { + "Group": "nomad", + "JobID": "nomad", + "Namespace": "default", + "Task": "" + }, + "ModifyIndex": 22, + "Name": "nomad-policy" + } +] +``` + ## Delete Policy This endpoint deletes the named ACL policy. This request is always forwarded to the diff --git a/website/content/docs/commands/acl/index.mdx b/website/content/docs/commands/acl/index.mdx index 7d89755b0..480eaf10e 100644 --- a/website/content/docs/commands/acl/index.mdx +++ b/website/content/docs/commands/acl/index.mdx @@ -37,6 +37,7 @@ subcommands are available: - [`acl policy delete`][policydelete] - Delete an existing ACL policies - [`acl policy info`][policyinfo] - Fetch information on an existing ACL policy - [`acl policy list`][policylist] - List available ACL policies +- [`acl policy self`][policyself] - List ACL policies attached to the current workload - [`acl role create`][rolecreate] - Create a new ACL role - [`acl role delete`][roledelete] - Delete an existing ACL role - [`acl role info`][roleinfo] - Get info on an existing ACL role @@ -64,6 +65,7 @@ subcommands are available: [policydelete]: /nomad/docs/commands/acl/policy/delete [policyinfo]: /nomad/docs/commands/acl/policy/info [policylist]: /nomad/docs/commands/acl/policy/list +[policyself]: /nomad/docs/commands/acl/policy/self [tokencreate]: /nomad/docs/commands/acl/token/create [tokenupdate]: /nomad/docs/commands/acl/token/update [tokendelete]: /nomad/docs/commands/acl/token/delete diff --git a/website/content/docs/commands/acl/policy/self.mdx b/website/content/docs/commands/acl/policy/self.mdx new file mode 100644 index 000000000..69eeff29d --- /dev/null +++ b/website/content/docs/commands/acl/policy/self.mdx @@ -0,0 +1,54 @@ +--- +layout: docs +page_title: 'nomad acl policy self command reference' +description: > + The `nomad acl policy self` command fetches information about the currently set access control list (ACL) policies available to the authenticated request. +--- + +# `nomad acl policy self` command reference + +The `nomad acl policy self` command fetches information about the currently +set ACL policies attached to the workload or ACL tokens. + +## Usage + +```plaintext +nomad acl policy self [options] +``` + +## General options + +@include 'general_options_no_namespace.mdx' + +## Self options + +- `-json` : Output the ACL policies in a JSON format. + +- `-t` : Format and display the ACL policies using a Go template. + +## Examples + +Fetch information about an existing ACL policies attached to the workload. + +```shell-session +$ echo $NOMAD_TOKEN +eyJhbGciOiJSUzI1NiIsImtpZCI6ImJiMmUwYjI5LTIyZTYtYjk0My0yN2M1LThkYmNmMjc5ODM0MCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJub21hZHByb2plY3QuaW8iLCJpYXQiOjE3NDM2OTI5NzksImp0aSI6IjJmOWI3ZGRmLWU1M2ItZGMxNS1kMzU4LTAyMDk5NjE1NThlMyIsIm5iZiI6MTc0MzY5Mjk3OSwibm9tYWRfYWxsb2NhdGlvbl9pZCI6IjhjMGJjMWFjLWRlMzMtYTNlYi03YWUwLTZiZjM3OGY5YzcxYiIsIm5vbWFkX2pvYl9pZCI6Im5vbWFkIiwibm9tYWRfbmFtZXNwYWNlIjoiZGVmYXVsdCIsIm5vbWFkX3Rhc2siOiJzZXJ2ZXIiLCJzdWIiOiJnbG9iYWw6ZGVmYXVsdDpub21hZDpub21hZDpzZXJ2ZXI6ZGVmYXVsdCJ9.IDZWTqGWRURDwI5OvO3LLjCsU1qzg6LEG4q5S7CfZawUXzMqAOoYajI_nynOGJp2aU77MqUyJmdFtrIBMoQnIxclEwNl9DkhfOrgjBsWefn9JqKEpORGD-0OLzaoYUgbu0k6aXCNktfpvHZN5uUsfL6nLOG-osQvHn9ZdboT31tjp1v6d-MfP96ZLG0NrXgLWMfwan2AAzuqMabIS9iO6OrZDNp2-TeeY_sqM-7sNEgfDo33GAeyhqTi8CAZhsDOv4wtJuFfMhrsbb33wHdAiltjXlafBtncMMaLHr07gbLvOMfty2_193i4Yi3H2PgPr7c4BYHoXyQJhFchDyYmFA +$ nomad acl policy self +Name Job ID Group Name Task Name +nomad-policy nomad nomad +$ nomad acl policy self -json +[ + { + "CreateIndex": 22, + "Description": "", + "JobACL": { + "Group": "nomad", + "JobID": "nomad", + "Namespace": "default", + "Task": "" + }, + "ModifyIndex": 22, + "Name": "nomad-policy" + } +] +``` \ No newline at end of file diff --git a/website/content/docs/commands/acl/token/self.mdx b/website/content/docs/commands/acl/token/self.mdx index daeea1a83..95192a2c3 100644 --- a/website/content/docs/commands/acl/token/self.mdx +++ b/website/content/docs/commands/acl/token/self.mdx @@ -40,3 +40,13 @@ Modify Index = 8 Policies = n/a Roles = n/a ``` + +The command also detects if the current Nomad token is a workload identity +JWT and respond with a hint if that's the case. + +```shell-session +$ echo $NOMAD_TOKEN +eyJhbGciOiJSUzI1NiIsImtpZCI6ImJiMmUwYjI5LTIyZTYtYjk0My0yN2M1LThkYmNmMjc5ODM0MCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJub21hZHByb2plY3QuaW8iLCJpYXQiOjE3NDM2OTI5NzksImp0aSI6IjJmOWI3ZGRmLWU1M2ItZGMxNS1kMzU4LTAyMDk5NjE1NThlMyIsIm5iZiI6MTc0MzY5Mjk3OSwibm9tYWRfYWxsb2NhdGlvbl9pZCI6IjhjMGJjMWFjLWRlMzMtYTNlYi03YWUwLTZiZjM3OGY5YzcxYiIsIm5vbWFkX2pvYl9pZCI6Im5vbWFkIiwibm9tYWRfbmFtZXNwYWNlIjoiZGVmYXVsdCIsIm5vbWFkX3Rhc2siOiJzZXJ2ZXIiLCJzdWIiOiJnbG9iYWw6ZGVmYXVsdDpub21hZDpub21hZDpzZXJ2ZXI6ZGVmYXVsdCJ9.IDZWTqGWRURDwI5OvO3LLjCsU1qzg6LEG4q5S7CfZawUXzMqAOoYajI_nynOGJp2aU77MqUyJmdFtrIBMoQnIxclEwNl9DkhfOrgjBsWefn9JqKEpORGD-0OLzaoYUgbu0k6aXCNktfpvHZN5uUsfL6nLOG-osQvHn9ZdboT31tjp1v6d-MfP96ZLG0NrXgLWMfwan2AAzuqMabIS9iO6OrZDNp2-TeeY_sqM-7sNEgfDo33GAeyhqTi8CAZhsDOv4wtJuFfMhrsbb33wHdAiltjXlafBtncMMaLHr07gbLvOMfty2_193i4Yi3H2PgPr7c4BYHoXyQJhFchDyYmFA +$ nomad acl token self +No ACL token found but there are ACL policies attached to this workload identity. You can query them with acl policy self command. +``` \ No newline at end of file diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index d084f5e6a..ecc7c5e10 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -453,6 +453,10 @@ { "title": "list", "path": "commands/acl/policy/list" + }, + { + "title": "self", + "path": "commands/acl/policy/self" } ] },