1package vault 2 3import ( 4 "context" 5 "crypto/sha256" 6 "crypto/sha512" 7 "encoding/base64" 8 "encoding/hex" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "hash" 13 "net/http" 14 "path/filepath" 15 "sort" 16 "strconv" 17 "strings" 18 "sync" 19 "time" 20 21 "github.com/hashicorp/vault/physical/raft" 22 23 "github.com/hashicorp/errwrap" 24 log "github.com/hashicorp/go-hclog" 25 memdb "github.com/hashicorp/go-memdb" 26 uuid "github.com/hashicorp/go-uuid" 27 "github.com/hashicorp/vault/helper/identity" 28 "github.com/hashicorp/vault/helper/metricsutil" 29 "github.com/hashicorp/vault/helper/namespace" 30 "github.com/hashicorp/vault/sdk/framework" 31 "github.com/hashicorp/vault/sdk/helper/compressutil" 32 "github.com/hashicorp/vault/sdk/helper/consts" 33 "github.com/hashicorp/vault/sdk/helper/jsonutil" 34 "github.com/hashicorp/vault/sdk/helper/parseutil" 35 "github.com/hashicorp/vault/sdk/helper/strutil" 36 "github.com/hashicorp/vault/sdk/helper/wrapping" 37 "github.com/hashicorp/vault/sdk/logical" 38 "github.com/mitchellh/mapstructure" 39) 40 41var ( 42 // protectedPaths cannot be accessed via the raw APIs. 43 // This is both for security and to prevent disrupting Vault. 44 protectedPaths = []string{ 45 keyringPath, 46 // Changing the cluster info path can change the cluster ID which can be disruptive 47 coreLocalClusterInfoPath, 48 } 49) 50 51const maxBytes = 128 * 1024 52 53func systemBackendMemDBSchema() *memdb.DBSchema { 54 systemSchema := &memdb.DBSchema{ 55 Tables: make(map[string]*memdb.TableSchema), 56 } 57 58 schemas := getSystemSchemas() 59 60 for _, schemaFunc := range schemas { 61 schema := schemaFunc() 62 if _, ok := systemSchema.Tables[schema.Name]; ok { 63 panic(fmt.Sprintf("duplicate table name: %s", schema.Name)) 64 } 65 systemSchema.Tables[schema.Name] = schema 66 } 67 68 return systemSchema 69} 70 71func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { 72 db, _ := memdb.NewMemDB(systemBackendMemDBSchema()) 73 74 b := &SystemBackend{ 75 Core: core, 76 db: db, 77 logger: logger, 78 mfaLogger: core.baseLogger.Named("mfa"), 79 mfaLock: &sync.RWMutex{}, 80 } 81 82 core.AddLogger(b.mfaLogger) 83 84 b.Backend = &framework.Backend{ 85 Help: strings.TrimSpace(sysHelpRoot), 86 87 PathsSpecial: &logical.Paths{ 88 Root: []string{ 89 "auth/*", 90 "remount", 91 "audit", 92 "audit/*", 93 "raw", 94 "raw/*", 95 "replication/primary/secondary-token", 96 "replication/performance/primary/secondary-token", 97 "replication/dr/primary/secondary-token", 98 "replication/reindex", 99 "replication/dr/reindex", 100 "replication/performance/reindex", 101 "rotate", 102 "config/cors", 103 "config/auditing/*", 104 "config/ui/headers/*", 105 "plugins/catalog/*", 106 "revoke-prefix/*", 107 "revoke-force/*", 108 "leases/revoke-prefix/*", 109 "leases/revoke-force/*", 110 "leases/lookup/*", 111 }, 112 113 Unauthenticated: []string{ 114 "wrapping/lookup", 115 "wrapping/pubkey", 116 "replication/status", 117 "internal/specs/openapi", 118 "internal/ui/mounts", 119 "internal/ui/mounts/*", 120 "internal/ui/namespaces", 121 "replication/performance/status", 122 "replication/dr/status", 123 "replication/dr/secondary/promote", 124 "replication/dr/secondary/update-primary", 125 "replication/dr/secondary/operation-token/delete", 126 "replication/dr/secondary/license", 127 "replication/dr/secondary/reindex", 128 "storage/raft/bootstrap/challenge", 129 "storage/raft/bootstrap/answer", 130 "init", 131 "seal-status", 132 "unseal", 133 "leader", 134 "health", 135 "generate-root/attempt", 136 "generate-root/update", 137 "rekey/init", 138 "rekey/update", 139 "rekey/verify", 140 "rekey-recovery-key/init", 141 "rekey-recovery-key/update", 142 "rekey-recovery-key/verify", 143 }, 144 145 LocalStorage: []string{ 146 expirationSubPath, 147 }, 148 }, 149 } 150 151 b.Backend.Paths = append(b.Backend.Paths, entPaths(b)...) 152 b.Backend.Paths = append(b.Backend.Paths, b.configPaths()...) 153 b.Backend.Paths = append(b.Backend.Paths, b.rekeyPaths()...) 154 b.Backend.Paths = append(b.Backend.Paths, b.sealPaths()...) 155 b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogListPaths()...) 156 b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogCRUDPath()) 157 b.Backend.Paths = append(b.Backend.Paths, b.pluginsReloadPath()) 158 b.Backend.Paths = append(b.Backend.Paths, b.auditPaths()...) 159 b.Backend.Paths = append(b.Backend.Paths, b.mountPaths()...) 160 b.Backend.Paths = append(b.Backend.Paths, b.authPaths()...) 161 b.Backend.Paths = append(b.Backend.Paths, b.leasePaths()...) 162 b.Backend.Paths = append(b.Backend.Paths, b.policyPaths()...) 163 b.Backend.Paths = append(b.Backend.Paths, b.wrappingPaths()...) 164 b.Backend.Paths = append(b.Backend.Paths, b.toolsPaths()...) 165 b.Backend.Paths = append(b.Backend.Paths, b.capabilitiesPaths()...) 166 b.Backend.Paths = append(b.Backend.Paths, b.internalPaths()...) 167 b.Backend.Paths = append(b.Backend.Paths, b.remountPath()) 168 b.Backend.Paths = append(b.Backend.Paths, b.metricsPath()) 169 170 if core.rawEnabled { 171 b.Backend.Paths = append(b.Backend.Paths, &framework.Path{ 172 Pattern: "(raw/?$|raw/(?P<path>.+))", 173 174 Fields: map[string]*framework.FieldSchema{ 175 "path": &framework.FieldSchema{ 176 Type: framework.TypeString, 177 }, 178 "value": &framework.FieldSchema{ 179 Type: framework.TypeString, 180 }, 181 }, 182 183 Operations: map[logical.Operation]framework.OperationHandler{ 184 logical.ReadOperation: &framework.PathOperation{ 185 Callback: b.handleRawRead, 186 Summary: "Read the value of the key at the given path.", 187 }, 188 logical.UpdateOperation: &framework.PathOperation{ 189 Callback: b.handleRawWrite, 190 Summary: "Update the value of the key at the given path.", 191 }, 192 logical.DeleteOperation: &framework.PathOperation{ 193 Callback: b.handleRawDelete, 194 Summary: "Delete the key with given path.", 195 }, 196 logical.ListOperation: &framework.PathOperation{ 197 Callback: b.handleRawList, 198 Summary: "Return a list keys for a given path prefix.", 199 }, 200 }, 201 202 HelpSynopsis: strings.TrimSpace(sysHelp["raw"][0]), 203 HelpDescription: strings.TrimSpace(sysHelp["raw"][1]), 204 }) 205 } 206 207 if _, ok := core.underlyingPhysical.(*raft.RaftBackend); ok { 208 b.Backend.Paths = append(b.Backend.Paths, b.raftStoragePaths()...) 209 } 210 211 b.Backend.Invalidate = sysInvalidate(b) 212 return b 213} 214 215// SystemBackend implements logical.Backend and is used to interact with 216// the core of the system. This backend is hardcoded to exist at the "sys" 217// prefix. Conceptually it is similar to procfs on Linux. 218type SystemBackend struct { 219 *framework.Backend 220 Core *Core 221 db *memdb.MemDB 222 mfaLock *sync.RWMutex 223 mfaLogger log.Logger 224 logger log.Logger 225} 226 227// handleCORSRead returns the current CORS configuration 228func (b *SystemBackend) handleCORSRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 229 corsConf := b.Core.corsConfig 230 231 enabled := corsConf.IsEnabled() 232 233 resp := &logical.Response{ 234 Data: map[string]interface{}{ 235 "enabled": enabled, 236 }, 237 } 238 239 if enabled { 240 corsConf.RLock() 241 resp.Data["allowed_origins"] = corsConf.AllowedOrigins 242 resp.Data["allowed_headers"] = corsConf.AllowedHeaders 243 corsConf.RUnlock() 244 } 245 246 return resp, nil 247} 248 249// handleCORSUpdate sets the list of origins that are allowed to make 250// cross-origin requests and sets the CORS enabled flag to true 251func (b *SystemBackend) handleCORSUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 252 origins := d.Get("allowed_origins").([]string) 253 headers := d.Get("allowed_headers").([]string) 254 255 return nil, b.Core.corsConfig.Enable(ctx, origins, headers) 256} 257 258// handleCORSDelete sets the CORS enabled flag to false and clears the list of 259// allowed origins & headers. 260func (b *SystemBackend) handleCORSDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 261 return nil, b.Core.corsConfig.Disable(ctx) 262} 263 264func (b *SystemBackend) handleTidyLeases(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 265 ns, err := namespace.FromContext(ctx) 266 if err != nil { 267 return nil, err 268 } 269 270 go func() { 271 tidyCtx := namespace.ContextWithNamespace(b.Core.activeContext, ns) 272 err := b.Core.expiration.Tidy(tidyCtx) 273 if err != nil { 274 b.Backend.Logger().Error("failed to tidy leases", "error", err) 275 return 276 } 277 }() 278 279 resp := &logical.Response{} 280 resp.AddWarning("Tidy operation successfully started. Any information from the operation will be printed to Vault's server logs.") 281 return logical.RespondWithStatusCode(resp, req, http.StatusAccepted) 282} 283 284func (b *SystemBackend) handlePluginCatalogTypedList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 285 pluginType, err := consts.ParsePluginType(d.Get("type").(string)) 286 if err != nil { 287 return nil, err 288 } 289 290 plugins, err := b.Core.pluginCatalog.List(ctx, pluginType) 291 if err != nil { 292 return nil, err 293 } 294 return logical.ListResponse(plugins), nil 295} 296 297func (b *SystemBackend) handlePluginCatalogUntypedList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 298 pluginsByType := make(map[string]interface{}) 299 for _, pluginType := range consts.PluginTypes { 300 plugins, err := b.Core.pluginCatalog.List(ctx, pluginType) 301 if err != nil { 302 return nil, err 303 } 304 if len(plugins) > 0 { 305 sort.Strings(plugins) 306 pluginsByType[pluginType.String()] = plugins 307 } 308 } 309 return &logical.Response{ 310 Data: pluginsByType, 311 }, nil 312} 313 314func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 315 pluginName := d.Get("name").(string) 316 if pluginName == "" { 317 return logical.ErrorResponse("missing plugin name"), nil 318 } 319 320 pluginTypeStr := d.Get("type").(string) 321 if pluginTypeStr == "" { 322 // If the plugin type is not provided, list it as unknown so that we 323 // add it to the catalog and UpdatePlugins later will sort it. 324 pluginTypeStr = "unknown" 325 } 326 pluginType, err := consts.ParsePluginType(pluginTypeStr) 327 if err != nil { 328 return nil, err 329 } 330 331 sha256 := d.Get("sha256").(string) 332 if sha256 == "" { 333 sha256 = d.Get("sha_256").(string) 334 if sha256 == "" { 335 return logical.ErrorResponse("missing SHA-256 value"), nil 336 } 337 } 338 339 command := d.Get("command").(string) 340 if command == "" { 341 return logical.ErrorResponse("missing command value"), nil 342 } 343 344 // For backwards compatibility, also accept args as part of command. Don't 345 // accepts args in both command and args. 346 args := d.Get("args").([]string) 347 parts := strings.Split(command, " ") 348 if len(parts) <= 0 { 349 return logical.ErrorResponse("missing command value"), nil 350 } else if len(parts) > 1 && len(args) > 0 { 351 return logical.ErrorResponse("must not specify args in command and args field"), nil 352 } else if len(parts) > 1 { 353 args = parts[1:] 354 } 355 356 env := d.Get("env").([]string) 357 358 sha256Bytes, err := hex.DecodeString(sha256) 359 if err != nil { 360 return logical.ErrorResponse("Could not decode SHA-256 value from Hex"), err 361 } 362 363 err = b.Core.pluginCatalog.Set(ctx, pluginName, pluginType, parts[0], args, env, sha256Bytes) 364 if err != nil { 365 return nil, err 366 } 367 368 return nil, nil 369} 370 371func (b *SystemBackend) handlePluginCatalogRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 372 pluginName := d.Get("name").(string) 373 if pluginName == "" { 374 return logical.ErrorResponse("missing plugin name"), nil 375 } 376 377 pluginTypeStr := d.Get("type").(string) 378 if pluginTypeStr == "" { 379 // If the plugin type is not provided (i.e. the old 380 // sys/plugins/catalog/:name endpoint is being requested) short-circuit here 381 // and return a warning 382 resp := &logical.Response{} 383 resp.AddWarning(fmt.Sprintf("Deprecated API endpoint, cannot read plugin information from catalog for %q", pluginName)) 384 return resp, nil 385 } 386 387 pluginType, err := consts.ParsePluginType(pluginTypeStr) 388 if err != nil { 389 return nil, err 390 } 391 392 plugin, err := b.Core.pluginCatalog.Get(ctx, pluginName, pluginType) 393 if err != nil { 394 return nil, err 395 } 396 if plugin == nil { 397 return nil, nil 398 } 399 400 command := "" 401 if !plugin.Builtin { 402 command, err = filepath.Rel(b.Core.pluginCatalog.directory, plugin.Command) 403 if err != nil { 404 return nil, err 405 } 406 } 407 408 data := map[string]interface{}{ 409 "name": plugin.Name, 410 "args": plugin.Args, 411 "command": command, 412 "sha256": hex.EncodeToString(plugin.Sha256), 413 "builtin": plugin.Builtin, 414 } 415 416 return &logical.Response{ 417 Data: data, 418 }, nil 419} 420 421func (b *SystemBackend) handlePluginCatalogDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 422 pluginName := d.Get("name").(string) 423 if pluginName == "" { 424 return logical.ErrorResponse("missing plugin name"), nil 425 } 426 427 var resp *logical.Response 428 pluginTypeStr := d.Get("type").(string) 429 if pluginTypeStr == "" { 430 // If the plugin type is not provided (i.e. the old 431 // sys/plugins/catalog/:name endpoint is being requested), set type to 432 // unknown and let pluginCatalog.Delete proceed. It should handle 433 // deregistering out of the old storage path (root of core/plugin-catalog) 434 resp = new(logical.Response) 435 resp.AddWarning(fmt.Sprintf("Deprecated API endpoint, cannot deregister plugin from catalog for %q", pluginName)) 436 pluginTypeStr = "unknown" 437 } 438 439 pluginType, err := consts.ParsePluginType(pluginTypeStr) 440 if err != nil { 441 return nil, err 442 } 443 if err := b.Core.pluginCatalog.Delete(ctx, pluginName, pluginType); err != nil { 444 return nil, err 445 } 446 447 return resp, nil 448} 449 450func (b *SystemBackend) handlePluginReloadUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 451 pluginName := d.Get("plugin").(string) 452 pluginMounts := d.Get("mounts").([]string) 453 454 if pluginName != "" && len(pluginMounts) > 0 { 455 return logical.ErrorResponse("plugin and mounts cannot be set at the same time"), nil 456 } 457 if pluginName == "" && len(pluginMounts) == 0 { 458 return logical.ErrorResponse("plugin or mounts must be provided"), nil 459 } 460 461 if pluginName != "" { 462 err := b.Core.reloadMatchingPlugin(ctx, pluginName) 463 if err != nil { 464 return nil, err 465 } 466 } else if len(pluginMounts) > 0 { 467 err := b.Core.reloadMatchingPluginMounts(ctx, pluginMounts) 468 if err != nil { 469 return nil, err 470 } 471 } 472 473 return nil, nil 474} 475 476// handleAuditedHeaderUpdate creates or overwrites a header entry 477func (b *SystemBackend) handleAuditedHeaderUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 478 header := d.Get("header").(string) 479 hmac := d.Get("hmac").(bool) 480 if header == "" { 481 return logical.ErrorResponse("missing header name"), nil 482 } 483 484 headerConfig := b.Core.AuditedHeadersConfig() 485 err := headerConfig.add(ctx, header, hmac) 486 if err != nil { 487 return nil, err 488 } 489 490 return nil, nil 491} 492 493// handleAuditedHeaderDelete deletes the header with the given name 494func (b *SystemBackend) handleAuditedHeaderDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 495 header := d.Get("header").(string) 496 if header == "" { 497 return logical.ErrorResponse("missing header name"), nil 498 } 499 500 headerConfig := b.Core.AuditedHeadersConfig() 501 err := headerConfig.remove(ctx, header) 502 if err != nil { 503 return nil, err 504 } 505 506 return nil, nil 507} 508 509// handleAuditedHeaderRead returns the header configuration for the given header name 510func (b *SystemBackend) handleAuditedHeaderRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 511 header := d.Get("header").(string) 512 if header == "" { 513 return logical.ErrorResponse("missing header name"), nil 514 } 515 516 headerConfig := b.Core.AuditedHeadersConfig() 517 settings, ok := headerConfig.Headers[strings.ToLower(header)] 518 if !ok { 519 return logical.ErrorResponse("Could not find header in config"), nil 520 } 521 522 return &logical.Response{ 523 Data: map[string]interface{}{ 524 header: settings, 525 }, 526 }, nil 527} 528 529// handleAuditedHeadersRead returns the whole audited headers config 530func (b *SystemBackend) handleAuditedHeadersRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 531 headerConfig := b.Core.AuditedHeadersConfig() 532 533 return &logical.Response{ 534 Data: map[string]interface{}{ 535 "headers": headerConfig.Headers, 536 }, 537 }, nil 538} 539 540// handleCapabilitiesAccessor returns the ACL capabilities of the 541// token associated with the given accessor for a given path. 542func (b *SystemBackend) handleCapabilitiesAccessor(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 543 accessor := d.Get("accessor").(string) 544 if accessor == "" { 545 return logical.ErrorResponse("missing accessor"), nil 546 } 547 548 aEntry, err := b.Core.tokenStore.lookupByAccessor(ctx, accessor, false, false) 549 if err != nil { 550 return nil, err 551 } 552 553 d.Raw["token"] = aEntry.TokenID 554 return b.handleCapabilities(ctx, req, d) 555} 556 557// handleCapabilities returns the ACL capabilities of the token for a given path 558func (b *SystemBackend) handleCapabilities(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 559 var token string 560 if strings.HasSuffix(req.Path, "capabilities-self") { 561 token = req.ClientToken 562 } else { 563 tokenRaw, ok := d.Raw["token"] 564 if ok { 565 token, _ = tokenRaw.(string) 566 } 567 } 568 if token == "" { 569 return nil, fmt.Errorf("no token found") 570 } 571 572 ret := &logical.Response{ 573 Data: map[string]interface{}{}, 574 } 575 576 paths := d.Get("paths").([]string) 577 if len(paths) == 0 { 578 // Read from the deprecated field 579 paths = d.Get("path").([]string) 580 } 581 582 if len(paths) == 0 { 583 return logical.ErrorResponse("paths must be supplied"), nil 584 } 585 586 for _, path := range paths { 587 pathCap, err := b.Core.Capabilities(ctx, token, path) 588 if err != nil { 589 if !strings.HasSuffix(req.Path, "capabilities-self") && errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { 590 return nil, &logical.StatusBadRequest{Err: "invalid token"} 591 } 592 return nil, err 593 } 594 ret.Data[path] = pathCap 595 } 596 597 // This is only here for backwards compatibility 598 if len(paths) == 1 { 599 ret.Data["capabilities"] = ret.Data[paths[0]] 600 } 601 602 return ret, nil 603} 604 605// handleRekeyRetrieve returns backed-up, PGP-encrypted unseal keys from a 606// rekey operation 607func (b *SystemBackend) handleRekeyRetrieve( 608 ctx context.Context, 609 req *logical.Request, 610 data *framework.FieldData, 611 recovery bool) (*logical.Response, error) { 612 backup, err := b.Core.RekeyRetrieveBackup(ctx, recovery) 613 if err != nil { 614 return nil, errwrap.Wrapf("unable to look up backed-up keys: {{err}}", err) 615 } 616 if backup == nil { 617 return logical.ErrorResponse("no backed-up keys found"), nil 618 } 619 620 keysB64 := map[string][]string{} 621 for k, v := range backup.Keys { 622 for _, j := range v { 623 currB64Keys := keysB64[k] 624 if currB64Keys == nil { 625 currB64Keys = []string{} 626 } 627 key, err := hex.DecodeString(j) 628 if err != nil { 629 return nil, errwrap.Wrapf("error decoding hex-encoded backup key: {{err}}", err) 630 } 631 currB64Keys = append(currB64Keys, base64.StdEncoding.EncodeToString(key)) 632 keysB64[k] = currB64Keys 633 } 634 } 635 636 // Format the status 637 resp := &logical.Response{ 638 Data: map[string]interface{}{ 639 "nonce": backup.Nonce, 640 "keys": backup.Keys, 641 "keys_base64": keysB64, 642 }, 643 } 644 645 return resp, nil 646} 647 648func (b *SystemBackend) handleRekeyRetrieveBarrier(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 649 return b.handleRekeyRetrieve(ctx, req, data, false) 650} 651 652func (b *SystemBackend) handleRekeyRetrieveRecovery(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 653 return b.handleRekeyRetrieve(ctx, req, data, true) 654} 655 656// handleRekeyDelete deletes backed-up, PGP-encrypted unseal keys from a rekey 657// operation 658func (b *SystemBackend) handleRekeyDelete( 659 ctx context.Context, 660 req *logical.Request, 661 data *framework.FieldData, 662 recovery bool) (*logical.Response, error) { 663 err := b.Core.RekeyDeleteBackup(ctx, recovery) 664 if err != nil { 665 return nil, errwrap.Wrapf("error during deletion of backed-up keys: {{err}}", err) 666 } 667 668 return nil, nil 669} 670 671func (b *SystemBackend) handleRekeyDeleteBarrier(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 672 return b.handleRekeyDelete(ctx, req, data, false) 673} 674 675func (b *SystemBackend) handleRekeyDeleteRecovery(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 676 return b.handleRekeyDelete(ctx, req, data, true) 677} 678 679func mountInfo(entry *MountEntry) map[string]interface{} { 680 info := map[string]interface{}{ 681 "type": entry.Type, 682 "description": entry.Description, 683 "accessor": entry.Accessor, 684 "local": entry.Local, 685 "seal_wrap": entry.SealWrap, 686 "options": entry.Options, 687 "uuid": entry.UUID, 688 } 689 entryConfig := map[string]interface{}{ 690 "default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()), 691 "max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()), 692 "force_no_cache": entry.Config.ForceNoCache, 693 } 694 if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok { 695 entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string) 696 } 697 if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok { 698 entryConfig["audit_non_hmac_response_keys"] = rawVal.([]string) 699 } 700 // Even though empty value is valid for ListingVisibility, we can ignore 701 // this case during mount since there's nothing to unset/hide. 702 if len(entry.Config.ListingVisibility) > 0 { 703 entryConfig["listing_visibility"] = entry.Config.ListingVisibility 704 } 705 if rawVal, ok := entry.synthesizedConfigCache.Load("passthrough_request_headers"); ok { 706 entryConfig["passthrough_request_headers"] = rawVal.([]string) 707 } 708 if rawVal, ok := entry.synthesizedConfigCache.Load("allowed_response_headers"); ok { 709 entryConfig["allowed_response_headers"] = rawVal.([]string) 710 } 711 if entry.Table == credentialTableType { 712 entryConfig["token_type"] = entry.Config.TokenType.String() 713 } 714 715 info["config"] = entryConfig 716 717 return info 718} 719 720// handleMountTable handles the "mounts" endpoint to provide the mount table 721func (b *SystemBackend) handleMountTable(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 722 ns, err := namespace.FromContext(ctx) 723 if err != nil { 724 return nil, err 725 } 726 727 b.Core.mountsLock.RLock() 728 defer b.Core.mountsLock.RUnlock() 729 730 resp := &logical.Response{ 731 Data: make(map[string]interface{}), 732 } 733 734 for _, entry := range b.Core.mounts.Entries { 735 // Only show entries for current namespace 736 if entry.Namespace().Path != ns.Path { 737 continue 738 } 739 740 cont, err := b.Core.checkReplicatedFiltering(ctx, entry, "") 741 if err != nil { 742 return nil, err 743 } 744 if cont { 745 continue 746 } 747 748 // Populate mount info 749 info := mountInfo(entry) 750 resp.Data[entry.Path] = info 751 } 752 753 return resp, nil 754} 755 756// handleMount is used to mount a new path 757func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 758 repState := b.Core.ReplicationState() 759 760 local := data.Get("local").(bool) 761 // If we are a performance secondary cluster we should forward the request 762 // to the primary. We fail early here since the view in use isn't marked as 763 // readonly 764 if !local && repState.HasState(consts.ReplicationPerformanceSecondary) { 765 return nil, logical.ErrReadOnly 766 } 767 768 // Get all the options 769 path := data.Get("path").(string) 770 path = sanitizeMountPath(path) 771 772 logicalType := data.Get("type").(string) 773 description := data.Get("description").(string) 774 pluginName := data.Get("plugin_name").(string) 775 sealWrap := data.Get("seal_wrap").(bool) 776 options := data.Get("options").(map[string]string) 777 778 var config MountConfig 779 var apiConfig APIMountConfig 780 781 configMap := data.Get("config").(map[string]interface{}) 782 if configMap != nil && len(configMap) != 0 { 783 err := mapstructure.Decode(configMap, &apiConfig) 784 if err != nil { 785 return logical.ErrorResponse( 786 "unable to convert given mount config information"), 787 logical.ErrInvalidRequest 788 } 789 } 790 791 switch apiConfig.DefaultLeaseTTL { 792 case "": 793 case "system": 794 default: 795 tmpDef, err := parseutil.ParseDurationSecond(apiConfig.DefaultLeaseTTL) 796 if err != nil { 797 return logical.ErrorResponse(fmt.Sprintf( 798 "unable to parse default TTL of %s: %s", apiConfig.DefaultLeaseTTL, err)), 799 logical.ErrInvalidRequest 800 } 801 config.DefaultLeaseTTL = tmpDef 802 } 803 804 switch apiConfig.MaxLeaseTTL { 805 case "": 806 case "system": 807 default: 808 tmpMax, err := parseutil.ParseDurationSecond(apiConfig.MaxLeaseTTL) 809 if err != nil { 810 return logical.ErrorResponse(fmt.Sprintf( 811 "unable to parse max TTL of %s: %s", apiConfig.MaxLeaseTTL, err)), 812 logical.ErrInvalidRequest 813 } 814 config.MaxLeaseTTL = tmpMax 815 } 816 817 if config.MaxLeaseTTL != 0 && config.DefaultLeaseTTL > config.MaxLeaseTTL { 818 return logical.ErrorResponse( 819 "given default lease TTL greater than given max lease TTL"), 820 logical.ErrInvalidRequest 821 } 822 823 if config.DefaultLeaseTTL > b.Core.maxLeaseTTL && config.MaxLeaseTTL == 0 { 824 return logical.ErrorResponse(fmt.Sprintf( 825 "given default lease TTL greater than system max lease TTL of %d", int(b.Core.maxLeaseTTL.Seconds()))), 826 logical.ErrInvalidRequest 827 } 828 829 switch logicalType { 830 case "": 831 return logical.ErrorResponse( 832 "backend type must be specified as a string"), 833 logical.ErrInvalidRequest 834 case "plugin": 835 // Only set plugin-name if mount is of type plugin, with apiConfig.PluginName 836 // option taking precedence. 837 switch { 838 case apiConfig.PluginName != "": 839 logicalType = apiConfig.PluginName 840 case pluginName != "": 841 logicalType = pluginName 842 default: 843 return logical.ErrorResponse( 844 "plugin_name must be provided for plugin backend"), 845 logical.ErrInvalidRequest 846 } 847 } 848 849 switch logicalType { 850 case "kv": 851 case "kv-v1": 852 // Alias KV v1 853 logicalType = "kv" 854 if options == nil { 855 options = map[string]string{} 856 } 857 options["version"] = "1" 858 859 case "kv-v2": 860 // Alias KV v2 861 logicalType = "kv" 862 if options == nil { 863 options = map[string]string{} 864 } 865 options["version"] = "2" 866 867 default: 868 if options != nil && options["version"] != "" { 869 return logical.ErrorResponse(fmt.Sprintf( 870 "secrets engine %q does not allow setting a version", logicalType)), 871 logical.ErrInvalidRequest 872 } 873 } 874 875 // Copy over the force no cache if set 876 if apiConfig.ForceNoCache { 877 config.ForceNoCache = true 878 } 879 880 if err := checkListingVisibility(apiConfig.ListingVisibility); err != nil { 881 return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", apiConfig.ListingVisibility)), nil 882 } 883 config.ListingVisibility = apiConfig.ListingVisibility 884 885 if len(apiConfig.AuditNonHMACRequestKeys) > 0 { 886 config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys 887 } 888 if len(apiConfig.AuditNonHMACResponseKeys) > 0 { 889 config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys 890 } 891 if len(apiConfig.PassthroughRequestHeaders) > 0 { 892 config.PassthroughRequestHeaders = apiConfig.PassthroughRequestHeaders 893 } 894 if len(apiConfig.AllowedResponseHeaders) > 0 { 895 config.AllowedResponseHeaders = apiConfig.AllowedResponseHeaders 896 } 897 898 // Create the mount entry 899 me := &MountEntry{ 900 Table: mountTableType, 901 Path: path, 902 Type: logicalType, 903 Description: description, 904 Config: config, 905 Local: local, 906 SealWrap: sealWrap, 907 Options: options, 908 } 909 910 // Attempt mount 911 if err := b.Core.mount(ctx, me); err != nil { 912 b.Backend.Logger().Error("mount failed", "path", me.Path, "error", err) 913 return handleError(err) 914 } 915 916 return nil, nil 917} 918 919// used to intercept an HTTPCodedError so it goes back to callee 920func handleError( 921 err error) (*logical.Response, error) { 922 if strings.Contains(err.Error(), logical.ErrReadOnly.Error()) { 923 return logical.ErrorResponse(err.Error()), err 924 } 925 switch err.(type) { 926 case logical.HTTPCodedError: 927 return logical.ErrorResponse(err.Error()), err 928 default: 929 return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest 930 } 931} 932 933// Performs a similar function to handleError, but upon seeing a ReadOnlyError 934// will actually strip it out to prevent forwarding 935func handleErrorNoReadOnlyForward( 936 err error) (*logical.Response, error) { 937 if strings.Contains(err.Error(), logical.ErrReadOnly.Error()) { 938 return nil, fmt.Errorf("operation could not be completed as storage is read-only") 939 } 940 switch err.(type) { 941 case logical.HTTPCodedError: 942 return logical.ErrorResponse(err.Error()), err 943 default: 944 return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest 945 } 946} 947 948// handleUnmount is used to unmount a path 949func (b *SystemBackend) handleUnmount(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 950 path := data.Get("path").(string) 951 path = sanitizeMountPath(path) 952 953 ns, err := namespace.FromContext(ctx) 954 if err != nil { 955 return nil, err 956 } 957 958 repState := b.Core.ReplicationState() 959 entry := b.Core.router.MatchingMountEntry(ctx, path) 960 961 // If we are a performance secondary cluster we should forward the request 962 // to the primary. We fail early here since the view in use isn't marked as 963 // readonly 964 if entry != nil && !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) { 965 return nil, logical.ErrReadOnly 966 } 967 968 // We return success when the mount does not exists to not expose if the 969 // mount existed or not 970 match := b.Core.router.MatchingMount(ctx, path) 971 if match == "" || ns.Path+path != match { 972 return nil, nil 973 } 974 975 prefix, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, path) 976 if !found { 977 b.Backend.Logger().Error("unable to find storage for path", "path", path) 978 return handleError(fmt.Errorf("unable to find storage for path: %q", path)) 979 } 980 981 // Attempt unmount 982 if err := b.Core.unmount(ctx, path); err != nil { 983 b.Backend.Logger().Error("unmount failed", "path", path, "error", err) 984 return handleError(err) 985 } 986 987 // Remove from filtered mounts 988 if err := b.Core.removePrefixFromFilteredPaths(ctx, prefix); err != nil { 989 b.Backend.Logger().Error("filtered path removal failed", path, "error", err) 990 return handleError(err) 991 } 992 993 return nil, nil 994} 995 996// handleRemount is used to remount a path 997func (b *SystemBackend) handleRemount(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 998 repState := b.Core.ReplicationState() 999 1000 // Get the paths 1001 fromPath := data.Get("from").(string) 1002 toPath := data.Get("to").(string) 1003 if fromPath == "" || toPath == "" { 1004 return logical.ErrorResponse( 1005 "both 'from' and 'to' path must be specified as a string"), 1006 logical.ErrInvalidRequest 1007 } 1008 1009 entry := b.Core.router.MatchingMountEntry(ctx, fromPath) 1010 // If we are a performance secondary cluster we should forward the request 1011 // to the primary. We fail early here since the view in use isn't marked as 1012 // readonly 1013 if entry != nil && !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) { 1014 return nil, logical.ErrReadOnly 1015 } 1016 1017 // Attempt remount 1018 if err := b.Core.remount(ctx, fromPath, toPath); err != nil { 1019 b.Backend.Logger().Error("remount failed", "from_path", fromPath, "to_path", toPath, "error", err) 1020 return handleError(err) 1021 } 1022 1023 return nil, nil 1024} 1025 1026// handleAuthTuneRead is used to get config settings on a auth path 1027func (b *SystemBackend) handleAuthTuneRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1028 path := data.Get("path").(string) 1029 if path == "" { 1030 return logical.ErrorResponse( 1031 "path must be specified as a string"), 1032 logical.ErrInvalidRequest 1033 } 1034 return b.handleTuneReadCommon(ctx, "auth/"+path) 1035} 1036 1037// handleMountTuneRead is used to get config settings on a backend 1038func (b *SystemBackend) handleMountTuneRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1039 path := data.Get("path").(string) 1040 if path == "" { 1041 return logical.ErrorResponse( 1042 "path must be specified as a string"), 1043 logical.ErrInvalidRequest 1044 } 1045 1046 // This call will read both logical backend's configuration as well as auth methods'. 1047 // Retaining this behavior for backward compatibility. If this behavior is not desired, 1048 // an error can be returned if path has a prefix of "auth/". 1049 return b.handleTuneReadCommon(ctx, path) 1050} 1051 1052// handleTuneReadCommon returns the config settings of a path 1053func (b *SystemBackend) handleTuneReadCommon(ctx context.Context, path string) (*logical.Response, error) { 1054 path = sanitizeMountPath(path) 1055 1056 sysView := b.Core.router.MatchingSystemView(ctx, path) 1057 if sysView == nil { 1058 b.Backend.Logger().Error("cannot fetch sysview", "path", path) 1059 return handleError(fmt.Errorf("cannot fetch sysview for path %q", path)) 1060 } 1061 1062 mountEntry := b.Core.router.MatchingMountEntry(ctx, path) 1063 if mountEntry == nil { 1064 b.Backend.Logger().Error("cannot fetch mount entry", "path", path) 1065 return handleError(fmt.Errorf("cannot fetch mount entry for path %q", path)) 1066 } 1067 1068 resp := &logical.Response{ 1069 Data: map[string]interface{}{ 1070 "default_lease_ttl": int(sysView.DefaultLeaseTTL().Seconds()), 1071 "max_lease_ttl": int(sysView.MaxLeaseTTL().Seconds()), 1072 "force_no_cache": mountEntry.Config.ForceNoCache, 1073 }, 1074 } 1075 1076 if mountEntry.Table == credentialTableType { 1077 resp.Data["token_type"] = mountEntry.Config.TokenType.String() 1078 } 1079 1080 if rawVal, ok := mountEntry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok { 1081 resp.Data["audit_non_hmac_request_keys"] = rawVal.([]string) 1082 } 1083 1084 if rawVal, ok := mountEntry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok { 1085 resp.Data["audit_non_hmac_response_keys"] = rawVal.([]string) 1086 } 1087 1088 if len(mountEntry.Config.ListingVisibility) > 0 { 1089 resp.Data["listing_visibility"] = mountEntry.Config.ListingVisibility 1090 } 1091 1092 if rawVal, ok := mountEntry.synthesizedConfigCache.Load("passthrough_request_headers"); ok { 1093 resp.Data["passthrough_request_headers"] = rawVal.([]string) 1094 } 1095 1096 if rawVal, ok := mountEntry.synthesizedConfigCache.Load("allowed_response_headers"); ok { 1097 resp.Data["allowed_response_headers"] = rawVal.([]string) 1098 } 1099 1100 if len(mountEntry.Options) > 0 { 1101 resp.Data["options"] = mountEntry.Options 1102 } 1103 1104 return resp, nil 1105} 1106 1107// handleAuthTuneWrite is used to set config settings on an auth path 1108func (b *SystemBackend) handleAuthTuneWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1109 path := data.Get("path").(string) 1110 if path == "" { 1111 return logical.ErrorResponse("missing path"), nil 1112 } 1113 1114 return b.handleTuneWriteCommon(ctx, "auth/"+path, data) 1115} 1116 1117// handleMountTuneWrite is used to set config settings on a backend 1118func (b *SystemBackend) handleMountTuneWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1119 path := data.Get("path").(string) 1120 if path == "" { 1121 return logical.ErrorResponse("missing path"), nil 1122 } 1123 1124 // This call will write both logical backend's configuration as well as auth methods'. 1125 // Retaining this behavior for backward compatibility. If this behavior is not desired, 1126 // an error can be returned if path has a prefix of "auth/". 1127 return b.handleTuneWriteCommon(ctx, path, data) 1128} 1129 1130// handleTuneWriteCommon is used to set config settings on a path 1131func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string, data *framework.FieldData) (*logical.Response, error) { 1132 repState := b.Core.ReplicationState() 1133 1134 path = sanitizeMountPath(path) 1135 1136 // Prevent protected paths from being changed 1137 for _, p := range untunableMounts { 1138 if strings.HasPrefix(path, p) { 1139 b.Backend.Logger().Error("cannot tune this mount", "path", path) 1140 return handleError(fmt.Errorf("cannot tune %q", path)) 1141 } 1142 } 1143 1144 mountEntry := b.Core.router.MatchingMountEntry(ctx, path) 1145 if mountEntry == nil { 1146 b.Backend.Logger().Error("tune failed", "error", "no mount entry found", "path", path) 1147 return handleError(fmt.Errorf("tune of path %q failed: no mount entry found", path)) 1148 } 1149 if mountEntry != nil && !mountEntry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) { 1150 return nil, logical.ErrReadOnly 1151 } 1152 1153 var lock *sync.RWMutex 1154 switch { 1155 case strings.HasPrefix(path, credentialRoutePrefix): 1156 lock = &b.Core.authLock 1157 default: 1158 lock = &b.Core.mountsLock 1159 } 1160 1161 lock.Lock() 1162 defer lock.Unlock() 1163 1164 // Check again after grabbing the lock 1165 mountEntry = b.Core.router.MatchingMountEntry(ctx, path) 1166 if mountEntry == nil { 1167 b.Backend.Logger().Error("tune failed", "error", "no mount entry found", "path", path) 1168 return handleError(fmt.Errorf("tune of path %q failed: no mount entry found", path)) 1169 } 1170 if mountEntry != nil && !mountEntry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) { 1171 return nil, logical.ErrReadOnly 1172 } 1173 1174 // Timing configuration parameters 1175 { 1176 var newDefault, newMax time.Duration 1177 defTTL := data.Get("default_lease_ttl").(string) 1178 switch defTTL { 1179 case "": 1180 newDefault = mountEntry.Config.DefaultLeaseTTL 1181 case "system": 1182 newDefault = time.Duration(0) 1183 default: 1184 tmpDef, err := parseutil.ParseDurationSecond(defTTL) 1185 if err != nil { 1186 return handleError(err) 1187 } 1188 newDefault = tmpDef 1189 } 1190 1191 maxTTL := data.Get("max_lease_ttl").(string) 1192 switch maxTTL { 1193 case "": 1194 newMax = mountEntry.Config.MaxLeaseTTL 1195 case "system": 1196 newMax = time.Duration(0) 1197 default: 1198 tmpMax, err := parseutil.ParseDurationSecond(maxTTL) 1199 if err != nil { 1200 return handleError(err) 1201 } 1202 newMax = tmpMax 1203 } 1204 1205 if newDefault != mountEntry.Config.DefaultLeaseTTL || 1206 newMax != mountEntry.Config.MaxLeaseTTL { 1207 1208 if err := b.tuneMountTTLs(ctx, path, mountEntry, newDefault, newMax); err != nil { 1209 b.Backend.Logger().Error("tuning failed", "path", path, "error", err) 1210 return handleError(err) 1211 } 1212 } 1213 } 1214 1215 if rawVal, ok := data.GetOk("description"); ok { 1216 description := rawVal.(string) 1217 1218 oldDesc := mountEntry.Description 1219 mountEntry.Description = description 1220 1221 // Update the mount table 1222 var err error 1223 switch { 1224 case strings.HasPrefix(path, "auth/"): 1225 err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local) 1226 default: 1227 err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local) 1228 } 1229 if err != nil { 1230 mountEntry.Description = oldDesc 1231 return handleError(err) 1232 } 1233 if b.Core.logger.IsInfo() { 1234 b.Core.logger.Info("mount tuning of description successful", "path", path, "description", description) 1235 } 1236 } 1237 1238 if rawVal, ok := data.GetOk("audit_non_hmac_request_keys"); ok { 1239 auditNonHMACRequestKeys := rawVal.([]string) 1240 1241 oldVal := mountEntry.Config.AuditNonHMACRequestKeys 1242 mountEntry.Config.AuditNonHMACRequestKeys = auditNonHMACRequestKeys 1243 1244 // Update the mount table 1245 var err error 1246 switch { 1247 case strings.HasPrefix(path, "auth/"): 1248 err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local) 1249 default: 1250 err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local) 1251 } 1252 if err != nil { 1253 mountEntry.Config.AuditNonHMACRequestKeys = oldVal 1254 return handleError(err) 1255 } 1256 1257 mountEntry.SyncCache() 1258 1259 if b.Core.logger.IsInfo() { 1260 b.Core.logger.Info("mount tuning of audit_non_hmac_request_keys successful", "path", path) 1261 } 1262 } 1263 1264 if rawVal, ok := data.GetOk("audit_non_hmac_response_keys"); ok { 1265 auditNonHMACResponseKeys := rawVal.([]string) 1266 1267 oldVal := mountEntry.Config.AuditNonHMACResponseKeys 1268 mountEntry.Config.AuditNonHMACResponseKeys = auditNonHMACResponseKeys 1269 1270 // Update the mount table 1271 var err error 1272 switch { 1273 case strings.HasPrefix(path, "auth/"): 1274 err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local) 1275 default: 1276 err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local) 1277 } 1278 if err != nil { 1279 mountEntry.Config.AuditNonHMACResponseKeys = oldVal 1280 return handleError(err) 1281 } 1282 1283 mountEntry.SyncCache() 1284 1285 if b.Core.logger.IsInfo() { 1286 b.Core.logger.Info("mount tuning of audit_non_hmac_response_keys successful", "path", path) 1287 } 1288 } 1289 1290 if rawVal, ok := data.GetOk("listing_visibility"); ok { 1291 lvString := rawVal.(string) 1292 listingVisibility := ListingVisibilityType(lvString) 1293 1294 if err := checkListingVisibility(listingVisibility); err != nil { 1295 return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", listingVisibility)), nil 1296 } 1297 1298 oldVal := mountEntry.Config.ListingVisibility 1299 mountEntry.Config.ListingVisibility = listingVisibility 1300 1301 // Update the mount table 1302 var err error 1303 switch { 1304 case strings.HasPrefix(path, "auth/"): 1305 err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local) 1306 default: 1307 err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local) 1308 } 1309 if err != nil { 1310 mountEntry.Config.ListingVisibility = oldVal 1311 return handleError(err) 1312 } 1313 1314 if b.Core.logger.IsInfo() { 1315 b.Core.logger.Info("mount tuning of listing_visibility successful", "path", path) 1316 } 1317 } 1318 1319 if rawVal, ok := data.GetOk("token_type"); ok { 1320 if !strings.HasPrefix(path, "auth/") { 1321 return logical.ErrorResponse(fmt.Sprintf("'token_type' can only be modified on auth mounts")), logical.ErrInvalidRequest 1322 } 1323 if mountEntry.Type == "token" || mountEntry.Type == "ns_token" { 1324 return logical.ErrorResponse(fmt.Sprintf("'token_type' cannot be set for 'token' or 'ns_token' auth mounts")), logical.ErrInvalidRequest 1325 } 1326 1327 tokenType := logical.TokenTypeDefaultService 1328 ttString := rawVal.(string) 1329 1330 switch ttString { 1331 case "", "default-service": 1332 case "default-batch": 1333 tokenType = logical.TokenTypeDefaultBatch 1334 case "service": 1335 tokenType = logical.TokenTypeService 1336 case "batch": 1337 tokenType = logical.TokenTypeBatch 1338 default: 1339 return logical.ErrorResponse(fmt.Sprintf( 1340 "invalid value for 'token_type'")), logical.ErrInvalidRequest 1341 } 1342 1343 oldVal := mountEntry.Config.TokenType 1344 mountEntry.Config.TokenType = tokenType 1345 1346 // Update the mount table 1347 if err := b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local); err != nil { 1348 mountEntry.Config.TokenType = oldVal 1349 return handleError(err) 1350 } 1351 1352 if b.Core.logger.IsInfo() { 1353 b.Core.logger.Info("mount tuning of token_type successful", "path", path, "token_type", ttString) 1354 } 1355 } 1356 1357 if rawVal, ok := data.GetOk("passthrough_request_headers"); ok { 1358 headers := rawVal.([]string) 1359 1360 oldVal := mountEntry.Config.PassthroughRequestHeaders 1361 mountEntry.Config.PassthroughRequestHeaders = headers 1362 1363 // Update the mount table 1364 var err error 1365 switch { 1366 case strings.HasPrefix(path, "auth/"): 1367 err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local) 1368 default: 1369 err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local) 1370 } 1371 if err != nil { 1372 mountEntry.Config.PassthroughRequestHeaders = oldVal 1373 return handleError(err) 1374 } 1375 1376 mountEntry.SyncCache() 1377 1378 if b.Core.logger.IsInfo() { 1379 b.Core.logger.Info("mount tuning of passthrough_request_headers successful", "path", path) 1380 } 1381 } 1382 1383 if rawVal, ok := data.GetOk("allowed_response_headers"); ok { 1384 headers := rawVal.([]string) 1385 1386 oldVal := mountEntry.Config.AllowedResponseHeaders 1387 mountEntry.Config.AllowedResponseHeaders = headers 1388 1389 // Update the mount table 1390 var err error 1391 switch { 1392 case strings.HasPrefix(path, "auth/"): 1393 err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local) 1394 default: 1395 err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local) 1396 } 1397 if err != nil { 1398 mountEntry.Config.AllowedResponseHeaders = oldVal 1399 return handleError(err) 1400 } 1401 1402 mountEntry.SyncCache() 1403 1404 if b.Core.logger.IsInfo() { 1405 b.Core.logger.Info("mount tuning of allowed_response_headers successful", "path", path) 1406 } 1407 } 1408 1409 var err error 1410 var resp *logical.Response 1411 var options map[string]string 1412 if optionsRaw, ok := data.GetOk("options"); ok { 1413 options = optionsRaw.(map[string]string) 1414 } 1415 1416 if len(options) > 0 { 1417 b.Core.logger.Info("mount tuning of options", "path", path, "options", options) 1418 newOptions := make(map[string]string) 1419 var kvUpgraded bool 1420 1421 // The version options should only apply to the KV mount, check that first 1422 if v, ok := options["version"]; ok { 1423 // Special case to make sure we can not disable versioning once it's 1424 // enabled. If the vkv backend suports downgrading this can be removed. 1425 meVersion, err := parseutil.ParseInt(mountEntry.Options["version"]) 1426 if err != nil { 1427 return nil, errwrap.Wrapf("unable to parse mount entry: {{err}}", err) 1428 } 1429 optVersion, err := parseutil.ParseInt(v) 1430 if err != nil { 1431 return handleError(errwrap.Wrapf("unable to parse options: {{err}}", err)) 1432 } 1433 1434 // Only accept valid versions 1435 switch optVersion { 1436 case 1: 1437 case 2: 1438 default: 1439 return logical.ErrorResponse(fmt.Sprintf("invalid version provided: %d", optVersion)), logical.ErrInvalidRequest 1440 } 1441 1442 if meVersion > optVersion { 1443 // Return early if version option asks for a downgrade 1444 return logical.ErrorResponse(fmt.Sprintf("cannot downgrade mount from version %d", meVersion)), logical.ErrInvalidRequest 1445 } 1446 if meVersion < optVersion { 1447 kvUpgraded = true 1448 resp = &logical.Response{} 1449 resp.AddWarning(fmt.Sprintf("Upgrading mount from version %d to version %d. This mount will be unavailable for a brief period and will resume service shortly.", meVersion, optVersion)) 1450 } 1451 } 1452 1453 // Upsert options value to a copy of the existing mountEntry's options 1454 for k, v := range mountEntry.Options { 1455 newOptions[k] = v 1456 } 1457 for k, v := range options { 1458 // If the value of the provided option is empty, delete the key We 1459 // special-case the version value here to guard against KV downgrades, but 1460 // this piece could potentially be refactored in the future to be non-KV 1461 // specific. 1462 if len(v) == 0 && k != "version" { 1463 delete(newOptions, k) 1464 } else { 1465 newOptions[k] = v 1466 } 1467 } 1468 1469 // Update the mount table 1470 oldVal := mountEntry.Options 1471 mountEntry.Options = newOptions 1472 switch { 1473 case strings.HasPrefix(path, "auth/"): 1474 err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local) 1475 default: 1476 err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local) 1477 } 1478 if err != nil { 1479 mountEntry.Options = oldVal 1480 return handleError(err) 1481 } 1482 1483 // Reload the backend to kick off the upgrade process. It should only apply to KV backend so we 1484 // trigger based on the version logic above. 1485 if kvUpgraded { 1486 err = b.Core.reloadBackendCommon(ctx, mountEntry, strings.HasPrefix(path, credentialRoutePrefix)) 1487 if err != nil { 1488 b.Core.logger.Error("mount tuning of options: could not reload backend", "error", err, "path", path, "options", options) 1489 } 1490 1491 } 1492 } 1493 1494 return resp, nil 1495} 1496 1497// handleLease is use to view the metadata for a given LeaseID 1498func (b *SystemBackend) handleLeaseLookup(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1499 leaseID := data.Get("lease_id").(string) 1500 if leaseID == "" { 1501 return logical.ErrorResponse("lease_id must be specified"), 1502 logical.ErrInvalidRequest 1503 } 1504 1505 leaseTimes, err := b.Core.expiration.FetchLeaseTimes(ctx, leaseID) 1506 if err != nil { 1507 b.Backend.Logger().Error("error retrieving lease", "lease_id", leaseID, "error", err) 1508 return handleError(err) 1509 } 1510 if leaseTimes == nil { 1511 return logical.ErrorResponse("invalid lease"), logical.ErrInvalidRequest 1512 } 1513 1514 resp := &logical.Response{ 1515 Data: map[string]interface{}{ 1516 "id": leaseID, 1517 "issue_time": leaseTimes.IssueTime, 1518 "expire_time": nil, 1519 "last_renewal": nil, 1520 "ttl": int64(0), 1521 }, 1522 } 1523 renewable, _ := leaseTimes.renewable() 1524 resp.Data["renewable"] = renewable 1525 1526 if !leaseTimes.LastRenewalTime.IsZero() { 1527 resp.Data["last_renewal"] = leaseTimes.LastRenewalTime 1528 } 1529 if !leaseTimes.ExpireTime.IsZero() { 1530 resp.Data["expire_time"] = leaseTimes.ExpireTime 1531 resp.Data["ttl"] = leaseTimes.ttl() 1532 } 1533 return resp, nil 1534} 1535 1536func (b *SystemBackend) handleLeaseLookupList(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1537 prefix := data.Get("prefix").(string) 1538 if prefix != "" && !strings.HasSuffix(prefix, "/") { 1539 prefix = prefix + "/" 1540 } 1541 1542 ns, err := namespace.FromContext(ctx) 1543 if err != nil { 1544 return nil, err 1545 } 1546 view := b.Core.expiration.leaseView(ns) 1547 keys, err := view.List(ctx, prefix) 1548 if err != nil { 1549 b.Backend.Logger().Error("error listing leases", "prefix", prefix, "error", err) 1550 return handleErrorNoReadOnlyForward(err) 1551 } 1552 return logical.ListResponse(keys), nil 1553} 1554 1555// handleRenew is used to renew a lease with a given LeaseID 1556func (b *SystemBackend) handleRenew(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1557 // Get all the options 1558 leaseID := data.Get("lease_id").(string) 1559 if leaseID == "" { 1560 leaseID = data.Get("url_lease_id").(string) 1561 } 1562 if leaseID == "" { 1563 return logical.ErrorResponse("lease_id must be specified"), 1564 logical.ErrInvalidRequest 1565 } 1566 incrementRaw := data.Get("increment").(int) 1567 1568 // Convert the increment 1569 increment := time.Duration(incrementRaw) * time.Second 1570 1571 // Invoke the expiration manager directly 1572 resp, err := b.Core.expiration.Renew(ctx, leaseID, increment) 1573 if err != nil { 1574 b.Backend.Logger().Error("lease renewal failed", "lease_id", leaseID, "error", err) 1575 return handleErrorNoReadOnlyForward(err) 1576 } 1577 return resp, err 1578} 1579 1580// handleRevoke is used to revoke a given LeaseID 1581func (b *SystemBackend) handleRevoke(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1582 // Get all the options 1583 leaseID := data.Get("lease_id").(string) 1584 if leaseID == "" { 1585 leaseID = data.Get("url_lease_id").(string) 1586 } 1587 if leaseID == "" { 1588 return logical.ErrorResponse("lease_id must be specified"), 1589 logical.ErrInvalidRequest 1590 } 1591 1592 ns, err := namespace.FromContext(ctx) 1593 if err != nil { 1594 return nil, err 1595 } 1596 revokeCtx := namespace.ContextWithNamespace(b.Core.activeContext, ns) 1597 if data.Get("sync").(bool) { 1598 // Invoke the expiration manager directly 1599 if err := b.Core.expiration.Revoke(revokeCtx, leaseID); err != nil { 1600 b.Backend.Logger().Error("lease revocation failed", "lease_id", leaseID, "error", err) 1601 return handleErrorNoReadOnlyForward(err) 1602 } 1603 1604 return nil, nil 1605 } 1606 1607 if err := b.Core.expiration.LazyRevoke(revokeCtx, leaseID); err != nil { 1608 b.Backend.Logger().Error("lease revocation failed", "lease_id", leaseID, "error", err) 1609 return handleErrorNoReadOnlyForward(err) 1610 } 1611 1612 return logical.RespondWithStatusCode(nil, nil, http.StatusAccepted) 1613} 1614 1615// handleRevokePrefix is used to revoke a prefix with many LeaseIDs 1616func (b *SystemBackend) handleRevokePrefix(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1617 return b.handleRevokePrefixCommon(ctx, req, data, false, data.Get("sync").(bool)) 1618} 1619 1620// handleRevokeForce is used to revoke a prefix with many LeaseIDs, ignoring errors 1621func (b *SystemBackend) handleRevokeForce(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1622 return b.handleRevokePrefixCommon(ctx, req, data, true, true) 1623} 1624 1625// handleRevokePrefixCommon is used to revoke a prefix with many LeaseIDs 1626func (b *SystemBackend) handleRevokePrefixCommon(ctx context.Context, 1627 req *logical.Request, data *framework.FieldData, force, sync bool) (*logical.Response, error) { 1628 // Get all the options 1629 prefix := data.Get("prefix").(string) 1630 1631 ns, err := namespace.FromContext(ctx) 1632 if err != nil { 1633 return nil, err 1634 } 1635 1636 // Invoke the expiration manager directly 1637 revokeCtx := namespace.ContextWithNamespace(b.Core.activeContext, ns) 1638 if force { 1639 err = b.Core.expiration.RevokeForce(revokeCtx, prefix) 1640 } else { 1641 err = b.Core.expiration.RevokePrefix(revokeCtx, prefix, sync) 1642 } 1643 if err != nil { 1644 b.Backend.Logger().Error("revoke prefix failed", "prefix", prefix, "error", err) 1645 return handleErrorNoReadOnlyForward(err) 1646 } 1647 1648 if sync { 1649 return nil, nil 1650 } 1651 1652 return logical.RespondWithStatusCode(nil, nil, http.StatusAccepted) 1653} 1654 1655// handleAuthTable handles the "auth" endpoint to provide the auth table 1656func (b *SystemBackend) handleAuthTable(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1657 ns, err := namespace.FromContext(ctx) 1658 if err != nil { 1659 return nil, err 1660 } 1661 1662 b.Core.authLock.RLock() 1663 defer b.Core.authLock.RUnlock() 1664 1665 resp := &logical.Response{ 1666 Data: make(map[string]interface{}), 1667 } 1668 1669 for _, entry := range b.Core.auth.Entries { 1670 // Only show entries for current namespace 1671 if entry.Namespace().Path != ns.Path { 1672 continue 1673 } 1674 1675 cont, err := b.Core.checkReplicatedFiltering(ctx, entry, credentialRoutePrefix) 1676 if err != nil { 1677 return nil, err 1678 } 1679 if cont { 1680 continue 1681 } 1682 1683 info := mountInfo(entry) 1684 resp.Data[entry.Path] = info 1685 } 1686 1687 return resp, nil 1688} 1689 1690// handleEnableAuth is used to enable a new credential backend 1691func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1692 repState := b.Core.ReplicationState() 1693 local := data.Get("local").(bool) 1694 1695 // If we are a performance secondary cluster we should forward the request 1696 // to the primary. We fail early here since the view in use isn't marked as 1697 // readonly 1698 if !local && repState.HasState(consts.ReplicationPerformanceSecondary) { 1699 return nil, logical.ErrReadOnly 1700 } 1701 1702 // Get all the options 1703 path := data.Get("path").(string) 1704 path = sanitizeMountPath(path) 1705 logicalType := data.Get("type").(string) 1706 description := data.Get("description").(string) 1707 pluginName := data.Get("plugin_name").(string) 1708 sealWrap := data.Get("seal_wrap").(bool) 1709 options := data.Get("options").(map[string]string) 1710 1711 var config MountConfig 1712 var apiConfig APIMountConfig 1713 1714 configMap := data.Get("config").(map[string]interface{}) 1715 if configMap != nil && len(configMap) != 0 { 1716 err := mapstructure.Decode(configMap, &apiConfig) 1717 if err != nil { 1718 return logical.ErrorResponse( 1719 "unable to convert given auth config information"), 1720 logical.ErrInvalidRequest 1721 } 1722 } 1723 1724 switch apiConfig.DefaultLeaseTTL { 1725 case "": 1726 case "system": 1727 default: 1728 tmpDef, err := parseutil.ParseDurationSecond(apiConfig.DefaultLeaseTTL) 1729 if err != nil { 1730 return logical.ErrorResponse(fmt.Sprintf( 1731 "unable to parse default TTL of %s: %s", apiConfig.DefaultLeaseTTL, err)), 1732 logical.ErrInvalidRequest 1733 } 1734 config.DefaultLeaseTTL = tmpDef 1735 } 1736 1737 switch apiConfig.MaxLeaseTTL { 1738 case "": 1739 case "system": 1740 default: 1741 tmpMax, err := parseutil.ParseDurationSecond(apiConfig.MaxLeaseTTL) 1742 if err != nil { 1743 return logical.ErrorResponse(fmt.Sprintf( 1744 "unable to parse max TTL of %s: %s", apiConfig.MaxLeaseTTL, err)), 1745 logical.ErrInvalidRequest 1746 } 1747 config.MaxLeaseTTL = tmpMax 1748 } 1749 1750 if config.MaxLeaseTTL != 0 && config.DefaultLeaseTTL > config.MaxLeaseTTL { 1751 return logical.ErrorResponse( 1752 "given default lease TTL greater than given max lease TTL"), 1753 logical.ErrInvalidRequest 1754 } 1755 1756 if config.DefaultLeaseTTL > b.Core.maxLeaseTTL && config.MaxLeaseTTL == 0 { 1757 return logical.ErrorResponse(fmt.Sprintf( 1758 "given default lease TTL greater than system max lease TTL of %d", int(b.Core.maxLeaseTTL.Seconds()))), 1759 logical.ErrInvalidRequest 1760 } 1761 1762 switch apiConfig.TokenType { 1763 case "", "default-service": 1764 config.TokenType = logical.TokenTypeDefaultService 1765 case "default-batch": 1766 config.TokenType = logical.TokenTypeDefaultBatch 1767 case "service": 1768 config.TokenType = logical.TokenTypeService 1769 case "batch": 1770 config.TokenType = logical.TokenTypeBatch 1771 default: 1772 return logical.ErrorResponse(fmt.Sprintf( 1773 "invalid value for 'token_type'")), logical.ErrInvalidRequest 1774 } 1775 1776 switch logicalType { 1777 case "": 1778 return logical.ErrorResponse( 1779 "backend type must be specified as a string"), 1780 logical.ErrInvalidRequest 1781 case "plugin": 1782 // Only set plugin name if mount is of type plugin, with apiConfig.PluginName 1783 // option taking precedence. 1784 switch { 1785 case apiConfig.PluginName != "": 1786 logicalType = apiConfig.PluginName 1787 case pluginName != "": 1788 logicalType = pluginName 1789 default: 1790 return logical.ErrorResponse( 1791 "plugin_name must be provided for plugin backend"), 1792 logical.ErrInvalidRequest 1793 } 1794 } 1795 1796 if options != nil && options["version"] != "" { 1797 return logical.ErrorResponse(fmt.Sprintf( 1798 "auth method %q does not allow setting a version", logicalType)), 1799 logical.ErrInvalidRequest 1800 } 1801 1802 if err := checkListingVisibility(apiConfig.ListingVisibility); err != nil { 1803 return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", apiConfig.ListingVisibility)), nil 1804 } 1805 config.ListingVisibility = apiConfig.ListingVisibility 1806 1807 if len(apiConfig.AuditNonHMACRequestKeys) > 0 { 1808 config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys 1809 } 1810 if len(apiConfig.AuditNonHMACResponseKeys) > 0 { 1811 config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys 1812 } 1813 if len(apiConfig.PassthroughRequestHeaders) > 0 { 1814 config.PassthroughRequestHeaders = apiConfig.PassthroughRequestHeaders 1815 } 1816 if len(apiConfig.AllowedResponseHeaders) > 0 { 1817 config.AllowedResponseHeaders = apiConfig.AllowedResponseHeaders 1818 } 1819 1820 // Create the mount entry 1821 me := &MountEntry{ 1822 Table: credentialTableType, 1823 Path: path, 1824 Type: logicalType, 1825 Description: description, 1826 Config: config, 1827 Local: local, 1828 SealWrap: sealWrap, 1829 Options: options, 1830 } 1831 1832 // Attempt enabling 1833 if err := b.Core.enableCredential(ctx, me); err != nil { 1834 b.Backend.Logger().Error("enable auth mount failed", "path", me.Path, "error", err) 1835 return handleError(err) 1836 } 1837 return nil, nil 1838} 1839 1840// handleDisableAuth is used to disable a credential backend 1841func (b *SystemBackend) handleDisableAuth(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1842 path := data.Get("path").(string) 1843 path = sanitizeMountPath(path) 1844 1845 ns, err := namespace.FromContext(ctx) 1846 if err != nil { 1847 return nil, err 1848 } 1849 fullPath := credentialRoutePrefix + path 1850 1851 repState := b.Core.ReplicationState() 1852 entry := b.Core.router.MatchingMountEntry(ctx, fullPath) 1853 1854 // If we are a performance secondary cluster we should forward the request 1855 // to the primary. We fail early here since the view in use isn't marked as 1856 // readonly 1857 if entry != nil && !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) { 1858 return nil, logical.ErrReadOnly 1859 } 1860 1861 // We return success when the mount does not exists to not expose if the 1862 // mount existed or not 1863 match := b.Core.router.MatchingMount(ctx, fullPath) 1864 if match == "" || ns.Path+fullPath != match { 1865 return nil, nil 1866 } 1867 1868 prefix, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, fullPath) 1869 if !found { 1870 b.Backend.Logger().Error("unable to find storage for path", "path", fullPath) 1871 return handleError(fmt.Errorf("unable to find storage for path: %q", fullPath)) 1872 } 1873 1874 // Attempt disable 1875 if err := b.Core.disableCredential(ctx, path); err != nil { 1876 b.Backend.Logger().Error("disable auth mount failed", "path", path, "error", err) 1877 return handleError(err) 1878 } 1879 1880 // Remove from filtered mounts 1881 if err := b.Core.removePrefixFromFilteredPaths(ctx, prefix); err != nil { 1882 b.Backend.Logger().Error("filtered path removal failed", path, "error", err) 1883 return handleError(err) 1884 } 1885 1886 return nil, nil 1887} 1888 1889// handlePoliciesList handles /sys/policy/ and /sys/policies/<type> endpoints to provide the enabled policies 1890func (b *SystemBackend) handlePoliciesList(policyType PolicyType) framework.OperationFunc { 1891 return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1892 ns, err := namespace.FromContext(ctx) 1893 if err != nil { 1894 return nil, err 1895 } 1896 policies, err := b.Core.policyStore.ListPolicies(ctx, policyType) 1897 if err != nil { 1898 return nil, err 1899 } 1900 1901 switch policyType { 1902 case PolicyTypeACL: 1903 // Add the special "root" policy if not egp and we are at the root namespace 1904 if ns.ID == namespace.RootNamespaceID { 1905 policies = append(policies, "root") 1906 } 1907 resp := logical.ListResponse(policies) 1908 1909 // If the request is from sys/policy/ we handle backwards compatibility 1910 if strings.HasPrefix(req.Path, "policy") { 1911 resp.Data["policies"] = resp.Data["keys"] 1912 } 1913 return resp, nil 1914 1915 case PolicyTypeRGP: 1916 return logical.ListResponse(policies), nil 1917 1918 case PolicyTypeEGP: 1919 nsScopedKeyInfo := getEGPListResponseKeyInfo(b, ns) 1920 return &logical.Response{ 1921 Data: map[string]interface{}{ 1922 "keys": policies, 1923 "key_info": nsScopedKeyInfo, 1924 }, 1925 }, nil 1926 } 1927 1928 return logical.ErrorResponse("unknown policy type"), nil 1929 } 1930} 1931 1932// handlePoliciesRead handles the "/sys/policy/<name>" and "/sys/policies/<type>/<name>" endpoints to read a policy 1933func (b *SystemBackend) handlePoliciesRead(policyType PolicyType) framework.OperationFunc { 1934 return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1935 name := data.Get("name").(string) 1936 1937 policy, err := b.Core.policyStore.GetPolicy(ctx, name, policyType) 1938 if err != nil { 1939 return handleError(err) 1940 } 1941 1942 if policy == nil { 1943 return nil, nil 1944 } 1945 1946 // If the request is from sys/policy/ we handle backwards compatibility 1947 var respDataPolicyName string 1948 if policyType == PolicyTypeACL && strings.HasPrefix(req.Path, "policy") { 1949 respDataPolicyName = "rules" 1950 } else { 1951 respDataPolicyName = "policy" 1952 } 1953 1954 resp := &logical.Response{ 1955 Data: map[string]interface{}{ 1956 "name": policy.Name, 1957 respDataPolicyName: policy.Raw, 1958 }, 1959 } 1960 1961 switch policy.Type { 1962 case PolicyTypeRGP, PolicyTypeEGP: 1963 addSentinelPolicyData(resp.Data, policy) 1964 } 1965 1966 return resp, nil 1967 } 1968} 1969 1970// handlePoliciesSet handles the "/sys/policy/<name>" and "/sys/policies/<type>/<name>" endpoints to set a policy 1971func (b *SystemBackend) handlePoliciesSet(policyType PolicyType) framework.OperationFunc { 1972 return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 1973 var resp *logical.Response 1974 1975 ns, err := namespace.FromContext(ctx) 1976 if err != nil { 1977 return nil, err 1978 } 1979 1980 policy := &Policy{ 1981 Name: strings.ToLower(data.Get("name").(string)), 1982 Type: policyType, 1983 namespace: ns, 1984 } 1985 if policy.Name == "" { 1986 return logical.ErrorResponse("policy name must be provided in the URL"), nil 1987 } 1988 1989 policy.Raw = data.Get("policy").(string) 1990 if policy.Raw == "" && policyType == PolicyTypeACL && strings.HasPrefix(req.Path, "policy") { 1991 policy.Raw = data.Get("rules").(string) 1992 if resp == nil { 1993 resp = &logical.Response{} 1994 } 1995 resp.AddWarning("'rules' is deprecated, please use 'policy' instead") 1996 } 1997 if policy.Raw == "" { 1998 return logical.ErrorResponse("'policy' parameter not supplied or empty"), nil 1999 } 2000 2001 if polBytes, err := base64.StdEncoding.DecodeString(policy.Raw); err == nil { 2002 policy.Raw = string(polBytes) 2003 } 2004 2005 switch policyType { 2006 case PolicyTypeACL: 2007 p, err := ParseACLPolicy(ns, policy.Raw) 2008 if err != nil { 2009 return handleError(err) 2010 } 2011 policy.Paths = p.Paths 2012 policy.Templated = p.Templated 2013 2014 case PolicyTypeRGP, PolicyTypeEGP: 2015 2016 default: 2017 return logical.ErrorResponse("unknown policy type"), nil 2018 } 2019 2020 if policy.Type == PolicyTypeRGP || policy.Type == PolicyTypeEGP { 2021 if errResp := inputSentinelPolicyData(data, policy); errResp != nil { 2022 return errResp, nil 2023 } 2024 } 2025 2026 // Update the policy 2027 if err := b.Core.policyStore.SetPolicy(ctx, policy); err != nil { 2028 return handleError(err) 2029 } 2030 return resp, nil 2031 } 2032} 2033 2034func (b *SystemBackend) handlePoliciesDelete(policyType PolicyType) framework.OperationFunc { 2035 return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2036 name := data.Get("name").(string) 2037 2038 if err := b.Core.policyStore.DeletePolicy(ctx, name, policyType); err != nil { 2039 return handleError(err) 2040 } 2041 return nil, nil 2042 } 2043} 2044 2045// handleAuditTable handles the "audit" endpoint to provide the audit table 2046func (b *SystemBackend) handleAuditTable(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2047 b.Core.auditLock.RLock() 2048 defer b.Core.auditLock.RUnlock() 2049 2050 resp := &logical.Response{ 2051 Data: make(map[string]interface{}), 2052 } 2053 for _, entry := range b.Core.audit.Entries { 2054 info := map[string]interface{}{ 2055 "path": entry.Path, 2056 "type": entry.Type, 2057 "description": entry.Description, 2058 "options": entry.Options, 2059 "local": entry.Local, 2060 } 2061 resp.Data[entry.Path] = info 2062 } 2063 return resp, nil 2064} 2065 2066// handleAuditHash is used to fetch the hash of the given input data with the 2067// specified audit backend's salt 2068func (b *SystemBackend) handleAuditHash(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2069 path := data.Get("path").(string) 2070 input := data.Get("input").(string) 2071 if input == "" { 2072 return logical.ErrorResponse("the \"input\" parameter is empty"), nil 2073 } 2074 2075 path = sanitizeMountPath(path) 2076 2077 hash, err := b.Core.auditBroker.GetHash(ctx, path, input) 2078 if err != nil { 2079 return logical.ErrorResponse(err.Error()), nil 2080 } 2081 2082 return &logical.Response{ 2083 Data: map[string]interface{}{ 2084 "hash": hash, 2085 }, 2086 }, nil 2087} 2088 2089// handleEnableAudit is used to enable a new audit backend 2090func (b *SystemBackend) handleEnableAudit(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2091 repState := b.Core.ReplicationState() 2092 2093 local := data.Get("local").(bool) 2094 // If we are a performance secondary cluster we should forward the request 2095 // to the primary. We fail early here since the view in use isn't marked as 2096 // readonly 2097 if !local && repState.HasState(consts.ReplicationPerformanceSecondary) { 2098 return nil, logical.ErrReadOnly 2099 } 2100 2101 // Get all the options 2102 path := data.Get("path").(string) 2103 backendType := data.Get("type").(string) 2104 description := data.Get("description").(string) 2105 options := data.Get("options").(map[string]string) 2106 2107 // Create the mount entry 2108 me := &MountEntry{ 2109 Table: auditTableType, 2110 Path: path, 2111 Type: backendType, 2112 Description: description, 2113 Options: options, 2114 Local: local, 2115 } 2116 2117 // Attempt enabling 2118 if err := b.Core.enableAudit(ctx, me, true); err != nil { 2119 b.Backend.Logger().Error("enable audit mount failed", "path", me.Path, "error", err) 2120 return handleError(err) 2121 } 2122 return nil, nil 2123} 2124 2125// handleDisableAudit is used to disable an audit backend 2126func (b *SystemBackend) handleDisableAudit(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2127 path := data.Get("path").(string) 2128 2129 if !strings.HasSuffix(path, "/") { 2130 path += "/" 2131 } 2132 2133 if path == "/" { 2134 return handleError(errors.New("audit device path must be specified")) 2135 } 2136 2137 b.Core.auditLock.RLock() 2138 table := b.Core.audit.shallowClone() 2139 entry, err := table.find(ctx, path) 2140 b.Core.auditLock.RUnlock() 2141 2142 if err != nil { 2143 return handleError(err) 2144 } 2145 if entry == nil { 2146 return nil, nil 2147 } 2148 2149 repState := b.Core.ReplicationState() 2150 2151 // If we are a performance secondary cluster we should forward the request 2152 // to the primary. We fail early here since the view in use isn't marked as 2153 // readonly 2154 if !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) { 2155 return nil, logical.ErrReadOnly 2156 } 2157 2158 // Attempt disable 2159 if existed, err := b.Core.disableAudit(ctx, path, true); existed && err != nil { 2160 b.Backend.Logger().Error("disable audit mount failed", "path", path, "error", err) 2161 return handleError(err) 2162 } 2163 return nil, nil 2164} 2165 2166func (b *SystemBackend) handleConfigUIHeadersRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2167 header := data.Get("header").(string) 2168 2169 value, err := b.Core.uiConfig.GetHeader(ctx, header) 2170 if err != nil { 2171 return nil, err 2172 } 2173 if value == "" { 2174 return nil, nil 2175 } 2176 2177 return &logical.Response{ 2178 Data: map[string]interface{}{ 2179 "value": value, 2180 }, 2181 }, nil 2182} 2183 2184func (b *SystemBackend) handleConfigUIHeadersList(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2185 headers, err := b.Core.uiConfig.HeaderKeys(ctx) 2186 if err != nil { 2187 return nil, err 2188 } 2189 if len(headers) == 0 { 2190 return nil, nil 2191 } 2192 2193 return logical.ListResponse(headers), nil 2194} 2195 2196func (b *SystemBackend) handleConfigUIHeadersUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2197 header := data.Get("header").(string) 2198 values := data.Get("values").([]string) 2199 if header == "" || len(values) == 0 { 2200 return logical.ErrorResponse("header and values must be specified"), logical.ErrInvalidRequest 2201 } 2202 2203 lowerHeader := strings.ToLower(header) 2204 if strings.HasPrefix(lowerHeader, "x-vault-") { 2205 return logical.ErrorResponse("X-Vault headers cannot be set"), logical.ErrInvalidRequest 2206 } 2207 2208 // Translate the list of values to the valid header string 2209 value := http.Header{} 2210 for _, v := range values { 2211 value.Add(header, v) 2212 } 2213 err := b.Core.uiConfig.SetHeader(ctx, header, value.Get(header)) 2214 if err != nil { 2215 return nil, err 2216 } 2217 2218 // Warn when overriding the CSP 2219 resp := &logical.Response{} 2220 if lowerHeader == "content-security-policy" { 2221 resp.AddWarning("overriding default Content-Security-Policy which is secure by default, proceed with caution") 2222 } 2223 2224 return resp, nil 2225} 2226 2227func (b *SystemBackend) handleConfigUIHeadersDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2228 header := data.Get("header").(string) 2229 err := b.Core.uiConfig.DeleteHeader(ctx, header) 2230 if err != nil { 2231 return nil, err 2232 } 2233 return nil, nil 2234} 2235 2236// handleRawRead is used to read directly from the barrier 2237func (b *SystemBackend) handleRawRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2238 path := data.Get("path").(string) 2239 2240 // Prevent access of protected paths 2241 for _, p := range protectedPaths { 2242 if strings.HasPrefix(path, p) { 2243 err := fmt.Sprintf("cannot read '%s'", path) 2244 return logical.ErrorResponse(err), logical.ErrInvalidRequest 2245 } 2246 } 2247 2248 // Run additional checks if needed 2249 if err := checkRaw(b, path); err != nil { 2250 b.Core.logger.Warn(err.Error(), "path", path) 2251 return logical.ErrorResponse("cannot read '%s'", path), logical.ErrInvalidRequest 2252 } 2253 2254 entry, err := b.Core.barrier.Get(ctx, path) 2255 if err != nil { 2256 return handleErrorNoReadOnlyForward(err) 2257 } 2258 if entry == nil { 2259 return nil, nil 2260 } 2261 2262 // Run this through the decompression helper to see if it's been compressed. 2263 // If the input contained the compression canary, `outputBytes` will hold 2264 // the decompressed data. If the input was not compressed, then `outputBytes` 2265 // will be nil. 2266 outputBytes, _, err := compressutil.Decompress(entry.Value) 2267 if err != nil { 2268 return handleErrorNoReadOnlyForward(err) 2269 } 2270 2271 // `outputBytes` is nil if the input is uncompressed. In that case set it to the original input. 2272 if outputBytes == nil { 2273 outputBytes = entry.Value 2274 } 2275 2276 resp := &logical.Response{ 2277 Data: map[string]interface{}{ 2278 "value": string(outputBytes), 2279 }, 2280 } 2281 return resp, nil 2282} 2283 2284// handleRawWrite is used to write directly to the barrier 2285func (b *SystemBackend) handleRawWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2286 path := data.Get("path").(string) 2287 2288 // Prevent access of protected paths 2289 for _, p := range protectedPaths { 2290 if strings.HasPrefix(path, p) { 2291 err := fmt.Sprintf("cannot write '%s'", path) 2292 return logical.ErrorResponse(err), logical.ErrInvalidRequest 2293 } 2294 } 2295 2296 value := data.Get("value").(string) 2297 entry := &logical.StorageEntry{ 2298 Key: path, 2299 Value: []byte(value), 2300 } 2301 if err := b.Core.barrier.Put(ctx, entry); err != nil { 2302 return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest 2303 } 2304 return nil, nil 2305} 2306 2307// handleRawDelete is used to delete directly from the barrier 2308func (b *SystemBackend) handleRawDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2309 path := data.Get("path").(string) 2310 2311 // Prevent access of protected paths 2312 for _, p := range protectedPaths { 2313 if strings.HasPrefix(path, p) { 2314 err := fmt.Sprintf("cannot delete '%s'", path) 2315 return logical.ErrorResponse(err), logical.ErrInvalidRequest 2316 } 2317 } 2318 2319 if err := b.Core.barrier.Delete(ctx, path); err != nil { 2320 return handleErrorNoReadOnlyForward(err) 2321 } 2322 return nil, nil 2323} 2324 2325// handleRawList is used to list directly from the barrier 2326func (b *SystemBackend) handleRawList(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2327 path := data.Get("path").(string) 2328 if path != "" && !strings.HasSuffix(path, "/") { 2329 path = path + "/" 2330 } 2331 2332 // Prevent access of protected paths 2333 for _, p := range protectedPaths { 2334 if strings.HasPrefix(path, p) { 2335 err := fmt.Sprintf("cannot list '%s'", path) 2336 return logical.ErrorResponse(err), logical.ErrInvalidRequest 2337 } 2338 } 2339 2340 // Run additional checks if needed 2341 if err := checkRaw(b, path); err != nil { 2342 b.Core.logger.Warn(err.Error(), "path", path) 2343 return logical.ErrorResponse("cannot list '%s'", path), logical.ErrInvalidRequest 2344 } 2345 2346 keys, err := b.Core.barrier.List(ctx, path) 2347 if err != nil { 2348 return handleErrorNoReadOnlyForward(err) 2349 } 2350 return logical.ListResponse(keys), nil 2351} 2352 2353// handleKeyStatus returns status information about the backend key 2354func (b *SystemBackend) handleKeyStatus(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2355 // Get the key info 2356 info, err := b.Core.barrier.ActiveKeyInfo() 2357 if err != nil { 2358 return nil, err 2359 } 2360 2361 resp := &logical.Response{ 2362 Data: map[string]interface{}{ 2363 "term": info.Term, 2364 "install_time": info.InstallTime.Format(time.RFC3339Nano), 2365 }, 2366 } 2367 return resp, nil 2368} 2369 2370// handleRotate is used to trigger a key rotation 2371func (b *SystemBackend) handleRotate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2372 repState := b.Core.ReplicationState() 2373 if repState.HasState(consts.ReplicationPerformanceSecondary) { 2374 return logical.ErrorResponse("cannot rotate on a replication secondary"), nil 2375 } 2376 2377 // Rotate to the new term 2378 newTerm, err := b.Core.barrier.Rotate(ctx) 2379 if err != nil { 2380 b.Backend.Logger().Error("failed to create new encryption key", "error", err) 2381 return handleError(err) 2382 } 2383 b.Backend.Logger().Info("installed new encryption key") 2384 2385 // In HA mode, we need to an upgrade path for the standby instances 2386 if b.Core.ha != nil { 2387 // Create the upgrade path to the new term 2388 if err := b.Core.barrier.CreateUpgrade(ctx, newTerm); err != nil { 2389 b.Backend.Logger().Error("failed to create new upgrade", "term", newTerm, "error", err) 2390 } 2391 2392 // Schedule the destroy of the upgrade path 2393 time.AfterFunc(KeyRotateGracePeriod, func() { 2394 b.Backend.Logger().Debug("cleaning up upgrade keys", "waited", KeyRotateGracePeriod) 2395 if err := b.Core.barrier.DestroyUpgrade(b.Core.activeContext, newTerm); err != nil { 2396 b.Backend.Logger().Error("failed to destroy upgrade", "term", newTerm, "error", err) 2397 } 2398 }) 2399 } 2400 2401 // Write to the canary path, which will force a synchronous truing during 2402 // replication 2403 if err := b.Core.barrier.Put(ctx, &logical.StorageEntry{ 2404 Key: coreKeyringCanaryPath, 2405 Value: []byte(fmt.Sprintf("new-rotation-term-%d", newTerm)), 2406 }); err != nil { 2407 b.Core.logger.Error("error saving keyring canary", "error", err) 2408 return nil, errwrap.Wrapf("failed to save keyring canary: {{err}}", err) 2409 } 2410 2411 return nil, nil 2412} 2413 2414func (b *SystemBackend) handleWrappingPubkey(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2415 x, _ := b.Core.wrappingJWTKey.X.MarshalText() 2416 y, _ := b.Core.wrappingJWTKey.Y.MarshalText() 2417 return &logical.Response{ 2418 Data: map[string]interface{}{ 2419 "jwt_x": string(x), 2420 "jwt_y": string(y), 2421 "jwt_curve": corePrivateKeyTypeP521, 2422 }, 2423 }, nil 2424} 2425 2426func (b *SystemBackend) handleWrappingWrap(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2427 if req.WrapInfo == nil || req.WrapInfo.TTL == 0 { 2428 return logical.ErrorResponse("endpoint requires response wrapping to be used"), logical.ErrInvalidRequest 2429 } 2430 2431 // N.B.: Do *NOT* allow JWT wrapping tokens to be created through this 2432 // endpoint. JWTs are signed so if we don't allow users to create wrapping 2433 // tokens using them we can ensure that an operator can't spoof a legit JWT 2434 // wrapped token, which makes certain init/rekey/generate-root cases have 2435 // better properties. 2436 req.WrapInfo.Format = "uuid" 2437 2438 return &logical.Response{ 2439 Data: data.Raw, 2440 }, nil 2441} 2442 2443// handleWrappingUnwrap will unwrap a response wrapping token or complete a 2444// request that required a control group. 2445func (b *SystemBackend) handleWrappingUnwrap(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2446 // If a third party is unwrapping (rather than the calling token being the 2447 // wrapping token) we detect this so that we can revoke the original 2448 // wrapping token after reading it 2449 var thirdParty bool 2450 2451 token := data.Get("token").(string) 2452 if token != "" { 2453 thirdParty = true 2454 } else { 2455 token = req.ClientToken 2456 } 2457 2458 // Get the policies so we can determine if this is a normal response 2459 // wrapping request or a control group token. 2460 // 2461 // We use lookupTainted here because the token might have already been used 2462 // by handleRequest(), this happens when it's a normal response wrapping 2463 // request and the token was provided "first party". We want to inspect the 2464 // token policies but will not use this token entry for anything else. 2465 te, err := b.Core.tokenStore.lookupTainted(ctx, token) 2466 if err != nil { 2467 return nil, err 2468 } 2469 if te == nil { 2470 return nil, nil 2471 } 2472 if len(te.Policies) != 1 { 2473 return nil, errors.New("token is not a valid unwrap token") 2474 } 2475 2476 unwrapNS, err := NamespaceByID(ctx, te.NamespaceID, b.Core) 2477 if err != nil { 2478 return nil, err 2479 } 2480 unwrapCtx := namespace.ContextWithNamespace(ctx, unwrapNS) 2481 2482 var response string 2483 switch te.Policies[0] { 2484 case controlGroupPolicyName: 2485 response, err = controlGroupUnwrap(unwrapCtx, b, token, thirdParty) 2486 case responseWrappingPolicyName: 2487 response, err = b.responseWrappingUnwrap(unwrapCtx, te, thirdParty) 2488 } 2489 if err != nil { 2490 var respErr *logical.Response 2491 if len(response) > 0 { 2492 respErr = logical.ErrorResponse(response) 2493 } 2494 2495 return respErr, err 2496 } 2497 2498 resp := &logical.Response{ 2499 Data: map[string]interface{}{}, 2500 } 2501 2502 // Most of the time we want to just send over the marshalled HTTP bytes. 2503 // However there is a sad separate case: if the original response was using 2504 // bare values we need to use those or else what comes back is garbled. 2505 httpResp := &logical.HTTPResponse{} 2506 err = jsonutil.DecodeJSON([]byte(response), httpResp) 2507 if err != nil { 2508 return nil, errwrap.Wrapf("error decoding wrapped response: {{err}}", err) 2509 } 2510 if httpResp.Data != nil && 2511 (httpResp.Data[logical.HTTPStatusCode] != nil || 2512 httpResp.Data[logical.HTTPRawBody] != nil || 2513 httpResp.Data[logical.HTTPContentType] != nil) { 2514 if httpResp.Data[logical.HTTPStatusCode] != nil { 2515 resp.Data[logical.HTTPStatusCode] = httpResp.Data[logical.HTTPStatusCode] 2516 } 2517 if httpResp.Data[logical.HTTPContentType] != nil { 2518 resp.Data[logical.HTTPContentType] = httpResp.Data[logical.HTTPContentType] 2519 } 2520 2521 rawBody := httpResp.Data[logical.HTTPRawBody] 2522 if rawBody != nil { 2523 // Decode here so that we can audit properly 2524 switch rawBody.(type) { 2525 case string: 2526 // Best effort decoding; if this works, the original value was 2527 // probably a []byte instead of a string, but was marshaled 2528 // when the value was saved, so this restores it as it was 2529 decBytes, err := base64.StdEncoding.DecodeString(rawBody.(string)) 2530 if err == nil { 2531 // We end up with []byte, will not be HMAC'd 2532 resp.Data[logical.HTTPRawBody] = decBytes 2533 } else { 2534 // We end up with string, will be HMAC'd 2535 resp.Data[logical.HTTPRawBody] = rawBody 2536 } 2537 default: 2538 b.Core.Logger().Error("unexpected type of raw body when decoding wrapped token", "type", fmt.Sprintf("%T", rawBody)) 2539 } 2540 2541 resp.Data[logical.HTTPRawBodyAlreadyJSONDecoded] = true 2542 } 2543 2544 return resp, nil 2545 } 2546 2547 if len(response) == 0 { 2548 resp.Data[logical.HTTPStatusCode] = 204 2549 } else { 2550 resp.Data[logical.HTTPStatusCode] = 200 2551 resp.Data[logical.HTTPRawBody] = []byte(response) 2552 resp.Data[logical.HTTPContentType] = "application/json" 2553 } 2554 2555 return resp, nil 2556} 2557 2558// responseWrappingUnwrap will read the stored response in the cubbyhole and 2559// return the raw HTTP response. 2560func (b *SystemBackend) responseWrappingUnwrap(ctx context.Context, te *logical.TokenEntry, thirdParty bool) (string, error) { 2561 tokenID := te.ID 2562 if thirdParty { 2563 // Use the token to decrement the use count to avoid a second operation on the token. 2564 _, err := b.Core.tokenStore.UseTokenByID(ctx, tokenID) 2565 if err != nil { 2566 return "", errwrap.Wrapf("error decrementing wrapping token's use-count: {{err}}", err) 2567 } 2568 2569 defer b.Core.tokenStore.revokeOrphan(ctx, tokenID) 2570 } 2571 2572 cubbyReq := &logical.Request{ 2573 Operation: logical.ReadOperation, 2574 Path: "cubbyhole/response", 2575 ClientToken: tokenID, 2576 } 2577 cubbyReq.SetTokenEntry(te) 2578 cubbyResp, err := b.Core.router.Route(ctx, cubbyReq) 2579 if err != nil { 2580 return "", errwrap.Wrapf("error looking up wrapping information: {{err}}", err) 2581 } 2582 if cubbyResp == nil { 2583 return "no information found; wrapping token may be from a previous Vault version", ErrInternalError 2584 } 2585 if cubbyResp != nil && cubbyResp.IsError() { 2586 return cubbyResp.Error().Error(), nil 2587 } 2588 if cubbyResp.Data == nil { 2589 return "wrapping information was nil; wrapping token may be from a previous Vault version", ErrInternalError 2590 } 2591 2592 responseRaw := cubbyResp.Data["response"] 2593 if responseRaw == nil { 2594 return "", fmt.Errorf("no response found inside the cubbyhole") 2595 } 2596 response, ok := responseRaw.(string) 2597 if !ok { 2598 return "", fmt.Errorf("could not decode response inside the cubbyhole") 2599 } 2600 2601 return response, nil 2602} 2603 2604func (b *SystemBackend) handleMetrics(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2605 format := data.Get("format").(string) 2606 if format == "" { 2607 format = metricsutil.FormatFromRequest(req) 2608 } 2609 return b.Core.metricsHelper.ResponseForFormat(format) 2610} 2611 2612func (b *SystemBackend) handleWrappingLookup(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2613 // This ordering of lookups has been validated already in the wrapping 2614 // validation func, we're just doing this for a safety check 2615 token := data.Get("token").(string) 2616 if token == "" { 2617 token = req.ClientToken 2618 if token == "" { 2619 return logical.ErrorResponse("missing \"token\" value in input"), logical.ErrInvalidRequest 2620 } 2621 } 2622 2623 te, err := b.Core.tokenStore.lookupTainted(ctx, token) 2624 if err != nil { 2625 return nil, err 2626 } 2627 if te == nil { 2628 return nil, nil 2629 } 2630 if len(te.Policies) != 1 { 2631 return nil, errors.New("token is not a valid unwrap token") 2632 } 2633 2634 cubbyReq := &logical.Request{ 2635 Operation: logical.ReadOperation, 2636 Path: "cubbyhole/wrapinfo", 2637 ClientToken: token, 2638 } 2639 cubbyReq.SetTokenEntry(te) 2640 cubbyResp, err := b.Core.router.Route(ctx, cubbyReq) 2641 if err != nil { 2642 return nil, errwrap.Wrapf("error looking up wrapping information: {{err}}", err) 2643 } 2644 if cubbyResp == nil { 2645 return logical.ErrorResponse("no information found; wrapping token may be from a previous Vault version"), nil 2646 } 2647 if cubbyResp != nil && cubbyResp.IsError() { 2648 return cubbyResp, nil 2649 } 2650 if cubbyResp.Data == nil { 2651 return logical.ErrorResponse("wrapping information was nil; wrapping token may be from a previous Vault version"), nil 2652 } 2653 2654 creationTTLRaw := cubbyResp.Data["creation_ttl"] 2655 creationTime := cubbyResp.Data["creation_time"] 2656 creationPath := cubbyResp.Data["creation_path"] 2657 2658 resp := &logical.Response{ 2659 Data: map[string]interface{}{}, 2660 } 2661 if creationTTLRaw != nil { 2662 creationTTL, err := creationTTLRaw.(json.Number).Int64() 2663 if err != nil { 2664 return nil, errwrap.Wrapf("error reading creation_ttl value from wrapping information: {{err}}", err) 2665 } 2666 resp.Data["creation_ttl"] = time.Duration(creationTTL).Seconds() 2667 } 2668 if creationTime != nil { 2669 // This was JSON marshaled so it's already a string in RFC3339 format 2670 resp.Data["creation_time"] = cubbyResp.Data["creation_time"] 2671 } 2672 if creationPath != nil { 2673 resp.Data["creation_path"] = cubbyResp.Data["creation_path"] 2674 } 2675 2676 return resp, nil 2677} 2678 2679func (b *SystemBackend) handleWrappingRewrap(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 2680 // If a third party is rewrapping (rather than the calling token being the 2681 // wrapping token) we detect this so that we can revoke the original 2682 // wrapping token after reading it. Right now wrapped tokens can't unwrap 2683 // themselves, but in case we change it, this will be ready to do the right 2684 // thing. 2685 var thirdParty bool 2686 2687 token := data.Get("token").(string) 2688 if token != "" { 2689 thirdParty = true 2690 } else { 2691 token = req.ClientToken 2692 } 2693 2694 te, err := b.Core.tokenStore.lookupTainted(ctx, token) 2695 if err != nil { 2696 return nil, err 2697 } 2698 if te == nil { 2699 return nil, nil 2700 } 2701 if len(te.Policies) != 1 { 2702 return nil, errors.New("token is not a valid unwrap token") 2703 } 2704 2705 if thirdParty { 2706 // Use the token to decrement the use count to avoid a second operation on the token. 2707 _, err := b.Core.tokenStore.UseTokenByID(ctx, token) 2708 if err != nil { 2709 return nil, errwrap.Wrapf("error decrementing wrapping token's use-count: {{err}}", err) 2710 } 2711 defer b.Core.tokenStore.revokeOrphan(ctx, token) 2712 } 2713 2714 // Fetch the original TTL 2715 cubbyReq := &logical.Request{ 2716 Operation: logical.ReadOperation, 2717 Path: "cubbyhole/wrapinfo", 2718 ClientToken: token, 2719 } 2720 cubbyReq.SetTokenEntry(te) 2721 cubbyResp, err := b.Core.router.Route(ctx, cubbyReq) 2722 if err != nil { 2723 return nil, errwrap.Wrapf("error looking up wrapping information: {{err}}", err) 2724 } 2725 if cubbyResp == nil { 2726 return logical.ErrorResponse("no information found; wrapping token may be from a previous Vault version"), nil 2727 } 2728 if cubbyResp != nil && cubbyResp.IsError() { 2729 return cubbyResp, nil 2730 } 2731 if cubbyResp.Data == nil { 2732 return logical.ErrorResponse("wrapping information was nil; wrapping token may be from a previous Vault version"), nil 2733 } 2734 2735 // Set the creation TTL on the request 2736 creationTTLRaw := cubbyResp.Data["creation_ttl"] 2737 if creationTTLRaw == nil { 2738 return nil, fmt.Errorf("creation_ttl value in wrapping information was nil") 2739 } 2740 creationTTL, err := cubbyResp.Data["creation_ttl"].(json.Number).Int64() 2741 if err != nil { 2742 return nil, errwrap.Wrapf("error reading creation_ttl value from wrapping information: {{err}}", err) 2743 } 2744 2745 // Get creation_path to return as the response later 2746 creationPathRaw := cubbyResp.Data["creation_path"] 2747 if creationPathRaw == nil { 2748 return nil, fmt.Errorf("creation_path value in wrapping information was nil") 2749 } 2750 creationPath := creationPathRaw.(string) 2751 2752 // Fetch the original response and return it as the data for the new response 2753 cubbyReq = &logical.Request{ 2754 Operation: logical.ReadOperation, 2755 Path: "cubbyhole/response", 2756 ClientToken: token, 2757 } 2758 cubbyReq.SetTokenEntry(te) 2759 cubbyResp, err = b.Core.router.Route(ctx, cubbyReq) 2760 if err != nil { 2761 return nil, errwrap.Wrapf("error looking up response: {{err}}", err) 2762 } 2763 if cubbyResp == nil { 2764 return logical.ErrorResponse("no information found; wrapping token may be from a previous Vault version"), nil 2765 } 2766 if cubbyResp != nil && cubbyResp.IsError() { 2767 return cubbyResp, nil 2768 } 2769 if cubbyResp.Data == nil { 2770 return logical.ErrorResponse("wrapping information was nil; wrapping token may be from a previous Vault version"), nil 2771 } 2772 2773 response := cubbyResp.Data["response"] 2774 if response == nil { 2775 return nil, fmt.Errorf("no response found inside the cubbyhole") 2776 } 2777 2778 // Return response in "response"; wrapping code will detect the rewrap and 2779 // slot in instead of nesting 2780 return &logical.Response{ 2781 Data: map[string]interface{}{ 2782 "response": response, 2783 }, 2784 WrapInfo: &wrapping.ResponseWrapInfo{ 2785 TTL: time.Duration(creationTTL), 2786 CreationPath: creationPath, 2787 }, 2788 }, nil 2789} 2790 2791func (b *SystemBackend) pathHashWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 2792 inputB64 := d.Get("input").(string) 2793 format := d.Get("format").(string) 2794 algorithm := d.Get("urlalgorithm").(string) 2795 if algorithm == "" { 2796 algorithm = d.Get("algorithm").(string) 2797 } 2798 2799 input, err := base64.StdEncoding.DecodeString(inputB64) 2800 if err != nil { 2801 return logical.ErrorResponse(fmt.Sprintf("unable to decode input as base64: %s", err)), logical.ErrInvalidRequest 2802 } 2803 2804 switch format { 2805 case "hex": 2806 case "base64": 2807 default: 2808 return logical.ErrorResponse(fmt.Sprintf("unsupported encoding format %s; must be \"hex\" or \"base64\"", format)), nil 2809 } 2810 2811 var hf hash.Hash 2812 switch algorithm { 2813 case "sha2-224": 2814 hf = sha256.New224() 2815 case "sha2-256": 2816 hf = sha256.New() 2817 case "sha2-384": 2818 hf = sha512.New384() 2819 case "sha2-512": 2820 hf = sha512.New() 2821 default: 2822 return logical.ErrorResponse(fmt.Sprintf("unsupported algorithm %s", algorithm)), nil 2823 } 2824 hf.Write(input) 2825 retBytes := hf.Sum(nil) 2826 2827 var retStr string 2828 switch format { 2829 case "hex": 2830 retStr = hex.EncodeToString(retBytes) 2831 case "base64": 2832 retStr = base64.StdEncoding.EncodeToString(retBytes) 2833 } 2834 2835 // Generate the response 2836 resp := &logical.Response{ 2837 Data: map[string]interface{}{ 2838 "sum": retStr, 2839 }, 2840 } 2841 return resp, nil 2842} 2843 2844func (b *SystemBackend) pathRandomWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 2845 bytes := 0 2846 var err error 2847 strBytes := d.Get("urlbytes").(string) 2848 if strBytes != "" { 2849 bytes, err = strconv.Atoi(strBytes) 2850 if err != nil { 2851 return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil 2852 } 2853 } else { 2854 bytes = d.Get("bytes").(int) 2855 } 2856 format := d.Get("format").(string) 2857 2858 if bytes < 1 { 2859 return logical.ErrorResponse(`"bytes" cannot be less than 1`), nil 2860 } 2861 2862 if bytes > maxBytes { 2863 return logical.ErrorResponse(`"bytes" should be less than %s`, maxBytes), nil 2864 } 2865 2866 switch format { 2867 case "hex": 2868 case "base64": 2869 default: 2870 return logical.ErrorResponse(fmt.Sprintf("unsupported encoding format %s; must be \"hex\" or \"base64\"", format)), nil 2871 } 2872 2873 randBytes, err := uuid.GenerateRandomBytes(bytes) 2874 if err != nil { 2875 return nil, err 2876 } 2877 2878 var retStr string 2879 switch format { 2880 case "hex": 2881 retStr = hex.EncodeToString(randBytes) 2882 case "base64": 2883 retStr = base64.StdEncoding.EncodeToString(randBytes) 2884 } 2885 2886 // Generate the response 2887 resp := &logical.Response{ 2888 Data: map[string]interface{}{ 2889 "random_bytes": retStr, 2890 }, 2891 } 2892 return resp, nil 2893} 2894 2895func hasMountAccess(ctx context.Context, acl *ACL, path string) bool { 2896 ns, err := namespace.FromContext(ctx) 2897 if err != nil { 2898 return false 2899 } 2900 2901 // If a policy is giving us direct access to the mount path then we can do 2902 // a fast return. 2903 capabilities := acl.Capabilities(ctx, ns.TrimmedPath(path)) 2904 if !strutil.StrListContains(capabilities, DenyCapability) { 2905 return true 2906 } 2907 2908 var aclCapabilitiesGiven bool 2909 walkFn := func(s string, v interface{}) bool { 2910 if v == nil { 2911 return false 2912 } 2913 2914 perms := v.(*ACLPermissions) 2915 2916 switch { 2917 case perms.CapabilitiesBitmap&DenyCapabilityInt > 0: 2918 return false 2919 2920 case perms.CapabilitiesBitmap&CreateCapabilityInt > 0, 2921 perms.CapabilitiesBitmap&DeleteCapabilityInt > 0, 2922 perms.CapabilitiesBitmap&ListCapabilityInt > 0, 2923 perms.CapabilitiesBitmap&ReadCapabilityInt > 0, 2924 perms.CapabilitiesBitmap&SudoCapabilityInt > 0, 2925 perms.CapabilitiesBitmap&UpdateCapabilityInt > 0: 2926 2927 aclCapabilitiesGiven = true 2928 2929 return true 2930 } 2931 2932 return false 2933 } 2934 2935 acl.exactRules.WalkPrefix(path, walkFn) 2936 if !aclCapabilitiesGiven { 2937 acl.prefixRules.WalkPrefix(path, walkFn) 2938 } 2939 2940 if !aclCapabilitiesGiven { 2941 if perms := acl.CheckAllowedFromNonExactPaths(path, true); perms != nil { 2942 return true 2943 } 2944 } 2945 2946 return aclCapabilitiesGiven 2947} 2948 2949func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 2950 ns, err := namespace.FromContext(ctx) 2951 if err != nil { 2952 return nil, err 2953 } 2954 2955 resp := &logical.Response{ 2956 Data: make(map[string]interface{}), 2957 } 2958 2959 secretMounts := make(map[string]interface{}) 2960 authMounts := make(map[string]interface{}) 2961 resp.Data["secret"] = secretMounts 2962 resp.Data["auth"] = authMounts 2963 2964 var acl *ACL 2965 var isAuthed bool 2966 if req.ClientToken != "" { 2967 isAuthed = true 2968 2969 var entity *identity.Entity 2970 var te *logical.TokenEntry 2971 // Load the ACL policies so we can walk the prefix for this mount 2972 acl, te, entity, _, err = b.Core.fetchACLTokenEntryAndEntity(ctx, req) 2973 if err != nil { 2974 return nil, err 2975 } 2976 if entity != nil && entity.Disabled { 2977 b.logger.Warn("permission denied as the entity on the token is disabled") 2978 return nil, logical.ErrPermissionDenied 2979 } 2980 if te != nil && te.EntityID != "" && entity == nil { 2981 b.logger.Warn("permission denied as the entity on the token is invalid") 2982 return nil, logical.ErrPermissionDenied 2983 } 2984 } 2985 2986 hasAccess := func(ctx context.Context, me *MountEntry) bool { 2987 if me.Config.ListingVisibility == ListingVisibilityUnauth { 2988 return true 2989 } 2990 2991 if isAuthed { 2992 return hasMountAccess(ctx, acl, me.Namespace().Path+me.Path) 2993 } 2994 2995 return false 2996 } 2997 2998 b.Core.mountsLock.RLock() 2999 for _, entry := range b.Core.mounts.Entries { 3000 filtered, err := b.Core.checkReplicatedFiltering(ctx, entry, "") 3001 if err != nil { 3002 b.Core.mountsLock.RUnlock() 3003 return nil, err 3004 } 3005 if filtered { 3006 continue 3007 } 3008 3009 if ns.ID == entry.NamespaceID && hasAccess(ctx, entry) { 3010 if isAuthed { 3011 // If this is an authed request return all the mount info 3012 secretMounts[entry.Path] = mountInfo(entry) 3013 } else { 3014 secretMounts[entry.Path] = map[string]interface{}{ 3015 "type": entry.Type, 3016 "description": entry.Description, 3017 "options": entry.Options, 3018 } 3019 } 3020 } 3021 } 3022 b.Core.mountsLock.RUnlock() 3023 3024 b.Core.authLock.RLock() 3025 for _, entry := range b.Core.auth.Entries { 3026 filtered, err := b.Core.checkReplicatedFiltering(ctx, entry, credentialRoutePrefix) 3027 if err != nil { 3028 b.Core.authLock.RUnlock() 3029 return nil, err 3030 } 3031 if filtered { 3032 continue 3033 } 3034 3035 if ns.ID == entry.NamespaceID && hasAccess(ctx, entry) { 3036 if isAuthed { 3037 // If this is an authed request return all the mount info 3038 authMounts[entry.Path] = mountInfo(entry) 3039 } else { 3040 authMounts[entry.Path] = map[string]interface{}{ 3041 "type": entry.Type, 3042 "description": entry.Description, 3043 "options": entry.Options, 3044 } 3045 } 3046 } 3047 } 3048 b.Core.authLock.RUnlock() 3049 3050 return resp, nil 3051} 3052 3053func (b *SystemBackend) pathInternalUIMountRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 3054 path := d.Get("path").(string) 3055 if path == "" { 3056 return logical.ErrorResponse("path not set"), logical.ErrInvalidRequest 3057 } 3058 path = sanitizeMountPath(path) 3059 3060 errResp := logical.ErrorResponse(fmt.Sprintf("preflight capability check returned 403, please ensure client's policies grant access to path %q", path)) 3061 3062 ns, err := namespace.FromContext(ctx) 3063 if err != nil { 3064 return nil, err 3065 } 3066 3067 me := b.Core.router.MatchingMountEntry(ctx, path) 3068 if me == nil { 3069 // Return a permission denied error here so this path cannot be used to 3070 // brute force a list of mounts. 3071 return errResp, logical.ErrPermissionDenied 3072 } 3073 3074 filtered, err := b.Core.checkReplicatedFiltering(ctx, me, "") 3075 if err != nil { 3076 return nil, err 3077 } 3078 if filtered { 3079 return errResp, logical.ErrPermissionDenied 3080 } 3081 3082 resp := &logical.Response{ 3083 Data: mountInfo(me), 3084 } 3085 resp.Data["path"] = me.Path 3086 if ns.ID != me.Namespace().ID { 3087 resp.Data["path"] = me.Namespace().Path + me.Path 3088 } 3089 3090 // Load the ACL policies so we can walk the prefix for this mount 3091 acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(ctx, req) 3092 if err != nil { 3093 return nil, err 3094 } 3095 if entity != nil && entity.Disabled { 3096 b.logger.Warn("permission denied as the entity on the token is disabled") 3097 return errResp, logical.ErrPermissionDenied 3098 } 3099 if te != nil && te.EntityID != "" && entity == nil { 3100 b.logger.Warn("permission denied as the entity on the token is invalid") 3101 return nil, logical.ErrPermissionDenied 3102 } 3103 3104 if !hasMountAccess(ctx, acl, ns.Path+me.Path) { 3105 return errResp, logical.ErrPermissionDenied 3106 } 3107 3108 return resp, nil 3109} 3110 3111func (b *SystemBackend) pathInternalCountersRequests(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 3112 counters, err := b.Core.loadAllRequestCounters(ctx, time.Now()) 3113 if err != nil { 3114 return nil, err 3115 } 3116 3117 resp := &logical.Response{ 3118 Data: map[string]interface{}{ 3119 "counters": counters, 3120 }, 3121 } 3122 3123 return resp, nil 3124} 3125 3126func (b *SystemBackend) pathInternalUIResultantACL(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 3127 if req.ClientToken == "" { 3128 // 204 -- no ACL 3129 return nil, nil 3130 } 3131 3132 acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(ctx, req) 3133 if err != nil { 3134 return nil, err 3135 } 3136 3137 if entity != nil && entity.Disabled { 3138 b.logger.Warn("permission denied as the entity on the token is disabled") 3139 return logical.ErrorResponse(logical.ErrPermissionDenied.Error()), nil 3140 } 3141 if te != nil && te.EntityID != "" && entity == nil { 3142 b.logger.Warn("permission denied as the entity on the token is invalid") 3143 return logical.ErrorResponse(logical.ErrPermissionDenied.Error()), nil 3144 } 3145 3146 resp := &logical.Response{ 3147 Data: map[string]interface{}{ 3148 "root": false, 3149 }, 3150 } 3151 3152 if acl.root { 3153 resp.Data["root"] = true 3154 return resp, nil 3155 } 3156 3157 exact := map[string]interface{}{} 3158 glob := map[string]interface{}{} 3159 3160 walkFn := func(pt map[string]interface{}, s string, v interface{}) { 3161 if v == nil { 3162 return 3163 } 3164 3165 perms := v.(*ACLPermissions) 3166 capabilities := []string{} 3167 3168 if perms.CapabilitiesBitmap&CreateCapabilityInt > 0 { 3169 capabilities = append(capabilities, CreateCapability) 3170 } 3171 if perms.CapabilitiesBitmap&DeleteCapabilityInt > 0 { 3172 capabilities = append(capabilities, DeleteCapability) 3173 } 3174 if perms.CapabilitiesBitmap&ListCapabilityInt > 0 { 3175 capabilities = append(capabilities, ListCapability) 3176 } 3177 if perms.CapabilitiesBitmap&ReadCapabilityInt > 0 { 3178 capabilities = append(capabilities, ReadCapability) 3179 } 3180 if perms.CapabilitiesBitmap&SudoCapabilityInt > 0 { 3181 capabilities = append(capabilities, SudoCapability) 3182 } 3183 if perms.CapabilitiesBitmap&UpdateCapabilityInt > 0 { 3184 capabilities = append(capabilities, UpdateCapability) 3185 } 3186 3187 // If "deny" is explicitly set or if the path has no capabilities at all, 3188 // set the path capabilities to "deny" 3189 if perms.CapabilitiesBitmap&DenyCapabilityInt > 0 || len(capabilities) == 0 { 3190 capabilities = []string{DenyCapability} 3191 } 3192 3193 res := map[string]interface{}{} 3194 if len(capabilities) > 0 { 3195 res["capabilities"] = capabilities 3196 } 3197 if perms.MinWrappingTTL != 0 { 3198 res["min_wrapping_ttl"] = int64(perms.MinWrappingTTL.Seconds()) 3199 } 3200 if perms.MaxWrappingTTL != 0 { 3201 res["max_wrapping_ttl"] = int64(perms.MaxWrappingTTL.Seconds()) 3202 } 3203 if len(perms.AllowedParameters) > 0 { 3204 res["allowed_parameters"] = perms.AllowedParameters 3205 } 3206 if len(perms.DeniedParameters) > 0 { 3207 res["denied_parameters"] = perms.DeniedParameters 3208 } 3209 if len(perms.RequiredParameters) > 0 { 3210 res["required_parameters"] = perms.RequiredParameters 3211 } 3212 3213 pt[s] = res 3214 } 3215 3216 exactWalkFn := func(s string, v interface{}) bool { 3217 walkFn(exact, s, v) 3218 return false 3219 } 3220 3221 globWalkFn := func(s string, v interface{}) bool { 3222 walkFn(glob, s, v) 3223 return false 3224 } 3225 3226 acl.exactRules.Walk(exactWalkFn) 3227 acl.prefixRules.Walk(globWalkFn) 3228 3229 resp.Data["exact_paths"] = exact 3230 resp.Data["glob_paths"] = glob 3231 3232 return resp, nil 3233} 3234 3235func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 3236 3237 // Limit output to authorized paths 3238 resp, err := b.pathInternalUIMountsRead(ctx, req, d) 3239 if err != nil { 3240 return nil, err 3241 } 3242 3243 context := d.Get("context").(string) 3244 3245 // Set up target document and convert to map[string]interface{} which is what will 3246 // be received from plugin backends. 3247 doc := framework.NewOASDocument() 3248 3249 procMountGroup := func(group, mountPrefix string) error { 3250 for mount := range resp.Data[group].(map[string]interface{}) { 3251 backend := b.Core.router.MatchingBackend(ctx, mountPrefix+mount) 3252 3253 if backend == nil { 3254 continue 3255 } 3256 3257 req := &logical.Request{ 3258 Operation: logical.HelpOperation, 3259 Storage: req.Storage, 3260 } 3261 3262 resp, err := backend.HandleRequest(ctx, req) 3263 if err != nil { 3264 return err 3265 } 3266 3267 var backendDoc *framework.OASDocument 3268 3269 // Normalize response type, which will be different if received 3270 // from an external plugin. 3271 switch v := resp.Data["openapi"].(type) { 3272 case *framework.OASDocument: 3273 backendDoc = v 3274 case map[string]interface{}: 3275 backendDoc, err = framework.NewOASDocumentFromMap(v) 3276 if err != nil { 3277 return err 3278 } 3279 default: 3280 continue 3281 } 3282 3283 // Prepare to add tags to default builtins that are 3284 // type "unknown" and won't already be tagged. 3285 var tag string 3286 switch mountPrefix + mount { 3287 case "cubbyhole/", "secret/": 3288 tag = "secrets" 3289 case "sys/": 3290 tag = "system" 3291 case "auth/token/": 3292 tag = "auth" 3293 case "identity/": 3294 tag = "identity" 3295 } 3296 3297 // Merge backend paths with existing document 3298 for path, obj := range backendDoc.Paths { 3299 path := strings.TrimPrefix(path, "/") 3300 3301 // Add tags to all of the operations if necessary 3302 if tag != "" { 3303 for _, op := range []*framework.OASOperation{obj.Get, obj.Post, obj.Delete} { 3304 // TODO: a special override for identity is used used here because the backend 3305 // is currently categorized as "secret", which will likely change. Also of interest 3306 // is removing all tag handling here and providing the mount information to OpenAPI. 3307 if op != nil && (len(op.Tags) == 0 || tag == "identity") { 3308 op.Tags = []string{tag} 3309 } 3310 } 3311 } 3312 3313 doc.Paths["/"+mountPrefix+mount+path] = obj 3314 } 3315 } 3316 return nil 3317 } 3318 3319 if err := procMountGroup("secret", ""); err != nil { 3320 return nil, err 3321 } 3322 if err := procMountGroup("auth", "auth/"); err != nil { 3323 return nil, err 3324 } 3325 3326 doc.CreateOperationIDs(context) 3327 3328 buf, err := json.Marshal(doc) 3329 if err != nil { 3330 return nil, err 3331 } 3332 3333 resp = &logical.Response{ 3334 Data: map[string]interface{}{ 3335 logical.HTTPStatusCode: 200, 3336 logical.HTTPRawBody: buf, 3337 logical.HTTPContentType: "application/json", 3338 }, 3339 } 3340 3341 return resp, nil 3342} 3343 3344func sanitizeMountPath(path string) string { 3345 if !strings.HasSuffix(path, "/") { 3346 path += "/" 3347 } 3348 3349 if strings.HasPrefix(path, "/") { 3350 path = path[1:] 3351 } 3352 3353 return path 3354} 3355 3356func checkListingVisibility(visibility ListingVisibilityType) error { 3357 switch visibility { 3358 case ListingVisibilityDefault: 3359 case ListingVisibilityHidden: 3360 case ListingVisibilityUnauth: 3361 default: 3362 return fmt.Errorf("invalid listing visilibity type") 3363 } 3364 3365 return nil 3366} 3367 3368const sysHelpRoot = ` 3369The system backend is built-in to Vault and cannot be remounted or 3370unmounted. It contains the paths that are used to configure Vault itself 3371as well as perform core operations. 3372` 3373 3374// sysHelp is all the help text for the sys backend. 3375var sysHelp = map[string][2]string{ 3376 "license": { 3377 "Sets the license of the server.", 3378 ` 3379The path responds to the following HTTP methods. 3380 3381 GET / 3382 Returns information on the installed license 3383 3384 POST 3385 Sets the license for the server 3386 `, 3387 }, 3388 "config/cors": { 3389 "Configures or returns the current configuration of CORS settings.", 3390 ` 3391This path responds to the following HTTP methods. 3392 3393 GET / 3394 Returns the configuration of the CORS setting. 3395 3396 POST / 3397 Sets the comma-separated list of origins that can make cross-origin requests. 3398 3399 DELETE / 3400 Clears the CORS configuration and disables acceptance of CORS requests. 3401 `, 3402 }, 3403 "config/ui/headers": { 3404 "Configures response headers that should be returned from the UI.", 3405 ` 3406This path responds to the following HTTP methods. 3407 GET /<header> 3408 Returns the header value. 3409 POST /<header> 3410 Sets the header value for the UI. 3411 DELETE /<header> 3412 Clears the header value for UI. 3413 3414 LIST / 3415 List the headers configured for the UI. 3416 `, 3417 }, 3418 "init": { 3419 "Initializes or returns the initialization status of the Vault.", 3420 ` 3421This path responds to the following HTTP methods. 3422 3423 GET / 3424 Returns the initialization status of the Vault. 3425 3426 POST / 3427 Initializes a new vault. 3428 `, 3429 }, 3430 "generate-root": { 3431 "Reads, generates, or deletes a root token regeneration process.", 3432 ` 3433This path responds to multiple HTTP methods which change the behavior. Those 3434HTTP methods are listed below. 3435 3436 GET /attempt 3437 Reads the configuration and progress of the current root generation 3438 attempt. 3439 3440 POST /attempt 3441 Initializes a new root generation attempt. Only a single root generation 3442 attempt can take place at a time. One (and only one) of otp or pgp_key 3443 are required. 3444 3445 DELETE /attempt 3446 Cancels any in-progress root generation attempt. This clears any 3447 progress made. This must be called to change the OTP or PGP key being 3448 used. 3449 `, 3450 }, 3451 "seal-status": { 3452 "Returns the seal status of the Vault.", 3453 ` 3454This path responds to the following HTTP methods. 3455 3456 GET / 3457 Returns the seal status of the Vault. This is an unauthenticated 3458 endpoint. 3459 `, 3460 }, 3461 "seal": { 3462 "Seals the Vault.", 3463 ` 3464This path responds to the following HTTP methods. 3465 3466 PUT / 3467 Seals the Vault. 3468 `, 3469 }, 3470 "unseal": { 3471 "Unseals the Vault.", 3472 ` 3473This path responds to the following HTTP methods. 3474 3475 PUT / 3476 Unseals the Vault. 3477 `, 3478 }, 3479 "mounts": { 3480 "List the currently mounted backends.", 3481 ` 3482This path responds to the following HTTP methods. 3483 3484 GET / 3485 Lists all the mounted secret backends. 3486 3487 GET /<mount point> 3488 Get information about the mount at the specified path. 3489 3490 POST /<mount point> 3491 Mount a new secret backend to the mount point in the URL. 3492 3493 POST /<mount point>/tune 3494 Tune configuration parameters for the given mount point. 3495 3496 DELETE /<mount point> 3497 Unmount the specified mount point. 3498 `, 3499 }, 3500 3501 "mount": { 3502 `Mount a new backend at a new path.`, 3503 ` 3504Mount a backend at a new path. A backend can be mounted multiple times at 3505multiple paths in order to configure multiple separately configured backends. 3506Example: you might have an AWS backend for the east coast, and one for the 3507west coast. 3508 `, 3509 }, 3510 3511 "mount_path": { 3512 `The path to mount to. Example: "aws/east"`, 3513 "", 3514 }, 3515 3516 "mount_type": { 3517 `The type of the backend. Example: "passthrough"`, 3518 "", 3519 }, 3520 3521 "mount_desc": { 3522 `User-friendly description for this mount.`, 3523 "", 3524 }, 3525 3526 "mount_config": { 3527 `Configuration for this mount, such as default_lease_ttl 3528and max_lease_ttl.`, 3529 }, 3530 3531 "mount_local": { 3532 `Mark the mount as a local mount, which is not replicated 3533and is unaffected by replication.`, 3534 }, 3535 3536 "mount_plugin_name": { 3537 `Name of the plugin to mount based from the name registered 3538in the plugin catalog.`, 3539 }, 3540 3541 "mount_options": { 3542 `The options to pass into the backend. Should be a json object with string keys and values.`, 3543 }, 3544 3545 "seal_wrap": { 3546 `Whether to turn on seal wrapping for the mount.`, 3547 }, 3548 3549 "tune_default_lease_ttl": { 3550 `The default lease TTL for this mount.`, 3551 }, 3552 3553 "tune_max_lease_ttl": { 3554 `The max lease TTL for this mount.`, 3555 }, 3556 3557 "tune_audit_non_hmac_request_keys": { 3558 `The list of keys in the request data object that will not be HMAC'ed by audit devices.`, 3559 }, 3560 3561 "tune_audit_non_hmac_response_keys": { 3562 `The list of keys in the response data object that will not be HMAC'ed by audit devices.`, 3563 }, 3564 3565 "tune_mount_options": { 3566 `The options to pass into the backend. Should be a json object with string keys and values.`, 3567 }, 3568 3569 "remount": { 3570 "Move the mount point of an already-mounted backend.", 3571 ` 3572This path responds to the following HTTP methods. 3573 3574 POST /sys/remount 3575 Changes the mount point of an already-mounted backend. 3576 `, 3577 }, 3578 3579 "auth_tune": { 3580 "Tune the configuration parameters for an auth path.", 3581 `Read and write the 'default-lease-ttl' and 'max-lease-ttl' values of 3582the auth path.`, 3583 }, 3584 3585 "mount_tune": { 3586 "Tune backend configuration parameters for this mount.", 3587 `Read and write the 'default-lease-ttl' and 'max-lease-ttl' values of 3588the mount.`, 3589 }, 3590 3591 "renew": { 3592 "Renew a lease on a secret", 3593 ` 3594When a secret is read, it may optionally include a lease interval 3595and a boolean indicating if renew is possible. For secrets that support 3596lease renewal, this endpoint is used to extend the validity of the 3597lease and to prevent an automatic revocation. 3598 `, 3599 }, 3600 3601 "lease_id": { 3602 "The lease identifier to renew. This is included with a lease.", 3603 "", 3604 }, 3605 3606 "increment": { 3607 "The desired increment in seconds to the lease", 3608 "", 3609 }, 3610 3611 "revoke": { 3612 "Revoke a leased secret immediately", 3613 ` 3614When a secret is generated with a lease, it is automatically revoked 3615at the end of the lease period if not renewed. However, in some cases 3616you may want to force an immediate revocation. This endpoint can be 3617used to revoke the secret with the given Lease ID. 3618 `, 3619 }, 3620 3621 "revoke-sync": { 3622 "Whether or not to perform the revocation synchronously", 3623 ` 3624If false, the call will return immediately and revocation will be queued; if it 3625fails, Vault will keep trying. If true, if the revocation fails, Vault will not 3626automatically try again and will return an error. For revoke-prefix, this 3627setting will apply to all leases being revoked. For revoke-force, since errors 3628are ignored, this setting is not supported. 3629`, 3630 }, 3631 3632 "revoke-prefix": { 3633 "Revoke all secrets generated in a given prefix", 3634 ` 3635Revokes all the secrets generated under a given mount prefix. As 3636an example, "prod/aws/" might be the AWS logical backend, and due to 3637a change in the "ops" policy, we may want to invalidate all the secrets 3638generated. We can do a revoke prefix at "prod/aws/ops" to revoke all 3639the ops secrets. This does a prefix match on the Lease IDs and revokes 3640all matching leases. 3641 `, 3642 }, 3643 3644 "revoke-prefix-path": { 3645 `The path to revoke keys under. Example: "prod/aws/ops"`, 3646 "", 3647 }, 3648 3649 "revoke-force": { 3650 "Revoke all secrets generated in a given prefix, ignoring errors.", 3651 ` 3652See the path help for 'revoke-prefix'; this behaves the same, except that it 3653ignores errors encountered during revocation. This can be used in certain 3654recovery situations; for instance, when you want to unmount a backend, but it 3655is impossible to fix revocation errors and these errors prevent the unmount 3656from proceeding. This is a DANGEROUS operation as it removes Vault's oversight 3657of external secrets. Access to this prefix should be tightly controlled. 3658 `, 3659 }, 3660 3661 "revoke-force-path": { 3662 `The path to revoke keys under. Example: "prod/aws/ops"`, 3663 "", 3664 }, 3665 3666 "auth-table": { 3667 "List the currently enabled credential backends.", 3668 ` 3669This path responds to the following HTTP methods. 3670 3671 GET / 3672 List the currently enabled credential backends: the name, the type of 3673 the backend, and a user friendly description of the purpose for the 3674 credential backend. 3675 3676 POST /<mount point> 3677 Enable a new auth method. 3678 3679 DELETE /<mount point> 3680 Disable the auth method at the given mount point. 3681 `, 3682 }, 3683 3684 "auth": { 3685 `Enable a new credential backend with a name.`, 3686 ` 3687Enable a credential mechanism at a new path. A backend can be mounted multiple times at 3688multiple paths in order to configure multiple separately configured backends. 3689Example: you might have an OAuth backend for GitHub, and one for Google Apps. 3690 `, 3691 }, 3692 3693 "auth_path": { 3694 `The path to mount to. Cannot be delimited. Example: "user"`, 3695 "", 3696 }, 3697 3698 "auth_type": { 3699 `The type of the backend. Example: "userpass"`, 3700 "", 3701 }, 3702 3703 "auth_desc": { 3704 `User-friendly description for this credential backend.`, 3705 "", 3706 }, 3707 3708 "auth_config": { 3709 `Configuration for this mount, such as plugin_name.`, 3710 }, 3711 3712 "auth_plugin": { 3713 `Name of the auth plugin to use based from the name in the plugin catalog.`, 3714 "", 3715 }, 3716 3717 "auth_options": { 3718 `The options to pass into the backend. Should be a json object with string keys and values.`, 3719 }, 3720 3721 "policy-list": { 3722 `List the configured access control policies.`, 3723 ` 3724This path responds to the following HTTP methods. 3725 3726 GET / 3727 List the names of the configured access control policies. 3728 3729 GET /<name> 3730 Retrieve the rules for the named policy. 3731 3732 PUT /<name> 3733 Add or update a policy. 3734 3735 DELETE /<name> 3736 Delete the policy with the given name. 3737 `, 3738 }, 3739 3740 "policy": { 3741 `Read, Modify, or Delete an access control policy.`, 3742 ` 3743Read the rules of an existing policy, create or update the rules of a policy, 3744or delete a policy. 3745 `, 3746 }, 3747 3748 "policy-name": { 3749 `The name of the policy. Example: "ops"`, 3750 "", 3751 }, 3752 3753 "policy-rules": { 3754 `The rules of the policy.`, 3755 "", 3756 }, 3757 3758 "policy-paths": { 3759 `The paths on which the policy should be applied.`, 3760 "", 3761 }, 3762 3763 "policy-enforcement-level": { 3764 `The enforcement level to apply to the policy.`, 3765 "", 3766 }, 3767 3768 "audit-hash": { 3769 "The hash of the given string via the given audit backend", 3770 "", 3771 }, 3772 3773 "audit-table": { 3774 "List the currently enabled audit backends.", 3775 ` 3776This path responds to the following HTTP methods. 3777 3778 GET / 3779 List the currently enabled audit backends. 3780 3781 PUT /<path> 3782 Enable an audit backend at the given path. 3783 3784 DELETE /<path> 3785 Disable the given audit backend. 3786 `, 3787 }, 3788 3789 "audit_path": { 3790 `The name of the backend. Cannot be delimited. Example: "mysql"`, 3791 "", 3792 }, 3793 3794 "audit_type": { 3795 `The type of the backend. Example: "mysql"`, 3796 "", 3797 }, 3798 3799 "audit_desc": { 3800 `User-friendly description for this audit backend.`, 3801 "", 3802 }, 3803 3804 "audit_opts": { 3805 `Configuration options for the audit backend.`, 3806 "", 3807 }, 3808 3809 "audit": { 3810 `Enable or disable audit backends.`, 3811 ` 3812Enable a new audit backend or disable an existing backend. 3813 `, 3814 }, 3815 3816 "key-status": { 3817 "Provides information about the backend encryption key.", 3818 ` 3819 Provides the current backend encryption key term and installation time. 3820 `, 3821 }, 3822 3823 "rotate": { 3824 "Rotates the backend encryption key used to persist data.", 3825 ` 3826 Rotate generates a new encryption key which is used to encrypt all 3827 data going to the storage backend. The old encryption keys are kept so 3828 that data encrypted using those keys can still be decrypted. 3829 `, 3830 }, 3831 3832 "rekey_backup": { 3833 "Allows fetching or deleting the backup of the rotated unseal keys.", 3834 "", 3835 }, 3836 3837 "capabilities": { 3838 "Fetches the capabilities of the given token on the given path.", 3839 `Returns the capabilities of the given token on the path. 3840 The path will be searched for a path match in all the policies associated with the token.`, 3841 }, 3842 3843 "capabilities_self": { 3844 "Fetches the capabilities of the given token on the given path.", 3845 `Returns the capabilities of the client token on the path. 3846 The path will be searched for a path match in all the policies associated with the client token.`, 3847 }, 3848 3849 "capabilities_accessor": { 3850 "Fetches the capabilities of the token associated with the given token, on the given path.", 3851 `When there is no access to the token, token accessor can be used to fetch the token's capabilities 3852 on a given path.`, 3853 }, 3854 3855 "tidy_leases": { 3856 `This endpoint performs cleanup tasks that can be run if certain error 3857conditions have occurred.`, 3858 `This endpoint performs cleanup tasks that can be run to clean up the 3859lease entries after certain error conditions. Usually running this is not 3860necessary, and is only required if upgrade notes or support personnel suggest 3861it.`, 3862 }, 3863 3864 "wrap": { 3865 "Response-wraps an arbitrary JSON object.", 3866 `Round trips the given input data into a response-wrapped token.`, 3867 }, 3868 3869 "wrappubkey": { 3870 "Returns pubkeys used in some wrapping formats.", 3871 "Returns pubkeys used in some wrapping formats.", 3872 }, 3873 3874 "unwrap": { 3875 "Unwraps a response-wrapped token.", 3876 `Unwraps a response-wrapped token. Unlike simply reading from cubbyhole/response, 3877 this provides additional validation on the token, and rather than a JSON-escaped 3878 string, the returned response is the exact same as the contained wrapped response.`, 3879 }, 3880 3881 "wraplookup": { 3882 "Looks up the properties of a response-wrapped token.", 3883 `Returns the creation TTL and creation time of a response-wrapped token.`, 3884 }, 3885 3886 "rewrap": { 3887 "Rotates a response-wrapped token.", 3888 `Rotates a response-wrapped token; the output is a new token with the same 3889 response wrapped inside and the same creation TTL. The original token is revoked.`, 3890 }, 3891 "audited-headers-name": { 3892 "Configures the headers sent to the audit logs.", 3893 ` 3894This path responds to the following HTTP methods. 3895 3896 GET /<name> 3897 Returns the setting for the header with the given name. 3898 3899 POST /<name> 3900 Enable auditing of the given header. 3901 3902 DELETE /<path> 3903 Disable auditing of the given header. 3904 `, 3905 }, 3906 "audited-headers": { 3907 "Lists the headers configured to be audited.", 3908 `Returns a list of headers that have been configured to be audited.`, 3909 }, 3910 "plugin-catalog-list-all": { 3911 "Lists all the plugins known to Vault", 3912 ` 3913This path responds to the following HTTP methods. 3914 LIST / 3915 Returns a list of names of configured plugins. 3916 `, 3917 }, 3918 "plugin-catalog": { 3919 "Configures the plugins known to Vault", 3920 ` 3921This path responds to the following HTTP methods. 3922 LIST / 3923 Returns a list of names of configured plugins. 3924 3925 GET /<name> 3926 Retrieve the metadata for the named plugin. 3927 3928 PUT /<name> 3929 Add or update plugin. 3930 3931 DELETE /<name> 3932 Delete the plugin with the given name. 3933 `, 3934 }, 3935 "plugin-catalog_name": { 3936 "The name of the plugin", 3937 "", 3938 }, 3939 "plugin-catalog_type": { 3940 "The type of the plugin, may be auth, secret, or database", 3941 "", 3942 }, 3943 "plugin-catalog_sha-256": { 3944 `The SHA256 sum of the executable used in the 3945command field. This should be HEX encoded.`, 3946 "", 3947 }, 3948 "plugin-catalog_command": { 3949 `The command used to start the plugin. The 3950executable defined in this command must exist in vault's 3951plugin directory.`, 3952 "", 3953 }, 3954 "plugin-catalog_args": { 3955 `The args passed to plugin command.`, 3956 "", 3957 }, 3958 "plugin-catalog_env": { 3959 `The environment variables passed to plugin command. 3960Each entry is of the form "key=value".`, 3961 "", 3962 }, 3963 "leases": { 3964 `View or list lease metadata.`, 3965 ` 3966This path responds to the following HTTP methods. 3967 3968 PUT / 3969 Retrieve the metadata for the provided lease id. 3970 3971 LIST /<prefix> 3972 Lists the leases for the named prefix. 3973 `, 3974 }, 3975 3976 "leases-list-prefix": { 3977 `The path to list leases under. Example: "aws/creds/deploy"`, 3978 "", 3979 }, 3980 "plugin-reload": { 3981 "Reload mounts that use a particular backend plugin.", 3982 `Reload mounts that use a particular backend plugin. Either the plugin name 3983 or the desired plugin backend mounts must be provided, but not both. In the 3984 case that the plugin name is provided, all mounted paths that use that plugin 3985 backend will be reloaded.`, 3986 }, 3987 "plugin-backend-reload-plugin": { 3988 `The name of the plugin to reload, as registered in the plugin catalog.`, 3989 "", 3990 }, 3991 "plugin-backend-reload-mounts": { 3992 `The mount paths of the plugin backends to reload.`, 3993 "", 3994 }, 3995 "hash": { 3996 "Generate a hash sum for input data", 3997 "Generates a hash sum of the given algorithm against the given input data.", 3998 }, 3999 "random": { 4000 "Generate random bytes", 4001 "This function can be used to generate high-entropy random bytes.", 4002 }, 4003 "listing_visibility": { 4004 "Determines the visibility of the mount in the UI-specific listing endpoint. Accepted value are 'unauth' and ''.", 4005 "", 4006 }, 4007 "passthrough_request_headers": { 4008 "A list of headers to whitelist and pass from the request to the plugin.", 4009 "", 4010 }, 4011 "allowed_response_headers": { 4012 "A list of headers to whitelist and allow a plugin to set on responses.", 4013 "", 4014 }, 4015 "token_type": { 4016 "The type of token to issue (service or batch).", 4017 "", 4018 }, 4019 "raw": { 4020 "Write, Read, and Delete data directly in the Storage backend.", 4021 "", 4022 }, 4023 "internal-ui-mounts": { 4024 "Information about mounts returned according to their tuned visibility. Internal API; its location, inputs, and outputs may change.", 4025 "", 4026 }, 4027 "internal-ui-namespaces": { 4028 "Information about visible child namespaces. Internal API; its location, inputs, and outputs may change.", 4029 `Information about visible child namespaces returned starting from the request's 4030 context namespace and filtered based on access from the client token. Internal API; 4031 its location, inputs, and outputs may change.`, 4032 }, 4033 "internal-ui-resultant-acl": { 4034 "Information about a token's resultant ACL. Internal API; its location, inputs, and outputs may change.", 4035 "", 4036 }, 4037 "metrics": { 4038 "Export the metrics aggregated for telemetry purpose.", 4039 "", 4040 }, 4041 "internal-counters-requests": { 4042 "Count of requests seen by this Vault cluster over time.", 4043 "Count of requests seen by this Vault cluster over time. Not included in count: health checks, UI asset requests, requests forwarded from another cluster.", 4044 }, 4045} 4046