Skip to content

feat: operator support set basic auth for prometheus server#7004

Open
Xu-Wentao wants to merge 1 commit intoprometheus-operator:mainfrom
Xu-Wentao:dev
Open

feat: operator support set basic auth for prometheus server#7004
Xu-Wentao wants to merge 1 commit intoprometheus-operator:mainfrom
Xu-Wentao:dev

Conversation

@Xu-Wentao
Copy link
Contributor

@Xu-Wentao Xu-Wentao commented Oct 11, 2024

Description

Support set basic auth info from secret and set statefulset probe's httpGet headers.
Aim to solve #5836

Type of change

What type of changes does your code introduce to the Prometheus operator? Put an x in the box that apply.

  • CHANGE (fix or feature that would cause existing functionality to not work as expected)
  • FEATURE (non-breaking change which adds functionality)
  • BUGFIX (non-breaking change which fixes an issue)
  • ENHANCEMENT (non-breaking change which improves existing functionality)
  • NONE (if none of the other choices apply. Example, tooling, build system, CI, docs, etc.)

Verification

Please check the Prometheus-Operator testing guidelines for recommendations about automated tests.

Changelog entry

Please put a one-line changelog entry below. This will be copied to the changelog file during the release process.


@Xu-Wentao Xu-Wentao requested a review from a team as a code owner October 11, 2024 10:56
Copy link
Contributor

@simonpasquier simonpasquier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to solve the issue of Kubernetes probes not working if you configure basic-auth.

// +k8s:openapi-gen=true
type WebBasicAuthUsers struct {
Username string `json:"username"`
Password string `json:"password"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't want passwords in clear text inside the Prometheus CRD.

Copy link
Contributor Author

@Xu-Wentao Xu-Wentao Oct 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, i'm on the way to slove this. i'm going to store username and password in secret use the BasicAuth, and when reconcile Prometheus resource, we can get info from secret and then set basic auth. Also, set probe to solve the kubernetes readiness check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not acceptable from a security standpoint to store credentials in clear-text in the statefulset spec. We need prometheus/exporter-toolkit#151 to allow unauthenticated access to the probe endpoints.

@Xu-Wentao Xu-Wentao force-pushed the dev branch 3 times, most recently from afbf6ef to f6f68dd Compare October 12, 2024 08:57
@Xu-Wentao
Copy link
Contributor Author

@simonpasquier hi, i have changed the type of basicAuthUsers to BasicAuth and basis auth info store in secret (should be created maunally first) currently. and when reconcile prometheus, we will set statefulset's probes with basic auth headers automaticly. But there's still have a promblem that, user can get basic auth info by decoding authorization info which in prometheus liveness probe or readiness probe.

@Xu-Wentao Xu-Wentao changed the title [WIP] feat: operator support set basic auth for prometheus server feat: operator support set basic auth for prometheus server Oct 12, 2024
@Xu-Wentao Xu-Wentao force-pushed the dev branch 3 times, most recently from 9d5cf60 to 4394394 Compare October 12, 2024 09:54
@simonpasquier
Copy link
Contributor

There's also the question of the Thanos sidecar and config reloader which require authentication credentials too.
Another option instead of picking up a random user/password from the list provided by the user would be to create a special username with a random password and inject the credentials in the generated passwd file.

@Xu-Wentao
Copy link
Contributor Author

There's also the question of the Thanos sidecar and config reloader which require authentication credentials too. Another option instead of picking up a random user/password from the list provided by the user would be to create a special username with a random password and inject the credentials in the generated passwd file.

ok.I will finish it. bytheway, maybe prometheus and other components can exclued health check api for authicated like the issue prometheus/prometheus#9166 mentioned.

@Xu-Wentao Xu-Wentao force-pushed the dev branch 2 times, most recently from f1c5222 to 7294342 Compare October 15, 2024 09:40
@simonpasquier
Copy link
Contributor

I'm on the fence regarding the API: I wonder if the operator should be in charge of producing the brypt hashes or if it should pass the secret data as-is to Prometheus (and let the user provide the hashes). In particular involving the operator with security concerns is always a risk for the long-term maintenance.

@simonpasquier
Copy link
Contributor

After more thinking, I realize that the simplest API might be for the user to provide a secret containing all the credentials where the username would be the key and the password the value:

apiVersion: v1
kind: Secret
metadata:
  name: web-users
stringData:
  foo: $2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay
  bar: $2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay
  prometheus: $2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay
---
apiVersion: v1
kind: Secret
metadata:
  name: example-webconfig
stringData:
  password: test
---
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: example
spec:
  web:
    basicAuthUsers:
      secretName: web-users
      podCredentials:
        username: prometheus
        password:
           key: password
           name: example-webconfig

The operator can fail the reconciliation if it detects invalid hashes in the secret (e.g. using bcrypt.Cost()).

Again sorry for the back and forth but I didn't put a lot of effort in this part before your PR so thanks for doing it!

@Xu-Wentao
Copy link
Contributor Author

After more thinking, I realize that the simplest API might be for the user to provide a secret containing all the credentials where the username would be the key and the password the value:

apiVersion: v1
kind: Secret
metadata:
  name: web-users
stringData:
  foo: $2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay
  bar: $2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay
  prometheus: $2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay
---
apiVersion: v1
kind: Secret
metadata:
  name: example-webconfig
stringData:
  password: test
---
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: example
spec:
  web:
    basicAuthUsers:
      secretName: web-users
      podCredentials:
        username: prometheus
        password:
           key: password
           name: example-webconfig

The operator can fail the reconciliation if it detects invalid hashes in the secret (e.g. using bcrypt.Cost()).

Again sorry for the back and forth but I didn't put a lot of effort in this part before your PR so thanks for doing it!

Ok, I'll try to change the API as you recommended, But the way, when i try to set a correct config of thanos sidecar to make sidecar can access to prometheus server normally, i find out that the sidecar currently set --prometheus.http-client config as a plain text, is that ok? coz if we want to use a config file for --promethes.http-client-config https://thanos.io/tip/components/sidecar.md/#configuration, this a lot of work need to do.

@simonpasquier
Copy link
Contributor

when i try to set a correct config of thanos sidecar to make sidecar can access to prometheus server normally, i find out that the sidecar currently set --prometheus.http-client config as a plain text, is that ok? coz if we want to use a config file for --promethes.http-client-config https://thanos.io/tip/components/sidecar.md/#configuration, this a lot of work need to do.

For sure, we don't want the HTTP client config to be in clear-text if it contains sensitive information like password so yes, we'll need to change this and probably use a dedicated secret mounted in the sidecar. And we need the same for the config reloader sidecar. I'd say that it should be a preliminary PR to this one to facilitate the review process.

For context, both thanos and config-reloader sidecars had to disable TLS verification because they only connect through localhost and we didn't want to bother with configuring the right CA.

@Xu-Wentao Xu-Wentao closed this Oct 18, 2024
@Xu-Wentao Xu-Wentao deleted the dev branch October 18, 2024 02:47
@Xu-Wentao Xu-Wentao restored the dev branch October 18, 2024 02:48
@Xu-Wentao Xu-Wentao reopened this Oct 18, 2024
@Xu-Wentao
Copy link
Contributor Author

when i try to set a correct config of thanos sidecar to make sidecar can access to prometheus server normally, i find out that the sidecar currently set --prometheus.http-client config as a plain text, is that ok? coz if we want to use a config file for --promethes.http-client-config https://thanos.io/tip/components/sidecar.md/#configuration, this a lot of work need to do.

For sure, we don't want the HTTP client config to be in clear-text if it contains sensitive information like password so yes, we'll need to change this and probably use a dedicated secret mounted in the sidecar. And we need the same for the config reloader sidecar. I'd say that it should be a preliminary PR to this one to facilitate the review process.

For context, both thanos and config-reloader sidecars had to disable TLS verification because they only connect through localhost and we didn't want to bother with configuring the right CA.

I find that the config-reloader's ReloaderURL is defined here https://github.com/prometheus-operator/prometheus-operator/blob/main/pkg/prometheus/common.go#L456 and did not support config basic auth headers, should i change the thanos config reloader to support this param?

@simonpasquier
Copy link
Contributor

simonpasquier commented Oct 18, 2024

For the config reloader, the idea should be to add CLI arguments to pass the basic-auth user/password in https://github.com/prometheus-operator/prometheus-operator/blob/main/cmd/prometheus-config-reloader/main.go and use something like https://pkg.go.dev/k8s.io/[email protected]/transport#NewBasicAuthRoundTripper to inject the header in the request.

if err != nil {
fmt.Printf("Failed to read password file: %v\n", err)
} else {
rt = newBasicAuthRoundTripper(podCredentialsUsername, strings.TrimSpace(string(password)), rt)
Copy link

@RouxAntoine RouxAntoine Jan 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an existing basic roundtripper in prometheus common config.NewBasicAuthRoundTripper() https://github.com/prometheus/common/blob/main/config/http_config.go#L839 maybe it would be better to reuse it

@Xu-Wentao Xu-Wentao force-pushed the dev branch 2 times, most recently from 1a40b77 to 5a97c7b Compare January 21, 2025 06:44
@Xu-Wentao Xu-Wentao force-pushed the dev branch 2 times, most recently from 51926ed to efc4822 Compare January 22, 2025 03:31
Copy link
Contributor

@simonpasquier simonpasquier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got remarks on the API. We need to add more validations.

RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build make prometheus-config-reloader

FROM quay.io/prometheus/busybox-${OS}-${ARCH}:latest
FROM quay.io/prometheus/busybox-linux-${ARCH}:latest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, i built on my mac and run on linux machine. i will revert this.

runtimeInfoURL := app.Flag("runtimeinfo-url", "URL to check the status of the runtime configuration").
Default("http://127.0.0.1:9090/api/v1/status/runtimeinfo").URL()

podCredentialsUsername := app.Flag("pod-credentials-username", "Username for basic auth for prometheus server.").Default("").String()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
podCredentialsUsername := app.Flag("pod-credentials-username", "Username for basic auth for prometheus server.").Default("").String()
podCredentialsUsername := app.Flag("basic-auth-username", "Username for Basic-Auth authorization.").Default("").String()

Default("http://127.0.0.1:9090/api/v1/status/runtimeinfo").URL()

podCredentialsUsername := app.Flag("pod-credentials-username", "Username for basic auth for prometheus server.").Default("").String()
podCredentialsPassword := app.Flag("pod-credentials-password-file", "Password file for basic auth for prometheus server.").Default("").String()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
podCredentialsPassword := app.Flag("pod-credentials-password-file", "Password file for basic auth for prometheus server.").Default("").String()
podCredentialsPassword := app.Flag("basic-auth-password-file", "File containing the password for Basic-Auth authorization.").Default("").String()

Comment on lines +326 to +327
// Defines the name of secret stored basic auth users.
SecretName *string `json:"secretName"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To follow https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#object-references

Suggested change
// Defines the name of secret stored basic auth users.
SecretName *string `json:"secretName"`
// Defines the secret containing the list of users (keys) with their hashed passwords (values).
// The secret must contain a key for the service account.
// +required
SecretRef SecretReference `json:"secretRef"`

with

type SecretReference struct {
	// Name of the secret in the resource's namespace.
	// +required
	// +kubebuilder:validation:MinLength=1
	Name string `json:"name"`
}

type BasicAuthUsers struct {
// Defines the name of secret stored basic auth users.
SecretName *string `json:"secretName"`
// Defines the pod credentials for health check probe of prometheus server.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Defines the pod credentials for health check probe of prometheus server.
// Defines the secret's key holding the password used by the service account to query the web server.
// The service account must exist in the users secret.

// Defines the name of secret stored basic auth users.
SecretName *string `json:"secretName"`
// Defines the pod credentials for health check probe of prometheus server.
PodCredentials *v1.SecretKeySelector `json:"podCredentials"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PodCredentials *v1.SecretKeySelector `json:"podCredentials"`
// +required
ServiceAccountPasswordRef SecretKeySelector `json:"serviceAccountPasswordRef"`
type SecretKeySelector struct {
	SecretReference
	// The key of the secret to select from.
	// +required
	// +kubebuilder:validation:MinLength=1
	Key string `json:"key"`
}

@Xu-Wentao Xu-Wentao force-pushed the dev branch 5 times, most recently from 042c98d to ac8a562 Compare March 20, 2025 17:13
@Xu-Wentao
Copy link
Contributor Author

@simonpasquier hi, i have use object reference to secrets as you mentioned, is there any other things i can to to move on?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants