Configuring Dashboard access when using certificates

Versions:

  • 2.11.1

Describe the issue:
I have followed a number of tutorials/guides on setting up an OpenSearch cluster with TLS/certificates. So far, it appears that the data nodes are coordinating amongst themselves, though I haven’t probed too far in this regard.

The problem I’m seeing these types of errors (though they’re curiously classified as INFO) on the data nodes:

[INFO ][o.o.s.p.PrivilegesEvaluator] [test-data-1.acme.com] No cluster-level perm match for User
[name=CN=test-dashboard-1.acme.com,OU=FOO,O=ACME,ST=MD,C=US, backend_roles=[],
requestedTenant=null] Resolved [aliases=[*], allIndices=[*], types=[*], originalRequested=[*],
remoteIndices=[]] [Action [cluster:monitor/nodes/info]] [RolesChecked [own_index]]. No
permissions for [cluster:monitor/nodes/info]

So, my inclination is to add a CN=test-dashboard-1.acme.com,OU=FOO,O=ACME,ST=MD,C=US
user to internal_users.yml on behalf of the dashboard and assign it some backend_roles (e.g.
kibana_server and cluster_monitor), but:

Users defined in this file require a hashed password value. If the dashboard node is authenticating with client certificates, then how is the password relevant? How should it be set? How would the dashboard even specify to authenticate with basic auth instead of the certs?

I assume/hope my use-case is not terribly unique, so hopefully there’s some good alternatives/solutions to this problem. Thanks in advance.

Hi @snagiel,

You do not need to create an internal user, you can treat your DN CN=test-dashboard-1.acme.com,OU=FOO,O=ACME,ST=MD,C=US as a username and map it directly to the role (i.e.):


kibana_server:
  reserved: true
  users:
  - "kibanaserver"
  - "CN=test-dashboard-1.acme.com,OU=FOO,O=ACME,ST=MD,C=US"

Best,
Mantas

Thanks, @Mantas

Your solution seems to have addressed my reported problem, so I’d like to award it as the solution. However, another problem seems to have crept up and I’m not sure if it’s related or not.

Namely, I’m seeing errors like this on the data nodes (note that I’ve switched the config.yml’s username_attribute: cn (rather than the full DN) so that the “user” is shorter/simpler):

Tenant global_tenant is not allowed for user test-dashboard-1.acme.com

… and errors like this on the dashboard side:

no permissions for [indices:admin/get] and User [name=test-dashboard-1.acme.com, backend_roles=[], requestedTenant=null]"

So it feels like there’s some extra configuration needed to map this dashboard entity with the global tenant? Or all tenants?

Hi @snagiel,

Could you please share your opensearch_dashboards.yml so I can get familiar with your multi-tenancy set-up?

Best,
mj

Thanks a lot for solution. MyCenturaHealth

@Mantas: Your reference to multi-tenancy had me wondering if there was some inherent problem there, since I had explicitly set mine to be disabled. As soon as I enabled it, these errors went away, so thank you for that (indirect) tip. (I might be dealing with another issue now, but still investigating…) I’ve included the updated files below.

config.yaml:

_meta:
  type: "config"
  config_version: 2

config:
  dynamic:
    http:
      anonymous_auth_enabled: false
      xff:
        enabled: false
    disable_rest_auth: false
    disable_intertransport_auth: false
    kibana:
      multitenancy_enabled: true
      private_tenant_enabled: true
      default_tenant: global_tenant
      server_username: kibanaserver
      index: ".opensearch_dashboards"
      do_not_fail_on_forbidden: true
    authc:
      basic_auth_internal:
        description: "HTTP basic auth against the internal users DB"
        http_enabled: true
        transport_enabled: true
        order: 1
        http_authenticator:
          type: "basic"
          challenge: false
        authentication_backend:
          type: "internal"
      clientcert_auth_domain:
        description: "SSL auth with client certificates"
        http_enabled: true
        transport_enabled: true
        order: 0
        http_authenticator:
          type: "clientcert"
          challenge: false
          config:
            username_attribute: cn
        authentication_backend:
          type: "noop"

opensearch_dashboards.yml:

server.name: "test-dashboard-1"
server.host: "test-dashboard-1.acme.com"
opensearch.hosts: ["http://test-data-1.acme.com:9200","http://test-data-2.acme.com:9200","http://test-data-2.acme.com:9200"]

opensearch.username: "kibanaserver"
opensearch.password: "*****"
opensearch.requestHeadersAllowlist: ["securitytenant", "Authorization"]

opensearch_security:
  multitenancy:
    enabled: true
    tenants:
      enable_global: true
      enable_private: true
      preferred: ["Private", "Global"]
    enable_filter: true
  readonly_mode.roles: ["kibana_read_only"]
  cookie.secure: true
  allow_client_certificates: true

# Secure traffic between the browser and dashboard
server.ssl:
  enabled: true
  certificate: "/usr/share/opensearch-dashboards/config/certificates/os-node.pem"
  certificateAuthorities: ["/usr/share/opensearch-dashboards/config/certificates/ca.pem"]
  key: "/usr/share/opensearch-dashboards/config/certificates/os-node.key"

# Secure traffic between dashboard and data nodes
opensearch.ssl:
  alwaysPresentCertificate: true
  certificate: "/usr/share/opensearch-dashboards/config/certificates/os-node.pem"
  ssl.key: "/usr/share/opensearch-dashboards/config/certificates/os-node.key"
  certificateAuthorities: ["/usr/share/opensearch-dashboards/config/certificates/ca.pem"]
  verificationMode: "certificate"

opensearchDashboards.index: ".opensearch_dashboards"
path.data: "/usr/share/opensearch-dashboards/data"
logging.verbose: true

Hi @snagiel,

Let me know if you need help with your new issue.

best,
mj

@Mantas Happy new year, and thanks for your patience as I work through my issue(s).

I feel like I’m playing a little whack-a-mole here, but there still seems to be some issue between my dashboard node and (3) data nodes.

On the data nodes, I see errors like this at startup:

Jan 02 15:19:21 test-data-1.acme.com docker[5104]test-data-1.acme.com  | [2024-01-02T15:19:21,776][WARN ][o.o.s.c.PrivilegesInterceptorImpl] [test-data-1.acme.com] Tenant global_tenant is not allowed for user test-dashboard-1.acme.com

Recall from my opensearch_dashboards.yml above that I have set the following with respect to the global tenant:

...
opensearch_security:
 multitenancy:
    enabled: true
    tenants:
      enable_global: true
...

… so this error does not make sense to me?

And seeing errors like this on the dashboard side:

Jan 02 15:22:07 test-dashboard-1.acme.com docker[5359]: test-dashboard-1.acme.com  |
 {"type":"log","@timestamp":"2024-01-02T15:22:07Z","tags":["debug","metrics"],"pid":1,"message":"Refreshing metrics"}
Jan 02 15:22:09 test-dashboard-1.acme.com docker[5359]: test-dashboard-1.acme.com | 
{"type":"log","@timestamp":"2024-01-02T15:22:09Z","tags":["error","opensearch","data"],"pid":1,"message":"[security_exception]: no permissions for [indices:admin/get] and User [name=test-dashboard-1.acme.com, backend_roles=[], requestedTenant=null]"}

Aha! i finally found a good configuration between the data cluster and the dashboard that has eliminated these errors. It appears that the OS documentation may be somewhat misleading, but I’ll verify and update the ticket later.

OK, per my last message, I think I’ve found an error in the OS docs. Per Multi-tenancy configuration, the description for config.dynamic.kibana.server_username is:

Must match the name of the OpenSearch Dashboards server user from opensearch_dashboards.yml. Default is kibanaserver.

First of all, this is a bit ambiguous since there are several potential “user names” from that file, e.g. server.name, server.host, and opensearch.username … none of which, it should be noted, default to kibanaserver in that file.

Second, the advice is wrong; I tried providing the identical name (assuming the intent was server.name) between these two files/attributes (e.g. “ACME test dashboard”), and the errors from above persisted.

However… I guessed that, because we were dealing with SSL-based authentication/authorization, the “server name” was tied to the certificate subject being presented to the data node(s). And it seems my hunch was correct. (When I did so, the errors were finally extinguished.)

So, putting it all together…

config.yaml

...
config:
  dynamic:
    kibana:
      multitenancy_enabled: true
      private_tenant_enabled: true
      default_tenant: global_tenant
      server_username: "test-dashboard-1.acme.com" # the dashboard cert's CN
    authc:
...
      clientcert_auth_domain:
        description: "SSL auth with client certificates"
        http_enabled: true
        transport_enabled: true
        http_authenticator:
          type: "clientcert"
          config:
            username_attribute: cn # (Not the full DN)
        authentication_backend:
          type: "noop"

opensearch-dashboards.yml

...
server.name: "test-dashboard-1.acme.com" # although this likely does not matter and can be any arbitrary value (used for display purposes, per the docs)
...
opensearch_security:
  multitenancy:
    enabled: true
    tenants:
      enable_global: true
      enable_private: true
      preferred: ["Private", "Global"]
    enable_filter: true
  readonly_mode.roles: ["kibana_read_only"]
  cookie.secure: true
  allow_client_certificates: true
...
# Secure traffic between dashboard and data nodes
opensearch.ssl:
  alwaysPresentCertificate: true
  certificate: "/usr/share/opensearch-dashboards/config/certificates/os-node.pem" #where the dashboard "user name" will be read from
  key: "/usr/share/opensearch-dashboards/config/certificates/os-node.key"
  certificateAuthorities: ["/usr/share/opensearch-dashboards/config/certificates/ca.pem"]
  verificationMode: "certificate"

roles_mapping.yml

Note that we list both the CN and the full DN as users below; I believe only the CN is required, however when I assemble the certificate, it includes both in the subject-alternative names (SANs), and the full DN is spit out when asking for the cert subject (e.g. openssl x509 -noout -subject -in os-node.pem).

...
kibana_server:
  reserved: true
  backend_roles:
    - "all_access"
    - "readall_and_monitor"
    - "kibana_user"
    - "kibanauser"
  users:
    - "kibanaserver"
    - "test-dashboard-1.acme.com" # the same CN
    - "CN=test-dashboard-1.acme.com,OU=FOO,O=ACME,ST=MD,C=US"  # the full DN, per above (unsure if necessary?)
...

Thanks again, @Mantas. I hope this ends up helping someone else in the future…

Hi @snagiel,

Thanks a lot for sharing your findings with the community!

Best,
mj