安全综述

本文档概述了Apache Druid的安全特性、配置说明以及一些保护Druid的最佳实践。

默认情况下,Druid中的安全特性被禁用,这简化了初始部署体验。但是,必须在生产部署中配置安全功能。这些功能包括TLS、身份验证和授权。

最佳实践

以下建议适用于Druid集群安装部署:

  • 以一个非特权Unix账号来运行Druid,不使用root用户来运行Druid

[!WARNING] Druid管理员拥有与运行Druid的Unix用户相同的操作系统权限。 详情请参阅 认证与授权模型。如果Druid进程在OS的root用户下运行,那么Druid管理员可以读取或写入root帐户有权访问的所有文件,包括敏感文件,如/etc/passw.

  • 为生产环境和其他可由不受信任的网络访问的环境启用对Druid集群的身份验证。
  • 启用授权,在未启用授权的情况下不要公开Druid控制台。如果未启用授权,则任何有权访问web控制台的用户都具有与运行Druid控制台进程的操作系统用户相同的权限。
  • 授予用户执行其功能所需的最低权限。例如,不允许只需要查询数据的用户写入数据源或查看状态。
  • 禁用JavaScript,在JavaScript指导中提到的

以下建议适用于运行Druid的网络:

  • 在集群内启用TLS来加密通信
  • 使用一个API网关来:
    • 限制来自不受信任网络的访问
    • 创建用户需要访问的特定API的允许列表
    • 实施帐户锁定和限制功能
  • 在可能的情况下,使用防火墙和其他网络层过滤仅公开您的场景特别需要的Druid服务和端口。例如,仅向执行查询的下游应用程序公开Broker端口。您可以限制对特定IP地址或IP范围的访问,以进一步加强安全性。

以下建议适用于Druid授权和身份验证模型:

  • 仅向受信任的用户授予对任何DATASOURCEWRITE权限。Druid的信任模型假设这些用户拥有与运行Druid控制台进程的操作系统用户相同的权限
  • 仅向高度信任的用户授予STATE READ, STATE WRITE, CONFIG WRITE, 和 DATASOURCE WRITE权限。这些权限允许用户代表Druid服务器进程访问资源,而不考虑数据源
  • 如果您的Druid客户端应用程序允许不太受信任的用户控制摄取任务的输入源或firehose,请验证来自用户的URL。可以将未经检查的URL指向网络或本地文件系统中的其他位置和资源

启用TLS

启用TLS会加密外部客户端和Druid集群之间的通信流量以及集群内服务之间的通信流量。

生成keys

在Druid启用TLS之前,需要生成KeyStore和TrustStore。 当一个Druid进程(例如Broker)与另一个进程(例如Historical)进行通信时, 第一个服务进程称为客户端,而第二个服务进程称为服务端

客户端使用包含客户端信任的证书的TrustStore,例如,Broker

服务器使用包含私钥和用于安全标识自身的证书链的KeyStore

以下示例演示如何使用Java keytool为服务生成KeyStore,然后创建TrustStore以信任客户端的密钥:

  1. 使用Java的keytool命令生成KeyStore
$> keytool -keystore keystore.jks -alias druid -genkey -keyalg RSA
  1. 导出一个公有证书
$> keytool -export -alias druid -keystore keystore.jks -rfc -file public.cert
  1. 创建一个TrustStore
$> keytool -import -file public.cert -alias druid -keystore truststore.jks

Druid使用Jetty当做内嵌的Web Server,详情可以参见Jetty文档的 配置SSL/TLS KeyStore部分

[!WARNING] 不要在生产环境中使用自签名证书。相反,依赖您当前的公钥基础结构来生成和分发可信密钥。

更新Druid TLS配置

编辑所有节点上用于所有Druid服务的 common.runtime.properties, 增加或者更新下列TLS相关的配置,然后重启集群

# Turn on TLS globally
druid.enableTlsPort=true

# Disable non-TLS communicatoins
druid.enablePlaintextPort=false

# For Druid processes acting as a client
# Load simple-client-sslcontext to enable client side TLS
# Add the following to extension load list
druid.extensions.loadList=[......., "simple-client-sslcontext"]

# Setup client side TLS
druid.client.https.protocol=TLSv1.2
druid.client.https.trustStoreType=jks
druid.client.https.trustStorePath=truststore.jks # replace with correct turstStore file
druid.client.https.trustStorePassword=secret123  # replace with your own password

# Setup server side TLS
druid.server.https.keyStoreType=jks
druid.server.https.keyStorePath=my-keystore.jks # replace with correct keyStore file
druid.server.https.keyStorePassword=secret123 # replace with your own password
druid.server.https.certAlias=druid

更多的信息可以查看 TLS支持Simple SSLContext

认证与授权

可以配置身份认证与授权来控制对Druid API的访问, 然后根据以下各部分所述,配置用户、角色和权限, 这些配置需要在集群中所有Druid服务中的 common.runtime.properties 文件中进行配置。

在Druid的操作环境中,Authenticators控制认证用户身份的方式, Authorizers使用用户角色将经过认证的用户与允许其访问的数据源关联起来。 可以基于每个数据源设置最细粒度的权限。

下图描述了通过身份验证过程的请求过程:

启用一个Authenticators

在Druid中认证请求需要配置一个Authenticators。 Authenticators扩展当前支持HTTP Basic认证, LDAP和Kerberos

下边为一个启动Basic认证的配置示例步骤:

  1. common.runtime.properties 配置文件中将 druid-basic-security 扩展加入到 druid.extensions.loadList 配置项中。 对于快速安装, 该属性文件位于 conf/druid/cluster/_common :
druid.extensions.loadList=["druid-basic-security", "druid-histogram", "druid-datasketches", "druid-kafka-indexing-service"]
  1. common.runtime.properties 文件中配置Basic Authenticator,Authorizer和Escalator。 Escalator定义Druid进程间如何进行相互认证。

一个示例配置如下:

# Druid basic security
druid.auth.authenticatorChain=["MyBasicMetadataAuthenticator"]
druid.auth.authenticator.MyBasicMetadataAuthenticator.type=basic

# Default password for 'admin' user, should be changed for production.
druid.auth.authenticator.MyBasicMetadataAuthenticator.initialAdminPassword=password1

# Default password for internal 'druid_system' user, should be changed for production.
druid.auth.authenticator.MyBasicMetadataAuthenticator.initialInternalClientPassword=password2

# Uses the metadata store for storing users, you can use authentication API to create new users and grant permissions
druid.auth.authenticator.MyBasicMetadataAuthenticator.credentialsValidator.type=metadata

# If true and the request credential doesn't exists in this credentials store, the request will proceed to next Authenticator in the chain.
druid.auth.authenticator.MyBasicMetadataAuthenticator.skipOnFailure=false

druid.auth.authenticator.MyBasicMetadataAuthenticator.authorizerName=MyBasicMetadataAuthorizer

# Escalator
druid.escalator.type=basic
druid.escalator.internalClientUsername=druid_system
druid.escalator.internalClientPassword=password2
druid.escalator.authorizerName=MyBasicMetadataAuthorizer

druid.auth.authorizers=["MyBasicMetadataAuthorizer"]

druid.auth.authorizer.MyBasicMetadataAuthorizer.type=basic
  1. 重启集群

关于Authenticator,Escalator和Authorizer 概念的详细信息可以查看 认证与授权部分。上边示例中使用的扩展详情可以查看 Basic认证, Kerberos认证

启用Authorizers

启用Basic认证扩展后,可以通过Coordinator的user接口来添加用户、角色和权限。 请注意:不能够直接分配权限到独立的个人,权限必须通过角色来分配。

下图描述了授权模型以及用户、角色、权限和资源之间的关系:

以下的步骤完成了一个简单的过程:

[!WARNING] 默认的非TLS连接的CoordinatorAPI端口是8081, 安全连接的端口是8082

  1. 通过对 druid-ext/basic-security/authentication/db/MyBasicMetadataAuthenticator/users/<USERNAME> 发送一个POST请求来添加一个用户, 将其中的USERNAME替换为要创建的新的用户名。例如:
 curl -u admin:password1 -XPOST https://my-coordinator-ip:8281/druid-ext/basic-security/authentication/db/basic/users/myname

[!WARNING] 如果启用了TLS,确保相应的调整curl命令。 例如,如果Druid服务使用的是自签名的证书,需要选择insecure的curl选项来放弃curl命令的证书检查

  1. 通过对 druid-ext/basic-security/authentication/db/MyBasicMetadataAuthenticator/users/<USERNAME>/credentials 发送一个POST请求来添加一个用户凭据,例如:
curl -u admin:password1 -H'Content-Type: application/json' -XPOST --data-binary @pass.json https://my-coordinator-ip:8281/druid-ext/basic-security/authentication/db/basic/users/myname/credentials

密码以下列格式写入在pass.json文件中:

{
  "password": "myname_password"
}
  1. 通过对 druid-ext/basic-security/authorization/db/MyBasicMetadataAuthorizer/users/<USERNAME> 发送一个POST请求,为每一个已经创建的认证用户创建一个相应的授权用户,例如:
curl -u admin:password1 -XPOST https://my-coordinator-ip:8281/druid-ext/basic-security/authorization/db/basic/users/myname
  1. 通过对 druid-ext/basic-security/authorization/db/MyBasicMetadataAuthorizer/roles/<ROLENAME> 发送一个POST请求创建一个用来控制权限的授权角色,比如:
curl -u admin:password1 -XPOST https://my-coordinator-ip:8281/druid-ext/basic-security/authorization/db/basic/roles/myrole
  1. 通过对 druid-ext/basic-security/authorization/db/MyBasicMetadataAuthorizer/users/<USERNAME>/roles/<ROLENAME> 发送一个POST请求将角色分配给用户,例如:
curl -u admin:password1 -XPOST https://my-coordinator-ip:8281/druid-ext/basic-security/authorization/db/basic/users/myname/roles/myrole | jq
  1. 通过对 druid-ext/basic-security/authorization/db/MyBasicMetadataAuthorizer/roles/<ROLENAME>/permissions 发送一个POST请求将权限附加到角色,以控制角色如何与Druid交互,例如:
curl -u admin:password1 -H'Content-Type: application/json' -XPOST --data-binary @perms.json https://my-coordinator-ip:8281/druid-ext/basic-security/authorization/db/basic/roles/myrole/permissions

perms.json的格式如下:

[
{
  "resource": {
    "name": "<PATTERN>",
    "type": "DATASOURCE"
  },
  "action": "READ"
},
{
  "resource": {
  "name": "STATE",
  "type": "STATE"
},
"action": "READ"
}
]

[!WARNING] 注意:Druid将资源名称视为正则表达式(regex)。您可以使用特定的数据源名称或正则表达式一次授予多个数据源的权限。

配置一个LDAP Authenticator

作为一个可以用来替代Basic认证的方式,可以使用LDAP来认证用户。 以下步骤提供了一个配置流程。 对于这些设置的更多信息,可以查看 LDAP用户认证属性

  1. common.runtime.properties 配置文件中,按照您希望评估请求的顺序将LDAP添加到验证器链中。例如:
# Druid basic security
druid.auth.authenticatorChain=["ldap", "MyBasicMetadataAuthenticator"]
  1. common.runtime.properties 配置文件中添加LDAP配置参数,例如:
druid.auth.authenticator.ldap.type=basic
druid.auth.authenticator.ldap.enableCacheNotifications=true
druid.auth.authenticator.ldap.credentialsValidator.type=ldap
druid.auth.authenticator.ldap.credentialsValidator.url=ldap://ad_host:389
druid.auth.authenticator.ldap.credentialsValidator.bindUser=ad_admin_user
druid.auth.authenticator.ldap.credentialsValidator.bindPassword=ad_admin_password
druid.auth.authenticator.ldap.credentialsValidator.baseDn=dc=example,dc=com 
druid.auth.authenticator.ldap.credentialsValidator.userSearch=(&(sAMAccountName=%s)(objectClass=user))
druid.auth.authenticator.ldap.credentialsValidator.userAttribute=sAMAccountName
druid.auth.authenticator.ldap.authorizerName=ldapauth
druid.escalator.type=basic
druid.escalator.internalClientUsername=ad_interal_user
druid.escalator.internalClientPassword=Welcome123
druid.escalator.authorizerName=ldapauth
druid.auth.authorizers=["ldapauth"]
druid.auth.authorizer.ldapauth.type=basic
druid.auth.authorizer.ldapauth.initialAdminUser=<ad_initial_admin_user>
druid.auth.authorizer.ldapauth.initialAdminRole=admin
druid.auth.authorizer.ldapauth.roleProvider.type=ldap
  1. 使用Druid API创建一个组映射以及分配初始角色。例如,使用curl以及给定一个group1的组名,运行:
curl -i -v  -H "Content-Type: application/json" -u internal -X POST -d @groupmap.json http://localhost:8081/druid-ext/basic-security/authorization/db/ldapauth/groupMappings/group1map

groupmap.json 文件内容将如下:

{
  "name": "group1map",
  "groupPattern": "CN=group1,CN=Users,DC=example,DC=com",
  "roles": [
      "readRole"
  ]
}
  1. 通过执行以下API可以检查组映射是否是否创建成功,该接口可以列出所有的组映射
curl -i -v  -H "Content-Type: application/json" -u internal -X GET http://localhost:8081/druid-ext/basic-security/authorization/db/ldapauth/groupMappings

另外, 可以通过以下API来获取一个特定组映射的详情:

curl -i -v  -H "Content-Type: application/json" -u internal -X GET http://localhost:8081/druid-ext/basic-security/authorization/db/ldapauth/groupMappings/group1map
  1. 通过以下的API可以将新的角色添加到组映射中:
curl -i -v  -H "Content-Type: application/json" -u internal -X POST http://localhost:8081/druid-ext/basic-security/authorization/db/ldapauth/groupMappings/group1/roles/<newrole>
  1. 通过以下的认证API,增加一个LDAP用户到Druid
curl -i -v  -H "Content-Type: application/json" -u internal -X POST http://localhost:8081/druid-ext/basic-security/authentication/db/ldap/users/<ad_user>
  1. 使用以下命令将分配一个角色给用户
curl -i -v  -H "Content-Type: application/json" -u internal -X POST http://localhost:8081/druid-ext/basic-security/authorization/db/ldapauth/users/<ad_user>/roles/<rolename>

恭喜你,你已经为Druid中用户分配的角色配置了权限!

Druid安全信任模型

在Druid的信任模型中,用户有以下集中不同的授权级别:

  • 具有资源写权限的用户可以随意操作,与Druid进程服务类似
  • 只有经过认证的读权限的用户,可以执行针对有权限资源的查询
  • 经过认证但没有任何权限的用户,可以对不需要准入控制的资源进行查询

另外,Druid根据以下规则进行操作:

从最内层开始:

  1. Druid进程对运行进程的指定系统用户授予的本地文件具有相同的访问权限。
  2. Druid摄入系统可以创建新的进程来执行任务。这些任务继承其父进程的用户。这意味着任何授权提交摄取任务的用户都可以使用摄取任务权限来读取或写入Druid进程有权访问的任何本地文件或外部资源。

[!WARNING] 注意: 仅将 DATASOURCE WRITE 权限授予给受信任的用户, 因为他们与Druid进程的角色一样

集群内部:

  1. Druid假设它是运行在一个孤立的、受保护的网络上运行,在这个网络中没有可访问的ip在对手的控制下。 当使用Druid时,请注意通过防火墙和其他安全措施来保护入站和出站连接。 Druid假设集群内的网络流量是加密的,包括API调用和数据传输,默认加密实现使用TLS
  2. Druid假设辅助服务(如元数据存储和ZooKeeper节点)不受对手控制。

集群到深度存储:

  1. Druid不会对深度存储的安全性做出假设,它遵循原生的安全策略来对深度存储进行认证与授权
  2. Druid不加密深度存储的文件。相反,它依赖存储系统自身的加密能力来保证与所有存储类型的加密方案兼容

集群到客户端:

  1. Druid基于配置的Authenticator对客户端进行认证
  2. Druid只在Authorizer授予权限时执行动作,默认配置是allowAll authorizer

渝ICP备16001958号 | Copyright © 2020 apache-druid.cn All right reserved,powered by Gitbook最近一次修改时间: 2021-11-19 16:46:05

results matching ""

    No results matching ""