Intercept every request from Kibana plugin

Hi,
I have tried a lot, but I can't really get it to work.
I want to intercept all requests from kibana to elastic and modify the HTTP request Headers.
I am also not sure what the correct approach to this would be - should I try to use kfetch?
Or is the better approach $http (I think this is the old deprecated way)?
If kfetch is the right way, how do I add it to my plugin (do I need to add it to package.json)?
And how do I reference it in a .js file?

import { kfetch } from '../../../../src/legacy/ui/public/kfetch/';
module.exports = {
    init : function(){
        kfetch.interceptors.push({
            request: async function(config) {
                Logger.log("request intercepted")
                // do something with request
                return config;
            }
        });
    }
}

Doing it like this, I get:

FATAL Error: Cannot find module 'ui/new_platform'

If I import from 'ui/kfetch' I get the following error message:

FATAL Error: Cannot find module 'ui/kfetch'

Please help me with this - I have gotten so far with my custom plugin and this is the last piece of the puzzle...
(Kibana version 7.6.2)
Thank you!

Both of them are deprecated and will be removed completely soon. To intercept requests, you should use the http.intercept()api of the core setup services.

If you are already in the new platform, it will be available on the core parameter of the setup lifecycle hook. If you are working with the legacy platform, it should be available via

import { npSetup } from 'ui/new_platform';

npSetup.core.http.intercept({/...

Hi, Thanks for your response!
could you give me a complete example on how to achieve the modification of a http header?

When I use npSetup I get the following error message:

Cannot read property 'http' of null

Thanks in advance!

The interface should be the same as the kfetch code in the original post: https://github.com/elastic/kibana/blob/master/docs/development/core/public/kibana-plugin-core-public.httpinterceptor.md

It seems like your plugin is not set up the right way and the new platform is not available. Can you share either your plugin code or a minimal reproducable example?

This is what I got now:
Index.js:

      import { npSetup } from '../../src/legacy/ui/public/new_platform';
      ...
      npSetup.core.http.intercept(new CustomHttpInterceptor);

CustomHttpInterceptor.ts:

import { HttpInterceptor, HttpFetchOptions, HttpFetchOptionsWithPath, IHttpInterceptController } from '../../../../src/core/public';

export class CustomHttpInterceptor implements HttpInterceptor {
  constructor() {}

  request(
    fetchOptions: Readonly<HttpFetchOptionsWithPath>,
    controller: IHttpInterceptController
  ) {
    //CHANGE HEADER HERE?
  }
}

this results in the above mentioned error...

I used the scripts/generate_plugin.js script to generate the plugin

When you are using the generator, then it will set up a new platform plugin for you and you can get the reference to the http service in the setup method of your plugin class (core.http.intercept).

Not sure what exactly index.js is referring to, this is not a file generated by the script.

Please share either your plugin or if that's not possible a minimal reproducable example of what you are trying to do, this makes helping you much simpler.

When I use the generator script I get the following directory structure:
README.md
index.js
node_modules/
package.json
public/
server/
yarn.lock

Do you mean the init() function in the index.js

Am I missing something here?
Looks like my plugin is not generated correctly...
:frowning:

Ah, I was looking at the wrong plugin generator, my bad.

To intercept the requests going from Kibana client to Kibana server, you have to do this in the hacks.js in the public folder - the index.js file is executed on the server, but these APIs are client-side APIs.

Then make sure to whitelist the header so it's passed through to Elasticsearch (elasticsearch.requestHeadersWhitelist in kibana.yml).

Ahh ok, thanks!
So how would I rewrite the headers in the hacks.js?
Should I just set the default ajax header like this:

$.ajaxSetup({
    beforeSend: function (xhr)
    {
       xhr.setRequestHeader("SomeHeader","SomeValue");

    }
});

or

XMLHttpRequest.prototype.origOpen = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open   = function () {
            this.origOpen.apply(this, arguments);
            this.setRequestHeader('SomeHeader', 'SomeValue');
        };

Or is there a more "Kibana"-way to do it?

Kind Regards!

The rest with CustomHttpInterceptor and

import { npSetup } from 'ui/new_platform';

npSetup.core.http.intercept({/...

should work in there.

In the request function of the interceptor you should be able to add it to the headers property of the first parameter.

I'll give that a try, thanks!

Sorry if I'm asking too much, I'm almost done (I hope) :stuck_out_tongue:
So in my HTTPInterceptor I have the following:

request( fetchOptions: Readonly<HttpFetchOptionsWithPath>, controller: IHttpInterceptController) {
    // add header here
}

the constructor tells me readonly, how would I add the headers here?
Thank you!!

I checked and you are right, but this type is just a type - it's not enforced. I tried this:

        (fetchOptions as any).headers = {
          ...(fetchOptions.headers || {}),
          'my-custom-header': 'abc',
        };

and it works fine (the header is added to the request).

So it is a way to do things, but it's not guaranteed this works in every scenario or in the next version because it's basically a hack. In that case monkey-patching the XMLHttpRequest class (like you are doing above) is probably also a good approach, but it might break in other ways.

I don't think there is another official "Kibana"-way to do this, I guess this case didn't pop up before.

I used Wireshark to inspect traffic between kibana and elastic, but could only find the header in the requests made from the xhr setRequestHeader.

I need the custom Header in every request to elasticsearch (also from server side requests)
:frowning:

Can you think of a better way to intercept traffic and add a http header between kibana and elasticsearch?
It doesn't necessarily need to be a plugin...
Thanks!

Using this code, I get the error message in the browser console:

TypeError: Cannot assign to read only property 'headers' of object '#<Request>'

I can't post another reply, because I am a new User...
Hi!
I did that add the header to the kibana.yml:

elasticsearch.requestHeadersWhitelist: [ test ]

hack.js:

const interceptor = new CustomHttpInterceptor();
npSetup.core.http.intercept(interceptor);
export class CutstomHttpInterceptor implements HttpInterceptor {
  constructor() {}
  request(fetchOptions: Readonly<HttpFetchOptionsWithPath>, controller: IHttpInterceptController) {
    console.log('----------------------------------------------');
    console.log(fetchOptions.headers);
    (fetchOptions as any).headers = {
      ...(fetchOptions.headers || {}),
      'test': 'LALALALALA',
    };
  }
}

Still getting:

Cannot assign to read only property 'headers' of object '#<Request>'

Yet another reply as edit:
Ah ok, sorry!
It works this way (Xmlhttprequest monkey patching) with ui originated requests (from browser).

If I would want to edit the kibana src code, is there a single file/directory where all the requests to elastic are handled?

Thanks for all your help!

See the second part of my post from above, you also have to set the header name in the respective kibana.yml file setting:

There are some requests this wouldn’t work for, though (because the Kibana server is also making requests on its own not directly coming from the client).

If the value of the header is static, you can also simply use the “elasticsearch.customHeaders” setting in Kibana.yml

If it has to be dynamic and for every single request sent to Elasticsearch, then I think there is currently no way to do this without changing the source code of Kibana itself (basically forking it).

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.