Skip to content
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

enhancement(http provider): Add automatic bearer token acquisition in http-client [version 2] #21583

Merged

Conversation

KowalczykBartek
Copy link
Contributor

@KowalczykBartek KowalczykBartek commented Oct 22, 2024

PR to solve a #20635 feature request.
This change brings new section in configuration for HttpSinkConfig :

pub struct HttpClientAuthorizationConfig {
    auth: HttpClientAuthorizationStrategy,
    tls: Option<TlsConfig>,
}

this allows to explicit define a oauth2 server which will be used to acquire bearer token (also, this request allows to bring mtls via TlsConfig),

sources:
  my_source_id:
    scrape_interval_secs: 1
    type: http_client
    endpoint: http://127.0.0.1:9898/logs

sinks:
  my_sink_id:
    http_client_authorization_strategy:
      auth:
        strategy: "o_auth2"
        client_id: "your.clientid"
        client_secret: "verysecretstring"
        token_endpoint: "https://your.provider.com/oauth/token"
    type: http
    encoding:
      codec: raw_message
    inputs:
      - my_source_id
    uri: http://127.0.0.1:9091/output

Some basic error handling is also on place, so invalid credentials will not crash a vector instance :) you can expect an event and error message with body from oauth server (unfortunately I over engineered this error handling a little bit, I have limited rust knowledge and was unable to fully understand all patterns - so, suggestions, how to improve this would be appreciated :) )

2024-10-22T21:51:18.256079Z  WARN sink{component_kind="sink" component_id=my_sink_id component_type=http}:request{request_id=1}:http: vector::internal_events::http_client: HTTP Auth extension error. error=Server error from authentication server: {"error":"invalid_client","error_description":"Bad credentials"} error_type="request_failed" stage="processing" internal_log_rate_limit=true

So, as discussed in #21023 (comment) - @pront I would be grateful for review :)

src/http.rs Outdated
//should request for token influence upstream service latency ?
if let Some(auth_extension) = auth_extension {
let auth_span = tracing::info_span!("auth_extension");
let res = auth_extension.modify_request(&mut request)
Copy link
Contributor Author

@KowalczykBartek KowalczykBartek Oct 22, 2024

Choose a reason for hiding this comment

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

I had no idea how to handle this

 Box<dyn Error>

to some vector friendly error better 😞

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was able to improve it a little bit with snafu

auth_extension.modify_request(&mut request)
    .instrument(auth_span.clone().or_current())
    .await
    .inspect_err(|error| {
        // Emit the error into the internal events system.
        emit!(http_client::GotAuthExtensionError{error});
    })
    .context(ExtensionAuthenticationSnafu)?;

@pront pront self-assigned this Oct 23, 2024
Copy link
Contributor

@pront pront left a comment

Choose a reason for hiding this comment

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

Hi @KowalczykBartek thank you for this contribution!

I did a quick scan of the changes and left a few comments.

.gitignore Outdated Show resolved Hide resolved
src/http.rs Show resolved Hide resolved
src/http.rs Outdated Show resolved Hide resolved
src/http.rs Outdated
pub struct HttpClientAuthorizationConfig {
/// Define how to authorize against an upstream.
#[configurable]
auth: HttpClientAuthorizationStrategy,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we flatten this to avoid repetition in configs, from:

    http_client_authorization_strategy:
      auth:
        strategy: "o_auth2"
        client_id: "your.clientid"
        client_secret: "verysecretstring"
        token_endpoint: "https://your.provider.com/oauth/token"

to

    http_client_authorization_strategy:
      strategy: "o_auth2"
      client_id: "your.clientid"
      client_secret: "verysecretstring"
      token_endpoint: "https://your.provider.com/oauth/token"

I think this makes sense from a UX perspective cc @vectordotdev/ux-team

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Honestly I was unable to figure out this - @pront can you pls point me some example with flattened enum ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I took another look at this:

  • We can replace pub http_client_authorization_strategy: Option<HttpClientAuthorizationConfig> with pub authorization_config: Option<AuthorizationConfig>
  • Rename auth to strategy

Copy link
Contributor Author

@KowalczykBartek KowalczykBartek Oct 25, 2024

Choose a reason for hiding this comment

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

then config will look like this

sinks:
  my_sink_id:
    authorization_config:
      strategy:
        strategy: "o_auth2"
        client_id: "some.client.id"
        client_secret: "ab!@#"
        grace_period: 780
        token_endpoint: "https://127.0.0.1:8091/oauth/token"

is this double strategy ok ? maybe I will change also tag from

/// Configuration of the authentication strategy for HTTP requests.
///
/// HTTP authentication should be used with HTTPS only, as the authentication credentials are passed as an
/// HTTP header without any additional encryption beyond what is provided by the transport itself.
#[configurable_component]
#[derive(Clone, Debug)]
#[serde(deny_unknown_fields, rename_all = "snake_case", tag = "strategy")]
#[configurable(metadata(docs::enum_tag_description = "The authentication strategy to use."))]

to 'protocol' ? maybe 'provider' ?

Copy link
Contributor Author

@KowalczykBartek KowalczykBartek Oct 28, 2024

Choose a reason for hiding this comment

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

together with fix for mtls extension I pushed renamed struct

#[configurable(derived)]
pub authorization_config: Option<AuthorizationConfig>,

src/internal_events/http_client.rs Outdated Show resolved Hide resolved
src/internal_events/http_client.rs Outdated Show resolved Hide resolved
src/http.rs Outdated Show resolved Hide resolved
@KowalczykBartek
Copy link
Contributor Author

KowalczykBartek commented Oct 23, 2024

I introduced grace_period property so, it can be adjusted - before it was hard coded for 1min. I also played around this feature more and found bug around grace calculation (overflow), so also fixed this.
I also verified following config works (so use case from original issue, oauth2 + mtls)

sinks:
  my_sink_id:
    http_client_authorization_strategy:
      auth:
        strategy: "o_auth2"
        client_id: "clientid"
        client_secret: "secret"
        grace_period: 300
        token_endpoint: "https://127.0.0.1:8091/oauth/token"
      tls:
        crt_path: /config/cert.pem
        key_file: /config/server.key

what I need still to do is test against more providers (I tested with UAA, its pretty common).

Hi @singhbaljit, may I ask you to check if that integration works in your env ? if there will be any required change I could make an adjustments.

src/sinks/http/config.rs Outdated Show resolved Hide resolved
src/http.rs Outdated
pub struct HttpClientAuthorizationConfig {
/// Define how to authorize against an upstream.
#[configurable]
auth: HttpClientAuthorizationStrategy,
Copy link
Contributor

Choose a reason for hiding this comment

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

I took another look at this:

  • We can replace pub http_client_authorization_strategy: Option<HttpClientAuthorizationConfig> with pub authorization_config: Option<AuthorizationConfig>
  • Rename auth to strategy

src/http.rs Show resolved Hide resolved
Copy link
Contributor

@pront pront left a comment

Choose a reason for hiding this comment

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

This is starting to look great. Left a few nits, these should improve config readability.

@KowalczykBartek
Copy link
Contributor Author

KowalczykBartek commented Oct 28, 2024

after testing on real environments I found another issue, oauth2 + mtls should be strict to the rfc https://datatracker.ietf.org/doc/html/rfc8705, so, speaking in cURL
this is oauth2 + mtls extension

curl --cert cert.pem --key key.pem --request POST \
  --url https://something/oauth/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data 'grant_type=client_credentials' \
  --data 'client_id=abcdabcdabcd'

this is oauth2

curl --request POST \
  --url https://something/oauth/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data client_secret=abcdabcdabcd \
  --data grant_type=client_credentials \
  --data client_id=abcdabcdabcd

I also updated a comment around HttpClientAuthorizationStrategy. With those changes I think I can say: it works :)

@pront
Copy link
Contributor

pront commented Oct 28, 2024

after testing on real environments I found another issue, oauth2 + mtls should be strict to the efc https://datatracker.ietf.org/doc/html/rfc8705, so, speaking in cURL this is oauth2 + mtls extension

curl --cert cert.pem --key key.pem --request POST \
  --url https://something/oauth/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data 'grant_type=client_credentials' \
  --data 'client_id=abcdabcdabcd'

this is oauth2

curl --request POST \
  --url https://something/oauth/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data client_secret=abcdabcdabcd \
  --data grant_type=client_credentials \
  --data client_id=abcdabcdabcd

I also updated a comment around HttpClientAuthorizationStrategy. With those changes I think I can say: it works :)

Thank you @KowalczykBartek, I will do another review tomorrow.

Copy link
Contributor

@pront pront left a comment

Choose a reason for hiding this comment

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

👋 Left some comments. Most of them are nits but we should get rid of the unwraps.

Also, we lack tests here. We could mock the endpoint but we can also test some internal bits separately. Please see existing tests for inspiration.

Thank you again for this contribution!

src/http.rs Outdated Show resolved Hide resolved
src/http.rs Outdated Show resolved Hide resolved
src/http.rs Outdated Show resolved Hide resolved
src/http.rs Outdated Show resolved Hide resolved
src/http.rs Outdated Show resolved Hide resolved
src/http.rs Outdated Show resolved Hide resolved
src/http.rs Outdated Show resolved Hide resolved
src/http.rs Outdated Show resolved Hide resolved
@KowalczykBartek
Copy link
Contributor Author

KowalczykBartek commented Oct 31, 2024

Also, we lack tests here. We could mock the endpoint but we can also test some internal bits separately. Please see existing tests for inspiration.

just FYI (because I see some jobs running :D ) - I will provide tests, I just need few days more :)

@pront
Copy link
Contributor

pront commented Oct 31, 2024

Also, we lack tests here. We could mock the endpoint but we can also test some internal bits separately. Please see existing tests for inspiration.

just FYI (because I see some jobs running :D ) - i will provide tests, I just need few days more :)

Thank you @KowalczykBartek, much appreciated. Also, I wanted to share a few notes to help with the CI checks:

  • we can add an http scope here
  • also see our changelog guide
  • cargo install -f --path vdev #cd to the repo root
  • Run cargo vdev fmt
  • Run cargo vdev check rust --clippy

@pront pront changed the title enhancement(http-client): Add automatic bearer token acquisition in http-client [version 2] enhancement(http): Add automatic bearer token acquisition in http-client [version 2] Oct 31, 2024
@pront pront enabled auto-merge November 7, 2024 17:55
@pront
Copy link
Contributor

pront commented Nov 7, 2024

Thank you again @KowalczykBartek for this great PR. You went above and beyond 👍 I enqueued it for merging.

@KowalczykBartek
Copy link
Contributor Author

Thank you @pront for support and patience :)

@pront
Copy link
Contributor

pront commented Nov 7, 2024

One more thing here, I noticed this failure: https://github.com/vectordotdev/vector/actions/runs/11728482979/job/32672282844?pr=21583.

This might be helpful:

make generate-component-docs
# review any changes
# commit if they look good
make check-component-docs
git push

@KowalczykBartek
Copy link
Contributor Author

One more thing here, I noticed this failure: https://github.com/vectordotdev/vector/actions/runs/11728482979/job/32672282844?pr=21583.

This might be helpful:

make generate-component-docs
# review any changes
# commit if they look good
make check-component-docs
git push

hmm, generate-component-docs fail with

vector/scripts/generate-component-docs.rb:1753:in `<main>': undefined method `filter_map' for #<Hash:0x00000001380c15a0> (NoMethodError)

@jszwedko
Copy link
Member

jszwedko commented Nov 7, 2024

hmm, generate-component-docs fail with

vector/scripts/generate-component-docs.rb:1753:in `<main>': undefined method `filter_map' for #<Hash:0x00000001380c15a0> (NoMethodError)

I think that means you are using Ruby 2, that script requires Ruby 3 (that should really be clearer 😓 ).

@KowalczykBartek
Copy link
Contributor Author

you are right, I have ruby 2, let me try to fix this :D

auto-merge was automatically disabled November 7, 2024 19:37

Head branch was pushed to by a user without write access

@KowalczykBartek KowalczykBartek requested review from a team as code owners November 7, 2024 19:37
@github-actions github-actions bot added the domain: external docs Anything related to Vector's external, public documentation label Nov 7, 2024
@KowalczykBartek
Copy link
Contributor Author

KowalczykBartek commented Nov 7, 2024

I was able to run this command,

# review any changes
# commit if they look good

but this this is tricky, I am not sure what this http.cue is :) It looks, good, but is it good ? not sure :)

btw, last time I saw ruby was like.. 6years ago when deploying database using bosh.io (not a drills company :)) :D

@@ -29,6 +29,7 @@ jobs:
revert
scopes: |
http
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
http

This change seems to affect the http and axiom sinks? Could we use those pre-existing scopes instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know :) I did this because it was one of the review comments #21583 (comment) :)

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought it is worth introducing a scope since this affects a lower level module used by multiple components. But I don't really have a strong opinion here.

Copy link
Member

Choose a reason for hiding this comment

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

Aha gotcha, sorry for the run-around 😅 Maybe http provider would be more consistent? We have other x providers already as scopes to use for lower level changes that affect multiple components that share a "provider".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed this http component from list as suggested

@pront pront changed the title enhancement(http): Add automatic bearer token acquisition in http-client [version 2] enhancement(http provider): Add automatic bearer token acquisition in http-client [version 2] Nov 7, 2024
@pront pront enabled auto-merge November 7, 2024 20:09
@pront pront added this pull request to the merge queue Nov 7, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 7, 2024
@KowalczykBartek
Copy link
Contributor Author

KowalczykBartek commented Nov 7, 2024

Windows has some differences across error messages, so, I just removed error msg expectation for connection refused and internal server errors, its too much library and platform dependent

@pront pront enabled auto-merge November 7, 2024 23:49
@pront pront added this pull request to the merge queue Nov 8, 2024
Merged via the queue into vectordotdev:master with commit fb9c5c1 Nov 8, 2024
39 of 40 checks passed
@singhbaljit
Copy link

Chiming in a bit late... this is highly appreciated! I'll certainly test this feature and report any issues I find. Thank you very much!

@tareksha
Copy link

many thanks @KowalczykBartek for the great work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain: external docs Anything related to Vector's external, public documentation domain: sinks Anything related to the Vector's sinks
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants