安全编辑

Kibana 通常能够对核心和插件开发者透明地实现安全性,并且在很大程度上仍然如此。Kibana 基于 Elasticsearch Cluster 提供的两种方法:callWithRequestcallWithInternalUser

callWithRequest 使用 Kibana 最终用户的身份验证凭据对 Elasticsearch 执行请求。因此,如果您在使用 callWithRequest 时以用户 foo 登录 Kibana,则 Kibana 会以用户 foo 的身份对 Elasticsearch 执行请求。过去,callWithRequest 已被广泛用于执行由 Kibana 最终用户发起的操作。

callWithInternalUser 使用内部 Kibana 服务器用户对 Elasticsearch 执行请求,并且过去一直用于执行不是由 Kibana 最终用户发起的操作;例如,创建初始 .kibana 索引或对 Elasticsearch 执行运行状况检查。

但是,随着基于角色的访问控制 (RBAC) 引入的更改,这不再是简单明了的事情。Kibana 现在要求对 .kibana 索引的所有访问都必须通过 SavedObjectsClient 进行。这曾经是一个最佳实践,因为 SavedObjectsClient 负责在 Elasticsearch 中存储的文档与已保存对象之间进行转换,但 RBAC 现在正在利用这种抽象来实现访问控制并确定何时使用 callWithRequestcallWithInternalUser

基于角色的访问控制编辑

Kibana 中的基于角色的访问控制 (RBAC) 依赖于 Elasticsearch 公开的应用程序权限。这允许 Kibana 定义 Kibana 希望授予用户的权限,使用角色将它们分配给相关用户,然后授权用户执行特定操作。这在 SavedObjectsClient 的安全实例中处理,并在使用 request.getSavedObjectsClient()savedObjects.getScopedSavedObjectsClient() 时对消费者透明地可用。

Kibana 权限编辑

当 Kibana 首次启动时,它会对 Elasticsearch 执行以下 POST 请求。这会将权限的定义与稍后用于授权用户的各种 操作 同步

POST /_security/privilege
Content-Type: application/json
Authorization: Basic {kib} changeme

{
   "kibana-.kibana":{
       "all":{
           "application":"kibana-.kibana",
           "name":"all",
           "actions":[
               "version:7.0.0-alpha1-SNAPSHOT",
               "action:login",
               "action:*"
           ],
           "metadata":{}
       },
       "read":{
           "application":"kibana-.kibana",
           "name":"read",
           "actions":[
               "version:7.0.0-alpha1-SNAPSHOT",
               "action:login",
               "saved_object:dashboard/get",
               "saved_object:dashboard/bulk_get",
               "saved_object:dashboard/find",
               ...
           ],"metadata":{}}
   }
}

应用程序是通过将 kibana- 的前缀与 kibana.ymlkibana.index 的值连接起来创建的,因此不同的 Kibana 租户彼此隔离。

分配 Kibana 权限编辑

Kibana 权限使用 applications 元素分配给特定角色。例如,以下角色将 * 资源(将来将用于保护空间)的所有权限分配给默认的 Kibana 应用程序

"new_kibana_user": {
   "applications": [
     {
       "application": "kibana-.kibana",
       "privileges": [
         "all"
       ],
       "resources": [
         "*"
       ]
     }
   ]
 }

授予Kibana 权限的角色应使用Kibana 角色管理 API管理 → 安全 → 角色页面进行管理,而不是直接使用 Elasticsearch 角色管理 API。然后可以使用 Elasticsearch 用户管理 API将此角色分配给用户。

授权编辑

Elasticsearch has privileges API 确定用户是否有权执行特定操作

POST /_security/user/_has_privileges
Content-Type: application/json
Authorization: Basic foo_read_only_user password

{
   "applications":[
       {
           "application":"kibana-.kibana",
           "resources":["*"],
           "privileges":[
             "saved_object:dashboard/save",
           ]
       }
   ]
}

Elasticsearch 检查用户是否被授予了特定操作的权限。如果用户被分配了一个授予权限的角色,Elasticsearch 会使用Kibana 权限定义将其与操作相关联,这使得以编程方式授权用户更加直观和灵活。

一旦我们授权用户执行特定操作,我们就可以使用 callWithInternalUser 执行请求。

插件功能注册编辑

如果您的插件将与 Kibana 的默认发行版一起使用,那么您就可以注册您的插件提供的功能。功能通常是 Kibana 中的应用程序;注册后,您就可以通过 Spaces 切换它们,并在启用安全功能时通过 Roles 保护它们。

UI 功能编辑

注册功能还可以让您的插件访问“UI 功能”。这些功能是布尔标志,您可以使用它们根据当前用户的权限有条件地呈现您的界面。例如,如果当前用户未经授权,您可以隐藏或禁用“保存”按钮。

注册功能编辑

功能注册由内置的 features 插件控制。要注册功能,请从插件的 setup 生命周期函数调用 featuresregisterKibanaFeature 函数,并提供相应的详细信息

setup(core, { features }) {
  features.registerKibanaFeature({
    // feature details here.
  });
}

功能详细信息编辑

注册功能包含以下字段。有关更多信息,请参阅功能注册表接口

字段名称 数据类型 示例 描述

id(必填)

字符串

"sample_feature"

功能的唯一标识符。通常,插件的 ID 就足够了。

name(必填)

字符串

"示例功能"

功能的可读名称。

category(必填)

AppCategory

DEFAULT_APP_CATEGORIES.kibana

最能代表您功能的 AppCategory。用于组织管理屏幕中功能的显示。

app(必填)

字符串[]

["sample_app", "kibana"]

此功能启用的应用程序数组。通常,您的插件的所有应用程序(来自 uiExports)都将包含在此处。

privileges(必填)

KibanaFeatureConfig.

请参阅示例 1示例 2

此功能正常运行所需的一组权限。

subFeatures(可选)

KibanaFeatureConfig.

请参阅示例 3

一组子功能,可实现比 allread 功能权限更精细的访问控制。这些选项仅在黄金级订阅及更高版本中可用。

权限定义编辑

功能注册的 privileges 部分允许插件为其应用程序实现读/写和只读模式。

有关字段和选项的完整说明,请参阅功能注册表接口

使用 UI 功能编辑

UI 功能可用于您的公共(客户端)插件代码。这些功能是只读的,用于通知 UI。此对象由功能 ID 命名空间。例如,如果您的功能 ID 是“foo”,则您的 UI 功能存储在 uiCapabilities.foo 中。可以从 core.application 服务的插件的 start 生命周期访问功能

public start(core) {
  const { capabilities } = core.application;

  const canUserSave = capabilities.foo.save;
  if (canUserSave) {
    // show save button
  }
}

示例 1:Canvas 应用程序编辑

public setup(core, { features }) {
  features.registerKibanaFeature({
    id: 'canvas',
    name: 'Canvas',
    category: DEFAULT_APP_CATEGORIES.kibana,
    app: ['canvas', 'kibana'],
    catalogue: ['canvas'],
    privileges: {
      all: {
        savedObject: {
          all: ['canvas-workpad'],
          read: ['index-pattern'],
        },
        ui: ['save'],
      },
      read: {
        savedObject: {
          all: [],
          read: ['index-pattern', 'canvas-workpad'],
        },
        ui: [],
      },
    },
  });
}

这显示了 Canvas 应用程序如何将自身注册为 Kibana 功能。请注意,它为每个权限指定了不同的 savedObject 访问级别

  • 具有读/写访问权限(all 权限)的用户需要能够读/写 canvas-workpad 已保存对象,并且他们需要对 index-pattern 已保存对象的只读访问权限。
  • 具有只读访问权限(read 权限)的用户不需要对任何已保存对象具有读/写访问权限,而是获得对 index-patterncanvas-workpad 已保存对象的只读访问权限。

此外,Canvas 还注册了 canvas UI 应用程序和 canvas 目录条目。这告诉 Kibana 这些实体可供具有 readall 权限的用户使用。

all 权限定义了一个“保存”UI 功能。要在 UI 中访问此功能,Canvas 可以

public start(core) {
  const { capabilities } = core.application;

  const canUserSave = capabilities.canvas.save;
  if (canUserSave) {
    // show save button
  }
}

由于 read 权限未定义 save 功能,因此具有只读访问权限的用户将会将其 uiCapabilities.canvas.save 标志设置为 false

示例 2:开发工具编辑

public setup(core, { features }) {
  features.registerKibanaFeature({
    id: 'dev_tools',
    name: i18n.translate('xpack.features.devToolsFeatureName', {
      defaultMessage: 'Dev Tools',
    }),
    category: DEFAULT_APP_CATEGORIES.management,
    app: ['kibana'],
    catalogue: ['console', 'searchprofiler', 'grokdebugger'],
    privileges: {
      all: {
        api: ['console'],
        savedObject: {
          all: [],
          read: [],
        },
        ui: ['show'],
      },
      read: {
        api: ['console'],
        savedObject: {
          all: [],
          read: [],
        },
        ui: ['show'],
      },
    },
    privilegesTooltip: i18n.translate('xpack.features.devToolsPrivilegesTooltip', {
     defaultMessage:
       'User should also be granted the appropriate {es} cluster and index privileges',
   }),
  });
}

与 Canvas 示例不同,开发工具不需要访问任何已保存的对象即可运行。但是,开发工具确实指定了一个 API 端点。配置此项后,安全插件将自动授权访问任何标记为 access:console 的服务器 API 路由,类似于以下内容

server.route({
 path: '/api/console/proxy',
 method: 'POST',
 config: {
   tags: ['access:console'],
   handler: async (req, h) => {
     // ...
   }
 }
});

示例 3:Discover编辑

Discover 利用子功能权限来实现细粒度的访问控制。在此示例中,定义了两个子功能权限:“创建短链接”和“生成 PDF 报告”。这些权限允许用户授予对此功能的访问权限,而无需授予对 Discover 的 all 权限。换句话说,您可以授予对 Discover 的 read 访问权限,还可以授予创建短链接或生成 PDF 报告的权限。

请注意,“生成 PDF 报告”子功能权限还有一个附加的 minimumPrivilege 选项。仅当满足许可证要求时,Kibana 才会提供此子功能权限。

public setup(core, { features }) {
  features.registerKibanaFeature({
    {
      id: 'discover',
      name: i18n.translate('xpack.features.discoverFeatureName', {
        defaultMessage: 'Discover',
      }),
      order: 100,
      category: DEFAULT_APP_CATEGORIES.kibana,
      app: ['kibana'],
      catalogue: ['discover'],
      privileges: {
        all: {
          app: ['kibana'],
          catalogue: ['discover'],
          savedObject: {
            all: ['search', 'query'],
            read: ['index-pattern'],
          },
          ui: ['show', 'save', 'saveQuery'],
        },
        read: {
          app: ['kibana'],
          catalogue: ['discover'],
          savedObject: {
            all: [],
            read: ['index-pattern', 'search', 'query'],
          },
          ui: ['show'],
        },
      },
      subFeatures: [
        {
          name: i18n.translate('xpack.features.ossFeatures.discoverShortUrlSubFeatureName', {
            defaultMessage: 'Short URLs',
          }),
          privilegeGroups: [
            {
              groupType: 'independent',
              privileges: [
                {
                  id: 'url_create',
                  name: i18n.translate(
                    'xpack.features.ossFeatures.discoverCreateShortUrlPrivilegeName',
                    {
                      defaultMessage: 'Create Short URLs',
                    }
                  ),
                  includeIn: 'all',
                  savedObject: {
                    all: ['url'],
                    read: [],
                  },
                  ui: ['createShortUrl'],
                },
              ],
            },
            {
              groupType: 'independent',
              privileges: [
                {
                  id: 'pdf_generate',
                  name: i18n.translate(
                    'xpack.features.ossFeatures.discoverGeneratePDFReportsPrivilegeName',
                    {
                      defaultMessage: 'Generate PDF Reports',
                    }
                  ),
                  minimumLicense: 'platinum',
                  includeIn: 'all',
                  savedObject: {
                    all: [],
                    read: [],
                  },
                  api: ['generatePDFReports'],
                  ui: ['generatePDFReports'],
                },
              ],
            },
          ],
        },
      ],
    }
  });
}