Unable to customize kibana_index from headers dynamic variables

Hello !

ReadOnlyRest Entreprise user here,

I’ve been trying for several days to create a user in readonlyrest which can select it’s kibana_index by passing a custom header (This account would be a technical one, used to import/export dashboards, visualization…)
However, it seems that my dynamic variable used in the ACL is not working… and I’m not sure why.

My versions :

Kibana and Elasticsearch 7.8.1
RorEntreprise 1.26.1

My config:
Kibana.yml:

        elasticsearch.requestHeadersWhitelist: ["x-kibana-index","authorization"]

Ror.yml:

readonlyrest:
    prompt_for_basic_auth: false
    access_control_rules:
    
    - name: "Allow HealthCheck"
      type: allow
      actions: ["cluster:monitor/health"]
      methods: [GET]
 
    - name: "::ADMIN::"
      auth_key_sha512: "admin:REDACTED"
      kibana_index: ".kibana"

    - name: "::KIBANA::"
      verbosity: error
      auth_key_sha512: "kibana:REDACTED"
      kibana_index: ".kibana"

    - name: "---- SSO ----"
      verbosity: error
      indices: ["*"]  
      ror_kbn_auth:
        groups: ["Administrators"]
        name: "keycloak"
    ### I've truncated several block similar to the SSO one (ror_kbn ACL rules)

      
    - name: "::Import::Export tool ::"
      kibana_access: "admin"
      verbosity: info
      auth_key: "import:REDACTED"
      kibana_index: ".kibana_@{header:X-Kibana-Index}"        


    ror_kbn:
    - name: keycloak
      signature_key: "${KIBANA_SIGNATURE}" ##Note : This is not an issue. KIBANA_SIGNATURE is available as an env var on the host. 

In the logs

{"type": "server", "timestamp": "2021-01-19T14:55:48,186+01:00", "level": "DEBUG", "component": "t.b.r.a.b.Block", "cluster.name": "om-kibana-client", "node.name": "elasticsearch-14-nl8wx", "message": "\u001B[36mmatched { name: '::Import::Export tool ::', policy: ALLOW, rules: [auth_key,kibana_access,kibana_index] { found: user=import }\u001B[0m", "cluster.uuid": "5mXw0oHVQW-duXD2fXb3rw", "node.id": "9jTi6WbxQum8Nasm_6RblQ"  }
{"type": "server", "timestamp": "2021-01-19T14:55:48,187+01:00", "level": "INFO", "component": "t.b.r.a.l.AccessControlLoggingDecorator", "cluster.name": "om-kibana-client", "node.name": "elasticsearch-14-nl8wx", "message": "\u001B[36mALLOWED by { name: '::Import::Export tool ::', policy: ALLOW, rules: [auth_key,kibana_access,kibana_index] req={  ID:1686469875-1502468625#25113,  TYP:RRUserMetadataRequest,  CGR:N/A,  USR:import,  BRS:false,  KDX:null,  ACT:cluster:ror/user_metadata/get,  OA:REDACTED,  XFF:null,  DA:REDACTED,  IDX:<N/A>,  MET:GET,  PTH:/_readonlyrest/metadata/current_user,  CNT:<N/A>,  HDR:Authorization=Basic REDACTED, Connection=keep-alive, Content-Length=0, Host=elasticsearch-service.REDACTED.svc:9200,  HIS:[Allow HealthCheck-> RULES:[methods->true, actions->false]], [::ADMIN::-> RULES:[auth_key_sha512->false]], [::KIBANA::-> RULES:[auth_key_sha512->false]], [---- SSO :: R0 Administrators-----> RULES:[ror_kbn_auth->false]], [---- SSO :: R1 QoS manager-----> RULES:[ror_kbn_auth->false]], [---- SSO :: R2 Country Project Manager-----> RULES:[ror_kbn_auth->false]], [---- SSO :: R3 Integrators-----> RULES:[ror_kbn_auth->false]], [---- SSO :: R4 Dev and Project Manager-----> RULES:[ror_kbn_auth->false]], [ReadonlyREST Enterprise instance - admin-> RULES:[ror_kbn_auth->false]], [::Import::Export tool ::-> RULES:[auth_key->true, kibana_access->true, kibana_index->true] RESOLVED:[user=import]],  }\u001B[0m", "cluster.uuid": "5mXw0oHVQW-duXD2fXb3rw", "node.id": "9jTi6WbxQum8Nasm_6RblQ"  }

{"type": "server", "timestamp": "2021-01-19T14:55:48,204+01:00", "level": "DEBUG", "component": "o.e.c.m.MetadataCreateIndexService", "cluster.name": "om-kibana-client", "node.name": "elasticsearch-14-nl8wx", "message": "[.kibana] failed to create", "cluster.uuid": "5mXw0oHVQW-duXD2fXb3rw", "node.id": "9jTi6WbxQum8Nasm_6RblQ" , 
"stacktrace": ["org.elasticsearch.indices.InvalidIndexNameException: Invalid index name [.kibana], already exists as alias",
"at org.elasticsearch.cluster.metadata.MetadataCreateIndexService.validateIndexName(MetadataCreateIndexService.java:175) ~[elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.metadata.MetadataCreateIndexService.validate(MetadataCreateIndexService.java:1077) ~[elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.metadata.MetadataCreateIndexService.applyCreateIndexRequest(MetadataCreateIndexService.java:312) ~[elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.metadata.MetadataCreateIndexService.applyCreateIndexRequest(MetadataCreateIndexService.java:355) ~[elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.metadata.MetadataCreateIndexService$1.execute(MetadataCreateIndexService.java:289) ~[elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.ClusterStateUpdateTask.execute(ClusterStateUpdateTask.java:47) ~[elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.service.MasterService.executeTasks(MasterService.java:702) ~[elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.service.MasterService.calculateTaskOutputs(MasterService.java:324) ~[elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.service.MasterService.runTasks(MasterService.java:219) [elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.service.MasterService.access$000(MasterService.java:73) [elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.service.MasterService$Batcher.run(MasterService.java:151) [elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.service.TaskBatcher.runIfNotProcessed(TaskBatcher.java:150) [elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.cluster.service.TaskBatcher$BatchedTask.run(TaskBatcher.java:188) [elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingRunnable.run(ThreadContext.java:636) [elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.runAndClean(PrioritizedEsThreadPoolExecutor.java:252) [elasticsearch-7.8.1.jar:7.8.1]",
"at org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.run(PrioritizedEsThreadPoolExecutor.java:215) [elasticsearch-7.8.1.jar:7.8.1]",
"at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) [?:?]",
"at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) [?:?]",
"at java.lang.Thread.run(Thread.java:832) [?:?]"] }    

{"type": "server", "timestamp": "2021-01-19T14:55:48,259+01:00", "level": "DEBUG", "component": "t.b.r.a.b.Block", "cluster.name": "om-kibana-client", "node.name": "elasticsearch-14-nl8wx", "message": "\u001B[36mmatched { name: '::Import::Export tool ::', policy: ALLOW, rules: [auth_key,kibana_access,kibana_index] { found: user=import;indices=.kibana;kibana_idx=.kibana_test2233 }\u001B[0m", "cluster.uuid": "5mXw0oHVQW-duXD2fXb3rw", "node.id": "9jTi6WbxQum8Nasm_6RblQ"  }
{"type": "server", "timestamp": "2021-01-19T14:55:48,259+01:00", "level": "INFO", "component": "t.b.r.a.l.AccessControlLoggingDecorator", "cluster.name": "om-kibana-client", "node.name": "elasticsearch-14-nl8wx", "message": "\u001B[36mALLOWED by { name: '::Import::Export tool ::', policy: ALLOW, rules: [auth_key,kibana_access,kibana_index] req={  ID:1507184384-1406048521#25117,  TYP:GetRequest,  CGR:N/A,  USR:import,  BRS:false,  KDX:.kibana_test2233,  ACT:indices:data/read/get,  OA:REDACTED,  XFF:null,  DA:REDACTED,  IDX:.kibana,  MET:GET,  PTH:/.kibana/_doc/config:7.8.1,  CNT:<N/A>,  HDR:Authorization=Basic REDACTED, Connection=keep-alive, Content-Length=0, Host=elasticsearch-service.REDACTED.svc:9200, x-kibana-index=test2233, x-ror-kibana-request-method=get, x-ror-kibana-request-path=/,  HIS:[Allow HealthCheck-> RULES:[methods->true, actions->false] RESOLVED:[indices=.kibana]], [::ADMIN::-> RULES:[auth_key_sha512->false] RESOLVED:[indices=.kibana]], [::KIBANA::-> RULES:[auth_key_sha512->false] RESOLVED:[indices=.kibana]], [---- SSO :: R0 Administrators-----> RULES:[ror_kbn_auth->false] RESOLVED:[indices=.kibana]], [---- SSO :: R1 QoS manager-----> RULES:[ror_kbn_auth->false] RESOLVED:[indices=.kibana]], [---- SSO :: R2 Country Project Manager-----> RULES:[ror_kbn_auth->false] RESOLVED:[indices=.kibana]], [---- SSO :: R3 Integrators-----> RULES:[ror_kbn_auth->false] RESOLVED:[indices=.kibana]], [---- SSO :: R4 Dev and Project Manager-----> RULES:[ror_kbn_auth->false] RESOLVED:[indices=.kibana]], [ReadonlyREST Enterprise instance - admin-> RULES:[ror_kbn_auth->false] RESOLVED:[indices=.kibana]], [::Import::Export tool ::-> RULES:[auth_key->true, kibana_access->true, kibana_index->true] RESOLVED:[user=import;indices=.kibana;kibana_idx=.kibana_test2233]],  }\u001B[0m", "cluster.uuid": "5mXw0oHVQW-duXD2fXb3rw", "node.id": "9jTi6WbxQum8Nasm_6RblQ"  }

What I suspect

It’s as if the header X-Kibana-Index is forwarded to Elasticsearch too late for it to be taken into account by the /api/saved_objects/_find Kibana endpoint.

Note: I perform my test through API calls, but I see the same behaviour when I call Kibana from a browser and add the X-Kibana-Index header through browser plugins.

On a side note, I’ve tried to set kibana_index to a static value and it works like a charm.

Is there anything I can do to solve this?

OK it took a while looking at these logs, but the difference between the first one (where the custom header is not forwarded, and the second one where it works well is that the first call is made by our internal ROR ES client.

The reason the internal ES client does not take in consideration the extra header being that In Kibana < 7.9 (ROR old platform) we cannot access to any other YAML configuration keys but our own readonlyrest_kbn prefixed ones (security reasons in Kibana plugin framework).

This stuff is all gone in ROR new platform, currently in beta and available only for 7.9.3 for now. As we inject our code in the core Kibana file that reads the YAML settings.

I will think about the easiest workaround for you.

I tweaked ROR: now all the x-* headers are blindly forwarded in login phase, as they are typically used in ACL dynamic variables. I will send you a build as soon as CI comes back with one.

1 Like

Wow, You solve issues faster than light !

Thanks a lot.

I’ll integrate it in our platform and keep you updated.

1 Like

Cool! See the build in the DMs

It works !

Thanks a lot @sscarduzio!

1 Like