mirror of
https://github.com/prometheus-operator/prometheus-operator.git
synced 2025-04-21 11:48:53 +00:00
pkg/prometheus: Enable users to configure bearer token from secret
To configure a bearer token users could only specify a file path in the service monitor, pointing to a bearer token file in the Prometheus container. This enables hostile users, being able to configure a service monitor and controlling the scrape target, to retrieve arbitrary files in the Prometheus container. In cases where users can not be trusted, this patch adds an option to disallow the above file path specification and replaces it by a secret reference. This secret has to be in the same namespace as the service monitor, shrinking the attack vector. pkg/prometheus: Add option to deny file system access through service monitors ArbitraryFSAccessThroughSMsConfig enables users to configure, whether a service monitor selected by the Prometheus instance is allowed to use arbitrary files on the file system of the Prometheus container. This is the case when e.g. a service monitor specifies a BearerTokenFile in an endpoint. A malicious user could create a service monitor selecting arbitrary secret files in the Prometheus container. Those secrets would then be send with a scrape request by Prometheus to a malicious target. Denying the above would prevent the attack, users can instead use the BearerTokenSecret field. test/basic-auth-test-app: Add mTLS endpoint pkg/prometheus: Enable users to configure tls from secret pkg/prometheus/operator: Validate TLS configs before retrieving assets Before retrieving TLS assets from Kubernetes secrets for a given service monitor, make sure the user did not specify both file and secret reference, e.g. both `CAFile` and `CASecret`. test: Rename basic-auth-test-app to instrumented-sample-app Given that the basic-auth-test-app not only supports basic auth, but also bearer token as well as tls authentication, this patch renames the app to a more generic name. test/e2e/prometheus_test: Test ArbitraryFSAccessThroughSM option for tls The Prometheus custom resource has the option to disable arbitrary filesystem access configured through service monitors. This commit adds an end-to-end test for this option in combination with the TLS configuration via files or secret references in service monitors. pkg/prometheus/operator: Move check for arbitrary fs access into func
This commit is contained in:
parent
67bccfd45b
commit
fd92cbfe94
31 changed files with 1662 additions and 330 deletions
Documentation
Makefileexample/prometheus-operator-crd
jsonnet/prometheus-operator
pkg
apis/monitoring/v1
prometheus
test
basic-auth-test-app
e2e
framework
instrumented-sample-app
|
@ -17,6 +17,7 @@ This Document documents the types introduced by the Prometheus Operator to be co
|
|||
* [AlertmanagerList](#alertmanagerlist)
|
||||
* [AlertmanagerSpec](#alertmanagerspec)
|
||||
* [AlertmanagerStatus](#alertmanagerstatus)
|
||||
* [ArbitraryFSAccessThroughSMsConfig](#arbitraryfsaccessthroughsmsconfig)
|
||||
* [BasicAuth](#basicauth)
|
||||
* [Endpoint](#endpoint)
|
||||
* [NamespaceSelector](#namespaceselector)
|
||||
|
@ -45,6 +46,7 @@ This Document documents the types introduced by the Prometheus Operator to be co
|
|||
* [ServiceMonitorSpec](#servicemonitorspec)
|
||||
* [StorageSpec](#storagespec)
|
||||
* [TLSConfig](#tlsconfig)
|
||||
* [TLSConfigValidationError](#tlsconfigvalidationerror)
|
||||
* [ThanosSpec](#thanosspec)
|
||||
|
||||
## APIServerConfig
|
||||
|
@ -164,14 +166,24 @@ AlertmanagerStatus is the most recent observed status of the Alertmanager cluste
|
|||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## ArbitraryFSAccessThroughSMsConfig
|
||||
|
||||
ArbitraryFSAccessThroughSMsConfig enables users to configure, whether a service monitor selected by the Prometheus instance is allowed to use arbitrary files on the file system of the Prometheus container. This is the case when e.g. a service monitor specifies a BearerTokenFile in an endpoint. A malicious user could create a service monitor selecting arbitrary secret files in the Prometheus container. Those secrets would then be send with a scrape request by Prometheus to a malicious target. Denying the above would prevent the attack, users can instead use the BearerTokenSecret field.
|
||||
|
||||
| Field | Description | Scheme | Required |
|
||||
| ----- | ----------- | ------ | -------- |
|
||||
| Deny | | bool | false |
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## BasicAuth
|
||||
|
||||
BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints
|
||||
|
||||
| Field | Description | Scheme | Required |
|
||||
| ----- | ----------- | ------ | -------- |
|
||||
| username | The secret that contains the username for authenticate | [v1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#secretkeyselector-v1-core) | false |
|
||||
| password | The secret that contains the password for authenticate | [v1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#secretkeyselector-v1-core) | false |
|
||||
| username | The secret in the service monitor namespace that contains the username for authentication. | [v1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#secretkeyselector-v1-core) | false |
|
||||
| password | The secret in the service monitor namespace that contains the password for authentication. | [v1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#secretkeyselector-v1-core) | false |
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
|
@ -190,6 +202,7 @@ Endpoint defines a scrapeable endpoint serving Prometheus metrics.
|
|||
| scrapeTimeout | Timeout after which the scrape is ended | string | false |
|
||||
| tlsConfig | TLS configuration to use when scraping the endpoint | *[TLSConfig](#tlsconfig) | false |
|
||||
| bearerTokenFile | File to read bearer token for scraping targets. | string | false |
|
||||
| bearerTokenSecret | Secret to mount to read bearer token for scraping targets. The secret needs to be in the same namespace as the service monitor and accessible by the Prometheus Operator. | [v1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#secretkeyselector-v1-core) | false |
|
||||
| honorLabels | HonorLabels chooses the metric's labels on collisions with target labels. | bool | false |
|
||||
| basicAuth | BasicAuth allow an endpoint to authenticate over basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints | *[BasicAuth](#basicauth) | false |
|
||||
| metricRelabelings | MetricRelabelConfigs to apply to samples before ingestion. | []*[RelabelConfig](#relabelconfig) | false |
|
||||
|
@ -380,6 +393,7 @@ PrometheusSpec is a specification of the desired behavior of the Prometheus clus
|
|||
| thanos | Thanos configuration allows configuring various aspects of a Prometheus server in a Thanos environment.\n\nThis section is experimental, it may change significantly without deprecation notice in any release.\n\nThis is experimental and may change significantly without backward compatibility in any release. | *[ThanosSpec](#thanosspec) | false |
|
||||
| priorityClassName | Priority class assigned to the Pods | string | false |
|
||||
| portName | Port name used for the pods and governing service. This defaults to web | string | false |
|
||||
| arbitraryFSAccessThroughSMs | ArbitraryFSAccessThroughSMs configures whether configuration based on a service monitor can access arbitrary files on the file system of the Prometheus container e.g. bearer token files. | [ArbitraryFSAccessThroughSMsConfig](#arbitraryfsaccessthroughsmsconfig) | true |
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
|
@ -583,14 +597,26 @@ TLSConfig specifies TLS configuration parameters.
|
|||
|
||||
| Field | Description | Scheme | Required |
|
||||
| ----- | ----------- | ------ | -------- |
|
||||
| caFile | The CA cert to use for the targets. | string | false |
|
||||
| certFile | The client cert file for the targets. | string | false |
|
||||
| keyFile | The client key file for the targets. | string | false |
|
||||
| caFile | Path to the CA cert in the Prometheus container to use for the targets. | string | false |
|
||||
| caSecret | Secret containing the CA cert to use for the targets. | *[v1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#secretkeyselector-v1-core) | false |
|
||||
| certFile | Path to the client cert file in the Prometheus container for the targets. | string | false |
|
||||
| certSecret | Secret containing the client cert file for the targets. | *[v1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#secretkeyselector-v1-core) | false |
|
||||
| keyFile | Path to the client key file in the Prometheus container for the targets. | string | false |
|
||||
| keySecret | Secret containing the client key file for the targets. | *[v1.SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#secretkeyselector-v1-core) | false |
|
||||
| serverName | Used to verify the hostname for the targets. | string | false |
|
||||
| insecureSkipVerify | Disable target certificate validation. | bool | false |
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## TLSConfigValidationError
|
||||
|
||||
TLSConfigValidationError is returned by TLSConfig.Validate() on semantically invalid tls configurations.
|
||||
|
||||
| Field | Description | Scheme | Required |
|
||||
| ----- | ----------- | ------ | -------- |
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## ThanosSpec
|
||||
|
||||
ThanosSpec defines parameters for a Prometheus server within a Thanos deployment.
|
||||
|
|
5
Makefile
5
Makefile
|
@ -214,9 +214,12 @@ test: test-unit test-e2e
|
|||
test-unit:
|
||||
go test -race $(TEST_RUN_ARGS) -short $(pkgs) -count=1
|
||||
|
||||
test/instrumented-sample-app/certs/cert.pem test/instrumented-sample-app/certs/key.pem:
|
||||
cd test/instrumented-sample-app && make generate-certs
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e: KUBECONFIG?=$(HOME)/.kube/config
|
||||
test-e2e:
|
||||
test-e2e: test/instrumented-sample-app/certs/cert.pem test/instrumented-sample-app/certs/key.pem
|
||||
go test -timeout 55m -v ./test/e2e/ $(TEST_RUN_ARGS) --kubeconfig=$(KUBECONFIG) --operator-image=$(REPO):$(TAG) -count=1
|
||||
|
||||
############
|
||||
|
|
|
@ -695,17 +695,71 @@ spec:
|
|||
description: TLSConfig specifies TLS configuration parameters.
|
||||
properties:
|
||||
caFile:
|
||||
description: The CA cert to use for the targets.
|
||||
description: Path to the CA cert in the Prometheus container
|
||||
to use for the targets.
|
||||
type: string
|
||||
caSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
certFile:
|
||||
description: The client cert file for the targets.
|
||||
description: Path to the client cert file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
certSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
insecureSkipVerify:
|
||||
description: Disable target certificate validation.
|
||||
type: boolean
|
||||
keyFile:
|
||||
description: The client key file for the targets.
|
||||
description: Path to the client key file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
keySecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
serverName:
|
||||
description: Used to verify the hostname for the targets.
|
||||
type: string
|
||||
|
@ -776,17 +830,71 @@ spec:
|
|||
description: TLSConfig specifies TLS configuration parameters.
|
||||
properties:
|
||||
caFile:
|
||||
description: The CA cert to use for the targets.
|
||||
description: Path to the CA cert in the Prometheus container
|
||||
to use for the targets.
|
||||
type: string
|
||||
caSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
certFile:
|
||||
description: The client cert file for the targets.
|
||||
description: Path to the client cert file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
certSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
insecureSkipVerify:
|
||||
description: Disable target certificate validation.
|
||||
type: boolean
|
||||
keyFile:
|
||||
description: The client key file for the targets.
|
||||
description: Path to the client key file in the Prometheus container
|
||||
for the targets.
|
||||
type: string
|
||||
keySecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
serverName:
|
||||
description: Used to verify the hostname for the targets.
|
||||
type: string
|
||||
|
@ -794,6 +902,7 @@ spec:
|
|||
required:
|
||||
- host
|
||||
type: object
|
||||
arbitraryFSAccessThroughSMs: {}
|
||||
baseImage:
|
||||
description: Base image to use for a Prometheus deployment.
|
||||
type: string
|
||||
|
@ -3164,17 +3273,71 @@ spec:
|
|||
description: TLSConfig specifies TLS configuration parameters.
|
||||
properties:
|
||||
caFile:
|
||||
description: The CA cert to use for the targets.
|
||||
description: Path to the CA cert in the Prometheus container
|
||||
to use for the targets.
|
||||
type: string
|
||||
caSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
certFile:
|
||||
description: The client cert file for the targets.
|
||||
description: Path to the client cert file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
certSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
insecureSkipVerify:
|
||||
description: Disable target certificate validation.
|
||||
type: boolean
|
||||
keyFile:
|
||||
description: The client key file for the targets.
|
||||
description: Path to the client key file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
keySecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
serverName:
|
||||
description: Used to verify the hostname for the targets.
|
||||
type: string
|
||||
|
@ -3290,17 +3453,71 @@ spec:
|
|||
description: TLSConfig specifies TLS configuration parameters.
|
||||
properties:
|
||||
caFile:
|
||||
description: The CA cert to use for the targets.
|
||||
description: Path to the CA cert in the Prometheus container
|
||||
to use for the targets.
|
||||
type: string
|
||||
caSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
certFile:
|
||||
description: The client cert file for the targets.
|
||||
description: Path to the client cert file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
certSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
insecureSkipVerify:
|
||||
description: Disable target certificate validation.
|
||||
type: boolean
|
||||
keyFile:
|
||||
description: The client key file for the targets.
|
||||
description: Path to the client key file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
keySecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
serverName:
|
||||
description: Used to verify the hostname for the targets.
|
||||
type: string
|
||||
|
@ -5392,6 +5609,8 @@ spec:
|
|||
description: Enable compression of the write-ahead log using Snappy.
|
||||
This flag is only available in versions of Prometheus >= 2.11.0.
|
||||
type: boolean
|
||||
required:
|
||||
- arbitraryFSAccessThroughSMs
|
||||
type: object
|
||||
status:
|
||||
description: 'PrometheusStatus is the most recent observed status of the
|
||||
|
|
|
@ -75,6 +75,23 @@ spec:
|
|||
bearerTokenFile:
|
||||
description: File to read bearer token for scraping targets.
|
||||
type: string
|
||||
bearerTokenSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
honorLabels:
|
||||
description: HonorLabels chooses the metric's labels on collisions
|
||||
with target labels.
|
||||
|
@ -201,17 +218,71 @@ spec:
|
|||
description: TLSConfig specifies TLS configuration parameters.
|
||||
properties:
|
||||
caFile:
|
||||
description: The CA cert to use for the targets.
|
||||
description: Path to the CA cert in the Prometheus container
|
||||
to use for the targets.
|
||||
type: string
|
||||
caSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
certFile:
|
||||
description: The client cert file for the targets.
|
||||
description: Path to the client cert file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
certSecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
insecureSkipVerify:
|
||||
description: Disable target certificate validation.
|
||||
type: boolean
|
||||
keyFile:
|
||||
description: The client key file for the targets.
|
||||
description: Path to the client key file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
keySecret:
|
||||
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:
|
||||
description: 'Name of the referent. 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
|
||||
serverName:
|
||||
description: Used to verify the hostname for the targets.
|
||||
type: string
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -899,13 +899,13 @@ func schema_pkg_apis_monitoring_v1_BasicAuth(ref common.ReferenceCallback) commo
|
|||
Properties: map[string]spec.Schema{
|
||||
"username": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The secret that contains the username for authenticate",
|
||||
Description: "The secret in the service monitor namespace that contains the username for authentication.",
|
||||
Ref: ref("k8s.io/api/core/v1.SecretKeySelector"),
|
||||
},
|
||||
},
|
||||
"password": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The secret that contains the password for authenticate",
|
||||
Description: "The secret in the service monitor namespace that contains the password for authentication.",
|
||||
Ref: ref("k8s.io/api/core/v1.SecretKeySelector"),
|
||||
},
|
||||
},
|
||||
|
@ -1000,6 +1000,12 @@ func schema_pkg_apis_monitoring_v1_Endpoint(ref common.ReferenceCallback) common
|
|||
Format: "",
|
||||
},
|
||||
},
|
||||
"bearerTokenSecret": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Secret to mount to read bearer token for scraping targets. The secret needs to be in the same namespace as the service monitor and accessible by the Prometheus Operator.",
|
||||
Ref: ref("k8s.io/api/core/v1.SecretKeySelector"),
|
||||
},
|
||||
},
|
||||
"honorLabels": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "HonorLabels chooses the metric's labels on collisions with target labels.",
|
||||
|
@ -1050,7 +1056,7 @@ func schema_pkg_apis_monitoring_v1_Endpoint(ref common.ReferenceCallback) common
|
|||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.BasicAuth", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.RelabelConfig", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.TLSConfig", "k8s.io/apimachinery/pkg/util/intstr.IntOrString"},
|
||||
"github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.BasicAuth", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.RelabelConfig", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.TLSConfig", "k8s.io/api/core/v1.SecretKeySelector", "k8s.io/apimachinery/pkg/util/intstr.IntOrString"},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2001,11 +2007,22 @@ func schema_pkg_apis_monitoring_v1_PrometheusSpec(ref common.ReferenceCallback)
|
|||
Format: "",
|
||||
},
|
||||
},
|
||||
"arbitraryFSAccessThroughSMs": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "ArbitraryFSAccessThroughSMs configures whether configuration based on a service monitor can access arbitrary files on the file system of the Prometheus container e.g. bearer token files.",
|
||||
Ref: ref("github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.ArbitraryFSAccessThroughSMsConfig"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"arbitraryFSAccessThroughSMs"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
<<<<<<< HEAD
|
||||
"github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.APIServerConfig", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.AlertingSpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.QuerySpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.RemoteReadSpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.RemoteWriteSpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.Rules", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.StorageSpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.ThanosSpec", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecretKeySelector", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
=======
|
||||
"github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.APIServerConfig", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.AlertingSpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.ArbitraryFSAccessThroughSMsConfig", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.QuerySpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.RemoteReadSpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.RemoteWriteSpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.Rules", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.StorageSpec", "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.ThanosSpec", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecretKeySelector", "k8s.io/api/core/v1.Toleration", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
>>>>>>> pkg/prometheus: Enable users to configure bearer token from secret
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2769,25 +2786,43 @@ func schema_pkg_apis_monitoring_v1_TLSConfig(ref common.ReferenceCallback) commo
|
|||
Properties: map[string]spec.Schema{
|
||||
"caFile": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The CA cert to use for the targets.",
|
||||
Description: "Path to the CA cert in the Prometheus container to use for the targets.",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"caSecret": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Secret containing the CA cert to use for the targets.",
|
||||
Ref: ref("k8s.io/api/core/v1.SecretKeySelector"),
|
||||
},
|
||||
},
|
||||
"certFile": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The client cert file for the targets.",
|
||||
Description: "Path to the client cert file in the Prometheus container for the targets.",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"certSecret": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Secret containing the client cert file for the targets.",
|
||||
Ref: ref("k8s.io/api/core/v1.SecretKeySelector"),
|
||||
},
|
||||
},
|
||||
"keyFile": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The client key file for the targets.",
|
||||
Description: "Path to the client key file in the Prometheus container for the targets.",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"keySecret": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Secret containing the client key file for the targets.",
|
||||
Ref: ref("k8s.io/api/core/v1.SecretKeySelector"),
|
||||
},
|
||||
},
|
||||
"serverName": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Used to verify the hostname for the targets.",
|
||||
|
@ -2805,6 +2840,8 @@ func schema_pkg_apis_monitoring_v1_TLSConfig(ref common.ReferenceCallback) commo
|
|||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"k8s.io/api/core/v1.SecretKeySelector"},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -281,6 +281,24 @@ type PrometheusSpec struct {
|
|||
// Port name used for the pods and governing service.
|
||||
// This defaults to web
|
||||
PortName string `json:"portName,omitempty"`
|
||||
// ArbitraryFSAccessThroughSMs configures whether configuration
|
||||
// based on a service monitor can access arbitrary files on the file system
|
||||
// of the Prometheus container e.g. bearer token files.
|
||||
ArbitraryFSAccessThroughSMs ArbitraryFSAccessThroughSMsConfig `json:"arbitraryFSAccessThroughSMs"`
|
||||
}
|
||||
|
||||
// ArbitraryFSAccessThroughSMsConfig enables users to configure, whether
|
||||
// a service monitor selected by the Prometheus instance is allowed to use
|
||||
// arbitrary files on the file system of the Prometheus container. This is the case
|
||||
// when e.g. a service monitor specifies a BearerTokenFile in an endpoint. A
|
||||
// malicious user could create a service monitor selecting arbitrary secret files
|
||||
// in the Prometheus container. Those secrets would then be send with a scrape
|
||||
// request by Prometheus to a malicious target. Denying the above would prevent the
|
||||
// attack, users can instead use the BearerTokenSecret field.
|
||||
type ArbitraryFSAccessThroughSMsConfig struct {
|
||||
Deny bool
|
||||
// TODO: To be implemented.
|
||||
// ServiceMonitorWhitelist
|
||||
}
|
||||
|
||||
// PrometheusStatus is the most recent observed status of the Prometheus cluster. Read-only. Not
|
||||
|
@ -552,6 +570,10 @@ type Endpoint struct {
|
|||
TLSConfig *TLSConfig `json:"tlsConfig,omitempty"`
|
||||
// File to read bearer token for scraping targets.
|
||||
BearerTokenFile string `json:"bearerTokenFile,omitempty"`
|
||||
// Secret to mount to read bearer token for scraping targets. The secret
|
||||
// needs to be in the same namespace as the service monitor and accessible by
|
||||
// the Prometheus Operator.
|
||||
BearerTokenSecret v1.SecretKeySelector `json:"bearerTokenSecret,omitempty"`
|
||||
// HonorLabels chooses the metric's labels on collisions with target labels.
|
||||
HonorLabels bool `json:"honorLabels,omitempty"`
|
||||
// BasicAuth allow an endpoint to authenticate over basic authentication
|
||||
|
@ -628,27 +650,66 @@ type PodMetricsEndpoint struct {
|
|||
// More info: https://prometheus.io/docs/operating/configuration/#endpoints
|
||||
// +k8s:openapi-gen=true
|
||||
type BasicAuth struct {
|
||||
// The secret that contains the username for authenticate
|
||||
// The secret in the service monitor namespace that contains the username
|
||||
// for authentication.
|
||||
Username v1.SecretKeySelector `json:"username,omitempty"`
|
||||
// The secret that contains the password for authenticate
|
||||
// The secret in the service monitor namespace that contains the password
|
||||
// for authentication.
|
||||
Password v1.SecretKeySelector `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
// TLSConfig specifies TLS configuration parameters.
|
||||
// +k8s:openapi-gen=true
|
||||
type TLSConfig struct {
|
||||
// The CA cert to use for the targets.
|
||||
// Path to the CA cert in the Prometheus container to use for the targets.
|
||||
CAFile string `json:"caFile,omitempty"`
|
||||
// The client cert file for the targets.
|
||||
// Secret containing the CA cert to use for the targets.
|
||||
CASecret *v1.SecretKeySelector `json:"caSecret,omitempty"`
|
||||
|
||||
// Path to the client cert file in the Prometheus container for the targets.
|
||||
CertFile string `json:"certFile,omitempty"`
|
||||
// The client key file for the targets.
|
||||
// Secret containing the client cert file for the targets.
|
||||
CertSecret *v1.SecretKeySelector `json:"certSecret,omitempty"`
|
||||
|
||||
// Path to the client key file in the Prometheus container for the targets.
|
||||
KeyFile string `json:"keyFile,omitempty"`
|
||||
// Secret containing the client key file for the targets.
|
||||
KeySecret *v1.SecretKeySelector `json:"keySecret,omitempty"`
|
||||
|
||||
// Used to verify the hostname for the targets.
|
||||
ServerName string `json:"serverName,omitempty"`
|
||||
// Disable target certificate validation.
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
|
||||
}
|
||||
|
||||
// TLSConfigValidationError is returned by TLSConfig.Validate() on semantically
|
||||
// invalid tls configurations.
|
||||
// +k8s:openapi-gen=false
|
||||
type TLSConfigValidationError struct {
|
||||
err string
|
||||
}
|
||||
|
||||
func (e *TLSConfigValidationError) Error() string {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// Validate semantically validates the given TLSConfig.
|
||||
func (c *TLSConfig) Validate() error {
|
||||
if c.CAFile != "" && c.CASecret != nil {
|
||||
return &TLSConfigValidationError{"tls config can not both specify CAFile and CASecret"}
|
||||
}
|
||||
|
||||
if c.CertFile != "" && c.CertSecret != nil {
|
||||
return &TLSConfigValidationError{"tls config can not both specify CertFile and CertSecret"}
|
||||
}
|
||||
|
||||
if c.KeyFile != "" && c.KeySecret != nil {
|
||||
return &TLSConfigValidationError{"tls config can not both specify KeyFile and KeySecret"}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServiceMonitorList is a list of ServiceMonitors.
|
||||
// +k8s:openapi-gen=true
|
||||
type ServiceMonitorList struct {
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestMarshallServiceMonitor(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
expected := `{"metadata":{"name":"test","namespace":"default","creationTimestamp":null,"labels":{"group":"group1"}},"spec":{"endpoints":[{"port":"metric"}],"selector":{},"namespaceSelector":{"matchNames":["test"]}}}`
|
||||
expected := `{"metadata":{"name":"test","namespace":"default","creationTimestamp":null,"labels":{"group":"group1"}},"spec":{"endpoints":[{"port":"metric","bearerTokenSecret":{"key":""}}],"selector":{},"namespaceSelector":{"matchNames":["test"]}}}`
|
||||
|
||||
r, err := json.Marshal(sm)
|
||||
if err != nil {
|
||||
|
|
|
@ -35,7 +35,7 @@ func (in *APIServerConfig) DeepCopyInto(out *APIServerConfig) {
|
|||
if in.TLSConfig != nil {
|
||||
in, out := &in.TLSConfig, &out.TLSConfig
|
||||
*out = new(TLSConfig)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ func (in *AlertmanagerEndpoints) DeepCopyInto(out *AlertmanagerEndpoints) {
|
|||
if in.TLSConfig != nil {
|
||||
in, out := &in.TLSConfig, &out.TLSConfig
|
||||
*out = new(TLSConfig)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -269,6 +269,22 @@ func (in *AlertmanagerStatus) DeepCopy() *AlertmanagerStatus {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ArbitraryFSAccessThroughSMsConfig) DeepCopyInto(out *ArbitraryFSAccessThroughSMsConfig) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArbitraryFSAccessThroughSMsConfig.
|
||||
func (in *ArbitraryFSAccessThroughSMsConfig) DeepCopy() *ArbitraryFSAccessThroughSMsConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ArbitraryFSAccessThroughSMsConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *BasicAuth) DeepCopyInto(out *BasicAuth) {
|
||||
*out = *in
|
||||
|
@ -350,8 +366,9 @@ func (in *Endpoint) DeepCopyInto(out *Endpoint) {
|
|||
if in.TLSConfig != nil {
|
||||
in, out := &in.TLSConfig, &out.TLSConfig
|
||||
*out = new(TLSConfig)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.BearerTokenSecret.DeepCopyInto(&out.BearerTokenSecret)
|
||||
if in.BasicAuth != nil {
|
||||
in, out := &in.BasicAuth, &out.BasicAuth
|
||||
*out = new(BasicAuth)
|
||||
|
@ -869,6 +886,7 @@ func (in *PrometheusSpec) DeepCopyInto(out *PrometheusSpec) {
|
|||
*out = new(ThanosSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
out.ArbitraryFSAccessThroughSMs = in.ArbitraryFSAccessThroughSMs
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -989,7 +1007,7 @@ func (in *RemoteReadSpec) DeepCopyInto(out *RemoteReadSpec) {
|
|||
if in.TLSConfig != nil {
|
||||
in, out := &in.TLSConfig, &out.TLSConfig
|
||||
*out = new(TLSConfig)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -1022,7 +1040,7 @@ func (in *RemoteWriteSpec) DeepCopyInto(out *RemoteWriteSpec) {
|
|||
if in.TLSConfig != nil {
|
||||
in, out := &in.TLSConfig, &out.TLSConfig
|
||||
*out = new(TLSConfig)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.QueueConfig != nil {
|
||||
in, out := &in.QueueConfig, &out.QueueConfig
|
||||
|
@ -1237,6 +1255,21 @@ func (in *StorageSpec) DeepCopy() *StorageSpec {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TLSConfig) DeepCopyInto(out *TLSConfig) {
|
||||
*out = *in
|
||||
if in.CASecret != nil {
|
||||
in, out := &in.CASecret, &out.CASecret
|
||||
*out = new(corev1.SecretKeySelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.CertSecret != nil {
|
||||
in, out := &in.CertSecret, &out.CertSecret
|
||||
*out = new(corev1.SecretKeySelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.KeySecret != nil {
|
||||
in, out := &in.KeySecret, &out.KeySecret
|
||||
*out = new(corev1.SecretKeySelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1250,6 +1283,22 @@ func (in *TLSConfig) DeepCopy() *TLSConfig {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TLSConfigValidationError) DeepCopyInto(out *TLSConfigValidationError) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSConfigValidationError.
|
||||
func (in *TLSConfigValidationError) DeepCopy() *TLSConfigValidationError {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TLSConfigValidationError)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ThanosSpec) DeepCopyInto(out *ThanosSpec) {
|
||||
*out = *in
|
||||
|
|
|
@ -164,11 +164,21 @@ type Namespaces struct {
|
|||
PrometheusAllowList, AlertmanagerAllowList []string
|
||||
}
|
||||
|
||||
// BasicAuthCredentials represents a username password pair to be used with
|
||||
// basic http authentication, see https://tools.ietf.org/html/rfc7617.
|
||||
type BasicAuthCredentials struct {
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
// BearerToken represents a bearer token, see
|
||||
// https://tools.ietf.org/html/rfc6750.
|
||||
type BearerToken string
|
||||
|
||||
// TLSAsset represents any TLS related opaque string, e.g. CA files, client
|
||||
// certificates.
|
||||
type TLSAsset string
|
||||
|
||||
// New creates a new controller.
|
||||
func New(conf Config, logger log.Logger) (*Operator, error) {
|
||||
cfg, err := k8sutil.NewClusterConfig(conf.Host, conf.TLSInsecure, &conf.TLSConfig)
|
||||
|
@ -1091,6 +1101,10 @@ func (c *Operator) sync(key string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := c.createOrUpdateTLSAssetSecret(p); err != nil {
|
||||
return errors.Wrap(err, "creating tls asset secret failed")
|
||||
}
|
||||
|
||||
// Create governing service if it doesn't exist.
|
||||
svcClient := c.kclient.CoreV1().Services(p.Namespace)
|
||||
if err := k8sutil.CreateOrUpdateService(svcClient, makeStatefulSetService(p, c.config)); err != nil {
|
||||
|
@ -1351,6 +1365,7 @@ func (c *Operator) loadBasicAuthSecrets(
|
|||
}
|
||||
secrets[fmt.Sprintf("serviceMonitor/%s/%s/%d", mon.Namespace, mon.Name, i)] = credentials
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1387,6 +1402,94 @@ func (c *Operator) loadBasicAuthSecrets(
|
|||
|
||||
}
|
||||
|
||||
func (c *Operator) loadBearerTokensFromSecrets(mons map[string]*monitoringv1.ServiceMonitor) (map[string]BearerToken, error) {
|
||||
tokens := map[string]BearerToken{}
|
||||
nsSecretCache := make(map[string]*v1.Secret)
|
||||
|
||||
for _, mon := range mons {
|
||||
for i, ep := range mon.Spec.Endpoints {
|
||||
if ep.BearerTokenSecret.Name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
sClient := c.kclient.CoreV1().Secrets(mon.Namespace)
|
||||
token, err := getCredFromSecret(
|
||||
sClient,
|
||||
ep.BearerTokenSecret,
|
||||
"bearertoken",
|
||||
mon.Namespace+"/"+ep.BearerTokenSecret.Name,
|
||||
nsSecretCache,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"failed to extract endpoint bearertoken for servicemonitor %v from secret %v in namespace %v",
|
||||
mon.Name, ep.BearerTokenSecret.Name, mon.Namespace,
|
||||
)
|
||||
}
|
||||
|
||||
tokens[fmt.Sprintf("serviceMonitor/%s/%s/%d", mon.Namespace, mon.Name, i)] = BearerToken(token)
|
||||
}
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func (c *Operator) loadTLSAssetsFromSecrets(mons map[string]*monitoringv1.ServiceMonitor) (map[string]TLSAsset, error) {
|
||||
assets := map[string]TLSAsset{}
|
||||
nsSecretCache := make(map[string]*v1.Secret)
|
||||
|
||||
for _, mon := range mons {
|
||||
for _, ep := range mon.Spec.Endpoints {
|
||||
if ep.TLSConfig == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
prefix := mon.Namespace + "/"
|
||||
secretSelectors := map[string]*v1.SecretKeySelector{}
|
||||
if ep.TLSConfig.CASecret != nil {
|
||||
secretSelectors[prefix+ep.TLSConfig.CASecret.Name+"/"+ep.TLSConfig.CASecret.Key] = ep.TLSConfig.CASecret
|
||||
}
|
||||
if ep.TLSConfig.CertSecret != nil {
|
||||
|
||||
secretSelectors[prefix+ep.TLSConfig.CertSecret.Name+"/"+ep.TLSConfig.CertSecret.Key] = ep.TLSConfig.CertSecret
|
||||
}
|
||||
if ep.TLSConfig.KeySecret != nil {
|
||||
secretSelectors[prefix+ep.TLSConfig.KeySecret.Name+"/"+ep.TLSConfig.KeySecret.Key] = ep.TLSConfig.KeySecret
|
||||
}
|
||||
|
||||
for key, selector := range secretSelectors {
|
||||
sClient := c.kclient.CoreV1().Secrets(mon.Namespace)
|
||||
asset, err := getCredFromSecret(
|
||||
sClient,
|
||||
*selector,
|
||||
"tls config",
|
||||
// TODO: Is the cache key really necessary? Can't this be part of `getCredFromSecret`?
|
||||
key,
|
||||
nsSecretCache,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"failed to extract endpoint tls asset for servicemonitor %v from secret %v and key %v in namespace %v",
|
||||
mon.Name, selector.Name, selector.Key, mon.Namespace,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Namespacing via underscores seems rather hacky.
|
||||
assets[fmt.Sprintf(
|
||||
"%v_%v_%v",
|
||||
mon.Namespace,
|
||||
selector.Name,
|
||||
selector.Key,
|
||||
)] = TLSAsset(asset)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus, ruleConfigMapNames []string) error {
|
||||
smons, err := c.selectServiceMonitors(p)
|
||||
if err != nil {
|
||||
|
@ -1409,6 +1512,11 @@ func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus,
|
|||
return err
|
||||
}
|
||||
|
||||
bearerTokens, err := c.loadBearerTokensFromSecrets(smons)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
additionalScrapeConfigs, err := c.loadAdditionalScrapeConfigsSecret(p.Spec.AdditionalScrapeConfigs, SecretsInPromNS)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "loading additional scrape configs from Secret failed")
|
||||
|
@ -1428,6 +1536,7 @@ func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus,
|
|||
smons,
|
||||
pmons,
|
||||
basicAuthSecrets,
|
||||
bearerTokens,
|
||||
additionalScrapeConfigs,
|
||||
additionalAlertRelabelConfigs,
|
||||
additionalAlertManagerConfigs,
|
||||
|
@ -1475,9 +1584,73 @@ func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus,
|
|||
return err
|
||||
}
|
||||
|
||||
func (c *Operator) createOrUpdateTLSAssetSecret(p *monitoringv1.Prometheus) error {
|
||||
boolTrue := true
|
||||
sClient := c.kclient.CoreV1().Secrets(p.Namespace)
|
||||
|
||||
smons, err := c.selectServiceMonitors(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "selecting ServiceMonitors failed")
|
||||
}
|
||||
|
||||
tlsAssets, err := c.loadTLSAssetsFromSecrets(smons)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsAssetsSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: tlsAssetsSecretName(p.Name),
|
||||
Labels: c.config.Labels.Merge(managedByOperatorLabels),
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: p.APIVersion,
|
||||
BlockOwnerDeletion: &boolTrue,
|
||||
Controller: &boolTrue,
|
||||
Kind: p.Kind,
|
||||
Name: p.Name,
|
||||
UID: p.UID,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
}
|
||||
|
||||
for key, asset := range tlsAssets {
|
||||
tlsAssetsSecret.Data[key] = []byte(asset)
|
||||
}
|
||||
|
||||
oldSecretExists := true
|
||||
_, err = sClient.Get(tlsAssetsSecret.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return errors.Wrapf(
|
||||
err,
|
||||
"failed to check whether tls assets secret already exists for Prometheus %v in namespace %v",
|
||||
p.Name,
|
||||
p.Namespace,
|
||||
)
|
||||
}
|
||||
|
||||
oldSecretExists = false
|
||||
}
|
||||
|
||||
if oldSecretExists {
|
||||
_, err = sClient.Update(tlsAssetsSecret)
|
||||
} else {
|
||||
_, err = sClient.Create(tlsAssetsSecret)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create tls assets secret for Prometheus %v in namespace %v", p.Name, p.Namespace)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Operator) selectServiceMonitors(p *monitoringv1.Prometheus) (map[string]*monitoringv1.ServiceMonitor, error) {
|
||||
namespaces := []string{}
|
||||
// Selectors might overlap. Deduplicate them along the keyFunc.
|
||||
// Selectors (<namespace>/<name>) might overlap. Deduplicate them along the keyFunc.
|
||||
res := make(map[string]*monitoringv1.ServiceMonitor)
|
||||
|
||||
servMonSelector, err := metav1.LabelSelectorAsSelector(p.Spec.ServiceMonitorSelector)
|
||||
|
@ -1511,6 +1684,25 @@ func (c *Operator) selectServiceMonitors(p *monitoringv1.Prometheus) (map[string
|
|||
})
|
||||
}
|
||||
|
||||
// If denied by Prometheus spec, filter out all service monitors that access
|
||||
// the file system.
|
||||
if p.Spec.ArbitraryFSAccessThroughSMs.Deny {
|
||||
for namespaceAndName, sm := range res {
|
||||
for _, endpoint := range sm.Spec.Endpoints {
|
||||
if err := testForArbitraryFSAccess(endpoint); err != nil {
|
||||
delete(res, namespaceAndName)
|
||||
level.Warn(c.logger).Log(
|
||||
"msg", "skipping servicemonitor",
|
||||
"error", err.Error(),
|
||||
"servicemonitor", namespaceAndName,
|
||||
"namespace", p.Namespace,
|
||||
"prometheus", p.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serviceMonitors := []string{}
|
||||
for k := range res {
|
||||
serviceMonitors = append(serviceMonitors, k)
|
||||
|
@ -1563,6 +1755,27 @@ func (c *Operator) selectPodMonitors(p *monitoringv1.Prometheus) (map[string]*mo
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func testForArbitraryFSAccess(e monitoringv1.Endpoint) error {
|
||||
if e.BearerTokenFile != "" {
|
||||
return errors.New("it accesses file system via bearer token file which Prometheus specification prohibits")
|
||||
}
|
||||
|
||||
tlsConf := e.TLSConfig
|
||||
if tlsConf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := e.TLSConfig.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if tlsConf.CAFile != "" || tlsConf.CertFile != "" || tlsConf.KeyFile != "" {
|
||||
return errors.New("it accesses file system via tls config which Prometheus specification prohibits")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listMatchingNamespaces lists all the namespaces that match the provided
|
||||
// selector.
|
||||
func (c *Operator) listMatchingNamespaces(selector labels.Selector) ([]string, error) {
|
||||
|
|
|
@ -16,6 +16,7 @@ package prometheus
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -70,20 +71,30 @@ func stringMapToMapSlice(m map[string]string) yaml.MapSlice {
|
|||
return res
|
||||
}
|
||||
|
||||
func addTLStoYaml(cfg yaml.MapSlice, tls *v1.TLSConfig) yaml.MapSlice {
|
||||
func addTLStoYaml(cfg yaml.MapSlice, namespace string, tls *v1.TLSConfig) yaml.MapSlice {
|
||||
if tls != nil {
|
||||
pathPrefix := path.Join(tlsAssetsDir, namespace)
|
||||
tlsConfig := yaml.MapSlice{
|
||||
{Key: "insecure_skip_verify", Value: tls.InsecureSkipVerify},
|
||||
}
|
||||
if tls.CAFile != "" {
|
||||
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "ca_file", Value: tls.CAFile})
|
||||
}
|
||||
if tls.CASecret != nil {
|
||||
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "ca_file", Value: pathPrefix + "_" + tls.CASecret.Name + "_" + tls.CASecret.Key})
|
||||
}
|
||||
if tls.CertFile != "" {
|
||||
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "cert_file", Value: tls.CertFile})
|
||||
}
|
||||
if tls.CertSecret != nil {
|
||||
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "cert_file", Value: pathPrefix + "_" + tls.CertSecret.Name + "_" + tls.CertSecret.Key})
|
||||
}
|
||||
if tls.KeyFile != "" {
|
||||
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "key_file", Value: tls.KeyFile})
|
||||
}
|
||||
if tls.KeySecret != nil {
|
||||
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "key_file", Value: pathPrefix + "_" + tls.KeySecret.Name + "_" + tls.KeySecret.Key})
|
||||
}
|
||||
if tls.ServerName != "" {
|
||||
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "server_name", Value: tls.ServerName})
|
||||
}
|
||||
|
@ -136,6 +147,7 @@ func (cg *configGenerator) generateConfig(
|
|||
sMons map[string]*v1.ServiceMonitor,
|
||||
pMons map[string]*v1.PodMonitor,
|
||||
basicAuthSecrets map[string]BasicAuthCredentials,
|
||||
bearerTokens map[string]BearerToken,
|
||||
additionalScrapeConfigs []byte,
|
||||
additionalAlertRelabelConfigs []byte,
|
||||
additionalAlertManagerConfigs []byte,
|
||||
|
@ -206,7 +218,7 @@ func (cg *configGenerator) generateConfig(
|
|||
var scrapeConfigs []yaml.MapSlice
|
||||
for _, identifier := range sMonIdentifiers {
|
||||
for i, ep := range sMons[identifier].Spec.Endpoints {
|
||||
scrapeConfigs = append(scrapeConfigs, cg.generateServiceMonitorConfig(version, sMons[identifier], ep, i, apiserverConfig, basicAuthSecrets))
|
||||
scrapeConfigs = append(scrapeConfigs, cg.generateServiceMonitorConfig(version, sMons[identifier], ep, i, apiserverConfig, basicAuthSecrets, bearerTokens))
|
||||
}
|
||||
}
|
||||
for _, identifier := range pMonIdentifiers {
|
||||
|
@ -523,7 +535,15 @@ func (cg *configGenerator) generatePodMonitorConfig(version semver.Version, m *v
|
|||
return cfg
|
||||
}
|
||||
|
||||
func (cg *configGenerator) generateServiceMonitorConfig(version semver.Version, m *v1.ServiceMonitor, ep v1.Endpoint, i int, apiserverConfig *v1.APIServerConfig, basicAuthSecrets map[string]BasicAuthCredentials) yaml.MapSlice {
|
||||
func (cg *configGenerator) generateServiceMonitorConfig(
|
||||
version semver.Version,
|
||||
m *v1.ServiceMonitor,
|
||||
ep v1.Endpoint,
|
||||
i int,
|
||||
apiserverConfig *v1.APIServerConfig,
|
||||
basicAuthSecrets map[string]BasicAuthCredentials,
|
||||
bearerTokens map[string]BearerToken,
|
||||
) yaml.MapSlice {
|
||||
cfg := yaml.MapSlice{
|
||||
{
|
||||
Key: "job_name",
|
||||
|
@ -568,12 +588,18 @@ func (cg *configGenerator) generateServiceMonitorConfig(version semver.Version,
|
|||
cfg = append(cfg, yaml.MapItem{Key: "scheme", Value: ep.Scheme})
|
||||
}
|
||||
|
||||
cfg = addTLStoYaml(cfg, ep.TLSConfig)
|
||||
cfg = addTLStoYaml(cfg, m.Namespace, ep.TLSConfig)
|
||||
|
||||
if ep.BearerTokenFile != "" {
|
||||
cfg = append(cfg, yaml.MapItem{Key: "bearer_token_file", Value: ep.BearerTokenFile})
|
||||
}
|
||||
|
||||
if ep.BearerTokenSecret.Name != "" {
|
||||
if s, ok := bearerTokens[fmt.Sprintf("serviceMonitor/%s/%s/%d", m.Namespace, m.Name, i)]; ok {
|
||||
cfg = append(cfg, yaml.MapItem{Key: "bearer_token", Value: s})
|
||||
}
|
||||
}
|
||||
|
||||
if ep.BasicAuth != nil {
|
||||
if s, ok := basicAuthSecrets[fmt.Sprintf("serviceMonitor/%s/%s/%d", m.Namespace, m.Name, i)]; ok {
|
||||
cfg = append(cfg, yaml.MapItem{
|
||||
|
@ -899,7 +925,9 @@ func (cg *configGenerator) generateK8SSDConfig(namespaces []string, apiserverCon
|
|||
k8sSDConfig = append(k8sSDConfig, yaml.MapItem{Key: "bearer_token_file", Value: apiserverConfig.BearerTokenFile})
|
||||
}
|
||||
|
||||
k8sSDConfig = addTLStoYaml(k8sSDConfig, apiserverConfig.TLSConfig)
|
||||
// TODO: If we want to support secret refs for k8s service discovery tls
|
||||
// config as well, make sure to path the right namespace here.
|
||||
k8sSDConfig = addTLStoYaml(k8sSDConfig, "", apiserverConfig.TLSConfig)
|
||||
}
|
||||
|
||||
return yaml.MapItem{
|
||||
|
@ -924,7 +952,9 @@ func (cg *configGenerator) generateAlertmanagerConfig(version semver.Version, am
|
|||
{Key: "scheme", Value: am.Scheme},
|
||||
}
|
||||
|
||||
cfg = addTLStoYaml(cfg, am.TLSConfig)
|
||||
// TODO: If we want to support secret refs for alertmanager config tls
|
||||
// config as well, make sure to path the right namespace here.
|
||||
cfg = addTLStoYaml(cfg, "", am.TLSConfig)
|
||||
|
||||
switch version.Major {
|
||||
case 1:
|
||||
|
@ -1021,7 +1051,9 @@ func (cg *configGenerator) generateRemoteReadConfig(version semver.Version, spec
|
|||
cfg = append(cfg, yaml.MapItem{Key: "bearer_token_file", Value: spec.BearerTokenFile})
|
||||
}
|
||||
|
||||
cfg = addTLStoYaml(cfg, spec.TLSConfig)
|
||||
// TODO: If we want to support secret refs for remote read tls
|
||||
// config as well, make sure to path the right namespace here.
|
||||
cfg = addTLStoYaml(cfg, "", spec.TLSConfig)
|
||||
|
||||
if spec.ProxyURL != "" {
|
||||
cfg = append(cfg, yaml.MapItem{Key: "proxy_url", Value: spec.ProxyURL})
|
||||
|
@ -1110,7 +1142,9 @@ func (cg *configGenerator) generateRemoteWriteConfig(version semver.Version, spe
|
|||
cfg = append(cfg, yaml.MapItem{Key: "bearer_token_file", Value: spec.BearerTokenFile})
|
||||
}
|
||||
|
||||
cfg = addTLStoYaml(cfg, spec.TLSConfig)
|
||||
// TODO: If we want to support secret refs for remote write tls
|
||||
// config as well, make sure to path the right namespace here.
|
||||
cfg = addTLStoYaml(cfg, "", spec.TLSConfig)
|
||||
|
||||
if spec.ProxyURL != "" {
|
||||
cfg = append(cfg, yaml.MapItem{Key: "proxy_url", Value: spec.ProxyURL})
|
||||
|
|
|
@ -254,6 +254,7 @@ func TestAlertmanagerBearerToken(t *testing.T) {
|
|||
nil,
|
||||
nil,
|
||||
map[string]BasicAuthCredentials{},
|
||||
map[string]BearerToken{},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
|
@ -329,6 +330,7 @@ func TestAdditionalAlertRelabelConfigs(t *testing.T) {
|
|||
nil,
|
||||
nil,
|
||||
map[string]BasicAuthCredentials{},
|
||||
map[string]BearerToken{},
|
||||
nil,
|
||||
[]byte(`- action: drop
|
||||
source_labels: [__meta_kubernetes_node_name]
|
||||
|
@ -408,6 +410,7 @@ func TestAdditionalAlertmanagers(t *testing.T) {
|
|||
nil,
|
||||
nil,
|
||||
map[string]BasicAuthCredentials{},
|
||||
map[string]BearerToken{},
|
||||
nil,
|
||||
nil,
|
||||
[]byte(`- static_configs:
|
||||
|
@ -500,6 +503,7 @@ func TestTargetLabels(t *testing.T) {
|
|||
},
|
||||
nil,
|
||||
map[string]BasicAuthCredentials{},
|
||||
map[string]BearerToken{},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
|
@ -622,6 +626,7 @@ func TestPodTargetLabels(t *testing.T) {
|
|||
},
|
||||
nil,
|
||||
map[string]BasicAuthCredentials{},
|
||||
map[string]BearerToken{},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
|
@ -842,6 +847,7 @@ func TestEmptyEndointPorts(t *testing.T) {
|
|||
},
|
||||
nil,
|
||||
map[string]BasicAuthCredentials{},
|
||||
map[string]BearerToken{},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
|
@ -971,6 +977,7 @@ func generateTestConfig(version string) ([]byte, error) {
|
|||
makeServiceMonitors(),
|
||||
makePodMonitors(),
|
||||
map[string]BasicAuthCredentials{},
|
||||
map[string]BearerToken{},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
|
|
|
@ -41,6 +41,7 @@ const (
|
|||
storageDir = "/prometheus"
|
||||
confDir = "/etc/prometheus/config"
|
||||
confOutDir = "/etc/prometheus/config_out"
|
||||
tlsAssetsDir = "/etc/prometheus/certs"
|
||||
rulesDir = "/etc/prometheus/rules"
|
||||
secretsDir = "/etc/prometheus/secrets/"
|
||||
configmapsDir = "/etc/prometheus/configmaps/"
|
||||
|
@ -496,6 +497,14 @@ func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMapName
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "tls-assets",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: tlsAssetsSecretName(p.Name),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "config-out",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
|
@ -530,6 +539,11 @@ func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMapName
|
|||
ReadOnly: true,
|
||||
MountPath: confOutDir,
|
||||
},
|
||||
{
|
||||
Name: "tls-assets",
|
||||
ReadOnly: true,
|
||||
MountPath: tlsAssetsDir,
|
||||
},
|
||||
{
|
||||
Name: volName,
|
||||
MountPath: storageDir,
|
||||
|
@ -894,7 +908,11 @@ func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMapName
|
|||
}
|
||||
|
||||
func configSecretName(name string) string {
|
||||
return prefixedName(name)
|
||||
return fmt.Sprintf("%s-config", prefixedName(name))
|
||||
}
|
||||
|
||||
func tlsAssetsSecretName(name string) string {
|
||||
return fmt.Sprintf("%s-tls-assets", prefixedName(name))
|
||||
}
|
||||
|
||||
func volumeName(name string) string {
|
||||
|
|
|
@ -185,6 +185,14 @@ func TestStatefulSetVolumeInitial(t *testing.T) {
|
|||
MountPath: "/etc/prometheus/config_out",
|
||||
SubPath: "",
|
||||
},
|
||||
{
|
||||
Name: "tls-assets",
|
||||
ReadOnly: true,
|
||||
MountPath: "/etc/prometheus/certs",
|
||||
SubPath: "",
|
||||
MountPropagation: nil,
|
||||
SubPathExpr: "",
|
||||
},
|
||||
{
|
||||
Name: "prometheus-volume-init-test-db",
|
||||
ReadOnly: false,
|
||||
|
@ -215,6 +223,14 @@ func TestStatefulSetVolumeInitial(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "tls-assets",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: tlsAssetsSecretName("volume-init-test"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "config-out",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
REG := quay.io/coreos
|
||||
APP := basic-auth-test-app
|
||||
VERSION ?= $(shell cat VERSION)
|
||||
|
||||
all: build push
|
||||
|
||||
build:
|
||||
@GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -mod=vendor -o basic-auth-test-app main.go
|
||||
@docker build --build-arg VERSION=$(VERSION) -t $(REG)/$(APP):$(VERSION) --file $(APP).dockerfile .
|
||||
@rm $(APP)
|
||||
|
||||
push:
|
||||
@docker push $(REG)/$(APP):$(VERSION)
|
|
@ -1 +0,0 @@
|
|||
0.1.0
|
|
@ -1,8 +0,0 @@
|
|||
FROM alpine:3.7
|
||||
|
||||
ARG VERSION="$VERSION"
|
||||
ENV VERSION="$VERSION"
|
||||
|
||||
COPY basic-auth-test-app /
|
||||
|
||||
ENTRYPOINT ["/basic-auth-test-app"]
|
|
@ -1,65 +0,0 @@
|
|||
// Copyright 2016 The prometheus-operator Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
|
||||
if checkAuth(w, r) {
|
||||
promhttp.Handler().ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="MY REALM"`)
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte("401 Unauthorized\n"))
|
||||
})
|
||||
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, time.Now().String())
|
||||
fmt.Fprintf(w, "\nAppVersion:"+os.Getenv("VERSION"))
|
||||
}
|
||||
|
||||
func checkAuth(w http.ResponseWriter, r *http.Request) bool {
|
||||
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
||||
if len(s) != 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(s[1])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pair := strings.SplitN(string(b), ":", 2)
|
||||
if len(pair) != 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
return pair[0] == "user" && pair[1] == "pass"
|
||||
}
|
|
@ -149,7 +149,9 @@ func testAllNS(t *testing.T) {
|
|||
"PromExposingWithKubernetesAPI": testPromExposingWithKubernetesAPI,
|
||||
"PromDiscoverTargetPort": testPromDiscoverTargetPort,
|
||||
"PromOpMatchPromAndServMonInDiffNSs": testPromOpMatchPromAndServMonInDiffNSs,
|
||||
"PromGetBasicAuthSecret": testPromGetBasicAuthSecret,
|
||||
"PromGetAuthSecret": testPromGetAuthSecret,
|
||||
"PromArbitraryFSAcc": testPromArbitraryFSAcc,
|
||||
"PromTLSConfigViaSecret": testPromTLSConfigViaSecret,
|
||||
"Thanos": testThanos,
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
@ -299,7 +300,7 @@ scrape_configs:
|
|||
|
||||
cfg := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("prometheus-%s", name),
|
||||
Name: fmt.Sprintf("prometheus-%s-config", name),
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"prometheus.yaml.gz": firstConfigCompressed,
|
||||
|
@ -477,7 +478,7 @@ func testPromAdditionalAlertManagerConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
err = wait.Poll(time.Second, 5*time.Minute, func() (done bool, err error) {
|
||||
response, err := framework.QueryPrometheusSVC(ns, svc.Name, "/api/v1/alertmanagers", map[string]string{})
|
||||
response, err := framework.PrometheusSVCGetRequest(ns, svc.Name, "/api/v1/alertmanagers", map[string]string{})
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
@ -862,7 +863,18 @@ func testPromOnlyUpdatedOnRelevantChanges(t *testing.T) {
|
|||
KubeClient.
|
||||
CoreV1().
|
||||
Secrets(ns).
|
||||
Get("prometheus-"+prometheusName, metav1.GetOptions{})
|
||||
Get("prometheus-"+prometheusName+"-config", metav1.GetOptions{})
|
||||
},
|
||||
MaxExpectedChanges: 2,
|
||||
},
|
||||
{
|
||||
Name: "tlsAssetSecret",
|
||||
Getter: func(prometheusName string) (versionedResource, error) {
|
||||
return framework.
|
||||
KubeClient.
|
||||
CoreV1().
|
||||
Secrets(ns).
|
||||
Get("prometheus-"+prometheusName+"-tls-assets", metav1.GetOptions{})
|
||||
},
|
||||
MaxExpectedChanges: 2,
|
||||
},
|
||||
|
@ -958,7 +970,7 @@ func testPromOnlyUpdatedOnRelevantChanges(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = isDiscoveryWorking(ns, pSVC.Name, prometheus.Name)
|
||||
err = framework.WaitForDiscoveryWorking(ns, pSVC.Name, prometheus.Name)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "validating Prometheus target discovery failed"))
|
||||
}
|
||||
|
@ -1055,12 +1067,12 @@ func testPromDiscovery(t *testing.T) {
|
|||
ctx.AddFinalizerFn(finalizerFn)
|
||||
}
|
||||
|
||||
_, err = framework.KubeClient.CoreV1().Secrets(ns).Get(fmt.Sprintf("prometheus-%s", prometheusName), metav1.GetOptions{})
|
||||
_, err = framework.KubeClient.CoreV1().Secrets(ns).Get(fmt.Sprintf("prometheus-%s-config", prometheusName), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal("Generated Secret could not be retrieved: ", err)
|
||||
}
|
||||
|
||||
err = isDiscoveryWorking(ns, svc.Name, prometheusName)
|
||||
err = framework.WaitForDiscoveryWorking(ns, svc.Name, prometheusName)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "validating Prometheus target discovery failed"))
|
||||
}
|
||||
|
@ -1098,7 +1110,7 @@ func testPromAlertmanagerDiscovery(t *testing.T) {
|
|||
t.Fatalf("Creating ServiceMonitor failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = framework.KubeClient.CoreV1().Secrets(ns).Get(fmt.Sprintf("prometheus-%s", prometheusName), metav1.GetOptions{})
|
||||
_, err = framework.KubeClient.CoreV1().Secrets(ns).Get(fmt.Sprintf("prometheus-%s-config", prometheusName), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Generated Secret could not be retrieved: %v", err)
|
||||
}
|
||||
|
@ -1192,12 +1204,12 @@ func testPromDiscoverTargetPort(t *testing.T) {
|
|||
ctx.AddFinalizerFn(finalizerFn)
|
||||
}
|
||||
|
||||
_, err := framework.KubeClient.CoreV1().Secrets(ns).Get(fmt.Sprintf("prometheus-%s", prometheusName), metav1.GetOptions{})
|
||||
_, err := framework.KubeClient.CoreV1().Secrets(ns).Get(fmt.Sprintf("prometheus-%s-config", prometheusName), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal("Generated Secret could not be retrieved: ", err)
|
||||
}
|
||||
|
||||
err = isDiscoveryWorking(ns, svc.Name, prometheusName)
|
||||
err = framework.WaitForDiscoveryWorking(ns, svc.Name, prometheusName)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "validating Prometheus target discovery failed"))
|
||||
}
|
||||
|
@ -1249,7 +1261,7 @@ func testPromOpMatchPromAndServMonInDiffNSs(t *testing.T) {
|
|||
ctx.AddFinalizerFn(finalizerFn)
|
||||
}
|
||||
|
||||
resp, err := framework.QueryPrometheusSVC(prometheusNSName, svc.Name, "/api/v1/status/config", map[string]string{})
|
||||
resp, err := framework.PrometheusSVCGetRequest(prometheusNSName, svc.Name, "/api/v1/status/config", map[string]string{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -1353,105 +1365,146 @@ func testThanos(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testPromGetBasicAuthSecret(t *testing.T) {
|
||||
func testPromGetAuthSecret(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := framework.NewTestCtx(t)
|
||||
defer ctx.Cleanup(t)
|
||||
ns := ctx.CreateNamespace(t, framework.KubeClient)
|
||||
ctx.SetupPrometheusRBACGlobal(t, ns, framework.KubeClient)
|
||||
|
||||
name := "test"
|
||||
|
||||
maptest := make(map[string]string)
|
||||
maptest["tc"] = ns
|
||||
prometheusCRD := framework.MakeBasicPrometheus(ns, name, name, 1)
|
||||
prometheusCRD.Spec.ServiceMonitorNamespaceSelector = &metav1.LabelSelector{
|
||||
MatchLabels: maptest,
|
||||
}
|
||||
|
||||
if _, err := framework.CreatePrometheusAndWaitUntilReady(ns, prometheusCRD); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testNamespace := ctx.CreateNamespace(t, framework.KubeClient)
|
||||
|
||||
err := testFramework.AddLabelsToNamespace(framework.KubeClient, testNamespace, maptest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
simple, err := testFramework.MakeDeployment("../../test/framework/ressources/basic-auth-app-deployment.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := testFramework.CreateDeployment(framework.KubeClient, testNamespace, simple); err != nil {
|
||||
t.Fatal("Creating simple basic auth app failed: ", err)
|
||||
}
|
||||
|
||||
authSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"user": []byte("user"),
|
||||
"password": []byte("pass"),
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := framework.KubeClient.CoreV1().Secrets(testNamespace).Create(authSecret); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
svc := &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: map[string]string{
|
||||
"group": name,
|
||||
},
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeLoadBalancer,
|
||||
Ports: []v1.ServicePort{
|
||||
v1.ServicePort{
|
||||
Name: "web",
|
||||
Port: 8080,
|
||||
tests := []struct {
|
||||
name string
|
||||
secret *v1.Secret
|
||||
serviceMonitor func() *monitoringv1.ServiceMonitor
|
||||
}{
|
||||
{
|
||||
name: "basic-auth",
|
||||
secret: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"user": []byte("user"),
|
||||
"password": []byte("pass"),
|
||||
},
|
||||
},
|
||||
Selector: map[string]string{
|
||||
"group": name,
|
||||
serviceMonitor: func() *monitoringv1.ServiceMonitor {
|
||||
sm := framework.MakeBasicServiceMonitor(name)
|
||||
sm.Spec.Endpoints[0].BasicAuth = &monitoringv1.BasicAuth{
|
||||
Username: v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "user",
|
||||
},
|
||||
Password: v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "password",
|
||||
},
|
||||
}
|
||||
|
||||
return sm
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bearer-token",
|
||||
secret: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"bearertoken": []byte("abc"),
|
||||
},
|
||||
},
|
||||
serviceMonitor: func() *monitoringv1.ServiceMonitor {
|
||||
sm := framework.MakeBasicServiceMonitor(name)
|
||||
sm.Spec.Endpoints[0].BearerTokenSecret = v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "bearertoken",
|
||||
}
|
||||
sm.Spec.Endpoints[0].Path = "/bearer-token"
|
||||
return sm
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sm := framework.MakeBasicServiceMonitor(name)
|
||||
sm.Spec.Endpoints[0].BasicAuth = &monitoringv1.BasicAuth{
|
||||
Username: v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "user",
|
||||
},
|
||||
Password: v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "password",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := framework.NewTestCtx(t)
|
||||
defer ctx.Cleanup(t)
|
||||
ns := ctx.CreateNamespace(t, framework.KubeClient)
|
||||
ctx.SetupPrometheusRBACGlobal(t, ns, framework.KubeClient)
|
||||
|
||||
if finalizerFn, err := testFramework.CreateServiceAndWaitUntilReady(framework.KubeClient, testNamespace, svc); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
ctx.AddFinalizerFn(finalizerFn)
|
||||
}
|
||||
maptest := make(map[string]string)
|
||||
maptest["tc"] = ns
|
||||
prometheusCRD := framework.MakeBasicPrometheus(ns, name, name, 1)
|
||||
prometheusCRD.Spec.ServiceMonitorNamespaceSelector = &metav1.LabelSelector{
|
||||
MatchLabels: maptest,
|
||||
}
|
||||
prometheusCRD.Spec.ScrapeInterval = "1s"
|
||||
|
||||
if _, err := framework.MonClientV1.ServiceMonitors(testNamespace).Create(sm); err != nil {
|
||||
t.Fatal("Creating ServiceMonitor failed: ", err)
|
||||
}
|
||||
if _, err := framework.CreatePrometheusAndWaitUntilReady(ns, prometheusCRD); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testNamespace := ctx.CreateNamespace(t, framework.KubeClient)
|
||||
|
||||
if err := framework.WaitForTargets(ns, "prometheus-operated", 1); err != nil {
|
||||
t.Fatal(err)
|
||||
err := testFramework.AddLabelsToNamespace(framework.KubeClient, testNamespace, maptest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
simple, err := testFramework.MakeDeployment("../../test/framework/ressources/basic-auth-app-deployment.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := testFramework.CreateDeployment(framework.KubeClient, testNamespace, simple); err != nil {
|
||||
t.Fatal("Creating simple basic auth app failed: ", err)
|
||||
}
|
||||
|
||||
authSecret := test.secret
|
||||
if _, err := framework.KubeClient.CoreV1().Secrets(testNamespace).Create(authSecret); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
svc := &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: map[string]string{
|
||||
"group": name,
|
||||
},
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeLoadBalancer,
|
||||
Ports: []v1.ServicePort{
|
||||
v1.ServicePort{
|
||||
Name: "web",
|
||||
Port: 8080,
|
||||
},
|
||||
},
|
||||
Selector: map[string]string{
|
||||
"group": name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sm := test.serviceMonitor()
|
||||
if finalizerFn, err := testFramework.CreateServiceAndWaitUntilReady(framework.KubeClient, testNamespace, svc); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
ctx.AddFinalizerFn(finalizerFn)
|
||||
}
|
||||
|
||||
if _, err := framework.MonClientV1.ServiceMonitors(testNamespace).Create(sm); err != nil {
|
||||
t.Fatal("Creating ServiceMonitor failed: ", err)
|
||||
}
|
||||
|
||||
if err := framework.WaitForTargets(ns, "prometheus-operated", 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1607,79 +1660,430 @@ func testOperatorNSScope(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func isDiscoveryWorking(ns, svcName, prometheusName string) error {
|
||||
var loopErr error
|
||||
// testPromArbitraryFSAcc tests the
|
||||
// github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1.PrometheusSpec.ArbitraryFSAccessThroughSMs
|
||||
// configuration with the service monitor bearer token and tls assets option.
|
||||
func testPromArbitraryFSAcc(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := wait.Poll(time.Second, 5*framework.DefaultTimeout, func() (bool, error) {
|
||||
pods, loopErr := framework.KubeClient.CoreV1().Pods(ns).List(prometheus.ListOptions(prometheusName))
|
||||
if loopErr != nil {
|
||||
return false, loopErr
|
||||
}
|
||||
if 1 != len(pods.Items) {
|
||||
return false, nil
|
||||
}
|
||||
podIP := pods.Items[0].Status.PodIP
|
||||
expectedTargets := []string{fmt.Sprintf("http://%s:9090/metrics", podIP)}
|
||||
name := "test"
|
||||
|
||||
activeTargets, loopErr := framework.GetActiveTargets(ns, svcName)
|
||||
if loopErr != nil {
|
||||
return false, loopErr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
arbitraryFSAccessThroughSMsConfig monitoringv1.ArbitraryFSAccessThroughSMsConfig
|
||||
endpoint monitoringv1.Endpoint
|
||||
expectTargets bool
|
||||
}{
|
||||
//
|
||||
// Bearer tokens:
|
||||
//
|
||||
{
|
||||
name: "allowed-bearer-file",
|
||||
arbitraryFSAccessThroughSMsConfig: monitoringv1.ArbitraryFSAccessThroughSMsConfig{
|
||||
Deny: false,
|
||||
},
|
||||
endpoint: monitoringv1.Endpoint{
|
||||
Port: "web",
|
||||
BearerTokenFile: "abc",
|
||||
},
|
||||
expectTargets: true,
|
||||
},
|
||||
{
|
||||
name: "denied-bearer-file",
|
||||
arbitraryFSAccessThroughSMsConfig: monitoringv1.ArbitraryFSAccessThroughSMsConfig{
|
||||
Deny: true,
|
||||
},
|
||||
endpoint: monitoringv1.Endpoint{
|
||||
Port: "web",
|
||||
BearerTokenFile: "abc",
|
||||
},
|
||||
expectTargets: false,
|
||||
},
|
||||
{
|
||||
name: "allowed-bearer-secret",
|
||||
arbitraryFSAccessThroughSMsConfig: monitoringv1.ArbitraryFSAccessThroughSMsConfig{
|
||||
Deny: false,
|
||||
},
|
||||
endpoint: monitoringv1.Endpoint{
|
||||
Port: "web",
|
||||
BearerTokenSecret: v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "bearer-token",
|
||||
},
|
||||
},
|
||||
expectTargets: true,
|
||||
},
|
||||
{
|
||||
name: "denied-bearer-secret",
|
||||
arbitraryFSAccessThroughSMsConfig: monitoringv1.ArbitraryFSAccessThroughSMsConfig{
|
||||
Deny: true,
|
||||
},
|
||||
endpoint: monitoringv1.Endpoint{
|
||||
Port: "web",
|
||||
BearerTokenSecret: v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "bearer-token",
|
||||
},
|
||||
},
|
||||
expectTargets: true,
|
||||
},
|
||||
//
|
||||
// TLS assets:
|
||||
//
|
||||
{
|
||||
name: "allowed-tls-file",
|
||||
arbitraryFSAccessThroughSMsConfig: monitoringv1.ArbitraryFSAccessThroughSMsConfig{
|
||||
Deny: false,
|
||||
},
|
||||
endpoint: monitoringv1.Endpoint{
|
||||
Port: "web",
|
||||
TLSConfig: &monitoringv1.TLSConfig{
|
||||
CAFile: "/etc/ca-certificates/example-ca.pem",
|
||||
CertFile: "/etc/ca-certificates/example-cert.pem",
|
||||
KeyFile: "/etc/ca-certificates/example-key.pem",
|
||||
},
|
||||
},
|
||||
expectTargets: true,
|
||||
},
|
||||
{
|
||||
name: "denied-tls-file",
|
||||
arbitraryFSAccessThroughSMsConfig: monitoringv1.ArbitraryFSAccessThroughSMsConfig{
|
||||
Deny: true,
|
||||
},
|
||||
endpoint: monitoringv1.Endpoint{
|
||||
Port: "web",
|
||||
TLSConfig: &monitoringv1.TLSConfig{
|
||||
CAFile: "/etc/ca-certificates/example-ca.pem",
|
||||
CertFile: "/etc/ca-certificates/example-cert.pem",
|
||||
KeyFile: "/etc/ca-certificates/example-key.pem",
|
||||
},
|
||||
},
|
||||
expectTargets: false,
|
||||
},
|
||||
{
|
||||
name: "allowed-tls-secret",
|
||||
arbitraryFSAccessThroughSMsConfig: monitoringv1.ArbitraryFSAccessThroughSMsConfig{
|
||||
Deny: false,
|
||||
},
|
||||
endpoint: monitoringv1.Endpoint{
|
||||
Port: "web",
|
||||
TLSConfig: &monitoringv1.TLSConfig{
|
||||
InsecureSkipVerify: true,
|
||||
CASecret: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "cert.pem",
|
||||
},
|
||||
CertSecret: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "cert.pem",
|
||||
},
|
||||
KeySecret: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "key.pem",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectTargets: true,
|
||||
},
|
||||
{
|
||||
name: "denied-tls-secret",
|
||||
arbitraryFSAccessThroughSMsConfig: monitoringv1.ArbitraryFSAccessThroughSMsConfig{
|
||||
Deny: true,
|
||||
},
|
||||
endpoint: monitoringv1.Endpoint{
|
||||
Port: "web",
|
||||
TLSConfig: &monitoringv1.TLSConfig{
|
||||
InsecureSkipVerify: true,
|
||||
CASecret: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "cert.pem",
|
||||
},
|
||||
CertSecret: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "cert.pem",
|
||||
},
|
||||
KeySecret: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "key.pem",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectTargets: true,
|
||||
},
|
||||
}
|
||||
|
||||
if loopErr = assertExpectedTargets(activeTargets, expectedTargets); loopErr != nil {
|
||||
return false, nil
|
||||
}
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
working, loopErr := basicQueryWorking(ns, svcName)
|
||||
if loopErr != nil {
|
||||
return false, loopErr
|
||||
}
|
||||
if !working {
|
||||
return false, nil
|
||||
}
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
return true, nil
|
||||
})
|
||||
ctx := framework.NewTestCtx(t)
|
||||
defer ctx.Cleanup(t)
|
||||
ns := ctx.CreateNamespace(t, framework.KubeClient)
|
||||
ctx.SetupPrometheusRBAC(t, ns, framework.KubeClient)
|
||||
|
||||
// Create secret either used by bearer token secret key ref, or tls
|
||||
// asset key ref.
|
||||
cert, err := ioutil.ReadFile("../../test/instrumented-sample-app/certs/cert.pem")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load cert.pem: %v", err)
|
||||
}
|
||||
|
||||
key, err := ioutil.ReadFile("../../test/instrumented-sample-app/certs/key.pem")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load key.pem: %v", err)
|
||||
}
|
||||
|
||||
tlsCertsSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"cert.pem": cert,
|
||||
"key.pem": key,
|
||||
"bearer-token": []byte("abc"),
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := framework.KubeClient.CoreV1().Secrets(ns).Create(tlsCertsSecret); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
prometheusCRD := framework.MakeBasicPrometheus(ns, name, name, 1)
|
||||
prometheusCRD.Namespace = ns
|
||||
prometheusCRD.Spec.ArbitraryFSAccessThroughSMs = test.arbitraryFSAccessThroughSMsConfig
|
||||
|
||||
if _, err := framework.CreatePrometheusAndWaitUntilReady(ns, prometheusCRD); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
svc := framework.MakePrometheusService(prometheusCRD.Name, name, v1.ServiceTypeClusterIP)
|
||||
if _, err := testFramework.CreateServiceAndWaitUntilReady(framework.KubeClient, ns, svc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s := framework.MakeBasicServiceMonitor(name)
|
||||
s.Spec.Endpoints[0] = test.endpoint
|
||||
if _, err := framework.MonClientV1.ServiceMonitors(ns).Create(s); err != nil {
|
||||
t.Fatal("creating ServiceMonitor failed: ", err)
|
||||
}
|
||||
|
||||
if test.expectTargets {
|
||||
if err := framework.WaitForTargets(ns, svc.Name, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure Prometheus has enough time to reload.
|
||||
time.Sleep(2 * time.Minute)
|
||||
if err := framework.WaitForTargets(ns, svc.Name, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// testPromTLSConfigViaSecret tests the service monitor endpoint option to load
|
||||
// certificate assets via Kubernetes secrets into the Prometheus container.
|
||||
func testPromTLSConfigViaSecret(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := framework.NewTestCtx(t)
|
||||
defer ctx.Cleanup(t)
|
||||
|
||||
ns := ctx.CreateNamespace(t, framework.KubeClient)
|
||||
ctx.SetupPrometheusRBAC(t, ns, framework.KubeClient)
|
||||
name := "test"
|
||||
|
||||
//
|
||||
// Setup sample app.
|
||||
//
|
||||
|
||||
cert, err := ioutil.ReadFile("../../test/instrumented-sample-app/certs/cert.pem")
|
||||
if err != nil {
|
||||
return fmt.Errorf("waiting for Prometheus to discover targets failed: %v: %v", err, loopErr)
|
||||
t.Fatalf("failed to load cert.pem: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type resultVector struct {
|
||||
Metric map[string]string `json:"metric"`
|
||||
Value []interface{} `json:"value"`
|
||||
}
|
||||
|
||||
type queryResult struct {
|
||||
ResultType string `json:"resultType"`
|
||||
Result []*resultVector `json:"result"`
|
||||
}
|
||||
|
||||
type prometheusQueryAPIResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data *queryResult `json:"data"`
|
||||
}
|
||||
|
||||
func basicQueryWorking(ns, svcName string) (bool, error) {
|
||||
response, err := framework.QueryPrometheusSVC(ns, svcName, "/api/v1/query", map[string]string{"query": "up"})
|
||||
key, err := ioutil.ReadFile("../../test/instrumented-sample-app/certs/key.pem")
|
||||
if err != nil {
|
||||
return false, err
|
||||
t.Fatalf("failed to load key.pem: %v", err)
|
||||
}
|
||||
|
||||
rq := prometheusQueryAPIResponse{}
|
||||
if err := json.NewDecoder(bytes.NewBuffer(response)).Decode(&rq); err != nil {
|
||||
return false, err
|
||||
tlsCertsSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"cert.pem": cert,
|
||||
"key.pem": key,
|
||||
},
|
||||
}
|
||||
|
||||
if rq.Status != "success" && rq.Data.Result[0].Value[1] == "1" {
|
||||
log.Printf("Query Response not successful.")
|
||||
return false, nil
|
||||
if _, err := framework.KubeClient.CoreV1().Secrets(ns).Create(tlsCertsSecret); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
simple, err := testFramework.MakeDeployment("../../test/framework/ressources/basic-auth-app-deployment.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
simple.Spec.Template.Spec.Containers[0].Args = []string{"--cert-path=/etc/certs"}
|
||||
|
||||
simple.Spec.Template.Spec.Volumes = []v1.Volume{
|
||||
v1.Volume{
|
||||
Name: "tls-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: tlsCertsSecret.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
simple.Spec.Template.Spec.Containers[0].VolumeMounts = []v1.VolumeMount{
|
||||
{
|
||||
Name: simple.Spec.Template.Spec.Volumes[0].Name,
|
||||
MountPath: "/etc/certs",
|
||||
},
|
||||
}
|
||||
|
||||
if err := testFramework.CreateDeployment(framework.KubeClient, ns, simple); err != nil {
|
||||
t.Fatal("Creating simple basic auth app failed: ", err)
|
||||
}
|
||||
|
||||
svc := &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: map[string]string{
|
||||
"group": name,
|
||||
},
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeLoadBalancer,
|
||||
Ports: []v1.ServicePort{
|
||||
v1.ServicePort{
|
||||
Name: "web",
|
||||
Port: 8080,
|
||||
},
|
||||
v1.ServicePort{
|
||||
Name: "mtls",
|
||||
Port: 8081,
|
||||
},
|
||||
},
|
||||
Selector: map[string]string{
|
||||
"group": name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := testFramework.CreateServiceAndWaitUntilReady(framework.KubeClient, ns, svc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
//
|
||||
// Setup monitoring.
|
||||
//
|
||||
|
||||
sm := framework.MakeBasicServiceMonitor(name)
|
||||
sm.Spec.Endpoints = []monitoringv1.Endpoint{
|
||||
{
|
||||
Port: "mtls",
|
||||
Interval: "30s",
|
||||
Scheme: "https",
|
||||
TLSConfig: &monitoringv1.TLSConfig{
|
||||
InsecureSkipVerify: true,
|
||||
CASecret: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: tlsCertsSecret.Name,
|
||||
},
|
||||
Key: "cert.pem",
|
||||
},
|
||||
CertSecret: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: tlsCertsSecret.Name,
|
||||
},
|
||||
Key: "cert.pem",
|
||||
},
|
||||
KeySecret: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: tlsCertsSecret.Name,
|
||||
},
|
||||
Key: "key.pem",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := framework.MonClientV1.ServiceMonitors(ns).Create(sm); err != nil {
|
||||
t.Fatal("creating ServiceMonitor failed: ", err)
|
||||
}
|
||||
|
||||
prometheusCRD := framework.MakeBasicPrometheus(ns, name, name, 1)
|
||||
|
||||
if _, err := framework.CreatePrometheusAndWaitUntilReady(ns, prometheusCRD); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
promSVC := framework.MakePrometheusService(prometheusCRD.Name, name, v1.ServiceTypeClusterIP)
|
||||
|
||||
if _, err := testFramework.CreateServiceAndWaitUntilReady(framework.KubeClient, ns, promSVC); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
//
|
||||
// Check for proper scraping.
|
||||
//
|
||||
|
||||
if err := framework.WaitForTargets(ns, promSVC.Name, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// TODO: Do a poll instead, should speed up things.
|
||||
time.Sleep(30 * time.Second)
|
||||
|
||||
response, err := framework.PrometheusSVCGetRequest(
|
||||
ns,
|
||||
promSVC.Name,
|
||||
"/api/v1/query",
|
||||
map[string]string{"query": fmt.Sprintf(`up{job="%v",endpoint="%v"}`, name, sm.Spec.Endpoints[0].Port)},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
q := testFramework.PrometheusQueryAPIResponse{}
|
||||
if err := json.NewDecoder(bytes.NewBuffer(response)).Decode(&q); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if q.Status != "success" {
|
||||
t.Fatalf("expected query status to be 'success' but got %v", q.Status)
|
||||
}
|
||||
|
||||
if q.Data.Result[0].Value[1] != "1" {
|
||||
t.Fatalf("expected query result to be '1' but got %v", q.Data.Result[0].Value[1])
|
||||
}
|
||||
}
|
||||
|
||||
func isAlertmanagerDiscoveryWorking(ns, promSVCName, alertmanagerName string) func() (bool, error) {
|
||||
|
@ -1696,7 +2100,7 @@ func isAlertmanagerDiscoveryWorking(ns, promSVCName, alertmanagerName string) fu
|
|||
expectedAlertmanagerTargets = append(expectedAlertmanagerTargets, fmt.Sprintf("http://%s:9093/api/v1/alerts", p.Status.PodIP))
|
||||
}
|
||||
|
||||
response, err := framework.QueryPrometheusSVC(ns, promSVCName, "/api/v1/alertmanagers", map[string]string{})
|
||||
response, err := framework.PrometheusSVCGetRequest(ns, promSVCName, "/api/v1/alertmanagers", map[string]string{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -1714,26 +2118,6 @@ func isAlertmanagerDiscoveryWorking(ns, promSVCName, alertmanagerName string) fu
|
|||
}
|
||||
}
|
||||
|
||||
func assertExpectedTargets(targets []*testFramework.Target, expectedTargets []string) error {
|
||||
existingTargets := []string{}
|
||||
|
||||
for _, t := range targets {
|
||||
existingTargets = append(existingTargets, t.ScrapeURL)
|
||||
}
|
||||
|
||||
sort.Strings(expectedTargets)
|
||||
sort.Strings(existingTargets)
|
||||
|
||||
if !reflect.DeepEqual(expectedTargets, existingTargets) {
|
||||
return fmt.Errorf(
|
||||
"expected targets %q but got %q", strings.Join(expectedTargets, ","),
|
||||
strings.Join(existingTargets, ","),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func assertExpectedAlertmanagerTargets(ams []*alertmanagerTarget, expectedTargets []string) bool {
|
||||
log.Printf("Expected Alertmanager Targets: %#+v\n", expectedTargets)
|
||||
|
||||
|
|
|
@ -22,12 +22,16 @@ import (
|
|||
)
|
||||
|
||||
func CreateClusterRoleBinding(kubeClient kubernetes.Interface, ns string, relativePath string) (finalizerFn, error) {
|
||||
finalizerFn := func() error { return DeleteClusterRoleBinding(kubeClient, relativePath) }
|
||||
finalizerFn := func() error { return DeleteClusterRoleBinding(kubeClient, ns, relativePath) }
|
||||
clusterRoleBinding, err := parseClusterRoleBindingYaml(relativePath)
|
||||
if err != nil {
|
||||
return finalizerFn, err
|
||||
}
|
||||
|
||||
// Make sure to create a new cluster role binding for each namespace to
|
||||
// prevent concurrent tests to delete each others bindings.
|
||||
clusterRoleBinding.Name = ns + "-" + clusterRoleBinding.Name
|
||||
|
||||
clusterRoleBinding.Subjects[0].Namespace = ns
|
||||
|
||||
_, err = kubeClient.RbacV1().ClusterRoleBindings().Get(clusterRoleBinding.Name, metav1.GetOptions{})
|
||||
|
@ -49,12 +53,16 @@ func CreateClusterRoleBinding(kubeClient kubernetes.Interface, ns string, relati
|
|||
return finalizerFn, err
|
||||
}
|
||||
|
||||
func DeleteClusterRoleBinding(kubeClient kubernetes.Interface, relativePath string) error {
|
||||
func DeleteClusterRoleBinding(kubeClient kubernetes.Interface, ns string, relativePath string) error {
|
||||
clusterRoleBinding, err := parseClusterRoleYaml(relativePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure to delete the specific cluster role binding for the namespace
|
||||
// it was created preventing concurrent tests to delete each others bindings.
|
||||
clusterRoleBinding.Name = ns + "-" + clusterRoleBinding.Name
|
||||
|
||||
return kubeClient.RbacV1().ClusterRoleBindings().Delete(clusterRoleBinding.Name, &metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
|
|
|
@ -40,12 +40,12 @@ func MakeDeployment(pathToYaml string) (*appsv1.Deployment, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tectonicPromOp := appsv1.Deployment{}
|
||||
if err := yaml.NewYAMLOrJSONDecoder(manifest, 100).Decode(&tectonicPromOp); err != nil {
|
||||
deployment := appsv1.Deployment{}
|
||||
if err := yaml.NewYAMLOrJSONDecoder(manifest, 100).Decode(&deployment); err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("failed to decode file %s", pathToYaml))
|
||||
}
|
||||
|
||||
return &tectonicPromOp, nil
|
||||
return &deployment, nil
|
||||
}
|
||||
|
||||
func CreateDeployment(kubeClient kubernetes.Interface, namespace string, d *appsv1.Deployment) error {
|
||||
|
|
|
@ -364,7 +364,7 @@ func (ctx *TestCtx) SetupPrometheusRBACGlobal(t *testing.T, ns string, kubeClien
|
|||
ctx.AddFinalizerFn(finalizerFn)
|
||||
}
|
||||
|
||||
if finalizerFn, err := CreateClusterRoleBinding(kubeClient, ns, "../../example/rbac/prometheus/prometheus-cluster-role-binding.yaml"); err != nil {
|
||||
if finalizerFn, err := CreateClusterRoleBinding(kubeClient, ns, "../../example/rbac/prometheus/prometheus-cluster-role-binding.yaml"); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
t.Fatal(errors.Wrap(err, "failed to create prometheus cluster role binding"))
|
||||
} else {
|
||||
ctx.AddFinalizerFn(finalizerFn)
|
||||
|
|
|
@ -18,6 +18,9 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
@ -252,14 +255,94 @@ func (f *Framework) WaitForTargets(ns, svcName string, amount int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (f *Framework) QueryPrometheusSVC(ns, svcName, endpoint string, query map[string]string) ([]byte, error) {
|
||||
func (f *Framework) WaitForDiscoveryWorking(ns, svcName, prometheusName string) error {
|
||||
var loopErr error
|
||||
|
||||
err := wait.Poll(time.Second, 5*f.DefaultTimeout, func() (bool, error) {
|
||||
pods, loopErr := f.KubeClient.CoreV1().Pods(ns).List(prometheus.ListOptions(prometheusName))
|
||||
if loopErr != nil {
|
||||
return false, loopErr
|
||||
}
|
||||
if 1 != len(pods.Items) {
|
||||
return false, nil
|
||||
}
|
||||
podIP := pods.Items[0].Status.PodIP
|
||||
expectedTargets := []string{fmt.Sprintf("http://%s:9090/metrics", podIP)}
|
||||
|
||||
activeTargets, loopErr := f.GetActiveTargets(ns, svcName)
|
||||
if loopErr != nil {
|
||||
return false, loopErr
|
||||
}
|
||||
|
||||
if loopErr = assertExpectedTargets(activeTargets, expectedTargets); loopErr != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
working, loopErr := f.basicQueryWorking(ns, svcName)
|
||||
if loopErr != nil {
|
||||
return false, loopErr
|
||||
}
|
||||
if !working {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("waiting for Prometheus to discover targets failed: %v: %v", err, loopErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Framework) basicQueryWorking(ns, svcName string) (bool, error) {
|
||||
response, err := f.PrometheusSVCGetRequest(ns, svcName, "/api/v1/query", map[string]string{"query": "up"})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
rq := PrometheusQueryAPIResponse{}
|
||||
if err := json.NewDecoder(bytes.NewBuffer(response)).Decode(&rq); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if rq.Status != "success" && rq.Data.Result[0].Value[1] == "1" {
|
||||
fmt.Printf("Query Response not successful.")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func assertExpectedTargets(targets []*Target, expectedTargets []string) error {
|
||||
existingTargets := []string{}
|
||||
|
||||
for _, t := range targets {
|
||||
existingTargets = append(existingTargets, t.ScrapeURL)
|
||||
}
|
||||
|
||||
sort.Strings(expectedTargets)
|
||||
sort.Strings(existingTargets)
|
||||
|
||||
if !reflect.DeepEqual(expectedTargets, existingTargets) {
|
||||
return fmt.Errorf(
|
||||
"expected targets %q but got %q", strings.Join(expectedTargets, ","),
|
||||
strings.Join(existingTargets, ","),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Framework) PrometheusSVCGetRequest(ns, svcName, endpoint string, query map[string]string) ([]byte, error) {
|
||||
ProxyGet := f.KubeClient.CoreV1().Services(ns).ProxyGet
|
||||
request := ProxyGet("", svcName, "web", endpoint, query)
|
||||
return request.DoRaw()
|
||||
}
|
||||
|
||||
func (f *Framework) GetActiveTargets(ns, svcName string) ([]*Target, error) {
|
||||
response, err := f.QueryPrometheusSVC(ns, svcName, "/api/v1/targets", map[string]string{})
|
||||
response, err := f.PrometheusSVCGetRequest(ns, svcName, "/api/v1/targets", map[string]string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -273,7 +356,7 @@ func (f *Framework) GetActiveTargets(ns, svcName string) ([]*Target, error) {
|
|||
}
|
||||
|
||||
func (f *Framework) CheckPrometheusFiringAlert(ns, svcName, alertName string) (bool, error) {
|
||||
response, err := f.QueryPrometheusSVC(
|
||||
response, err := f.PrometheusSVCGetRequest(
|
||||
ns,
|
||||
svcName,
|
||||
"/api/v1/query",
|
||||
|
@ -283,7 +366,7 @@ func (f *Framework) CheckPrometheusFiringAlert(ns, svcName, alertName string) (b
|
|||
return false, err
|
||||
}
|
||||
|
||||
q := prometheusQueryAPIResponse{}
|
||||
q := PrometheusQueryAPIResponse{}
|
||||
if err := json.NewDecoder(bytes.NewBuffer(response)).Decode(&q); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -334,15 +417,17 @@ type prometheusTargetAPIResponse struct {
|
|||
Data *targetDiscovery `json:"data"`
|
||||
}
|
||||
|
||||
type prometheusQueryResult struct {
|
||||
type PrometheusQueryResult struct {
|
||||
Metric map[string]string `json:"metric"`
|
||||
Value []interface{} `json:"value"`
|
||||
}
|
||||
|
||||
type prometheusQueryData struct {
|
||||
Result []prometheusQueryResult `json:"result"`
|
||||
type PrometheusQueryData struct {
|
||||
ResultType string `json:"resultType"`
|
||||
Result []PrometheusQueryResult `json:"result"`
|
||||
}
|
||||
|
||||
type prometheusQueryAPIResponse struct {
|
||||
type PrometheusQueryAPIResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data *prometheusQueryData `json:"data"`
|
||||
Data *PrometheusQueryData `json:"data"`
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: basic-auth-test-app
|
||||
name: instrumented-sample-app
|
||||
labels:
|
||||
group: test
|
||||
spec:
|
||||
|
@ -16,8 +16,10 @@ spec:
|
|||
spec:
|
||||
containers:
|
||||
- name: example-app
|
||||
image: quay.io/coreos/basic-auth-test-app:0.1.0
|
||||
image: quay.io/mxinden/instrumented-sample-app:0.2.0-bearer-mtls-1
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: web
|
||||
containerPort: 8080
|
||||
- name: mtls
|
||||
containerPort: 8081
|
||||
|
|
1
test/instrumented-sample-app/.gitignore
vendored
Normal file
1
test/instrumented-sample-app/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
certs
|
22
test/instrumented-sample-app/Makefile
Normal file
22
test/instrumented-sample-app/Makefile
Normal file
|
@ -0,0 +1,22 @@
|
|||
REG := quay.io/mxinden
|
||||
APP := instrumented-sample-app
|
||||
VERSION ?= $(shell cat VERSION)
|
||||
|
||||
all: build push
|
||||
|
||||
build:
|
||||
@GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -mod=vendor -o instrumented-sample-app main.go
|
||||
@docker build --build-arg VERSION=$(VERSION) -t $(REG)/$(APP):$(VERSION) --file $(APP).dockerfile .
|
||||
@rm $(APP)
|
||||
|
||||
push:
|
||||
@docker push $(REG)/$(APP):$(VERSION)
|
||||
|
||||
generate-certs:
|
||||
mkdir -p certs && \
|
||||
openssl req -newkey rsa:2048 \
|
||||
-new -nodes -x509 \
|
||||
-days 3650 \
|
||||
-out certs/cert.pem \
|
||||
-keyout certs/key.pem \
|
||||
-subj "/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=localhost"
|
1
test/instrumented-sample-app/VERSION
Normal file
1
test/instrumented-sample-app/VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
0.2.0-bearer-mtls-1
|
|
@ -0,0 +1,8 @@
|
|||
FROM alpine:3.7
|
||||
|
||||
ARG VERSION="$VERSION"
|
||||
ENV VERSION="$VERSION"
|
||||
|
||||
COPY instrumented-sample-app /
|
||||
|
||||
ENTRYPOINT ["/instrumented-sample-app"]
|
140
test/instrumented-sample-app/main.go
Normal file
140
test/instrumented-sample-app/main.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
// Copyright 2016 The prometheus-operator Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
var (
|
||||
certPath = flag.String("cert-path", "", "path to tls certificates for mutual TLS endpoint")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *certPath != "" {
|
||||
go func() {
|
||||
log.Fatal(mTLSEndpoint())
|
||||
}()
|
||||
}
|
||||
|
||||
http.HandleFunc("/", handler)
|
||||
http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
|
||||
if checkBasicAuth(w, r) {
|
||||
promhttp.Handler().ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="MY REALM"`)
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte("401 Unauthorized\n"))
|
||||
})
|
||||
|
||||
http.HandleFunc("/bearer-metrics", func(w http.ResponseWriter, r *http.Request) {
|
||||
if checkBearerAuth(w, r) {
|
||||
promhttp.Handler().ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("WWW-Authenticate", `Bearer realm="MY REALM"`)
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte("401 Unauthorized\n"))
|
||||
})
|
||||
|
||||
address := ":8080"
|
||||
|
||||
fmt.Printf("listening for metric requests on '%v' protected via basic auth or bearer token\n", address)
|
||||
|
||||
http.ListenAndServe(address, nil)
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, time.Now().String())
|
||||
fmt.Fprintf(w, "\nAppVersion:"+os.Getenv("VERSION"))
|
||||
}
|
||||
|
||||
func checkBasicAuth(w http.ResponseWriter, r *http.Request) bool {
|
||||
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
||||
if len(s) != 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(s[1])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pair := strings.SplitN(string(b), ":", 2)
|
||||
if len(pair) != 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
return pair[0] == "user" && pair[1] == "pass"
|
||||
}
|
||||
|
||||
func checkBearerAuth(w http.ResponseWriter, r *http.Request) bool {
|
||||
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
||||
if len(s) != 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
fmt.Println(s[1])
|
||||
|
||||
return s[1] == "abc"
|
||||
}
|
||||
|
||||
func mTLSEndpoint() error {
|
||||
certPool := x509.NewCertPool()
|
||||
pem, err := ioutil.ReadFile(path.Join(*certPath, "cert.pem"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load certificate authority")
|
||||
}
|
||||
ok := certPool.AppendCertsFromPEM(pem)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to add certificate authority to certificate pool")
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
ClientCAs: certPool,
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
}
|
||||
tlsConfig.BuildNameToCertificate()
|
||||
|
||||
address := ":8081"
|
||||
|
||||
server := &http.Server{
|
||||
Addr: address,
|
||||
TLSConfig: tlsConfig,
|
||||
Handler: promhttp.Handler(),
|
||||
}
|
||||
|
||||
fmt.Printf("listening for metric requests on '%v' protected via mutual tls\n", address)
|
||||
|
||||
return server.ListenAndServeTLS(path.Join(*certPath, "cert.pem"), path.Join(*certPath, "key.pem"))
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue