github.com/outbrain/consul@v1.4.5/agent/structs/acl.go (about) 1 package structs 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "hash/fnv" 8 "sort" 9 "strings" 10 "time" 11 12 "github.com/hashicorp/consul/acl" 13 "github.com/hashicorp/consul/sentinel" 14 "golang.org/x/crypto/blake2b" 15 ) 16 17 type ACLMode string 18 19 const ( 20 // ACLs are disabled by configuration 21 ACLModeDisabled ACLMode = "0" 22 // ACLs are enabled 23 ACLModeEnabled ACLMode = "1" 24 // DEPRECATED (ACL-Legacy-Compat) - only needed while legacy ACLs are supported 25 // ACLs are enabled and using legacy ACLs 26 ACLModeLegacy ACLMode = "2" 27 // DEPRECATED (ACL-Legacy-Compat) - only needed while legacy ACLs are supported 28 // ACLs are assumed enabled but not being advertised 29 ACLModeUnknown ACLMode = "3" 30 ) 31 32 // ACLOp is used in RPCs to encode ACL operations. 33 type ACLOp string 34 35 type ACLTokenIDType string 36 37 const ( 38 ACLTokenSecret ACLTokenIDType = "secret" 39 ACLTokenAccessor ACLTokenIDType = "accessor" 40 ) 41 42 type ACLPolicyIDType string 43 44 const ( 45 ACLPolicyName ACLPolicyIDType = "name" 46 ACLPolicyID ACLPolicyIDType = "id" 47 ) 48 49 const ( 50 // All policy ids with the first 120 bits set to all zeroes are 51 // reserved for builtin policies. Policy creation will ensure we 52 // dont accidentally create them when autogenerating uuids. 53 54 // This policy gives unlimited access to everything. Users 55 // may rename if desired but cannot delete or modify the rules. 56 ACLPolicyGlobalManagementID = "00000000-0000-0000-0000-000000000001" 57 ACLPolicyGlobalManagement = ` 58 acl = "write" 59 agent_prefix "" { 60 policy = "write" 61 } 62 event_prefix "" { 63 policy = "write" 64 } 65 key_prefix "" { 66 policy = "write" 67 } 68 keyring = "write" 69 node_prefix "" { 70 policy = "write" 71 } 72 operator = "write" 73 query_prefix "" { 74 policy = "write" 75 } 76 service_prefix "" { 77 policy = "write" 78 intentions = "write" 79 } 80 session_prefix "" { 81 policy = "write" 82 }` 83 84 // This is the policy ID for anonymous access. This is configurable by the 85 // user. 86 ACLTokenAnonymousID = "00000000-0000-0000-0000-000000000002" 87 ) 88 89 func ACLIDReserved(id string) bool { 90 return strings.HasPrefix(id, "00000000-0000-0000-0000-0000000000") 91 } 92 93 const ( 94 // ACLSet creates or updates a token. 95 ACLSet ACLOp = "set" 96 97 // ACLDelete deletes a token. 98 ACLDelete ACLOp = "delete" 99 ) 100 101 // ACLBootstrapNotAllowedErr is returned once we know that a bootstrap can no 102 // longer be done since the cluster was bootstrapped 103 var ACLBootstrapNotAllowedErr = errors.New("ACL bootstrap no longer allowed") 104 105 // ACLBootstrapInvalidResetIndexErr is returned when bootstrap is requested with a non-zero 106 // reset index but the index doesn't match the bootstrap index 107 var ACLBootstrapInvalidResetIndexErr = errors.New("Invalid ACL bootstrap reset index") 108 109 type ACLIdentity interface { 110 // ID returns a string that can be used for logging and telemetry. This should not 111 // contain any secret data used for authentication 112 ID() string 113 SecretToken() string 114 PolicyIDs() []string 115 EmbeddedPolicy() *ACLPolicy 116 } 117 118 type ACLTokenPolicyLink struct { 119 ID string 120 Name string `hash:"ignore"` 121 } 122 123 type ACLToken struct { 124 // This is the UUID used for tracking and management purposes 125 AccessorID string 126 127 // This is the UUID used as the api token by clients 128 SecretID string 129 130 // Human readable string to display for the token (Optional) 131 Description string 132 133 // List of policy links - nil/empty for legacy tokens 134 // Note this is the list of IDs and not the names. Prior to token creation 135 // the list of policy names gets validated and the policy IDs get stored herein 136 Policies []ACLTokenPolicyLink 137 138 // Type is the V1 Token Type 139 // DEPRECATED (ACL-Legacy-Compat) - remove once we no longer support v1 ACL compat 140 // Even though we are going to auto upgrade management tokens we still 141 // want to be able to have the old APIs operate on the upgraded management tokens 142 // so this field is being kept to identify legacy tokens even after an auto-upgrade 143 Type string `json:"-"` 144 145 // Rules is the V1 acl rules associated with 146 // DEPRECATED (ACL-Legacy-Compat) - remove once we no longer support v1 ACL compat 147 Rules string `json:",omitempty"` 148 149 // Whether this token is DC local. This means that it will not be synced 150 // to the ACL datacenter and replicated to others. 151 Local bool 152 153 // The time when this token was created 154 CreateTime time.Time `json:",omitempty"` 155 156 // Hash of the contents of the token 157 // 158 // This is needed mainly for replication purposes. When replicating from 159 // one DC to another keeping the content Hash will allow us to avoid 160 // unnecessary calls to the authoritative DC 161 Hash []byte 162 163 // Embedded Raft Metadata 164 RaftIndex 165 } 166 167 func (t *ACLToken) Clone() *ACLToken { 168 t2 := *t 169 t2.Policies = nil 170 171 if len(t.Policies) > 0 { 172 t2.Policies = make([]ACLTokenPolicyLink, len(t.Policies)) 173 copy(t2.Policies, t.Policies) 174 } 175 return &t2 176 } 177 178 func (t *ACLToken) ID() string { 179 return t.AccessorID 180 } 181 182 func (t *ACLToken) SecretToken() string { 183 return t.SecretID 184 } 185 186 func (t *ACLToken) PolicyIDs() []string { 187 var ids []string 188 for _, link := range t.Policies { 189 ids = append(ids, link.ID) 190 } 191 return ids 192 } 193 194 func (t *ACLToken) EmbeddedPolicy() *ACLPolicy { 195 // DEPRECATED (ACL-Legacy-Compat) 196 // 197 // For legacy tokens with embedded rules this provides a way to map those 198 // rules to an ACLPolicy. This function can just return nil once legacy 199 // acl compatibility is no longer needed. 200 // 201 // Additionally for management tokens we must embed the policy rules 202 // as well 203 policy := &ACLPolicy{} 204 if t.Type == ACLTokenTypeManagement { 205 hasher := fnv.New128a() 206 policy.ID = fmt.Sprintf("%x", hasher.Sum([]byte(ACLPolicyGlobalManagement))) 207 policy.Name = "legacy-management" 208 policy.Rules = ACLPolicyGlobalManagement 209 policy.Syntax = acl.SyntaxCurrent 210 } else if t.Rules != "" || t.Type == ACLTokenTypeClient { 211 hasher := fnv.New128a() 212 policy.ID = fmt.Sprintf("%x", hasher.Sum([]byte(t.Rules))) 213 policy.Name = fmt.Sprintf("legacy-policy-%s", policy.ID) 214 policy.Rules = t.Rules 215 policy.Syntax = acl.SyntaxLegacy 216 } else { 217 return nil 218 } 219 220 policy.SetHash(true) 221 return policy 222 } 223 224 func (t *ACLToken) SetHash(force bool) []byte { 225 if force || t.Hash == nil { 226 // Initialize a 256bit Blake2 hash (32 bytes) 227 hash, err := blake2b.New256(nil) 228 if err != nil { 229 panic(err) 230 } 231 232 // Write all the user set fields 233 hash.Write([]byte(t.Description)) 234 hash.Write([]byte(t.Type)) 235 hash.Write([]byte(t.Rules)) 236 237 if t.Local { 238 hash.Write([]byte("local")) 239 } else { 240 hash.Write([]byte("global")) 241 } 242 243 for _, link := range t.Policies { 244 hash.Write([]byte(link.ID)) 245 } 246 247 // Finalize the hash 248 hashVal := hash.Sum(nil) 249 250 // Set and return the hash 251 t.Hash = hashVal 252 } 253 return t.Hash 254 } 255 256 func (t *ACLToken) EstimateSize() int { 257 // 33 = 16 (RaftIndex) + 8 (Hash) + 8 (CreateTime) + 1 (Local) 258 size := 33 + len(t.AccessorID) + len(t.SecretID) + len(t.Description) + len(t.Type) + len(t.Rules) 259 for _, link := range t.Policies { 260 size += len(link.ID) + len(link.Name) 261 } 262 return size 263 } 264 265 // ACLTokens is a slice of ACLTokens. 266 type ACLTokens []*ACLToken 267 268 type ACLTokenListStub struct { 269 AccessorID string 270 Description string 271 Policies []ACLTokenPolicyLink 272 Local bool 273 CreateTime time.Time `json:",omitempty"` 274 Hash []byte 275 CreateIndex uint64 276 ModifyIndex uint64 277 Legacy bool `json:",omitempty"` 278 } 279 280 type ACLTokenListStubs []*ACLTokenListStub 281 282 func (token *ACLToken) Stub() *ACLTokenListStub { 283 return &ACLTokenListStub{ 284 AccessorID: token.AccessorID, 285 Description: token.Description, 286 Policies: token.Policies, 287 Local: token.Local, 288 CreateTime: token.CreateTime, 289 Hash: token.Hash, 290 CreateIndex: token.CreateIndex, 291 ModifyIndex: token.ModifyIndex, 292 Legacy: token.Rules != "", 293 } 294 } 295 296 func (tokens ACLTokens) Sort() { 297 sort.Slice(tokens, func(i, j int) bool { 298 return tokens[i].AccessorID < tokens[j].AccessorID 299 }) 300 } 301 302 func (tokens ACLTokenListStubs) Sort() { 303 sort.Slice(tokens, func(i, j int) bool { 304 return tokens[i].AccessorID < tokens[j].AccessorID 305 }) 306 } 307 308 type ACLPolicy struct { 309 // This is the internal UUID associated with the policy 310 ID string 311 312 // Unique name to reference the policy by. 313 // - Valid Characters: [a-zA-Z0-9-] 314 // - Valid Lengths: 1 - 128 315 Name string 316 317 // Human readable description (Optional) 318 Description string 319 320 // The rule set (using the updated rule syntax) 321 Rules string 322 323 // DEPRECATED (ACL-Legacy-Compat) - This is only needed while we support the legacy ACLs 324 Syntax acl.SyntaxVersion `json:"-"` 325 326 // Datacenters that the policy is valid within. 327 // - No wildcards allowed 328 // - If empty then the policy is valid within all datacenters 329 Datacenters []string `json:",omitempty"` 330 331 // Hash of the contents of the policy 332 // This does not take into account the ID (which is immutable) 333 // nor the raft metadata. 334 // 335 // This is needed mainly for replication purposes. When replicating from 336 // one DC to another keeping the content Hash will allow us to avoid 337 // unnecessary calls to the authoritative DC 338 Hash []byte 339 340 // Embedded Raft Metadata 341 RaftIndex `hash:"ignore"` 342 } 343 344 func (p *ACLPolicy) Clone() *ACLPolicy { 345 p2 := *p 346 p2.Datacenters = nil 347 if len(p.Datacenters) > 0 { 348 p2.Datacenters = make([]string, len(p.Datacenters)) 349 copy(p2.Datacenters, p.Datacenters) 350 } 351 return &p2 352 } 353 354 type ACLPolicyListStub struct { 355 ID string 356 Name string 357 Description string 358 Datacenters []string 359 Hash []byte 360 CreateIndex uint64 361 ModifyIndex uint64 362 } 363 364 func (p *ACLPolicy) Stub() *ACLPolicyListStub { 365 return &ACLPolicyListStub{ 366 ID: p.ID, 367 Name: p.Name, 368 Description: p.Description, 369 Datacenters: p.Datacenters, 370 Hash: p.Hash, 371 CreateIndex: p.CreateIndex, 372 ModifyIndex: p.ModifyIndex, 373 } 374 } 375 376 type ACLPolicies []*ACLPolicy 377 type ACLPolicyListStubs []*ACLPolicyListStub 378 379 func (p *ACLPolicy) SetHash(force bool) []byte { 380 if force || p.Hash == nil { 381 // Initialize a 256bit Blake2 hash (32 bytes) 382 hash, err := blake2b.New256(nil) 383 if err != nil { 384 panic(err) 385 } 386 387 // Write all the user set fields 388 hash.Write([]byte(p.Name)) 389 hash.Write([]byte(p.Description)) 390 hash.Write([]byte(p.Rules)) 391 for _, dc := range p.Datacenters { 392 hash.Write([]byte(dc)) 393 } 394 395 // Finalize the hash 396 hashVal := hash.Sum(nil) 397 398 // Set and return the hash 399 p.Hash = hashVal 400 } 401 return p.Hash 402 } 403 404 func (p *ACLPolicy) EstimateSize() int { 405 // This is just an estimate. There is other data structure overhead 406 // pointers etc that this does not account for. 407 408 // 64 = 36 (uuid) + 16 (RaftIndex) + 8 (Hash) + 4 (Syntax) 409 size := 64 + len(p.Name) + len(p.Description) + len(p.Rules) 410 for _, dc := range p.Datacenters { 411 size += len(dc) 412 } 413 414 return size 415 } 416 417 // ACLPolicyListHash returns a consistent hash for a set of policies. 418 func (policies ACLPolicies) HashKey() string { 419 cacheKeyHash, err := blake2b.New256(nil) 420 if err != nil { 421 panic(err) 422 } 423 for _, policy := range policies { 424 cacheKeyHash.Write([]byte(policy.ID)) 425 // including the modify index prevents a policy set from being 426 // cached if one of the policies has changed 427 binary.Write(cacheKeyHash, binary.BigEndian, policy.ModifyIndex) 428 } 429 return fmt.Sprintf("%x", cacheKeyHash.Sum(nil)) 430 } 431 432 func (policies ACLPolicies) Sort() { 433 sort.Slice(policies, func(i, j int) bool { 434 return policies[i].ID < policies[j].ID 435 }) 436 } 437 438 func (policies ACLPolicyListStubs) Sort() { 439 sort.Slice(policies, func(i, j int) bool { 440 return policies[i].ID < policies[j].ID 441 }) 442 } 443 444 func (policies ACLPolicies) resolveWithCache(cache *ACLCaches, sentinel sentinel.Evaluator) ([]*acl.Policy, error) { 445 // Parse the policies 446 parsed := make([]*acl.Policy, 0, len(policies)) 447 for _, policy := range policies { 448 policy.SetHash(false) 449 cacheKey := fmt.Sprintf("%x", policy.Hash) 450 cachedPolicy := cache.GetParsedPolicy(cacheKey) 451 if cachedPolicy != nil { 452 // policies are content hashed so no need to check the age 453 parsed = append(parsed, cachedPolicy.Policy) 454 continue 455 } 456 457 p, err := acl.NewPolicyFromSource(policy.ID, policy.ModifyIndex, policy.Rules, policy.Syntax, sentinel) 458 if err != nil { 459 return nil, fmt.Errorf("failed to parse %q: %v", policy.Name, err) 460 } 461 462 cache.PutParsedPolicy(cacheKey, p) 463 parsed = append(parsed, p) 464 } 465 466 return parsed, nil 467 } 468 469 func (policies ACLPolicies) Compile(parent acl.Authorizer, cache *ACLCaches, sentinel sentinel.Evaluator) (acl.Authorizer, error) { 470 // Determine the cache key 471 cacheKey := policies.HashKey() 472 entry := cache.GetAuthorizer(cacheKey) 473 if entry != nil { 474 // the hash key takes into account the policy contents. There is no reason to expire this cache or check its age. 475 return entry.Authorizer, nil 476 } 477 478 parsed, err := policies.resolveWithCache(cache, sentinel) 479 if err != nil { 480 return nil, fmt.Errorf("failed to parse the ACL policies: %v", err) 481 } 482 483 // Create the ACL object 484 authorizer, err := acl.NewPolicyAuthorizer(parent, parsed, sentinel) 485 if err != nil { 486 return nil, fmt.Errorf("failed to construct ACL Authorizer: %v", err) 487 } 488 489 // Update the cache 490 cache.PutAuthorizer(cacheKey, authorizer) 491 return authorizer, nil 492 } 493 494 func (policies ACLPolicies) Merge(cache *ACLCaches, sentinel sentinel.Evaluator) (*acl.Policy, error) { 495 parsed, err := policies.resolveWithCache(cache, sentinel) 496 if err != nil { 497 return nil, err 498 } 499 500 return acl.MergePolicies(parsed), nil 501 } 502 503 type ACLReplicationType string 504 505 const ( 506 ACLReplicateLegacy ACLReplicationType = "legacy" 507 ACLReplicatePolicies ACLReplicationType = "policies" 508 ACLReplicateTokens ACLReplicationType = "tokens" 509 ) 510 511 // ACLReplicationStatus provides information about the health of the ACL 512 // replication system. 513 type ACLReplicationStatus struct { 514 Enabled bool 515 Running bool 516 SourceDatacenter string 517 ReplicationType ACLReplicationType 518 ReplicatedIndex uint64 519 ReplicatedTokenIndex uint64 520 LastSuccess time.Time 521 LastError time.Time 522 } 523 524 // ACLTokenSetRequest is used for token creation and update operations 525 // at the RPC layer 526 type ACLTokenSetRequest struct { 527 ACLToken ACLToken // Token to manipulate - I really dislike this name but "Token" is taken in the WriteRequest 528 Datacenter string // The datacenter to perform the request within 529 WriteRequest 530 } 531 532 func (r *ACLTokenSetRequest) RequestDatacenter() string { 533 return r.Datacenter 534 } 535 536 // ACLTokenGetRequest is used for token read operations at the RPC layer 537 type ACLTokenGetRequest struct { 538 TokenID string // id used for the token lookup 539 TokenIDType ACLTokenIDType // The Type of ID used to lookup the token 540 Datacenter string // The datacenter to perform the request within 541 QueryOptions 542 } 543 544 func (r *ACLTokenGetRequest) RequestDatacenter() string { 545 return r.Datacenter 546 } 547 548 // ACLTokenDeleteRequest is used for token deletion operations at the RPC layer 549 type ACLTokenDeleteRequest struct { 550 TokenID string // ID of the token to delete 551 Datacenter string // The datacenter to perform the request within 552 WriteRequest 553 } 554 555 func (r *ACLTokenDeleteRequest) RequestDatacenter() string { 556 return r.Datacenter 557 } 558 559 // ACLTokenListRequest is used for token listing operations at the RPC layer 560 type ACLTokenListRequest struct { 561 IncludeLocal bool // Whether local tokens should be included 562 IncludeGlobal bool // Whether global tokens should be included 563 Policy string // Policy filter 564 Datacenter string // The datacenter to perform the request within 565 QueryOptions 566 } 567 568 func (r *ACLTokenListRequest) RequestDatacenter() string { 569 return r.Datacenter 570 } 571 572 // ACLTokenListResponse is used to return the secret data free stubs 573 // of the tokens 574 type ACLTokenListResponse struct { 575 Tokens ACLTokenListStubs 576 QueryMeta 577 } 578 579 // ACLTokenBatchGetRequest is used for reading multiple tokens, this is 580 // different from the the token list request in that only tokens with the 581 // the requested ids are returned 582 type ACLTokenBatchGetRequest struct { 583 AccessorIDs []string // List of accessor ids to fetch 584 Datacenter string // The datacenter to perform the request within 585 QueryOptions 586 } 587 588 func (r *ACLTokenBatchGetRequest) RequestDatacenter() string { 589 return r.Datacenter 590 } 591 592 // ACLTokenBatchSetRequest is used only at the Raft layer 593 // for batching multiple token creation/update operations 594 // 595 // This is particularly useful during token replication and during 596 // automatic legacy token upgrades. 597 type ACLTokenBatchSetRequest struct { 598 Tokens ACLTokens 599 CAS bool 600 } 601 602 // ACLTokenBatchDeleteRequest is used only at the Raft layer 603 // for batching multiple token deletions. 604 // 605 // This is particularly useful during token replication when 606 // multiple tokens need to be removed from the local DCs state. 607 type ACLTokenBatchDeleteRequest struct { 608 TokenIDs []string // Tokens to delete 609 } 610 611 // ACLTokenBootstrapRequest is used only at the Raft layer 612 // for ACL bootstrapping 613 // 614 // The RPC layer will use a generic DCSpecificRequest to indicate 615 // that bootstrapping must be performed but the actual token 616 // and the resetIndex will be generated by that RPC endpoint 617 type ACLTokenBootstrapRequest struct { 618 Token ACLToken // Token to use for bootstrapping 619 ResetIndex uint64 // Reset index 620 } 621 622 // ACLTokenResponse returns a single Token + metadata 623 type ACLTokenResponse struct { 624 Token *ACLToken 625 Redacted bool // whether the token's secret was redacted 626 QueryMeta 627 } 628 629 // ACLTokenBatchResponse returns multiple Tokens associated with the same metadata 630 type ACLTokenBatchResponse struct { 631 Tokens []*ACLToken 632 Redacted bool // whether the token secrets were redacted. 633 QueryMeta 634 } 635 636 // ACLPolicySetRequest is used at the RPC layer for creation and update requests 637 type ACLPolicySetRequest struct { 638 Policy ACLPolicy // The policy to upsert 639 Datacenter string // The datacenter to perform the request within 640 WriteRequest 641 } 642 643 func (r *ACLPolicySetRequest) RequestDatacenter() string { 644 return r.Datacenter 645 } 646 647 // ACLPolicyDeleteRequest is used at the RPC layer deletion requests 648 type ACLPolicyDeleteRequest struct { 649 PolicyID string // The id of the policy to delete 650 Datacenter string // The datacenter to perform the request within 651 WriteRequest 652 } 653 654 func (r *ACLPolicyDeleteRequest) RequestDatacenter() string { 655 return r.Datacenter 656 } 657 658 // ACLPolicyGetRequest is used at the RPC layer to perform policy read operations 659 type ACLPolicyGetRequest struct { 660 PolicyID string // id used for the policy lookup 661 Datacenter string // The datacenter to perform the request within 662 QueryOptions 663 } 664 665 func (r *ACLPolicyGetRequest) RequestDatacenter() string { 666 return r.Datacenter 667 } 668 669 // ACLPolicyListRequest is used at the RPC layer to request a listing of policies 670 type ACLPolicyListRequest struct { 671 Datacenter string // The datacenter to perform the request within 672 QueryOptions 673 } 674 675 func (r *ACLPolicyListRequest) RequestDatacenter() string { 676 return r.Datacenter 677 } 678 679 type ACLPolicyListResponse struct { 680 Policies ACLPolicyListStubs 681 QueryMeta 682 } 683 684 // ACLPolicyBatchGetRequest is used at the RPC layer to request a subset of 685 // the policies associated with the token used for retrieval 686 type ACLPolicyBatchGetRequest struct { 687 PolicyIDs []string // List of policy ids to fetch 688 Datacenter string // The datacenter to perform the request within 689 QueryOptions 690 } 691 692 func (r *ACLPolicyBatchGetRequest) RequestDatacenter() string { 693 return r.Datacenter 694 } 695 696 // ACLPolicyResponse returns a single policy + metadata 697 type ACLPolicyResponse struct { 698 Policy *ACLPolicy 699 QueryMeta 700 } 701 702 type ACLPolicyBatchResponse struct { 703 Policies []*ACLPolicy 704 QueryMeta 705 } 706 707 // ACLPolicyBatchSetRequest is used at the Raft layer for batching 708 // multiple policy creations and updates 709 // 710 // This is particularly useful during replication 711 type ACLPolicyBatchSetRequest struct { 712 Policies ACLPolicies 713 } 714 715 // ACLPolicyBatchDeleteRequest is used at the Raft layer for batching 716 // multiple policy deletions 717 // 718 // This is particularly useful during replication 719 type ACLPolicyBatchDeleteRequest struct { 720 PolicyIDs []string 721 }