diff --git a/api/sudo_paths.go b/api/sudo_paths.go index 7ecb53454a..493e68c284 100644 --- a/api/sudo_paths.go +++ b/api/sudo_paths.go @@ -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$`), diff --git a/changelog/21772.txt b/changelog/21772.txt new file mode 100644 index 0000000000..2ebdbf3956 --- /dev/null +++ b/changelog/21772.txt @@ -0,0 +1,3 @@ +```release-note:improvement +core: Fix OpenAPI representation and `-output-policy` recognition of some non-standard sudo paths +``` diff --git a/vault/logical_system.go b/vault/logical_system.go index a332276b93..79124863d3 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -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{ diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index cea1c8afbc..44f21c8b41 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -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() diff --git a/vault/token_store.go b/vault/token_store.go index d25aa629df..df5c45990d 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -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) diff --git a/vault/token_store_test.go b/vault/token_store_test.go index 5495cdae3c..068333ac24 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -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) diff --git a/website/content/docs/concepts/policies.mdx b/website/content/docs/concepts/policies.mdx index 13297a6543..2763a9efee 100644 --- a/website/content/docs/concepts/policies.mdx +++ b/website/content/docs/concepts/policies.mdx @@ -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