added unauthorized_user field in vmauth users config (#4083) (#4157)

added `unauthorized_user` field in vmauth users config (#4083)

---------

Signed-off-by: Alexander Marshalov <_@marshalov.org>
This commit is contained in:
Alexander Marshalov 2023-04-24 14:57:13 +02:00 committed by GitHub
parent 77f76371d0
commit 73e22dcf81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 4 deletions

View file

@ -62,6 +62,10 @@ The following [metrics](#monitoring) related to concurrency limits are exposed b
- `vmauth_user_concurrent_requests_current{username="..."}` - the current number of concurrent requests for the given `username`. - `vmauth_user_concurrent_requests_current{username="..."}` - the current number of concurrent requests for the given `username`.
- `vmauth_user_concurrent_requests_limit_reached_total{username="foo"}` - the number of requests rejected with `429 Too Many Requests` error - `vmauth_user_concurrent_requests_limit_reached_total{username="foo"}` - the number of requests rejected with `429 Too Many Requests` error
because of the concurrency limit has been reached for the given `username`. because of the concurrency limit has been reached for the given `username`.
- `vmauth_unauthorized_user_concurrent_requests_capacity` - the limit on the number of concurrent requests for unauthorized users (if `unauthorized_user` section is used).
- `vmauth_unauthorized_user_concurrent_requests_current` - the current number of concurrent requests for unauthorized users (if `unauthorized_user` section is used).
- `vmauth_unauthorized_user_concurrent_requests_limit_reached_total` - the number of requests rejected with `429 Too Many Requests` error
because of the concurrency limit has been reached for unauthorized users (if `unauthorized_user` section is used).
## Auth config ## Auth config
@ -152,6 +156,18 @@ users:
url_prefix: "http://vminsert:8480/insert/42/prometheus" url_prefix: "http://vminsert:8480/insert/42/prometheus"
headers: headers:
- "X-Scope-OrgID: abc" - "X-Scope-OrgID: abc"
# This requests will be executed for requests without Authorization header.
# For instance, http://vmauth:8427/api/v1/query will be proxied to http://vmselect1:8481/select/0/prometheus/api/v1/query
unauthorized_user:
url_map:
- src_paths:
- /health
- /api/v1/query/
- /api/v1/query_range
url_prefix:
- http://vmselect1:8481/select/0/prometheus
- http://vmselect2:8481/select/0/prometheus
``` ```
The config may contain `%{ENV_VAR}` placeholders, which are substituted by the corresponding `ENV_VAR` environment variable values. The config may contain `%{ENV_VAR}` placeholders, which are substituted by the corresponding `ENV_VAR` environment variable values.
@ -194,6 +210,8 @@ users:
# other config options here # other config options here
``` ```
For unauthorized users `vmauth` exports `vmauth_unauthorized_user_requests_total` metric without label (if `unauthorized_user` section of config is used).
## How to build from sources ## How to build from sources
It is recommended using [binary releases](https://github.com/VictoriaMetrics/VictoriaMetrics/releases) - `vmauth` is located in `vmutils-*` archives there. It is recommended using [binary releases](https://github.com/VictoriaMetrics/VictoriaMetrics/releases) - `vmauth` is located in `vmutils-*` archives there.

View file

@ -32,6 +32,7 @@ var (
// AuthConfig represents auth config. // AuthConfig represents auth config.
type AuthConfig struct { type AuthConfig struct {
Users []UserInfo `yaml:"users,omitempty"` Users []UserInfo `yaml:"users,omitempty"`
UnauthorizedUser *UserInfo `yaml:"unauthorized_user,omitempty"`
} }
// UserInfo is user information read from authConfigPath // UserInfo is user information read from authConfigPath
@ -374,6 +375,18 @@ func parseAuthConfig(data []byte) (*AuthConfig, error) {
if err = yaml.UnmarshalStrict(data, &ac); err != nil { if err = yaml.UnmarshalStrict(data, &ac); err != nil {
return nil, fmt.Errorf("cannot unmarshal AuthConfig data: %w", err) return nil, fmt.Errorf("cannot unmarshal AuthConfig data: %w", err)
} }
ui := ac.UnauthorizedUser
if ui != nil {
ui.requests = metrics.GetOrCreateCounter(`vmauth_unauthorized_user_requests_total`)
ui.concurrencyLimitCh = make(chan struct{}, ui.getMaxConcurrentRequests())
ui.concurrencyLimitReached = metrics.GetOrCreateCounter(`vmauth_unauthorized_user_concurrent_requests_limit_reached_total`)
_ = metrics.GetOrCreateGauge(`vmauth_unauthorized_user_concurrent_requests_capacity`, func() float64 {
return float64(cap(ui.concurrencyLimitCh))
})
_ = metrics.GetOrCreateGauge(`vmauth_unauthorized_user_concurrent_requests_current`, func() float64 {
return float64(len(ui.concurrencyLimitCh))
})
}
return &ac, nil return &ac, nil
} }

View file

@ -84,6 +84,13 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
} }
authToken := r.Header.Get("Authorization") authToken := r.Header.Get("Authorization")
if authToken == "" { if authToken == "" {
// Process requests for unauthorized users
ui := authConfig.Load().UnauthorizedUser
if ui != nil {
processUserRequest(w, r, ui)
return true
}
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
http.Error(w, "missing `Authorization` request header", http.StatusUnauthorized) http.Error(w, "missing `Authorization` request header", http.StatusUnauthorized)
return true return true
@ -110,6 +117,12 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
} }
return true return true
} }
processUserRequest(w, r, ui)
return true
}
func processUserRequest(w http.ResponseWriter, r *http.Request, ui *UserInfo) {
ui.requests.Inc() ui.requests.Inc()
// Limit the concurrency of requests to backends // Limit the concurrency of requests to backends
@ -119,18 +132,17 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
if err := ui.beginConcurrencyLimit(); err != nil { if err := ui.beginConcurrencyLimit(); err != nil {
handleConcurrencyLimitError(w, r, err) handleConcurrencyLimitError(w, r, err)
<-concurrencyLimitCh <-concurrencyLimitCh
return true return
} }
default: default:
concurrentRequestsLimitReached.Inc() concurrentRequestsLimitReached.Inc()
err := fmt.Errorf("cannot serve more than -maxConcurrentRequests=%d concurrent requests", cap(concurrencyLimitCh)) err := fmt.Errorf("cannot serve more than -maxConcurrentRequests=%d concurrent requests", cap(concurrencyLimitCh))
handleConcurrencyLimitError(w, r, err) handleConcurrencyLimitError(w, r, err)
return true return
} }
processRequest(w, r, ui) processRequest(w, r, ui)
ui.endConcurrencyLimit() ui.endConcurrencyLimit()
<-concurrencyLimitCh <-concurrencyLimitCh
return true
} }
func processRequest(w http.ResponseWriter, r *http.Request, ui *UserInfo) { func processRequest(w http.ResponseWriter, r *http.Request, ui *UserInfo) {

View file

@ -66,6 +66,10 @@ The following [metrics](#monitoring) related to concurrency limits are exposed b
- `vmauth_user_concurrent_requests_current{username="..."}` - the current number of concurrent requests for the given `username`. - `vmauth_user_concurrent_requests_current{username="..."}` - the current number of concurrent requests for the given `username`.
- `vmauth_user_concurrent_requests_limit_reached_total{username="foo"}` - the number of requests rejected with `429 Too Many Requests` error - `vmauth_user_concurrent_requests_limit_reached_total{username="foo"}` - the number of requests rejected with `429 Too Many Requests` error
because of the concurrency limit has been reached for the given `username`. because of the concurrency limit has been reached for the given `username`.
- `vmauth_unauthorized_user_concurrent_requests_capacity` - the limit on the number of concurrent requests for unauthorized users (if `unauthorized_user` section is used).
- `vmauth_unauthorized_user_concurrent_requests_current` - the current number of concurrent requests for unauthorized users (if `unauthorized_user` section is used).
- `vmauth_unauthorized_user_concurrent_requests_limit_reached_total` - the number of requests rejected with `429 Too Many Requests` error
because of the concurrency limit has been reached for unauthorized users (if `unauthorized_user` section is used).
## Auth config ## Auth config
@ -156,6 +160,18 @@ users:
url_prefix: "http://vminsert:8480/insert/42/prometheus" url_prefix: "http://vminsert:8480/insert/42/prometheus"
headers: headers:
- "X-Scope-OrgID: abc" - "X-Scope-OrgID: abc"
# This requests will be executed for requests without Authorization header.
# For instance, http://vmauth:8427/api/v1/query will be proxied to http://vmselect1:8481/select/0/prometheus/api/v1/query
unauthorized_user:
url_map:
- src_paths:
- /health
- /api/v1/query/
- /api/v1/query_range
url_prefix:
- http://vmselect1:8481/select/0/prometheus
- http://vmselect2:8481/select/0/prometheus
``` ```
The config may contain `%{ENV_VAR}` placeholders, which are substituted by the corresponding `ENV_VAR` environment variable values. The config may contain `%{ENV_VAR}` placeholders, which are substituted by the corresponding `ENV_VAR` environment variable values.
@ -198,6 +214,8 @@ users:
# other config options here # other config options here
``` ```
For unauthorized users `vmauth` exports `vmauth_unauthorized_user_requests_total` metric without label (if `unauthorized_user` section of config is used).
## How to build from sources ## How to build from sources
It is recommended using [binary releases](https://github.com/VictoriaMetrics/VictoriaMetrics/releases) - `vmauth` is located in `vmutils-*` archives there. It is recommended using [binary releases](https://github.com/VictoriaMetrics/VictoriaMetrics/releases) - `vmauth` is located in `vmutils-*` archives there.