Custom Realm with JWT token

I am struggling to make it work. I have understood the sample code given in https://github.com/elastic/shield-custom-realm-example

This is my use case. I want to enable SSO for my Kibana/Elastic Search instance through Siteminder

This is my application setup

Browser -> NGINX -> Kibana -> Elastic Search

  1. User enters the url (mycompany.domain.com) on the browser
  2. Browser redirects the URL to our corporate SSO page where the user enters his/her userID and password
  3. After successful authentication, Siteminder generates a JWT token and places in the Authorization Header
    The authorization header would look as below
Authorization : Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJqdmVtdWd1bnRhIiwicm9sZXMiOiJzdXBlcnVzZXIiLCJleHAiOjE1MTA0OTQ1MDQsImlhdCI6MTUxMDQ1ODUwNH0.KwmtMgLBMwdL3mDmO0VgQ1k_-GF5HSig991h7jIVJ4bbJNoSlGAhd13YN60YhpgLGHG5jCTsR92W2txp4pqWpKnk1whaHMUn_et4u1XkgrlXnErtrihwVdyAYufR5FdksrItW9udFL82r4QnSdaQb4U2J7c0R6dgRg7S2paetetlYIYCZQ_uEwsOHDbRkM0Sxn4FDBy8mKwwSJsmrbLwZ2Ph2WaFfgqO1dCLW0hyMc-l82Ka1kZ0V9iDFLVRsaBXuSKPIVz_uxFYn9vFhcQ9KLGwoS_JbC7Ds2j8cfXGR1Rm11yueqh2ZCvhjLFCDVtyerFkMm6IDEzbiRlXR9KHVjuJ0J7QwJDZiRiNJG7dobhtNBFX5XbzK40ihqwT3ETRSgb6p0SOqBCOrDG1bv1Udb0EYmPTvjShH0kzZoKh_6lNqjhACk5gOZs__GCIY8eJ5EZuk0Ijg-PN97jth_ny7dH2CHJLuWm8-cu1uekhHXAD_UawRleQYrt6zqJKFGf-hFNJHd0AgpLkKk30-cHEXwPrK2zOFOxpBOoHU7unGW9OAuf4X5FIvGfb4L0qIMNpL2rtHilAm6-f4CPOhXPxTkVEGXg-Jc_yQCUAPZxiGhL-02pJ_Y2G4R8aSUEZS0rVIDWx2DwUd6oodxFhEotZhgfvq3EaOSK8OxHrp6IyKzA
  1. I have implemented my CustimRealmExtension as
public Collection<String> getRestHeaders() {
        return Arrays.asList("Authorization");
    }
  1. I have my yaml configuration in elasticsearch as
xpack.security:
  authc:
    anonymous:
      roles: kibana_user
      authz_exception: false
    realms:
      jwt-realm:
        type: jwt-realm
        order: 0
      file:
        type: file
        order: 1

when I restart the server, I am getting the error as

[2017-11-12T11:15:45,501][WARN ][o.e.c.InternalClusterInfoService] [DWzMZYO] Failed to execute IndicesStatsAction for ClusterInfoUpdateJob
org.elasticsearch.ElasticsearchSecurityException: error attempting to authenticate request
	at org.elasticsearch.xpack.security.support.Exceptions.authenticationError(Exceptions.java:33) ~[?:?]
	at org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler.exceptionProcessingRequest(DefaultAuthenticationFailureHandler.java:64) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService$AuditableTransportRequest.exceptionProcessingRequest(AuthenticationService.java:541) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lambda$extractToken$10(AuthenticationService.java:281) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.extractToken(AuthenticationService.java:284) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lambda$null$0(AuthenticationService.java:218) ~[?:?]
	at org.elasticsearch.action.ActionListener$1.onResponse(ActionListener.java:59) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.xpack.security.authc.TokenService.getAndValidateToken(TokenService.java:218) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lambda$authenticateAsync$2(AuthenticationService.java:214) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lambda$lookForExistingAuthentication$4(AuthenticationService.java:246) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lookForExistingAuthentication(AuthenticationService.java:257) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.authenticateAsync(AuthenticationService.java:210) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.access$000(AuthenticationService.java:159) ~[?:?]
	at org.elasticsearch.xpack.security.authc.AuthenticationService.authenticate(AuthenticationService.java:122) ~[?:?]
	at org.elasticsearch.xpack.security.action.filter.SecurityActionFilter.applyInternal(SecurityActionFilter.java:185) ~[?:?]
	at org.elasticsearch.xpack.security.action.filter.SecurityActionFilter.apply(SecurityActionFilter.java:145) ~[?:?]
	at org.elasticsearch.action.support.TransportAction$RequestFilterChain.proceed(TransportAction.java:168) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.action.support.TransportAction.execute(TransportAction.java:142) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.action.support.TransportAction.execute(TransportAction.java:84) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.client.node.NodeClient.executeLocally(NodeClient.java:83) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.client.node.NodeClient.doExecute(NodeClient.java:72) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.client.support.AbstractClient.execute(AbstractClient.java:408) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.client.support.AbstractClient$IndicesAdmin.execute(AbstractClient.java:1256) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.client.support.AbstractClient$IndicesAdmin.stats(AbstractClient.java:1577) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.cluster.InternalClusterInfoService.updateIndicesStats(InternalClusterInfoService.java:270) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.cluster.InternalClusterInfoService.refresh(InternalClusterInfoService.java:321) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.cluster.InternalClusterInfoService.maybeRefresh(InternalClusterInfoService.java:277) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.cluster.InternalClusterInfoService.access$500(InternalClusterInfoService.java:67) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.cluster.InternalClusterInfoService$SubmitReschedulingClusterInfoUpdatedJob.lambda$run$0(InternalClusterInfoService.java:224) ~[elasticsearch-5.6.0.jar:5.6.0]
	at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingRunnable.run(ThreadContext.java:569) [elasticsearch-5.6.0.jar:5.6.0]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_131]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_131]
	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
Caused by: java.lang.IllegalArgumentException: JWT Token can't be empty. JWT Token should be passed as authorization header.e.g Authorization Bearer <Token>
	at com.jvemugunta.es.security.realm.JWTRealm.token(JWTRealm.java:119) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.extractToken(AuthenticationService.java:273) ~[?:?]

Appreciate your help and I am stuck on this one

Please make the effort to format your post to be as readable as possible - there's a live preview panel for exactly this reasons.
Lots of people read these forums, and many of them will simply skip over a post that is difficult to read, because it's just too large an investment of their time to try and follow a wall of badly formatted text.
If you are interested in getting an answer, it's in your interest to make it as easy to read and understand as possible.

This exception is being thrown by your code, so I really don't understand what you are asking us.

Why does your code throw this exception? Is the Authorization header missing, or is it coming through as a standard Basic auth header?
If you can provide concrete details about what Elasticsearch is doing, we can help, but I can't just guess what's going on from an error message in your code.

Please also provide precise version numbers in your questions, our answers are almost always dependent on the version of ES you are running.

However, I can provide some pointers on your setup:

  • Don't throw an exception from your token method. That will cause fatal errors in the authentication module. Instead, call setFailedAuthenticationDetails to log the error and then return a null result.
  • You've enabled a file realm and an anonymous role, but then your code assumes that every incoming request will have a JWT token. That doesn't make sense - if you are allowing anonymous users and file based users, then you can't expect that every request will have a JWT header.
  • How does your data get into Elasticsearch? You seem to expect that every request comes via nginx and Kibana, but that's not going to be true for data ingest.

/*

  • Licensed to Elasticsearch under one or more contributor
  • license agreements. See the NOTICE file distributed with
  • this work for additional information regarding copyright
  • ownership. Elasticsearch licenses this file to you under
  • the Apache License, Version 2.0 (the "License"); you may
  • not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  • http://www.apache.org/licenses/LICENSE-2.0
  • Unless required by applicable law or agreed to in writing,
  • software distributed under the License is distributed on an
  • "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  • KIND, either express or implied. See the License for the
  • specific language governing permissions and limitations
  • under the License.
    */
    package com.jvemugunta.es.security.realm;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.user.User;

import com.bcbsfl.es.security.utils.JWTValidator;
import com.bcbsfl.es.security.utils.UserInfo;

public class JWTRealm extends Realm {

public static final String TYPE = "jwt-realm";

public static final String AUTHORIZATION_HEADER = "Authorization";

/**
 * Constructor for the Realm. This constructor delegates to the super class
 * to initialize the common aspects such as the logger.
 * 
 * @param config
 *            the configuration specific to this realm
 */
public JWTRealm(RealmConfig config) {
    super(TYPE, config);
}

/**
 * This constructor should be used by extending classes so that they can
 * specify their own specific type
 * 
 * @param type
 *            the type of the realm
 * @param config
 *            the configuration specific to this realm
 */
protected JWTRealm(String type, RealmConfig config) {
    super(TYPE, config);
}

@Deprecated
@Override
public User authenticate(AuthenticationToken token) {
    throw new UnsupportedOperationException("Deprecated");
}

/**
 * Method that handles the actual authentication of the token. This method
 * will only be called if the token is a supported token. The method
 * validates the credentials of the user and if they match, a {@link User}
 * will be returned as the argument to the {@code listener}'s
 * {@link ActionListener#onResponse(Object)} method. Else {@code null} is
 * returned.
 * 
 * @param authenticationToken
 *            the token to authenticate
 * @param listener
 *            return authentication result by calling
 *            {@link ActionListener#onResponse(Object)}
 */
@Override
public void authenticate(AuthenticationToken authenticationToken, ActionListener<User> listener) {
    try {
        JWTToken token = (JWTToken) authenticationToken;
        final String jwtToken = (String) token.credentials();

        UserInfo userInfo = new JWTValidator().parseAndValidateJWTToken(jwtToken);

        if (userInfo != null) {
            listener.onResponse(new User(userInfo.getSubject(), userInfo.getRoles()));
        } else {
            listener.onResponse(null);
        }
    } catch (Exception e) {
        listener.onFailure(e);
    }
}

@Override
public User lookupUser(String arg0) {
    return null;
}

@Override
public boolean supports(AuthenticationToken token) {
    return false;
}

@Override
public AuthenticationToken token(ThreadContext threadContext) {
    String header = threadContext.getHeader(AUTHORIZATION_HEADER);
    String token = null;
    if (header != null && header.startsWith("Bearer")) {

        token = header.substring("Bearer".length()).trim();
    }
    if (Strings.isEmpty(token)) {
        throw new IllegalArgumentException(
                "JWT Token can't be empty. JWT Token should be passed as authorization header.e.g Authorization Bearer <Token>");
    }
    return new JWTToken(token);
}

@Override
public boolean userLookupSupported() {
    return false;
}

}

Please find the above code.

I have changed the yaml file as

xpack.security:
authc:
realms:
jwt-realm:
type: jwt-realm
order: 0
file:
type: file
order: 1

when I start the elasticsearch, I am getting the error at the immedietly after the startup as

Caused by: java.lang.IllegalArgumentException: JWT Token can't be empty. JWT Token should be passed as authorization header.e.g Authorization Bearer
** at com.bcbsfl.es.security.realm.JWTRealm.token(JWTRealm.java:119) ~[?:?]**
** at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.extractToken(AuthenticationService.java:273) ~[?:?]**
** ... 28 more**

How does your data get into Elasticsearch? You seem to expect that every request comes via nginx and Kibana, but that's not going to be true for data ingest.

There are two ways to access elastic search

  1. The data ingest happens with Java client API using "elastic" user as Authorization Basic header and the data ingest is working

for example the below request works

curl --user 'elastic:changeme' -XPUT 'localhost:9200/index1/tweet/2?pretty' -H 'Content-Type: application/json' -d'

{
"user" : "jvemugunta",
"post_date" : "2009-11-15T14:12:12",
"message" : "Jagadish Vemugunta"
}
'
{
"_index" : "index1",
"_type" : "tweet",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : true
}

  1. The second method is to search the data ingested as above. The flow would be as
    Browser -> NGINX -> Kibana -> Elastic Search
    All the requests coming from the browser will reach Kibana as
    Authorization : Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJqdmVtdWd1bnRhIiwicm9sZXMiOiJzdXBlcnVzZXIiLCJleHAiOjE1MTA0OTQ1MDQsImlhdCI6MTUxMDQ1ODUwNH0.KwmtMgLBMwdL3mDmO0VgQ1k_-GF5HSig991h7jIVJ4bbJNoSlGAhd13YN60YhpgLGHG5jCTsR92W2txp4pqWpKnk1whaHMUn_et4u1XkgrlXnErtrihwVdyAYufR5FdksrItW9udFL82r4QnSdaQb4U2J7c0R6dgRg7S2paetetlYIYCZQ_uEwsOHDbRkM0Sxn4FDBy8mKwwSJsmrbLwZ2Ph2WaFfgqO1dCLW0hyMc-l82Ka1kZ0V9iDFLVRsaBXuSKPIVz_uxFYn9vFhcQ9KLGwoS_JbC7Ds2j8cfXGR1Rm11yueqh2ZCvhjLFCDVtyerFkMm6IDEzbiRlXR9KHVjuJ0J7QwJDZiRiNJG7dobhtNBFX5XbzK40ihqwT3ETRSgb6p0SOqBCOrDG1bv1Udb0EYmPTvjShH0kzZoKh_6lNqjhACk5gOZs__GCIY8eJ5EZuk0Ijg-PN97jth_ny7dH2CHJLuWm8-cu1uekhHXAD_UawRleQYrt6zqJKFGf-hFNJHd0AgpLkKk30-cHEXwPrK2zOFOxpBOoHU7unGW9OAuf4X5FIvGfb4L0qIMNpL2rtHilAm6-f4CPOhXPxTkVEGXg-Jc_yQCUAPZxiGhL-02pJ_Y2G4R8aSUEZS0rVIDWx2DwUd6oodxFhEotZhgfvq3EaOSK8OxHrp6IyKzA

This header is nothing but as JWT token. the token comes as "Authorization Bearer "

Hope this makes sense to undersatnd what I want to achieve

As I said earlier, Don't throw exceptions from your token method.

If you receive a header that you don't understand, just return a null user.

You cannot assume that every request that comes to the server is for your realm and will have your headers included.

Hi Tim,
Thanks for your time.

Here are the final changes

  1. I am running elastic search on 5.6.0
  2. I have added the right checks using setFailedAuthenticationDetails method
  3. Reinstalled the custom realm using
    bin/x-pack/extension install file:///Users/jvemugunta/git_repos/shield-custom-realm-example/build/distributions/x-pack-custom-realm-extension-example-5.6.0.zip
  4. When I start up, I don't see any more errors
  5. However, when I call the rest API using JWT token, I end up this error

curl -H 'Authorization:Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJqdmVtdWd1bnRhIiwicm9sZXMiOiJzdXBlcnVzZXIiLCJleHAiOjE1MTA2MTE3MzIsImlhdCI6MTUxMDU3NTczM30.MLAN-OAIRkyl1hQ5a_kru2MaK0jqwXyp6C0yMx4WGRn_Z-Ky-YYw8dYl0uDrpbeYSMpQUcdrtReSLQ4V2X1p5odwzxn_MS-mSQsCwDLPwNtOK3PfSVsCpfBAI2hLjY4w3KvjdXWoylUOXNy8EpkM_nuKVtX5qniDyBLYhvaWH1nHQPqb5ugZGIovMk7AhDt0o0AMrlDaa_ZSEeS0-XtJ2SSe3djRoE8q1XE41d3fZDb8WddK379BcEZyGSIJlPAZB5oaZJZUUBba28s6WafI_S7tmdBW_M8Aho7N6WOYuOHlqJlUBKX1VRstq8sg9qA4t-f1CJnSMeH47njXVJwSVwbObxU5OXlz1G5kIqemo8ue8x6IJze9nNRgpgRfvbE4QsaLstcLhRLbnhXtqcVz0SHQ-ZPHpewU9djamZfXv0vcJkxoyYngn-M_QsMStBc9jfcQh3lKBspifijszPKDO_iUXfFZr8zGhpruZvq1cL_41P4R6CCFoo4VQk1NWbz0EiL1vtdodLMZWcBJveO8Z2vHoz6wePzAV3_9PP8otRtCJvSmBWkByMZ-LfZvxeOcadsz7z2vCVlUppq4wRaMdv0duscGsSVdyJziIAG18J0mVsh5K0fKFVl6b0fQbDblPT7wWGcZCb4kIRSMUQijxN4uPY9gzlA8SO3BNjHgl88' 'localhost:9200/index1/tweet/1?pretty'
{
"error" : {
"root_cause" : [
{
"type" : "security_exception",
"reason" : "error attempting to authenticate request",
"header" : {
"WWW-Authenticate" : "Basic realm="security" charset="UTF-8""
}
}
],
"type" : "security_exception",
"reason" : "error attempting to authenticate request",
"caused_by" : {
"type" : "access_control_exception",
"reason" : "access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")"
},
"header" : {
"WWW-Authenticate" : "Basic realm="security" charset="UTF-8""
}
},
"status" : 401
}

Here is the modified realm code for your reference

/*

  • Licensed to Elasticsearch under one or more contributor
  • license agreements. See the NOTICE file distributed with
  • this work for additional information regarding copyright
  • ownership. Elasticsearch licenses this file to you under
  • the Apache License, Version 2.0 (the "License"); you may
  • not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  • http://www.apache.org/licenses/LICENSE-2.0
  • Unless required by applicable law or agreed to in writing,
  • software distributed under the License is distributed on an
  • "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  • KIND, either express or implied. See the License for the
  • specific language governing permissions and limitations
  • under the License.
    */
    package com.jvemugunta.es.security.realm;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.user.User;

import com.bcbsfl.es.security.utils.JWTValidator;
import com.bcbsfl.es.security.utils.UserInfo;

public class JWTRealm extends Realm {

public static final String TYPE = "jwt-realm";

public static final String AUTHORIZATION_HEADER = "Authorization";
Logger logger = ESLoggerFactory.getLogger(JWTRealm.class);

/**
 * Constructor for the Realm. This constructor delegates to the super class
 * to initialize the common aspects such as the logger.
 * 
 * @param config
 *            the configuration specific to this realm
 */
public JWTRealm(RealmConfig config) {
    super(TYPE, config);
}

/**
 * This constructor should be used by extending classes so that they can
 * specify their own specific type
 * 
 * @param type
 *            the type of the realm
 * @param config
 *            the configuration specific to this realm
 */
protected JWTRealm(String type, RealmConfig config) {
    super(TYPE, config);
}

@Deprecated
@Override
public User authenticate(AuthenticationToken token) {
    throw new UnsupportedOperationException("Deprecated");
}

/**
 * Method that handles the actual authentication of the token. This method
 * will only be called if the token is a supported token. The method
 * validates the credentials of the user and if they match, a {@link User}
 * will be returned as the argument to the {@code listener}'s
 * {@link ActionListener#onResponse(Object)} method. Else {@code null} is
 * returned.
 * 
 * @param authenticationToken
 *            the token to authenticate
 * @param listener
 *            return authentication result by calling
 *            {@link ActionListener#onResponse(Object)}
 */
@Override
public void authenticate(AuthenticationToken authenticationToken, ActionListener<User> listener) {
    try {
        JWTToken token = (JWTToken) authenticationToken;
        final String jwtToken = (String) token.credentials();

        UserInfo userInfo = new JWTValidator().parseAndValidateJWTToken(jwtToken);

        if (userInfo != null) {
            listener.onResponse(new User(userInfo.getSubject(), userInfo.getRoles()));
        } else {
            listener.onResponse(null);
        }
    } catch (Exception e) {
        listener.onFailure(e);
    }
}

@Override
public User lookupUser(String arg0) {
    return null;
}

@Override
public boolean supports(AuthenticationToken token) {
    return false;
}

@Override
public AuthenticationToken token(ThreadContext threadContext) {
    String header = threadContext.getHeader(AUTHORIZATION_HEADER);

    if (Strings.isEmpty(header)) {
        logger.log(Level.INFO, "Authorization header should not be empty");
        setFailedAuthenticationDetails("Authorization header should not be empty", null);
        return null;
    }

    String token = null;
    if (header != null && header.startsWith("Bearer")) {

        token = header.substring("Bearer".length()).trim();
    }
    if (Strings.isEmpty(token)) {
        logger.log(Level.INFO, "token is not valid " + header);
        setFailedAuthenticationDetails("JWT Token should be passed as authorization header.e.g Authorization Bearer <Token>",
                new IllegalArgumentException("token is not valid " + header));
        return null;
    }

    return new JWTToken(token);
}

@Override
public boolean userLookupSupported() {
    return false;
}

}

Your custom realm runs within the X-Pack plugin, so you need to read the plugin authoring guide.

The section on the security manager is relevant here.
https://www.elastic.co/guide/en/elasticsearch/plugins/5.6/plugin-authors.html#plugin-authors-jsm

Thanks Tim. I was able to fix the permissions. Can you advise me on the root cause of the problem
My roles.yaml file looks as below
role1:
indices:
- names: [ 'index1' ]
privileges: [ 'read' ]
role2:
indices:
- names: [ 'index2' ,'index1']
privileges: [ 'read' ]

when I call the below URL, it throws the error as

curl -H 'Authorization:Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJqdmVtdWd1bnRhIiwicm9sZXMiOiJpbmRleDEsaW5kZXgyIiwiZXhwIjoxNTEwNjYyODk5LCJpYXQiOjE1MTA2MjY4OTl9.KHrGL1l4Kx5XnJGtRrIsBMn5f0L5d2PiD-V0HHvBDKEj5I-k4DUxaxdLVPeOQyDa9cPnSj7kSDgxQUpf-hFCL_FPhKjkfhKDf4X9GlQyXdY3Thz5Ghgm1_41ASTgDs3UbLKk0YJemIj3Qcv3BxX8-4frrZBUV75vxAbhNZ_tYIy7WNO4MCEAKtY_llNjkRbvhaYg7Ai6uwoehD41Uvd6Z8E5jFkqj1RDSN-XXz5rFmEsnCYb9DMcLesEnZEYa4vuR2M5Fg8KOXRa3eiTDDy-TD-wPjhpShi4jA-YAmMyQ7YxtkAx4aT_SIw2g0zAknH6FdOSZDp10M7jdybzenRDc9ZMaQ6Adm9eOE9HU6VKfeebvwp19Iw5JD4Joc2z7dtthGmdTmSsYQI2YpOU0hD_xGq_tEdzmKGt9MgI29lvsLY9DIIDP6-dGMpvVWXpIbgktM2eTyrId2hxelFnfFJehpDxLIqSkyi3YTvgBCbm2tIqXIUIAiLF1eiFovq3DeAUz1cKDvQAUC4JtgelLfrIDjsYO4KXt8Pl943pEhKdQEVwueCQS78Ibpwj-MQnbCwidycBPFUDgwcjyRZn7YP9cJXx-T4VU38gFdiL_lhct2CLdw9iZWkqJB7XyA3Helrg96vOygiAVe9jG9u_12Y_J49s9XWaqmifnfWe-9UIQxc' 'localhost:9200/index1/tweet/1?pretty'
{
"error" : {
"root_cause" : [
{
"type" : "security_exception",
"reason" : "unable to authenticate user [jvemugunta] for REST request [/index1/tweet/1?pretty]",
"header" : {
"WWW-Authenticate" : "Basic realm="security" charset="UTF-8""
}
}
],
"type" : "security_exception",
"reason" : "unable to authenticate user [jvemugunta] for REST request [/index1/tweet/1?pretty]",
"header" : {
"WWW-Authenticate" : "Basic realm="security" charset="UTF-8""
}
},
"status" : 401
}

The java error stack shows as

[2017-11-13T21:37:24,711][DEBUG][r.suppressed ] path: /index1/tweet/1, params: {pretty=, index=index1, id=1, type=tweet}
org.elasticsearch.ElasticsearchSecurityException: unable to authenticate user [jvemugunta] for REST request [/index1/tweet/1?pretty]
at org.elasticsearch.xpack.security.support.Exceptions.authenticationError(Exceptions.java:39) ~[?:?]
at org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler.failedAuthentication(DefaultAuthenticationFailureHandler.java:37) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$AuditableRestRequest.authenticationFailed(AuthenticationService.java:607) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.consumeUser(AuthenticationService.java:385) ~[?:?]
at org.elasticsearch.action.ActionListener$1.onResponse(ActionListener.java:59) ~[elasticsearch-5.6.0.jar:5.6.0]
at org.elasticsearch.xpack.common.IteratingActionListener.onResponse(IteratingActionListener.java:108) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lambda$consumeToken$13(AuthenticationService.java:318) ~[?:?]
at org.elasticsearch.xpack.common.IteratingActionListener.onResponse(IteratingActionListener.java:111) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lambda$consumeToken$13(AuthenticationService.java:318) ~[?:?]
at org.elasticsearch.xpack.common.IteratingActionListener.onResponse(IteratingActionListener.java:111) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lambda$consumeToken$13(AuthenticationService.java:318) ~[?:?]
at org.elasticsearch.xpack.common.IteratingActionListener.run(IteratingActionListener.java:93) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.consumeToken(AuthenticationService.java:326) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lambda$extractToken$9(AuthenticationService.java:275) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.extractToken(AuthenticationService.java:284) ~[?:?]
at org.elasticsearch.xpack.security.authc.AuthenticationService$Authenticator.lambda$null$0(AuthenticationService.java:218) ~[?:?]
at org.elasticsearch.action.ActionListener$1.onResponse(ActionListener.java:59) ~[elasticsearch-5.6.0.jar:5.6.0]

That means what it says: the request could not be authenticated.
This is an issue for you to debug - why didn't your realm authenticate that request?

Given that the error message contains a username, it indicates that something (presumably your realm) generated a token, but then the token was considered to be invalid. It would seem that your authenticate(AuthenticationToken, ActionListener<User>) method, isn't doing the right thing, but the issue is within your JWTValidator, and I can't help with that.

Sorry, Tim. I didn't get your point. Apologies. This is the code you are suggesting to look into.
I don't see this method being called in the stackTrace or in the logs.
Can you help me on where you saw the reference to JWTValidator in the logs.

@Override
public void authenticate(AuthenticationToken authenticationToken, ActionListener listener) {

    logger.log(Level.DEBUG, "Inside AuthenticationToken method");
    try {
        JWTToken token = (JWTToken) authenticationToken;
        final String jwtToken = (String) token.credentials();

        UserInfo userInfo = new JWTValidator().parseAndValidateJWTToken(jwtToken);

        if (userInfo != null) {
            logger.log(Level.DEBUG, "Subject is " + userInfo.getSubject());
            logger.log(Level.DEBUG, "Roles are " + userInfo.getRoles());

            listener.onResponse(new User(userInfo.getSubject(), userInfo.getRoles()));
        } else {
            listener.onResponse(null);
        }
    } catch (Exception e) {
        listener.onFailure(e);
    }
}

Hi Tim, I have figured out the issue. I have not implemented supports() method in the custom realm. Once I have implemented this method, things start flowing

@Override
public boolean supports(AuthenticationToken token) {

    return token instanceof JWTToken;
}

However, the authentication is still not successful when the code tries to load a resource(public certificate in pem format). I can see the public certificate is in the plugin class path. This is my final distribution jar file and you can see the public_key.pem file in the jar file

jar tvf x-pack-custom-realm-extension-example-5.6.0-all.jar | grep public
2329 Tue Nov 14 16:22:02 EST 2017 public_key.pem

This is how I am trying to load the public_key.pem file in my code

InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("public_key.pem");

The input stream is coming as null. I do the run similar code with my test cases and they all are working good

Can you give me some pointers on why the resources is not getting loaded

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