Remove legacy acl tokens (#15947)
* remove legacy tokens * Update test comment Co-authored-by: Paul Glass <pglass@hashicorp.com> * fix imports * update docs for additional CLI changes * add test case for anonymous token * set deprecated api fields to json ignore and fix patch errors * update changelog to breaking-change * fix import * update api docs to remove legacy reference * fix docs nav data --------- Co-authored-by: Paul Glass <pglass@hashicorp.com>
This commit is contained in:
parent
e69e7fd1f2
commit
77d805ceb6
|
@ -0,0 +1,3 @@
|
|||
```release-note:breaking-change
|
||||
acl: remove all acl migration functionality and references to the legacy acl system.
|
||||
```
|
|
@ -96,9 +96,7 @@ func (e PermissionDeniedError) Error() string {
|
|||
return message.String()
|
||||
}
|
||||
|
||||
if e.Accessor == "" {
|
||||
message.WriteString(": provided token")
|
||||
} else if e.Accessor == AnonymousTokenID {
|
||||
if e.Accessor == AnonymousTokenID {
|
||||
message.WriteString(": anonymous token")
|
||||
} else {
|
||||
fmt.Fprintf(&message, ": token with AccessorID '%s'", e.Accessor)
|
||||
|
|
|
@ -17,6 +17,7 @@ func TestPermissionDeniedError(t *testing.T) {
|
|||
}
|
||||
|
||||
auth1 := MockAuthorizer{}
|
||||
auth2 := AllowAuthorizer{nil, AnonymousTokenID}
|
||||
|
||||
cases := []testCase{
|
||||
{
|
||||
|
@ -29,11 +30,15 @@ func TestPermissionDeniedError(t *testing.T) {
|
|||
},
|
||||
{
|
||||
err: PermissionDeniedByACL(&auth1, nil, ResourceService, AccessRead, "foobar"),
|
||||
expected: "Permission denied: provided token lacks permission 'service:read' on \"foobar\"",
|
||||
expected: "Permission denied: token with AccessorID '' lacks permission 'service:read' on \"foobar\"",
|
||||
},
|
||||
{
|
||||
err: PermissionDeniedByACLUnnamed(&auth1, nil, ResourceService, AccessRead),
|
||||
expected: "Permission denied: provided token lacks permission 'service:read'",
|
||||
expected: "Permission denied: token with AccessorID '' lacks permission 'service:read'",
|
||||
},
|
||||
{
|
||||
err: PermissionDeniedByACLUnnamed(auth2, nil, ResourceService, AccessRead),
|
||||
expected: "Permission denied: anonymous token lacks permission 'service:read'. The anonymous token is used implicitly when a request does not specify a token.",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -428,10 +428,8 @@ func (s *HTTPHandlers) aclTokenSetInternal(req *http.Request, tokenID string, cr
|
|||
}
|
||||
|
||||
if !create {
|
||||
if args.ACLToken.AccessorID != "" && args.ACLToken.AccessorID != tokenID {
|
||||
if args.ACLToken.AccessorID != tokenID {
|
||||
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Token Accessor ID in URL and payload do not match"}
|
||||
} else if args.ACLToken.AccessorID == "" {
|
||||
args.ACLToken.AccessorID = tokenID
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (s *HTTPHandlers) ACLLegacy(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
resp.WriteHeader(http.StatusGone)
|
||||
msg := "Endpoint %v for the legacy ACL system was removed in Consul 1.11."
|
||||
fmt.Fprintf(resp, msg, req.URL.Path)
|
||||
return nil, nil
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHTTPHandlers_ACLLegacy(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("too slow for testing.Short")
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
a := NewTestAgent(t, "")
|
||||
defer a.Shutdown()
|
||||
|
||||
type testCase struct {
|
||||
method string
|
||||
path string
|
||||
}
|
||||
|
||||
run := func(t *testing.T, tc testCase) {
|
||||
req, err := http.NewRequest(tc.method, tc.path, nil)
|
||||
require.NoError(t, err)
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
a.srv.h.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusGone, resp.Code)
|
||||
require.Contains(t, resp.Body.String(), "the legacy ACL system was removed")
|
||||
}
|
||||
|
||||
var testCases = []testCase{
|
||||
{method: http.MethodPut, path: "/v1/acl/create"},
|
||||
{method: http.MethodPut, path: "/v1/acl/update"},
|
||||
{method: http.MethodPut, path: "/v1/acl/destroy/ID"},
|
||||
{method: http.MethodGet, path: "/v1/acl/info/ID"},
|
||||
{method: http.MethodPut, path: "/v1/acl/clone/ID"},
|
||||
{method: http.MethodGet, path: "/v1/acl/list"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.method+tc.path, func(t *testing.T) {
|
||||
run(t, tc)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -864,8 +864,9 @@ func TestACL_HTTP(t *testing.T) {
|
|||
t.Run("Update", func(t *testing.T) {
|
||||
originalToken := tokenMap[idMap["token-cloned"]]
|
||||
|
||||
// Accessor and Secret will be filled in
|
||||
// Secret will be filled in
|
||||
tokenInput := &structs.ACLToken{
|
||||
AccessorID: tokenMap[idMap["token-cloned"]].AccessorID,
|
||||
Description: "Better description for this cloned token",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{
|
||||
|
|
|
@ -180,10 +180,9 @@ func authzFromPolicy(policy *acl.Policy, cfg *acl.Config) (acl.Authorizer, error
|
|||
return acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, cfg)
|
||||
}
|
||||
|
||||
type testToken struct {
|
||||
type testTokenRules struct {
|
||||
token structs.ACLToken
|
||||
// yes the rules can exist on the token itself but that is legacy behavior
|
||||
// that I would prefer these tests not rely on
|
||||
// rules to create associated policy
|
||||
rules string
|
||||
}
|
||||
|
||||
|
@ -194,7 +193,7 @@ var (
|
|||
serviceRWSecret = "4a1017a2-f788-4be3-93f2-90566f1340bb"
|
||||
otherRWSecret = "a38e8016-91b6-4876-b3e7-a307abbb2002"
|
||||
|
||||
testTokens = map[string]testToken{
|
||||
testACLs = map[string]testTokenRules{
|
||||
nodeROSecret: {
|
||||
token: structs.ACLToken{
|
||||
AccessorID: "9df2d1a4-2d07-414e-8ead-6053f56ed2eb",
|
||||
|
@ -233,8 +232,8 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func catalogPolicy(token string) (structs.ACLIdentity, acl.Authorizer, error) {
|
||||
tok, ok := testTokens[token]
|
||||
func catalogPolicy(testACL string) (structs.ACLIdentity, acl.Authorizer, error) {
|
||||
tok, ok := testACLs[testACL]
|
||||
if !ok {
|
||||
return nil, nil, acl.ErrNotFound
|
||||
}
|
||||
|
@ -248,8 +247,8 @@ func catalogPolicy(token string) (structs.ACLIdentity, acl.Authorizer, error) {
|
|||
return &tok.token, authz, err
|
||||
}
|
||||
|
||||
func catalogIdent(token string) (structs.ACLIdentity, error) {
|
||||
tok, ok := testTokens[token]
|
||||
func catalogIdent(testACL string) (structs.ACLIdentity, error) {
|
||||
tok, ok := testACLs[testACL]
|
||||
if !ok {
|
||||
return nil, acl.ErrNotFound
|
||||
}
|
||||
|
|
|
@ -470,10 +470,6 @@ func (a *ACL) TokenClone(args *structs.ACLTokenSetRequest, reply *structs.ACLTok
|
|||
return fmt.Errorf("Cannot clone a token created from an auth method")
|
||||
}
|
||||
|
||||
if token.Rules != "" {
|
||||
return fmt.Errorf("Cannot clone a legacy ACL with this endpoint")
|
||||
}
|
||||
|
||||
clone := &structs.ACLToken{
|
||||
Policies: token.Policies,
|
||||
Roles: token.Roles,
|
||||
|
|
|
@ -483,7 +483,6 @@ func TestACLEndpoint_TokenClone(t *testing.T) {
|
|||
require.Equal(t, t1.Roles, t2.Roles)
|
||||
require.Equal(t, t1.ServiceIdentities, t2.ServiceIdentities)
|
||||
require.Equal(t, t1.NodeIdentities, t2.NodeIdentities)
|
||||
require.Equal(t, t1.Rules, t2.Rules)
|
||||
require.Equal(t, t1.Local, t2.Local)
|
||||
require.NotEqual(t, t1.AccessorID, t2.AccessorID)
|
||||
require.NotEqual(t, t1.SecretID, t2.SecretID)
|
||||
|
|
|
@ -306,10 +306,6 @@ func (w *TokenWriter) write(token, existing *structs.ACLToken, fromLogin bool) (
|
|||
}
|
||||
token.NodeIdentities = nodeIdentities
|
||||
|
||||
if token.Rules != "" {
|
||||
return nil, errors.New("Rules cannot be specified for this token")
|
||||
}
|
||||
|
||||
if token.Type != "" {
|
||||
return nil, errors.New("Type cannot be specified for this token")
|
||||
}
|
||||
|
|
|
@ -98,10 +98,6 @@ func TestTokenWriter_Create_Validation(t *testing.T) {
|
|||
fromLogin: false,
|
||||
errorContains: "AuthMethod field is disallowed outside of login",
|
||||
},
|
||||
"Rules set": {
|
||||
token: structs.ACLToken{Rules: "some rules"},
|
||||
errorContains: "Rules cannot be specified for this token",
|
||||
},
|
||||
"Type set": {
|
||||
token: structs.ACLToken{Type: "some-type"},
|
||||
errorContains: "Type cannot be specified for this token",
|
||||
|
@ -498,10 +494,6 @@ func TestTokenWriter_Update_Validation(t *testing.T) {
|
|||
token: structs.ACLToken{AccessorID: token.AccessorID, ExpirationTime: timePointer(token.ExpirationTime.Add(1 * time.Minute))},
|
||||
errorContains: "Cannot change expiration time",
|
||||
},
|
||||
"Rules set": {
|
||||
token: structs.ACLToken{AccessorID: token.AccessorID, Rules: "some rules"},
|
||||
errorContains: "Rules cannot be specified for this token",
|
||||
},
|
||||
"Type set": {
|
||||
token: structs.ACLToken{AccessorID: token.AccessorID, Type: "some-type"},
|
||||
errorContains: "Type cannot be specified for this token",
|
||||
|
|
|
@ -20,8 +20,6 @@ func init() {
|
|||
registerRestorer(structs.KVSRequestType, restoreKV)
|
||||
registerRestorer(structs.TombstoneRequestType, restoreTombstone)
|
||||
registerRestorer(structs.SessionRequestType, restoreSession)
|
||||
registerRestorer(structs.DeprecatedACLRequestType, restoreACL) // TODO(ACL-Legacy-Compat) - remove in phase 2
|
||||
registerRestorer(structs.ACLBootstrapRequestType, restoreACLBootstrap)
|
||||
registerRestorer(structs.CoordinateBatchUpdateType, restoreCoordinates)
|
||||
registerRestorer(structs.PreparedQueryRequestType, restorePreparedQuery)
|
||||
registerRestorer(structs.AutopilotRequestType, restoreAutopilot)
|
||||
|
@ -660,73 +658,6 @@ func restoreSession(header *SnapshotHeader, restore *state.Restore, decoder *cod
|
|||
return nil
|
||||
}
|
||||
|
||||
// TODO(ACL-Legacy-Compat) - remove in phase 2
|
||||
func restoreACL(_ *SnapshotHeader, restore *state.Restore, decoder *codec.Decoder) error {
|
||||
var req LegacyACL
|
||||
if err := decoder.Decode(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := restore.ACLToken(req.Convert()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(ACL-Legacy-Compat) - remove in phase 2
|
||||
type LegacyACL struct {
|
||||
ID string
|
||||
Name string
|
||||
Type string
|
||||
Rules string
|
||||
|
||||
structs.RaftIndex
|
||||
}
|
||||
|
||||
// TODO(ACL-Legacy-Compat): remove in phase 2, used by snapshot restore
|
||||
func (a LegacyACL) Convert() *structs.ACLToken {
|
||||
correctedRules := structs.SanitizeLegacyACLTokenRules(a.Rules)
|
||||
if correctedRules != "" {
|
||||
a.Rules = correctedRules
|
||||
}
|
||||
|
||||
token := &structs.ACLToken{
|
||||
AccessorID: "",
|
||||
SecretID: a.ID,
|
||||
Description: a.Name,
|
||||
Policies: nil,
|
||||
ServiceIdentities: nil,
|
||||
NodeIdentities: nil,
|
||||
Type: a.Type,
|
||||
Rules: a.Rules,
|
||||
Local: false,
|
||||
RaftIndex: a.RaftIndex,
|
||||
}
|
||||
|
||||
token.SetHash(true)
|
||||
return token
|
||||
}
|
||||
|
||||
// TODO(ACL-Legacy-Compat) - remove in phase 2
|
||||
func restoreACLBootstrap(_ *SnapshotHeader, restore *state.Restore, decoder *codec.Decoder) error {
|
||||
type ACLBootstrap struct {
|
||||
// AllowBootstrap will only be true if no existing management tokens
|
||||
// have been found.
|
||||
AllowBootstrap bool
|
||||
|
||||
structs.RaftIndex
|
||||
}
|
||||
|
||||
var req ACLBootstrap
|
||||
if err := decoder.Decode(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// With V2 ACLs whether bootstrapping has been performed is stored in the index table like nomad
|
||||
// so this "restores" into that index table.
|
||||
return restore.IndexRestore(&state.IndexEntry{Key: "acl-token-bootstrap", Value: req.ModifyIndex})
|
||||
}
|
||||
|
||||
func restoreCoordinates(header *SnapshotHeader, restore *state.Restore, decoder *codec.Decoder) error {
|
||||
var req structs.Coordinates
|
||||
if err := decoder.Decode(&req); err != nil {
|
||||
|
@ -819,14 +750,6 @@ func restoreToken(header *SnapshotHeader, restore *state.Restore, decoder *codec
|
|||
return err
|
||||
}
|
||||
|
||||
// DEPRECATED (ACL-Legacy-Compat)
|
||||
if req.Rules != "" {
|
||||
// When we restore a snapshot we may have to correct old HCL in legacy
|
||||
// tokens to prevent the in-memory representation from using an older
|
||||
// syntax.
|
||||
structs.SanitizeLegacyACLToken(&req)
|
||||
}
|
||||
|
||||
// only set if unset - mitigates a bug where converted legacy tokens could end up without a hash
|
||||
req.SetHash(false)
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package fsm
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -12,7 +11,6 @@ import (
|
|||
|
||||
"github.com/hashicorp/consul-net-rpc/go-msgpack/codec"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/connect"
|
||||
"github.com/hashicorp/consul/agent/consul/state"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
|
@ -87,7 +85,6 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
|||
Name: "global-management",
|
||||
Description: "Builtin Policy that grants unlimited access",
|
||||
Rules: structs.ACLPolicyGlobalManagement,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
}
|
||||
policy.SetHash(true)
|
||||
require.NoError(t, fsm.state.ACLPolicySet(1, policy))
|
||||
|
@ -537,21 +534,6 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
|||
// be persisted but that we still need to be able to restore
|
||||
encoder := codec.NewEncoder(sink, structs.MsgpackHandle)
|
||||
|
||||
// Persist a legacy ACL token - this is not done in newer code
|
||||
// but we want to ensure that restoring legacy tokens works as
|
||||
// expected so we must inject one here manually
|
||||
_, err = sink.Write([]byte{byte(structs.DeprecatedACLRequestType)})
|
||||
require.NoError(t, err)
|
||||
|
||||
acl := LegacyACL{
|
||||
ID: "1057354f-69ef-4487-94ab-aead3c755445",
|
||||
Name: "test-legacy",
|
||||
Type: "client",
|
||||
Rules: `operator = "read"`,
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
}
|
||||
require.NoError(t, encoder.Encode(&acl))
|
||||
|
||||
// Persist a ACLToken without a Hash - the state store will
|
||||
// now tack these on but we want to ensure we can restore
|
||||
// tokens without a hash and have the hash be set.
|
||||
|
@ -561,8 +543,13 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
|||
Description: "Test No Hash",
|
||||
CreateTime: time.Now(),
|
||||
Local: false,
|
||||
Rules: `operator = "read"`,
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{
|
||||
Name: "global-management",
|
||||
ID: structs.ACLPolicyGlobalManagementID,
|
||||
},
|
||||
},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
}
|
||||
|
||||
_, err = sink.Write([]byte{byte(structs.ACLTokenSetRequestType)})
|
||||
|
@ -673,16 +660,6 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
|||
// adds the Hash to our local var.
|
||||
require.Equal(t, token, rtoken)
|
||||
|
||||
// Verify legacy ACL is restored
|
||||
_, rtoken, err = fsm2.state.ACLTokenGetBySecret(nil, acl.ID, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, rtoken)
|
||||
require.NotEmpty(t, rtoken.Hash)
|
||||
|
||||
restoredACL, err := convertACLTokenToLegacy(rtoken)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &acl, restoredACL)
|
||||
|
||||
// Verify ACLToken without hash computes the Hash during restoration
|
||||
_, rtoken, err = fsm2.state.ACLTokenGetByAccessor(nil, token2.AccessorID, nil)
|
||||
require.NoError(t, err)
|
||||
|
@ -884,23 +861,6 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// convertACLTokenToLegacy attempts to convert an ACLToken into an legacy ACL.
|
||||
// TODO(ACL-Legacy-Compat): remove in phase 2, used by snapshot restore
|
||||
func convertACLTokenToLegacy(tok *structs.ACLToken) (*LegacyACL, error) {
|
||||
if tok.Type == "" {
|
||||
return nil, fmt.Errorf("Cannot convert ACLToken into compat token")
|
||||
}
|
||||
|
||||
compat := &LegacyACL{
|
||||
ID: tok.SecretID,
|
||||
Name: tok.Description,
|
||||
Type: tok.Type,
|
||||
Rules: tok.Rules,
|
||||
RaftIndex: tok.RaftIndex,
|
||||
}
|
||||
return compat, nil
|
||||
}
|
||||
|
||||
func TestFSM_BadRestore_OSS(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Create an FSM with some state.
|
||||
|
|
|
@ -364,8 +364,6 @@ func (s *Server) revokeLeadership() {
|
|||
|
||||
s.stopACLTokenReaping()
|
||||
|
||||
s.stopACLUpgrade()
|
||||
|
||||
s.resetConsistentReadReady()
|
||||
|
||||
s.autopilot.DisableReconciliation()
|
||||
|
@ -541,9 +539,6 @@ func (s *Server) initializeACLs(ctx context.Context) error {
|
|||
if err := s.setSystemMetadataKey(structs.ServerManagementTokenAccessorID, secretID); err != nil {
|
||||
return fmt.Errorf("failed to persist server management token: %w", err)
|
||||
}
|
||||
|
||||
// launch the upgrade go routine to generate accessors for everything
|
||||
s.startACLUpgrade(ctx)
|
||||
} else {
|
||||
s.startACLReplication(ctx)
|
||||
}
|
||||
|
@ -553,100 +548,6 @@ func (s *Server) initializeACLs(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// legacyACLTokenUpgrade runs a single time to upgrade any tokens that may
|
||||
// have been created immediately before the Consul upgrade, or any legacy tokens
|
||||
// from a restored snapshot.
|
||||
// TODO(ACL-Legacy-Compat): remove in phase 2
|
||||
func (s *Server) legacyACLTokenUpgrade(ctx context.Context) error {
|
||||
// aclUpgradeRateLimit is the number of batch upgrade requests per second allowed.
|
||||
const aclUpgradeRateLimit rate.Limit = 1.0
|
||||
|
||||
// aclUpgradeBatchSize controls how many tokens we look at during each round of upgrading. Individual raft logs
|
||||
// will be further capped using the aclBatchUpsertSize. This limit just prevents us from creating a single slice
|
||||
// with all tokens in it.
|
||||
const aclUpgradeBatchSize = 128
|
||||
|
||||
limiter := rate.NewLimiter(aclUpgradeRateLimit, int(aclUpgradeRateLimit))
|
||||
for {
|
||||
if err := limiter.Wait(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// actually run the upgrade here
|
||||
state := s.fsm.State()
|
||||
tokens, _, err := state.ACLTokenListUpgradeable(aclUpgradeBatchSize)
|
||||
if err != nil {
|
||||
s.logger.Warn("encountered an error while searching for tokens without accessor ids", "error", err)
|
||||
}
|
||||
// No need to check expiration time here, as that only exists for v2 tokens.
|
||||
|
||||
if len(tokens) == 0 {
|
||||
// No new legacy tokens can be created, so we can exit
|
||||
s.stopACLUpgrade() // required to prevent goroutine leak, according to TestAgentLeaks_Server
|
||||
return nil
|
||||
}
|
||||
|
||||
var newTokens structs.ACLTokens
|
||||
for _, token := range tokens {
|
||||
// This should be entirely unnecessary but is just a small safeguard against changing accessor IDs
|
||||
if token.AccessorID != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
newToken := *token
|
||||
if token.SecretID == anonymousToken {
|
||||
newToken.AccessorID = acl.AnonymousTokenID
|
||||
} else {
|
||||
accessor, err := lib.GenerateUUID(s.checkTokenUUID)
|
||||
if err != nil {
|
||||
s.logger.Warn("failed to generate accessor during token auto-upgrade", "error", err)
|
||||
continue
|
||||
}
|
||||
newToken.AccessorID = accessor
|
||||
}
|
||||
|
||||
// Assign the global-management policy to legacy management tokens
|
||||
if len(newToken.Policies) == 0 &&
|
||||
len(newToken.ServiceIdentities) == 0 &&
|
||||
len(newToken.NodeIdentities) == 0 &&
|
||||
len(newToken.Roles) == 0 &&
|
||||
newToken.Type == "management" {
|
||||
newToken.Policies = append(newToken.Policies, structs.ACLTokenPolicyLink{ID: structs.ACLPolicyGlobalManagementID})
|
||||
}
|
||||
|
||||
// need to copy these as we are going to do a CAS operation.
|
||||
newToken.CreateIndex = token.CreateIndex
|
||||
newToken.ModifyIndex = token.ModifyIndex
|
||||
|
||||
newToken.SetHash(true)
|
||||
|
||||
newTokens = append(newTokens, &newToken)
|
||||
}
|
||||
|
||||
req := &structs.ACLTokenBatchSetRequest{Tokens: newTokens, CAS: true}
|
||||
|
||||
_, err = s.raftApply(structs.ACLTokenSetRequestType, req)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to apply acl token upgrade batch", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(ACL-Legacy-Compat): remove in phase 2. Keeping it for now so that we
|
||||
// can upgrade any tokens created immediately before the upgrade happens.
|
||||
func (s *Server) startACLUpgrade(ctx context.Context) {
|
||||
if s.config.PrimaryDatacenter != s.config.Datacenter {
|
||||
// token upgrades should only run in the primary
|
||||
return
|
||||
}
|
||||
|
||||
s.leaderRoutineManager.Start(ctx, aclUpgradeRoutineName, s.legacyACLTokenUpgrade)
|
||||
}
|
||||
|
||||
func (s *Server) stopACLUpgrade() {
|
||||
s.leaderRoutineManager.Stop(aclUpgradeRoutineName)
|
||||
}
|
||||
|
||||
func (s *Server) startACLReplication(ctx context.Context) {
|
||||
if s.InPrimaryDatacenter() {
|
||||
return
|
||||
|
|
|
@ -118,7 +118,6 @@ const (
|
|||
aclRoleReplicationRoutineName = "ACL role replication"
|
||||
aclTokenReplicationRoutineName = "ACL token replication"
|
||||
aclTokenReapingRoutineName = "acl token reaping"
|
||||
aclUpgradeRoutineName = "legacy ACL token upgrade"
|
||||
caRootPruningRoutineName = "CA root pruning"
|
||||
caRootMetricRoutineName = "CA root expiration metric"
|
||||
caSigningMetricRoutineName = "CA signing expiration metric"
|
||||
|
|
|
@ -422,14 +422,10 @@ type ACLTokenSetOptions struct {
|
|||
CAS bool
|
||||
AllowMissingPolicyAndRoleIDs bool
|
||||
ProhibitUnprivileged bool
|
||||
Legacy bool // TODO(ACL-Legacy-Compat): remove
|
||||
FromReplication bool
|
||||
}
|
||||
|
||||
func (s *Store) ACLTokenBatchSet(idx uint64, tokens structs.ACLTokens, opts ACLTokenSetOptions) error {
|
||||
if opts.Legacy {
|
||||
return fmt.Errorf("failed inserting acl token: cannot use this endpoint to persist legacy tokens")
|
||||
}
|
||||
|
||||
tx := s.db.WriteTxn(idx)
|
||||
defer tx.Abort()
|
||||
|
@ -451,25 +447,13 @@ func aclTokenSetTxn(tx WriteTxn, idx uint64, token *structs.ACLToken, opts ACLTo
|
|||
return ErrMissingACLTokenSecret
|
||||
}
|
||||
|
||||
if !opts.Legacy && token.AccessorID == "" {
|
||||
if token.AccessorID == "" {
|
||||
return ErrMissingACLTokenAccessor
|
||||
}
|
||||
|
||||
if opts.FromReplication && token.Local {
|
||||
return fmt.Errorf("Cannot replicate local tokens")
|
||||
}
|
||||
|
||||
// DEPRECATED (ACL-Legacy-Compat)
|
||||
if token.Rules != "" {
|
||||
// When we update a legacy acl token we may have to correct old HCL to
|
||||
// prevent the propagation of older syntax into the state store and
|
||||
// into in-memory representations.
|
||||
correctedRules := structs.SanitizeLegacyACLTokenRules(token.Rules)
|
||||
if correctedRules != "" {
|
||||
token.Rules = correctedRules
|
||||
}
|
||||
}
|
||||
|
||||
// Check for an existing ACL
|
||||
// DEPRECATED (ACL-Legacy-Compat) - transition to using accessor index instead of secret once v1 compat is removed
|
||||
_, existing, err := aclTokenGetFromIndex(tx, token.SecretID, "id", nil)
|
||||
|
@ -498,10 +482,6 @@ func aclTokenSetTxn(tx WriteTxn, idx uint64, token *structs.ACLToken, opts ACLTo
|
|||
}
|
||||
}
|
||||
|
||||
if opts.Legacy && original != nil {
|
||||
return fmt.Errorf("legacy tokens can not be modified")
|
||||
}
|
||||
|
||||
if err := aclTokenUpsertValidateEnterprise(tx, token, original); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -719,29 +699,6 @@ func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy, role
|
|||
return idx, result, nil
|
||||
}
|
||||
|
||||
// TODO(ACL-Legacy-Compat): remove in phase 2
|
||||
func (s *Store) ACLTokenListUpgradeable(max int) (structs.ACLTokens, <-chan struct{}, error) {
|
||||
tx := s.db.Txn(false)
|
||||
defer tx.Abort()
|
||||
|
||||
iter, err := tx.Get(tableACLTokens, "needs-upgrade", true)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed acl token listing: %v", err)
|
||||
}
|
||||
|
||||
var tokens structs.ACLTokens
|
||||
i := 0
|
||||
for token := iter.Next(); token != nil; token = iter.Next() {
|
||||
tokens = append(tokens, token.(*structs.ACLToken))
|
||||
i += 1
|
||||
if i >= max {
|
||||
return tokens, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
return tokens, iter.WatchCh(), nil
|
||||
}
|
||||
|
||||
func (s *Store) ACLTokenMinExpirationTime(local bool) (time.Time, error) {
|
||||
tx := s.db.Txn(false)
|
||||
defer tx.Abort()
|
||||
|
|
|
@ -31,9 +31,8 @@ func tokensTableSchema() *memdb.TableSchema {
|
|||
Name: tableACLTokens,
|
||||
Indexes: map[string]*memdb.IndexSchema{
|
||||
indexAccessor: {
|
||||
Name: indexAccessor,
|
||||
// DEPRECATED (ACL-Legacy-Compat) - we should not AllowMissing here once legacy compat is removed
|
||||
AllowMissing: true,
|
||||
Name: indexAccessor,
|
||||
AllowMissing: false,
|
||||
Unique: true,
|
||||
Indexer: indexerSingle[string, *structs.ACLToken]{
|
||||
readIndex: indexFromUUIDString,
|
||||
|
@ -104,23 +103,6 @@ func tokensTableSchema() *memdb.TableSchema {
|
|||
writeIndex: indexExpiresLocalFromACLToken,
|
||||
},
|
||||
},
|
||||
|
||||
// DEPRECATED (ACL-Legacy-Compat) - This index is only needed while we support upgrading v1 to v2 acls
|
||||
// This table indexes all the ACL tokens that do not have an AccessorID
|
||||
// TODO(ACL-Legacy-Compat): remove in phase 2
|
||||
"needs-upgrade": {
|
||||
Name: "needs-upgrade",
|
||||
AllowMissing: false,
|
||||
Unique: false,
|
||||
Indexer: &memdb.ConditionalIndex{
|
||||
Conditional: func(obj interface{}) (bool, error) {
|
||||
if token, ok := obj.(*structs.ACLToken); ok {
|
||||
return token.AccessorID == "", nil
|
||||
}
|
||||
return false, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -768,112 +768,6 @@ func TestStateStore_ACLTokens_UpsertBatchRead(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestStateStore_ACLTokens_ListUpgradeable(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := testACLTokensStateStore(t)
|
||||
|
||||
aclTokenSetLegacy := func(idx uint64, token *structs.ACLToken) error {
|
||||
tx := s.db.WriteTxn(idx)
|
||||
defer tx.Abort()
|
||||
|
||||
opts := ACLTokenSetOptions{Legacy: true}
|
||||
if err := aclTokenSetTxn(tx, idx, token, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
const ACLTokenTypeManagement = "management"
|
||||
|
||||
require.NoError(t, aclTokenSetLegacy(2, &structs.ACLToken{
|
||||
SecretID: "34ec8eb3-095d-417a-a937-b439af7a8e8b",
|
||||
Type: ACLTokenTypeManagement,
|
||||
}))
|
||||
|
||||
require.NoError(t, aclTokenSetLegacy(3, &structs.ACLToken{
|
||||
SecretID: "8de2dd39-134d-4cb1-950b-b7ab96ea20ba",
|
||||
Type: ACLTokenTypeManagement,
|
||||
}))
|
||||
|
||||
require.NoError(t, aclTokenSetLegacy(4, &structs.ACLToken{
|
||||
SecretID: "548bdb8e-c0d6-477b-bcc4-67fb836e9e61",
|
||||
Type: ACLTokenTypeManagement,
|
||||
}))
|
||||
|
||||
require.NoError(t, aclTokenSetLegacy(5, &structs.ACLToken{
|
||||
SecretID: "3ee33676-d9b8-4144-bf0b-92618cff438b",
|
||||
Type: ACLTokenTypeManagement,
|
||||
}))
|
||||
|
||||
require.NoError(t, aclTokenSetLegacy(6, &structs.ACLToken{
|
||||
SecretID: "fa9d658a-6e26-42ab-a5f0-1ea05c893dee",
|
||||
Type: ACLTokenTypeManagement,
|
||||
}))
|
||||
|
||||
tokens, _, err := s.ACLTokenListUpgradeable(3)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, tokens, 3)
|
||||
|
||||
tokens, _, err = s.ACLTokenListUpgradeable(10)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, tokens, 5)
|
||||
|
||||
updates := structs.ACLTokens{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "f1093997-b6c7-496d-bfb8-6b1b1895641b",
|
||||
SecretID: "34ec8eb3-095d-417a-a937-b439af7a8e8b",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{
|
||||
ID: structs.ACLPolicyGlobalManagementID,
|
||||
},
|
||||
},
|
||||
},
|
||||
&structs.ACLToken{
|
||||
AccessorID: "54866514-3cf2-4fec-8a8a-710583831834",
|
||||
SecretID: "8de2dd39-134d-4cb1-950b-b7ab96ea20ba",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{
|
||||
ID: structs.ACLPolicyGlobalManagementID,
|
||||
},
|
||||
},
|
||||
},
|
||||
&structs.ACLToken{
|
||||
AccessorID: "47eea4da-bda1-48a6-901c-3e36d2d9262f",
|
||||
SecretID: "548bdb8e-c0d6-477b-bcc4-67fb836e9e61",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{
|
||||
ID: structs.ACLPolicyGlobalManagementID,
|
||||
},
|
||||
},
|
||||
},
|
||||
&structs.ACLToken{
|
||||
AccessorID: "af1dffe5-8ac2-4282-9336-aeed9f7d951a",
|
||||
SecretID: "3ee33676-d9b8-4144-bf0b-92618cff438b",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{
|
||||
ID: structs.ACLPolicyGlobalManagementID,
|
||||
},
|
||||
},
|
||||
},
|
||||
&structs.ACLToken{
|
||||
AccessorID: "511df589-3316-4784-b503-6e25ead4d4e1",
|
||||
SecretID: "fa9d658a-6e26-42ab-a5f0-1ea05c893dee",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{
|
||||
ID: structs.ACLPolicyGlobalManagementID,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
require.NoError(t, s.ACLTokenBatchSet(7, updates, ACLTokenSetOptions{}))
|
||||
|
||||
tokens, _, err = s.ACLTokenListUpgradeable(10)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, tokens, 0)
|
||||
}
|
||||
|
||||
func TestStateStore_ACLToken_List(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := testACLTokensStateStore(t)
|
||||
|
|
|
@ -945,7 +945,6 @@ node "node1" {
|
|||
aclToken := &structs.ACLToken{
|
||||
AccessorID: tokenID,
|
||||
SecretID: token,
|
||||
Rules: "",
|
||||
}
|
||||
require.NoError(t, backend.store.ACLTokenSet(ids.Next("update"), aclToken))
|
||||
|
||||
|
|
|
@ -123,14 +123,4 @@ func init() {
|
|||
registerEndpoint("/v1/status/peers", []string{"GET"}, (*HTTPHandlers).StatusPeers)
|
||||
registerEndpoint("/v1/snapshot", []string{"GET", "PUT"}, (*HTTPHandlers).Snapshot)
|
||||
registerEndpoint("/v1/txn", []string{"PUT"}, (*HTTPHandlers).Txn)
|
||||
|
||||
// Deprecated ACL endpoints, they do nothing but return an error
|
||||
registerEndpoint("/v1/acl/create", []string{"PUT"}, (*HTTPHandlers).ACLLegacy)
|
||||
registerEndpoint("/v1/acl/update", []string{"PUT"}, (*HTTPHandlers).ACLLegacy)
|
||||
registerEndpoint("/v1/acl/destroy/", []string{"PUT"}, (*HTTPHandlers).ACLLegacy)
|
||||
registerEndpoint("/v1/acl/info/", []string{"GET"}, (*HTTPHandlers).ACLLegacy)
|
||||
registerEndpoint("/v1/acl/clone/", []string{"PUT"}, (*HTTPHandlers).ACLLegacy)
|
||||
registerEndpoint("/v1/acl/list", []string{"GET"}, (*HTTPHandlers).ACLLegacy)
|
||||
registerEndpoint("/v1/acl/rules/translate", []string{"POST"}, (*HTTPHandlers).ACLLegacy)
|
||||
registerEndpoint("/v1/acl/rules/translate/", []string{"GET"}, (*HTTPHandlers).ACLLegacy)
|
||||
}
|
||||
|
|
|
@ -114,6 +114,6 @@ func TestServerPeeringList_ACLEnforcement(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
err = getEventError(t, eventCh)
|
||||
require.Contains(t, err.Error(), "provided token lacks permission 'peering:read'")
|
||||
require.Contains(t, err.Error(), "token with AccessorID '' lacks permission 'peering:read'")
|
||||
})
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ func TestServerTrustBundle_ACLEnforcement(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
err = getEventError(t, eventCh)
|
||||
require.Contains(t, err.Error(), "provided token lacks permission 'service:write' on \"any service\"")
|
||||
require.Contains(t, err.Error(), "token with AccessorID '' lacks permission 'service:write' on \"any service\"")
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -324,7 +324,7 @@ func TestServerTrustBundleList_ACLEnforcement(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
err = getEventError(t, eventCh)
|
||||
require.Contains(t, err.Error(), "provided token lacks permission 'service:write' on \"web\"")
|
||||
require.Contains(t, err.Error(), "token with AccessorID '' lacks permission 'service:write' on \"web\"")
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -391,7 +391,7 @@ func TestServerTrustBundleList_ACLEnforcement(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
err = getEventError(t, eventCh)
|
||||
require.Contains(t, err.Error(), "provided token lacks permission 'service:write'")
|
||||
require.Contains(t, err.Error(), "token with AccessorID '' lacks permission 'service:write'")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,16 +3,17 @@ package operator
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/acl/resolver"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/hashicorp/consul/proto/pboperator"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type MockBackend struct {
|
||||
|
@ -40,7 +41,7 @@ func TestLeaderTransfer_ACL_Deny(t *testing.T) {
|
|||
|
||||
_, err := server.TransferLeader(context.Background(), &pboperator.TransferLeaderRequest{})
|
||||
require.Error(t, err)
|
||||
require.Equal(t, "Permission denied: provided token lacks permission 'operator:write'", err.Error())
|
||||
require.Equal(t, "Permission denied: token with AccessorID '' lacks permission 'operator:write'", err.Error())
|
||||
}
|
||||
|
||||
func TestLeaderTransfer_ACL_Allowed(t *testing.T) {
|
||||
|
|
|
@ -293,10 +293,6 @@ type ACLToken struct {
|
|||
// so this field is being kept to identify legacy tokens even after an auto-upgrade
|
||||
Type string `json:"-"`
|
||||
|
||||
// Rules is the V1 acl rules associated with
|
||||
// DEPRECATED (ACL-Legacy-Compat) - remove once we no longer support v1 ACL compat
|
||||
Rules string `json:",omitempty"`
|
||||
|
||||
// Whether this token is DC local. This means that it will not be synced
|
||||
// to the ACL datacenter and replicated to others.
|
||||
Local bool
|
||||
|
@ -484,7 +480,6 @@ func (t *ACLToken) SetHash(force bool) []byte {
|
|||
// Write all the user set fields
|
||||
hash.Write([]byte(t.Description))
|
||||
hash.Write([]byte(t.Type))
|
||||
hash.Write([]byte(t.Rules))
|
||||
|
||||
if t.Local {
|
||||
hash.Write([]byte("local"))
|
||||
|
@ -521,7 +516,7 @@ func (t *ACLToken) SetHash(force bool) []byte {
|
|||
|
||||
func (t *ACLToken) EstimateSize() int {
|
||||
// 41 = 16 (RaftIndex) + 8 (Hash) + 8 (ExpirationTime) + 8 (CreateTime) + 1 (Local)
|
||||
size := 41 + len(t.AccessorID) + len(t.SecretID) + len(t.Description) + len(t.Type) + len(t.Rules) + len(t.AuthMethod)
|
||||
size := 41 + len(t.AccessorID) + len(t.SecretID) + len(t.Description) + len(t.Type) + len(t.AuthMethod)
|
||||
for _, link := range t.Policies {
|
||||
size += len(link.ID) + len(link.Name)
|
||||
}
|
||||
|
@ -555,7 +550,6 @@ type ACLTokenListStub struct {
|
|||
Hash []byte
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
Legacy bool `json:",omitempty"`
|
||||
acl.EnterpriseMeta
|
||||
ACLAuthMethodEnterpriseMeta
|
||||
}
|
||||
|
@ -578,7 +572,6 @@ func (token *ACLToken) Stub() *ACLTokenListStub {
|
|||
Hash: token.Hash,
|
||||
CreateIndex: token.CreateIndex,
|
||||
ModifyIndex: token.ModifyIndex,
|
||||
Legacy: token.Rules != "",
|
||||
EnterpriseMeta: token.EnterpriseMeta,
|
||||
ACLAuthMethodEnterpriseMeta: token.ACLAuthMethodEnterpriseMeta,
|
||||
}
|
||||
|
|
|
@ -215,7 +215,6 @@ func TestStructs_ACLToken_Stub(t *testing.T) {
|
|||
require.Equal(t, token.Hash, stub.Hash)
|
||||
require.Equal(t, token.CreateIndex, stub.CreateIndex)
|
||||
require.Equal(t, token.ModifyIndex, stub.ModifyIndex)
|
||||
require.False(t, stub.Legacy)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
//go:build !consulent
|
||||
// +build !consulent
|
||||
|
||||
package structs
|
||||
|
||||
// SanitizeLegacyACLToken does nothing in the OSS builds. It does not mutate
|
||||
// the input argument at all.
|
||||
//
|
||||
// In enterprise builds this hook is necessary to support fixing old multiline
|
||||
// HCL strings in legacy token Sentinel policies into heredocs. If the token
|
||||
// was updated and previously had a Hash set, this will also update it.
|
||||
//
|
||||
// DEPRECATED (ACL-Legacy-Compat)
|
||||
func SanitizeLegacyACLToken(token *ACLToken) {
|
||||
}
|
||||
|
||||
// SanitizeLegacyACLTokenRules does nothing in the OSS builds. It always
|
||||
// returns an empty string.
|
||||
//
|
||||
// In enterprise builds this hook is necessary to support fixing any old
|
||||
// multiline HCL strings in legacy token Sentinel policies into heredocs.
|
||||
//
|
||||
// DEPRECATED (ACL-Legacy-Compat)
|
||||
func SanitizeLegacyACLTokenRules(rules string) string {
|
||||
return ""
|
||||
}
|
57
api/acl.go
57
api/acl.go
|
@ -45,8 +45,8 @@ type ACLToken struct {
|
|||
Hash []byte `json:",omitempty"`
|
||||
|
||||
// DEPRECATED (ACL-Legacy-Compat)
|
||||
// Rules will only be present for legacy tokens returned via the new APIs
|
||||
Rules string `json:",omitempty"`
|
||||
// Rules are an artifact of legacy tokens deprecated in Consul 1.4
|
||||
Rules string `json:"-"`
|
||||
|
||||
// Namespace is the namespace the ACLToken is associated with.
|
||||
// Namespaces are a Consul Enterprise feature.
|
||||
|
@ -90,7 +90,7 @@ type ACLTokenListEntry struct {
|
|||
ExpirationTime *time.Time `json:",omitempty"`
|
||||
CreateTime time.Time
|
||||
Hash []byte
|
||||
Legacy bool
|
||||
Legacy bool `json:"-"` // DEPRECATED
|
||||
|
||||
// Namespace is the namespace the ACLTokenListEntry is associated with.
|
||||
// Namespacing is a Consul Enterprise feature.
|
||||
|
@ -1042,58 +1042,19 @@ func (a *ACL) PolicyList(q *QueryOptions) ([]*ACLPolicyListEntry, *QueryMeta, er
|
|||
|
||||
// RulesTranslate translates the legacy rule syntax into the current syntax.
|
||||
//
|
||||
// Deprecated: Support for the legacy syntax translation will be removed
|
||||
// when legacy ACL support is removed.
|
||||
// Deprecated: Support for the legacy syntax translation has been removed.
|
||||
// This function always returns an error.
|
||||
func (a *ACL) RulesTranslate(rules io.Reader) (string, error) {
|
||||
r := a.c.newRequest("POST", "/v1/acl/rules/translate")
|
||||
r.body = rules
|
||||
r.header.Set("Content-Type", "text/plain")
|
||||
rtt, resp, err := a.c.doRequest(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer closeResponseBody(resp)
|
||||
if err := requireOK(resp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
qm := &QueryMeta{}
|
||||
parseQueryMeta(resp, qm)
|
||||
qm.RequestTime = rtt
|
||||
|
||||
ruleBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to read translated rule body: %v", err)
|
||||
}
|
||||
|
||||
return string(ruleBytes), nil
|
||||
return "", fmt.Errorf("Legacy ACL rules were deprecated in Consul 1.4")
|
||||
}
|
||||
|
||||
// RulesTranslateToken translates the rules associated with the legacy syntax
|
||||
// into the current syntax and returns the results.
|
||||
//
|
||||
// Deprecated: Support for the legacy syntax translation will be removed
|
||||
// when legacy ACL support is removed.
|
||||
// Deprecated: Support for the legacy syntax translation has been removed.
|
||||
// This function always returns an error.
|
||||
func (a *ACL) RulesTranslateToken(tokenID string) (string, error) {
|
||||
r := a.c.newRequest("GET", "/v1/acl/rules/translate/"+tokenID)
|
||||
rtt, resp, err := a.c.doRequest(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer closeResponseBody(resp)
|
||||
if err := requireOK(resp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
qm := &QueryMeta{}
|
||||
parseQueryMeta(resp, qm)
|
||||
qm.RequestTime = rtt
|
||||
|
||||
ruleBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to read translated rule body: %v", err)
|
||||
}
|
||||
|
||||
return string(ruleBytes), nil
|
||||
return "", fmt.Errorf("Legacy ACL tokens and rules were deprecated in Consul 1.4")
|
||||
}
|
||||
|
||||
// RoleCreate will create a new role. It is not allowed for the role parameters
|
||||
|
|
16
api/agent.go
16
api/agent.go
|
@ -1268,35 +1268,35 @@ func (a *Agent) monitor(loglevel string, logJSON bool, stopCh <-chan struct{}, q
|
|||
}
|
||||
|
||||
// UpdateACLToken updates the agent's "acl_token". See updateToken for more
|
||||
// details.
|
||||
// details. Deprecated in Consul 1.4.
|
||||
//
|
||||
// DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateDefaultACLToken for v1.4.3 and above
|
||||
func (a *Agent) UpdateACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
|
||||
return a.updateToken("acl_token", token, q)
|
||||
return nil, fmt.Errorf("Legacy ACL Tokens were deprecated in Consul 1.4")
|
||||
}
|
||||
|
||||
// UpdateACLAgentToken updates the agent's "acl_agent_token". See updateToken
|
||||
// for more details.
|
||||
// for more details. Deprecated in Consul 1.4.
|
||||
//
|
||||
// DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateAgentACLToken for v1.4.3 and above
|
||||
func (a *Agent) UpdateACLAgentToken(token string, q *WriteOptions) (*WriteMeta, error) {
|
||||
return a.updateToken("acl_agent_token", token, q)
|
||||
return nil, fmt.Errorf("Legacy ACL Tokens were deprecated in Consul 1.4")
|
||||
}
|
||||
|
||||
// UpdateACLAgentMasterToken updates the agent's "acl_agent_master_token". See
|
||||
// updateToken for more details.
|
||||
// updateToken for more details. Deprecated in Consul 1.4.
|
||||
//
|
||||
// DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateAgentMasterACLToken for v1.4.3 and above
|
||||
func (a *Agent) UpdateACLAgentMasterToken(token string, q *WriteOptions) (*WriteMeta, error) {
|
||||
return a.updateToken("acl_agent_master_token", token, q)
|
||||
return nil, fmt.Errorf("Legacy ACL Tokens were deprecated in Consul 1.4")
|
||||
}
|
||||
|
||||
// UpdateACLReplicationToken updates the agent's "acl_replication_token". See
|
||||
// updateToken for more details.
|
||||
// updateToken for more details. Deprecated in Consul 1.4.
|
||||
//
|
||||
// DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateReplicationACLToken for v1.4.3 and above
|
||||
func (a *Agent) UpdateACLReplicationToken(token string, q *WriteOptions) (*WriteMeta, error) {
|
||||
return a.updateToken("acl_replication_token", token, q)
|
||||
return nil, fmt.Errorf("Legacy ACL Tokens were deprecated in Consul 1.4")
|
||||
}
|
||||
|
||||
// UpdateDefaultACLToken updates the agent's "default" token. See updateToken
|
||||
|
|
|
@ -14,10 +14,11 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/serf/serf"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/serf/serf"
|
||||
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
"github.com/hashicorp/consul/sdk/testutil/retry"
|
||||
)
|
||||
|
@ -1543,19 +1544,19 @@ func TestAPI_AgentUpdateToken(t *testing.T) {
|
|||
t.Run("deprecated", func(t *testing.T) {
|
||||
agent := c.Agent()
|
||||
if _, err := agent.UpdateACLToken("root", nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
require.Contains(t, err.Error(), "Legacy ACL Tokens were deprecated in Consul 1.4")
|
||||
}
|
||||
|
||||
if _, err := agent.UpdateACLAgentToken("root", nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
require.Contains(t, err.Error(), "Legacy ACL Tokens were deprecated in Consul 1.4")
|
||||
}
|
||||
|
||||
if _, err := agent.UpdateACLAgentMasterToken("root", nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
require.Contains(t, err.Error(), "Legacy ACL Tokens were deprecated in Consul 1.4")
|
||||
}
|
||||
|
||||
if _, err := agent.UpdateACLReplicationToken("root", nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
require.Contains(t, err.Error(), "Legacy ACL Tokens were deprecated in Consul 1.4")
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -915,7 +915,7 @@ func TestAPI_Headers(t *testing.T) {
|
|||
`))
|
||||
// ACL support is disabled
|
||||
require.Error(t, err)
|
||||
require.Equal(t, "text/plain", request.Header.Get("Content-Type"))
|
||||
require.Equal(t, "application/octet-stream", request.Header.Get("Content-Type"))
|
||||
|
||||
_, _, err = c.Event().Fire(&UserEvent{
|
||||
Name: "test",
|
||||
|
|
|
@ -94,37 +94,6 @@ func GetPolicyIDByName(client *api.Client, name string) (string, error) {
|
|||
return policy.ID, nil
|
||||
}
|
||||
|
||||
func GetRulesFromLegacyToken(client *api.Client, tokenID string, isSecret bool) (string, error) {
|
||||
tokenID, err := GetTokenIDFromPartial(client, tokenID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var token *api.ACLToken
|
||||
if isSecret {
|
||||
qopts := api.QueryOptions{
|
||||
Token: tokenID,
|
||||
}
|
||||
token, _, err = client.ACL().TokenReadSelf(&qopts)
|
||||
} else {
|
||||
token, _, err = client.ACL().TokenRead(tokenID, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error reading token: %v", err)
|
||||
}
|
||||
|
||||
if token == nil {
|
||||
return "", fmt.Errorf("Token not found for ID")
|
||||
}
|
||||
|
||||
if token.Rules == "" {
|
||||
return "", fmt.Errorf("Token is not a legacy token with rules")
|
||||
}
|
||||
|
||||
return token.Rules, nil
|
||||
}
|
||||
|
||||
func GetRoleIDFromPartial(client *api.Client, partialID string) (string, error) {
|
||||
// the full UUID string was given
|
||||
if len(partialID) == 36 {
|
||||
|
|
|
@ -8,9 +8,7 @@ import (
|
|||
|
||||
"github.com/mitchellh/cli"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/api"
|
||||
aclhelpers "github.com/hashicorp/consul/command/acl"
|
||||
"github.com/hashicorp/consul/command/acl/policy"
|
||||
"github.com/hashicorp/consul/command/flags"
|
||||
"github.com/hashicorp/consul/command/helpers"
|
||||
|
@ -33,10 +31,8 @@ type cmd struct {
|
|||
datacenters []string
|
||||
rules string
|
||||
|
||||
fromToken string
|
||||
tokenIsSecret bool
|
||||
showMeta bool
|
||||
format string
|
||||
showMeta bool
|
||||
format string
|
||||
|
||||
testStdin io.Reader
|
||||
}
|
||||
|
@ -52,11 +48,6 @@ func (c *cmd) init() {
|
|||
c.flags.StringVar(&c.rules, "rules", "", "The policy rules. May be prefixed with '@' "+
|
||||
"to indicate that the value is a file path to load the rules from. '-' may also be "+
|
||||
"given to indicate that the rules are available on stdin")
|
||||
c.flags.StringVar(&c.fromToken, "from-token", "", "The legacy token to retrieve the rules "+
|
||||
"for when creating this policy. When this is specified no other rules should be given. "+
|
||||
"Similar to the -rules option the token to use can be loaded from stdin or from a file")
|
||||
c.flags.BoolVar(&c.tokenIsSecret, "token-secret", false, "Indicates the token provided with "+
|
||||
"-from-token is a SecretID and not an AccessorID")
|
||||
c.flags.StringVar(
|
||||
&c.format,
|
||||
"format",
|
||||
|
@ -71,29 +62,6 @@ func (c *cmd) init() {
|
|||
c.help = flags.Usage(help, c.flags)
|
||||
}
|
||||
|
||||
func (c *cmd) getRules(client *api.Client) (string, error) {
|
||||
if c.fromToken != "" && c.rules != "" {
|
||||
return "", fmt.Errorf("Cannot specify both -rules and -from-token")
|
||||
}
|
||||
|
||||
if c.fromToken != "" {
|
||||
tokenID, err := helpers.LoadDataSource(c.fromToken, c.testStdin)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Invalid -from-token value: %v", err)
|
||||
}
|
||||
|
||||
rules, err := aclhelpers.GetRulesFromLegacyToken(client, tokenID, c.tokenIsSecret)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
translated, err := acl.TranslateLegacyRules([]byte(rules))
|
||||
return string(translated), err
|
||||
}
|
||||
|
||||
return helpers.LoadDataSource(c.rules, c.testStdin)
|
||||
}
|
||||
|
||||
func (c *cmd) Run(args []string) int {
|
||||
if err := c.flags.Parse(args); err != nil {
|
||||
return 1
|
||||
|
@ -111,7 +79,7 @@ func (c *cmd) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
rules, err := c.getRules(client)
|
||||
rules, err := helpers.LoadDataSource(c.rules, c.testStdin)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error loading rules: %v", err))
|
||||
return 1
|
||||
|
@ -160,10 +128,10 @@ const (
|
|||
help = `
|
||||
Usage: consul acl policy create -name NAME [options]
|
||||
|
||||
Both the -rules and -from-token option values allow loading the value
|
||||
from stdin, a file or the raw value. To use stdin pass '-' as the value.
|
||||
To load the value from a file prefix the value with an '@'. Any other
|
||||
values will be used directly.
|
||||
The -rules option values allows loading the value from stdin, a file
|
||||
or the raw value. To use stdin pass '-' as the value. To load the value
|
||||
from a file prefix the value with an '@'. Any other values will be used
|
||||
directly.
|
||||
|
||||
Create a new policy:
|
||||
|
||||
|
@ -172,11 +140,5 @@ Usage: consul acl policy create -name NAME [options]
|
|||
-datacenter "dc1" \
|
||||
-datacenter "dc2" \
|
||||
-rules @rules.hcl
|
||||
|
||||
Creation a policy from a legacy token:
|
||||
|
||||
$ consul acl policy create -name "legacy-policy" \
|
||||
-description "Token Converted to policy" \
|
||||
-from-token "c1e34113-e7ab-4451-b1a6-336ddcc58fc6"
|
||||
`
|
||||
)
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
package rules
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/cli"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
aclhelpers "github.com/hashicorp/consul/command/acl"
|
||||
"github.com/hashicorp/consul/command/flags"
|
||||
"github.com/hashicorp/consul/command/helpers"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
tokenAccessor bool
|
||||
tokenSecret bool
|
||||
|
||||
// testStdin is the input for testing
|
||||
testStdin io.Reader
|
||||
}
|
||||
|
||||
func (c *cmd) init() {
|
||||
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
c.flags.BoolVar(&c.tokenAccessor, "token-accessor", false, "Specifies that "+
|
||||
"the TRANSLATE argument refers to a ACL token AccessorID. "+
|
||||
"The rules to translate will then be read from the retrieved token")
|
||||
|
||||
c.flags.BoolVar(&c.tokenSecret, "token-secret", false,
|
||||
"Specifies that the TRANSLATE argument refers to a ACL token SecretID. "+
|
||||
"The rules to translate will then be read from the retrieved token")
|
||||
|
||||
c.http = &flags.HTTPFlags{}
|
||||
flags.Merge(c.flags, c.http.ClientFlags())
|
||||
flags.Merge(c.flags, c.http.ServerFlags())
|
||||
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.tokenSecret && c.tokenAccessor {
|
||||
c.UI.Error(fmt.Sprintf("Error - cannot specify both -token-secret and -token-accessor"))
|
||||
return 1
|
||||
}
|
||||
|
||||
data, err := c.dataFromArgs(c.flags.Args())
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error! %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
if c.tokenSecret || c.tokenAccessor {
|
||||
client, err := c.http.APIClient()
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error connecting to Consul Agent: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Trim whitespace and newlines (e.g. from echo without -n)
|
||||
data = strings.TrimSpace(data)
|
||||
|
||||
// It is not a bug that this doesn't look at tokenAccessor. We already know that we want the rules from
|
||||
// a token and just need to tell the helper function whether it should be retrieved by its secret or accessor
|
||||
if rules, err := aclhelpers.GetRulesFromLegacyToken(client, data, c.tokenSecret); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
} else {
|
||||
data = rules
|
||||
}
|
||||
}
|
||||
|
||||
translated, err := acl.TranslateLegacyRules([]byte(data))
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error translating rules: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
c.UI.Info(string(translated))
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *cmd) dataFromArgs(args []string) (string, error) {
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return "", fmt.Errorf("Missing TRANSLATE argument")
|
||||
case 1:
|
||||
data, err := helpers.LoadDataSource(args[0], c.testStdin)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
default:
|
||||
return "", fmt.Errorf("Too many arguments: expected 1 got %d", len(args))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cmd) Synopsis() string {
|
||||
return synopsis
|
||||
}
|
||||
|
||||
func (c *cmd) Help() string {
|
||||
return flags.Usage(c.help, nil)
|
||||
}
|
||||
|
||||
const synopsis = "Translate the legacy rule syntax into the current syntax"
|
||||
const help = `
|
||||
Usage: consul acl translate-rules [options] TRANSLATE
|
||||
|
||||
Translates the legacy ACL rule syntax into the current syntax.
|
||||
|
||||
Translate rules within a file:
|
||||
|
||||
$ consul acl translate-rules @rules.hcl
|
||||
|
||||
Translate rules from stdin:
|
||||
|
||||
$ consul acl translate-rules -
|
||||
|
||||
Translate rules from a string argument:
|
||||
|
||||
$ consul acl translate-rules 'key "" { policy = "write"}'
|
||||
|
||||
Translate rules for a legacy ACL token using its SecretID passed from stdin:
|
||||
|
||||
$ consul acl translate-rules -token-secret -
|
||||
|
||||
Translate rules for a legacy ACL token using its AccessorID:
|
||||
|
||||
$ consul acl translate-rules -token-accessor 429cd746-03d5-4bbb-a83a-18b164171c89
|
||||
`
|
|
@ -1,117 +0,0 @@
|
|||
package rules
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/agent"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
"github.com/hashicorp/consul/testrpc"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRulesTranslateCommand_noTabs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if strings.ContainsRune(New(cli.NewMockUi()).Help(), '\t') {
|
||||
t.Fatal("help has tabs")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRulesTranslateCommand(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("too slow for testing.Short")
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
|
||||
testDir := testutil.TempDir(t, "acl")
|
||||
|
||||
a := agent.NewTestAgent(t, `
|
||||
primary_datacenter = "dc1"
|
||||
acl {
|
||||
enabled = true
|
||||
tokens {
|
||||
initial_management = "root"
|
||||
}
|
||||
}`)
|
||||
|
||||
defer a.Shutdown()
|
||||
testrpc.WaitForLeader(t, a.RPC, "dc1")
|
||||
stdinR, stdinW := io.Pipe()
|
||||
|
||||
ui := cli.NewMockUi()
|
||||
cmd := New(ui)
|
||||
cmd.testStdin = stdinR
|
||||
|
||||
rules := "service \"\" { policy = \"write\" }"
|
||||
expected := "service_prefix \"\" {\n policy = \"write\"\n}"
|
||||
|
||||
// From a file
|
||||
t.Run("file", func(t *testing.T) {
|
||||
err := os.WriteFile(testDir+"/rules.hcl", []byte(rules), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
args := []string{
|
||||
"-http-addr=" + a.HTTPAddr(),
|
||||
"-token=root",
|
||||
"@" + testDir + "/rules.hcl",
|
||||
}
|
||||
|
||||
code := cmd.Run(args)
|
||||
require.Equal(t, code, 0)
|
||||
require.Empty(t, ui.ErrorWriter.String())
|
||||
require.Contains(t, ui.OutputWriter.String(), expected)
|
||||
})
|
||||
|
||||
// From stdin
|
||||
t.Run("stdin", func(t *testing.T) {
|
||||
go func() {
|
||||
stdinW.Write([]byte(rules))
|
||||
stdinW.Close()
|
||||
}()
|
||||
|
||||
args := []string{
|
||||
"-http-addr=" + a.HTTPAddr(),
|
||||
"-token=root",
|
||||
"-",
|
||||
}
|
||||
|
||||
code := cmd.Run(args)
|
||||
require.Equal(t, code, 0)
|
||||
require.Empty(t, ui.ErrorWriter.String())
|
||||
require.Contains(t, ui.OutputWriter.String(), expected)
|
||||
})
|
||||
|
||||
// From arg
|
||||
t.Run("arg", func(t *testing.T) {
|
||||
args := []string{
|
||||
"-http-addr=" + a.HTTPAddr(),
|
||||
"-token=root",
|
||||
rules,
|
||||
}
|
||||
|
||||
code := cmd.Run(args)
|
||||
require.Equal(t, code, 0)
|
||||
require.Empty(t, ui.ErrorWriter.String())
|
||||
require.Contains(t, ui.OutputWriter.String(), expected)
|
||||
})
|
||||
|
||||
// cannot specify both secret and accessor
|
||||
t.Run("exclusive-options", func(t *testing.T) {
|
||||
args := []string{
|
||||
"-http-addr=" + a.HTTPAddr(),
|
||||
"-token=root",
|
||||
"-token-secret",
|
||||
"-token-accessor",
|
||||
`token "" { policy = "write" }`,
|
||||
}
|
||||
|
||||
code := cmd.Run(args)
|
||||
require.Equal(t, 1, code, 0)
|
||||
require.Equal(t, "Error - cannot specify both -token-secret and -token-accessor\n", ui.ErrorWriter.String())
|
||||
})
|
||||
}
|
|
@ -17,7 +17,6 @@ import (
|
|||
)
|
||||
|
||||
func parseCloneOutput(t *testing.T, output string) *api.ACLToken {
|
||||
// This will only work for non-legacy tokens
|
||||
re := regexp.MustCompile("AccessorID: ([a-zA-Z0-9\\-]{36})\n" +
|
||||
"SecretID: ([a-zA-Z0-9\\-]{36})\n" +
|
||||
"(?:Partition: default\n)?" +
|
||||
|
|
|
@ -106,10 +106,6 @@ func (f *prettyFormatter) FormatToken(token *api.ACLToken) (string, error) {
|
|||
buffer.WriteString(fmt.Sprintf(" %s (Datacenter: %s)\n", nodeid.NodeName, nodeid.Datacenter))
|
||||
}
|
||||
}
|
||||
if token.Rules != "" {
|
||||
buffer.WriteString(fmt.Sprintln("Rules:"))
|
||||
buffer.WriteString(fmt.Sprintln(token.Rules))
|
||||
}
|
||||
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
@ -303,7 +299,6 @@ func (f *prettyFormatter) formatTokenListEntry(token *api.ACLTokenListEntry) str
|
|||
if token.ExpirationTime != nil && !token.ExpirationTime.IsZero() {
|
||||
buffer.WriteString(fmt.Sprintf("Expiration Time: %v\n", *token.ExpirationTime))
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("Legacy: %t\n", token.Legacy))
|
||||
if f.showMeta {
|
||||
buffer.WriteString(fmt.Sprintf("Hash: %x\n", token.Hash))
|
||||
buffer.WriteString(fmt.Sprintf("Create Index: %d\n", token.CreateIndex))
|
||||
|
|
|
@ -9,8 +9,9 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
// update allows golden files to be updated based on the current output.
|
||||
|
@ -56,14 +57,6 @@ func TestFormatToken(t *testing.T) {
|
|||
ModifyIndex: 100,
|
||||
},
|
||||
},
|
||||
"legacy": {
|
||||
token: api.ACLToken{
|
||||
AccessorID: "8acc7486-ca54-4d3c-9aed-5cd85651b0ee",
|
||||
SecretID: "legacy-secret",
|
||||
Description: "legacy",
|
||||
Rules: `operator = "read"`,
|
||||
},
|
||||
},
|
||||
"complex": {
|
||||
token: api.ACLToken{
|
||||
AccessorID: "fbd2447f-7479-4329-ad13-b021d74f86ba",
|
||||
|
@ -166,16 +159,6 @@ func TestFormatTokenList(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
"legacy": {
|
||||
tokens: []*api.ACLTokenListEntry{
|
||||
{
|
||||
AccessorID: "8acc7486-ca54-4d3c-9aed-5cd85651b0ee",
|
||||
SecretID: "257ade69-748c-4022-bafd-76d27d9143f8",
|
||||
Description: "legacy",
|
||||
Legacy: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"complex": {
|
||||
tokens: []*api.ACLTokenListEntry{
|
||||
{
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"CreateIndex": 0,
|
||||
"ModifyIndex": 0,
|
||||
"AccessorID": "8acc7486-ca54-4d3c-9aed-5cd85651b0ee",
|
||||
"SecretID": "legacy-secret",
|
||||
"Description": "legacy",
|
||||
"Local": false,
|
||||
"CreateTime": "0001-01-01T00:00:00Z",
|
||||
"Rules": "operator = \"read\""
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
AccessorID: 8acc7486-ca54-4d3c-9aed-5cd85651b0ee
|
||||
SecretID: legacy-secret
|
||||
Description: legacy
|
||||
Local: false
|
||||
Create Time: 0001-01-01 00:00:00 +0000 UTC
|
||||
Hash:
|
||||
Create Index: 0
|
||||
Modify Index: 0
|
||||
Rules:
|
||||
operator = "read"
|
|
@ -1,7 +0,0 @@
|
|||
AccessorID: 8acc7486-ca54-4d3c-9aed-5cd85651b0ee
|
||||
SecretID: legacy-secret
|
||||
Description: legacy
|
||||
Local: false
|
||||
Create Time: 0001-01-01 00:00:00 +0000 UTC
|
||||
Rules:
|
||||
operator = "read"
|
|
@ -7,7 +7,6 @@
|
|||
"Description": "test token",
|
||||
"Local": false,
|
||||
"CreateTime": "2020-05-22T18:52:31Z",
|
||||
"Hash": "YWJjZGVmZ2g=",
|
||||
"Legacy": false
|
||||
"Hash": "YWJjZGVmZ2g="
|
||||
}
|
||||
]
|
|
@ -3,7 +3,6 @@ SecretID: 257ade69-748c-4022-bafd-76d27d9143f8
|
|||
Description: test token
|
||||
Local: false
|
||||
Create Time: 2020-05-22 18:52:31 +0000 UTC
|
||||
Legacy: false
|
||||
Hash: 6162636465666768
|
||||
Create Index: 42
|
||||
Modify Index: 100
|
||||
|
|
|
@ -3,4 +3,3 @@ SecretID: 257ade69-748c-4022-bafd-76d27d9143f8
|
|||
Description: test token
|
||||
Local: false
|
||||
Create Time: 2020-05-22 18:52:31 +0000 UTC
|
||||
Legacy: false
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
"ExpirationTime": "2020-05-22T19:52:31Z",
|
||||
"CreateTime": "2020-05-22T18:52:31Z",
|
||||
"Hash": "YWJjZGVmZ2g=",
|
||||
"Legacy": false,
|
||||
"Namespace": "foo",
|
||||
"AuthMethodNamespace": "baz"
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ Local: false
|
|||
Auth Method: bar (Namespace: baz)
|
||||
Create Time: 2020-05-22 18:52:31 +0000 UTC
|
||||
Expiration Time: 2020-05-22 19:52:31 +0000 UTC
|
||||
Legacy: false
|
||||
Hash: 6162636465666768
|
||||
Create Index: 5
|
||||
Modify Index: 10
|
||||
|
|
|
@ -6,7 +6,6 @@ Local: false
|
|||
Auth Method: bar (Namespace: baz)
|
||||
Create Time: 2020-05-22 18:52:31 +0000 UTC
|
||||
Expiration Time: 2020-05-22 19:52:31 +0000 UTC
|
||||
Legacy: false
|
||||
Policies:
|
||||
beb04680-815b-4d7c-9e33-3d707c24672c - hobbiton
|
||||
18788457-584c-4812-80d3-23d403148a90 - bywater
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
[
|
||||
{
|
||||
"CreateIndex": 0,
|
||||
"ModifyIndex": 0,
|
||||
"AccessorID": "8acc7486-ca54-4d3c-9aed-5cd85651b0ee",
|
||||
"SecretID": "257ade69-748c-4022-bafd-76d27d9143f8",
|
||||
"Description": "legacy",
|
||||
"Local": false,
|
||||
"CreateTime": "0001-01-01T00:00:00Z",
|
||||
"Hash": null,
|
||||
"Legacy": true
|
||||
}
|
||||
]
|
|
@ -1,9 +0,0 @@
|
|||
AccessorID: 8acc7486-ca54-4d3c-9aed-5cd85651b0ee
|
||||
SecretID: 257ade69-748c-4022-bafd-76d27d9143f8
|
||||
Description: legacy
|
||||
Local: false
|
||||
Create Time: 0001-01-01 00:00:00 +0000 UTC
|
||||
Legacy: true
|
||||
Hash:
|
||||
Create Index: 0
|
||||
Modify Index: 0
|
|
@ -1,6 +0,0 @@
|
|||
AccessorID: 8acc7486-ca54-4d3c-9aed-5cd85651b0ee
|
||||
SecretID: 257ade69-748c-4022-bafd-76d27d9143f8
|
||||
Description: legacy
|
||||
Local: false
|
||||
Create Time: 0001-01-01 00:00:00 +0000 UTC
|
||||
Legacy: true
|
|
@ -38,7 +38,6 @@ type cmd struct {
|
|||
mergeServiceIdents bool
|
||||
mergeNodeIdents bool
|
||||
showMeta bool
|
||||
upgradeLegacy bool
|
||||
format string
|
||||
}
|
||||
|
||||
|
@ -72,11 +71,6 @@ func (c *cmd) init() {
|
|||
c.flags.Var((*flags.AppendSliceValue)(&c.nodeIdents), "node-identity", "Name of a "+
|
||||
"node identity to use for this token. May be specified multiple times. Format is "+
|
||||
"NODENAME:DATACENTER")
|
||||
c.flags.BoolVar(&c.upgradeLegacy, "upgrade-legacy", false, "Add new polices "+
|
||||
"to a legacy token replacing all existing rules. This will cause the legacy "+
|
||||
"token to behave exactly like a new token but keep the same Secret.\n"+
|
||||
"WARNING: you must ensure that the new policy or policies specified grant "+
|
||||
"equivalent or appropriate access for the existing clients using this token.")
|
||||
c.flags.StringVar(
|
||||
&c.format,
|
||||
"format",
|
||||
|
@ -119,18 +113,6 @@ func (c *cmd) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
if c.upgradeLegacy {
|
||||
if t.Rules == "" {
|
||||
// This is just for convenience it should actually be harmless to allow it
|
||||
// to go through anyway.
|
||||
c.UI.Error(fmt.Sprintf("Can't use -upgrade-legacy on a non-legacy token"))
|
||||
return 1
|
||||
}
|
||||
// Reset the rules to nothing forcing this to be updated as a non-legacy
|
||||
// token but with same secret.
|
||||
t.Rules = ""
|
||||
}
|
||||
|
||||
if c.description != "" {
|
||||
// Only update description if the user specified a new one. This does make
|
||||
// it impossible to completely clear descriptions from CLI but that seems
|
||||
|
|
|
@ -57,9 +57,6 @@ func TestTokenUpdateCommand(t *testing.T) {
|
|||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We fetch the legacy token later to give server time to async background
|
||||
// upgrade it.
|
||||
|
||||
run := func(t *testing.T, args []string) *api.ACLToken {
|
||||
ui := cli.NewMockUi()
|
||||
cmd := New(ui)
|
||||
|
|
|
@ -2,7 +2,6 @@ package command
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hashicorp/consul/command/operator/raft/transferleader"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
@ -34,7 +33,6 @@ import (
|
|||
aclrlist "github.com/hashicorp/consul/command/acl/role/list"
|
||||
aclrread "github.com/hashicorp/consul/command/acl/role/read"
|
||||
aclrupdate "github.com/hashicorp/consul/command/acl/role/update"
|
||||
aclrules "github.com/hashicorp/consul/command/acl/rules"
|
||||
acltoken "github.com/hashicorp/consul/command/acl/token"
|
||||
acltclone "github.com/hashicorp/consul/command/acl/token/clone"
|
||||
acltcreate "github.com/hashicorp/consul/command/acl/token/create"
|
||||
|
@ -97,6 +95,7 @@ import (
|
|||
operraft "github.com/hashicorp/consul/command/operator/raft"
|
||||
operraftlist "github.com/hashicorp/consul/command/operator/raft/listpeers"
|
||||
operraftremove "github.com/hashicorp/consul/command/operator/raft/removepeer"
|
||||
"github.com/hashicorp/consul/command/operator/raft/transferleader"
|
||||
"github.com/hashicorp/consul/command/peering"
|
||||
peerdelete "github.com/hashicorp/consul/command/peering/delete"
|
||||
peerestablish "github.com/hashicorp/consul/command/peering/establish"
|
||||
|
@ -139,7 +138,6 @@ func RegisteredCommands(ui cli.Ui) map[string]mcli.CommandFactory {
|
|||
entry{"acl policy read", func(ui cli.Ui) (cli.Command, error) { return aclpread.New(ui), nil }},
|
||||
entry{"acl policy update", func(ui cli.Ui) (cli.Command, error) { return aclpupdate.New(ui), nil }},
|
||||
entry{"acl policy delete", func(ui cli.Ui) (cli.Command, error) { return aclpdelete.New(ui), nil }},
|
||||
entry{"acl translate-rules", func(ui cli.Ui) (cli.Command, error) { return aclrules.New(ui), nil }},
|
||||
entry{"acl set-agent-token", func(ui cli.Ui) (cli.Command, error) { return aclagent.New(ui), nil }},
|
||||
entry{"acl token", func(cli.Ui) (cli.Command, error) { return acltoken.New(), nil }},
|
||||
entry{"acl token create", func(ui cli.Ui) (cli.Command, error) { return acltcreate.New(ui), nil }},
|
||||
|
|
|
@ -6,9 +6,7 @@ description: The /acl endpoints manage the Consul's ACL system.
|
|||
|
||||
# ACL HTTP API
|
||||
|
||||
-> **1.4.0+:** This API documentation is for Consul versions 1.4.0 and later. The documentation for the legacy ACL API is [here](/consul/api-docs/acl/legacy).
|
||||
|
||||
The `/acl` endpoints are used to manage ACL tokens and policies in Consul, [bootstrap the ACL system](#bootstrap-acls), [check ACL replication status](#check-acl-replication), and [translate rules](#translate-rules). There are additional pages for managing [tokens](/consul/api-docs/acl/tokens) and [policies](/consul/api-docs/acl/policies) with the `/acl` endpoints.
|
||||
The `/acl` endpoints are used to manage ACL tokens and policies in Consul, [bootstrap the ACL system](#bootstrap-acls) and [check ACL replication status](#check-acl-replication). There are additional pages for managing [tokens](/consul/api-docs/acl/tokens) and [policies](/consul/api-docs/acl/policies) with the `/acl` endpoints.
|
||||
|
||||
For more information on how to setup ACLs, please check
|
||||
the [ACL tutorial](/consul/tutorials/security/access-control-setup-production).
|
||||
|
@ -50,12 +48,8 @@ $ curl \
|
|||
|
||||
### Sample Response
|
||||
|
||||
-> **Deprecated** - The `ID` field in the response is for legacy compatibility and is a copy of the `SecretID` field. New
|
||||
applications should ignore the `ID` field as it may be removed in a future major Consul version.
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": "527347d3-9653-07dc-adc0-598b8f2b0f4d",
|
||||
"AccessorID": "b5b1a918-50bc-fc46-dec2-d481359da4e3",
|
||||
"SecretID": "527347d3-9653-07dc-adc0-598b8f2b0f4d",
|
||||
"Description": "Bootstrap Token (Global Management)",
|
||||
|
@ -163,18 +157,13 @@ $ curl \
|
|||
|
||||
- `ReplicationType` - The type of replication that is currently in use.
|
||||
|
||||
- `legacy` - (removed in Consul 1.11.0) ACL replication is in legacy mode and is replicating legacy ACL tokens.
|
||||
|
||||
- `policies` - ACL replication is only replicating policies as token replication
|
||||
is disabled.
|
||||
|
||||
- `tokens` - ACL replication is replicating both policies and tokens.
|
||||
|
||||
- `ReplicatedIndex` - The last index that was successfully replicated. Which data
|
||||
the replicated index refers to depends on the replication type. For `legacy`
|
||||
replication this can be compared with the value of the `X-Consul-Index` header
|
||||
returned by the [`/v1/acl/list`](/consul/api-docs/acl/legacy#list-acls) endpoint to
|
||||
determine if the replication process has gotten all available ACLs. When in either
|
||||
the replicated index refers to depends on the replication type. When in either
|
||||
`tokens` or `policies` mode, this index can be compared with the value of the
|
||||
`X-Consul-Index` header returned by the [`/v1/acl/policies`](/consul/api-docs/acl/policies#list-policies)
|
||||
endpoint to determine if the policy replication process has gotten all available
|
||||
|
@ -201,94 +190,6 @@ $ curl \
|
|||
- `LastErrorMessage` - The last error message produced at the time of `LastError`.
|
||||
An empty string indicates that no sync has resulted in an error.
|
||||
|
||||
## Translate Rules
|
||||
|
||||
-> **Deprecated** - This endpoint was removed in Consul 1.11.0.
|
||||
This endpoint was introduced in Consul 1.4.0 for migration from the previous ACL system.
|
||||
|
||||
This endpoint translates the legacy rule syntax into the latest syntax. It is intended
|
||||
to be used by operators managing Consul's ACLs and performing legacy token to new policy
|
||||
migrations.
|
||||
|
||||
| Method | Path | Produces |
|
||||
| ------ | ---------------------- | ------------ |
|
||||
| `POST` | `/acl/rules/translate` | `text/plain` |
|
||||
|
||||
The table below shows this endpoint's support for
|
||||
[blocking queries](/consul/api-docs/features/blocking),
|
||||
[consistency modes](/consul/api-docs/features/consistency),
|
||||
[agent caching](/consul/api-docs/features/caching), and
|
||||
[required ACLs](/consul/api-docs/api-structure#authentication).
|
||||
|
||||
| Blocking Queries | Consistency Modes | Agent Caching | ACL Required |
|
||||
| ---------------- | ----------------- | ------------- | ------------ |
|
||||
| `NO` | `none` | `none` | `acl:read` |
|
||||
|
||||
The corresponding CLI command is [`consul acl translate-rules`](/consul/commands/acl/translate-rules).
|
||||
|
||||
### Sample Payload
|
||||
|
||||
```hcl
|
||||
agent "" {
|
||||
policy = "read"
|
||||
}
|
||||
```
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl --request POST --data @rules.hcl http://127.0.0.1:8500/v1/acl/rules/translate
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```hcl
|
||||
agent_prefix "" {
|
||||
policy = "read"
|
||||
}
|
||||
```
|
||||
|
||||
## Translate a Legacy Token's Rules
|
||||
|
||||
-> **Deprecated** - This endpoint was removed in Consul 1.11.0.
|
||||
This endpoint was introduced in Consul 1.4.0 for migration from the previous ACL system.
|
||||
|
||||
This endpoint translates the legacy rules embedded within a legacy ACL into the latest
|
||||
syntax. It is intended to be used by operators managing Consul's ACLs and performing
|
||||
legacy token to new policy migrations. Note that this API requires the auto-generated
|
||||
Accessor ID of the legacy token. This ID can be retrieved using the
|
||||
[`/v1/acl/token/self`](/consul/api-docs/acl/tokens#read-self-token) endpoint.
|
||||
|
||||
| Method | Path | Produces |
|
||||
| ------ | ----------------------------------- | ------------ |
|
||||
| `GET` | `/acl/rules/translate/:accessor_id` | `text/plain` |
|
||||
|
||||
The table below shows this endpoint's support for
|
||||
[blocking queries](/consul/api-docs/features/blocking),
|
||||
[consistency modes](/consul/api-docs/features/consistency),
|
||||
[agent caching](/consul/api-docs/features/caching), and
|
||||
[required ACLs](/consul/api-docs/api-structure#authentication).
|
||||
|
||||
| Blocking Queries | Consistency Modes | Agent Caching | ACL Required |
|
||||
| ---------------- | ----------------- | ------------- | ------------ |
|
||||
| `NO` | `none` | `none` | `acl:read` |
|
||||
|
||||
The corresponding CLI command is [`consul acl translate-rules`](/consul/commands/acl/translate-rules).
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl --request GET http://127.0.0.1:8500/v1/acl/rules/translate/4f48f7e6-9359-4890-8e67-6144a962b0a5
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```hcl
|
||||
agent_prefix "" {
|
||||
policy = "read"
|
||||
}
|
||||
```
|
||||
|
||||
## Login to Auth Method
|
||||
|
||||
This endpoint was added in Consul 1.5.0 and is used to exchange an [auth
|
||||
|
|
|
@ -1,299 +0,0 @@
|
|||
---
|
||||
layout: api
|
||||
page_title: Legacy ACLs - HTTP API
|
||||
description: >-
|
||||
The legacy /acl endpoints to create, update, destroy, and query legacy ACL tokens in
|
||||
Consul.
|
||||
---
|
||||
|
||||
# ACL HTTP API
|
||||
|
||||
-> **The legacy ACL system was deprecated in Consul 1.4.0 and removed in Consul 1.11.0.** It's _strongly_
|
||||
recommended you do not build anything using the legacy system and use
|
||||
the new ACL [Token](/consul/api-docs/acl/tokens) and [Policy](/consul/api-docs/acl/policies) APIs instead.
|
||||
|
||||
The legacy `/acl` endpoints to create, update, destroy, and query legacy ACL tokens in Consul.
|
||||
|
||||
For more information about ACLs, please check the [ACL tutorial](/consul/tutorials/security/access-control-setup-production).
|
||||
|
||||
## Create ACL Token
|
||||
|
||||
This endpoint makes a new ACL token.
|
||||
|
||||
| Method | Path | Produces |
|
||||
| ------ | ------------- | ------------------ |
|
||||
| `PUT` | `/acl/create` | `application/json` |
|
||||
|
||||
The table below shows this endpoint's support for
|
||||
[blocking queries](/consul/api-docs/features/blocking),
|
||||
[consistency modes](/consul/api-docs/features/consistency),
|
||||
[agent caching](/consul/api-docs/features/caching), and
|
||||
[required ACLs](/consul/api-docs/api-structure#authentication).
|
||||
|
||||
| Blocking Queries | Consistency Modes | Agent Caching | ACL Required |
|
||||
| ---------------- | ----------------- | ------------- | ------------ |
|
||||
| `NO` | `none` | `none` | `management` |
|
||||
|
||||
### Parameters
|
||||
|
||||
- `ID` `(string: "")` - Specifies the ID of the ACL. If not provided, a UUID is
|
||||
generated.
|
||||
|
||||
- `Name` `(string: "")` - Specifies a human-friendly name for the ACL token.
|
||||
|
||||
- `Type` `(string: "client")` - Specifies the type of ACL token. Valid values
|
||||
are: `client` and `management`.
|
||||
|
||||
- `Rules` `(string: "")` - Specifies rules for this ACL token. The format of the
|
||||
`Rules` property is detailed in the [ACL Rule documentation](/consul/docs/security/acl/acl-rules).
|
||||
|
||||
### Sample Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"Name": "my-app-token",
|
||||
"Type": "client",
|
||||
"Rules": ""
|
||||
}
|
||||
```
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
--request PUT \
|
||||
--data @payload.json \
|
||||
http://127.0.0.1:8500/v1/acl/create
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": "adf4238a-882b-9ddc-4a9d-5b6758e4159e"
|
||||
}
|
||||
```
|
||||
|
||||
## Update ACL Token
|
||||
|
||||
This endpoint is used to modify the policy for a given ACL token. Instead of
|
||||
generating a new token ID, the `ID` field must be provided.
|
||||
|
||||
| Method | Path | Produces |
|
||||
| ------ | ------------- | ------------------ |
|
||||
| `PUT` | `/acl/update` | `application/json` |
|
||||
|
||||
The table below shows this endpoint's support for
|
||||
[blocking queries](/consul/api-docs/features/blocking),
|
||||
[consistency modes](/consul/api-docs/features/consistency),
|
||||
[agent caching](/consul/api-docs/features/caching), and
|
||||
[required ACLs](/consul/api-docs/api-structure#authentication).
|
||||
|
||||
| Blocking Queries | Consistency Modes | Agent Caching | ACL Required |
|
||||
| ---------------- | ----------------- | ------------- | ------------ |
|
||||
| `NO` | `none` | `none` | `management` |
|
||||
|
||||
### Parameters
|
||||
|
||||
The parameters are the same as the _create_ endpoint, except the `ID` field is
|
||||
required.
|
||||
|
||||
### Sample Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": "adf4238a-882b-9ddc-4a9d-5b6758e4159e",
|
||||
"Name": "my-app-token-updated",
|
||||
"Type": "client",
|
||||
"Rules": "# New Rules"
|
||||
}
|
||||
```
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
--request PUT \
|
||||
--data @payload.json \
|
||||
http://127.0.0.1:8500/v1/acl/update
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": "adf4238a-882b-9ddc-4a9d-5b6758e4159e"
|
||||
}
|
||||
```
|
||||
|
||||
## Delete ACL Token
|
||||
|
||||
This endpoint deletes an ACL token with the given ID.
|
||||
|
||||
| Method | Path | Produces |
|
||||
| ------ | -------------------- | ------------------ |
|
||||
| `PUT` | `/acl/destroy/:uuid` | `application/json` |
|
||||
|
||||
Even though the return type is application/json, the value is either true or
|
||||
false, indicating whether the delete succeeded.
|
||||
|
||||
The table below shows this endpoint's support for
|
||||
[blocking queries](/consul/api-docs/features/blocking),
|
||||
[consistency modes](/consul/api-docs/features/consistency),
|
||||
[agent caching](/consul/api-docs/features/caching), and
|
||||
[required ACLs](/consul/api-docs/api-structure#authentication).
|
||||
|
||||
| Blocking Queries | Consistency Modes | Agent Caching | ACL Required |
|
||||
| ---------------- | ----------------- | ------------- | ------------ |
|
||||
| `NO` | `none` | `none` | `management` |
|
||||
|
||||
### Parameters
|
||||
|
||||
- `uuid` `(string: <required>)` - Specifies the UUID of the ACL token to
|
||||
destroy. This is required and is specified as part of the URL path.
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
--request PUT \
|
||||
http://127.0.0.1:8500/v1/acl/destroy/8f246b77-f3e1-ff88-5b48-8ec93abf3e05
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```json
|
||||
true
|
||||
```
|
||||
|
||||
## Read ACL Token
|
||||
|
||||
This endpoint reads an ACL token with the given ID.
|
||||
|
||||
| Method | Path | Produces |
|
||||
| ------ | ----------------- | ------------------ |
|
||||
| `GET` | `/acl/info/:uuid` | `application/json` |
|
||||
|
||||
The table below shows this endpoint's support for
|
||||
[blocking queries](/consul/api-docs/features/blocking),
|
||||
[consistency modes](/consul/api-docs/features/consistency),
|
||||
[agent caching](/consul/api-docs/features/caching), and
|
||||
[required ACLs](/consul/api-docs/api-structure#authentication).
|
||||
|
||||
| Blocking Queries | Consistency Modes | Agent Caching | ACL Required |
|
||||
| ---------------- | ----------------- | ------------- | ------------ |
|
||||
| `YES` | `all` | `none` | `none` |
|
||||
|
||||
Note: No ACL is required because the ACL is specified in the URL path.
|
||||
|
||||
### Parameters
|
||||
|
||||
- `uuid` `(string: <required>)` - Specifies the UUID of the ACL token to
|
||||
read. This is required and is specified as part of the URL path.
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
http://127.0.0.1:8500/v1/acl/info/8f246b77-f3e1-ff88-5b48-8ec93abf3e05
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"CreateIndex": 3,
|
||||
"ModifyIndex": 3,
|
||||
"ID": "8f246b77-f3e1-ff88-5b48-8ec93abf3e05",
|
||||
"Name": "Client Token",
|
||||
"Type": "client",
|
||||
"Rules": "..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Clone ACL Token
|
||||
|
||||
This endpoint clones an ACL and returns a new token `ID`. This allows a token to
|
||||
serve as a template for others, making it simple to generate new tokens without
|
||||
complex rule management.
|
||||
|
||||
| Method | Path | Produces |
|
||||
| ------ | ------------------ | ------------------ |
|
||||
| `PUT` | `/acl/clone/:uuid` | `application/json` |
|
||||
|
||||
The table below shows this endpoint's support for
|
||||
[blocking queries](/consul/api-docs/features/blocking),
|
||||
[consistency modes](/consul/api-docs/features/consistency),
|
||||
[agent caching](/consul/api-docs/features/caching), and
|
||||
[required ACLs](/consul/api-docs/api-structure#authentication).
|
||||
|
||||
| Blocking Queries | Consistency Modes | Agent Caching | ACL Required |
|
||||
| ---------------- | ----------------- | ------------- | ------------ |
|
||||
| `NO` | `none` | `none` | `management` |
|
||||
|
||||
### Parameters
|
||||
|
||||
- `uuid` `(string: <required>)` - Specifies the UUID of the ACL token to
|
||||
be cloned. This is required and is specified as part of the URL path.
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
--request PUT \
|
||||
http://127.0.0.1:8500/v1/acl/clone/8f246b77-f3e1-ff88-5b48-8ec93abf3e05
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": "adf4238a-882b-9ddc-4a9d-5b6758e4159e"
|
||||
}
|
||||
```
|
||||
|
||||
## List ACLs
|
||||
|
||||
This endpoint lists all the active ACL tokens.
|
||||
|
||||
| Method | Path | Produces |
|
||||
| ------ | ----------- | ------------------ |
|
||||
| `GET` | `/acl/list` | `application/json` |
|
||||
|
||||
The table below shows this endpoint's support for
|
||||
[blocking queries](/consul/api-docs/features/blocking),
|
||||
[consistency modes](/consul/api-docs/features/consistency),
|
||||
[agent caching](/consul/api-docs/features/caching), and
|
||||
[required ACLs](/consul/api-docs/api-structure#authentication).
|
||||
|
||||
| Blocking Queries | Consistency Modes | Agent Caching | ACL Required |
|
||||
| ---------------- | ----------------- | ------------- | ------------ |
|
||||
| `YES` | `all` | `none` | `management` |
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
http://127.0.0.1:8500/v1/acl/list
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"CreateIndex": 3,
|
||||
"ModifyIndex": 3,
|
||||
"ID": "8f246b77-f3e1-ff88-5b48-8ec93abf3e05",
|
||||
"Name": "Client Token",
|
||||
"Type": "client",
|
||||
"Rules": "..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Check ACL Replication
|
||||
|
||||
The check ACL replication endpoint has not changed between the legacy system and the new system. Review the [latest documentation](/consul/api-docs/acl#check-acl-replication) to learn more about this endpoint.
|
|
@ -6,8 +6,6 @@ description: The /acl/policy endpoints manage Consul's ACL policies.
|
|||
|
||||
# ACL Policy HTTP API
|
||||
|
||||
-> **1.4.0+:** The APIs are available in Consul versions 1.4.0 and later. The documentation for the legacy ACL API is [here](/consul/api-docs/acl/legacy).
|
||||
|
||||
The `/acl/policy` endpoints [create](#create-a-policy), [read](#read-a-policy),
|
||||
[update](#update-a-policy), [list](#list-policies) and
|
||||
[delete](#delete-a-policy) ACL policies in Consul.
|
||||
|
|
|
@ -6,8 +6,6 @@ description: The /acl/token endpoints manage Consul's ACL Tokens.
|
|||
|
||||
# ACL Token HTTP API
|
||||
|
||||
-> **1.4.0+:** The APIs are available in Consul versions 1.4.0 and later. The documentation for the legacy ACL API is [here](/consul/api-docs/acl/legacy).
|
||||
|
||||
The `/acl/token` endpoints [create](#create-a-token), [read](#read-a-token),
|
||||
[update](#update-a-token), [list](#list-tokens), [clone](#clone-a-token) and [delete](#delete-a-token) ACL tokens in Consul.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
layout: commands
|
||||
page_title: 'Commands: ACL'
|
||||
description: >-
|
||||
The `consul acl` command exposes top-level commands for bootstrapping the ACL system, managing tokens and policies, translating legacy rules, and setting the tokens for use by an agent.
|
||||
The `consul acl` command exposes top-level commands for bootstrapping the ACL system, managing tokens and policies, and setting the tokens for use by an agent.
|
||||
---
|
||||
|
||||
# Consul ACLs
|
||||
|
@ -11,7 +11,7 @@ Command: `consul acl`
|
|||
|
||||
The `acl` command is used to interact with Consul's ACLs via the command
|
||||
line. It exposes top-level commands for bootstrapping the ACL system,
|
||||
managing tokens and policies, translating legacy rules, and setting the
|
||||
managing tokens and policies, and setting the
|
||||
tokens for use by an agent.
|
||||
|
||||
ACLs are also accessible via the [HTTP API](/consul/api-docs/acl).
|
||||
|
@ -101,8 +101,6 @@ Subcommands:
|
|||
role Manage Consul's ACL roles
|
||||
set-agent-token Assign tokens for the Consul Agent's usage
|
||||
token Manage Consul's ACL tokens
|
||||
translate-rules Translate the legacy rule syntax into the current syntax
|
||||
|
||||
```
|
||||
|
||||
For more information, examples, and usage about a subcommand, click on the name
|
||||
|
|
|
@ -11,14 +11,10 @@ Command: `consul acl policy create`
|
|||
|
||||
Corresponding HTTP API Endpoint: [\[PUT\] /v1/acl/policy](/consul/api-docs/acl/policies#create-a-policy)
|
||||
|
||||
The `acl policy create` command creates new policies. The policies rules can either be set explicitly or the
|
||||
`-from-token` parameter may be used to load the rules from a legacy ACL token. When loading
|
||||
the rules from an existing legacy ACL token, the rules get translated from the legacy syntax
|
||||
to the new syntax.
|
||||
The `acl policy create` command creates new policies.
|
||||
|
||||
Both the `-rules` and `-from-token` parameter values allow loading the value
|
||||
from stdin, a file or the raw value. To use stdin pass `-` as the value.
|
||||
To load the value from a file prefix the value with an `@`. Any other
|
||||
The `-rules` parameter value allow loading the value from stdin, a file or the raw value.
|
||||
To use stdin pass `-` as the value. To load the value from a file prefix the value with an `@`. Any other
|
||||
values will be used directly.
|
||||
|
||||
The table below shows this command's [required ACLs](/consul/api-docs/api-structure#authentication). Configuration of
|
||||
|
@ -29,10 +25,6 @@ are not supported from commands, but may be from the corresponding HTTP endpoint
|
|||
| ------------ |
|
||||
| `acl:write` |
|
||||
|
||||
-> **Deprecated:** The `-from-token` and `-token-secret` arguments exist only as a convenience
|
||||
to make legacy ACL migration easier. These will be removed in a future major release when
|
||||
support for the legacy ACL system is removed.
|
||||
|
||||
## Usage
|
||||
|
||||
Usage: `consul acl policy create [options] [args]`
|
||||
|
@ -41,11 +33,6 @@ Usage: `consul acl policy create [options] [args]`
|
|||
|
||||
- `-description=<string>` - A description of the policy.
|
||||
|
||||
- `-from-token=<string>` - The legacy token to retrieve the rules for when creating this
|
||||
policy. When this is specified no other rules should be given.
|
||||
Similar to the -rules option the token to use can be loaded from
|
||||
stdin or from a file.
|
||||
|
||||
- `-meta` - Indicates that policy metadata such as the content hash and raft
|
||||
indices should be shown for each entry.
|
||||
|
||||
|
@ -55,9 +42,6 @@ Usage: `consul acl policy create [options] [args]`
|
|||
value is a file path to load the rules from. '-' may also be given
|
||||
to indicate that the rules are available on stdin.
|
||||
|
||||
- `-token-secret` - Indicates the token provided with -from-token is a SecretID and not
|
||||
an AccessorID.
|
||||
|
||||
- `-valid-datacenter=<value>` - Datacenter that the policy should be valid within.
|
||||
This flag may be specified multiple times.
|
||||
|
||||
|
@ -105,20 +89,3 @@ service_prefix "" {
|
|||
}
|
||||
```
|
||||
|
||||
Create a new policy with rules equivalent to that of a legacy ACL token:
|
||||
|
||||
```shell-session
|
||||
$ consul acl policy create -name "node-services-read" -from-token 5793a5ce -description "Can read any node and service"
|
||||
ID: 06acc965-df4b-5a99-58cb-3250930c6324
|
||||
Name: node-services-read
|
||||
Description: Can read any node and service
|
||||
Datacenters:
|
||||
Rules:
|
||||
service_prefix "" {
|
||||
policy = "read"
|
||||
}
|
||||
|
||||
node_prefix "" {
|
||||
policy = "read"
|
||||
}
|
||||
```
|
||||
|
|
|
@ -54,7 +54,6 @@ AccessorID: 4d123dff-f460-73c3-02c4-8dd64d136e01
|
|||
Description: Bootstrap Token (Global Management)
|
||||
Local: false
|
||||
Create Time: 2018-10-22 11:27:04.479026 -0400 EDT
|
||||
Legacy: false
|
||||
Policies:
|
||||
00000000-0000-0000-0000-000000000001 - global-management
|
||||
|
||||
|
@ -62,7 +61,6 @@ AccessorID: 00000000-0000-0000-0000-000000000002
|
|||
Description: Anonymous Token
|
||||
Local: false
|
||||
Create Time: 0001-01-01 00:00:00 +0000 UTC
|
||||
Legacy: false
|
||||
Policies:
|
||||
06acc965-df4b-5a99-58cb-3250930c6324 - node-services-read
|
||||
|
||||
|
@ -70,7 +68,6 @@ AccessorID: 986193b5-e2b5-eb26-6264-b524ea60cc6d
|
|||
Description: WonderToken
|
||||
Local: false
|
||||
Create Time: 2018-10-22 15:33:39.01789 -0400 EDT
|
||||
Legacy: false
|
||||
Policies:
|
||||
06acc965-df4b-5a99-58cb-3250930c6324 - node-services-read
|
||||
Service Identities:
|
||||
|
@ -80,7 +77,6 @@ AccessorID: def4895d-eeb9-f78a-fbb9-2a15a568af31
|
|||
Description: Node 1 agent token
|
||||
Local: false
|
||||
Create Time: 2020-12-22 04:19:30.552528733 +0000 UTC
|
||||
Legacy: false
|
||||
Node Identities:
|
||||
node1 (Datacenter: dc1)
|
||||
```
|
||||
|
|
|
@ -60,16 +60,6 @@ Usage: `consul acl token update [options]`
|
|||
- `-service-identity=<value>` - Name of a service identity to use for this
|
||||
token. May be specified multiple times. Format is the `SERVICENAME` or
|
||||
`SERVICENAME:DATACENTER1,DATACENTER2,...`
|
||||
- `-upgrade-legacy` - Add new polices to a legacy token replacing all existing
|
||||
rules. This will cause the legacy token to behave exactly like a new token
|
||||
but keep the same secret.
|
||||
|
||||
~> When upgrading a legacy token you must ensure that the new policy or policies
|
||||
specified grant equivalent or appropriate access for the existing clients using
|
||||
this token. You can find examples on how to use the parameter in the [legacy
|
||||
token
|
||||
migration](https://learn.hashicorp.com/consul/day-2-agent-authentication/migrate-acl-tokens)
|
||||
guide.
|
||||
|
||||
- `-format={pretty|json}` - Command output format. The default value is `pretty`.
|
||||
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
---
|
||||
layout: commands
|
||||
page_title: 'Commands: ACL Translate Rules'
|
||||
description: >-
|
||||
The `consul acl translate-rules` command translates the legacy ACL rules into the new syntax when migrating from the legacy ACL system.
|
||||
---
|
||||
|
||||
-> **Deprecated:** This command exists only as a convenience to make legacy ACL migration easier.
|
||||
It will be removed in a future major release when support for the legacy ACL system is removed.
|
||||
|
||||
# Consul ACL Translate Rules
|
||||
|
||||
Command: `consul acl translate-rules`
|
||||
|
||||
Corresponding HTTP API Endpoint: [\[GET\] /v1/acl/rules/translate/:accessor_id](/consul/api-docs/acl#translate-a-legacy-token-s-rules)
|
||||
|
||||
This command translates the legacy ACL rule syntax into the new syntax.
|
||||
|
||||
The table below shows this command's [required ACLs](/consul/api-docs/api-structure#authentication). Configuration of
|
||||
[blocking queries](/consul/api-docs/features/blocking) and [agent caching](/consul/api-docs/features/caching)
|
||||
are not supported from commands, but may be from the corresponding HTTP endpoint.
|
||||
|
||||
| ACL Required |
|
||||
| ------------ |
|
||||
| `acl:read` |
|
||||
|
||||
### Usage
|
||||
|
||||
Usage: `consul acl translate-rules [options] TRANSLATE`
|
||||
|
||||
#### Command Options
|
||||
|
||||
- `TRANSLATE` - The rules to translate. If `-` is used, then
|
||||
the rules will be read from stdin. If `@` is prefixed to
|
||||
the value then the value is considered to be a file and
|
||||
the rules will be read from that file.
|
||||
|
||||
- `-token-secret` - Specifies that what the `TRANSLATE` argument
|
||||
holds is not a rule set but rather the token secret ID of a
|
||||
legacy ACL token that holds the rule set.
|
||||
|
||||
- `-token-accessor` - Specifies that what the `TRANSLATE` argument
|
||||
holds is not a rule set but rather the token accessor ID of a
|
||||
legacy ACL token that holds the rule set.
|
||||
|
||||
#### API Options
|
||||
|
||||
@include 'http_api_options_client.mdx'
|
||||
|
||||
@include 'http_api_options_server.mdx'
|
||||
|
||||
### Examples
|
||||
|
||||
Translate rules within a file:
|
||||
|
||||
```shell-session
|
||||
$ consul acl translate-rules @rules.hcl
|
||||
```
|
||||
|
||||
Translate rules from stdin:
|
||||
|
||||
```shell-session
|
||||
$ consul acl translate-rules -
|
||||
```
|
||||
|
||||
Translate rules from a string argument:
|
||||
|
||||
```shell-session
|
||||
$ consul acl translate-rules 'key "" { policy = "write"}'
|
||||
```
|
||||
|
||||
Translate rules for a legacy ACL token using its SecretID passed from stdin:
|
||||
|
||||
```shell-session
|
||||
$ consul acl translate-rules --token-secret -
|
||||
```
|
||||
|
||||
Translate rules for a legacy ACL token using its AccessorID:
|
||||
|
||||
```shell-session
|
||||
$ consul acl translate-rules 429cd746-03d5-4bbb-a83a-18b164171c89
|
||||
```
|
|
@ -409,8 +409,8 @@ These metrics are used to monitor the health of the Consul servers.
|
|||
|-----------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|---------|
|
||||
| `consul.acl.ResolveToken` | Measures the time it takes to resolve an ACL token. | ms | timer |
|
||||
| `consul.acl.ResolveTokenToIdentity` | Measures the time it takes to resolve an ACL token to an Identity. This metric was removed in Consul 1.12. The time will now be reflected in `consul.acl.ResolveToken`. | ms | timer |
|
||||
| `consul.acl.token.cache_hit` | Increments if Consul is able to resolve a token's identity, or a legacy token, from the cache. | cache read op | counter |
|
||||
| `consul.acl.token.cache_miss` | Increments if Consul cannot resolve a token's identity, or a legacy token, from the cache. | cache read op | counter |
|
||||
| `consul.acl.token.cache_hit` | Increments if Consul is able to resolve a token's identity from the cache. | cache read op | counter |
|
||||
| `consul.acl.token.cache_miss` | Increments if Consul cannot resolve a token's identity from the cache. | cache read op | counter |
|
||||
| `consul.cache.bypass` | Counts how many times a request bypassed the cache because no cache-key was provided. | counter | counter |
|
||||
| `consul.cache.fetch_success` | Counts the number of successful fetches by the cache. | counter | counter |
|
||||
| `consul.cache.fetch_error` | Counts the number of failed fetches by the cache. | counter | counter |
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,393 +0,0 @@
|
|||
---
|
||||
layout: docs
|
||||
page_title: Legacy ACL Token Migration
|
||||
description: >-
|
||||
Migrate legacy tokens when updating to Consul 1.4.0+ from earlier versions to use the current ACL system. Learn about the migration process, how to update tokens, and examples for creating policies.
|
||||
---
|
||||
|
||||
# ACL Token Migration
|
||||
|
||||
Consul 1.4.0 introduces a new ACL system with improvements for the security and
|
||||
management of ACL tokens and policies. This guide documents how to upgrade
|
||||
existing (now called "legacy") tokens after upgrading to 1.4.0.
|
||||
|
||||
Since the policy syntax changed to be more precise and flexible to manage, it's
|
||||
necessary to manually translate old tokens into new ones to take advantage of
|
||||
the new ACL system features. Tooling is provided to help automate this and this
|
||||
guide describes the overall process.
|
||||
|
||||
~> **Note**: Before starting the token migration process all Consul agents, servers
|
||||
and clients, must be running at least version 1.4.0. Additionally, you
|
||||
must ensure the cluster is in a healthy state including a functioning leader. Once
|
||||
the leader has determined that all servers in the cluster are capable of using the
|
||||
new ACL system, the leader will transition itself. Then, the other servers will
|
||||
transition themselves to the new system, followed by the client agents. You can
|
||||
use `consul info` to investigate the cluster health.
|
||||
|
||||
Consul 1.4.0 retains full support for "legacy" ACL tokens so upgrades
|
||||
from Consul 1.3.0 are safe. Existing tokens will continue to work in the same
|
||||
way for at least two "major" releases (1.5.x, 1.6.x, etc; note HashiCorp does
|
||||
not use SemVer for our products).
|
||||
|
||||
This document will briefly describes the [high-level migration process](#migration-process) and provides some [specific examples](#migration-examples) of migration strategies.
|
||||
|
||||
## Migration Process
|
||||
|
||||
While "legacy" tokens will continue to work for several major releases, it's
|
||||
advisable to plan on migrating existing tokens as soon as is convenient.
|
||||
Migrating also enables using the new policy management improvements, stricter
|
||||
policy syntax rules and other features of the new system without
|
||||
re-issuing all the secrets in use.
|
||||
|
||||
The high-level process for migrating a legacy token is as follows:
|
||||
|
||||
1. Create a new policy or policies that grant the required access
|
||||
2. Update the existing token to use those policies
|
||||
|
||||
### Prerequisites
|
||||
|
||||
This process assumes that the 1.4.0 upgrade is complete including all legacy
|
||||
ACLs having their accessor IDs populated. This might take up to several minutes
|
||||
after the servers upgrade in the primary datacenter. You can tell if this is the
|
||||
case by using `consul acl token list` and checking that no tokens exist with a
|
||||
blank `AccessorID`.
|
||||
|
||||
In addition, it is assumed that all clients that might _create_ ACL tokens (e.g.
|
||||
Vault's Consul secrets engine) have been updated to use the [new ACL
|
||||
APIs](/consul/api-docs/acl/tokens).
|
||||
|
||||
Specifically if you are using Vault's Consul secrets engine you need to be
|
||||
running Vault 1.0.0 or higher, _and_ you must update all roles defined in Vault
|
||||
to specify a list of policy names rather than an inline policy (which causes
|
||||
Vault to use the legacy API).
|
||||
|
||||
~> **Note:** if you have systems still creating "legacy" tokens with the old
|
||||
APIs, the migration steps below will still work, however you'll have to keep
|
||||
re-running them until nothing is creating legacy tokens to ensure all tokens are
|
||||
migrated.
|
||||
|
||||
### Creating Policies
|
||||
|
||||
There are a range of different strategies for creating new policies from existing
|
||||
tokens. Two high-level strategies are described here although others or a
|
||||
mixture of these may be most appropriate depending on the ACL tokens you already
|
||||
have.
|
||||
|
||||
#### Strategy 1: Simple Policy Mapping
|
||||
|
||||
The simplest and most automatic strategy is to create one new policy for every
|
||||
existing token. This is easy to automate, but may result in a lot of policies
|
||||
with exactly the same rules and with non-human-readable names which will make
|
||||
managing policies harder. This approach can be accomplished using the [`consul acl policy create`](/consul/commands/acl/policy/create) command with
|
||||
`-from-token` option.
|
||||
|
||||
| Pros | Cons |
|
||||
| ------------------------ | ------------------------------------------- |
|
||||
| ✅ Simple | ❌ May leave many duplicated policies |
|
||||
| ✅ Easy to automate | ❌ Policy names not human-readable |
|
||||
|
||||
A detailed example of using this approach is [given
|
||||
below](#simple-policy-mapping).
|
||||
|
||||
#### Strategy 2: Combining Policies
|
||||
|
||||
This strategy takes a more manual approach to create a more manageable set of
|
||||
policies. There are a spectrum of options for how to do this which tradeoff
|
||||
increasing human involvement for increasing clarity and re-usability of the
|
||||
resulting policies.
|
||||
|
||||
For example you could use hashes of the policy rules to de-duplicate identical
|
||||
token policies automatically, however naming them something meaningful for
|
||||
humans would likely still need manual intervention.
|
||||
|
||||
Toward the other end of the spectrum it might be beneficial for security to
|
||||
translate prefix matches into exact matches. This however requires the operator
|
||||
knowing that clients using the token really doesn't rely on the prefix matching
|
||||
semantics of the old ACL system.
|
||||
|
||||
To assist with this approach, there is a CLI tool and corresponding API that can
|
||||
translate a legacy ACL token's rules into a new ACL policy that is exactly
|
||||
equivalent. See [`consul acl translate-rules`](/consul/commands/acl/translate-rules).
|
||||
|
||||
| Pros | Cons |
|
||||
| ------------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| ✅ Clearer, more manageable policies | ❌ Requires more manual effort |
|
||||
| ✅ Policies can be re-used by new ACL tokens | ❌ May take longer for large or complex existing policy sets |
|
||||
|
||||
A detailed example of using this approach is [given below](#combining-policies).
|
||||
|
||||
### Updating Existing Tokens
|
||||
|
||||
Once you have created one or more policies that adequately express the rules
|
||||
needed for a legacy token, you can update the token via the CLI or API to use
|
||||
those policies.
|
||||
|
||||
After updating, the token is no longer considered "legacy" and will have all the
|
||||
properties of a new token, however it keeps its `SecretID` (the secret part of
|
||||
the token used in API calls) so clients already using that token will continue
|
||||
to work. It is assumed that the policies you attach continue to grant the
|
||||
necessary access for existing clients; this is up to the operator to ensure.
|
||||
|
||||
#### Update via API
|
||||
|
||||
Use the [`PUT /v1/acl/token/:AccessorID`](/consul/api-docs/acl/tokens#update-a-token)
|
||||
endpoint. Specifically, ensure that the `Rules` field is omitted or empty. Empty
|
||||
`Rules` indicates that this is now treated as a new token.
|
||||
|
||||
#### Update via CLI
|
||||
|
||||
Use the [`consul acl token update`](/consul/commands/acl/token/update)
|
||||
command to update the token. Specifically you need to use `-upgrade-legacy`
|
||||
which will ensure that legacy rules are removed as well as the new policies
|
||||
added.
|
||||
|
||||
## Migration Examples
|
||||
|
||||
Below are two detailed examples of the two high-level strategies for creating
|
||||
policies discussed above. It should be noted these are intended to clarify the
|
||||
concrete steps you might take. **We don't recommend you perform production
|
||||
migrations with ad-hoc terminal commands**. Combining these or something similar
|
||||
into a script might be appropriate.
|
||||
|
||||
### Simple Policy Mapping
|
||||
|
||||
This strategy uses the CLI to create a new policy for every existing legacy
|
||||
token with exactly equivalent rules. It's easy to automate and clients will see
|
||||
no change in behavior for their tokens, but it does leave you with a lot of
|
||||
potentially identical policies to manage or clean up later.
|
||||
|
||||
#### Create Policies
|
||||
|
||||
You can get the AccessorID of every legacy token from the API. For example,
|
||||
using `curl` and `jq` in bash:
|
||||
|
||||
```shell-session
|
||||
$ LEGACY_IDS=$(curl --silent --header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
|
||||
'localhost:8500/v1/acl/tokens' | jq --raw-output '.[] | select (.Legacy) | .AccessorID')
|
||||
$ echo "$LEGACY_IDS"
|
||||
621cbd12-dde7-de06-9be0-e28d067b5b7f
|
||||
65cecc86-eb5b-ced5-92dc-f861cf7636fe
|
||||
ba464aa8-d857-3d26-472c-4d49c3bdae72
|
||||
```
|
||||
|
||||
To create a policy for each one we can use something like:
|
||||
|
||||
```shell
|
||||
for id in $LEGACY_IDS; do \
|
||||
consul acl policy create -name "migrated-$id" -from-token $id \
|
||||
-description "Migrated from legacy ACL token"; \
|
||||
done
|
||||
```
|
||||
|
||||
Each policy now has an identical set of rules to the original token. You can
|
||||
inspect these:
|
||||
|
||||
```shell-session
|
||||
$ consul acl policy read -name migrated-621cbd12-dde7-de06-9be0-e28d067b5b7f
|
||||
ID: 573d84bd-8b08-3061-e391-d2602e1b4947
|
||||
Name: migrated-621cbd12-dde7-de06-9be0-e28d067b5b7f
|
||||
Description: Migrated from legacy ACL token
|
||||
Datacenters:
|
||||
Rules:
|
||||
service_prefix "" {
|
||||
policy = "write"
|
||||
}
|
||||
```
|
||||
|
||||
Notice how the policy here is `service_prefix` and not `service` since the old
|
||||
ACL syntax was an implicit prefix match. This ensures any clients relying on
|
||||
prefix matching behavior will still work.
|
||||
|
||||
#### Update Tokens
|
||||
|
||||
With the policies created as above, we can automatically upgrade all legacy
|
||||
tokens.
|
||||
|
||||
```shell
|
||||
for id in $LEGACY_IDS; do \
|
||||
consul acl token update -id $id -policy-name "migrated-$id" -upgrade-legacy; \
|
||||
done
|
||||
```
|
||||
|
||||
The update is now complete, all legacy tokens are now new tokens with identical
|
||||
secrets and enforcement rules.
|
||||
|
||||
### Combining Policies
|
||||
|
||||
This strategy has more manual elements but results in a cleaner and more
|
||||
manageable set of policies than the fully automatic solutions. Note that this is
|
||||
**just an example** to illustrate a few ways you may choose to merge or
|
||||
manipulate policies.
|
||||
|
||||
#### Find All Unique Policies
|
||||
|
||||
You can get the AccessorID of every legacy token from the API. For example,
|
||||
using `curl` and `jq` in bash:
|
||||
|
||||
```shell-session
|
||||
$ LEGACY_IDS=$(curl --silent --header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
|
||||
'localhost:8500/v1/acl/tokens' | jq --raw-output '.[] | select (.Legacy) | .AccessorID')
|
||||
$ echo "$LEGACY_IDS"
|
||||
8b65fdf9-303e-0894-9f87-e71b3273600c
|
||||
d9deb39b-1b30-e100-b9c5-04aba3f593a1
|
||||
f2bce42e-cdcc-848d-28ca-cfd0556a22e3
|
||||
```
|
||||
|
||||
Now we want to read the actual policy for each legacy token and de-duplicate
|
||||
them. We can use the `translate-rules` helper sub-command which will read the
|
||||
token's policy and return a new ACL policy that is exactly equivalent.
|
||||
|
||||
```shell-session
|
||||
$ for id in $LEGACY_IDS; do \
|
||||
echo "Policy for $id:"
|
||||
consul acl translate-rules -token-accessor "$id"; \
|
||||
done
|
||||
Policy for 8b65fdf9-303e-0894-9f87-e71b3273600c:
|
||||
service_prefix "bar" {
|
||||
policy = "write"
|
||||
}
|
||||
Policy for d9deb39b-1b30-e100-b9c5-04aba3f593a1:
|
||||
service_prefix "foo" {
|
||||
policy = "write"
|
||||
}
|
||||
Policy for f2bce42e-cdcc-848d-28ca-cfd0556a22e3:
|
||||
service_prefix "bar" {
|
||||
policy = "write"
|
||||
}
|
||||
```
|
||||
|
||||
Notice that two policies are the same and one different.
|
||||
|
||||
We can change the loop above to take a hash of this policy definition to
|
||||
de-duplicate the policies into a set of files locally. This example uses command
|
||||
available on macOS but equivalents for other platforms should be easy to find.
|
||||
|
||||
```shell-session
|
||||
$ mkdir policies
|
||||
$ for id in $LEGACY_IDS; do \
|
||||
# Fetch the equivalent new policy rules based on the legacy token rules
|
||||
NEW_POLICY=$(consul acl translate-rules -token-accessor "$id"); \
|
||||
# Sha1 hash the rules
|
||||
HASH=$(echo -n "$NEW_POLICY" | shasum | awk '{ print $1 }'); \
|
||||
# Write rules to a policy file named with the hash to de-duplicated
|
||||
echo "$NEW_POLICY" > policies/$HASH.hcl; \
|
||||
done
|
||||
$ tree policies
|
||||
policies
|
||||
├── 024ce11f26f59436c518fb31f0999d1400485c17.hcl
|
||||
└── 501b787c9444fbd62f346ab257eeb27197be2444.hcl
|
||||
```
|
||||
|
||||
#### Cleaning Up Policies
|
||||
|
||||
You can now manually inspect and potentially edit these policies. For example we
|
||||
could rename them according to their intended use. In this case we maintain the
|
||||
hash as it will allow us to match tokens to policies later.
|
||||
|
||||
```shell-session
|
||||
$ cat policies/024ce11f26f59436c518fb31f0999d1400485c17.hcl
|
||||
service_prefix "bar" {
|
||||
policy = "write"
|
||||
}
|
||||
$ # Add human-readable suffix to the file name so policies end up clearly named
|
||||
$ mv policies/024ce11f26f59436c518fb31f0999d1400485c17.hcl \
|
||||
policies/024ce11f26f59436c518fb31f0999d1400485c17-bar-service.hcl
|
||||
```
|
||||
|
||||
You might also choose to tighten up the rules, for example if you know you never
|
||||
rely on prefix-matching the service name `foo` you might choose to modify the
|
||||
policy to use exact match.
|
||||
|
||||
```shell-session
|
||||
$ cat policies/501b787c9444fbd62f346ab257eeb27197be2444.hcl
|
||||
service_prefix "foo" {
|
||||
policy = "write"
|
||||
}
|
||||
$ echo 'service "foo" { policy = "write" }' > policies/501b787c9444fbd62f346ab257eeb27197be2444.hcl
|
||||
$ # Add human-readable suffix to the file name so policies end up clearly named
|
||||
$ mv policies/501b787c9444fbd62f346ab257eeb27197be2444.hcl \
|
||||
policies/501b787c9444fbd62f346ab257eeb27197be2444-foo-service.hcl
|
||||
```
|
||||
|
||||
#### Creating Policies
|
||||
|
||||
We now have a minimal set of policies to create, with human-readable names. We
|
||||
can create each one with something like the following.
|
||||
|
||||
```shell-session
|
||||
$ for p in $(ls policies | grep ".hcl"); do \
|
||||
# Extract the hash part of the file name
|
||||
HASH=$(echo "$p" | cut -d - -f 1); \
|
||||
# Extract the name suffix without .hcl
|
||||
NAME=$(echo "$p" | cut -d - -f 2- | cut -d . -f 1); \
|
||||
# Create new policy based on the rules in the file and the name we gave
|
||||
consul acl policy create -name $NAME \
|
||||
-rules "@policies/$p" \
|
||||
-description "Migrated from legacy token"; \
|
||||
done
|
||||
ID: da2a9f9b-4e44-13f8-e308-76ce7a8dcb21
|
||||
Name: bar-service
|
||||
Description: Migrated from legacy token
|
||||
Datacenters:
|
||||
Rules:
|
||||
service_prefix "bar" {
|
||||
policy = "write"
|
||||
}
|
||||
|
||||
ID: 9fbded86-9140-efe4-b661-c8bd07b6c584
|
||||
Name: foo-service
|
||||
Description: Migrated from legacy token
|
||||
Datacenters:
|
||||
Rules:
|
||||
service "foo" { policy = "write" }
|
||||
|
||||
```
|
||||
|
||||
#### Upgrading Tokens
|
||||
|
||||
Finally we can map our existing tokens to those policies using the hash in the
|
||||
policy file names. The `-upgrade-legacy` flag removes the token's legacy
|
||||
embedded rules at the same time as associating them with the new policies
|
||||
created from those rules.
|
||||
|
||||
```shell-session
|
||||
$ for id in $LEGACY_IDS; do \
|
||||
NEW_POLICY=$(consul acl translate-rules -token-accessor "$id"); \
|
||||
HASH=$(echo -n "$NEW_POLICY" | shasum | awk '{ print $1 }'); \
|
||||
# Lookup the hash->new policy mapping from the policy file names
|
||||
POLICY_FILE=$(ls policies | grep "^$HASH"); \
|
||||
POLICY_NAME=$(echo "$POLICY_FILE" | cut -d - -f 2- | cut -d . -f 1); \
|
||||
echo "==> Mapping token $id to policy $POLICY_NAME"; \
|
||||
consul acl token update -id $id -policy-name $POLICY_NAME -upgrade-legacy; \
|
||||
done
|
||||
==> Mapping token 8b65fdf9-303e-0894-9f87-e71b3273600c to policy bar-service
|
||||
Token updated successfully.
|
||||
AccessorID: 8b65fdf9-303e-0894-9f87-e71b3273600c
|
||||
SecretID: 3dbb3981-7654-733a-3475-5ce20fc5a7b9
|
||||
Description:
|
||||
Local: false
|
||||
Create Time: 0001-01-01 00:00:00 +0000 UTC
|
||||
Policies:
|
||||
da2a9f9b-4e44-13f8-e308-76ce7a8dcb21 - bar-service
|
||||
==> Mapping token d9deb39b-1b30-e100-b9c5-04aba3f593a1 to policy foo-service
|
||||
Token updated successfully.
|
||||
AccessorID: d9deb39b-1b30-e100-b9c5-04aba3f593a1
|
||||
SecretID: 5f54733b-4c76-eb74-8781-3550c20f4969
|
||||
Description:
|
||||
Local: false
|
||||
Create Time: 0001-01-01 00:00:00 +0000 UTC
|
||||
Policies:
|
||||
9fbded86-9140-efe4-b661-c8bd07b6c584 - foo-service
|
||||
==> Mapping token f2bce42e-cdcc-848d-28ca-cfd0556a22e3 to policy bar-service
|
||||
Token updated successfully.
|
||||
AccessorID: f2bce42e-cdcc-848d-28ca-cfd0556a22e3
|
||||
SecretID: f3aaa3e2-2c6f-cf3c-1e86-454de728e8ab
|
||||
Description:
|
||||
Local: false
|
||||
Create Time: 0001-01-01 00:00:00 +0000 UTC
|
||||
Policies:
|
||||
da2a9f9b-4e44-13f8-e308-76ce7a8dcb21 - bar-service
|
||||
```
|
||||
|
||||
At this point all tokens are upgraded and can use new ACL features while
|
||||
retaining the same secret clients are already using.
|
|
@ -189,7 +189,7 @@ You should receive output similar to this:
|
|||
}
|
||||
```
|
||||
|
||||
**6.** Migrate your legacy ACL tokens to the new ACL system by following the instructions in our [ACL Token Migration guide](/consul/docs/security/acl/acl-migrate-tokens).
|
||||
**6.** Migrate your legacy ACL tokens to the new ACL system.
|
||||
|
||||
~> This step _must_ be completed before upgrading to a version higher than 1.6.x.
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ Check whether you are using a `token` query parameter by searching your Consul a
|
|||
$ This request used the token query parameter which is deprecated and will be removed in Consul 1.17
|
||||
```
|
||||
|
||||
Deprecated authentication using the `token` query parameter:
|
||||
Deprecated authentication using the `token` query parameter:
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
|
@ -410,20 +410,10 @@ The `consul acl set-agent-token master` subcommand has been replaced with
|
|||
|
||||
### Legacy ACL System Removal
|
||||
|
||||
The legacy ACL system that was deprecated in Consul 1.4.0 has been removed.
|
||||
The legacy ACL system that was deprecated in Consul 1.4.0 was removed in 1.11.0.
|
||||
Before upgrading you should verify that nothing is still using the legacy ACL
|
||||
system. Complete the [Migrate Legacy ACL Tokens](https://learn.hashicorp.com/consul/day-2-agent-authentication/migrate-acl-tokens) tutorial to learn more.
|
||||
|
||||
Due to this removal the following endpoints no longer function:
|
||||
|
||||
- [`PUT /v1/acl/create`](/consul/api-docs/acl/legacy#create-acl-token)
|
||||
- [`PUT /v1/acl/update`](/consul/api-docs/acl/legacy#update-acl-token)
|
||||
- [`PUT /v1/acl/destroy/`](/consul/api-docs/acl/legacy#delete-acl-token)
|
||||
- [`GET /v1/acl/info/`](/consul/api-docs/acl/legacy#read-acl-token)
|
||||
- [`PUT /v1/acl/clone/`](/consul/api-docs/acl/legacy#clone-acl-token)
|
||||
- [`GET /v1/acl/list`](/consul/api-docs/acl/legacy#list-acls)
|
||||
- [`GET,POST /v1/acl/rules/translate`](/consul/api-docs/acl#translate-rules)
|
||||
|
||||
### Raft Storage Changes
|
||||
|
||||
The underlying library used for persisting the Raft log to persistent storage
|
||||
|
@ -786,8 +776,6 @@ as soon as possible after upgrade, as well as updating any integrations to work
|
|||
with the the new ACL [Token](/consul/api-docs/acl/tokens) and
|
||||
[Policy](/consul/api-docs/acl/policies) APIs.
|
||||
|
||||
More complete details on how to upgrade "legacy" tokens is available [here](/consul/docs/security/acl/acl-migrate-tokens).
|
||||
|
||||
### Connect Multi-datacenter
|
||||
|
||||
This only applies to users upgrading from an older version of Consul Enterprise to Consul Enterprise 1.4.0 (all license types).
|
||||
|
|
|
@ -43,10 +43,6 @@
|
|||
"title": "Tokens",
|
||||
"path": "acl/tokens"
|
||||
},
|
||||
{
|
||||
"title": "Legacy Tokens",
|
||||
"path": "acl/legacy"
|
||||
},
|
||||
{
|
||||
"title": "Policies",
|
||||
"path": "acl/policies"
|
||||
|
|
|
@ -166,10 +166,6 @@
|
|||
"path": "acl/token/update"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "translate-rules",
|
||||
"path": "acl/translate-rules"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -656,14 +656,6 @@
|
|||
"title": "Rules Reference",
|
||||
"path": "security/acl/acl-rules"
|
||||
},
|
||||
{
|
||||
"title": "Legacy Mode",
|
||||
"path": "security/acl/acl-legacy"
|
||||
},
|
||||
{
|
||||
"title": "Token Migration",
|
||||
"path": "security/acl/acl-migrate-tokens"
|
||||
},
|
||||
{
|
||||
"title": "ACLs in Federated Datacenters",
|
||||
"path": "security/acl/acl-federated-datacenters"
|
||||
|
|
Loading…
Reference in New Issue