Fix sudo paths missing from OpenAPI and docs (#21772)

* Fix sudo paths missing from OpenAPI and docs

Various sudo (a.k.a. root-protected) paths are implemented in
non-standard ways, and as a result:

* are not declared as x-vault-sudo in the OpenAPI spec

* and as a result of that, are not included in the hardcoded patterns
  powering the Vault CLI `-output-policy` flag

* and in some cases are missing from the table of all sudo paths in the
  docs too

Fix these problems by:

* Adding `seal` and `step-down` to the list of root paths for the system
  backend. They don't need to be there for enforcement, as those two
  special endpoints bypass the standard request handling code, but they
  do need to be there for the OpenAPI generator to be able to know they
  require sudo.

  The way in which those two endpoints do things differently can be
  observed in the code search results for `RootPrivsRequired`:
  https://github.com/search?q=repo%3Ahashicorp%2Fvault%20RootPrivsRequired&type=code

* Fix the implementation of `auth/token/revoke-orphan` to implement
  endpoint sudo requirements in the standard way. Currently, it has an
  **incorrect** path declared in the special paths metadata, and then
  compensates with custom code throwing an error within the request
  handler function itself.

* changelog

* As discussed in PR, delete test which is just testing equality of a constant

* Restore sudo check as requested, and add comment

* Update vault/token_store.go

Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com>

---------

Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com>
This commit is contained in:
Max Bowsher
2023-07-19 17:28:17 +01:00
committed by GitHub
parent def7c1b0a7
commit 188bdca4bd
7 changed files with 58 additions and 82 deletions

View File

@@ -14,8 +14,8 @@ import (
// path matches that path or not (useful specifically for the paths that
// contain templated fields.)
var sudoPaths = map[string]*regexp.Regexp{
"/auth/token/accessors": regexp.MustCompile(`^/auth/token/accessors/?$`),
// TODO /auth/token/revoke-orphan requires sudo but isn't represented as such in the OpenAPI spec
"/auth/token/accessors": regexp.MustCompile(`^/auth/token/accessors/?$`),
"/auth/token/revoke-orphan": regexp.MustCompile(`^/auth/token/revoke-orphan$`),
"/pki/root": regexp.MustCompile(`^/pki/root$`),
"/pki/root/sign-self-issued": regexp.MustCompile(`^/pki/root/sign-self-issued$`),
"/sys/audit": regexp.MustCompile(`^/sys/audit$`),
@@ -45,8 +45,8 @@ var sudoPaths = map[string]*regexp.Regexp{
"/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`),
"/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`),
"/sys/rotate": regexp.MustCompile(`^/sys/rotate$`),
// TODO /sys/seal requires sudo but isn't represented as such in the OpenAPI spec
// TODO /sys/step-down requires sudo but isn't represented as such in the OpenAPI spec
"/sys/seal": regexp.MustCompile(`^/sys/seal$`),
"/sys/step-down": regexp.MustCompile(`^/sys/step-down$`),
// enterprise-only paths
"/sys/replication/dr/primary/secondary-token": regexp.MustCompile(`^/sys/replication/dr/primary/secondary-token$`),

3
changelog/21772.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:improvement
core: Fix OpenAPI representation and `-output-policy` recognition of some non-standard sudo paths
```

View File

@@ -120,6 +120,11 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend {
"storage/raft/snapshot-auto/config/*",
"leases",
"internal/inspect/*",
// sys/seal and sys/step-down actually have their sudo requirement enforced through hardcoding
// PolicyCheckOpts.RootPrivsRequired in dedicated calls to Core.performPolicyChecks, but we still need
// to declare them here so that the generated OpenAPI spec gets their sudo status correct.
"seal",
"step-down",
},
Unauthenticated: []string{

View File

@@ -41,42 +41,6 @@ import (
"github.com/mitchellh/mapstructure"
)
func TestSystemBackend_RootPaths(t *testing.T) {
expected := []string{
"auth/*",
"remount",
"audit",
"audit/*",
"raw",
"raw/*",
"replication/primary/secondary-token",
"replication/performance/primary/secondary-token",
"replication/dr/primary/secondary-token",
"replication/reindex",
"replication/dr/reindex",
"replication/performance/reindex",
"rotate",
"config/cors",
"config/auditing/*",
"config/ui/headers/*",
"plugins/catalog/*",
"revoke-prefix/*",
"revoke-force/*",
"leases/revoke-prefix/*",
"leases/revoke-force/*",
"leases/lookup/*",
"storage/raft/snapshot-auto/config/*",
"leases",
"internal/inspect/*",
}
b := testSystemBackend(t)
actual := b.SpecialPaths().Root
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: mismatch\nexpected:\n%#v\ngot:\n%#v", expected, actual)
}
}
func TestSystemConfigCORS(t *testing.T) {
b := testSystemBackend(t)
paths := b.(*SystemBackend).configPaths()

View File

@@ -783,8 +783,8 @@ func NewTokenStore(ctx context.Context, logger log.Logger, core *Core, config *l
PathsSpecial: &logical.Paths{
Root: []string{
"revoke-orphan/*",
"accessors*",
"revoke-orphan",
"accessors/",
},
// Most token store items are local since tokens are local, but a
@@ -3285,8 +3285,8 @@ func (ts *TokenStore) revokeCommon(ctx context.Context, req *logical.Request, da
return nil, nil
}
// handleRevokeOrphan handles the auth/token/revoke-orphan/id path for revocation of tokens
// in a way that leaves child tokens orphaned. Normally, using sys/revoke/leaseID will revoke
// handleRevokeOrphan handles the auth/token/revoke-orphan path for revocation of tokens
// in a way that leaves child tokens orphaned. Normally, using sys/leases/revoke/{lease_id} will revoke
// the token and all children.
func (ts *TokenStore) handleRevokeOrphan(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Parse the id
@@ -3295,6 +3295,8 @@ func (ts *TokenStore) handleRevokeOrphan(ctx context.Context, req *logical.Reque
return logical.ErrorResponse("missing token ID"), logical.ErrInvalidRequest
}
// TODO #21772 makes the sudo check below redundant, by correcting the TokenStore's PathsSpecial.Root to match this endpoint
// Check if the client token has sudo/root privileges for the requested path
isSudo := ts.System().(extendedSystemView).SudoPrivilege(ctx, req.MountPoint+req.Path, req.ClientToken)

View File

@@ -6,6 +6,7 @@ package vault
import (
"context"
"encoding/json"
"errors"
"fmt"
"path"
"reflect"
@@ -2325,12 +2326,12 @@ func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) {
testMakeServiceTokenViaBackend(t, ts, root, "child", "60s", []string{"root", "foo"})
testMakeServiceTokenViaBackend(t, ts, "child", "sub-child", "50s", []string{"foo"})
req := logical.TestRequest(t, logical.UpdateOperation, "revoke-orphan")
req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/revoke-orphan")
req.Data = map[string]interface{}{
"token": "child",
}
req.ClientToken = root
resp, err := ts.HandleRequest(namespace.RootContext(nil), req)
resp, err := c.HandleRequest(namespace.RootContext(nil), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err: %v\nresp: %#v", err, resp)
}
@@ -2384,14 +2385,14 @@ func TestTokenStore_HandleRequest_RevokeOrphan_NonRoot(t *testing.T) {
t.Fatalf("bad: %v", out)
}
req := logical.TestRequest(t, logical.UpdateOperation, "revoke-orphan")
req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/revoke-orphan")
req.Data = map[string]interface{}{
"token": "child",
}
req.ClientToken = "child"
resp, err := ts.HandleRequest(namespace.RootContext(nil), req)
if err != logical.ErrInvalidRequest {
t.Fatalf("did not get error when non-root revoking itself with orphan flag; resp is %#v", resp)
resp, err := c.HandleRequest(namespace.RootContext(nil), req)
if !errors.Is(err, logical.ErrPermissionDenied) {
t.Fatalf("did not get expected error when non-root revoking itself with orphan flag; resp is %#v; err is %#v", resp, err)
}
time.Sleep(200 * time.Millisecond)

View File

@@ -799,44 +799,45 @@ authenticated user.
## Root protected API endpoints
~> **Note:** Vault treats the HTTP POST and PUT verbs as equivalent, so for each mention
of POST in the table above, PUT may also be used. Vault uses the non-standard LIST HTTP
of POST in the table below, PUT may also be used. Vault uses the non-standard LIST HTTP
verb, but also allows list requests to be made using the GET verb along with `?list=true`
as a query parameter, so for each mention of LIST in the table above, GET with `?list=true`
may also be used.
The following paths requires a root token or `sudo` capability in the policy:
| Path | HTTP verb | Description |
| -------------------------------------------------------------------------------------------------------------------------------------------------------| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| [auth/token/accessors](/vault/api-docs/auth/token#list-accessors) | LIST | List token accessors for all current Vault service tokens |
| [auth/token/create](/vault/api-docs/auth/token#create-token) | POST | Create a periodic or an orphan token (`period` or `no_parent`) option |
| [pki/root](/vault/api-docs/secret/pki#delete-all-issuers-and-keys) | DELETE | Delete the current CA key ([pki secrets engine](/vault/docs/secrets/pki)) |
| [pki/root/sign-self-issued](/vault/api-docs/secret/pki#sign-self-issued) | POST | Use the configured CA certificate to sign a self-issued certificate ([pki secrets engine](/vault/docs/secrets/pki)) |
| [sys/audit](/vault/api-docs/system/audit) | GET | List enabled audit devices |
| [sys/audit/:path](/vault/api-docs/system/audit) | POST, DELETE | Enable or remove an audit device |
| [sys/auth/:path](/vault/api-docs/system/auth) | GET, POST, DELETE | Manage the auth methods (enable, read, and delete) |
| [sys/auth/:path/tune](/vault/api-docs/system/auth#tune-auth-method) | GET, POST | Manage the auth methods (enable, read, delete, and tune) |
| [sys/config/auditing/request-headers](/vault/api-docs/system/config-auditing) | GET | List the request headers that are configured to be audited |
| [sys/config/auditing/request-headers/:name](/vault/api-docs/system/config-auditing) | GET, POST, DELETE | Manage the auditing headers (create, update, read and delete) |
| [sys/config/cors](/vault/api-docs/system/config-cors) | GET, POST, DELETE | Configure CORS setting |
| [sys/config/ui/headers](/vault/api-docs/system/config-ui) | GET, LIST | Configure the UI settings |
| [sys/config/ui/headers/:name](/vault/api-docs/system/config-ui#name) | POST, DELETE | Configure custom HTTP headers to be served with the UI |
| [sys/internal/inspect/router/:tag](/vault/api-docs/system/inspect/router) | GET | Inspect the internal components of Vault's router. `tag` must be one of root, uuid, accessor, or storage |
| [sys/leases/lookup/:prefix](/vault/api-docs/system/leases#list-leases) | LIST | List lease IDs |
| [sys/leases/revoke-force/:prefix](/vault/api-docs/system/leases#revoke-force) | POST | Revoke all secrets or tokens ignoring backend errors |
| [sys/leases/revoke-prefix/:prefix](/vault/api-docs/system/leases#revoke-prefix) | POST | Revoke all secrets generated under a given prefix |
| [sys/plugins/catalog/:type/:name](/vault/api-docs/system/plugins-catalog#register-plugin) | GET, POST, DELETE | Register a new plugin, or read/remove an existing plugin |
| [sys/raw:path](/vault/api-docs/system/raw) | GET, POST, DELETE | Used to access the raw underlying store in Vault |
| [sys/raw:prefix](/vault/api-docs/system/raw#list-raw) | GET, LIST | Returns a list keys for a given path prefix |
| [sys/remount](/vault/api-docs/system/remount) | POST | Moves an already-mounted backend to a new mount point |
| [sys/replication/reindex](/vault/api-docs/system/replication#reindex-replication) | POST | Reindex the local data storage |
| [sys/replication/performance/primary/secondary-token](/vault/api-docs/system/replication/replication-performance#generate-performance-secondary-token) | POST | Generate a performance secondary activation token |
| [sys/replication/dr/primary/secondary-token](/vault/api-docs/system/replication/replication-dr#generate-dr-secondary-token) | POST | Generate a DR secondary activation token |
| [sys/rotate](/vault/api-docs/system/rotate) | POST | Trigger a rotation of the backend encryption key |
| [sys/seal](/vault/api-docs/system/seal) | POST | Seals the Vault |
| [sys/step-down](/vault/api-docs/system/step-down) | POST | Forces a node to give up active status |
| [sys/storage/raft/snapshot-auto/config](/vault/api-docs/system/storage/raftautosnapshots#list-automated-snapshots-configs) | LIST | Lists named configurations |
| [sys/storage/raft/snapshot-auto/config/:name](/vault/api-docs/system/storage/raftautosnapshots) | GET, POST, DELETE | Creates or updates a named configuration |
| Path | HTTP verb | Description |
|--------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|---------------------------------------------------------------------------------------------------------------------|
| [auth/token/accessors](/vault/api-docs/auth/token#list-accessors) | LIST | List token accessors for all current Vault service tokens |
| [auth/token/create](/vault/api-docs/auth/token#create-token) | POST | Create a periodic or an orphan token (`period` or `no_parent`) option |
| [auth/token/revoke-orphan](/vault/api-docs/auth/token#revoke-token-and-orphan-children) | POST | Revoke a token but not its child tokens, which will be orphaned |
| [pki/root](/vault/api-docs/secret/pki#delete-all-issuers-and-keys) | DELETE | Delete the current CA key ([pki secrets engine](/vault/docs/secrets/pki)) |
| [pki/root/sign-self-issued](/vault/api-docs/secret/pki#sign-self-issued) | POST | Use the configured CA certificate to sign a self-issued certificate ([pki secrets engine](/vault/docs/secrets/pki)) |
| [sys/audit](/vault/api-docs/system/audit) | GET | List enabled audit devices |
| [sys/audit/:path](/vault/api-docs/system/audit) | POST, DELETE | Enable or remove an audit device |
| [sys/auth/:path](/vault/api-docs/system/auth) | GET, POST, DELETE | Manage the auth methods (enable, read, and delete) |
| [sys/auth/:path/tune](/vault/api-docs/system/auth#tune-auth-method) | GET, POST | Manage the auth methods (enable, read, delete, and tune) |
| [sys/config/auditing/request-headers](/vault/api-docs/system/config-auditing) | GET | List the request headers that are configured to be audited |
| [sys/config/auditing/request-headers/:name](/vault/api-docs/system/config-auditing) | GET, POST, DELETE | Manage the auditing headers (create, update, read and delete) |
| [sys/config/cors](/vault/api-docs/system/config-cors) | GET, POST, DELETE | Configure CORS setting |
| [sys/config/ui/headers](/vault/api-docs/system/config-ui) | GET, LIST | Configure the UI settings |
| [sys/config/ui/headers/:name](/vault/api-docs/system/config-ui#name) | POST, DELETE | Configure custom HTTP headers to be served with the UI |
| [sys/internal/inspect/router/:tag](/vault/api-docs/system/inspect/router) | GET | Inspect the internal components of Vault's router. `tag` must be one of root, uuid, accessor, or storage |
| [sys/leases/lookup/:prefix](/vault/api-docs/system/leases#list-leases) | LIST | List lease IDs |
| [sys/leases/revoke-force/:prefix](/vault/api-docs/system/leases#revoke-force) | POST | Revoke all secrets or tokens ignoring backend errors |
| [sys/leases/revoke-prefix/:prefix](/vault/api-docs/system/leases#revoke-prefix) | POST | Revoke all secrets generated under a given prefix |
| [sys/plugins/catalog/:type/:name](/vault/api-docs/system/plugins-catalog#register-plugin) | GET, POST, DELETE | Register a new plugin, or read/remove an existing plugin |
| [sys/raw:path](/vault/api-docs/system/raw) | GET, POST, DELETE | Used to access the raw underlying store in Vault |
| [sys/raw:prefix](/vault/api-docs/system/raw#list-raw) | GET, LIST | Returns a list keys for a given path prefix |
| [sys/remount](/vault/api-docs/system/remount) | POST | Moves an already-mounted backend to a new mount point |
| [sys/replication/reindex](/vault/api-docs/system/replication#reindex-replication) | POST | Reindex the local data storage |
| [sys/replication/performance/primary/secondary-token](/vault/api-docs/system/replication/replication-performance#generate-performance-secondary-token) | POST | Generate a performance secondary activation token |
| [sys/replication/dr/primary/secondary-token](/vault/api-docs/system/replication/replication-dr#generate-dr-secondary-token) | POST | Generate a DR secondary activation token |
| [sys/rotate](/vault/api-docs/system/rotate) | POST | Trigger a rotation of the backend encryption key |
| [sys/seal](/vault/api-docs/system/seal) | POST | Seals the Vault |
| [sys/step-down](/vault/api-docs/system/step-down) | POST | Forces a node to give up active status |
| [sys/storage/raft/snapshot-auto/config](/vault/api-docs/system/storage/raftautosnapshots#list-automated-snapshots-configs) | LIST | Lists named configurations |
| [sys/storage/raft/snapshot-auto/config/:name](/vault/api-docs/system/storage/raftautosnapshots) | GET, POST, DELETE | Creates or updates a named configuration |
### Tokens