wi: new endpoint for listing workload attached ACL policies (#25588)

This introduces a new HTTP endpoint (and an associated CLI command) for querying
ACL policies associated with a workload identity. It allows users that want
to learn about the ACL capabilities from within WI-tasks to know what sort of
policies are enabled.

---------

Co-authored-by: Tim Gross <tgross@hashicorp.com>
Co-authored-by: Aimee Ukasick <aimee.ukasick@hashicorp.com>
This commit is contained in:
Piotr Kazmierczak
2025-05-19 19:54:12 +02:00
committed by GitHub
parent 953910dc5d
commit cdc308a0eb
14 changed files with 484 additions and 8 deletions

3
.changelog/25588.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:improvement
wi: new API endpoint for listing workload-attached ACL policies
```

View File

@@ -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
}

129
command/acl_policy_self.go Normal file
View File

@@ -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, "<unavailable>", "<unavailable>", "<unavailable>")
} 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 "<not specified>"
}
return jobACL
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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 == "" {

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 <not specified>
$ nomad acl policy self -json
[
{
"CreateIndex": 22,
"Description": "",
"JobACL": {
"Group": "nomad",
"JobID": "nomad",
"Namespace": "default",
"Task": ""
},
"ModifyIndex": 22,
"Name": "nomad-policy"
}
]
```

View File

@@ -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.
```

View File

@@ -453,6 +453,10 @@
{
"title": "list",
"path": "commands/acl/policy/list"
},
{
"title": "self",
"path": "commands/acl/policy/self"
}
]
},