Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/26607.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
core: Fix `redact_version` listener parameter being ignored for some OpenAPI related endpoints.
```
114 changes: 114 additions & 0 deletions http/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@
package http

import (
"net/http"
"strings"
"testing"

"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
"github.com/hashicorp/vault/internalshared/configutil"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
"github.com/hashicorp/vault/version"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -157,3 +165,109 @@ func TestOptions_WithRedactVersion(t *testing.T) {
})
}
}

// TestRedactVersionListener tests that the version will be redacted
// from e.g. sys/health and the OpenAPI response if `redact_version`
// is set on the listener.
func TestRedactVersionListener(t *testing.T) {
conf := &vault.CoreConfig{
EnableUI: false,
EnableRaw: true,
BuiltinRegistry: corehelpers.NewMockBuiltinRegistry(),
}
core, _, token := vault.TestCoreUnsealedWithConfig(t, conf)

// Setup listener without redaction
ln, addr := TestListener(t)
props := &vault.HandlerProperties{
Core: core,
ListenerConfig: &configutil.Listener{
RedactVersion: false,
},
}
TestServerWithListenerAndProperties(t, ln, addr, core, props)
defer ln.Close()
TestServerAuth(t, addr, token)

testRedactVersionEndpoints(t, addr, token, version.Version)

// Setup listener with redaction
ln, addr = TestListener(t)
props.ListenerConfig.RedactVersion = true
TestServerWithListenerAndProperties(t, ln, addr, core, props)
defer ln.Close()
TestServerAuth(t, addr, token)

testRedactVersionEndpoints(t, addr, token, "")
}

// testRedactVersionEndpoints tests the endpoints containing versions
// contain the expected version
func testRedactVersionEndpoints(t *testing.T, addr, token, expectedVersion string) {
client := cleanhttp.DefaultClient()
req, err := http.NewRequest("GET", addr+"/v1/auth/token?help=1", nil)
require.NoError(t, err)

req.Header.Set(consts.AuthHeaderName, token)
resp, err := client.Do(req)
require.NoError(t, err)

testResponseStatus(t, resp, 200)

var actual map[string]interface{}
testResponseBody(t, resp, &actual)

require.NotNil(t, actual["openapi"])
openAPI, ok := actual["openapi"].(map[string]interface{})
require.True(t, ok)

require.NotNil(t, openAPI["info"])
info, ok := openAPI["info"].(map[string]interface{})
require.True(t, ok)

require.NotNil(t, info["version"])
version, ok := info["version"].(string)
require.True(t, ok)
require.Equal(t, expectedVersion, version)

req, err = http.NewRequest("GET", addr+"/v1/sys/internal/specs/openapi", nil)
require.NoError(t, err)

req.Header.Set(consts.AuthHeaderName, "")
resp, err = client.Do(req)
require.NoError(t, err)

testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)

require.NotNil(t, actual["info"])
info, ok = openAPI["info"].(map[string]interface{})
require.True(t, ok)

require.NotNil(t, info["version"])
version, ok = info["version"].(string)
require.True(t, ok)
require.Equal(t, expectedVersion, version)

req, err = http.NewRequest("GET", addr+"/v1/sys/health", nil)
require.NoError(t, err)

req.Header.Set(consts.AuthHeaderName, "")
resp, err = client.Do(req)
require.NoError(t, err)

testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)

require.NotNil(t, actual["version"])
version, ok = actual["version"].(string)
require.True(t, ok)

// sys/health is special and uses a different format to the OpenAPI
// version.GetVersion().VersionNumber() instead of version.Version
// We use substring to make sure the check works anyway.
// In practice, version.GetVersion().VersionNumber() will give something like 1.17.0-beta1
// and version.Version gives something like 1.17.0
require.Truef(t, strings.HasPrefix(version, expectedVersion), "version was not as expected, version=%s, expectedVersion=%s",
version, expectedVersion)
}
8 changes: 6 additions & 2 deletions sdk/framework/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func (b *Backend) HandleRequest(ctx context.Context, req *logical.Request) (*log

// If the path is empty and it is a help operation, handle that.
if req.Path == "" && req.Operation == logical.HelpOperation {
return b.handleRootHelp(req)
return b.handleRootHelp(ctx, req)
}

// Find the matching route
Expand Down Expand Up @@ -548,7 +548,7 @@ func (b *Backend) route(path string) (*Path, map[string]string) {
return nil, nil
}

func (b *Backend) handleRootHelp(req *logical.Request) (*logical.Response, error) {
func (b *Backend) handleRootHelp(ctx context.Context, req *logical.Request) (*logical.Response, error) {
// Build a mapping of the paths and get the paths alphabetized to
// make the output prettier.
pathsMap := make(map[string]*Path)
Expand Down Expand Up @@ -596,6 +596,10 @@ func (b *Backend) handleRootHelp(req *logical.Request) (*logical.Response, error
vaultVersion = env.VaultVersion
}

redactVersion, _, _, _ := logical.CtxRedactionSettingsValue(ctx)
if redactVersion {
vaultVersion = ""
}
doc := NewOASDocument(vaultVersion)
if err := documentPaths(b, requestResponsePrefix, doc); err != nil {
b.Logger().Warn("error generating OpenAPI", "error", err)
Expand Down
4 changes: 4 additions & 0 deletions sdk/framework/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,10 @@ func (p *Path) helpCallback(b *Backend) OperationFunc {
vaultVersion = env.VaultVersion
}
}
redactVersion, _, _, _ := logical.CtxRedactionSettingsValue(ctx)
if redactVersion {
vaultVersion = ""
}
doc := NewOASDocument(vaultVersion)
if err := documentPath(p, b, requestResponsePrefix, doc); err != nil {
b.Logger().Warn("error generating OpenAPI", "error", err)
Expand Down
8 changes: 7 additions & 1 deletion vault/logical_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -5259,8 +5259,14 @@ func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Re

context := d.Get("context").(string)

vaultVersion := version.Version
redactVersion, _, _, _ := logical.CtxRedactionSettingsValue(ctx)
if redactVersion {
vaultVersion = ""
}

// Set up target document
doc := framework.NewOASDocument(version.Version)
doc := framework.NewOASDocument(vaultVersion)

// Generic mount paths will primarily be used for code generation purposes.
// This will result in parameterized mount paths being returned instead of
Expand Down