Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sudo paths missing from OpenAPI and docs #21772

Merged
merged 9 commits into from
Jul 19, 2023
6 changes: 3 additions & 3 deletions api/sudo_paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
// 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/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$`),
Expand Down Expand Up @@ -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$`),
Expand Down
3 changes: 3 additions & 0 deletions changelog/21772.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
core: Fix OpenAPI representation and `-output-policy` recognition of some non-standard sudo paths
```
5 changes: 5 additions & 0 deletions vault/logical_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
36 changes: 0 additions & 36 deletions vault/logical_system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
10 changes: 6 additions & 4 deletions vault/token_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/",
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewer:

The prior definition of revoke-orphan/* is just plain wrong, so it is fixed.

The prior definition of accessors* technically works, because it uses a needless wildcard - that change is just for precision.


// Most token store items are local since tokens are local, but a
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand Down
13 changes: 7 additions & 6 deletions vault/token_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package vault
import (
"context"
"encoding/json"
"errors"
"fmt"
"path"
"reflect"
Expand Down Expand Up @@ -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()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewer: Since the common sudo-enforcing logic is part of the Core, we need to fix tests which care about the sudo behaviour, and were previously slipping requests directly to the backend, bypassing the Core, to make the request via the proper layers.

This test is the case where the requesting token does have sudo rights, testing the "allowed" case, and the next test below is testing the "denied" case.

t.Fatalf("err: %v\nresp: %#v", err, resp)
}
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions website/content/docs/concepts/policies.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -799,17 +799,18 @@ 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:

Copy link
Contributor Author

@maxb maxb Jul 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewer: Only one line in the table has semantic changes, please use a whitespace-ignoring diff mode to see what is really going on: https://github.com/hashicorp/vault/pull/21772/files?diff=unified&w=1#diff-cc6f6f882b5e1952856decc6cefb2caf95af3ead2ece1d3b3cd1073d92c2625e

There are other factual inaccuracies in this table (see issue #20780), but I've refrained from expanding the scope of this PR beyond the specific endpoints I was making code changes to.

| 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 |
Expand Down