diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index c751035c1591..5314d7d8a84a 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -101,6 +101,7 @@ https://github.com/elastic/beats/compare/v8.2.0\...main[Check the HEAD diff] - Change threatintel module from beta to GA. {pull}31693[31693] - Add template helper function for hashing strings. {issue}31613[31613] {pull}31630[31630] - Add extended okta.debug_context.debug_data handling. {pull}31676[31676] +- Add `auth.oauth2.google.jwt_json` option to `httpjson` input. {pull}31750[31750] *Auditbeat* diff --git a/x-pack/filebeat/docs/inputs/input-httpjson.asciidoc b/x-pack/filebeat/docs/inputs/input-httpjson.asciidoc index 3011e71d6dca..8be96f31af6f 100644 --- a/x-pack/filebeat/docs/inputs/input-httpjson.asciidoc +++ b/x-pack/filebeat/docs/inputs/input-httpjson.asciidoc @@ -373,10 +373,20 @@ NOTE: Only one of the credentials settings can be set at once. If none is provid default credentials from the environment will be attempted via ADC. For more information about how to provide Google credentials, please refer to https://cloud.google.com/docs/authentication. +[float] +==== `auth.oauth2.google.jwt_json` + +The JWT Account Key file as raw JSON. + +NOTE: Only one of the credentials settings can be set at once. If none is provided, loading +default credentials from the environment will be attempted via ADC. For more information about +how to provide Google credentials, please refer to https://cloud.google.com/docs/authentication. + [float] ==== `auth.oauth2.google.delegated_account` -Email of the delegated account used to create the credentials (usually an admin). +Email of the delegated account used to create the credentials (usually an admin). Used in combination +with `auth.oauth2.google.jwt_file` or `auth.oauth2.google.jwt_json`. [[request-parameters]] [float] diff --git a/x-pack/filebeat/input/httpjson/config_auth.go b/x-pack/filebeat/input/httpjson/config_auth.go index 43649eb6710a..71911cd6f46c 100644 --- a/x-pack/filebeat/input/httpjson/config_auth.go +++ b/x-pack/filebeat/input/httpjson/config_auth.go @@ -9,7 +9,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io/fs" "net/http" "os" "strings" @@ -96,6 +96,7 @@ type oAuth2Config struct { GoogleCredentialsFile string `config:"google.credentials_file"` GoogleCredentialsJSON common.JSONBlob `config:"google.credentials_json"` GoogleJWTFile string `config:"google.jwt_file"` + GoogleJWTJSON common.JSONBlob `config:"google.jwt_json"` GoogleDelegatedAccount string `config:"google.delegated_account"` // microsoft azure specific @@ -147,8 +148,8 @@ func (o *oAuth2Config) client(ctx context.Context, client *http.Client) (*http.C case oAuth2ProviderAzure: return o.clientCredentialsGrant(ctx, client), nil case oAuth2ProviderGoogle: - if o.GoogleJWTFile != "" { - cfg, err := google.JWTConfigFromJSON(o.GoogleCredentialsJSON, o.Scopes...) + if len(o.GoogleJWTJSON) != 0 { + cfg, err := google.JWTConfigFromJSON(o.GoogleJWTJSON, o.Scopes...) if err != nil { return nil, fmt.Errorf("oauth2 client: error loading jwt credentials: %w", err) } @@ -228,12 +229,12 @@ var findDefaultGoogleCredentials = google.FindDefaultCredentials func (o *oAuth2Config) validateGoogleProvider() error { if o.TokenURL != "" || o.ClientID != "" || o.ClientSecret != "" || - o.AzureTenantID != "" || o.AzureResource != "" || len(o.EndpointParams) > 0 { + o.AzureTenantID != "" || o.AzureResource != "" || len(o.EndpointParams) != 0 { return errors.New("none of token_url and client credentials can be used, use google.credentials_file, google.jwt_file, google.credentials_json or ADC instead") } // credentials_json - if len(o.GoogleCredentialsJSON) > 0 { + if len(o.GoogleCredentialsJSON) != 0 { if o.GoogleDelegatedAccount != "" { return errors.New("google.delegated_account can only be provided with a jwt_file") } @@ -245,12 +246,17 @@ func (o *oAuth2Config) validateGoogleProvider() error { if o.GoogleDelegatedAccount != "" { return errors.New("google.delegated_account can only be provided with a jwt_file") } - return o.populateCredentialsJSONFromFile(o.GoogleCredentialsFile) + return populateJSONFromFile(o.GoogleCredentialsFile, &o.GoogleCredentialsJSON) } // jwt_file if o.GoogleJWTFile != "" { - return o.populateCredentialsJSONFromFile(o.GoogleJWTFile) + return populateJSONFromFile(o.GoogleJWTFile, &o.GoogleJWTJSON) + } + + // jwt_json + if len(o.GoogleJWTJSON) != 0 { + return nil } // Application Default Credentials (ADC) @@ -263,21 +269,21 @@ func (o *oAuth2Config) validateGoogleProvider() error { return fmt.Errorf("no authentication credentials were configured or detected (ADC)") } -func (o *oAuth2Config) populateCredentialsJSONFromFile(file string) error { - if _, err := os.Stat(file); os.IsNotExist(err) { +func populateJSONFromFile(file string, dst *common.JSONBlob) error { + if _, err := os.Stat(file); errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("the file %q cannot be found", file) } - credBytes, err := ioutil.ReadFile(file) + b, err := os.ReadFile(file) if err != nil { return fmt.Errorf("the file %q cannot be read", file) } - if !json.Valid(credBytes) { + if !json.Valid(b) { return fmt.Errorf("the file %q does not contain valid JSON", file) } - o.GoogleCredentialsJSON = credBytes + *dst = b return nil } diff --git a/x-pack/filebeat/input/httpjson/config_test.go b/x-pack/filebeat/input/httpjson/config_test.go index e896097bb2de..2ee8c4276c6a 100644 --- a/x-pack/filebeat/input/httpjson/config_test.go +++ b/x-pack/filebeat/input/httpjson/config_test.go @@ -324,6 +324,21 @@ func TestConfigOauth2Validation(t *testing.T) { }, }, }, + { + name: "google must work if jwt_json is correct", + input: map[string]interface{}{ + "auth.oauth2": map[string]interface{}{ + "provider": "google", + "google.jwt_json": `{ + "type": "service_account", + "project_id": "foo", + "private_key_id": "x", + "client_email": "foo@bar.com", + "client_id": "0" + }`, + }, + }, + }, { name: "google must work if credentials_json is correct", input: map[string]interface{}{ @@ -349,6 +364,16 @@ func TestConfigOauth2Validation(t *testing.T) { }, }, }, + { + name: "google must fail if jwt_json is not a valid JSON", + expectedErr: "the field can't be converted to valid JSON accessing 'auth.oauth2.google.jwt_json'", + input: map[string]interface{}{ + "auth.oauth2": map[string]interface{}{ + "provider": "google", + "google.jwt_json": `invalid`, + }, + }, + }, { name: "google must fail if the provided credentials file is not a valid JSON", expectedErr: "the file \"./testdata/invalid_credentials.json\" does not contain valid JSON accessing 'auth.oauth2'",