安全
通常,Kibana 能够以对核心和插件开发者透明的方式实现安全性,并且在很大程度上仍然如此。Kibana 基于 Elasticsearch Cluster
提供的两种方法:callWithRequest
和 callWithInternalUser
。
callWithRequest
使用 Kibana 最终用户的身份验证凭据对 Elasticsearch 执行请求。因此,如果您使用 foo
用户登录 Kibana,当使用 callWithRequest
时,Kibana 将以 foo
用户身份对 Elasticsearch 执行请求。从历史上看,callWithRequest
已被广泛用于执行由 Kibana 最终用户发起的动作。
callWithInternalUser
使用 Kibana 内部服务器用户对 Elasticsearch 执行请求,并且从历史上看,它已被用于执行并非由 Kibana 最终用户发起的动作;例如,创建初始 .kibana
索引或对 Elasticsearch 执行运行状况检查。
但是,随着基于角色的访问控制 (RBAC) 引入的更改,情况不再如此简单。Kibana 现在要求所有对 .kibana
索引的访问都必须通过 SavedObjectsClient
。这曾经是一种最佳实践,因为 SavedObjectsClient
负责将存储在 Elasticsearch 中的文档与保存的对象相互转换,但 RBAC 现在正在利用此抽象来实现访问控制并确定何时使用 callWithRequest
与 callWithInternalUser
。
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
生命周期函数中调用 features
的 registerKibanaFeature
函数,并提供适当的详细信息
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 |
启用的子功能集比 all 和 read 功能权限提供更精细的访问控制。这些选项仅在 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-pattern
和canvas-workpad
保存的对象的只读访问权限。
此外,Canvas 还会注册 canvas
UI 应用和 canvas
目录条目。这告诉 Kibana,这些实体可供具有 read
或 all
权限的用户使用。
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'],
},
],
},
],
},
],
}
});
}