diff --git a/.golangci.yml b/.golangci.yml
index 1b46338dc..ba9f8be36 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -44,7 +44,7 @@ linters:
alias: metav1
- pkg: k8s.io/api/apps/v1
alias: appsv1
- - pkg: k8s.io/api/autoscaling/v2"
+ - pkg: k8s.io/api/autoscaling/v2
alias: autoscalingv2
- pkg: k8s.io/apimachinery/pkg/api/errors
alias: k8serrors
diff --git a/api/operator/v1beta1/vmauth_types.go b/api/operator/v1beta1/vmauth_types.go
index 3d6ee9bd9..9dec240b8 100644
--- a/api/operator/v1beta1/vmauth_types.go
+++ b/api/operator/v1beta1/vmauth_types.go
@@ -8,6 +8,7 @@ import (
"strings"
appsv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@@ -98,6 +99,9 @@ type VMAuthSpec struct {
// currently it has collision with inlined fields
// IPFilters VMUserIPFilters `json:"ip_filters,omitempty"`
+ // JWTIssuers represents configuration section for JWT issuers
+ // +optional
+ JWTIssuers []*VMAuthJWTIssuer `json:"jwt_issuers,omitempty"`
// License allows to configure license key to be used for enterprise features.
// Using license key is supported starting from VictoriaMetrics v1.94.0.
// See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/)
@@ -147,6 +151,26 @@ type VMAuthSpec struct {
HPA *EmbeddedHPA `json:"hpa,omitempty"`
}
+// VMAuthJWTIssuer defines JWT issuer parameters
+type VMAuthJWTIssuer struct {
+ // Match defines map of claims to match issuer against
+ Match map[string]string `json:"match,omitempty"`
+ // DiscoveryURL is OpenID Connect discovery URL
+ // +optional
+ DiscoveryURL string `json:"discovery_url,omitempty"`
+ // JWKsURL is the OpenID Connect JWKS URL
+ // +optional
+ JWKsURL string `json:"jwks_url,omitempty"`
+ // PublicKeyFiles is a list of paths pointing to public key files in PEM format to use
+ // for verifying JWT tokens
+ PublicKeyFiles []string `json:"public_key_files,omitempty"`
+ // PublicKeySecrets is a list of k8s Secret selectors pointing to public key files in PEM format to use
+ // for verifying JWT tokens
+ PublicKeySecrets []*corev1.SecretKeySelector `json:"public_key_secrets,omitempty"`
+ // SyncPeriod defines how frequently JWT issuer keys are synchronized
+ SyncPeriod string `json:"sync_period,omitempty"`
+}
+
// VMAuthUnauthorizedUserAccessSpec defines unauthorized_user section configuration for vmauth
type VMAuthUnauthorizedUserAccessSpec struct {
// URLPrefix defines prefix prefix for destination
@@ -447,7 +471,6 @@ func (cr *VMAuth) Validate() error {
return fmt.Errorf("incorrect cr.spec UnauthorizedAccessConfig options: %w", err)
}
}
-
if cr.Spec.UnauthorizedUserAccessSpec != nil {
if err := cr.Spec.UnauthorizedUserAccessSpec.Validate(); err != nil {
return fmt.Errorf("incorrect cr.spec.UnauthorizedUserAccess syntax: %w", err)
diff --git a/api/operator/v1beta1/vmuser_types.go b/api/operator/v1beta1/vmuser_types.go
index 5284e743d..04d0e78fb 100644
--- a/api/operator/v1beta1/vmuser_types.go
+++ b/api/operator/v1beta1/vmuser_types.go
@@ -16,6 +16,8 @@ type VMUserSpec struct {
// Name of the VMUser object.
// +optional
Name *string `json:"name,omitempty"`
+ // JWTToken defines JWT auth section for user
+ JWTToken *VMUserJWTToken `json:"jwt_token,omitempty"`
// Username basic auth user name for accessing protected endpoint,
// will be replaced with metadata.name of VMUser if omitted.
// +optional
@@ -53,6 +55,14 @@ type VMUserSpec struct {
ManagedMetadata *ManagedObjectsMetadata `json:"managedMetadata,omitempty"`
}
+// VMUserJWTToken describes JWT auth for user
+type VMUserJWTToken struct {
+ // Match defines claim match map
+ Match map[string]string `json:"match,omitempty" yaml:"match,omitempty"`
+ // AllowUnhealthy defines if unhealthy JWT issuer status is ignored
+ AllowUnhealthy bool `json:"allow_unhealthy,omitempty" yaml:"allow_unhealthy,omitempty"`
+}
+
// TargetRef describes target for user traffic forwarding.
// one of target types can be chosen:
// crd or static per targetRef.
diff --git a/api/operator/v1beta1/zz_generated.deepcopy.go b/api/operator/v1beta1/zz_generated.deepcopy.go
index da990529b..1dcf95cc3 100644
--- a/api/operator/v1beta1/zz_generated.deepcopy.go
+++ b/api/operator/v1beta1/zz_generated.deepcopy.go
@@ -5093,6 +5093,44 @@ func (in *VMAuth) DeepCopyObject() runtime.Object {
return nil
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VMAuthJWTIssuer) DeepCopyInto(out *VMAuthJWTIssuer) {
+ *out = *in
+ if in.Match != nil {
+ in, out := &in.Match, &out.Match
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ if in.PublicKeyFiles != nil {
+ in, out := &in.PublicKeyFiles, &out.PublicKeyFiles
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ if in.PublicKeySecrets != nil {
+ in, out := &in.PublicKeySecrets, &out.PublicKeySecrets
+ *out = make([]*v1.SecretKeySelector, len(*in))
+ for i := range *in {
+ if (*in)[i] != nil {
+ in, out := &(*in)[i], &(*out)[i]
+ *out = new(v1.SecretKeySelector)
+ (*in).DeepCopyInto(*out)
+ }
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMAuthJWTIssuer.
+func (in *VMAuthJWTIssuer) DeepCopy() *VMAuthJWTIssuer {
+ if in == nil {
+ return nil
+ }
+ out := new(VMAuthJWTIssuer)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VMAuthList) DeepCopyInto(out *VMAuthList) {
*out = *in
@@ -5259,6 +5297,17 @@ func (in *VMAuthSpec) DeepCopyInto(out *VMAuthSpec) {
*out = new(VMAuthUnauthorizedUserAccessSpec)
(*in).DeepCopyInto(*out)
}
+ if in.JWTIssuers != nil {
+ in, out := &in.JWTIssuers, &out.JWTIssuers
+ *out = make([]*VMAuthJWTIssuer, len(*in))
+ for i := range *in {
+ if (*in)[i] != nil {
+ in, out := &(*in)[i], &(*out)[i]
+ *out = new(VMAuthJWTIssuer)
+ (*in).DeepCopyInto(*out)
+ }
+ }
+ }
if in.License != nil {
in, out := &in.License, &out.License
*out = new(License)
@@ -6947,6 +6996,28 @@ func (in *VMUserIPFilters) DeepCopy() *VMUserIPFilters {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VMUserJWTToken) DeepCopyInto(out *VMUserJWTToken) {
+ *out = *in
+ if in.Match != nil {
+ in, out := &in.Match, &out.Match
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMUserJWTToken.
+func (in *VMUserJWTToken) DeepCopy() *VMUserJWTToken {
+ if in == nil {
+ return nil
+ }
+ out := new(VMUserJWTToken)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VMUserList) DeepCopyInto(out *VMUserList) {
*out = *in
@@ -6987,6 +7058,11 @@ func (in *VMUserSpec) DeepCopyInto(out *VMUserSpec) {
*out = new(string)
**out = **in
}
+ if in.JWTToken != nil {
+ in, out := &in.JWTToken, &out.JWTToken
+ *out = new(VMUserJWTToken)
+ (*in).DeepCopyInto(*out)
+ }
if in.Username != nil {
in, out := &in.Username, &out.Username
*out = new(string)
diff --git a/config/crd/overlay/crd.yaml b/config/crd/overlay/crd.yaml
index 8e9d45804..8b2fb50ea 100644
--- a/config/crd/overlay/crd.yaml
+++ b/config/crd/overlay/crd.yaml
@@ -25978,6 +25978,64 @@ spec:
and v1.111.0 vmauth version
related doc https://docs.victoriametrics.com/victoriametrics/vmauth/#security
type: string
+ jwt_issuers:
+ description: JWTIssuers represents configuration section for JWT issuers
+ items:
+ description: VMAuthJWTIssuer defines JWT issuer parameters
+ properties:
+ discovery_url:
+ description: DiscoveryURL is OpenID Connect discovery URL
+ type: string
+ jwks_url:
+ description: JWKsURL is the OpenID Connect JWKS URL
+ type: string
+ match:
+ additionalProperties:
+ type: string
+ description: Match defines map of claims to match issuer against
+ type: object
+ public_key_files:
+ description: |-
+ PublicKeyFiles is a list of paths pointing to public key files in PEM format to use
+ for verifying JWT tokens
+ items:
+ type: string
+ type: array
+ public_key_secrets:
+ description: |-
+ PublicKeySecrets is a list of k8s Secret selectors pointing to public key files in PEM format to use
+ for verifying JWT tokens
+ items:
+ description: SecretKeySelector selects a key of a Secret.
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ name:
+ default: ""
+ description: |-
+ Name of the referent.
+ This field is effectively required, but due to backwards compatibility is
+ allowed to be empty. Instances of this type with an empty value here are
+ almost certainly wrong.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ type: string
+ optional:
+ description: Specify whether the Secret or its key must
+ be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ sync_period:
+ description: SyncPeriod defines how frequently JWT issuer keys
+ are synchronized
+ type: string
+ type: object
+ type: array
license:
description: |-
License allows to configure license key to be used for enterprise features.
@@ -41988,6 +42046,19 @@ spec:
type: string
type: array
type: object
+ jwt_token:
+ description: JWTToken defines JWT auth section for user
+ properties:
+ allow_unhealthy:
+ description: AllowUnhealthy defines if unhealthy JWT issuer status
+ is ignored
+ type: boolean
+ match:
+ additionalProperties:
+ type: string
+ description: Match defines claim match map
+ type: object
+ type: object
load_balancing_policy:
description: |-
LoadBalancingPolicy defines load balancing policy to use for backend urls.
diff --git a/docs/api.md b/docs/api.md
index 52eadc134..60250635d 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -671,7 +671,6 @@ Appears in: [VMAnomalyMonitoringSpec](#vmanomalymonitoringspec)
VMAnomalyMonitoringPushSpec defines metrics push configuration
-
VMAnomaly uses prometheus text exposition format
Appears in: [VMAnomalyMonitoringSpec](#vmanomalymonitoringspec)
@@ -1609,6 +1608,7 @@ Appears in: [VMAlertmanagerSpec](#vmalertmanagerspec)
| Field | Description |
| --- | --- |
| key#
_string_ | _(Required)_
The ConfigMap key to refer to. |
+| name#
_string_ | _(Optional)_
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names |
#### ConsulSDConfig
@@ -1817,7 +1817,6 @@ Appears in: [VLInsert](#vlinsert), [VLSelect](#vlselect), [VLStorage](#vlstorage
EmbeddedHTTPRoute describes httproute configuration options.
-
Requires gateway-controller CRD installed and VM_GATEWAY_API_ENABLED=true env var
See https://gateway-api.sigs.k8s.io/guides/#installing-a-gateway-controller
@@ -1899,7 +1898,7 @@ Appears in: [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlse
| maxUnavailable#
_[IntOrString](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#intorstring-intstr-util)_ | _(Optional)_
An eviction is allowed if at most "maxUnavailable" pods selected by
"selector" are unavailable after the eviction, i.e. even in absence of
the evicted pod. For example, one can prevent all voluntary evictions
by specifying 0. This is a mutually exclusive setting with "minAvailable". |
| minAvailable#
_[IntOrString](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#intorstring-intstr-util)_ | _(Optional)_
An eviction is allowed if at least "minAvailable" pods selected by
"selector" will still be available after the eviction, i.e. even in the
absence of the evicted pod. So for example you can prevent all voluntary
evictions by specifying "100%". |
| selectorLabels#
_object (keys:string, values:string)_ | _(Optional)_
replaces default labels selector generated by operator
it's useful when you need to create custom budget |
-| unhealthyPodEvictionPolicy#
_string_ | _(Optional)_
UnhealthyPodEvictionPolicy defines the criteria for when unhealthy pods
Valid policies are IfHealthyBudget and AlwaysAllow.
If no policy is specified, the default behavior will be used,
which corresponds to the IfHealthyBudget policy.
Available from operator v0.64.0 |
+| unhealthyPodEvictionPolicy#
_string_ | _(Optional)_
UnhealthyPodEvictionPolicy defines the criteria for when unhealthy pods
Valid policies are IfHealthyBudget and AlwaysAllow.
If no policy is specified, the default behavior will be used,
which corresponds to the IfHealthyBudget policy.
Available from operator v0.64.0 |
#### EmbeddedProbes
@@ -2051,7 +2050,6 @@ The private IP address is used by default, but may be changed to
the public IP address with relabeling.
See [here](https://docs.victoriametrics.com/victoriametrics/sd_configs/#gce_sd_configs)
-
The GCE service discovery will load the Google Cloud credentials
from the file specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable.
See https://cloud.google.com/kubernetes-engine/docs/tutorials/authenticating-to-cloud-platform
@@ -2838,7 +2836,7 @@ Appears in: [CommonScrapeParams](#commonscrapeparams), [VMAgentSpec](#vmagentspe
| basicAuth#
_[BasicAuth](#basicauth)_ | _(Optional)_
BasicAuth allow an endpoint to authenticate over basic authentication |
| bearerTokenFile#
_string_ | _(Optional)_
File to read bearer token for scraping targets. |
| bearerTokenSecret#
_[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | _(Optional)_
Secret to mount to read bearer token for scraping targets. The secret
needs to be in the same namespace as the scrape object and accessible by
the victoria-metrics operator. |
-| default#
_boolean_ | _(Optional)_
default defines that the scrape applies to all scrape objects that
don't configure an explicit scrape class name.
Only one scrape class can be set as the default. |
+| default#
_boolean_ | _(Optional)_
default defines that the scrape applies to all scrape objects that
don't configure an explicit scrape class name.
Only one scrape class can be set as the default. |
| metricRelabelConfigs#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
MetricRelabelConfigs to apply to samples after scrapping. |
| name#
_string_ | _(Required)_
name of the scrape class. |
| oauth2#
_[OAuth2](#oauth2)_ | _(Optional)_
OAuth2 defines auth configuration |
@@ -3114,9 +3112,9 @@ Appears in: [StreamAggrConfig](#streamaggrconfig)
| Field | Description |
| --- | --- |
-| by#
_string array_ | _(Optional)_
By is an optional list of labels for grouping input series.
See also Without.
If neither By nor Without are set, then the Outputs are calculated
individually per each input time series. |
+| by#
_string array_ | _(Optional)_
By is an optional list of labels for grouping input series.
See also Without.
If neither By nor Without are set, then the Outputs are calculated
individually per each input time series. |
| dedup_interval#
_string_ | _(Optional)_
DedupInterval is an optional interval for deduplication. |
-| drop_input_labels#
_string_ | _(Optional)_
DropInputLabels is an optional list with labels, which must be dropped before further processing of input samples.
Labels are dropped before de-duplication and aggregation. |
+| drop_input_labels#
_string_ | _(Optional)_
DropInputLabels is an optional list with labels, which must be dropped before further processing of input samples.
Labels are dropped before de-duplication and aggregation. |
| enable_windows#
_boolean_ | _(Optional)_
EnableWindows enables aggregating data in separate windows |
| flush_on_shutdown#
_boolean_ | _(Optional)_
FlushOnShutdown defines whether to flush the aggregation state on process termination
or config reload. Is `false` by default.
It is not recommended changing this setting, unless unfinished aggregations states
are preferred to missing data points. |
| ignoreFirstSampleInterval#
_string_ | _(Required)_
IgnoreFirstSampleInterval sets interval for total and prometheus_total during which first samples will be ignored |
@@ -3125,12 +3123,12 @@ Appears in: [StreamAggrConfig](#streamaggrconfig)
| input_relabel_configs#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
InputRelabelConfigs is an optional relabeling rules, which are applied on the input
before aggregation. |
| interval#
_string_ | _(Required)_
Interval is the interval between aggregations. |
| keep_metric_names#
_boolean_ | _(Optional)_
KeepMetricNames instructs to leave metric names as is for the output time series without adding any suffix. |
-| match#
_[StringOrArray](#stringorarray)_ | _(Optional)_
Match is a label selector (or list of label selectors) for filtering time series for the given selector.
If the match isn't set, then all the input time series are processed. |
+| match#
_[StringOrArray](#stringorarray)_ | _(Optional)_
Match is a label selector (or list of label selectors) for filtering time series for the given selector.
If the match isn't set, then all the input time series are processed. |
| no_align_flush_to_interval#
_boolean_ | _(Optional)_
NoAlignFlushToInterval disables aligning of flushes to multiples of Interval.
By default flushes are aligned to Interval. |
| output_relabel_configs#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
OutputRelabelConfigs is an optional relabeling rules, which are applied
on the aggregated output before being sent to remote storage. |
-| outputs#
_string array_ | _(Required)_
Outputs is a list of output aggregate functions to produce.
The following names are allowed:
- total - aggregates input counters
- increase - counts the increase over input counters
- count_series - counts the input series
- count_samples - counts the input samples
- sum_samples - sums the input samples
- last - the last biggest sample value
- min - the minimum sample value
- max - the maximum sample value
- avg - the average value across all the samples
- stddev - standard deviation across all the samples
- stdvar - standard variance across all the samples
- histogram_bucket - creates VictoriaMetrics histogram for input samples
- quantiles(phi1, ..., phiN) - quantiles' estimation for phi in the range [0..1]
The output time series will have the following names:
input_name:aggr__