github.com/hernad/nomad@v1.6.112/nomad/structs/acl.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package structs 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "regexp" 12 "strconv" 13 "time" 14 15 "github.com/hashicorp/go-bexpr" 16 "github.com/hashicorp/go-multierror" 17 "github.com/hashicorp/go-set" 18 lru "github.com/hashicorp/golang-lru/v2" 19 "github.com/hernad/nomad/helper" 20 "github.com/hernad/nomad/helper/pointer" 21 "github.com/hernad/nomad/helper/uuid" 22 "github.com/hernad/nomad/lib/lang" 23 "golang.org/x/crypto/blake2b" 24 "golang.org/x/exp/slices" 25 "oss.indeed.com/go/libtime" 26 ) 27 28 const ( 29 // ACLUpsertPoliciesRPCMethod is the RPC method for batch creating or 30 // modifying ACL policies. 31 // 32 // Args: ACLPolicyUpsertRequest 33 // Reply: GenericResponse 34 ACLUpsertPoliciesRPCMethod = "ACL.UpsertPolicies" 35 36 // ACLUpsertTokensRPCMethod is the RPC method for batch creating or 37 // modifying ACL tokens. 38 // 39 // Args: ACLTokenUpsertRequest 40 // Reply: ACLTokenUpsertResponse 41 ACLUpsertTokensRPCMethod = "ACL.UpsertTokens" 42 43 // ACLDeleteTokensRPCMethod is the RPC method for batch deleting ACL 44 // tokens. 45 // 46 // Args: ACLTokenDeleteRequest 47 // Reply: GenericResponse 48 ACLDeleteTokensRPCMethod = "ACL.DeleteTokens" 49 50 // ACLUpsertRolesRPCMethod is the RPC method for batch creating or 51 // modifying ACL roles. 52 // 53 // Args: ACLRolesUpsertRequest 54 // Reply: ACLRolesUpsertResponse 55 ACLUpsertRolesRPCMethod = "ACL.UpsertRoles" 56 57 // ACLDeleteRolesByIDRPCMethod the RPC method for batch deleting ACL 58 // roles by their ID. 59 // 60 // Args: ACLRolesDeleteByIDRequest 61 // Reply: ACLRolesDeleteByIDResponse 62 ACLDeleteRolesByIDRPCMethod = "ACL.DeleteRolesByID" 63 64 // ACLListRolesRPCMethod is the RPC method for listing ACL roles. 65 // 66 // Args: ACLRolesListRequest 67 // Reply: ACLRolesListResponse 68 ACLListRolesRPCMethod = "ACL.ListRoles" 69 70 // ACLGetRolesByIDRPCMethod is the RPC method for detailing a number of ACL 71 // roles using their ID. This is an internal only RPC endpoint and used by 72 // the ACL Role replication process. 73 // 74 // Args: ACLRolesByIDRequest 75 // Reply: ACLRolesByIDResponse 76 ACLGetRolesByIDRPCMethod = "ACL.GetRolesByID" 77 78 // ACLGetRoleByIDRPCMethod is the RPC method for detailing an individual 79 // ACL role using its ID. 80 // 81 // Args: ACLRoleByIDRequest 82 // Reply: ACLRoleByIDResponse 83 ACLGetRoleByIDRPCMethod = "ACL.GetRoleByID" 84 85 // ACLGetRoleByNameRPCMethod is the RPC method for detailing an individual 86 // ACL role using its name. 87 // 88 // Args: ACLRoleByNameRequest 89 // Reply: ACLRoleByNameResponse 90 ACLGetRoleByNameRPCMethod = "ACL.GetRoleByName" 91 92 // ACLUpsertAuthMethodsRPCMethod is the RPC method for batch creating or 93 // modifying auth methods. 94 // 95 // Args: ACLAuthMethodsUpsertRequest 96 // Reply: ACLAuthMethodUpsertResponse 97 ACLUpsertAuthMethodsRPCMethod = "ACL.UpsertAuthMethods" 98 99 // ACLDeleteAuthMethodsRPCMethod is the RPC method for batch deleting auth 100 // methods. 101 // 102 // Args: ACLAuthMethodDeleteRequest 103 // Reply: ACLAuthMethodDeleteResponse 104 ACLDeleteAuthMethodsRPCMethod = "ACL.DeleteAuthMethods" 105 106 // ACLListAuthMethodsRPCMethod is the RPC method for listing auth methods. 107 // 108 // Args: ACLAuthMethodListRequest 109 // Reply: ACLAuthMethodListResponse 110 ACLListAuthMethodsRPCMethod = "ACL.ListAuthMethods" 111 112 // ACLGetAuthMethodRPCMethod is the RPC method for detailing an individual 113 // auth method using its name. 114 // 115 // Args: ACLAuthMethodGetRequest 116 // Reply: ACLAuthMethodGetResponse 117 ACLGetAuthMethodRPCMethod = "ACL.GetAuthMethod" 118 119 // ACLGetAuthMethodsRPCMethod is the RPC method for getting multiple auth 120 // methods using their names. 121 // 122 // Args: ACLAuthMethodsGetRequest 123 // Reply: ACLAuthMethodsGetResponse 124 ACLGetAuthMethodsRPCMethod = "ACL.GetAuthMethods" 125 126 // ACLUpsertBindingRulesRPCMethod is the RPC method for batch creating or 127 // modifying binding rules. 128 // 129 // Args: ACLBindingRulesUpsertRequest 130 // Reply: ACLBindingRulesUpsertResponse 131 ACLUpsertBindingRulesRPCMethod = "ACL.UpsertBindingRules" 132 133 // ACLDeleteBindingRulesRPCMethod is the RPC method for batch deleting 134 // binding rules. 135 // 136 // Args: ACLBindingRulesDeleteRequest 137 // Reply: ACLBindingRulesDeleteResponse 138 ACLDeleteBindingRulesRPCMethod = "ACL.DeleteBindingRules" 139 140 // ACLListBindingRulesRPCMethod is the RPC method listing binding rules. 141 // 142 // Args: ACLBindingRulesListRequest 143 // Reply: ACLBindingRulesListResponse 144 ACLListBindingRulesRPCMethod = "ACL.ListBindingRules" 145 146 // ACLGetBindingRulesRPCMethod is the RPC method for getting multiple 147 // binding rules using their IDs. 148 // 149 // Args: ACLBindingRulesRequest 150 // Reply: ACLBindingRulesResponse 151 ACLGetBindingRulesRPCMethod = "ACL.GetBindingRules" 152 153 // ACLGetBindingRuleRPCMethod is the RPC method for detailing an individual 154 // binding rule using its ID. 155 // 156 // Args: ACLBindingRuleRequest 157 // Reply: ACLBindingRuleResponse 158 ACLGetBindingRuleRPCMethod = "ACL.GetBindingRule" 159 160 // ACLOIDCAuthURLRPCMethod is the RPC method for starting the OIDC login 161 // workflow. It generates the OIDC provider URL which will be used for user 162 // authentication. 163 // 164 // Args: ACLOIDCAuthURLRequest 165 // Reply: ACLOIDCAuthURLResponse 166 ACLOIDCAuthURLRPCMethod = "ACL.OIDCAuthURL" 167 168 // ACLOIDCCompleteAuthRPCMethod is the RPC method for completing the OIDC 169 // login workflow. It exchanges the OIDC provider token for a Nomad ACL 170 // token with roles as defined within the remote provider. 171 // 172 // Args: ACLOIDCCompleteAuthRequest 173 // Reply: ACLOIDCCompleteAuthResponse 174 ACLOIDCCompleteAuthRPCMethod = "ACL.OIDCCompleteAuth" 175 176 // ACLLoginRPCMethod is the RPC method for performing a non-OIDC login 177 // workflow. It exchanges the provided token for a Nomad ACL token with 178 // roles as defined within the remote provider. 179 // 180 // Args: ACLLoginRequest 181 // Reply: ACLLoginResponse 182 ACLLoginRPCMethod = "ACL.Login" 183 ) 184 185 const ( 186 // ACLMaxExpiredBatchSize is the maximum number of expired ACL tokens that 187 // will be garbage collected in a single trigger. This number helps limit 188 // the replication pressure due to expired token deletion. If there are a 189 // large number of expired tokens pending garbage collection, this value is 190 // a potential limiting factor. 191 ACLMaxExpiredBatchSize = 4096 192 193 // maxACLRoleDescriptionLength limits an ACL roles description length. 194 maxACLRoleDescriptionLength = 256 195 196 // maxACLBindingRuleDescriptionLength limits an ACL binding rules 197 // description length and should be used to validate the object. 198 maxACLBindingRuleDescriptionLength = 256 199 200 // ACLAuthMethodTokenLocalityLocal is the ACLAuthMethod.TokenLocality that 201 // will generate ACL tokens which can only be used on the local cluster the 202 // request was made. 203 ACLAuthMethodTokenLocalityLocal = "local" 204 205 // ACLAuthMethodTokenLocalityGlobal is the ACLAuthMethod.TokenLocality that 206 // will generate ACL tokens which can be used on all federated clusters. 207 ACLAuthMethodTokenLocalityGlobal = "global" 208 209 // ACLAuthMethodTypeOIDC the ACLAuthMethod.Type and represents an 210 // auth-method which uses the OIDC protocol. 211 ACLAuthMethodTypeOIDC = "OIDC" 212 213 // ACLAuthMethodTypeJWT the ACLAuthMethod.Type and represents an auth-method 214 // which uses the JWT type. 215 ACLAuthMethodTypeJWT = "JWT" 216 ) 217 218 var ( 219 // ValidACLRoleName is used to validate an ACL role name. 220 ValidACLRoleName = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$") 221 222 // ValidACLAuthMethod is used to validate an ACL auth method name. 223 ValidACLAuthMethod = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$") 224 225 // ValitACLAuthMethodTypes lists supported auth method types. 226 ValidACLAuthMethodTypes = []string{ACLAuthMethodTypeOIDC, ACLAuthMethodTypeJWT} 227 ) 228 229 type ACLCacheEntry[T any] lang.Pair[T, time.Time] 230 231 func (e ACLCacheEntry[T]) Age() time.Duration { 232 return time.Since(e.Second) 233 } 234 235 func (e ACLCacheEntry[T]) Get() T { 236 return e.First 237 } 238 239 // An ACLCache caches ACL tokens by their policy content. 240 type ACLCache[T any] struct { 241 *lru.TwoQueueCache[string, ACLCacheEntry[T]] 242 clock libtime.Clock 243 } 244 245 func (c *ACLCache[T]) Add(key string, item T) { 246 c.AddAtTime(key, item, c.clock.Now()) 247 } 248 249 func (c *ACLCache[T]) AddAtTime(key string, item T, now time.Time) { 250 c.TwoQueueCache.Add(key, ACLCacheEntry[T]{ 251 First: item, 252 Second: now, 253 }) 254 } 255 256 func NewACLCache[T any](size int) *ACLCache[T] { 257 c, err := lru.New2Q[string, ACLCacheEntry[T]](size) 258 if err != nil { 259 panic(err) // not possible 260 } 261 return &ACLCache[T]{ 262 TwoQueueCache: c, 263 clock: libtime.SystemClock(), 264 } 265 } 266 267 // ACLTokenRoleLink is used to link an ACL token to an ACL role. The ACL token 268 // can therefore inherit all the ACL policy permissions that the ACL role 269 // contains. 270 type ACLTokenRoleLink struct { 271 272 // ID is the ACLRole.ID UUID. This field is immutable and represents the 273 // absolute truth for the link. 274 ID string 275 276 // Name is the human friendly identifier for the ACL role and is a 277 // convenience field for operators. This field is always resolved to the 278 // ID and discarded before the token is stored in state. This is because 279 // operators can change the name of an ACL role. 280 Name string 281 } 282 283 // Canonicalize performs basic canonicalization on the ACL token object. It is 284 // important for callers to understand certain fields such as AccessorID are 285 // set if it is empty, so copies should be taken if needed before calling this 286 // function. 287 func (a *ACLToken) Canonicalize() { 288 289 // If the accessor ID is empty, it means this is creation of a new token, 290 // therefore we need to generate base information. 291 if a.AccessorID == "" { 292 293 a.AccessorID = uuid.Generate() 294 a.SecretID = uuid.Generate() 295 a.CreateTime = time.Now().UTC() 296 297 // If the user has not set the expiration time, but has provided a TTL, we 298 // calculate and populate the former filed. 299 if a.ExpirationTime == nil && a.ExpirationTTL != 0 { 300 a.ExpirationTime = pointer.Of(a.CreateTime.Add(a.ExpirationTTL)) 301 } 302 } 303 } 304 305 // Validate is used to check a token for reasonableness 306 func (a *ACLToken) Validate(minTTL, maxTTL time.Duration, existing *ACLToken) error { 307 var mErr multierror.Error 308 309 // The human friendly name of an ACL token cannot exceed 256 characters. 310 if len(a.Name) > maxTokenNameLength { 311 mErr.Errors = append(mErr.Errors, errors.New("token name too long")) 312 } 313 314 // The type of an ACL token must be set. An ACL token of type client must 315 // have associated policies or roles, whereas a management token cannot be 316 // associated with policies. 317 switch a.Type { 318 case ACLClientToken: 319 if len(a.Policies) == 0 && len(a.Roles) == 0 { 320 mErr.Errors = append(mErr.Errors, errors.New("client token missing policies or roles")) 321 } 322 case ACLManagementToken: 323 if len(a.Policies) != 0 || len(a.Roles) != 0 { 324 mErr.Errors = append(mErr.Errors, errors.New("management token cannot be associated with policies or roles")) 325 } 326 default: 327 mErr.Errors = append(mErr.Errors, errors.New("token type must be client or management")) 328 } 329 330 // There are different validation rules depending on whether the ACL token 331 // is being created or updated. 332 switch existing { 333 case nil: 334 if a.ExpirationTTL < 0 { 335 mErr.Errors = append(mErr.Errors, 336 fmt.Errorf("token expiration TTL '%s' should not be negative", a.ExpirationTTL)) 337 } 338 339 if a.ExpirationTime != nil && !a.ExpirationTime.IsZero() { 340 341 if a.CreateTime.After(*a.ExpirationTime) { 342 mErr.Errors = append(mErr.Errors, errors.New("expiration time cannot be before create time")) 343 } 344 345 // Create a time duration which details the time-til-expiry, so we can 346 // check this against the regions max and min values. 347 expiresIn := a.ExpirationTime.Sub(a.CreateTime) 348 if expiresIn > maxTTL { 349 mErr.Errors = append(mErr.Errors, 350 fmt.Errorf("expiration time cannot be more than %s in the future (was %s)", 351 maxTTL, expiresIn)) 352 353 } else if expiresIn < minTTL { 354 mErr.Errors = append(mErr.Errors, 355 fmt.Errorf("expiration time cannot be less than %s in the future (was %s)", 356 minTTL, expiresIn)) 357 } 358 } 359 default: 360 if existing.Global != a.Global { 361 mErr.Errors = append(mErr.Errors, errors.New("cannot toggle global mode")) 362 } 363 if existing.ExpirationTTL != a.ExpirationTTL { 364 mErr.Errors = append(mErr.Errors, errors.New("cannot update expiration TTL")) 365 } 366 if existing.ExpirationTime != a.ExpirationTime { 367 mErr.Errors = append(mErr.Errors, errors.New("cannot update expiration time")) 368 } 369 } 370 371 return mErr.ErrorOrNil() 372 } 373 374 // HasExpirationTime checks whether the ACL token has an expiration time value 375 // set. 376 func (a *ACLToken) HasExpirationTime() bool { 377 if a == nil || a.ExpirationTime == nil { 378 return false 379 } 380 return !a.ExpirationTime.IsZero() 381 } 382 383 // IsExpired compares the ACLToken.ExpirationTime against the passed t to 384 // identify whether the token is considered expired. The function can be called 385 // without checking whether the ACL token has an expiry time. 386 func (a *ACLToken) IsExpired(t time.Time) bool { 387 388 // Check the token has an expiration time before potentially modifying the 389 // supplied time. This allows us to avoid extra work, if it isn't needed. 390 if !a.HasExpirationTime() { 391 return false 392 } 393 394 // Check and ensure the time location is set to UTC. This is vital for 395 // consistency with multi-region global tokens. 396 if t.Location() != time.UTC { 397 t = t.UTC() 398 } 399 400 return a.ExpirationTime.Before(t) || t.IsZero() 401 } 402 403 // HasRoles checks if a given set of role IDs are assigned to the ACL token. It 404 // does not account for management tokens, therefore it is the responsibility 405 // of the caller to perform this check, if required. 406 func (a *ACLToken) HasRoles(roleIDs []string) bool { 407 408 // Generate a set of role IDs that the token is assigned. 409 roleSet := set.FromFunc(a.Roles, func(roleLink *ACLTokenRoleLink) string { return roleLink.ID }) 410 411 // Iterate the role IDs within the request and check whether these are 412 // present within the token assignment. 413 for _, roleID := range roleIDs { 414 if !roleSet.Contains(roleID) { 415 return false 416 } 417 } 418 return true 419 } 420 421 // MarshalJSON implements the json.Marshaler interface and allows 422 // ACLToken.ExpirationTTL to be marshaled correctly. 423 func (a *ACLToken) MarshalJSON() ([]byte, error) { 424 type Alias ACLToken 425 exported := &struct { 426 ExpirationTTL string 427 *Alias 428 }{ 429 ExpirationTTL: a.ExpirationTTL.String(), 430 Alias: (*Alias)(a), 431 } 432 if a.ExpirationTTL == 0 { 433 exported.ExpirationTTL = "" 434 } 435 return json.Marshal(exported) 436 } 437 438 // UnmarshalJSON implements the json.Unmarshaler interface and allows 439 // ACLToken.ExpirationTTL to be unmarshalled correctly. 440 func (a *ACLToken) UnmarshalJSON(data []byte) (err error) { 441 type Alias ACLToken 442 aux := &struct { 443 ExpirationTTL interface{} 444 Hash string 445 *Alias 446 }{ 447 Alias: (*Alias)(a), 448 } 449 450 if err = json.Unmarshal(data, &aux); err != nil { 451 return err 452 } 453 if aux.ExpirationTTL != nil { 454 switch v := aux.ExpirationTTL.(type) { 455 case string: 456 if v != "" { 457 if a.ExpirationTTL, err = time.ParseDuration(v); err != nil { 458 return err 459 } 460 } 461 case float64: 462 a.ExpirationTTL = time.Duration(v) 463 } 464 465 } 466 if aux.Hash != "" { 467 a.Hash = []byte(aux.Hash) 468 } 469 return nil 470 } 471 472 // ACLRole is an abstraction for the ACL system which allows the grouping of 473 // ACL policies into a single object. ACL tokens can be created and linked to 474 // a role; the token then inherits all the permissions granted by the policies. 475 type ACLRole struct { 476 477 // ID is an internally generated UUID for this role and is controlled by 478 // Nomad. 479 ID string 480 481 // Name is unique across the entire set of federated clusters and is 482 // supplied by the operator on role creation. The name can be modified by 483 // updating the role and including the Nomad generated ID. This update will 484 // not affect tokens created and linked to this role. This is a required 485 // field. 486 Name string 487 488 // Description is a human-readable, operator set description that can 489 // provide additional context about the role. This is an operational field. 490 Description string 491 492 // Policies is an array of ACL policy links. Although currently policies 493 // can only be linked using their name, in the future we will want to add 494 // IDs also and thus allow operators to specify either a name, an ID, or 495 // both. 496 Policies []*ACLRolePolicyLink 497 498 // Hash is the hashed value of the role and is generated using all fields 499 // above this point. 500 Hash []byte 501 502 CreateIndex uint64 503 ModifyIndex uint64 504 } 505 506 // ACLRolePolicyLink is used to link a policy to an ACL role. We use a struct 507 // rather than a list of strings as in the future we will want to add IDs to 508 // policies and then link via these. 509 type ACLRolePolicyLink struct { 510 511 // Name is the ACLPolicy.Name value which will be linked to the ACL role. 512 Name string 513 } 514 515 // SetHash is used to compute and set the hash of the ACL role. This should be 516 // called every and each time a user specified field on the role is changed 517 // before updating the Nomad state store. 518 func (a *ACLRole) SetHash() []byte { 519 520 // Initialize a 256bit Blake2 hash (32 bytes). 521 hash, err := blake2b.New256(nil) 522 if err != nil { 523 panic(err) 524 } 525 526 // Write all the user set fields. 527 _, _ = hash.Write([]byte(a.Name)) 528 _, _ = hash.Write([]byte(a.Description)) 529 530 for _, policyLink := range a.Policies { 531 _, _ = hash.Write([]byte(policyLink.Name)) 532 } 533 534 // Finalize the hash. 535 hashVal := hash.Sum(nil) 536 537 // Set and return the hash. 538 a.Hash = hashVal 539 return hashVal 540 } 541 542 // Validate ensure the ACL role contains valid information which meets Nomad's 543 // internal requirements. This does not include any state calls, such as 544 // ensuring the linked policies exist. 545 func (a *ACLRole) Validate() error { 546 547 var mErr multierror.Error 548 549 if !ValidACLRoleName.MatchString(a.Name) { 550 mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid name '%s'", a.Name)) 551 } 552 553 if len(a.Description) > maxACLRoleDescriptionLength { 554 mErr.Errors = append(mErr.Errors, fmt.Errorf("description longer than %d", maxACLRoleDescriptionLength)) 555 } 556 557 if len(a.Policies) < 1 { 558 mErr.Errors = append(mErr.Errors, errors.New("at least one policy should be specified")) 559 } 560 561 return mErr.ErrorOrNil() 562 } 563 564 // Canonicalize performs basic canonicalization on the ACL role object. It is 565 // important for callers to understand certain fields such as ID are set if it 566 // is empty, so copies should be taken if needed before calling this function. 567 func (a *ACLRole) Canonicalize() { 568 if a.ID == "" { 569 a.ID = uuid.Generate() 570 } 571 } 572 573 // Equal performs an equality check on the two service registrations. It 574 // handles nil objects. 575 func (a *ACLRole) Equal(o *ACLRole) bool { 576 if a == nil || o == nil { 577 return a == o 578 } 579 if len(a.Hash) == 0 { 580 a.SetHash() 581 } 582 if len(o.Hash) == 0 { 583 o.SetHash() 584 } 585 return bytes.Equal(a.Hash, o.Hash) 586 } 587 588 // Copy creates a deep copy of the ACL role. This copy can then be safely 589 // modified. It handles nil objects. 590 func (a *ACLRole) Copy() *ACLRole { 591 if a == nil { 592 return nil 593 } 594 595 c := new(ACLRole) 596 *c = *a 597 598 c.Policies = slices.Clone(a.Policies) 599 c.Hash = slices.Clone(a.Hash) 600 601 return c 602 } 603 604 // Stub converts the ACLRole object into a ACLRoleListStub object. 605 func (a *ACLRole) Stub() *ACLRoleListStub { 606 return &ACLRoleListStub{ 607 ID: a.ID, 608 Name: a.Name, 609 Description: a.Description, 610 Policies: a.Policies, 611 Hash: a.Hash, 612 CreateIndex: a.CreateIndex, 613 ModifyIndex: a.ModifyIndex, 614 } 615 } 616 617 // ACLRoleListStub is the stub object returned when performing a listing of ACL 618 // roles. While it might not currently be different to the full response 619 // object, it allows us to future-proof the RPC in the event the ACLRole object 620 // grows over time. 621 type ACLRoleListStub struct { 622 623 // ID is an internally generated UUID for this role and is controlled by 624 // Nomad. 625 ID string 626 627 // Name is unique across the entire set of federated clusters and is 628 // supplied by the operator on role creation. The name can be modified by 629 // updating the role and including the Nomad generated ID. This update will 630 // not affect tokens created and linked to this role. This is a required 631 // field. 632 Name string 633 634 // Description is a human-readable, operator set description that can 635 // provide additional context about the role. This is an operational field. 636 Description string 637 638 // Policies is an array of ACL policy links. Although currently policies 639 // can only be linked using their name, in the future we will want to add 640 // IDs also and thus allow operators to specify either a name, an ID, or 641 // both. 642 Policies []*ACLRolePolicyLink 643 644 // Hash is the hashed value of the role and is generated using all fields 645 // above this point. 646 Hash []byte 647 648 CreateIndex uint64 649 ModifyIndex uint64 650 } 651 652 // ACLRolesUpsertRequest is the request object used to upsert one or more ACL 653 // roles. 654 type ACLRolesUpsertRequest struct { 655 ACLRoles []*ACLRole 656 657 // AllowMissingPolicies skips the ACL Role policy link verification and is 658 // used by the replication process. The replication cannot ensure policies 659 // are present before ACL Roles are replicated. 660 AllowMissingPolicies bool 661 662 WriteRequest 663 } 664 665 // ACLRolesUpsertResponse is the response object when one or more ACL roles 666 // have been successfully upserted into state. 667 type ACLRolesUpsertResponse struct { 668 ACLRoles []*ACLRole 669 WriteMeta 670 } 671 672 // ACLRolesDeleteByIDRequest is the request object to delete one or more ACL 673 // roles using the role ID. 674 type ACLRolesDeleteByIDRequest struct { 675 ACLRoleIDs []string 676 WriteRequest 677 } 678 679 // ACLRolesDeleteByIDResponse is the response object when performing a deletion 680 // of one or more ACL roles using the role ID. 681 type ACLRolesDeleteByIDResponse struct { 682 WriteMeta 683 } 684 685 // ACLRolesListRequest is the request object when performing ACL role listings. 686 type ACLRolesListRequest struct { 687 QueryOptions 688 } 689 690 // ACLRolesListResponse is the response object when performing ACL role 691 // listings. 692 type ACLRolesListResponse struct { 693 ACLRoles []*ACLRoleListStub 694 QueryMeta 695 } 696 697 // ACLRolesByIDRequest is the request object when performing a lookup of 698 // multiple roles by the ID. 699 type ACLRolesByIDRequest struct { 700 ACLRoleIDs []string 701 QueryOptions 702 } 703 704 // ACLRolesByIDResponse is the response object when performing a lookup of 705 // multiple roles by their IDs. 706 type ACLRolesByIDResponse struct { 707 ACLRoles map[string]*ACLRole 708 QueryMeta 709 } 710 711 // ACLRoleByIDRequest is the request object to perform a lookup of an ACL 712 // role using a specific ID. 713 type ACLRoleByIDRequest struct { 714 RoleID string 715 QueryOptions 716 } 717 718 // ACLRoleByIDResponse is the response object when performing a lookup of an 719 // ACL role matching a specific ID. 720 type ACLRoleByIDResponse struct { 721 ACLRole *ACLRole 722 QueryMeta 723 } 724 725 // ACLRoleByNameRequest is the request object to perform a lookup of an ACL 726 // role using a specific name. 727 type ACLRoleByNameRequest struct { 728 RoleName string 729 QueryOptions 730 } 731 732 // ACLRoleByNameResponse is the response object when performing a lookup of an 733 // ACL role matching a specific name. 734 type ACLRoleByNameResponse struct { 735 ACLRole *ACLRole 736 QueryMeta 737 } 738 739 // ACLAuthMethod is used to capture the properties of an authentication method 740 // used for single sing-on 741 type ACLAuthMethod struct { 742 Name string 743 Type string 744 TokenLocality string // is the token valid locally or globally? 745 MaxTokenTTL time.Duration 746 Default bool 747 Config *ACLAuthMethodConfig 748 749 Hash []byte 750 751 CreateTime time.Time 752 ModifyTime time.Time 753 CreateIndex uint64 754 ModifyIndex uint64 755 } 756 757 // SetHash is used to compute and set the hash of the ACL auth method. This 758 // should be called every and each time a user specified field on the method is 759 // changed before updating the Nomad state store. 760 func (a *ACLAuthMethod) SetHash() []byte { 761 762 // Initialize a 256bit Blake2 hash (32 bytes). 763 hash, err := blake2b.New256(nil) 764 if err != nil { 765 panic(err) 766 } 767 768 _, _ = hash.Write([]byte(a.Name)) 769 _, _ = hash.Write([]byte(a.Type)) 770 _, _ = hash.Write([]byte(a.TokenLocality)) 771 _, _ = hash.Write([]byte(a.MaxTokenTTL.String())) 772 _, _ = hash.Write([]byte(strconv.FormatBool(a.Default))) 773 774 if a.Config != nil { 775 _, _ = hash.Write([]byte(a.Config.OIDCDiscoveryURL)) 776 _, _ = hash.Write([]byte(a.Config.OIDCClientID)) 777 _, _ = hash.Write([]byte(a.Config.OIDCClientSecret)) 778 for _, ba := range a.Config.BoundAudiences { 779 _, _ = hash.Write([]byte(ba)) 780 } 781 for _, uri := range a.Config.AllowedRedirectURIs { 782 _, _ = hash.Write([]byte(uri)) 783 } 784 for _, pem := range a.Config.DiscoveryCaPem { 785 _, _ = hash.Write([]byte(pem)) 786 } 787 for _, sa := range a.Config.SigningAlgs { 788 _, _ = hash.Write([]byte(sa)) 789 } 790 for k, v := range a.Config.ClaimMappings { 791 _, _ = hash.Write([]byte(k)) 792 _, _ = hash.Write([]byte(v)) 793 } 794 for k, v := range a.Config.ListClaimMappings { 795 _, _ = hash.Write([]byte(k)) 796 _, _ = hash.Write([]byte(v)) 797 } 798 } 799 800 // Finalize the hash. 801 hashVal := hash.Sum(nil) 802 803 // Set and return the hash. 804 a.Hash = hashVal 805 return hashVal 806 } 807 808 // MarshalJSON implements the json.Marshaler interface and allows 809 // ACLAuthMethod.MaxTokenTTL to be marshaled correctly. 810 func (a *ACLAuthMethod) MarshalJSON() ([]byte, error) { 811 type Alias ACLAuthMethod 812 exported := &struct { 813 MaxTokenTTL string 814 *Alias 815 }{ 816 MaxTokenTTL: a.MaxTokenTTL.String(), 817 Alias: (*Alias)(a), 818 } 819 if a.MaxTokenTTL == 0 { 820 exported.MaxTokenTTL = "" 821 } 822 return json.Marshal(exported) 823 } 824 825 // UnmarshalJSON implements the json.Unmarshaler interface and allows 826 // ACLAuthMethod.MaxTokenTTL to be unmarshalled correctly. 827 func (a *ACLAuthMethod) UnmarshalJSON(data []byte) (err error) { 828 type Alias ACLAuthMethod 829 aux := &struct { 830 MaxTokenTTL interface{} 831 *Alias 832 }{ 833 Alias: (*Alias)(a), 834 } 835 if err = json.Unmarshal(data, &aux); err != nil { 836 return err 837 } 838 if aux.MaxTokenTTL != nil { 839 switch v := aux.MaxTokenTTL.(type) { 840 case string: 841 if a.MaxTokenTTL, err = time.ParseDuration(v); err != nil { 842 return err 843 } 844 case float64: 845 a.MaxTokenTTL = time.Duration(v) 846 } 847 } 848 return nil 849 } 850 851 func (a *ACLAuthMethod) Stub() *ACLAuthMethodStub { 852 return &ACLAuthMethodStub{ 853 Name: a.Name, 854 Type: a.Type, 855 Default: a.Default, 856 Hash: a.Hash, 857 CreateIndex: a.CreateIndex, 858 ModifyIndex: a.ModifyIndex, 859 } 860 } 861 862 func (a *ACLAuthMethod) Equal(other *ACLAuthMethod) bool { 863 if a == nil || other == nil { 864 return a == other 865 } 866 if len(a.Hash) == 0 { 867 a.SetHash() 868 } 869 if len(other.Hash) == 0 { 870 other.SetHash() 871 } 872 return bytes.Equal(a.Hash, other.Hash) 873 874 } 875 876 // Copy creates a deep copy of the ACL auth method. This copy can then be safely 877 // modified. It handles nil objects. 878 func (a *ACLAuthMethod) Copy() *ACLAuthMethod { 879 if a == nil { 880 return nil 881 } 882 883 c := new(ACLAuthMethod) 884 *c = *a 885 886 c.Hash = slices.Clone(a.Hash) 887 c.Config = a.Config.Copy() 888 889 return c 890 } 891 892 // Canonicalize performs basic canonicalization on the ACL auth method object. 893 func (a *ACLAuthMethod) Canonicalize() { 894 t := time.Now().UTC() 895 896 if a.CreateTime.IsZero() { 897 a.CreateTime = t 898 } 899 a.ModifyTime = t 900 } 901 902 // Merge merges auth method a with method b. It sets all required empty fields 903 // of method a to corresponding values of method b, except for "default" and 904 // "name." 905 func (a *ACLAuthMethod) Merge(b *ACLAuthMethod) { 906 if b != nil { 907 a.Type = helper.Merge(a.Type, b.Type) 908 a.TokenLocality = helper.Merge(a.TokenLocality, b.TokenLocality) 909 a.MaxTokenTTL = helper.Merge(a.MaxTokenTTL, b.MaxTokenTTL) 910 a.Config = helper.Merge(a.Config, b.Config) 911 } 912 } 913 914 // Validate returns an error is the ACLAuthMethod is invalid. 915 // 916 // TODO revisit possible other validity conditions in the future 917 func (a *ACLAuthMethod) Validate(minTTL, maxTTL time.Duration) error { 918 var mErr multierror.Error 919 920 if !ValidACLAuthMethod.MatchString(a.Name) { 921 mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid name '%s'", a.Name)) 922 } 923 924 if !slices.Contains([]string{"local", "global"}, a.TokenLocality) { 925 mErr.Errors = append( 926 mErr.Errors, fmt.Errorf("invalid token locality '%s'", a.TokenLocality)) 927 } 928 929 if !slices.Contains(ValidACLAuthMethodTypes, a.Type) { 930 mErr.Errors = append( 931 mErr.Errors, fmt.Errorf("invalid token type '%s'", a.Type)) 932 } 933 934 if minTTL > a.MaxTokenTTL || a.MaxTokenTTL > maxTTL { 935 mErr.Errors = append(mErr.Errors, fmt.Errorf( 936 "invalid MaxTokenTTL value '%s' (should be between %s and %s)", 937 a.MaxTokenTTL.String(), minTTL.String(), maxTTL.String())) 938 } 939 940 return mErr.ErrorOrNil() 941 } 942 943 // TokenLocalityIsGlobal returns whether the auth method creates global ACL 944 // tokens or not. 945 func (a *ACLAuthMethod) TokenLocalityIsGlobal() bool { return a.TokenLocality == "global" } 946 947 // ACLAuthMethodConfig is used to store configuration of an auth method 948 type ACLAuthMethodConfig struct { 949 // A list of PEM-encoded public keys to use to authenticate signatures 950 // locally 951 JWTValidationPubKeys []string 952 953 // JSON Web Key Sets url for authenticating signatures 954 JWKSURL string 955 956 // The OIDC Discovery URL, without any .well-known component (base path) 957 OIDCDiscoveryURL string 958 959 // The OAuth Client ID configured with the OIDC provider 960 OIDCClientID string 961 962 // The OAuth Client Secret configured with the OIDC provider 963 OIDCClientSecret string 964 965 // List of OIDC scopes 966 OIDCScopes []string 967 968 // List of auth claims that are valid for login 969 BoundAudiences []string 970 971 // The value against which to match the iss claim in a JWT 972 BoundIssuer []string 973 974 // A list of allowed values for redirect_uri 975 AllowedRedirectURIs []string 976 977 // PEM encoded CA certs for use by the TLS client used to talk with the 978 // OIDC Discovery URL. 979 DiscoveryCaPem []string 980 981 // PEM encoded CA cert for use by the TLS client used to talk with the JWKS 982 // URL 983 JWKSCACert string 984 985 // A list of supported signing algorithms 986 SigningAlgs []string 987 988 // Duration in seconds of leeway when validating expiration of a token to 989 // account for clock skew 990 ExpirationLeeway time.Duration 991 992 // Duration in seconds of leeway when validating not before values of a 993 // token to account for clock skew. 994 NotBeforeLeeway time.Duration 995 996 // Duration in seconds of leeway when validating all claims to account for 997 // clock skew. 998 ClockSkewLeeway time.Duration 999 1000 // Mappings of claims (key) that will be copied to a metadata field 1001 // (value). 1002 ClaimMappings map[string]string 1003 ListClaimMappings map[string]string 1004 } 1005 1006 func (a *ACLAuthMethodConfig) Copy() *ACLAuthMethodConfig { 1007 if a == nil { 1008 return nil 1009 } 1010 1011 c := new(ACLAuthMethodConfig) 1012 *c = *a 1013 1014 c.JWTValidationPubKeys = slices.Clone(a.JWTValidationPubKeys) 1015 c.OIDCScopes = slices.Clone(a.OIDCScopes) 1016 c.BoundAudiences = slices.Clone(a.BoundAudiences) 1017 c.BoundIssuer = slices.Clone(a.BoundIssuer) 1018 c.AllowedRedirectURIs = slices.Clone(a.AllowedRedirectURIs) 1019 c.DiscoveryCaPem = slices.Clone(a.DiscoveryCaPem) 1020 c.SigningAlgs = slices.Clone(a.SigningAlgs) 1021 1022 return c 1023 } 1024 1025 // MarshalJSON implements the json.Marshaler interface and allows 1026 // time.Diration fields to be marshaled correctly. 1027 func (a *ACLAuthMethodConfig) MarshalJSON() ([]byte, error) { 1028 type Alias ACLAuthMethodConfig 1029 exported := &struct { 1030 ExpirationLeeway string 1031 NotBeforeLeeway string 1032 ClockSkewLeeway string 1033 *Alias 1034 }{ 1035 ExpirationLeeway: a.ExpirationLeeway.String(), 1036 NotBeforeLeeway: a.NotBeforeLeeway.String(), 1037 ClockSkewLeeway: a.ClockSkewLeeway.String(), 1038 Alias: (*Alias)(a), 1039 } 1040 if a.ExpirationLeeway == 0 { 1041 exported.ExpirationLeeway = "" 1042 } 1043 if a.NotBeforeLeeway == 0 { 1044 exported.NotBeforeLeeway = "" 1045 } 1046 if a.ClockSkewLeeway == 0 { 1047 exported.ClockSkewLeeway = "" 1048 } 1049 return json.Marshal(exported) 1050 } 1051 1052 // UnmarshalJSON implements the json.Unmarshaler interface and allows 1053 // time.Duration fields to be unmarshalled correctly. 1054 func (a *ACLAuthMethodConfig) UnmarshalJSON(data []byte) (err error) { 1055 type Alias ACLAuthMethodConfig 1056 aux := &struct { 1057 ExpirationLeeway any 1058 NotBeforeLeeway any 1059 ClockSkewLeeway any 1060 *Alias 1061 }{ 1062 Alias: (*Alias)(a), 1063 } 1064 if err = json.Unmarshal(data, &aux); err != nil { 1065 return err 1066 } 1067 if aux.ExpirationLeeway != nil { 1068 switch v := aux.ExpirationLeeway.(type) { 1069 case string: 1070 if v != "" { 1071 if a.ExpirationLeeway, err = time.ParseDuration(v); err != nil { 1072 return err 1073 } 1074 } 1075 case float64: 1076 a.ExpirationLeeway = time.Duration(v) 1077 default: 1078 return fmt.Errorf("unexpected ExpirationLeeway type: %v", v) 1079 } 1080 } 1081 if aux.NotBeforeLeeway != nil { 1082 switch v := aux.NotBeforeLeeway.(type) { 1083 case string: 1084 if v != "" { 1085 if a.NotBeforeLeeway, err = time.ParseDuration(v); err != nil { 1086 return err 1087 } 1088 } 1089 case float64: 1090 a.NotBeforeLeeway = time.Duration(v) 1091 default: 1092 return fmt.Errorf("unexpected NotBeforeLeeway type: %v", v) 1093 } 1094 } 1095 if aux.ClockSkewLeeway != nil { 1096 switch v := aux.ClockSkewLeeway.(type) { 1097 case string: 1098 if v != "" { 1099 if a.ClockSkewLeeway, err = time.ParseDuration(v); err != nil { 1100 return err 1101 } 1102 } 1103 case float64: 1104 a.ClockSkewLeeway = time.Duration(v) 1105 default: 1106 return fmt.Errorf("unexpected ClockSkewLeeway type: %v", v) 1107 } 1108 } 1109 return nil 1110 } 1111 1112 // ACLAuthClaims is the claim mapping of the OIDC auth method in a format that 1113 // can be used with go-bexpr. This structure is used during rule binding 1114 // evaluation. 1115 type ACLAuthClaims struct { 1116 Value map[string]string `bexpr:"value"` 1117 List map[string][]string `bexpr:"list"` 1118 } 1119 1120 // ACLAuthMethodStub is used for listing ACL auth methods 1121 type ACLAuthMethodStub struct { 1122 Name string 1123 Type string 1124 Default bool 1125 1126 // Hash is the hashed value of the auth-method and is generated using all 1127 // fields from the full object except the create and modify times and 1128 // indexes. 1129 Hash []byte 1130 1131 CreateIndex uint64 1132 ModifyIndex uint64 1133 } 1134 1135 // ACLAuthMethodListRequest is used to list auth methods 1136 type ACLAuthMethodListRequest struct { 1137 QueryOptions 1138 } 1139 1140 // ACLAuthMethodListResponse is used to list auth methods 1141 type ACLAuthMethodListResponse struct { 1142 AuthMethods []*ACLAuthMethodStub 1143 QueryMeta 1144 } 1145 1146 // ACLAuthMethodGetRequest is used to query a specific auth method 1147 type ACLAuthMethodGetRequest struct { 1148 MethodName string 1149 QueryOptions 1150 } 1151 1152 // ACLAuthMethodGetResponse is used to return a single auth method 1153 type ACLAuthMethodGetResponse struct { 1154 AuthMethod *ACLAuthMethod 1155 QueryMeta 1156 } 1157 1158 // ACLAuthMethodsGetRequest is used to query a set of auth methods 1159 type ACLAuthMethodsGetRequest struct { 1160 Names []string 1161 QueryOptions 1162 } 1163 1164 // ACLAuthMethodsGetResponse is used to return a set of auth methods 1165 type ACLAuthMethodsGetResponse struct { 1166 AuthMethods map[string]*ACLAuthMethod 1167 QueryMeta 1168 } 1169 1170 // ACLAuthMethodUpsertRequest is used to upsert a set of auth methods 1171 type ACLAuthMethodUpsertRequest struct { 1172 AuthMethods []*ACLAuthMethod 1173 WriteRequest 1174 } 1175 1176 // ACLAuthMethodUpsertResponse is a response of the upsert ACL auth methods 1177 // operation 1178 type ACLAuthMethodUpsertResponse struct { 1179 AuthMethods []*ACLAuthMethod 1180 WriteMeta 1181 } 1182 1183 // ACLAuthMethodDeleteRequest is used to delete a set of auth methods by their 1184 // name 1185 type ACLAuthMethodDeleteRequest struct { 1186 Names []string 1187 WriteRequest 1188 } 1189 1190 // ACLAuthMethodDeleteResponse is a response of the delete ACL auth methods 1191 // operation 1192 type ACLAuthMethodDeleteResponse struct { 1193 WriteMeta 1194 } 1195 1196 type ACLWhoAmIResponse struct { 1197 Identity *AuthenticatedIdentity 1198 QueryMeta 1199 } 1200 1201 // ACLBindingRule contains a direct relation to an ACLAuthMethod and represents 1202 // a rule to apply when logging in via the named AuthMethod. This allows the 1203 // transformation of OIDC provider claims, to Nomad based ACL concepts such as 1204 // ACL Roles and Policies. 1205 type ACLBindingRule struct { 1206 1207 // ID is an internally generated UUID for this rule and is controlled by 1208 // Nomad. 1209 ID string 1210 1211 // Description is a human-readable, operator set description that can 1212 // provide additional context about the binding role. This is an 1213 // operational field. 1214 Description string 1215 1216 // AuthMethod is the name of the auth method for which this rule applies 1217 // to. This is required and the method must exist within state before the 1218 // cluster administrator can create the rule. 1219 AuthMethod string 1220 1221 // Selector is an expression that matches against verified identity 1222 // attributes returned from the auth method during login. This is optional 1223 // and when not set, provides a catch-all rule. 1224 Selector string 1225 1226 // BindType adjusts how this binding rule is applied at login time. The 1227 // valid values are ACLBindingRuleBindTypeRole, 1228 // ACLBindingRuleBindTypePolicy, and ACLBindingRuleBindTypeManagement. 1229 BindType string 1230 1231 // BindName is the target of the binding. Can be lightly templated using 1232 // HIL ${foo} syntax from available field names. How it is used depends 1233 // upon the BindType. 1234 BindName string 1235 1236 // Hash is the hashed value of the binding rule and is generated using all 1237 // fields from the full object except the create and modify times and 1238 // indexes. 1239 Hash []byte 1240 1241 CreateTime time.Time 1242 ModifyTime time.Time 1243 CreateIndex uint64 1244 ModifyIndex uint64 1245 } 1246 1247 const ( 1248 // ACLBindingRuleBindTypeRole is the ACL binding rule bind type that only 1249 // allows the binding rule to function if a role exists at login-time. The 1250 // role will be specified within the ACLBindingRule.BindName parameter, and 1251 // will identify whether this is an ID or Name. 1252 ACLBindingRuleBindTypeRole = "role" 1253 1254 // ACLBindingRuleBindTypePolicy is the ACL binding rule bind type that 1255 // assigns a policy to the generate ACL token. The role will be specified 1256 // within the ACLBindingRule.BindName parameter, and will be the policy 1257 // name. 1258 ACLBindingRuleBindTypePolicy = "policy" 1259 1260 // ACLBindingRuleBindTypeManagement is the ACL binding rule bind type that 1261 // will generate management ACL tokens when matched. 1262 ACLBindingRuleBindTypeManagement = "management" 1263 ) 1264 1265 // Canonicalize performs basic canonicalization on the ACL token object. It is 1266 // important for callers to understand certain fields such as ID are set if it 1267 // is empty, so copies should be taken if needed before calling this function. 1268 func (a *ACLBindingRule) Canonicalize() { 1269 1270 now := time.Now().UTC() 1271 1272 // If the ID is empty, it means this is creation of a new binding rule, 1273 // therefore we need to generate base information. 1274 if a.ID == "" { 1275 a.ID = uuid.Generate() 1276 a.CreateTime = now 1277 } 1278 1279 // The fact this function is being called indicates we are attempting an 1280 // upsert into state. Therefore, update the modify time. 1281 a.ModifyTime = now 1282 } 1283 1284 // Validate ensures the ACL binding rule contains valid information which meets 1285 // Nomad's internal requirements. 1286 func (a *ACLBindingRule) Validate() error { 1287 1288 var mErr multierror.Error 1289 1290 if a.AuthMethod == "" { 1291 mErr.Errors = append(mErr.Errors, errors.New("auth method is missing")) 1292 } 1293 if len(a.Description) > maxACLBindingRuleDescriptionLength { 1294 mErr.Errors = append(mErr.Errors, fmt.Errorf("description longer than %d", maxACLRoleDescriptionLength)) 1295 } 1296 1297 // Depending on the bind type, we have some specific validation. Catching 1298 // the empty string also provides easier to understand feedback to the 1299 // user. 1300 switch a.BindType { 1301 case "": 1302 mErr.Errors = append(mErr.Errors, errors.New("bind type is missing")) 1303 case ACLBindingRuleBindTypeRole, ACLBindingRuleBindTypePolicy: 1304 if a.BindName == "" { 1305 mErr.Errors = append(mErr.Errors, errors.New("bind name is missing")) 1306 } 1307 case ACLBindingRuleBindTypeManagement: 1308 if a.BindName != "" { 1309 mErr.Errors = append(mErr.Errors, errors.New("bind name should be empty")) 1310 } 1311 default: 1312 mErr.Errors = append(mErr.Errors, fmt.Errorf("unsupported bind type: %q", a.BindType)) 1313 } 1314 1315 // If there is a selector configured, ensure that go-bexpr can parse this. 1316 // Otherwise, the user will get an ambiguous failure when attempting to 1317 // login. 1318 if a.Selector != "" { 1319 if _, err := bexpr.CreateEvaluator(a.Selector, nil); err != nil { 1320 mErr.Errors = append(mErr.Errors, fmt.Errorf("selector is invalid: %v", err)) 1321 } 1322 } 1323 1324 return mErr.ErrorOrNil() 1325 } 1326 1327 // Merge merges binding rule a with b. It sets all required empty fields of rule 1328 // a to corresponding values of rule b, except for "ID" which must be provided. 1329 func (a *ACLBindingRule) Merge(b *ACLBindingRule) { 1330 a.BindName = helper.Merge(a.BindName, b.BindName) 1331 a.BindType = helper.Merge(a.BindType, b.BindType) 1332 a.AuthMethod = helper.Merge(a.AuthMethod, b.AuthMethod) 1333 } 1334 1335 // SetHash is used to compute and set the hash of the ACL binding rule. This 1336 // should be called every and each time a user specified field on the method is 1337 // changed before updating the Nomad state store. 1338 func (a *ACLBindingRule) SetHash() []byte { 1339 1340 // Initialize a 256bit Blake2 hash (32 bytes). 1341 hash, err := blake2b.New256(nil) 1342 if err != nil { 1343 panic(err) 1344 } 1345 1346 _, _ = hash.Write([]byte(a.ID)) 1347 _, _ = hash.Write([]byte(a.Description)) 1348 _, _ = hash.Write([]byte(a.AuthMethod)) 1349 _, _ = hash.Write([]byte(a.Selector)) 1350 _, _ = hash.Write([]byte(a.BindType)) 1351 _, _ = hash.Write([]byte(a.BindName)) 1352 1353 // Finalize the hash. 1354 hashVal := hash.Sum(nil) 1355 1356 // Set and return the hash. 1357 a.Hash = hashVal 1358 return hashVal 1359 } 1360 1361 // Equal performs an equality check on the two ACL binding rules. It handles 1362 // nil objects. 1363 func (a *ACLBindingRule) Equal(other *ACLBindingRule) bool { 1364 if a == nil || other == nil { 1365 return a == other 1366 } 1367 if len(a.Hash) == 0 { 1368 a.SetHash() 1369 } 1370 if len(other.Hash) == 0 { 1371 other.SetHash() 1372 } 1373 return bytes.Equal(a.Hash, other.Hash) 1374 } 1375 1376 // Copy creates a deep copy of the ACL binding rule. This copy can then be 1377 // safely modified. It handles nil objects. 1378 func (a *ACLBindingRule) Copy() *ACLBindingRule { 1379 if a == nil { 1380 return nil 1381 } 1382 1383 c := new(ACLBindingRule) 1384 *c = *a 1385 c.Hash = slices.Clone(a.Hash) 1386 1387 return c 1388 } 1389 1390 // Stub converts the ACLBindingRule object into a ACLBindingRuleListStub 1391 // object. 1392 func (a *ACLBindingRule) Stub() *ACLBindingRuleListStub { 1393 return &ACLBindingRuleListStub{ 1394 ID: a.ID, 1395 Description: a.Description, 1396 AuthMethod: a.AuthMethod, 1397 Hash: a.Hash, 1398 CreateIndex: a.CreateIndex, 1399 ModifyIndex: a.ModifyIndex, 1400 } 1401 } 1402 1403 // ACLBindingRuleListStub is the stub object returned when performing a listing 1404 // of ACL binding rules. 1405 type ACLBindingRuleListStub struct { 1406 1407 // ID is an internally generated UUID for this role and is controlled by 1408 // Nomad. 1409 ID string 1410 1411 // Description is a human-readable, operator set description that can 1412 // provide additional context about the binding role. This is an 1413 // operational field. 1414 Description string 1415 1416 // AuthMethod is the name of the auth method for which this rule applies 1417 // to. This is required and the method must exist within state before the 1418 // cluster administrator can create the rule. 1419 AuthMethod string 1420 1421 // Hash is the hashed value of the binding rule and is generated using all 1422 // fields from the full object except the create and modify times and 1423 // indexes. 1424 Hash []byte 1425 1426 CreateIndex uint64 1427 ModifyIndex uint64 1428 } 1429 1430 // ACLBindingRulesUpsertRequest is used to upsert a set of ACL binding rules. 1431 type ACLBindingRulesUpsertRequest struct { 1432 ACLBindingRules []*ACLBindingRule 1433 1434 // AllowMissingAuthMethods skips the ACL binding rule auth method link 1435 // verification and is used by the replication process. The replication 1436 // cannot ensure auth methods are present before ACL binding rules are 1437 // replicated. 1438 AllowMissingAuthMethods bool 1439 1440 WriteRequest 1441 } 1442 1443 // ACLBindingRulesUpsertResponse is a response of the upsert ACL binding rules 1444 // operation. 1445 type ACLBindingRulesUpsertResponse struct { 1446 ACLBindingRules []*ACLBindingRule 1447 WriteMeta 1448 } 1449 1450 // ACLBindingRulesDeleteRequest is used to delete a set of ACL binding rules by 1451 // their IDs. 1452 type ACLBindingRulesDeleteRequest struct { 1453 ACLBindingRuleIDs []string 1454 WriteRequest 1455 } 1456 1457 // ACLBindingRulesDeleteResponse is a response of the delete ACL binding rules 1458 // operation. 1459 type ACLBindingRulesDeleteResponse struct { 1460 WriteMeta 1461 } 1462 1463 // ACLBindingRulesListRequest is the request object when performing ACL 1464 // binding rules listings. 1465 type ACLBindingRulesListRequest struct { 1466 QueryOptions 1467 } 1468 1469 // ACLBindingRulesListResponse is the response object when performing ACL 1470 // binding rule listings. 1471 type ACLBindingRulesListResponse struct { 1472 ACLBindingRules []*ACLBindingRuleListStub 1473 QueryMeta 1474 } 1475 1476 // ACLBindingRulesRequest is the request object when performing a lookup of 1477 // multiple binding rules by the ID. 1478 type ACLBindingRulesRequest struct { 1479 ACLBindingRuleIDs []string 1480 QueryOptions 1481 } 1482 1483 // ACLBindingRulesResponse is the response object when performing a lookup of 1484 // multiple binding rules by their IDs. 1485 type ACLBindingRulesResponse struct { 1486 ACLBindingRules map[string]*ACLBindingRule 1487 QueryMeta 1488 } 1489 1490 // ACLBindingRuleRequest is the request object to perform a lookup of an ACL 1491 // binding rule using a specific ID. 1492 type ACLBindingRuleRequest struct { 1493 ACLBindingRuleID string 1494 QueryOptions 1495 } 1496 1497 // ACLBindingRuleResponse is the response object when performing a lookup of an 1498 // ACL binding rule matching a specific ID. 1499 type ACLBindingRuleResponse struct { 1500 ACLBindingRule *ACLBindingRule 1501 QueryMeta 1502 } 1503 1504 // ACLOIDCAuthURLRequest is the request to make when starting the OIDC 1505 // authentication login flow. 1506 type ACLOIDCAuthURLRequest struct { 1507 1508 // AuthMethodName is the OIDC auth-method to use. This is a required 1509 // parameter. 1510 AuthMethodName string 1511 1512 // RedirectURI is the URL that authorization should redirect to. This is a 1513 // required parameter. 1514 RedirectURI string 1515 1516 // ClientNonce is a randomly generated string to prevent replay attacks. It 1517 // is up to the client to generate this and Go integrations should use the 1518 // oidc.NewID function within the hashicorp/cap library. This must then be 1519 // passed back to ACLOIDCCompleteAuthRequest. This is a required parameter. 1520 ClientNonce string 1521 1522 // WriteRequest is used due to the requirement by the RPC forwarding 1523 // mechanism. This request doesn't write anything to Nomad's internal 1524 // state. 1525 WriteRequest 1526 } 1527 1528 // Validate ensures the request object contains all the required fields in 1529 // order to start the OIDC authentication flow. 1530 func (a *ACLOIDCAuthURLRequest) Validate() error { 1531 1532 var mErr multierror.Error 1533 1534 if a.AuthMethodName == "" { 1535 mErr.Errors = append(mErr.Errors, errors.New("missing auth method name")) 1536 } 1537 if a.ClientNonce == "" { 1538 mErr.Errors = append(mErr.Errors, errors.New("missing client nonce")) 1539 } 1540 if a.RedirectURI == "" { 1541 mErr.Errors = append(mErr.Errors, errors.New("missing redirect URI")) 1542 } 1543 return mErr.ErrorOrNil() 1544 } 1545 1546 // ACLOIDCAuthURLResponse is the response when starting the OIDC authentication 1547 // login flow. 1548 type ACLOIDCAuthURLResponse struct { 1549 1550 // AuthURL is URL to begin authorization and is where the user logging in 1551 // should go. 1552 AuthURL string 1553 } 1554 1555 // ACLOIDCCompleteAuthRequest is the request object to begin completing the 1556 // OIDC auth cycle after receiving the callback from the OIDC provider. 1557 type ACLOIDCCompleteAuthRequest struct { 1558 1559 // AuthMethodName is the name of the auth method being used to login via 1560 // OIDC. This will match ACLOIDCAuthURLRequest.AuthMethodName. This is a 1561 // required parameter. 1562 AuthMethodName string 1563 1564 // ClientNonce, State, and Code are provided from the parameters given to 1565 // the redirect URL. These are all required parameters. 1566 ClientNonce string 1567 State string 1568 Code string 1569 1570 // RedirectURI is the URL that authorization should redirect to. This is a 1571 // required parameter. 1572 RedirectURI string 1573 1574 WriteRequest 1575 } 1576 1577 // Validate ensures the request object contains all the required fields in 1578 // order to complete the OIDC authentication flow. 1579 func (a *ACLOIDCCompleteAuthRequest) Validate() error { 1580 1581 var mErr multierror.Error 1582 1583 if a.AuthMethodName == "" { 1584 mErr.Errors = append(mErr.Errors, errors.New("missing auth method name")) 1585 } 1586 if a.ClientNonce == "" { 1587 mErr.Errors = append(mErr.Errors, errors.New("missing client nonce")) 1588 } 1589 if a.State == "" { 1590 mErr.Errors = append(mErr.Errors, errors.New("missing state")) 1591 } 1592 if a.Code == "" { 1593 mErr.Errors = append(mErr.Errors, errors.New("missing code")) 1594 } 1595 if a.RedirectURI == "" { 1596 mErr.Errors = append(mErr.Errors, errors.New("missing redirect URI")) 1597 } 1598 return mErr.ErrorOrNil() 1599 } 1600 1601 // ACLLoginResponse is the response when the auth flow has been 1602 // completed successfully. 1603 type ACLLoginResponse struct { 1604 ACLToken *ACLToken 1605 WriteMeta 1606 } 1607 1608 // ACLLoginRequest is the request object to begin auth with an external 1609 // token provider. 1610 type ACLLoginRequest struct { 1611 1612 // AuthMethodName is the name of the auth method being used to login. This 1613 // is a required parameter. 1614 AuthMethodName string 1615 1616 // LoginToken is the 3rd party token that we use to exchange for Nomad ACL 1617 // Token in order to authenticate. This is a required parameter. 1618 LoginToken string 1619 1620 WriteRequest 1621 } 1622 1623 // Validate ensures the request object contains all the required fields in 1624 // order to complete the authentication flow. 1625 func (a *ACLLoginRequest) Validate() error { 1626 1627 var mErr multierror.Error 1628 1629 if a.AuthMethodName == "" { 1630 mErr.Errors = append(mErr.Errors, errors.New("missing auth method name")) 1631 } 1632 if a.LoginToken == "" { 1633 mErr.Errors = append(mErr.Errors, errors.New("missing login token")) 1634 } 1635 return mErr.ErrorOrNil() 1636 }