mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-01 14:47:38 +00:00
app/vmauth: add ability to drop the specified number of /
-delimited prefix parts from request path
This can be done via `drop_src_path_prefix_parts` option at `url_map` and `user` levels. See https://docs.victoriametrics.com/vmauth.html#dropping-request-path-prefix
This commit is contained in:
parent
12cd32fd75
commit
c1f651a9f9
8 changed files with 219 additions and 51 deletions
|
@ -32,6 +32,38 @@ Pass `-help` to `vmauth` in order to see all the supported command-line flags wi
|
||||||
Feel free [contacting us](mailto:info@victoriametrics.com) if you need customized auth proxy for VictoriaMetrics with the support of LDAP, SSO, RBAC, SAML,
|
Feel free [contacting us](mailto:info@victoriametrics.com) if you need customized auth proxy for VictoriaMetrics with the support of LDAP, SSO, RBAC, SAML,
|
||||||
accounting and rate limiting such as [vmgateway](https://docs.victoriametrics.com/vmgateway.html).
|
accounting and rate limiting such as [vmgateway](https://docs.victoriametrics.com/vmgateway.html).
|
||||||
|
|
||||||
|
## Dropping request path prefix
|
||||||
|
|
||||||
|
By default `vmauth` doesn't drop the path prefix from the original request when proxying the request to the matching backend.
|
||||||
|
Sometimes it is needed to drop path prefix before routing the request to the backend. This can be done by specifying the number of `/`-delimited
|
||||||
|
prefix parts to drop from the request path via `drop_src_path_prefix_parts` option at `url_map` level or at `user` level.
|
||||||
|
|
||||||
|
For example, if you need to serve requests to [vmalert](https://docs.victoriametrics.com/vmalert.html) at `/vmalert/` path prefix,
|
||||||
|
while serving requests to [vmagent](https://docs.victoriametrics.com/vmagent.html) at `/vmagent/` path prefix for a particular user,
|
||||||
|
then the following [-auth.config](#auth-config) can be used:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
users:
|
||||||
|
- username: foo
|
||||||
|
url_map:
|
||||||
|
|
||||||
|
# proxy all the requests, which start with `/vmagent/`, to vmagent backend
|
||||||
|
- src_paths:
|
||||||
|
- "/vmagent/.+"
|
||||||
|
|
||||||
|
# drop /vmagent/ path prefix from the original request before proxying it to url_prefix.
|
||||||
|
drop_src_path_prefix_parts: 1
|
||||||
|
url_prefix: "http://vmagent-backend:8429/"
|
||||||
|
|
||||||
|
# proxy all the requests, which start with `/vmalert`, to vmalert backend
|
||||||
|
- src_paths:
|
||||||
|
- "/vmalert/.+"
|
||||||
|
|
||||||
|
# drop /vmalert/ path prefix from the original request before proxying it to url_prefix.
|
||||||
|
drop_src_path_prefix_parts: 1
|
||||||
|
url_prefix: "http://vmalert-backend:8880/"
|
||||||
|
```
|
||||||
|
|
||||||
## Load balancing
|
## Load balancing
|
||||||
|
|
||||||
Each `url_prefix` in the [-auth.config](#auth-config) may contain either a single url or a list of urls.
|
Each `url_prefix` in the [-auth.config](#auth-config) may contain either a single url or a list of urls.
|
||||||
|
|
|
@ -40,18 +40,19 @@ type AuthConfig struct {
|
||||||
|
|
||||||
// UserInfo is user information read from authConfigPath
|
// UserInfo is user information read from authConfigPath
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
Name string `yaml:"name,omitempty"`
|
Name string `yaml:"name,omitempty"`
|
||||||
BearerToken string `yaml:"bearer_token,omitempty"`
|
BearerToken string `yaml:"bearer_token,omitempty"`
|
||||||
Username string `yaml:"username,omitempty"`
|
Username string `yaml:"username,omitempty"`
|
||||||
Password string `yaml:"password,omitempty"`
|
Password string `yaml:"password,omitempty"`
|
||||||
URLPrefix *URLPrefix `yaml:"url_prefix,omitempty"`
|
URLPrefix *URLPrefix `yaml:"url_prefix,omitempty"`
|
||||||
URLMaps []URLMap `yaml:"url_map,omitempty"`
|
URLMaps []URLMap `yaml:"url_map,omitempty"`
|
||||||
HeadersConf HeadersConf `yaml:",inline"`
|
HeadersConf HeadersConf `yaml:",inline"`
|
||||||
MaxConcurrentRequests int `yaml:"max_concurrent_requests,omitempty"`
|
MaxConcurrentRequests int `yaml:"max_concurrent_requests,omitempty"`
|
||||||
DefaultURL *URLPrefix `yaml:"default_url,omitempty"`
|
DefaultURL *URLPrefix `yaml:"default_url,omitempty"`
|
||||||
RetryStatusCodes []int `yaml:"retry_status_codes,omitempty"`
|
RetryStatusCodes []int `yaml:"retry_status_codes,omitempty"`
|
||||||
TLSInsecureSkipVerify *bool `yaml:"tls_insecure_skip_verify,omitempty"`
|
DropSrcPathPrefixParts int `yaml:"drop_src_path_prefix_parts,omitempty"`
|
||||||
TLSCAFile string `yaml:"tls_ca_file,omitempty"`
|
TLSInsecureSkipVerify *bool `yaml:"tls_insecure_skip_verify,omitempty"`
|
||||||
|
TLSCAFile string `yaml:"tls_ca_file,omitempty"`
|
||||||
|
|
||||||
concurrencyLimitCh chan struct{}
|
concurrencyLimitCh chan struct{}
|
||||||
concurrencyLimitReached *metrics.Counter
|
concurrencyLimitReached *metrics.Counter
|
||||||
|
@ -119,10 +120,11 @@ func (h *Header) MarshalYAML() (interface{}, error) {
|
||||||
|
|
||||||
// URLMap is a mapping from source paths to target urls.
|
// URLMap is a mapping from source paths to target urls.
|
||||||
type URLMap struct {
|
type URLMap struct {
|
||||||
SrcPaths []*SrcPath `yaml:"src_paths,omitempty"`
|
SrcPaths []*SrcPath `yaml:"src_paths,omitempty"`
|
||||||
URLPrefix *URLPrefix `yaml:"url_prefix,omitempty"`
|
URLPrefix *URLPrefix `yaml:"url_prefix,omitempty"`
|
||||||
HeadersConf HeadersConf `yaml:",inline"`
|
HeadersConf HeadersConf `yaml:",inline"`
|
||||||
RetryStatusCodes []int `yaml:"retry_status_codes,omitempty"`
|
RetryStatusCodes []int `yaml:"retry_status_codes,omitempty"`
|
||||||
|
DropSrcPathPrefixParts int `yaml:"drop_src_path_prefix_parts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SrcPath represents an src path
|
// SrcPath represents an src path
|
||||||
|
|
|
@ -249,6 +249,8 @@ users:
|
||||||
- http://node1:343/bbb
|
- http://node1:343/bbb
|
||||||
- http://node2:343/bbb
|
- http://node2:343/bbb
|
||||||
tls_insecure_skip_verify: false
|
tls_insecure_skip_verify: false
|
||||||
|
retry_status_codes: [500, 501]
|
||||||
|
drop_src_path_prefix_parts: 1
|
||||||
`, map[string]*UserInfo{
|
`, map[string]*UserInfo{
|
||||||
getAuthToken("", "foo", "bar"): {
|
getAuthToken("", "foo", "bar"): {
|
||||||
Username: "foo",
|
Username: "foo",
|
||||||
|
@ -257,7 +259,9 @@ users:
|
||||||
"http://node1:343/bbb",
|
"http://node1:343/bbb",
|
||||||
"http://node2:343/bbb",
|
"http://node2:343/bbb",
|
||||||
}),
|
}),
|
||||||
TLSInsecureSkipVerify: &insecureSkipVerifyFalse,
|
TLSInsecureSkipVerify: &insecureSkipVerifyFalse,
|
||||||
|
RetryStatusCodes: []int{500, 501},
|
||||||
|
DropSrcPathPrefixParts: 1,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ func processUserRequest(w http.ResponseWriter, r *http.Request, ui *UserInfo) {
|
||||||
|
|
||||||
func processRequest(w http.ResponseWriter, r *http.Request, ui *UserInfo) {
|
func processRequest(w http.ResponseWriter, r *http.Request, ui *UserInfo) {
|
||||||
u := normalizeURL(r.URL)
|
u := normalizeURL(r.URL)
|
||||||
up, hc, retryStatusCodes := ui.getURLPrefixAndHeaders(u)
|
up, hc, retryStatusCodes, dropSrcPathPrefixParts := ui.getURLPrefixAndHeaders(u)
|
||||||
isDefault := false
|
isDefault := false
|
||||||
if up == nil {
|
if up == nil {
|
||||||
if ui.DefaultURL == nil {
|
if ui.DefaultURL == nil {
|
||||||
|
@ -198,7 +198,7 @@ func processRequest(w http.ResponseWriter, r *http.Request, ui *UserInfo) {
|
||||||
query.Set("request_path", u.Path)
|
query.Set("request_path", u.Path)
|
||||||
targetURL.RawQuery = query.Encode()
|
targetURL.RawQuery = query.Encode()
|
||||||
} else { // Update path for regular routes.
|
} else { // Update path for regular routes.
|
||||||
targetURL = mergeURLs(targetURL, u)
|
targetURL = mergeURLs(targetURL, u, dropSrcPathPrefixParts)
|
||||||
}
|
}
|
||||||
ok := tryProcessingRequest(w, r, targetURL, hc, retryStatusCodes, ui.httpTransport)
|
ok := tryProcessingRequest(w, r, targetURL, hc, retryStatusCodes, ui.httpTransport)
|
||||||
bu.put()
|
bu.put()
|
||||||
|
|
|
@ -6,12 +6,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mergeURLs(uiURL, requestURI *url.URL) *url.URL {
|
func mergeURLs(uiURL, requestURI *url.URL, dropSrcPathPrefixParts int) *url.URL {
|
||||||
targetURL := *uiURL
|
targetURL := *uiURL
|
||||||
if strings.HasPrefix(requestURI.Path, "/") {
|
srcPath := dropPrefixParts(requestURI.Path, dropSrcPathPrefixParts)
|
||||||
|
if strings.HasPrefix(srcPath, "/") {
|
||||||
targetURL.Path = strings.TrimSuffix(targetURL.Path, "/")
|
targetURL.Path = strings.TrimSuffix(targetURL.Path, "/")
|
||||||
}
|
}
|
||||||
targetURL.Path += requestURI.Path
|
targetURL.Path += srcPath
|
||||||
requestParams := requestURI.Query()
|
requestParams := requestURI.Query()
|
||||||
// fast path
|
// fast path
|
||||||
if len(requestParams) == 0 {
|
if len(requestParams) == 0 {
|
||||||
|
@ -32,18 +33,34 @@ func mergeURLs(uiURL, requestURI *url.URL) *url.URL {
|
||||||
return &targetURL
|
return &targetURL
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *UserInfo) getURLPrefixAndHeaders(u *url.URL) (*URLPrefix, HeadersConf, []int) {
|
func dropPrefixParts(path string, parts int) string {
|
||||||
|
if parts <= 0 {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
for parts > 0 {
|
||||||
|
path = strings.TrimPrefix(path, "/")
|
||||||
|
n := strings.IndexByte(path, '/')
|
||||||
|
if n < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
path = path[n:]
|
||||||
|
parts--
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UserInfo) getURLPrefixAndHeaders(u *url.URL) (*URLPrefix, HeadersConf, []int, int) {
|
||||||
for _, e := range ui.URLMaps {
|
for _, e := range ui.URLMaps {
|
||||||
for _, sp := range e.SrcPaths {
|
for _, sp := range e.SrcPaths {
|
||||||
if sp.match(u.Path) {
|
if sp.match(u.Path) {
|
||||||
return e.URLPrefix, e.HeadersConf, e.RetryStatusCodes
|
return e.URLPrefix, e.HeadersConf, e.RetryStatusCodes, e.DropSrcPathPrefixParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ui.URLPrefix != nil {
|
if ui.URLPrefix != nil {
|
||||||
return ui.URLPrefix, ui.HeadersConf, ui.RetryStatusCodes
|
return ui.URLPrefix, ui.HeadersConf, ui.RetryStatusCodes, ui.DropSrcPathPrefixParts
|
||||||
}
|
}
|
||||||
return nil, HeadersConf{}, nil
|
return nil, HeadersConf{}, nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeURL(uOrig *url.URL) *url.URL {
|
func normalizeURL(uOrig *url.URL) *url.URL {
|
||||||
|
|
|
@ -7,20 +7,91 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestDropPrefixParts(t *testing.T) {
|
||||||
|
f := func(path string, parts int, expectedResult string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
result := dropPrefixParts(path, parts)
|
||||||
|
if result != expectedResult {
|
||||||
|
t.Fatalf("unexpected result; got %q; want %q", result, expectedResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f("", 0, "")
|
||||||
|
f("", 1, "")
|
||||||
|
f("", 10, "")
|
||||||
|
f("foo", 0, "foo")
|
||||||
|
f("foo", -1, "foo")
|
||||||
|
f("foo", 1, "")
|
||||||
|
|
||||||
|
f("/foo", 0, "/foo")
|
||||||
|
f("/foo/bar", 0, "/foo/bar")
|
||||||
|
f("/foo/bar/baz", 0, "/foo/bar/baz")
|
||||||
|
|
||||||
|
f("foo", 0, "foo")
|
||||||
|
f("foo/bar", 0, "foo/bar")
|
||||||
|
f("foo/bar/baz", 0, "foo/bar/baz")
|
||||||
|
|
||||||
|
f("/foo/", 0, "/foo/")
|
||||||
|
f("/foo/bar/", 0, "/foo/bar/")
|
||||||
|
f("/foo/bar/baz/", 0, "/foo/bar/baz/")
|
||||||
|
|
||||||
|
f("/foo", 1, "")
|
||||||
|
f("/foo/bar", 1, "/bar")
|
||||||
|
f("/foo/bar/baz", 1, "/bar/baz")
|
||||||
|
|
||||||
|
f("foo", 1, "")
|
||||||
|
f("foo/bar", 1, "/bar")
|
||||||
|
f("foo/bar/baz", 1, "/bar/baz")
|
||||||
|
|
||||||
|
f("/foo/", 1, "/")
|
||||||
|
f("/foo/bar/", 1, "/bar/")
|
||||||
|
f("/foo/bar/baz/", 1, "/bar/baz/")
|
||||||
|
|
||||||
|
f("/foo", 2, "")
|
||||||
|
f("/foo/bar", 2, "")
|
||||||
|
f("/foo/bar/baz", 2, "/baz")
|
||||||
|
|
||||||
|
f("foo", 2, "")
|
||||||
|
f("foo/bar", 2, "")
|
||||||
|
f("foo/bar/baz", 2, "/baz")
|
||||||
|
|
||||||
|
f("/foo/", 2, "")
|
||||||
|
f("/foo/bar/", 2, "/")
|
||||||
|
f("/foo/bar/baz/", 2, "/baz/")
|
||||||
|
|
||||||
|
f("/foo", 3, "")
|
||||||
|
f("/foo/bar", 3, "")
|
||||||
|
f("/foo/bar/baz", 3, "")
|
||||||
|
|
||||||
|
f("foo", 3, "")
|
||||||
|
f("foo/bar", 3, "")
|
||||||
|
f("foo/bar/baz", 3, "")
|
||||||
|
|
||||||
|
f("/foo/", 3, "")
|
||||||
|
f("/foo/bar/", 3, "")
|
||||||
|
f("/foo/bar/baz/", 3, "/")
|
||||||
|
|
||||||
|
f("/foo/", 4, "")
|
||||||
|
f("/foo/bar/", 4, "")
|
||||||
|
f("/foo/bar/baz/", 4, "")
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateTargetURLSuccess(t *testing.T) {
|
func TestCreateTargetURLSuccess(t *testing.T) {
|
||||||
f := func(ui *UserInfo, requestURI, expectedTarget, expectedRequestHeaders, expectedResponseHeaders string, expectedRetryStatusCodes []int) {
|
f := func(ui *UserInfo, requestURI, expectedTarget, expectedRequestHeaders, expectedResponseHeaders string,
|
||||||
|
expectedRetryStatusCodes []int, expectedDropSrcPathPrefixParts int) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
u, err := url.Parse(requestURI)
|
u, err := url.Parse(requestURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("cannot parse %q: %s", requestURI, err)
|
t.Fatalf("cannot parse %q: %s", requestURI, err)
|
||||||
}
|
}
|
||||||
u = normalizeURL(u)
|
u = normalizeURL(u)
|
||||||
up, hc, retryStatusCodes := ui.getURLPrefixAndHeaders(u)
|
up, hc, retryStatusCodes, dropSrcPathPrefixParts := ui.getURLPrefixAndHeaders(u)
|
||||||
if up == nil {
|
if up == nil {
|
||||||
t.Fatalf("cannot determie backend: %s", err)
|
t.Fatalf("cannot determie backend: %s", err)
|
||||||
}
|
}
|
||||||
bu := up.getLeastLoadedBackendURL()
|
bu := up.getLeastLoadedBackendURL()
|
||||||
target := mergeURLs(bu.url, u)
|
target := mergeURLs(bu.url, u, dropSrcPathPrefixParts)
|
||||||
bu.put()
|
bu.put()
|
||||||
if target.String() != expectedTarget {
|
if target.String() != expectedTarget {
|
||||||
t.Fatalf("unexpected target; got %q; want %q", target, expectedTarget)
|
t.Fatalf("unexpected target; got %q; want %q", target, expectedTarget)
|
||||||
|
@ -32,11 +103,14 @@ func TestCreateTargetURLSuccess(t *testing.T) {
|
||||||
if !reflect.DeepEqual(retryStatusCodes, expectedRetryStatusCodes) {
|
if !reflect.DeepEqual(retryStatusCodes, expectedRetryStatusCodes) {
|
||||||
t.Fatalf("unexpected retryStatusCodes; got %d; want %d", retryStatusCodes, expectedRetryStatusCodes)
|
t.Fatalf("unexpected retryStatusCodes; got %d; want %d", retryStatusCodes, expectedRetryStatusCodes)
|
||||||
}
|
}
|
||||||
|
if dropSrcPathPrefixParts != expectedDropSrcPathPrefixParts {
|
||||||
|
t.Fatalf("unexpected dropSrcPathPrefixParts; got %d; want %d", dropSrcPathPrefixParts, expectedDropSrcPathPrefixParts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Simple routing with `url_prefix`
|
// Simple routing with `url_prefix`
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
URLPrefix: mustParseURL("http://foo.bar"),
|
URLPrefix: mustParseURL("http://foo.bar"),
|
||||||
}, "", "http://foo.bar/.", "[]", "[]", nil)
|
}, "", "http://foo.bar/.", "[]", "[]", nil, 0)
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
URLPrefix: mustParseURL("http://foo.bar"),
|
URLPrefix: mustParseURL("http://foo.bar"),
|
||||||
HeadersConf: HeadersConf{
|
HeadersConf: HeadersConf{
|
||||||
|
@ -45,29 +119,30 @@ func TestCreateTargetURLSuccess(t *testing.T) {
|
||||||
Value: "aaa",
|
Value: "aaa",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
RetryStatusCodes: []int{503, 501},
|
RetryStatusCodes: []int{503, 501},
|
||||||
}, "/", "http://foo.bar", `[{"bb" "aaa"}]`, `[]`, []int{503, 501})
|
DropSrcPathPrefixParts: 2,
|
||||||
|
}, "/a/b/c", "http://foo.bar/c", `[{"bb" "aaa"}]`, `[]`, []int{503, 501}, 2)
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
URLPrefix: mustParseURL("http://foo.bar/federate"),
|
URLPrefix: mustParseURL("http://foo.bar/federate"),
|
||||||
}, "/", "http://foo.bar/federate", "[]", "[]", nil)
|
}, "/", "http://foo.bar/federate", "[]", "[]", nil, 0)
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
URLPrefix: mustParseURL("http://foo.bar"),
|
URLPrefix: mustParseURL("http://foo.bar"),
|
||||||
}, "a/b?c=d", "http://foo.bar/a/b?c=d", "[]", "[]", nil)
|
}, "a/b?c=d", "http://foo.bar/a/b?c=d", "[]", "[]", nil, 0)
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
URLPrefix: mustParseURL("https://sss:3894/x/y"),
|
URLPrefix: mustParseURL("https://sss:3894/x/y"),
|
||||||
}, "/z", "https://sss:3894/x/y/z", "[]", "[]", nil)
|
}, "/z", "https://sss:3894/x/y/z", "[]", "[]", nil, 0)
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
URLPrefix: mustParseURL("https://sss:3894/x/y"),
|
URLPrefix: mustParseURL("https://sss:3894/x/y"),
|
||||||
}, "/../../aaa", "https://sss:3894/x/y/aaa", "[]", "[]", nil)
|
}, "/../../aaa", "https://sss:3894/x/y/aaa", "[]", "[]", nil, 0)
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
URLPrefix: mustParseURL("https://sss:3894/x/y"),
|
URLPrefix: mustParseURL("https://sss:3894/x/y"),
|
||||||
}, "/./asd/../../aaa?a=d&s=s/../d", "https://sss:3894/x/y/aaa?a=d&s=s%2F..%2Fd", "[]", "[]", nil)
|
}, "/./asd/../../aaa?a=d&s=s/../d", "https://sss:3894/x/y/aaa?a=d&s=s%2F..%2Fd", "[]", "[]", nil, 0)
|
||||||
|
|
||||||
// Complex routing with `url_map`
|
// Complex routing with `url_map`
|
||||||
ui := &UserInfo{
|
ui := &UserInfo{
|
||||||
URLMaps: []URLMap{
|
URLMaps: []URLMap{
|
||||||
{
|
{
|
||||||
SrcPaths: getSrcPaths([]string{"/api/v1/query"}),
|
SrcPaths: getSrcPaths([]string{"/vmsingle/api/v1/query"}),
|
||||||
URLPrefix: mustParseURL("http://vmselect/0/prometheus"),
|
URLPrefix: mustParseURL("http://vmselect/0/prometheus"),
|
||||||
HeadersConf: HeadersConf{
|
HeadersConf: HeadersConf{
|
||||||
RequestHeaders: []Header{
|
RequestHeaders: []Header{
|
||||||
|
@ -87,7 +162,8 @@ func TestCreateTargetURLSuccess(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RetryStatusCodes: []int{503, 500, 501},
|
RetryStatusCodes: []int{503, 500, 501},
|
||||||
|
DropSrcPathPrefixParts: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SrcPaths: getSrcPaths([]string{"/api/v1/write"}),
|
SrcPaths: getSrcPaths([]string{"/api/v1/write"}),
|
||||||
|
@ -105,11 +181,12 @@ func TestCreateTargetURLSuccess(t *testing.T) {
|
||||||
Value: "y",
|
Value: "y",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
RetryStatusCodes: []int{502},
|
RetryStatusCodes: []int{502},
|
||||||
|
DropSrcPathPrefixParts: 2,
|
||||||
}
|
}
|
||||||
f(ui, "/api/v1/query?query=up", "http://vmselect/0/prometheus/api/v1/query?query=up", `[{"xx" "aa"} {"yy" "asdf"}]`, `[{"qwe" "rty"}]`, []int{503, 500, 501})
|
f(ui, "/vmsingle/api/v1/query?query=up", "http://vmselect/0/prometheus/api/v1/query?query=up", `[{"xx" "aa"} {"yy" "asdf"}]`, `[{"qwe" "rty"}]`, []int{503, 500, 501}, 1)
|
||||||
f(ui, "/api/v1/write", "http://vminsert/0/prometheus/api/v1/write", "[]", "[]", nil)
|
f(ui, "/api/v1/write", "http://vminsert/0/prometheus/api/v1/write", "[]", "[]", nil, 0)
|
||||||
f(ui, "/api/v1/query_range", "http://default-server/api/v1/query_range", `[{"bb" "aaa"}]`, `[{"x" "y"}]`, []int{502})
|
f(ui, "/foo/bar/api/v1/query_range", "http://default-server/api/v1/query_range", `[{"bb" "aaa"}]`, `[{"x" "y"}]`, []int{502}, 2)
|
||||||
|
|
||||||
// Complex routing regexp paths in `url_map`
|
// Complex routing regexp paths in `url_map`
|
||||||
ui = &UserInfo{
|
ui = &UserInfo{
|
||||||
|
@ -125,17 +202,17 @@ func TestCreateTargetURLSuccess(t *testing.T) {
|
||||||
},
|
},
|
||||||
URLPrefix: mustParseURL("http://default-server"),
|
URLPrefix: mustParseURL("http://default-server"),
|
||||||
}
|
}
|
||||||
f(ui, "/api/v1/query?query=up", "http://vmselect/0/prometheus/api/v1/query?query=up", "[]", "[]", nil)
|
f(ui, "/api/v1/query?query=up", "http://vmselect/0/prometheus/api/v1/query?query=up", "[]", "[]", nil, 0)
|
||||||
f(ui, "/api/v1/query_range?query=up", "http://vmselect/0/prometheus/api/v1/query_range?query=up", "[]", "[]", nil)
|
f(ui, "/api/v1/query_range?query=up", "http://vmselect/0/prometheus/api/v1/query_range?query=up", "[]", "[]", nil, 0)
|
||||||
f(ui, "/api/v1/label/foo/values", "http://vmselect/0/prometheus/api/v1/label/foo/values", "[]", "[]", nil)
|
f(ui, "/api/v1/label/foo/values", "http://vmselect/0/prometheus/api/v1/label/foo/values", "[]", "[]", nil, 0)
|
||||||
f(ui, "/api/v1/write", "http://vminsert/0/prometheus/api/v1/write", "[]", "[]", nil)
|
f(ui, "/api/v1/write", "http://vminsert/0/prometheus/api/v1/write", "[]", "[]", nil, 0)
|
||||||
f(ui, "/api/v1/foo/bar", "http://default-server/api/v1/foo/bar", "[]", "[]", nil)
|
f(ui, "/api/v1/foo/bar", "http://default-server/api/v1/foo/bar", "[]", "[]", nil, 0)
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
URLPrefix: mustParseURL("http://foo.bar?extra_label=team=dev"),
|
URLPrefix: mustParseURL("http://foo.bar?extra_label=team=dev"),
|
||||||
}, "/api/v1/query", "http://foo.bar/api/v1/query?extra_label=team=dev", "[]", "[]", nil)
|
}, "/api/v1/query", "http://foo.bar/api/v1/query?extra_label=team=dev", "[]", "[]", nil, 0)
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
URLPrefix: mustParseURL("http://foo.bar?extra_label=team=mobile"),
|
URLPrefix: mustParseURL("http://foo.bar?extra_label=team=mobile"),
|
||||||
}, "/api/v1/query?extra_label=team=dev", "http://foo.bar/api/v1/query?extra_label=team%3Dmobile", "[]", "[]", nil)
|
}, "/api/v1/query?extra_label=team=dev", "http://foo.bar/api/v1/query?extra_label=team%3Dmobile", "[]", "[]", nil, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateTargetURLFailure(t *testing.T) {
|
func TestCreateTargetURLFailure(t *testing.T) {
|
||||||
|
@ -146,7 +223,7 @@ func TestCreateTargetURLFailure(t *testing.T) {
|
||||||
t.Fatalf("cannot parse %q: %s", requestURI, err)
|
t.Fatalf("cannot parse %q: %s", requestURI, err)
|
||||||
}
|
}
|
||||||
u = normalizeURL(u)
|
u = normalizeURL(u)
|
||||||
up, hc, retryStatusCodes := ui.getURLPrefixAndHeaders(u)
|
up, hc, retryStatusCodes, dropSrcPathPrefixParts := ui.getURLPrefixAndHeaders(u)
|
||||||
if up != nil {
|
if up != nil {
|
||||||
t.Fatalf("unexpected non-empty up=%#v", up)
|
t.Fatalf("unexpected non-empty up=%#v", up)
|
||||||
}
|
}
|
||||||
|
@ -159,6 +236,9 @@ func TestCreateTargetURLFailure(t *testing.T) {
|
||||||
if retryStatusCodes != nil {
|
if retryStatusCodes != nil {
|
||||||
t.Fatalf("unexpected non-empty retryStatusCodes=%d", retryStatusCodes)
|
t.Fatalf("unexpected non-empty retryStatusCodes=%d", retryStatusCodes)
|
||||||
}
|
}
|
||||||
|
if dropSrcPathPrefixParts != 0 {
|
||||||
|
t.Fatalf("unexpected non-zero dropSrcPathPrefixParts=%d", dropSrcPathPrefixParts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f(&UserInfo{}, "/foo/bar")
|
f(&UserInfo{}, "/foo/bar")
|
||||||
f(&UserInfo{
|
f(&UserInfo{
|
||||||
|
|
|
@ -79,6 +79,7 @@ The sandbox cluster installation is running under the constant load generated by
|
||||||
* FEATURE: [vmalert-tool](https://docs.victoriametrics.com/#vmalert-tool): add `unittest` command to run unittest for alerting and recording rules. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4789) for details.
|
* FEATURE: [vmalert-tool](https://docs.victoriametrics.com/#vmalert-tool): add `unittest` command to run unittest for alerting and recording rules. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4789) for details.
|
||||||
* FEATURE: dashboards/vmalert: add new panel `Missed evaluations` for indicating alerting groups that miss their evaluations.
|
* FEATURE: dashboards/vmalert: add new panel `Missed evaluations` for indicating alerting groups that miss their evaluations.
|
||||||
* FEATURE: all: track requests with wrong auth key and wrong basic auth at `vm_http_request_errors_total` [metric](https://docs.victoriametrics.com/#monitoring) with `reason="wrong_auth_key"` and `reason="wrong_basic_auth"`. See [this issue](https://github.com/victoriaMetrics/victoriaMetrics/issues/4590). Thanks to @venkatbvc for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5166).
|
* FEATURE: all: track requests with wrong auth key and wrong basic auth at `vm_http_request_errors_total` [metric](https://docs.victoriametrics.com/#monitoring) with `reason="wrong_auth_key"` and `reason="wrong_basic_auth"`. See [this issue](https://github.com/victoriaMetrics/victoriaMetrics/issues/4590). Thanks to @venkatbvc for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5166).
|
||||||
|
* FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth.html): add ability to drop the specified number of `/`-delimited prefix parts from the request path before proxying the request to the matching backend. See [these docs](https://docs.victoriametrics.com/vmauth.html#dropping-request-path-prefix).
|
||||||
* FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth.html): add ability to skip TLS verification and to specify TLS Root CA when connecting to backends. See [these docs](https://docs.victoriametrics.com/vmauth.html#backend-tls-setup) and [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5240).
|
* FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth.html): add ability to skip TLS verification and to specify TLS Root CA when connecting to backends. See [these docs](https://docs.victoriametrics.com/vmauth.html#backend-tls-setup) and [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5240).
|
||||||
* FEATURE: `vmstorage`: add `-blockcache.missesBeforeCaching` command-line flag, which can be used for fine-tuning RAM usage for `indexdb/dataBlocks` cache when queries touching big number of time series are executed.
|
* FEATURE: `vmstorage`: add `-blockcache.missesBeforeCaching` command-line flag, which can be used for fine-tuning RAM usage for `indexdb/dataBlocks` cache when queries touching big number of time series are executed.
|
||||||
* FEATURE: add `-loggerMaxArgLen` command-line flag for fine-tuning the maximum lengths of logged args.
|
* FEATURE: add `-loggerMaxArgLen` command-line flag for fine-tuning the maximum lengths of logged args.
|
||||||
|
|
|
@ -43,6 +43,38 @@ Pass `-help` to `vmauth` in order to see all the supported command-line flags wi
|
||||||
Feel free [contacting us](mailto:info@victoriametrics.com) if you need customized auth proxy for VictoriaMetrics with the support of LDAP, SSO, RBAC, SAML,
|
Feel free [contacting us](mailto:info@victoriametrics.com) if you need customized auth proxy for VictoriaMetrics with the support of LDAP, SSO, RBAC, SAML,
|
||||||
accounting and rate limiting such as [vmgateway](https://docs.victoriametrics.com/vmgateway.html).
|
accounting and rate limiting such as [vmgateway](https://docs.victoriametrics.com/vmgateway.html).
|
||||||
|
|
||||||
|
## Dropping request path prefix
|
||||||
|
|
||||||
|
By default `vmauth` doesn't drop the path prefix from the original request when proxying the request to the matching backend.
|
||||||
|
Sometimes it is needed to drop path prefix before routing the request to the backend. This can be done by specifying the number of `/`-delimited
|
||||||
|
prefix parts to drop from the request path via `drop_src_path_prefix_parts` option at `url_map` level or at `user` level.
|
||||||
|
|
||||||
|
For example, if you need to serve requests to [vmalert](https://docs.victoriametrics.com/vmalert.html) at `/vmalert/` path prefix,
|
||||||
|
while serving requests to [vmagent](https://docs.victoriametrics.com/vmagent.html) at `/vmagent/` path prefix for a particular user,
|
||||||
|
then the following [-auth.config](#auth-config) can be used:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
users:
|
||||||
|
- username: foo
|
||||||
|
url_map:
|
||||||
|
|
||||||
|
# proxy all the requests, which start with `/vmagent/`, to vmagent backend
|
||||||
|
- src_paths:
|
||||||
|
- "/vmagent/.+"
|
||||||
|
|
||||||
|
# drop /vmagent/ path prefix from the original request before proxying it to url_prefix.
|
||||||
|
drop_src_path_prefix_parts: 1
|
||||||
|
url_prefix: "http://vmagent-backend:8429/"
|
||||||
|
|
||||||
|
# proxy all the requests, which start with `/vmalert`, to vmalert backend
|
||||||
|
- src_paths:
|
||||||
|
- "/vmalert/.+"
|
||||||
|
|
||||||
|
# drop /vmalert/ path prefix from the original request before proxying it to url_prefix.
|
||||||
|
drop_src_path_prefix_parts: 1
|
||||||
|
url_prefix: "http://vmalert-backend:8880/"
|
||||||
|
```
|
||||||
|
|
||||||
## Load balancing
|
## Load balancing
|
||||||
|
|
||||||
Each `url_prefix` in the [-auth.config](#auth-config) may contain either a single url or a list of urls.
|
Each `url_prefix` in the [-auth.config](#auth-config) may contain either a single url or a list of urls.
|
||||||
|
|
Loading…
Reference in a new issue