How to get a NodeClient inside a X-Pack plugin?


(Marcel Hillmann) #1

I would like to read/write an index inside our custom X-Pack plugin.
I use the SecurityExtension interface v6.3.1 to load the Realm.

In org.elasticsearch.xpack.security.Security is no delegation for the Client object.
I tried to use the RestHighLevelClient but that failed at runtime.

I checked, different X-Pack plugin implementation but could not find a solution.

What is the right solution?

Thanks
Marcel


(David Turner) #2

A Client is passed into each Plugin via the createComponents() method:

In practice what it gets seems to be a NodeClient but you shouldn't rely on this.


(Marcel Hillmann) #3

Hi David,

that is correct for Elasticsearch Plugins not for X-Pack Plugins, in
org.elasticsearch.xpack.security.Security

https://github.com/elastic/elasticsearch/blob/master/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java#L383-L467

Is no delagation of the Client object to the plugin.

Thanks
Marcel


(Georg Summer) #4

I am facing the same issue.

The createComponents gets called in the *Plugin class; The CustomRealm is created in SecurityExtension.
Additionally the getRealms method of the SecurityExtension is called before the createComponents of the Plugin.


(David Turner) #5

I misunderstood your question. You are not asking about plugins (i.e. things that derive from org.elasticsearch.plugins.Plugin) which includes various X-pack plugins. You are asking about security extensions (i.e. things that derive from org.elasticsearch.xpack.core.security.SecurityExtension). I cannot see an easy way to get hold of a client from within such a thing.


(Georg Summer) #6

I am currently trying to have my Plugin class implement the SecurityExtension. The plugin / realm starts. The above mentioned issue with the order in which what is called I "solved" by declaring a

protected static Client client;

and using a Supplier <Client> to feed it into the Realm. Currently when I use the Client

SearchRequestBuilder request = client.prepareSearch("admin-stuff").setTypes("grp") .setQuery(QueryBuilders.matchAllQuery()); logger.info(request.toString()); SearchHits result = request.get().getHits();

I get the the following log output and then ES dies because of an StackOverflow Exception

JWTSecurityRealm] REALM: org.elasticsearch.client.node.NodeClient@588d630d
[2018-11-13T16:03:15,451][INFO ][i.z.s.p.e.j.JWTSecurityRealm] {"query":{"match_all":{"boost":1.0}}}
[2018-11-13T16:03:15,454][WARN ][i.z.s.p.e.j.JWTSecurityRealm] REALM: org.elasticsearch.client.node.NodeClient@588d630d
[2018-11-13T16:03:15,454][INFO ][i.z.s.p.e.j.JWTSecurityRealm] {"query":{"match_all":{"boost":1.0}}}
[2018-11-13T16:03:15,456][WARN ][i.z.s.p.e.j.JWTSecurityRealm] REALM: org.elasticsearch.client.node.NodeClient@588d630d
[2018-11-13T16:03:15,456][INFO ][i.z.s.p.e.j.JWTSecurityRealm] {"query":{"match_all":{"boost":1.0}}}


(Yogesh Gaikwad) #7

Hi @gsu Could you please share the exception stack trace? Thanks.


(Georg Summer) #8

Have a look at:

The stack-trace is too big for this text-box here.


(Marcel Hillmann) #9

Look like a recursive call.

Like calling Endpoint, which calls an Endpoint, .... .

So the jvm is killing the server to protect the environment.

Thanks
Marcel


(Georg Summer) #10

Figured so. I could imagine that the nodeclient tries to authenticate itself. So I added the withHeaders and authorization to it. It obviously isn't working. Alternative would be to try and create a new NodeClient with authorization backed it. Kind of defeats the whole purpose of what we want to achieve.


(Marcel Hillmann) #11

Sorry, but without JWTSecurityRealm source I couldn't give you an advise.

Thanks
Marcel


(Georg Summer) #12

added the code calling the client and the Classes properties.


(Marcel Hillmann) #13

You need an exit dialog in the method token, like
" public AuthenticationToken token(final ThreadContext threadContext) {
if(threadContext.getHeader("internal-callback") != null){
return null;
}"

and in "protected Set getPermissions(JsonWebToken jwt) {" you have to set the header.

Thx
Marcel


(Georg Summer) #14

Good idea, thanks @Marcel-Hillmann. Worked like a charm until

{
        "error": {
            "root_cause": [
                {
                    "type": "security_exception",
                    "reason": "action [indices:data/read/search] is unauthorized for user [_system]",
                    "header": {
                        "WWW-Authenticate": "Basic realm=\"security\" charset=\"UTF-8\""
                    }
                }
            ],
            "type": "security_exception",
            "reason": "action [indices:data/read/search] is unauthorized for user [_system]",
            "header": {
                "WWW-Authenticate": "Basic realm=\"security\" charset=\"UTF-8\""
            }
        },
        "status": 403
    }

The _system user is used by xpack internally. It seems
Looking at the documentation
https://www.elastic.co/guide/en/elastic-stack-overview/6.4/java-clients.html
tells us:

Using the Java Node Client with secured clusters is not recommended or supported.

I added the _system user via the user_roles file to the superuser group, didn't change anything. Interestingly the Authorization: Basic header I add isn't used in the request even though the internal-callback is.

{
    Map<String, String> headers = new HashMap<String, String>();
    headers.put("internal-callback", "internal-callback");
    headers.put("Authorization", "Basic ");
    Client authClient = client.filterWithHeader(headers);
    SearchRequestBuilder request = authClient.prepareSearch("admin-stuff").setTypes("grp").setQuery(QueryBuilders.matchAllQuery());
}

Resulting in log output:

[2018-11-14T12:33:54,975][INFO ][i.z.s.p.e.j.JWTSecurityRealm] TOKEN:{Authorization=Bearer JWT-TOKEN}
[2018-11-14T12:33:55,085][WARN ][i.z.s.p.e.j.JWTSecurityRealm] REALM: org.elasticsearch.client.node.NodeClient@588d630d
[2018-11-14T12:33:55,092][INFO ][i.z.s.p.e.j.JWTSecurityRealm] {"query":{"match_all":{"boost":1.0}}}
[2018-11-14T12:33:55,093][INFO ][i.z.s.p.e.j.JWTSecurityRealm] TOKEN:{Authorization=Bearer JWT-TOKEN, internal-callback=internal-callback}