backport of commit 0e11fbfe59f8d38f36384269019991891bf64400 (#31060)

Co-authored-by: Kuba Wieczorek <kuba.wieczorek@hashicorp.com>
This commit is contained in:
hc-github-team-secure-vault-core 2025-06-20 10:56:59 -07:00 committed by GitHub
parent f1687255b7
commit fbdc4e952f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 69 additions and 5 deletions

View File

@ -15,7 +15,6 @@ import (
"io"
"net/http"
"net/url"
"regexp"
"strings"
"time"
@ -30,6 +29,7 @@ import (
"github.com/hashicorp/go-retryablehttp"
"github.com/hashicorp/go-secure-stdlib/awsutil"
"github.com/hashicorp/go-secure-stdlib/parseutil"
iRegexp "github.com/hashicorp/go-secure-stdlib/regexp"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/pkcs7"
@ -1678,8 +1678,10 @@ func validateVaultHeaderValue(method string, headers http.Header, parsedUrl *url
case http.MethodPost:
if authzHeaders, ok := headers["Authorization"]; ok {
// authzHeader looks like AWS4-HMAC-SHA256 Credential=AKI..., SignedHeaders=host;x-amz-date;x-vault-awsiam-id, Signature=...
// We need to extract out the SignedHeaders
re := regexp.MustCompile(".*SignedHeaders=([^,]+)")
// We need to extract out the SignedHeaders.
// We are using an interned regexp library here to avoid redundant objects and save memory.
re := iRegexp.MustCompileInterned(".*SignedHeaders=([^,]+)")
authzHeader := strings.Join(authzHeaders, ",")
matches := re.FindSubmatch([]byte(authzHeader))
if len(matches) < 1 {

3
changelog/31022.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
core: Improve memory use of path management for namespaces, auth methods, and secrets engines. Now Vault should handle larger numbers of namespaces and multiple instances of the same secrets engine or auth method more efficiently.
```

1
go.mod
View File

@ -116,6 +116,7 @@ require (
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0
github.com/hashicorp/go-secure-stdlib/password v0.1.1
github.com/hashicorp/go-secure-stdlib/permitpool v1.0.0
github.com/hashicorp/go-secure-stdlib/regexp v1.0.0
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3

2
go.sum
View File

@ -1479,6 +1479,8 @@ github.com/hashicorp/go-secure-stdlib/permitpool v1.0.0 h1:U6y5MXGiDVOOtkWJ6o/tu
github.com/hashicorp/go-secure-stdlib/permitpool v1.0.0/go.mod h1:ecDb3o+8D4xtP0nTCufJaAVawHavy5M2eZ64Nq/8/LM=
github.com/hashicorp/go-secure-stdlib/plugincontainer v0.4.1 h1:JY+zGg8gOmslwif1fiCqT5Hu1SikLZQcHkmQhCoA9gY=
github.com/hashicorp/go-secure-stdlib/plugincontainer v0.4.1/go.mod h1:jW3KCTvdPyAdVecOUwiiO2XaYgUJ/isigt++ISkszkY=
github.com/hashicorp/go-secure-stdlib/regexp v1.0.0 h1:08mz6j5MsCG9sf8tvC8Lhboe/ZMiNg41IPSh6unK5T4=
github.com/hashicorp/go-secure-stdlib/regexp v1.0.0/go.mod h1:n/Gj3sYIEEOYds8uKS55bFf7XiYvWN4e+d+UOA7r/YU=
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI=
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=

View File

@ -24,6 +24,7 @@ import (
"github.com/hashicorp/go-kms-wrapping/entropy/v2"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-secure-stdlib/parseutil"
iRegexp "github.com/hashicorp/go-secure-stdlib/regexp"
"github.com/hashicorp/vault/sdk/database/helper/connutil"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/errutil"
@ -537,8 +538,10 @@ func (b *Backend) init() {
p.Pattern = p.Pattern + "$"
}
// Detect the coding error of an invalid Pattern
b.pathsRe[i] = regexp.MustCompile(p.Pattern)
// Detect the coding error of an invalid Pattern. We are using an interned
// regexps library here to save memory, since we can have many instances of the
// same backend.
b.pathsRe[i] = iRegexp.MustCompileInterned(p.Pattern)
}
}

View File

@ -35,6 +35,7 @@ require (
github.com/hashicorp/go-secure-stdlib/password v0.1.1
github.com/hashicorp/go-secure-stdlib/permitpool v1.0.0
github.com/hashicorp/go-secure-stdlib/plugincontainer v0.4.1
github.com/hashicorp/go-secure-stdlib/regexp v1.0.0
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3
github.com/hashicorp/go-sockaddr v1.0.7

View File

@ -215,6 +215,8 @@ github.com/hashicorp/go-secure-stdlib/permitpool v1.0.0 h1:U6y5MXGiDVOOtkWJ6o/tu
github.com/hashicorp/go-secure-stdlib/permitpool v1.0.0/go.mod h1:ecDb3o+8D4xtP0nTCufJaAVawHavy5M2eZ64Nq/8/LM=
github.com/hashicorp/go-secure-stdlib/plugincontainer v0.4.1 h1:JY+zGg8gOmslwif1fiCqT5Hu1SikLZQcHkmQhCoA9gY=
github.com/hashicorp/go-secure-stdlib/plugincontainer v0.4.1/go.mod h1:jW3KCTvdPyAdVecOUwiiO2XaYgUJ/isigt++ISkszkY=
github.com/hashicorp/go-secure-stdlib/regexp v1.0.0 h1:08mz6j5MsCG9sf8tvC8Lhboe/ZMiNg41IPSh6unK5T4=
github.com/hashicorp/go-secure-stdlib/regexp v1.0.0/go.mod h1:n/Gj3sYIEEOYds8uKS55bFf7XiYvWN4e+d+UOA7r/YU=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3 h1:xbrxd0U9XQW8qL1BAz2XrAjAF/P2vcqUTAues9c24B8=

View File

@ -0,0 +1,50 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package loadedsnapshots
import (
"fmt"
"strconv"
"sync"
"testing"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/helper/testhelpers/minimal"
"github.com/stretchr/testify/require"
)
// TestInternedRegexpConcurrentAccess tests that multiple goroutines can
// concurrently access and create multiple instances of the same type of mount
// without causing any issues. This is important to ensure that the interned
// regular expressions used in the mount paths do not cause have concurrency
// faults.
func TestInternedRegexpConcurrentAccess(t *testing.T) {
cluster := minimal.NewTestSoloCluster(t, nil)
client := cluster.Cores[0].Client
// Build mount input
mountInput := &api.MountInput{
Type: "pki",
}
// Mount 10 PKI secrets engine mounts.
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
mountPath := "pki" + strconv.Itoa(i)
wg.Add(1)
go func() {
err := client.Sys().Mount(mountPath, mountInput)
require.NoError(t, err)
// Verify the mount was created
_, err = client.Sys().GetMount(mountPath)
require.NoError(t, err)
_, err = client.Logical().List(fmt.Sprintf("/%s/roles", mountPath))
require.NoError(t, err)
wg.Done()
}()
}
wg.Wait()
}