-
Notifications
You must be signed in to change notification settings - Fork 537
feat(otelcol): add support for htpasswd file authentication #3916
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c48d7b5
2389d55
13210c8
daab23e
4c8b355
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,27 +36,68 @@ otelcol.auth.basic "<LABEL>" { | |
|
|
||
| ## Arguments | ||
|
|
||
| {{< admonition type="caution" >}} | ||
| Don't use the top-level `username` and `password` arguments for new configurations as they are deprecated. Use the `client_auth` block for client authentication and the `htpasswd` block for server authentication instead. | ||
| {{< /admonition >}} | ||
|
|
||
| You can use the following arguments with `otelcol.auth.basic`: | ||
|
|
||
| | Name | Type | Description | Default | Required | | ||
| | ---------- | -------- | -------------------------------------------------- | ------- | -------- | | ||
| | `password` | `secret` | Password to use for basic authentication requests. | | yes | | ||
| | `username` | `string` | Username to use for basic authentication requests. | | yes | | ||
| | Name | Type | Description | Default | Required | | ||
| |------------|----------|-----------------------------------------------------------------|---------|----------| | ||
| | `password` | `secret` | (Deprecated) Password to use for basic authentication requests. | | no | | ||
| | `username` | `string` | (Deprecated) Username to use for basic authentication requests. | | no | | ||
|
|
||
|
|
||
| ## Blocks | ||
|
|
||
| You can use the following block with `otelcol.auth.basic`: | ||
|
|
||
| | Block | Description | Required | | ||
| | -------------------------------- | -------------------------------------------------------------------------- | -------- | | ||
| |----------------------------------|----------------------------------------------------------------------------|----------| | ||
| | [`client_auth`][client_auth] | Configures client authentication credentials for exporters. | no | | ||
| | [`debug_metrics`][debug_metrics] | Configures the metrics that this component generates to monitor its state. | no | | ||
| | [`htpasswd`][htpasswd] | Configures server authentication using htpasswd format for receivers. | no | | ||
|
|
||
|
|
||
| [client_auth]: #client_auth | ||
| [debug_metrics]: #debug_metrics | ||
| [htpasswd]: #htpasswd | ||
|
|
||
| ### `client_auth` | ||
|
|
||
| The `client_auth` block configures credentials that client extensions (such as exporters) use to authenticate to servers. | ||
|
|
||
| | Name | Type | Description | Default | Required | | ||
| | ---------- | -------- | -------------------------------------------------- | ------- | -------- | | ||
| | `password` | `string` | Password to use for basic authentication requests. | | yes | | ||
| | `username` | `string` | Username to use for basic authentication requests. | | yes | | ||
|
|
||
| {{< admonition type="note" >}} | ||
| When you specify both the `client_auth` block and the deprecated top-level `username` and `password` attributes, the `client_auth` block takes precedence and {{< param "PRODUCT_NAME" >}} ignores the top-level attributes for client authentication. | ||
| {{< /admonition >}} | ||
|
|
||
| ### `debug_metrics` | ||
|
|
||
| {{< docs/shared lookup="reference/components/otelcol-debug-metrics-block.md" source="alloy" version="<ALLOY_VERSION>" >}} | ||
|
|
||
| ### `htpasswd` | ||
|
|
||
| The `htpasswd` block configures how server extensions (such as receivers) authenticate incoming requests using the `htpasswd` format. | ||
|
|
||
| | Name | Type | Description | Default | Required | | ||
| | -------- | -------- | --------------------------------------------------------------------- | ------- | -------- | | ||
| | `file` | `string` | Path to the `htpasswd` file to use for basic authentication requests. | `""` | no | | ||
| | `inline` | `string` | The `htpasswd` file content in inline format. | `""` | no | | ||
|
|
||
| You can specify either `file`, `inline`, or both. | ||
| When you use `inline`, the format should be `username:password` with each user on a new line. | ||
|
|
||
| {{< admonition type="note" >}} | ||
| When you specify both the `htpasswd` block and the deprecated top-level `username` and `password` attributes, {{< param "PRODUCT_NAME" >}} automatically appends the deprecated credentials to the `inline` content. | ||
| This allows authentication using credentials from both the `htpasswd` configuration and the deprecated attributes. | ||
| If the same username appears in both the `file` and `inline` content, including appended deprecated credentials, the entry in the `inline` content takes precedence. | ||
| {{< /admonition >}} | ||
|
|
||
| ## Exported fields | ||
|
|
||
| The following fields are exported and can be referenced by other components: | ||
|
|
@@ -73,7 +114,11 @@ The following fields are exported and can be referenced by other components: | |
|
|
||
| `otelcol.auth.basic` doesn't expose any component-specific debug information. | ||
|
|
||
| ## Example | ||
| ## Examples | ||
|
|
||
| This section includes examples to help you configure basic authentication for exporters and receivers. | ||
|
|
||
| ### Forward signals to exporters | ||
|
|
||
| This example configures [`otelcol.exporter.otlp`][otelcol.exporter.otlp] to use basic authentication: | ||
|
|
||
|
|
@@ -91,4 +136,134 @@ otelcol.auth.basic "creds" { | |
| } | ||
| ``` | ||
|
|
||
| ### Authenticating requests for receivers | ||
|
|
||
| These examples show how to perform basic authentication using the `client_auth` block for exporters or the `htpasswd` block for receivers. | ||
|
|
||
| #### Use client authentication | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the difference between this example and the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The difference is that the first (
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think pairing them together would be better, but similar to the other thread I'm not a fan of showing deprecated fields in config examples in most cases. Let's see what our docs team says. |
||
|
|
||
| This example configures [`otelcol.exporter.otlp`][otelcol.exporter.otlp] to use basic authentication with a single username and password combination: | ||
|
|
||
| ```alloy | ||
| otelcol.receiver.otlp "example" { | ||
| grpc { | ||
| endpoint = "127.0.0.1:4317" | ||
| } | ||
|
|
||
| output { | ||
| metrics = [otelcol.exporter.otlp.default.input] | ||
| logs = [otelcol.exporter.otlp.default.input] | ||
| traces = [otelcol.exporter.otlp.default.input] | ||
| } | ||
| } | ||
|
|
||
| otelcol.exporter.otlp "default" { | ||
| client { | ||
| endpoint = "my-otlp-grpc-server:4317" | ||
| auth = otelcol.auth.basic.creds.handler | ||
| } | ||
| } | ||
|
|
||
| otelcol.auth.basic "creds" { | ||
| client_auth { | ||
| username = "demo" | ||
| password = sys.env("API_KEY") | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| {{< admonition type="note" >}} | ||
| To migrate from the deprecated `username` and `password` attributes, move them into the `client_auth` block for client authentication. | ||
| {{< /admonition >}} | ||
|
|
||
|
|
||
| #### Use htpasswd file | ||
|
|
||
| This example configures [`otelcol.receiver.otlp`][otelcol.receiver.otlp] to use basic authentication using an `htpasswd` file containing the users to use for basic authentication: | ||
|
|
||
| ```alloy | ||
| otelcol.receiver.otlp "example" { | ||
| grpc { | ||
| endpoint = "127.0.0.1:4317" | ||
|
|
||
| auth = otelcol.auth.basic.creds.handler | ||
| } | ||
|
|
||
| output { | ||
| metrics = [otelcol.exporter.debug.default.input] | ||
| logs = [otelcol.exporter.debug.default.input] | ||
| traces = [otelcol.exporter.debug.default.input] | ||
| } | ||
| } | ||
|
|
||
| otelcol.exporter.debug "default" {} | ||
|
|
||
| otelcol.auth.basic "creds" { | ||
| htpasswd { | ||
| file = "/etc/alloy/.htpasswd" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| #### Use htpasswd inline content | ||
|
|
||
| This example shows how to specify `htpasswd` content directly in the configuration: | ||
|
|
||
| ```alloy | ||
| otelcol.receiver.otlp "example" { | ||
| grpc { | ||
| endpoint = "127.0.0.1:4317" | ||
|
|
||
| auth = otelcol.auth.basic.creds.handler | ||
| } | ||
|
|
||
| output { | ||
| metrics = [otelcol.exporter.debug.default.input] | ||
| logs = [otelcol.exporter.debug.default.input] | ||
| traces = [otelcol.exporter.debug.default.input] | ||
| } | ||
| } | ||
|
|
||
| otelcol.exporter.debug "default" {} | ||
|
|
||
| otelcol.auth.basic "creds" { | ||
| htpasswd { | ||
| inline = "user1:password1\nuser2:password2" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| {{< admonition type="note" >}} | ||
| To make the migration from the deprecated `username` and `password` attributes easier, you can specify both the deprecated attributes and the `htpasswd` block in the same configuration. | ||
| {{< param "PRODUCT_NAME" >}} appends the deprecated attributes to the `htpasswd` content. | ||
|
|
||
| ```alloy | ||
| otelcol.receiver.otlp "example" { | ||
| grpc { | ||
| endpoint = "127.0.0.1:4317" | ||
|
|
||
| auth = otelcol.auth.basic.creds.handler | ||
| } | ||
|
|
||
| output { | ||
| metrics = [otelcol.exporter.debug.default.input] | ||
| logs = [otelcol.exporter.debug.default.input] | ||
| traces = [otelcol.exporter.debug.default.input] | ||
| } | ||
| } | ||
|
|
||
| otelcol.exporter.debug "default" {} | ||
|
|
||
| otelcol.auth.basic "creds" { | ||
| username = "demo" | ||
| password = sys.env("API_KEY") | ||
|
|
||
| htpasswd { | ||
| file = "/etc/alloy/.htpasswd" | ||
| } | ||
| } | ||
| ``` | ||
| {{< /admonition >}} | ||
|
|
||
| [otelcol.receiver.otlp]: ../otelcol.receiver.otlp/ | ||
| [otelcol.exporter.otlp]: ../otelcol.exporter.otlp/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| package basic | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
|
|
||
| "github.com/grafana/alloy/internal/component" | ||
|
|
@@ -15,6 +16,11 @@ import ( | |
| "go.opentelemetry.io/collector/pipeline" | ||
| ) | ||
|
|
||
| var ( | ||
| errNoCredentialSource = errors.New("no credential source provided") | ||
| errNoPasswordProvided = errors.New("no password provided") | ||
| ) | ||
|
|
||
| func init() { | ||
| component.Register(component.Registration{ | ||
| Name: "otelcol.auth.basic", | ||
|
|
@@ -29,10 +35,41 @@ func init() { | |
| }) | ||
| } | ||
|
|
||
| type HtpasswdConfig struct { | ||
| File string `alloy:"file,attr,optional"` | ||
| Inline string `alloy:"inline,attr,optional"` | ||
| } | ||
|
|
||
| func (c HtpasswdConfig) convert() *basicauthextension.HtpasswdSettings { | ||
| return &basicauthextension.HtpasswdSettings{ | ||
| File: c.File, | ||
| Inline: c.Inline, | ||
| } | ||
| } | ||
|
|
||
| type ClientAuthConfig struct { | ||
| Username string `alloy:"username,attr"` | ||
| Password string `alloy:"password,attr"` | ||
| } | ||
|
|
||
| func (c ClientAuthConfig) convert() *basicauthextension.ClientAuthSettings { | ||
| if c.Username == "" && c.Password == "" { | ||
| return nil | ||
| } | ||
| return &basicauthextension.ClientAuthSettings{ | ||
| Username: c.Username, | ||
| Password: configopaque.String(c.Password), | ||
| } | ||
| } | ||
|
|
||
| // Arguments configures the otelcol.auth.basic component. | ||
| type Arguments struct { | ||
| Username string `alloy:"username,attr"` | ||
| Password alloytypes.Secret `alloy:"password,attr"` | ||
| Username string `alloy:"username,attr,optional"` // Deprecated: Use ClientAuth instead | ||
| Password alloytypes.Secret `alloy:"password,attr,optional"` // Deprecated: Use ClientAuth instead | ||
|
|
||
| ClientAuth *ClientAuthConfig `alloy:"client_auth,block,optional"` | ||
|
|
||
| Htpasswd *HtpasswdConfig `alloy:"htpasswd,block,optional"` | ||
|
|
||
| // DebugMetrics configures component internal metrics. Optional. | ||
| DebugMetrics otelcolCfg.DebugMetricsArguments `alloy:"debug_metrics,block,optional"` | ||
|
|
@@ -45,23 +82,55 @@ func (args *Arguments) SetToDefault() { | |
| args.DebugMetrics.SetToDefault() | ||
| } | ||
|
|
||
| // Validate implements syntax.Validator | ||
| func (args Arguments) Validate() error { | ||
| // check if no argument was provided | ||
| if args.Username == "" && args.Password == "" && args.Htpasswd == nil && args.ClientAuth == nil { | ||
| return errNoCredentialSource | ||
| } | ||
| // the downstream basicauthextension package supports having both inline | ||
| // and htpasswd files, so we should not error out in case both are | ||
| // provided | ||
|
|
||
| // check if password was not provided when username is provided | ||
| if args.Username != "" && args.Password == "" { | ||
| return errNoPasswordProvided | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // ConvertClient implements auth.Arguments. | ||
| func (args Arguments) ConvertClient() (otelcomponent.Config, error) { | ||
| return &basicauthextension.Config{ | ||
| ClientAuth: &basicauthextension.ClientAuthSettings{ | ||
| Username: args.Username, | ||
| Password: configopaque.String(args.Password), | ||
| }, | ||
| }, nil | ||
| c := &basicauthextension.Config{} | ||
| // If the client config is specified, ignore the deprecated | ||
| // username and password attributes. | ||
| if args.ClientAuth != nil { | ||
| c.ClientAuth = args.ClientAuth.convert() | ||
| return c, nil | ||
| } | ||
|
|
||
| c.ClientAuth = &basicauthextension.ClientAuthSettings{ | ||
| Username: args.Username, | ||
| Password: configopaque.String(args.Password), | ||
| } | ||
| return c, nil | ||
| } | ||
|
|
||
| // ConvertServer implements auth.Arguments. | ||
| func (args Arguments) ConvertServer() (otelcomponent.Config, error) { | ||
| return &basicauthextension.Config{ | ||
| Htpasswd: &basicauthextension.HtpasswdSettings{ | ||
| Inline: fmt.Sprintf("%s:%s", args.Username, args.Password), | ||
| }, | ||
| }, nil | ||
| c := &basicauthextension.Config{ | ||
| Htpasswd: &basicauthextension.HtpasswdSettings{}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'd be better if this is only created if |
||
| } | ||
| if args.Htpasswd != nil { | ||
| c.Htpasswd = args.Htpasswd.convert() | ||
| } | ||
| // Keeping this to avoid breaking existing use cases. Remove this for v2 | ||
| if args.Username != "" && args.Password != "" { | ||
| c.Htpasswd.Inline += fmt.Sprintf("\n%s:%s", args.Username, args.Password) | ||
| } | ||
|
|
||
| return c, nil | ||
| } | ||
|
|
||
| // AuthFeatures implements auth.Arguments. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.