正在加载

安全

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

callWithRequest 使用 Kibana 最终用户的身份验证凭据对 Elasticsearch 执行请求。因此,如果您使用 foo 用户登录 Kibana,当使用 callWithRequest 时,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 首次启动时,它会对 Elasticsearch 执行以下 POST 请求。这会将权限的定义与各种 actions 同步,这些 actions 稍后用于授权用户

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.yml 中的 kibana.index 的值连接起来创建的,因此不同的 Kibana 租户彼此隔离。

Kibana 权限使用 applications 元素分配给特定角色。例如,以下角色将 all 权限在 * resources(将来用于保护空间)分配给默认的 Kibana application

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

授予 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 功能”。这些功能是布尔标志,您可以使用它们根据当前用户的权限有条件地呈现您的界面。例如,如果当前用户未获得授权,您可以隐藏或禁用“保存”按钮。

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

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

注册功能包括以下字段。有关更多信息,请参阅 feature registry interface

字段名称 数据类型 示例 描述
id(必需)
string
"sample_feature"
功能的唯一标识符。通常,插件的 ID 就足够了。
name(必需)
string
"示例功能"
功能的易于理解的名称。
category(必需)
AppCategory
DEFAULT_APP_CATEGORIES.kibana
最能代表您的功能的 AppCategory。用于组织管理屏幕中功能的显示。
app(必需)
string[]
["sample_app", "kibana"]
此功能启用的应用程序数组。通常,您的插件的所有应用程序(来自 uiExports)都将包含在此处。
privileges(必需)
KibanaFeatureConfig.
请参阅 示例 1示例 2
此功能要运行所需的权限集。
subFeatures(可选)
KibanaFeatureConfig.
请参阅 示例 3
启用的子功能集比 allread 功能权限提供更精细的访问控制。这些选项仅在 Gold 订阅级别或更高级别中可用。
scope(可选)
string[]
["spaces", "security"]
默认 security。Scope 标识功能是否应同时出现在 Spaces 可见性切换和安全功能权限中,还是仅出现在安全功能权限中。

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

有关字段和选项的完整说明,请参阅 feature registry interface

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
  }
}
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

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) => {
     // ...
   }
 }
});

Discover 利用子功能权限来实现细粒度的访问控制。在此示例中,定义了两个子功能权限:“创建短 URL”和“生成 PDF 报告”。这些功能允许用户授予对此功能的访问权限,而无需向 Discover 授予 all 权限。换句话说,您可以授予对 Discover 的 read 访问权限,并且还可以授予创建短 URL 或生成 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'],
                },
              ],
            },
          ],
        },
      ],
    }
  });
}
© . All rights reserved.