github.com/hashicorp/vault/sdk@v0.13.0/logical/request.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package logical 5 6 import ( 7 "context" 8 "fmt" 9 "io" 10 "net/http" 11 "strings" 12 "time" 13 14 "github.com/mitchellh/copystructure" 15 ) 16 17 // RequestWrapInfo is a struct that stores information about desired response 18 // and seal wrapping behavior 19 type RequestWrapInfo struct { 20 // Setting to non-zero specifies that the response should be wrapped. 21 // Specifies the desired TTL of the wrapping token. 22 TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl" sentinel:""` 23 24 // The format to use for the wrapped response; if not specified it's a bare 25 // token 26 Format string `json:"format" structs:"format" mapstructure:"format" sentinel:""` 27 28 // A flag to conforming backends that data for a given request should be 29 // seal wrapped 30 SealWrap bool `json:"seal_wrap" structs:"seal_wrap" mapstructure:"seal_wrap" sentinel:""` 31 } 32 33 func (r *RequestWrapInfo) SentinelGet(key string) (interface{}, error) { 34 if r == nil { 35 return nil, nil 36 } 37 switch key { 38 case "ttl": 39 return r.TTL, nil 40 case "ttl_seconds": 41 return int64(r.TTL.Seconds()), nil 42 } 43 44 return nil, nil 45 } 46 47 func (r *RequestWrapInfo) SentinelKeys() []string { 48 return []string{ 49 "ttl", 50 "ttl_seconds", 51 } 52 } 53 54 //go:generate enumer -type=ClientTokenSource -trimprefix=ClientTokenFrom -transform=snake 55 type ClientTokenSource uint32 56 57 const ( 58 NoClientToken ClientTokenSource = iota 59 ClientTokenFromVaultHeader 60 ClientTokenFromAuthzHeader 61 ClientTokenFromInternalAuth 62 ) 63 64 type WALState struct { 65 ClusterID string 66 LocalIndex uint64 67 ReplicatedIndex uint64 68 } 69 70 const indexStateCtxKey = "index_state" 71 72 // IndexStateContext returns a context with an added value holding the index 73 // state that should be populated on writes. 74 func IndexStateContext(ctx context.Context, state *WALState) context.Context { 75 return context.WithValue(ctx, indexStateCtxKey, state) 76 } 77 78 // IndexStateFromContext is a helper to look up if the provided context contains 79 // an index state pointer. 80 func IndexStateFromContext(ctx context.Context) *WALState { 81 s, ok := ctx.Value(indexStateCtxKey).(*WALState) 82 if !ok { 83 return nil 84 } 85 return s 86 } 87 88 // Request is a struct that stores the parameters and context of a request 89 // being made to Vault. It is used to abstract the details of the higher level 90 // request protocol from the handlers. 91 // 92 // Note: Many of these have Sentinel disabled because they are values populated 93 // by the router after policy checks; the token namespace would be the right 94 // place to access them via Sentinel 95 type Request struct { 96 // Id is the uuid associated with each request 97 ID string `json:"id" structs:"id" mapstructure:"id" sentinel:""` 98 99 // If set, the name given to the replication secondary where this request 100 // originated 101 ReplicationCluster string `json:"replication_cluster" structs:"replication_cluster" mapstructure:"replication_cluster" sentinel:""` 102 103 // Operation is the requested operation type 104 Operation Operation `json:"operation" structs:"operation" mapstructure:"operation"` 105 106 // Path is the full path of the request 107 Path string `json:"path" structs:"path" mapstructure:"path" sentinel:""` 108 109 // Request data is an opaque map that must have string keys. 110 Data map[string]interface{} `json:"map" structs:"data" mapstructure:"data"` 111 112 // Storage can be used to durably store and retrieve state. 113 Storage Storage `json:"-" sentinel:""` 114 115 // Secret will be non-nil only for Revoke and Renew operations 116 // to represent the secret that was returned prior. 117 Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret" sentinel:""` 118 119 // Auth will be non-nil only for Renew operations 120 // to represent the auth that was returned prior. 121 Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth" sentinel:""` 122 123 // Headers will contain the http headers from the request. This value will 124 // be used in the audit broker to ensure we are auditing only the allowed 125 // headers. 126 Headers map[string][]string `json:"headers" structs:"headers" mapstructure:"headers" sentinel:""` 127 128 // Connection will be non-nil only for credential providers to 129 // inspect the connection information and potentially use it for 130 // authentication/protection. 131 Connection *Connection `json:"connection" structs:"connection" mapstructure:"connection"` 132 133 // ClientToken is provided to the core so that the identity 134 // can be verified and ACLs applied. This value is passed 135 // through to the logical backends but after being salted and 136 // hashed. 137 ClientToken string `json:"client_token" structs:"client_token" mapstructure:"client_token" sentinel:""` 138 139 // ClientTokenAccessor is provided to the core so that the it can get 140 // logged as part of request audit logging. 141 ClientTokenAccessor string `json:"client_token_accessor" structs:"client_token_accessor" mapstructure:"client_token_accessor" sentinel:""` 142 143 // DisplayName is provided to the logical backend to help associate 144 // dynamic secrets with the source entity. This is not a sensitive 145 // name, but is useful for operators. 146 DisplayName string `json:"display_name" structs:"display_name" mapstructure:"display_name" sentinel:""` 147 148 // MountPoint is provided so that a logical backend can generate 149 // paths relative to itself. The `Path` is effectively the client 150 // request path with the MountPoint trimmed off. 151 MountPoint string `json:"mount_point" structs:"mount_point" mapstructure:"mount_point" sentinel:""` 152 153 // MountType is provided so that a logical backend can make decisions 154 // based on the specific mount type (e.g., if a mount type has different 155 // aliases, generating different defaults depending on the alias) 156 MountType string `json:"mount_type" structs:"mount_type" mapstructure:"mount_type" sentinel:""` 157 158 // MountAccessor is provided so that identities returned by the authentication 159 // backends can be tied to the mount it belongs to. 160 MountAccessor string `json:"mount_accessor" structs:"mount_accessor" mapstructure:"mount_accessor" sentinel:""` 161 162 // mountRunningVersion is used internally to propagate the semantic version 163 // of the mounted plugin as reported by its vault.MountEntry to audit logging 164 mountRunningVersion string 165 166 // mountRunningSha256 is used internally to propagate the encoded sha256 167 // of the mounted plugin as reported its vault.MountEntry to audit logging 168 mountRunningSha256 string 169 170 // mountIsExternalPlugin is used internally to propagate whether 171 // the backend of the mounted plugin is running externally (i.e., over GRPC) 172 // to audit logging 173 mountIsExternalPlugin bool 174 175 // mountClass is used internally to propagate the mount class of the mounted plugin to audit logging 176 mountClass string 177 178 // WrapInfo contains requested response wrapping parameters 179 WrapInfo *RequestWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info" sentinel:""` 180 181 // ClientTokenRemainingUses represents the allowed number of uses left on the 182 // token supplied 183 ClientTokenRemainingUses int `json:"client_token_remaining_uses" structs:"client_token_remaining_uses" mapstructure:"client_token_remaining_uses"` 184 185 // EntityID is the identity of the caller extracted out of the token used 186 // to make this request 187 EntityID string `json:"entity_id" structs:"entity_id" mapstructure:"entity_id" sentinel:""` 188 189 // PolicyOverride indicates that the requestor wishes to override 190 // soft-mandatory Sentinel policies 191 PolicyOverride bool `json:"policy_override" structs:"policy_override" mapstructure:"policy_override"` 192 193 // Whether the request is unauthenticated, as in, had no client token 194 // attached. Useful in some situations where the client token is not made 195 // accessible. 196 Unauthenticated bool `json:"unauthenticated" structs:"unauthenticated" mapstructure:"unauthenticated"` 197 198 // PathLimited indicates that the request path is marked for special-case 199 // request limiting. 200 PathLimited bool `json:"path_limited" structs:"path_limited" mapstructure:"path_limited"` 201 202 // MFACreds holds the parsed MFA information supplied over the API as part of 203 // X-Vault-MFA header 204 MFACreds MFACreds `json:"mfa_creds" structs:"mfa_creds" mapstructure:"mfa_creds" sentinel:""` 205 206 // Cached token entry. This avoids another lookup in request handling when 207 // we've already looked it up at http handling time. Note that this token 208 // has not been "used", as in it will not properly take into account use 209 // count limitations. As a result this field should only ever be used for 210 // transport to a function that would otherwise do a lookup and then 211 // properly use the token. 212 tokenEntry *TokenEntry 213 214 // For replication, contains the last WAL on the remote side after handling 215 // the request, used for best-effort avoidance of stale read-after-write 216 lastRemoteWAL uint64 217 218 // ControlGroup holds the authorizations that have happened on this 219 // request 220 ControlGroup *ControlGroup `json:"control_group" structs:"control_group" mapstructure:"control_group" sentinel:""` 221 222 // ClientTokenSource tells us where the client token was sourced from, so 223 // we can delete it before sending off to plugins 224 ClientTokenSource ClientTokenSource 225 226 // HTTPRequest, if set, can be used to access fields from the HTTP request 227 // that generated this logical.Request object, such as the request body. 228 HTTPRequest *http.Request `json:"-" sentinel:""` 229 230 // ResponseWriter if set can be used to stream a response value to the http 231 // request that generated this logical.Request object. 232 ResponseWriter *HTTPResponseWriter `json:"-" sentinel:""` 233 234 // requiredState is used internally to propagate the X-Vault-Index request 235 // header to later levels of request processing that operate only on 236 // logical.Request. 237 requiredState []string 238 239 // responseState is used internally to propagate the state that should appear 240 // in response headers; it's attached to the request rather than the response 241 // because not all requests yields non-nil responses. 242 responseState *WALState 243 244 // ClientID is the identity of the caller. If the token is associated with an 245 // entity, it will be the same as the EntityID . If the token has no entity, 246 // this will be the sha256(sorted policies + namespace) associated with the 247 // client token. 248 ClientID string `json:"client_id" structs:"client_id" mapstructure:"client_id" sentinel:""` 249 250 // InboundSSCToken is the token that arrives on an inbound request, supplied 251 // by the vault user. 252 InboundSSCToken string 253 254 // When a request has been forwarded, contains information of the host the request was forwarded 'from' 255 ForwardedFrom string `json:"forwarded_from,omitempty"` 256 257 // Name of the chroot namespace for the listener that the request was made against 258 ChrootNamespace string `json:"chroot_namespace,omitempty"` 259 260 // RequestLimiterDisabled tells whether the request context has Request Limiter applied. 261 RequestLimiterDisabled bool `json:"request_limiter_disabled,omitempty"` 262 } 263 264 // Clone returns a deep copy (almost) of the request. 265 // It will set unexported fields which were only previously accessible outside 266 // the package via receiver methods. 267 // NOTE: Request.Connection is NOT deep-copied, due to issues with the results 268 // of copystructure on serial numbers within the x509.Certificate objects. 269 func (r *Request) Clone() (*Request, error) { 270 cpy, err := copystructure.Copy(r) 271 if err != nil { 272 return nil, err 273 } 274 275 req := cpy.(*Request) 276 277 // Add the unexported values that were only retrievable via receivers. 278 // copystructure isn't able to do this, which is why we're doing it manually. 279 req.mountClass = r.MountClass() 280 req.mountRunningVersion = r.MountRunningVersion() 281 req.mountRunningSha256 = r.MountRunningSha256() 282 req.mountIsExternalPlugin = r.MountIsExternalPlugin() 283 // This needs to be overwritten as the internal connection state is not cloned properly 284 // mainly the big.Int serial numbers within the x509.Certificate objects get mangled. 285 req.Connection = r.Connection 286 287 return req, nil 288 } 289 290 // Get returns a data field and guards for nil Data 291 func (r *Request) Get(key string) interface{} { 292 if r.Data == nil { 293 return nil 294 } 295 return r.Data[key] 296 } 297 298 // GetString returns a data field as a string 299 func (r *Request) GetString(key string) string { 300 raw := r.Get(key) 301 s, _ := raw.(string) 302 return s 303 } 304 305 func (r *Request) GoString() string { 306 return fmt.Sprintf("*%#v", *r) 307 } 308 309 func (r *Request) SentinelGet(key string) (interface{}, error) { 310 switch key { 311 case "path": 312 // Sanitize it here so that it's consistent in policies 313 return strings.TrimPrefix(r.Path, "/"), nil 314 315 case "wrapping", "wrap_info": 316 // If the pointer is nil accessing the wrap info is considered 317 // "undefined" so this allows us to instead discover a TTL of zero 318 if r.WrapInfo == nil { 319 return &RequestWrapInfo{}, nil 320 } 321 return r.WrapInfo, nil 322 } 323 324 return nil, nil 325 } 326 327 func (r *Request) SentinelKeys() []string { 328 return []string{ 329 "path", 330 "wrapping", 331 "wrap_info", 332 } 333 } 334 335 func (r *Request) MountRunningVersion() string { 336 return r.mountRunningVersion 337 } 338 339 func (r *Request) SetMountRunningVersion(mountRunningVersion string) { 340 r.mountRunningVersion = mountRunningVersion 341 } 342 343 func (r *Request) MountRunningSha256() string { 344 return r.mountRunningSha256 345 } 346 347 func (r *Request) SetMountRunningSha256(mountRunningSha256 string) { 348 r.mountRunningSha256 = mountRunningSha256 349 } 350 351 func (r *Request) MountIsExternalPlugin() bool { 352 return r.mountIsExternalPlugin 353 } 354 355 func (r *Request) SetMountIsExternalPlugin(mountIsExternalPlugin bool) { 356 r.mountIsExternalPlugin = mountIsExternalPlugin 357 } 358 359 func (r *Request) MountClass() string { 360 return r.mountClass 361 } 362 363 func (r *Request) SetMountClass(mountClass string) { 364 r.mountClass = mountClass 365 } 366 367 func (r *Request) LastRemoteWAL() uint64 { 368 return r.lastRemoteWAL 369 } 370 371 func (r *Request) SetLastRemoteWAL(last uint64) { 372 r.lastRemoteWAL = last 373 } 374 375 func (r *Request) RequiredState() []string { 376 return r.requiredState 377 } 378 379 func (r *Request) SetRequiredState(state []string) { 380 r.requiredState = state 381 } 382 383 func (r *Request) ResponseState() *WALState { 384 return r.responseState 385 } 386 387 func (r *Request) SetResponseState(w *WALState) { 388 r.responseState = w 389 } 390 391 func (r *Request) TokenEntry() *TokenEntry { 392 return r.tokenEntry 393 } 394 395 func (r *Request) SetTokenEntry(te *TokenEntry) { 396 r.tokenEntry = te 397 } 398 399 // RenewRequest creates the structure of the renew request. 400 func RenewRequest(path string, secret *Secret, data map[string]interface{}) *Request { 401 return &Request{ 402 Operation: RenewOperation, 403 Path: path, 404 Data: data, 405 Secret: secret, 406 } 407 } 408 409 // RenewAuthRequest creates the structure of the renew request for an auth. 410 func RenewAuthRequest(path string, auth *Auth, data map[string]interface{}) *Request { 411 return &Request{ 412 Operation: RenewOperation, 413 Path: path, 414 Data: data, 415 Auth: auth, 416 } 417 } 418 419 // RevokeRequest creates the structure of the revoke request. 420 func RevokeRequest(path string, secret *Secret, data map[string]interface{}) *Request { 421 return &Request{ 422 Operation: RevokeOperation, 423 Path: path, 424 Data: data, 425 Secret: secret, 426 } 427 } 428 429 // RollbackRequest creates the structure of the revoke request. 430 func RollbackRequest(path string) *Request { 431 return &Request{ 432 Operation: RollbackOperation, 433 Path: path, 434 Data: make(map[string]interface{}), 435 } 436 } 437 438 // Operation is an enum that is used to specify the type 439 // of request being made 440 type Operation string 441 442 const ( 443 // The operations below are called per path 444 CreateOperation Operation = "create" 445 ReadOperation = "read" 446 UpdateOperation = "update" 447 PatchOperation = "patch" 448 DeleteOperation = "delete" 449 ListOperation = "list" 450 HelpOperation = "help" 451 AliasLookaheadOperation = "alias-lookahead" 452 ResolveRoleOperation = "resolve-role" 453 HeaderOperation = "header" 454 455 // The operations below are called globally, the path is less relevant. 456 RevokeOperation Operation = "revoke" 457 RenewOperation = "renew" 458 RollbackOperation = "rollback" 459 ) 460 461 type MFACreds map[string][]string 462 463 // InitializationRequest stores the parameters and context of an Initialize() 464 // call being made to a logical.Backend. 465 type InitializationRequest struct { 466 // Storage can be used to durably store and retrieve state. 467 Storage Storage 468 } 469 470 type CustomHeader struct { 471 Name string 472 Value string 473 } 474 475 type CtxKeyInFlightRequestID struct{} 476 477 func (c CtxKeyInFlightRequestID) String() string { 478 return "in-flight-request-ID" 479 } 480 481 type CtxKeyInFlightRequestPriority struct{} 482 483 func (c CtxKeyInFlightRequestPriority) String() string { 484 return "in-flight-request-priority" 485 } 486 487 // CtxKeyInFlightTraceID is used for passing a trace ID through request 488 // forwarding. The CtxKeyInFlightRequestID created at the HTTP layer is 489 // propagated on through any forwarded requests using this key. 490 // 491 // Note that this applies to replication service RPCs (including 492 // ForwardingRequest from perf standbys or secondaries). The Forwarding RPC 493 // service may propagate the context but the handling on the active node runs 494 // back through the `http` package handler which builds a new context from HTTP 495 // request properties and creates a fresh request ID. Forwarding RPC is used 496 // exclusively in Community Edition but also in some special cases in Enterprise 497 // such as when forwarding is forced by an HTTP header. 498 type CtxKeyInFlightTraceID struct{} 499 500 func (c CtxKeyInFlightTraceID) String() string { 501 return "in-flight-trace-ID" 502 } 503 504 type CtxKeyRequestRole struct{} 505 506 func (c CtxKeyRequestRole) String() string { 507 return "request-role" 508 } 509 510 // ctxKeyDisableReplicationStatusEndpoints is a custom type used as a key in 511 // context.Context to store the value `true` when the 512 // disable_replication_status_endpoints configuration parameter is set to true 513 // for the listener through which a request was received. 514 type ctxKeyDisableReplicationStatusEndpoints struct{} 515 516 // String returns a string representation of the receiver type. 517 func (c ctxKeyDisableReplicationStatusEndpoints) String() string { 518 return "disable-replication-status-endpoints" 519 } 520 521 // ContextDisableReplicationStatusEndpointsValue examines the provided 522 // context.Context for the disable replication status endpoints value and 523 // returns it as a bool value if it's found along with the ok return value set 524 // to true; otherwise the ok return value is false. 525 func ContextDisableReplicationStatusEndpointsValue(ctx context.Context) (value, ok bool) { 526 value, ok = ctx.Value(ctxKeyDisableReplicationStatusEndpoints{}).(bool) 527 528 return 529 } 530 531 // CreateContextDisableReplicationStatusEndpoints creates a new context.Context 532 // based on the provided parent that also includes the provided value for the 533 // ctxKeyDisableReplicationStatusEndpoints key. 534 func CreateContextDisableReplicationStatusEndpoints(parent context.Context, value bool) context.Context { 535 return context.WithValue(parent, ctxKeyDisableReplicationStatusEndpoints{}, value) 536 } 537 538 // CtxKeyOriginalRequestPath is a custom type used as a key in context.Context 539 // to store the original request path. 540 type ctxKeyOriginalRequestPath struct{} 541 542 // String returns a string representation of the receiver type. 543 func (c ctxKeyOriginalRequestPath) String() string { 544 return "original_request_path" 545 } 546 547 // ContextOriginalRequestPathValue examines the provided context.Context for the 548 // original request path value and returns it as a string value if it's found 549 // along with the ok value set to true; otherwise the ok return value is false. 550 func ContextOriginalRequestPathValue(ctx context.Context) (value string, ok bool) { 551 value, ok = ctx.Value(ctxKeyOriginalRequestPath{}).(string) 552 553 return 554 } 555 556 // CreateContextOriginalRequestPath creates a new context.Context based on the 557 // provided parent that also includes the provided original request path value 558 // for the ctxKeyOriginalRequestPath key. 559 func CreateContextOriginalRequestPath(parent context.Context, value string) context.Context { 560 return context.WithValue(parent, ctxKeyOriginalRequestPath{}, value) 561 } 562 563 type ctxKeyOriginalBody struct{} 564 565 func ContextOriginalBodyValue(ctx context.Context) (io.ReadCloser, bool) { 566 value, ok := ctx.Value(ctxKeyOriginalBody{}).(io.ReadCloser) 567 return value, ok 568 } 569 570 func CreateContextOriginalBody(parent context.Context, body io.ReadCloser) context.Context { 571 return context.WithValue(parent, ctxKeyOriginalBody{}, body) 572 } 573 574 type CtxKeyDisableRequestLimiter struct{} 575 576 func (c CtxKeyDisableRequestLimiter) String() string { 577 return "disable_request_limiter" 578 } 579 580 // ctxKeyRedactionSettings is a custom type used as a key in context.Context to 581 // store the value the redaction settings for the listener that received the 582 // request. 583 type ctxKeyRedactionSettings struct{} 584 585 // String returns a string representation of the receiver type. 586 func (c ctxKeyRedactionSettings) String() string { 587 return "redaction-settings" 588 } 589 590 // CtxRedactionSettingsValue examines the provided context.Context for the 591 // redaction settings value and returns them as a tuple of bool values if they 592 // are found along with the ok return value set to true; otherwise the ok return 593 // value is false. 594 func CtxRedactionSettingsValue(ctx context.Context) (redactVersion, redactAddresses, redactClusterName, ok bool) { 595 value, ok := ctx.Value(ctxKeyRedactionSettings{}).([]bool) 596 if !ok { 597 return false, false, false, false 598 } 599 600 return value[0], value[1], value[2], true 601 } 602 603 // CreatecontextRedactionSettings creates a new context.Context based on the 604 // provided parent that also includes the provided redaction settings values for 605 // the ctxKeyRedactionSettings key. 606 func CreateContextRedactionSettings(parent context.Context, redactVersion, redactAddresses, redactClusterName bool) context.Context { 607 return context.WithValue(parent, ctxKeyRedactionSettings{}, []bool{redactVersion, redactAddresses, redactClusterName}) 608 }