Skip to content

Commit d9e1f81

Browse files
authored
Restrict path segments in TenantIDs (#4375)
This prevents paths generated from TenantIDs to become vulnerable to path traversal attacks. CVE-2021-36157 Signed-off-by: Christian Simon <[email protected]>
1 parent 1e4e0ca commit d9e1f81

File tree

5 files changed

+75
-5
lines changed

5 files changed

+75
-5
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* [CHANGE] Update Go version to 1.16.6. #4362
77
* [CHANGE] Querier / ruler: Change `-querier.max-fetched-chunks-per-query` configuration to limit to maximum number of chunks that can be fetched in a single query. The number of chunks fetched by ingesters AND long-term storare combined should not exceed the value configured on `-querier.max-fetched-chunks-per-query`. #4260
88
* [CHANGE] Memberlist: the `memberlist_kv_store_value_bytes` has been removed due to values no longer being stored in-memory as encoded bytes. #4345
9+
* [CHANGE] Prevent path traversal attack from users able to control the HTTP header `X-Scope-OrgID`. #4375 (CVE-2021-36157)
10+
* Users only have control of the HTTP header when Cortex is not frontend by an auth proxy validating the tenant IDs
911
* [ENHANCEMENT] Add timeout for waiting on compactor to become ACTIVE in the ring. #4262
1012
* [ENHANCEMENT] Reduce memory used by streaming queries, particularly in ruler. #4341
1113
* [ENHANCEMENT] Ring: allow experimental configuration of disabling of heartbeat timeouts by setting the relevant configuration value to zero. Applies to the following: #4342

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ lint:
176176
# Configured via .golangci.yml.
177177
golangci-lint run
178178

179-
# Ensure no blacklisted package is imported.
179+
# Ensure no blocklisted package is imported.
180180
GOFLAGS="-tags=requires_docker" faillint -paths "github.com/bmizerany/assert=github.com/stretchr/testify/assert,\
181181
golang.org/x/net/context=context,\
182182
sync/atomic=go.uber.org/atomic,\

docs/guides/limitations.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The following character sets are generally **safe for use in the tenant ID**:
2424
- Exclamation point (`!`)
2525
- Hyphen (`-`)
2626
- Underscore (`_`)
27-
- Period (`.`)
27+
- Single Period (`.`), but the tenant IDs `.` and `..` is considered invalid
2828
- Asterisk (`*`)
2929
- Single quote (`'`)
3030
- Open parenthesis (`(`)

pkg/tenant/resolver.go

+29-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tenant
22

33
import (
44
"context"
5+
"errors"
56
"net/http"
67
"strings"
78

@@ -59,14 +60,36 @@ func NewSingleResolver() *SingleResolver {
5960
type SingleResolver struct {
6061
}
6162

63+
// containsUnsafePathSegments will return true if the string is a directory
64+
// reference like `.` and `..` or if any path separator character like `/` and
65+
// `\` can be found.
66+
func containsUnsafePathSegments(id string) bool {
67+
// handle the relative reference to current and parent path.
68+
if id == "." || id == ".." {
69+
return true
70+
}
71+
72+
return strings.ContainsAny(id, "\\/")
73+
}
74+
75+
var errInvalidTenantID = errors.New("invalid tenant ID")
76+
6277
func (t *SingleResolver) TenantID(ctx context.Context) (string, error) {
6378
//lint:ignore faillint wrapper around upstream method
64-
return user.ExtractOrgID(ctx)
79+
id, err := user.ExtractOrgID(ctx)
80+
if err != nil {
81+
return "", err
82+
}
83+
84+
if containsUnsafePathSegments(id) {
85+
return "", errInvalidTenantID
86+
}
87+
88+
return id, nil
6589
}
6690

6791
func (t *SingleResolver) TenantIDs(ctx context.Context) ([]string, error) {
68-
//lint:ignore faillint wrapper around upstream method
69-
orgID, err := user.ExtractOrgID(ctx)
92+
orgID, err := t.TenantID(ctx)
7093
if err != nil {
7194
return nil, err
7295
}
@@ -109,6 +132,9 @@ func (t *MultiResolver) TenantIDs(ctx context.Context) ([]string, error) {
109132
if err := ValidTenantID(orgID); err != nil {
110133
return nil, err
111134
}
135+
if containsUnsafePathSegments(orgID) {
136+
return nil, errInvalidTenantID
137+
}
112138
}
113139

114140
return NormalizeTenantIDs(orgIDs), nil

pkg/tenant/resolver_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ var commonResolverTestCases = []resolverTestCase{
6464
tenantID: "tenant-a",
6565
tenantIDs: []string{"tenant-a"},
6666
},
67+
{
68+
name: "parent-dir",
69+
headerValue: strptr(".."),
70+
errTenantID: errInvalidTenantID,
71+
errTenantIDs: errInvalidTenantID,
72+
},
73+
{
74+
name: "current-dir",
75+
headerValue: strptr("."),
76+
errTenantID: errInvalidTenantID,
77+
errTenantIDs: errInvalidTenantID,
78+
},
6779
}
6880

6981
func TestSingleResolver(t *testing.T) {
@@ -75,6 +87,18 @@ func TestSingleResolver(t *testing.T) {
7587
tenantID: "tenant-a|tenant-b",
7688
tenantIDs: []string{"tenant-a|tenant-b"},
7789
},
90+
{
91+
name: "containing-forward-slash",
92+
headerValue: strptr("forward/slash"),
93+
errTenantID: errInvalidTenantID,
94+
errTenantIDs: errInvalidTenantID,
95+
},
96+
{
97+
name: "containing-backward-slash",
98+
headerValue: strptr(`backward\slash`),
99+
errTenantID: errInvalidTenantID,
100+
errTenantIDs: errInvalidTenantID,
101+
},
78102
}...) {
79103
t.Run(tc.name, tc.test(r))
80104
}
@@ -101,6 +125,24 @@ func TestMultiResolver(t *testing.T) {
101125
errTenantID: user.ErrTooManyOrgIDs,
102126
tenantIDs: []string{"tenant-a", "tenant-b"},
103127
},
128+
{
129+
name: "multi-tenant-with-relative-path",
130+
headerValue: strptr("tenant-a|tenant-b|.."),
131+
errTenantID: errInvalidTenantID,
132+
errTenantIDs: errInvalidTenantID,
133+
},
134+
{
135+
name: "containing-forward-slash",
136+
headerValue: strptr("forward/slash"),
137+
errTenantID: &errTenantIDUnsupportedCharacter{pos: 7, tenantID: "forward/slash"},
138+
errTenantIDs: &errTenantIDUnsupportedCharacter{pos: 7, tenantID: "forward/slash"},
139+
},
140+
{
141+
name: "containing-backward-slash",
142+
headerValue: strptr(`backward\slash`),
143+
errTenantID: &errTenantIDUnsupportedCharacter{pos: 8, tenantID: "backward\\slash"},
144+
errTenantIDs: &errTenantIDUnsupportedCharacter{pos: 8, tenantID: "backward\\slash"},
145+
},
104146
}...) {
105147
t.Run(tc.name, tc.test(r))
106148
}

0 commit comments

Comments
 (0)