Skip to content

Commit ad3208a

Browse files
simonswinebboreham
authored andcommitted
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 d2ff153 commit ad3208a

File tree

5 files changed

+78
-5
lines changed

5 files changed

+78
-5
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## master / unreleased
44

5+
## 1.10.0-rc.1 / 2021-07-21
6+
7+
* [CHANGE] Prevent path traversal attack from users able to control the HTTP header `X-Scope-OrgID`. #4375 (CVE-2021-36157)
8+
* Users only have control of the HTTP header when Cortex is not frontend by an auth proxy validating the tenant IDs
9+
510
## 1.10.0-rc.0 / 2021-06-28
611

712
* [CHANGE] Enable strict JSON unmarshal for `pkg/util/validation.Limits` struct. The custom `UnmarshalJSON()` will now fail if the input has unknown fields. #4298

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)