github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/structs/acl.go (about) 1 package structs 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "regexp" 9 "strconv" 10 "time" 11 12 "github.com/hashicorp/go-multierror" 13 "github.com/hashicorp/go-set" 14 "github.com/hashicorp/nomad/helper/pointer" 15 "github.com/hashicorp/nomad/helper/uuid" 16 "golang.org/x/crypto/blake2b" 17 "golang.org/x/exp/slices" 18 ) 19 20 const ( 21 // ACLUpsertPoliciesRPCMethod is the RPC method for batch creating or 22 // modifying ACL policies. 23 // 24 // Args: ACLPolicyUpsertRequest 25 // Reply: GenericResponse 26 ACLUpsertPoliciesRPCMethod = "ACL.UpsertPolicies" 27 28 // ACLUpsertTokensRPCMethod is the RPC method for batch creating or 29 // modifying ACL tokens. 30 // 31 // Args: ACLTokenUpsertRequest 32 // Reply: ACLTokenUpsertResponse 33 ACLUpsertTokensRPCMethod = "ACL.UpsertTokens" 34 35 // ACLDeleteTokensRPCMethod is the RPC method for batch deleting ACL 36 // tokens. 37 // 38 // Args: ACLTokenDeleteRequest 39 // Reply: GenericResponse 40 ACLDeleteTokensRPCMethod = "ACL.DeleteTokens" 41 42 // ACLUpsertRolesRPCMethod is the RPC method for batch creating or 43 // modifying ACL roles. 44 // 45 // Args: ACLRolesUpsertRequest 46 // Reply: ACLRolesUpsertResponse 47 ACLUpsertRolesRPCMethod = "ACL.UpsertRoles" 48 49 // ACLDeleteRolesByIDRPCMethod the RPC method for batch deleting ACL 50 // roles by their ID. 51 // 52 // Args: ACLRolesDeleteByIDRequest 53 // Reply: ACLRolesDeleteByIDResponse 54 ACLDeleteRolesByIDRPCMethod = "ACL.DeleteRolesByID" 55 56 // ACLListRolesRPCMethod is the RPC method for listing ACL roles. 57 // 58 // Args: ACLRolesListRequest 59 // Reply: ACLRolesListResponse 60 ACLListRolesRPCMethod = "ACL.ListRoles" 61 62 // ACLGetRolesByIDRPCMethod is the RPC method for detailing a number of ACL 63 // roles using their ID. This is an internal only RPC endpoint and used by 64 // the ACL Role replication process. 65 // 66 // Args: ACLRolesByIDRequest 67 // Reply: ACLRolesByIDResponse 68 ACLGetRolesByIDRPCMethod = "ACL.GetRolesByID" 69 70 // ACLGetRoleByIDRPCMethod is the RPC method for detailing an individual 71 // ACL role using its ID. 72 // 73 // Args: ACLRoleByIDRequest 74 // Reply: ACLRoleByIDResponse 75 ACLGetRoleByIDRPCMethod = "ACL.GetRoleByID" 76 77 // ACLGetRoleByNameRPCMethod is the RPC method for detailing an individual 78 // ACL role using its name. 79 // 80 // Args: ACLRoleByNameRequest 81 // Reply: ACLRoleByNameResponse 82 ACLGetRoleByNameRPCMethod = "ACL.GetRoleByName" 83 84 // ACLUpsertAuthMethodsRPCMethod is the RPC method for batch creating or 85 // modifying auth methods. 86 // 87 // Args: ACLAuthMethodsUpsertRequest 88 // Reply: ACLAuthMethodUpsertResponse 89 ACLUpsertAuthMethodsRPCMethod = "ACL.UpsertAuthMethods" 90 91 // ACLDeleteAuthMethodsRPCMethod is the RPC method for batch deleting auth 92 // methods. 93 // 94 // Args: ACLAuthMethodDeleteRequest 95 // Reply: ACLAuthMethodDeleteResponse 96 ACLDeleteAuthMethodsRPCMethod = "ACL.DeleteAuthMethods" 97 98 // ACLListAuthMethodsRPCMethod is the RPC method for listing auth methods. 99 // 100 // Args: ACLAuthMethodListRequest 101 // Reply: ACLAuthMethodListResponse 102 ACLListAuthMethodsRPCMethod = "ACL.ListAuthMethods" 103 104 // ACLGetAuthMethodRPCMethod is the RPC method for detailing an individual 105 // auth method using its name. 106 // 107 // Args: ACLAuthMethodGetRequest 108 // Reply: ACLAuthMethodGetResponse 109 ACLGetAuthMethodRPCMethod = "ACL.GetAuthMethod" 110 111 // ACLGetAuthMethodsRPCMethod is the RPC method for getting multiple auth 112 // methods using their names. 113 // 114 // Args: ACLAuthMethodsGetRequest 115 // Reply: ACLAuthMethodsGetResponse 116 ACLGetAuthMethodsRPCMethod = "ACL.GetAuthMethods" 117 118 // ACLUpsertBindingRulesRPCMethod is the RPC method for batch creating or 119 // modifying binding rules. 120 // 121 // Args: ACLBindingRulesUpsertRequest 122 // Reply: ACLBindingRulesUpsertResponse 123 ACLUpsertBindingRulesRPCMethod = "ACL.UpsertBindingRules" 124 125 // ACLDeleteBindingRulesRPCMethod is the RPC method for batch deleting 126 // binding rules. 127 // 128 // Args: ACLBindingRulesDeleteRequest 129 // Reply: ACLBindingRulesDeleteResponse 130 ACLDeleteBindingRulesRPCMethod = "ACL.DeleteBindingRules" 131 132 // ACLListBindingRulesRPCMethod is the RPC method listing binding rules. 133 // 134 // Args: ACLBindingRulesListRequest 135 // Reply: ACLBindingRulesListResponse 136 ACLListBindingRulesRPCMethod = "ACL.ListBindingRules" 137 138 // ACLGetBindingRulesRPCMethod is the RPC method for getting multiple 139 // binding rules using their IDs. 140 // 141 // Args: ACLBindingRulesRequest 142 // Reply: ACLBindingRulesResponse 143 ACLGetBindingRulesRPCMethod = "ACL.GetBindingRules" 144 145 // ACLGetBindingRuleRPCMethod is the RPC method for detailing an individual 146 // binding rule using its ID. 147 // 148 // Args: ACLBindingRuleRequest 149 // Reply: ACLBindingRuleResponse 150 ACLGetBindingRuleRPCMethod = "ACL.GetBindingRule" 151 ) 152 153 const ( 154 // ACLMaxExpiredBatchSize is the maximum number of expired ACL tokens that 155 // will be garbage collected in a single trigger. This number helps limit 156 // the replication pressure due to expired token deletion. If there are a 157 // large number of expired tokens pending garbage collection, this value is 158 // a potential limiting factor. 159 ACLMaxExpiredBatchSize = 4096 160 161 // maxACLRoleDescriptionLength limits an ACL roles description length. 162 maxACLRoleDescriptionLength = 256 163 164 // maxACLBindingRuleDescriptionLength limits an ACL binding rules 165 // description length and should be used to validate the object. 166 maxACLBindingRuleDescriptionLength = 256 167 ) 168 169 var ( 170 // validACLRoleName is used to validate an ACL role name. 171 validACLRoleName = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$") 172 173 // validACLAuthMethodName is used to validate an ACL auth method name. 174 validACLAuthMethod = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$") 175 ) 176 177 // ACLTokenRoleLink is used to link an ACL token to an ACL role. The ACL token 178 // can therefore inherit all the ACL policy permissions that the ACL role 179 // contains. 180 type ACLTokenRoleLink struct { 181 182 // ID is the ACLRole.ID UUID. This field is immutable and represents the 183 // absolute truth for the link. 184 ID string 185 186 // Name is the human friendly identifier for the ACL role and is a 187 // convenience field for operators. This field is always resolved to the 188 // ID and discarded before the token is stored in state. This is because 189 // operators can change the name of an ACL role. 190 Name string 191 } 192 193 // Canonicalize performs basic canonicalization on the ACL token object. It is 194 // important for callers to understand certain fields such as AccessorID are 195 // set if it is empty, so copies should be taken if needed before calling this 196 // function. 197 func (a *ACLToken) Canonicalize() { 198 199 // If the accessor ID is empty, it means this is creation of a new token, 200 // therefore we need to generate base information. 201 if a.AccessorID == "" { 202 203 a.AccessorID = uuid.Generate() 204 a.SecretID = uuid.Generate() 205 a.CreateTime = time.Now().UTC() 206 207 // If the user has not set the expiration time, but has provided a TTL, we 208 // calculate and populate the former filed. 209 if a.ExpirationTime == nil && a.ExpirationTTL != 0 { 210 a.ExpirationTime = pointer.Of(a.CreateTime.Add(a.ExpirationTTL)) 211 } 212 } 213 } 214 215 // Validate is used to check a token for reasonableness 216 func (a *ACLToken) Validate(minTTL, maxTTL time.Duration, existing *ACLToken) error { 217 var mErr multierror.Error 218 219 // The human friendly name of an ACL token cannot exceed 256 characters. 220 if len(a.Name) > maxTokenNameLength { 221 mErr.Errors = append(mErr.Errors, errors.New("token name too long")) 222 } 223 224 // The type of an ACL token must be set. An ACL token of type client must 225 // have associated policies or roles, whereas a management token cannot be 226 // associated with policies. 227 switch a.Type { 228 case ACLClientToken: 229 if len(a.Policies) == 0 && len(a.Roles) == 0 { 230 mErr.Errors = append(mErr.Errors, errors.New("client token missing policies or roles")) 231 } 232 case ACLManagementToken: 233 if len(a.Policies) != 0 || len(a.Roles) != 0 { 234 mErr.Errors = append(mErr.Errors, errors.New("management token cannot be associated with policies or roles")) 235 } 236 default: 237 mErr.Errors = append(mErr.Errors, errors.New("token type must be client or management")) 238 } 239 240 // There are different validation rules depending on whether the ACL token 241 // is being created or updated. 242 switch existing { 243 case nil: 244 if a.ExpirationTTL < 0 { 245 mErr.Errors = append(mErr.Errors, 246 fmt.Errorf("token expiration TTL '%s' should not be negative", a.ExpirationTTL)) 247 } 248 249 if a.ExpirationTime != nil && !a.ExpirationTime.IsZero() { 250 251 if a.CreateTime.After(*a.ExpirationTime) { 252 mErr.Errors = append(mErr.Errors, errors.New("expiration time cannot be before create time")) 253 } 254 255 // Create a time duration which details the time-til-expiry, so we can 256 // check this against the regions max and min values. 257 expiresIn := a.ExpirationTime.Sub(a.CreateTime) 258 if expiresIn > maxTTL { 259 mErr.Errors = append(mErr.Errors, 260 fmt.Errorf("expiration time cannot be more than %s in the future (was %s)", 261 maxTTL, expiresIn)) 262 263 } else if expiresIn < minTTL { 264 mErr.Errors = append(mErr.Errors, 265 fmt.Errorf("expiration time cannot be less than %s in the future (was %s)", 266 minTTL, expiresIn)) 267 } 268 } 269 default: 270 if existing.Global != a.Global { 271 mErr.Errors = append(mErr.Errors, errors.New("cannot toggle global mode")) 272 } 273 if existing.ExpirationTTL != a.ExpirationTTL { 274 mErr.Errors = append(mErr.Errors, errors.New("cannot update expiration TTL")) 275 } 276 if existing.ExpirationTime != a.ExpirationTime { 277 mErr.Errors = append(mErr.Errors, errors.New("cannot update expiration time")) 278 } 279 } 280 281 return mErr.ErrorOrNil() 282 } 283 284 // HasExpirationTime checks whether the ACL token has an expiration time value 285 // set. 286 func (a *ACLToken) HasExpirationTime() bool { 287 if a == nil || a.ExpirationTime == nil { 288 return false 289 } 290 return !a.ExpirationTime.IsZero() 291 } 292 293 // IsExpired compares the ACLToken.ExpirationTime against the passed t to 294 // identify whether the token is considered expired. The function can be called 295 // without checking whether the ACL token has an expiry time. 296 func (a *ACLToken) IsExpired(t time.Time) bool { 297 298 // Check the token has an expiration time before potentially modifying the 299 // supplied time. This allows us to avoid extra work, if it isn't needed. 300 if !a.HasExpirationTime() { 301 return false 302 } 303 304 // Check and ensure the time location is set to UTC. This is vital for 305 // consistency with multi-region global tokens. 306 if t.Location() != time.UTC { 307 t = t.UTC() 308 } 309 310 return a.ExpirationTime.Before(t) || t.IsZero() 311 } 312 313 // HasRoles checks if a given set of role IDs are assigned to the ACL token. It 314 // does not account for management tokens, therefore it is the responsibility 315 // of the caller to perform this check, if required. 316 func (a *ACLToken) HasRoles(roleIDs []string) bool { 317 318 // Generate a set of role IDs that the token is assigned. 319 roleSet := set.FromFunc(a.Roles, func(roleLink *ACLTokenRoleLink) string { return roleLink.ID }) 320 321 // Iterate the role IDs within the request and check whether these are 322 // present within the token assignment. 323 for _, roleID := range roleIDs { 324 if !roleSet.Contains(roleID) { 325 return false 326 } 327 } 328 return true 329 } 330 331 // ACLRole is an abstraction for the ACL system which allows the grouping of 332 // ACL policies into a single object. ACL tokens can be created and linked to 333 // a role; the token then inherits all the permissions granted by the policies. 334 type ACLRole struct { 335 336 // ID is an internally generated UUID for this role and is controlled by 337 // Nomad. 338 ID string 339 340 // Name is unique across the entire set of federated clusters and is 341 // supplied by the operator on role creation. The name can be modified by 342 // updating the role and including the Nomad generated ID. This update will 343 // not affect tokens created and linked to this role. This is a required 344 // field. 345 Name string 346 347 // Description is a human-readable, operator set description that can 348 // provide additional context about the role. This is an operational field. 349 Description string 350 351 // Policies is an array of ACL policy links. Although currently policies 352 // can only be linked using their name, in the future we will want to add 353 // IDs also and thus allow operators to specify either a name, an ID, or 354 // both. 355 Policies []*ACLRolePolicyLink 356 357 // Hash is the hashed value of the role and is generated using all fields 358 // above this point. 359 Hash []byte 360 361 CreateIndex uint64 362 ModifyIndex uint64 363 } 364 365 // ACLRolePolicyLink is used to link a policy to an ACL role. We use a struct 366 // rather than a list of strings as in the future we will want to add IDs to 367 // policies and then link via these. 368 type ACLRolePolicyLink struct { 369 370 // Name is the ACLPolicy.Name value which will be linked to the ACL role. 371 Name string 372 } 373 374 // SetHash is used to compute and set the hash of the ACL role. This should be 375 // called every and each time a user specified field on the role is changed 376 // before updating the Nomad state store. 377 func (a *ACLRole) SetHash() []byte { 378 379 // Initialize a 256bit Blake2 hash (32 bytes). 380 hash, err := blake2b.New256(nil) 381 if err != nil { 382 panic(err) 383 } 384 385 // Write all the user set fields. 386 _, _ = hash.Write([]byte(a.Name)) 387 _, _ = hash.Write([]byte(a.Description)) 388 389 for _, policyLink := range a.Policies { 390 _, _ = hash.Write([]byte(policyLink.Name)) 391 } 392 393 // Finalize the hash. 394 hashVal := hash.Sum(nil) 395 396 // Set and return the hash. 397 a.Hash = hashVal 398 return hashVal 399 } 400 401 // Validate ensure the ACL role contains valid information which meets Nomad's 402 // internal requirements. This does not include any state calls, such as 403 // ensuring the linked policies exist. 404 func (a *ACLRole) Validate() error { 405 406 var mErr multierror.Error 407 408 if !validACLRoleName.MatchString(a.Name) { 409 mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid name '%s'", a.Name)) 410 } 411 412 if len(a.Description) > maxACLRoleDescriptionLength { 413 mErr.Errors = append(mErr.Errors, fmt.Errorf("description longer than %d", maxACLRoleDescriptionLength)) 414 } 415 416 if len(a.Policies) < 1 { 417 mErr.Errors = append(mErr.Errors, errors.New("at least one policy should be specified")) 418 } 419 420 return mErr.ErrorOrNil() 421 } 422 423 // Canonicalize performs basic canonicalization on the ACL role object. It is 424 // important for callers to understand certain fields such as ID are set if it 425 // is empty, so copies should be taken if needed before calling this function. 426 func (a *ACLRole) Canonicalize() { 427 if a.ID == "" { 428 a.ID = uuid.Generate() 429 } 430 } 431 432 // Equal performs an equality check on the two service registrations. It 433 // handles nil objects. 434 func (a *ACLRole) Equal(o *ACLRole) bool { 435 if a == nil || o == nil { 436 return a == o 437 } 438 if len(a.Hash) == 0 { 439 a.SetHash() 440 } 441 if len(o.Hash) == 0 { 442 o.SetHash() 443 } 444 return bytes.Equal(a.Hash, o.Hash) 445 } 446 447 // Copy creates a deep copy of the ACL role. This copy can then be safely 448 // modified. It handles nil objects. 449 func (a *ACLRole) Copy() *ACLRole { 450 if a == nil { 451 return nil 452 } 453 454 c := new(ACLRole) 455 *c = *a 456 457 c.Policies = slices.Clone(a.Policies) 458 c.Hash = slices.Clone(a.Hash) 459 460 return c 461 } 462 463 // Stub converts the ACLRole object into a ACLRoleListStub object. 464 func (a *ACLRole) Stub() *ACLRoleListStub { 465 return &ACLRoleListStub{ 466 ID: a.ID, 467 Name: a.Name, 468 Description: a.Description, 469 Policies: a.Policies, 470 Hash: a.Hash, 471 CreateIndex: a.CreateIndex, 472 ModifyIndex: a.ModifyIndex, 473 } 474 } 475 476 // ACLRoleListStub is the stub object returned when performing a listing of ACL 477 // roles. While it might not currently be different to the full response 478 // object, it allows us to future-proof the RPC in the event the ACLRole object 479 // grows over time. 480 type ACLRoleListStub struct { 481 482 // ID is an internally generated UUID for this role and is controlled by 483 // Nomad. 484 ID string 485 486 // Name is unique across the entire set of federated clusters and is 487 // supplied by the operator on role creation. The name can be modified by 488 // updating the role and including the Nomad generated ID. This update will 489 // not affect tokens created and linked to this role. This is a required 490 // field. 491 Name string 492 493 // Description is a human-readable, operator set description that can 494 // provide additional context about the role. This is an operational field. 495 Description string 496 497 // Policies is an array of ACL policy links. Although currently policies 498 // can only be linked using their name, in the future we will want to add 499 // IDs also and thus allow operators to specify either a name, an ID, or 500 // both. 501 Policies []*ACLRolePolicyLink 502 503 // Hash is the hashed value of the role and is generated using all fields 504 // above this point. 505 Hash []byte 506 507 CreateIndex uint64 508 ModifyIndex uint64 509 } 510 511 // ACLRolesUpsertRequest is the request object used to upsert one or more ACL 512 // roles. 513 type ACLRolesUpsertRequest struct { 514 ACLRoles []*ACLRole 515 516 // AllowMissingPolicies skips the ACL Role policy link verification and is 517 // used by the replication process. The replication cannot ensure policies 518 // are present before ACL Roles are replicated. 519 AllowMissingPolicies bool 520 521 WriteRequest 522 } 523 524 // ACLRolesUpsertResponse is the response object when one or more ACL roles 525 // have been successfully upserted into state. 526 type ACLRolesUpsertResponse struct { 527 ACLRoles []*ACLRole 528 WriteMeta 529 } 530 531 // ACLRolesDeleteByIDRequest is the request object to delete one or more ACL 532 // roles using the role ID. 533 type ACLRolesDeleteByIDRequest struct { 534 ACLRoleIDs []string 535 WriteRequest 536 } 537 538 // ACLRolesDeleteByIDResponse is the response object when performing a deletion 539 // of one or more ACL roles using the role ID. 540 type ACLRolesDeleteByIDResponse struct { 541 WriteMeta 542 } 543 544 // ACLRolesListRequest is the request object when performing ACL role listings. 545 type ACLRolesListRequest struct { 546 QueryOptions 547 } 548 549 // ACLRolesListResponse is the response object when performing ACL role 550 // listings. 551 type ACLRolesListResponse struct { 552 ACLRoles []*ACLRoleListStub 553 QueryMeta 554 } 555 556 // ACLRolesByIDRequest is the request object when performing a lookup of 557 // multiple roles by the ID. 558 type ACLRolesByIDRequest struct { 559 ACLRoleIDs []string 560 QueryOptions 561 } 562 563 // ACLRolesByIDResponse is the response object when performing a lookup of 564 // multiple roles by their IDs. 565 type ACLRolesByIDResponse struct { 566 ACLRoles map[string]*ACLRole 567 QueryMeta 568 } 569 570 // ACLRoleByIDRequest is the request object to perform a lookup of an ACL 571 // role using a specific ID. 572 type ACLRoleByIDRequest struct { 573 RoleID string 574 QueryOptions 575 } 576 577 // ACLRoleByIDResponse is the response object when performing a lookup of an 578 // ACL role matching a specific ID. 579 type ACLRoleByIDResponse struct { 580 ACLRole *ACLRole 581 QueryMeta 582 } 583 584 // ACLRoleByNameRequest is the request object to perform a lookup of an ACL 585 // role using a specific name. 586 type ACLRoleByNameRequest struct { 587 RoleName string 588 QueryOptions 589 } 590 591 // ACLRoleByNameResponse is the response object when performing a lookup of an 592 // ACL role matching a specific name. 593 type ACLRoleByNameResponse struct { 594 ACLRole *ACLRole 595 QueryMeta 596 } 597 598 // ACLAuthMethod is used to capture the properties of an authentication method 599 // used for single sing-on 600 type ACLAuthMethod struct { 601 Name string 602 Type string 603 TokenLocality string // is the token valid locally or globally? 604 MaxTokenTTL time.Duration 605 Default bool 606 Config *ACLAuthMethodConfig 607 608 Hash []byte 609 610 CreateTime time.Time 611 ModifyTime time.Time 612 CreateIndex uint64 613 ModifyIndex uint64 614 } 615 616 // SetHash is used to compute and set the hash of the ACL auth method. This 617 // should be called every and each time a user specified field on the method is 618 // changed before updating the Nomad state store. 619 func (a *ACLAuthMethod) SetHash() []byte { 620 621 // Initialize a 256bit Blake2 hash (32 bytes). 622 hash, err := blake2b.New256(nil) 623 if err != nil { 624 panic(err) 625 } 626 627 _, _ = hash.Write([]byte(a.Name)) 628 _, _ = hash.Write([]byte(a.Type)) 629 _, _ = hash.Write([]byte(a.TokenLocality)) 630 _, _ = hash.Write([]byte(a.MaxTokenTTL.String())) 631 _, _ = hash.Write([]byte(strconv.FormatBool(a.Default))) 632 633 if a.Config != nil { 634 _, _ = hash.Write([]byte(a.Config.OIDCDiscoveryURL)) 635 _, _ = hash.Write([]byte(a.Config.OIDCClientID)) 636 _, _ = hash.Write([]byte(a.Config.OIDCClientSecret)) 637 for _, ba := range a.Config.BoundAudiences { 638 _, _ = hash.Write([]byte(ba)) 639 } 640 for _, uri := range a.Config.AllowedRedirectURIs { 641 _, _ = hash.Write([]byte(uri)) 642 } 643 for _, pem := range a.Config.DiscoveryCaPem { 644 _, _ = hash.Write([]byte(pem)) 645 } 646 for _, sa := range a.Config.SigningAlgs { 647 _, _ = hash.Write([]byte(sa)) 648 } 649 for k, v := range a.Config.ClaimMappings { 650 _, _ = hash.Write([]byte(k)) 651 _, _ = hash.Write([]byte(v)) 652 } 653 for k, v := range a.Config.ListClaimMappings { 654 _, _ = hash.Write([]byte(k)) 655 _, _ = hash.Write([]byte(v)) 656 } 657 } 658 659 // Finalize the hash. 660 hashVal := hash.Sum(nil) 661 662 // Set and return the hash. 663 a.Hash = hashVal 664 return hashVal 665 } 666 667 // MarshalJSON implements the json.Marshaler interface and allows 668 // ACLAuthMethod.MaxTokenTTL to be marshaled correctly. 669 func (a *ACLAuthMethod) MarshalJSON() ([]byte, error) { 670 type Alias ACLAuthMethod 671 exported := &struct { 672 MaxTokenTTL string 673 *Alias 674 }{ 675 MaxTokenTTL: a.MaxTokenTTL.String(), 676 Alias: (*Alias)(a), 677 } 678 if a.MaxTokenTTL == 0 { 679 exported.MaxTokenTTL = "" 680 } 681 return json.Marshal(exported) 682 } 683 684 // UnmarshalJSON implements the json.Unmarshaler interface and allows 685 // ACLAuthMethod.MaxTokenTTL to be unmarshalled correctly. 686 func (a *ACLAuthMethod) UnmarshalJSON(data []byte) (err error) { 687 type Alias ACLAuthMethod 688 aux := &struct { 689 MaxTokenTTL interface{} 690 *Alias 691 }{ 692 Alias: (*Alias)(a), 693 } 694 if err = json.Unmarshal(data, &aux); err != nil { 695 return err 696 } 697 if aux.MaxTokenTTL != nil { 698 switch v := aux.MaxTokenTTL.(type) { 699 case string: 700 if a.MaxTokenTTL, err = time.ParseDuration(v); err != nil { 701 return err 702 } 703 case float64: 704 a.MaxTokenTTL = time.Duration(v) 705 } 706 } 707 return nil 708 } 709 710 func (a *ACLAuthMethod) Stub() *ACLAuthMethodStub { 711 return &ACLAuthMethodStub{ 712 Name: a.Name, 713 Type: a.Type, 714 Default: a.Default, 715 Hash: a.Hash, 716 CreateIndex: a.CreateIndex, 717 ModifyIndex: a.ModifyIndex, 718 } 719 } 720 721 func (a *ACLAuthMethod) Equal(other *ACLAuthMethod) bool { 722 if a == nil || other == nil { 723 return a == other 724 } 725 if len(a.Hash) == 0 { 726 a.SetHash() 727 } 728 if len(other.Hash) == 0 { 729 other.SetHash() 730 } 731 return bytes.Equal(a.Hash, other.Hash) 732 733 } 734 735 // Copy creates a deep copy of the ACL auth method. This copy can then be safely 736 // modified. It handles nil objects. 737 func (a *ACLAuthMethod) Copy() *ACLAuthMethod { 738 if a == nil { 739 return nil 740 } 741 742 c := new(ACLAuthMethod) 743 *c = *a 744 745 c.Hash = slices.Clone(a.Hash) 746 c.Config = a.Config.Copy() 747 748 return c 749 } 750 751 // Canonicalize performs basic canonicalization on the ACL auth method object. 752 func (a *ACLAuthMethod) Canonicalize() { 753 t := time.Now().UTC() 754 755 if a.CreateTime.IsZero() { 756 a.CreateTime = t 757 } 758 a.ModifyTime = t 759 } 760 761 // Validate returns an error is the ACLAuthMethod is invalid. 762 // 763 // TODO revisit possible other validity conditions in the future 764 func (a *ACLAuthMethod) Validate(minTTL, maxTTL time.Duration) error { 765 var mErr multierror.Error 766 767 if !validACLAuthMethod.MatchString(a.Name) { 768 mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid name '%s'", a.Name)) 769 } 770 771 if !slices.Contains([]string{"local", "global"}, a.TokenLocality) { 772 mErr.Errors = append( 773 mErr.Errors, fmt.Errorf("invalid token locality '%s'", a.TokenLocality)) 774 } 775 776 if a.Type != "OIDC" { 777 mErr.Errors = append( 778 mErr.Errors, fmt.Errorf("invalid token type '%s'", a.Type)) 779 } 780 781 if minTTL > a.MaxTokenTTL || a.MaxTokenTTL > maxTTL { 782 mErr.Errors = append(mErr.Errors, fmt.Errorf( 783 "invalid MaxTokenTTL value '%s' (should be between %s and %s)", 784 a.MaxTokenTTL.String(), minTTL.String(), maxTTL.String())) 785 } 786 787 return mErr.ErrorOrNil() 788 } 789 790 // ACLAuthMethodConfig is used to store configuration of an auth method 791 type ACLAuthMethodConfig struct { 792 OIDCDiscoveryURL string 793 OIDCClientID string 794 OIDCClientSecret string 795 BoundAudiences []string 796 AllowedRedirectURIs []string 797 DiscoveryCaPem []string 798 SigningAlgs []string 799 ClaimMappings map[string]string 800 ListClaimMappings map[string]string 801 } 802 803 func (a *ACLAuthMethodConfig) Copy() *ACLAuthMethodConfig { 804 if a == nil { 805 return nil 806 } 807 808 c := new(ACLAuthMethodConfig) 809 *c = *a 810 811 c.BoundAudiences = slices.Clone(a.BoundAudiences) 812 c.AllowedRedirectURIs = slices.Clone(a.AllowedRedirectURIs) 813 c.DiscoveryCaPem = slices.Clone(a.DiscoveryCaPem) 814 c.SigningAlgs = slices.Clone(a.SigningAlgs) 815 816 return c 817 } 818 819 // ACLAuthMethodStub is used for listing ACL auth methods 820 type ACLAuthMethodStub struct { 821 Name string 822 Type string 823 Default bool 824 825 // Hash is the hashed value of the auth-method and is generated using all 826 // fields from the full object except the create and modify times and 827 // indexes. 828 Hash []byte 829 830 CreateIndex uint64 831 ModifyIndex uint64 832 } 833 834 // ACLAuthMethodListRequest is used to list auth methods 835 type ACLAuthMethodListRequest struct { 836 QueryOptions 837 } 838 839 // ACLAuthMethodListResponse is used to list auth methods 840 type ACLAuthMethodListResponse struct { 841 AuthMethods []*ACLAuthMethodStub 842 QueryMeta 843 } 844 845 // ACLAuthMethodGetRequest is used to query a specific auth method 846 type ACLAuthMethodGetRequest struct { 847 MethodName string 848 QueryOptions 849 } 850 851 // ACLAuthMethodGetResponse is used to return a single auth method 852 type ACLAuthMethodGetResponse struct { 853 AuthMethod *ACLAuthMethod 854 QueryMeta 855 } 856 857 // ACLAuthMethodsGetRequest is used to query a set of auth methods 858 type ACLAuthMethodsGetRequest struct { 859 Names []string 860 QueryOptions 861 } 862 863 // ACLAuthMethodsGetResponse is used to return a set of auth methods 864 type ACLAuthMethodsGetResponse struct { 865 AuthMethods map[string]*ACLAuthMethod 866 QueryMeta 867 } 868 869 // ACLAuthMethodUpsertRequest is used to upsert a set of auth methods 870 type ACLAuthMethodUpsertRequest struct { 871 AuthMethods []*ACLAuthMethod 872 WriteRequest 873 } 874 875 // ACLAuthMethodUpsertResponse is a response of the upsert ACL auth methods 876 // operation 877 type ACLAuthMethodUpsertResponse struct { 878 AuthMethods []*ACLAuthMethod 879 WriteMeta 880 } 881 882 // ACLAuthMethodDeleteRequest is used to delete a set of auth methods by their 883 // name 884 type ACLAuthMethodDeleteRequest struct { 885 Names []string 886 WriteRequest 887 } 888 889 // ACLAuthMethodDeleteResponse is a response of the delete ACL auth methods 890 // operation 891 type ACLAuthMethodDeleteResponse struct { 892 WriteMeta 893 } 894 895 type ACLWhoAmIResponse struct { 896 Identity *AuthenticatedIdentity 897 QueryMeta 898 } 899 900 // ACLBindingRule contains a direct relation to an ACLAuthMethod and represents 901 // a rule to apply when logging in via the named AuthMethod. This allows the 902 // transformation of OIDC provider claims, to Nomad based ACL concepts such as 903 // ACL Roles and Policies. 904 type ACLBindingRule struct { 905 906 // ID is an internally generated UUID for this role and is controlled by 907 // Nomad. 908 ID string 909 910 // Description is a human-readable, operator set description that can 911 // provide additional context about the binding role. This is an 912 // operational field. 913 Description string 914 915 // AuthMethod is the name of the auth method for which this rule applies 916 // to. This is required and the method must exist within state before the 917 // cluster administrator can create the rule. 918 AuthMethod string 919 920 // Selector is an expression that matches against verified identity 921 // attributes returned from the auth method during login. This is optional 922 // and when not set, provides a catch-all rule. 923 Selector string 924 925 // BindType adjusts how this binding rule is applied at login time. The 926 // valid values are ACLBindingRuleBindTypeRole and 927 // ACLBindingRuleBindTypePolicy. 928 BindType string 929 930 // BindName is the target of the binding. Can be lightly templated using 931 // HIL ${foo} syntax from available field names. How it is used depends 932 // upon the BindType. 933 BindName string 934 935 // Hash is the hashed value of the binding rule and is generated using all 936 // fields from the full object except the create and modify times and 937 // indexes. 938 Hash []byte 939 940 CreateTime time.Time 941 ModifyTime time.Time 942 CreateIndex uint64 943 ModifyIndex uint64 944 } 945 946 const ( 947 // ACLBindingRuleBindTypeRole is the ACL binding rule bind type that only 948 // allows the binding rule to function if a role exists at login-time. The 949 // role will be specified within the ACLBindingRule.BindName parameter, and 950 // will identify whether this is an ID or Name. 951 ACLBindingRuleBindTypeRole = "role" 952 953 // ACLBindingRuleBindTypePolicy is the ACL binding rule bind type that 954 // assigns a policy to the generate ACL token. The role will be specified 955 // within the ACLBindingRule.BindName parameter, and will be the policy 956 // name. 957 ACLBindingRuleBindTypePolicy = "policy" 958 ) 959 960 // Canonicalize performs basic canonicalization on the ACL token object. It is 961 // important for callers to understand certain fields such as ID are set if it 962 // is empty, so copies should be taken if needed before calling this function. 963 func (a *ACLBindingRule) Canonicalize() { 964 965 now := time.Now().UTC() 966 967 // If the ID is empty, it means this is creation of a new binding rule, 968 // therefore we need to generate base information. 969 if a.ID == "" { 970 a.ID = uuid.Generate() 971 a.CreateTime = now 972 } 973 974 // The fact this function is being called indicates we are attempting an 975 // upsert into state. Therefore, update the modify time. 976 a.ModifyTime = now 977 } 978 979 // Validate ensures the ACL binding rule contains valid information which meets 980 // Nomad's internal requirements. 981 func (a *ACLBindingRule) Validate() error { 982 983 var mErr multierror.Error 984 985 if a.AuthMethod == "" { 986 mErr.Errors = append(mErr.Errors, errors.New("auth method is missing")) 987 } 988 if a.BindName == "" { 989 mErr.Errors = append(mErr.Errors, errors.New("bind name is missing")) 990 } 991 if len(a.Description) > maxACLBindingRuleDescriptionLength { 992 mErr.Errors = append(mErr.Errors, fmt.Errorf("description longer than %d", maxACLRoleDescriptionLength)) 993 } 994 995 // Be specific about the error as returning an error that includes an empty 996 // quote ("") can be a little confusing. 997 if a.BindType == "" { 998 mErr.Errors = append(mErr.Errors, errors.New("bind type is missing")) 999 } else { 1000 switch a.BindType { 1001 case ACLBindingRuleBindTypeRole, ACLBindingRuleBindTypePolicy: // fall-through. 1002 default: 1003 mErr.Errors = append(mErr.Errors, fmt.Errorf("unsupported bind type: %q", a.BindType)) 1004 } 1005 } 1006 1007 return mErr.ErrorOrNil() 1008 } 1009 1010 // SetHash is used to compute and set the hash of the ACL binding rule. This 1011 // should be called every and each time a user specified field on the method is 1012 // changed before updating the Nomad state store. 1013 func (a *ACLBindingRule) SetHash() []byte { 1014 1015 // Initialize a 256bit Blake2 hash (32 bytes). 1016 hash, err := blake2b.New256(nil) 1017 if err != nil { 1018 panic(err) 1019 } 1020 1021 _, _ = hash.Write([]byte(a.ID)) 1022 _, _ = hash.Write([]byte(a.Description)) 1023 _, _ = hash.Write([]byte(a.AuthMethod)) 1024 _, _ = hash.Write([]byte(a.Selector)) 1025 _, _ = hash.Write([]byte(a.BindType)) 1026 _, _ = hash.Write([]byte(a.BindName)) 1027 1028 // Finalize the hash. 1029 hashVal := hash.Sum(nil) 1030 1031 // Set and return the hash. 1032 a.Hash = hashVal 1033 return hashVal 1034 } 1035 1036 // Equal performs an equality check on the two ACL binding rules. It handles 1037 // nil objects. 1038 func (a *ACLBindingRule) Equal(other *ACLBindingRule) bool { 1039 if a == nil || other == nil { 1040 return a == other 1041 } 1042 if len(a.Hash) == 0 { 1043 a.SetHash() 1044 } 1045 if len(other.Hash) == 0 { 1046 other.SetHash() 1047 } 1048 return bytes.Equal(a.Hash, other.Hash) 1049 } 1050 1051 // Copy creates a deep copy of the ACL binding rule. This copy can then be 1052 // safely modified. It handles nil objects. 1053 func (a *ACLBindingRule) Copy() *ACLBindingRule { 1054 if a == nil { 1055 return nil 1056 } 1057 1058 c := new(ACLBindingRule) 1059 *c = *a 1060 c.Hash = slices.Clone(a.Hash) 1061 1062 return c 1063 } 1064 1065 // Stub converts the ACLBindingRule object into a ACLBindingRuleListStub 1066 // object. 1067 func (a *ACLBindingRule) Stub() *ACLBindingRuleListStub { 1068 return &ACLBindingRuleListStub{ 1069 ID: a.ID, 1070 Description: a.Description, 1071 AuthMethod: a.AuthMethod, 1072 Hash: a.Hash, 1073 CreateIndex: a.CreateIndex, 1074 ModifyIndex: a.ModifyIndex, 1075 } 1076 } 1077 1078 // ACLBindingRuleListStub is the stub object returned when performing a listing 1079 // of ACL binding rules. 1080 type ACLBindingRuleListStub struct { 1081 1082 // ID is an internally generated UUID for this role and is controlled by 1083 // Nomad. 1084 ID string 1085 1086 // Description is a human-readable, operator set description that can 1087 // provide additional context about the binding role. This is an 1088 // operational field. 1089 Description string 1090 1091 // AuthMethod is the name of the auth method for which this rule applies 1092 // to. This is required and the method must exist within state before the 1093 // cluster administrator can create the rule. 1094 AuthMethod string 1095 1096 // Hash is the hashed value of the binding rule and is generated using all 1097 // fields from the full object except the create and modify times and 1098 // indexes. 1099 Hash []byte 1100 1101 CreateIndex uint64 1102 ModifyIndex uint64 1103 } 1104 1105 // ACLBindingRulesUpsertRequest is used to upsert a set of ACL binding rules. 1106 type ACLBindingRulesUpsertRequest struct { 1107 ACLBindingRules []*ACLBindingRule 1108 WriteRequest 1109 } 1110 1111 // ACLBindingRulesUpsertResponse is a response of the upsert ACL binding rules 1112 // operation. 1113 type ACLBindingRulesUpsertResponse struct { 1114 ACLBindingRules []*ACLBindingRule 1115 WriteMeta 1116 } 1117 1118 // ACLBindingRulesDeleteRequest is used to delete a set of ACL binding rules by 1119 // their IDs. 1120 type ACLBindingRulesDeleteRequest struct { 1121 ACLBindingRuleIDs []string 1122 WriteRequest 1123 } 1124 1125 // ACLBindingRulesDeleteResponse is a response of the delete ACL binding rules 1126 // operation. 1127 type ACLBindingRulesDeleteResponse struct { 1128 WriteMeta 1129 } 1130 1131 // ACLBindingRulesListRequest is the request object when performing ACL 1132 // binding rules listings. 1133 type ACLBindingRulesListRequest struct { 1134 QueryOptions 1135 } 1136 1137 // ACLBindingRulesListResponse is the response object when performing ACL 1138 // binding rule listings. 1139 type ACLBindingRulesListResponse struct { 1140 ACLBindingRules []*ACLBindingRuleListStub 1141 QueryMeta 1142 } 1143 1144 // ACLBindingRulesRequest is the request object when performing a lookup of 1145 // multiple binding rules by the ID. 1146 type ACLBindingRulesRequest struct { 1147 ACLBindingRuleIDs []string 1148 QueryOptions 1149 } 1150 1151 // ACLBindingRulesResponse is the response object when performing a lookup of 1152 // multiple binding rules by their IDs. 1153 type ACLBindingRulesResponse struct { 1154 ACLBindingRules map[string]*ACLBindingRule 1155 QueryMeta 1156 } 1157 1158 // ACLBindingRuleRequest is the request object to perform a lookup of an ACL 1159 // binding rule using a specific ID. 1160 type ACLBindingRuleRequest struct { 1161 ACLBindingRuleID string 1162 QueryOptions 1163 } 1164 1165 // ACLBindingRuleResponse is the response object when performing a lookup of an 1166 // ACL binding rule matching a specific ID. 1167 type ACLBindingRuleResponse struct { 1168 ACLBindingRule *ACLBindingRule 1169 QueryMeta 1170 }