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:
skpratt 2023-01-27 09:17:07 -06:00 committed by GitHub
parent e69e7fd1f2
commit 77d805ceb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 91 additions and 3227 deletions

3
.changelog/15947.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:breaking-change
acl: remove all acl migration functionality and references to the legacy acl system.
```

View File

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

View File

@ -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.",
},
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -945,7 +945,6 @@ node "node1" {
aclToken := &structs.ACLToken{
AccessorID: tokenID,
SecretID: token,
Rules: "",
}
require.NoError(t, backend.store.ACLTokenSet(ids.Next("update"), aclToken))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"
`
)

View File

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

View File

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

View File

@ -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)?" +

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,6 @@
"Description": "test token",
"Local": false,
"CreateTime": "2020-05-22T18:52:31Z",
"Hash": "YWJjZGVmZ2g=",
"Legacy": false
"Hash": "YWJjZGVmZ2g="
}
]

View File

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

View File

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

View File

@ -44,7 +44,6 @@
"ExpirationTime": "2020-05-22T19:52:31Z",
"CreateTime": "2020-05-22T18:52:31Z",
"Hash": "YWJjZGVmZ2g=",
"Legacy": false,
"Namespace": "foo",
"AuthMethodNamespace": "baz"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 |
| ------------------------ | ------------------------------------------- |
| &#9989; Simple | &#10060; May leave many duplicated policies |
| &#9989; Easy to automate | &#10060; 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 |
| ------------------------------------------------- | ------------------------------------------------------------------ |
| &#9989; Clearer, more manageable policies | &#10060; Requires more manual effort |
| &#9989; Policies can be re-used by new ACL tokens | &#10060; 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.

View File

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

View File

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

View File

@ -43,10 +43,6 @@
"title": "Tokens",
"path": "acl/tokens"
},
{
"title": "Legacy Tokens",
"path": "acl/legacy"
},
{
"title": "Policies",
"path": "acl/policies"

View File

@ -166,10 +166,6 @@
"path": "acl/token/update"
}
]
},
{
"title": "translate-rules",
"path": "acl/translate-rules"
}
]
},

View File

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