github.com/adityamillind98/nomad@v0.11.8/nomad/acl_endpoint.go (about) 1 package nomad 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 "time" 10 11 metrics "github.com/armon/go-metrics" 12 log "github.com/hashicorp/go-hclog" 13 memdb "github.com/hashicorp/go-memdb" 14 policy "github.com/hashicorp/nomad/acl" 15 "github.com/hashicorp/nomad/helper/uuid" 16 "github.com/hashicorp/nomad/nomad/state" 17 "github.com/hashicorp/nomad/nomad/structs" 18 ) 19 20 var ( 21 // aclDisabled is returned when an ACL endpoint is hit but ACLs are not enabled 22 aclDisabled = structs.NewErrRPCCoded(400, "ACL support disabled") 23 ) 24 25 const ( 26 // aclBootstrapReset is the file name to create in the data dir. It's only contents 27 // should be the reset index 28 aclBootstrapReset = "acl-bootstrap-reset" 29 ) 30 31 // ACL endpoint is used for manipulating ACL tokens and policies 32 type ACL struct { 33 srv *Server 34 logger log.Logger 35 } 36 37 // UpsertPolicies is used to create or update a set of policies 38 func (a *ACL) UpsertPolicies(args *structs.ACLPolicyUpsertRequest, reply *structs.GenericResponse) error { 39 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 40 if !a.srv.config.ACLEnabled { 41 return aclDisabled 42 } 43 args.Region = a.srv.config.AuthoritativeRegion 44 45 if done, err := a.srv.forward("ACL.UpsertPolicies", args, args, reply); done { 46 return err 47 } 48 defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_policies"}, time.Now()) 49 50 // Check management level permissions 51 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 52 return err 53 } else if acl == nil || !acl.IsManagement() { 54 return structs.ErrPermissionDenied 55 } 56 57 // Validate non-zero set of policies 58 if len(args.Policies) == 0 { 59 return structs.NewErrRPCCoded(400, "must specify as least one policy") 60 } 61 62 // Validate each policy, compute hash 63 for idx, policy := range args.Policies { 64 if err := policy.Validate(); err != nil { 65 return structs.NewErrRPCCodedf(404, "policy %d invalid: %v", idx, err) 66 } 67 policy.SetHash() 68 } 69 70 // Update via Raft 71 _, index, err := a.srv.raftApply(structs.ACLPolicyUpsertRequestType, args) 72 if err != nil { 73 return err 74 } 75 76 // Update the index 77 reply.Index = index 78 return nil 79 } 80 81 // DeletePolicies is used to delete policies 82 func (a *ACL) DeletePolicies(args *structs.ACLPolicyDeleteRequest, reply *structs.GenericResponse) error { 83 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 84 if !a.srv.config.ACLEnabled { 85 return aclDisabled 86 } 87 args.Region = a.srv.config.AuthoritativeRegion 88 89 if done, err := a.srv.forward("ACL.DeletePolicies", args, args, reply); done { 90 return err 91 } 92 defer metrics.MeasureSince([]string{"nomad", "acl", "delete_policies"}, time.Now()) 93 94 // Check management level permissions 95 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 96 return err 97 } else if acl == nil || !acl.IsManagement() { 98 return structs.ErrPermissionDenied 99 } 100 101 // Validate non-zero set of policies 102 if len(args.Names) == 0 { 103 return structs.NewErrRPCCoded(400, "must specify as least one policy") 104 } 105 106 // Update via Raft 107 _, index, err := a.srv.raftApply(structs.ACLPolicyDeleteRequestType, args) 108 if err != nil { 109 return err 110 } 111 112 // Update the index 113 reply.Index = index 114 return nil 115 } 116 117 // ListPolicies is used to list the policies 118 func (a *ACL) ListPolicies(args *structs.ACLPolicyListRequest, reply *structs.ACLPolicyListResponse) error { 119 if !a.srv.config.ACLEnabled { 120 return aclDisabled 121 } 122 123 if done, err := a.srv.forward("ACL.ListPolicies", args, args, reply); done { 124 return err 125 } 126 defer metrics.MeasureSince([]string{"nomad", "acl", "list_policies"}, time.Now()) 127 128 // Check management level permissions 129 acl, err := a.srv.ResolveToken(args.AuthToken) 130 if err != nil { 131 return err 132 } else if acl == nil { 133 return structs.ErrPermissionDenied 134 } 135 136 // If it is not a management token determine the policies that may be listed 137 mgt := acl.IsManagement() 138 var policies map[string]struct{} 139 if !mgt { 140 token, err := a.requestACLToken(args.AuthToken) 141 if err != nil { 142 return err 143 } 144 if token == nil { 145 return structs.ErrTokenNotFound 146 } 147 148 policies = make(map[string]struct{}, len(token.Policies)) 149 for _, p := range token.Policies { 150 policies[p] = struct{}{} 151 } 152 } 153 154 // Setup the blocking query 155 opts := blockingOptions{ 156 queryOpts: &args.QueryOptions, 157 queryMeta: &reply.QueryMeta, 158 run: func(ws memdb.WatchSet, state *state.StateStore) error { 159 // Iterate over all the policies 160 var err error 161 var iter memdb.ResultIterator 162 if prefix := args.QueryOptions.Prefix; prefix != "" { 163 iter, err = state.ACLPolicyByNamePrefix(ws, prefix) 164 } else { 165 iter, err = state.ACLPolicies(ws) 166 } 167 if err != nil { 168 return err 169 } 170 171 // Convert all the policies to a list stub 172 reply.Policies = nil 173 for { 174 raw := iter.Next() 175 if raw == nil { 176 break 177 } 178 policy := raw.(*structs.ACLPolicy) 179 if _, ok := policies[policy.Name]; ok || mgt { 180 reply.Policies = append(reply.Policies, policy.Stub()) 181 } 182 } 183 184 // Use the last index that affected the policy table 185 index, err := state.Index("acl_policy") 186 if err != nil { 187 return err 188 } 189 190 // Ensure we never set the index to zero, otherwise a blocking query cannot be used. 191 // We floor the index at one, since realistically the first write must have a higher index. 192 if index == 0 { 193 index = 1 194 } 195 reply.Index = index 196 return nil 197 }} 198 return a.srv.blockingRPC(&opts) 199 } 200 201 // GetPolicy is used to get a specific policy 202 func (a *ACL) GetPolicy(args *structs.ACLPolicySpecificRequest, reply *structs.SingleACLPolicyResponse) error { 203 if !a.srv.config.ACLEnabled { 204 return aclDisabled 205 } 206 207 if done, err := a.srv.forward("ACL.GetPolicy", args, args, reply); done { 208 return err 209 } 210 defer metrics.MeasureSince([]string{"nomad", "acl", "get_policy"}, time.Now()) 211 212 // Check management level permissions 213 acl, err := a.srv.ResolveToken(args.AuthToken) 214 if err != nil { 215 return err 216 } else if acl == nil { 217 return structs.ErrPermissionDenied 218 } 219 220 // If the policy is the anonymous one, anyone can get it 221 // If it is not a management token determine if it can get this policy 222 mgt := acl.IsManagement() 223 if !mgt && args.Name != "anonymous" { 224 token, err := a.requestACLToken(args.AuthToken) 225 if err != nil { 226 return err 227 } 228 if token == nil { 229 return structs.ErrTokenNotFound 230 } 231 232 found := false 233 for _, p := range token.Policies { 234 if p == args.Name { 235 found = true 236 break 237 } 238 } 239 240 if !found { 241 return structs.ErrPermissionDenied 242 } 243 } 244 245 // Setup the blocking query 246 opts := blockingOptions{ 247 queryOpts: &args.QueryOptions, 248 queryMeta: &reply.QueryMeta, 249 run: func(ws memdb.WatchSet, state *state.StateStore) error { 250 // Look for the policy 251 out, err := state.ACLPolicyByName(ws, args.Name) 252 if err != nil { 253 return err 254 } 255 256 // Setup the output 257 reply.Policy = out 258 if out != nil { 259 reply.Index = out.ModifyIndex 260 rules, err := policy.Parse(out.Rules) 261 262 if err != nil { 263 return err 264 } 265 reply.Policy.RulesJSON = rules 266 } else { 267 // Use the last index that affected the policy table 268 index, err := state.Index("acl_policy") 269 if err != nil { 270 return err 271 } 272 reply.Index = index 273 } 274 return nil 275 }} 276 return a.srv.blockingRPC(&opts) 277 } 278 279 func (a *ACL) requestACLToken(secretID string) (*structs.ACLToken, error) { 280 if secretID == "" { 281 return structs.AnonymousACLToken, nil 282 } 283 284 snap, err := a.srv.fsm.State().Snapshot() 285 if err != nil { 286 return nil, err 287 } 288 289 return snap.ACLTokenBySecretID(nil, secretID) 290 } 291 292 // GetPolicies is used to get a set of policies 293 func (a *ACL) GetPolicies(args *structs.ACLPolicySetRequest, reply *structs.ACLPolicySetResponse) error { 294 if !a.srv.config.ACLEnabled { 295 return aclDisabled 296 } 297 if done, err := a.srv.forward("ACL.GetPolicies", args, args, reply); done { 298 return err 299 } 300 defer metrics.MeasureSince([]string{"nomad", "acl", "get_policies"}, time.Now()) 301 302 // For client typed tokens, allow them to query any policies associated with that token. 303 // This is used by clients which are resolving the policies to enforce. Any associated 304 // policies need to be fetched so that the client can determine what to allow. 305 token, err := a.requestACLToken(args.AuthToken) 306 if err != nil { 307 return err 308 } 309 310 if token == nil { 311 return structs.ErrTokenNotFound 312 } 313 if token.Type != structs.ACLManagementToken && !token.PolicySubset(args.Names) { 314 return structs.ErrPermissionDenied 315 } 316 317 // Setup the blocking query 318 opts := blockingOptions{ 319 queryOpts: &args.QueryOptions, 320 queryMeta: &reply.QueryMeta, 321 run: func(ws memdb.WatchSet, state *state.StateStore) error { 322 // Setup the output 323 reply.Policies = make(map[string]*structs.ACLPolicy, len(args.Names)) 324 325 // Look for the policy 326 for _, policyName := range args.Names { 327 out, err := state.ACLPolicyByName(ws, policyName) 328 if err != nil { 329 return err 330 } 331 if out != nil { 332 reply.Policies[policyName] = out 333 } 334 } 335 336 // Use the last index that affected the policy table 337 index, err := state.Index("acl_policy") 338 if err != nil { 339 return err 340 } 341 reply.Index = index 342 return nil 343 }} 344 return a.srv.blockingRPC(&opts) 345 } 346 347 // Bootstrap is used to bootstrap the initial token 348 func (a *ACL) Bootstrap(args *structs.ACLTokenBootstrapRequest, reply *structs.ACLTokenUpsertResponse) error { 349 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 350 if !a.srv.config.ACLEnabled { 351 return aclDisabled 352 } 353 args.Region = a.srv.config.AuthoritativeRegion 354 355 if done, err := a.srv.forward("ACL.Bootstrap", args, args, reply); done { 356 return err 357 } 358 defer metrics.MeasureSince([]string{"nomad", "acl", "bootstrap"}, time.Now()) 359 360 // Always ignore the reset index from the arguments 361 args.ResetIndex = 0 362 363 // Snapshot the state 364 state, err := a.srv.State().Snapshot() 365 if err != nil { 366 return err 367 } 368 369 // Verify bootstrap is possible. The state store method re-verifies this, 370 // but we do an early check to avoid raft transactions when possible. 371 ok, resetIdx, err := state.CanBootstrapACLToken() 372 if err != nil { 373 return err 374 } 375 if !ok { 376 // Check if there is a reset index specified 377 specifiedIndex := a.fileBootstrapResetIndex() 378 if specifiedIndex == 0 { 379 return structs.NewErrRPCCodedf(400, "ACL bootstrap already done (reset index: %d)", resetIdx) 380 } else if specifiedIndex != resetIdx { 381 return structs.NewErrRPCCodedf(400, "Invalid bootstrap reset index (specified %d, reset index: %d)", specifiedIndex, resetIdx) 382 } 383 384 // Setup the reset index to allow bootstrapping again 385 args.ResetIndex = resetIdx 386 } 387 388 // Create a new global management token, override any parameter 389 args.Token = &structs.ACLToken{ 390 AccessorID: uuid.Generate(), 391 SecretID: uuid.Generate(), 392 Name: "Bootstrap Token", 393 Type: structs.ACLManagementToken, 394 Global: true, 395 CreateTime: time.Now().UTC(), 396 } 397 args.Token.SetHash() 398 399 // Update via Raft 400 _, index, err := a.srv.raftApply(structs.ACLTokenBootstrapRequestType, args) 401 if err != nil { 402 return err 403 } 404 405 // Populate the response. We do a lookup against the state to 406 // pickup the proper create / modify times. 407 state, err = a.srv.State().Snapshot() 408 if err != nil { 409 return err 410 } 411 out, err := state.ACLTokenByAccessorID(nil, args.Token.AccessorID) 412 if err != nil { 413 return structs.NewErrRPCCodedf(400, "token lookup failed: %v", err) 414 } 415 reply.Tokens = append(reply.Tokens, out) 416 417 // Update the index 418 reply.Index = index 419 return nil 420 } 421 422 // fileBootstrapResetIndex is used to read the reset file from <data-dir>/acl-bootstrap-reset 423 func (a *ACL) fileBootstrapResetIndex() uint64 { 424 // Determine the file path to check 425 path := filepath.Join(a.srv.config.DataDir, aclBootstrapReset) 426 427 // Read the file 428 raw, err := ioutil.ReadFile(path) 429 if err != nil { 430 if !os.IsNotExist(err) { 431 a.logger.Error("failed to read bootstrap file", "path", path, "error", err) 432 } 433 return 0 434 } 435 436 // Attempt to parse the file 437 var resetIdx uint64 438 if _, err := fmt.Sscanf(string(raw), "%d", &resetIdx); err != nil { 439 a.logger.Error("failed to parse bootstrap file", "path", path, "error", err) 440 return 0 441 } 442 443 // Return the reset index 444 a.logger.Warn("bootstrap file parsed", "path", path, "reset_index", resetIdx) 445 return resetIdx 446 } 447 448 // UpsertTokens is used to create or update a set of tokens 449 func (a *ACL) UpsertTokens(args *structs.ACLTokenUpsertRequest, reply *structs.ACLTokenUpsertResponse) error { 450 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 451 if !a.srv.config.ACLEnabled { 452 return aclDisabled 453 } 454 455 // Validate non-zero set of tokens 456 if len(args.Tokens) == 0 { 457 return structs.NewErrRPCCoded(400, "must specify as least one token") 458 } 459 460 // Force the request to the authoritative region if we are creating global tokens 461 hasGlobal := false 462 allGlobal := true 463 for _, token := range args.Tokens { 464 if token.Global { 465 hasGlobal = true 466 } else { 467 allGlobal = false 468 } 469 } 470 471 // Disallow mixed requests with global and non-global tokens since we forward 472 // the entire request as a single batch. 473 if hasGlobal { 474 if !allGlobal { 475 return structs.NewErrRPCCoded(400, "cannot upsert mixed global and non-global tokens") 476 } 477 478 // Force the request to the authoritative region if it has global 479 args.Region = a.srv.config.AuthoritativeRegion 480 } 481 482 if done, err := a.srv.forward("ACL.UpsertTokens", args, args, reply); done { 483 return err 484 } 485 defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_tokens"}, time.Now()) 486 487 // Check management level permissions 488 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 489 return err 490 } else if acl == nil || !acl.IsManagement() { 491 return structs.ErrPermissionDenied 492 } 493 494 // Snapshot the state 495 state, err := a.srv.State().Snapshot() 496 if err != nil { 497 return err 498 } 499 500 // Validate each token 501 for idx, token := range args.Tokens { 502 if err := token.Validate(); err != nil { 503 return structs.NewErrRPCCodedf(400, "token %d invalid: %v", idx, err) 504 } 505 506 // Generate an accessor and secret ID if new 507 if token.AccessorID == "" { 508 token.AccessorID = uuid.Generate() 509 token.SecretID = uuid.Generate() 510 token.CreateTime = time.Now().UTC() 511 512 } else { 513 // Verify the token exists 514 out, err := state.ACLTokenByAccessorID(nil, token.AccessorID) 515 if err != nil { 516 return structs.NewErrRPCCodedf(400, "token lookup failed: %v", err) 517 } 518 if out == nil { 519 return structs.NewErrRPCCodedf(404, "cannot find token %s", token.AccessorID) 520 } 521 522 // Cannot toggle the "Global" mode 523 if token.Global != out.Global { 524 return structs.NewErrRPCCodedf(400, "cannot toggle global mode of %s", token.AccessorID) 525 } 526 } 527 528 // Compute the token hash 529 token.SetHash() 530 } 531 532 // Update via Raft 533 _, index, err := a.srv.raftApply(structs.ACLTokenUpsertRequestType, args) 534 if err != nil { 535 return err 536 } 537 538 // Populate the response. We do a lookup against the state to 539 // pickup the proper create / modify times. 540 state, err = a.srv.State().Snapshot() 541 if err != nil { 542 return err 543 } 544 for _, token := range args.Tokens { 545 out, err := state.ACLTokenByAccessorID(nil, token.AccessorID) 546 if err != nil { 547 return structs.NewErrRPCCodedf(400, "token lookup failed: %v", err) 548 } 549 reply.Tokens = append(reply.Tokens, out) 550 } 551 552 // Update the index 553 reply.Index = index 554 return nil 555 } 556 557 // DeleteTokens is used to delete tokens 558 func (a *ACL) DeleteTokens(args *structs.ACLTokenDeleteRequest, reply *structs.GenericResponse) error { 559 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 560 if !a.srv.config.ACLEnabled { 561 return aclDisabled 562 } 563 564 // Validate non-zero set of tokens 565 if len(args.AccessorIDs) == 0 { 566 return structs.NewErrRPCCoded(400, "must specify as least one token") 567 } 568 569 if done, err := a.srv.forward("ACL.DeleteTokens", args, args, reply); done { 570 return err 571 } 572 defer metrics.MeasureSince([]string{"nomad", "acl", "delete_tokens"}, time.Now()) 573 574 // Check management level permissions 575 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 576 return err 577 } else if acl == nil || !acl.IsManagement() { 578 return structs.ErrPermissionDenied 579 } 580 581 // Snapshot the state 582 state, err := a.srv.State().Snapshot() 583 if err != nil { 584 return err 585 } 586 587 // Determine if we are deleting local or global tokens 588 hasGlobal := false 589 allGlobal := true 590 nonexistentTokens := make([]string, 0) 591 for _, accessor := range args.AccessorIDs { 592 token, err := state.ACLTokenByAccessorID(nil, accessor) 593 if err != nil { 594 return structs.NewErrRPCCodedf(400, "token lookup failed: %v", err) 595 } 596 if token == nil { 597 nonexistentTokens = append(nonexistentTokens, accessor) 598 continue 599 } 600 if token.Global { 601 hasGlobal = true 602 } else { 603 allGlobal = false 604 } 605 } 606 607 if len(nonexistentTokens) != 0 { 608 return structs.NewErrRPCCodedf(400, "Cannot delete nonexistent tokens: %v", strings.Join(nonexistentTokens, ", ")) 609 } 610 611 // Disallow mixed requests with global and non-global tokens since we forward 612 // the entire request as a single batch. 613 if hasGlobal { 614 if !allGlobal { 615 return structs.NewErrRPCCoded(400, "cannot delete mixed global and non-global tokens") 616 } 617 618 // Force the request to the authoritative region if it has global 619 if a.srv.config.Region != a.srv.config.AuthoritativeRegion { 620 args.Region = a.srv.config.AuthoritativeRegion 621 _, err := a.srv.forward("ACL.DeleteTokens", args, args, reply) 622 return err 623 } 624 } 625 626 // Update via Raft 627 _, index, err := a.srv.raftApply(structs.ACLTokenDeleteRequestType, args) 628 if err != nil { 629 return err 630 } 631 632 // Update the index 633 reply.Index = index 634 return nil 635 } 636 637 // ListTokens is used to list the tokens 638 func (a *ACL) ListTokens(args *structs.ACLTokenListRequest, reply *structs.ACLTokenListResponse) error { 639 if !a.srv.config.ACLEnabled { 640 return aclDisabled 641 } 642 if done, err := a.srv.forward("ACL.ListTokens", args, args, reply); done { 643 return err 644 } 645 defer metrics.MeasureSince([]string{"nomad", "acl", "list_tokens"}, time.Now()) 646 647 // Check management level permissions 648 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 649 return err 650 } else if acl == nil || !acl.IsManagement() { 651 return structs.ErrPermissionDenied 652 } 653 654 // Setup the blocking query 655 opts := blockingOptions{ 656 queryOpts: &args.QueryOptions, 657 queryMeta: &reply.QueryMeta, 658 run: func(ws memdb.WatchSet, state *state.StateStore) error { 659 // Iterate over all the tokens 660 var err error 661 var iter memdb.ResultIterator 662 if prefix := args.QueryOptions.Prefix; prefix != "" { 663 iter, err = state.ACLTokenByAccessorIDPrefix(ws, prefix) 664 } else if args.GlobalOnly { 665 iter, err = state.ACLTokensByGlobal(ws, true) 666 } else { 667 iter, err = state.ACLTokens(ws) 668 } 669 if err != nil { 670 return err 671 } 672 673 // Convert all the tokens to a list stub 674 reply.Tokens = nil 675 for { 676 raw := iter.Next() 677 if raw == nil { 678 break 679 } 680 token := raw.(*structs.ACLToken) 681 reply.Tokens = append(reply.Tokens, token.Stub()) 682 } 683 684 // Use the last index that affected the token table 685 index, err := state.Index("acl_token") 686 if err != nil { 687 return err 688 } 689 reply.Index = index 690 return nil 691 }} 692 return a.srv.blockingRPC(&opts) 693 } 694 695 // GetToken is used to get a specific token 696 func (a *ACL) GetToken(args *structs.ACLTokenSpecificRequest, reply *structs.SingleACLTokenResponse) error { 697 if !a.srv.config.ACLEnabled { 698 return aclDisabled 699 } 700 if done, err := a.srv.forward("ACL.GetToken", args, args, reply); done { 701 return err 702 } 703 defer metrics.MeasureSince([]string{"nomad", "acl", "get_token"}, time.Now()) 704 705 acl, err := a.srv.ResolveToken(args.AuthToken) 706 if err != nil { 707 return err 708 } 709 710 // Ensure ACLs are enabled and this call is made with one 711 if acl == nil { 712 return structs.ErrPermissionDenied 713 } 714 715 // Setup the blocking query 716 opts := blockingOptions{ 717 queryOpts: &args.QueryOptions, 718 queryMeta: &reply.QueryMeta, 719 run: func(ws memdb.WatchSet, state *state.StateStore) error { 720 // Look for the token 721 out, err := state.ACLTokenByAccessorID(ws, args.AccessorID) 722 if err != nil { 723 return err 724 } 725 726 if out == nil { 727 // If the token doesn't resolve, only allow management tokens to 728 // block. 729 if !acl.IsManagement() { 730 return structs.ErrPermissionDenied 731 } 732 733 // Check management level permissions or that the secret ID matches the 734 // accessor ID 735 } else if !acl.IsManagement() && out.SecretID != args.AuthToken { 736 return structs.ErrPermissionDenied 737 } 738 739 // Setup the output 740 reply.Token = out 741 if out != nil { 742 reply.Index = out.ModifyIndex 743 } else { 744 // Use the last index that affected the token table 745 index, err := state.Index("acl_token") 746 if err != nil { 747 return err 748 } 749 reply.Index = index 750 } 751 return nil 752 }} 753 return a.srv.blockingRPC(&opts) 754 } 755 756 // GetTokens is used to get a set of token 757 func (a *ACL) GetTokens(args *structs.ACLTokenSetRequest, reply *structs.ACLTokenSetResponse) error { 758 if !a.srv.config.ACLEnabled { 759 return aclDisabled 760 } 761 if done, err := a.srv.forward("ACL.GetTokens", args, args, reply); done { 762 return err 763 } 764 defer metrics.MeasureSince([]string{"nomad", "acl", "get_tokens"}, time.Now()) 765 766 // Check management level permissions 767 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 768 return err 769 } else if acl == nil || !acl.IsManagement() { 770 return structs.ErrPermissionDenied 771 } 772 773 // Setup the blocking query 774 opts := blockingOptions{ 775 queryOpts: &args.QueryOptions, 776 queryMeta: &reply.QueryMeta, 777 run: func(ws memdb.WatchSet, state *state.StateStore) error { 778 // Setup the output 779 reply.Tokens = make(map[string]*structs.ACLToken, len(args.AccessorIDS)) 780 781 // Look for the token 782 for _, accessor := range args.AccessorIDS { 783 out, err := state.ACLTokenByAccessorID(ws, accessor) 784 if err != nil { 785 return err 786 } 787 if out != nil { 788 reply.Tokens[out.AccessorID] = out 789 } 790 } 791 792 // Use the last index that affected the token table 793 index, err := state.Index("acl_token") 794 if err != nil { 795 return err 796 } 797 reply.Index = index 798 return nil 799 }} 800 return a.srv.blockingRPC(&opts) 801 } 802 803 // ResolveToken is used to lookup a specific token by a secret ID. This is used for enforcing ACLs by clients. 804 func (a *ACL) ResolveToken(args *structs.ResolveACLTokenRequest, reply *structs.ResolveACLTokenResponse) error { 805 if !a.srv.config.ACLEnabled { 806 return aclDisabled 807 } 808 if done, err := a.srv.forward("ACL.ResolveToken", args, args, reply); done { 809 return err 810 } 811 defer metrics.MeasureSince([]string{"nomad", "acl", "resolve_token"}, time.Now()) 812 813 // Setup the query meta 814 a.srv.setQueryMeta(&reply.QueryMeta) 815 816 // Snapshot the state 817 state, err := a.srv.State().Snapshot() 818 if err != nil { 819 return err 820 } 821 822 // Look for the token 823 out, err := state.ACLTokenBySecretID(nil, args.SecretID) 824 if err != nil { 825 return err 826 } 827 828 // Setup the output 829 reply.Token = out 830 if out != nil { 831 reply.Index = out.ModifyIndex 832 } else { 833 // Use the last index that affected the token table 834 index, err := state.Index("acl_token") 835 if err != nil { 836 return err 837 } 838 reply.Index = index 839 } 840 return nil 841 }