ZooKeeper安全认证机制:ZNode ACL
ZooKeeper的Client-Server互认证机制是从3.4.0版本开始引入的,本文主要介绍znodes的ACL的定义,任务服务接口定义与几种已有的认证服务实现,以及ACL与多种认证服务是如何建立联系的。本文内容基于ZooKeeper 3.5.1版本。
ACL
ZooKeeper的ACL可针对znodes
设置相应的权限信息。ACL数据的表示格式为:schema:id:permissions
-
schema 支持的几种schema为:
- world
只有一个名为
anyone
的Id
,world:anyone
代表任何人,也就是说,对应节点任何人可访问- auth
代表任何通过认证的用户,该schema不需要配置
Id
信息- digest
基于
username:password
生成的MD5 Hash值作为Id
信息,认证基于username:password
明文认证,但在acl中存储的是username:base64(password)
- ip
基于IP地址作为
Id
,支持IP地址或IP地址段 - id 代表用户
- permissions 权限定义为(READ, WRITE, CREATE, DELETE, ADMIN, ALL)
由ACL的定义信息,可以看出来,ZooKeeper可以针对不同的znodes
来提供不同的认证机制。
AuthenticationProvider
每一种认证服务均需要实现AuthenticationProvider
接口来支持一种新的schema,所有的AuthenticationProvider
实现类都被注册在ProviderRegistry
中。ZooKeeper中已经提供的AuthenticationProvider`的实现类:
每一个AuthenticationProvider
实现类所关联的schema
如下所示:
DigestAuthenticationProvider | digest |
IPAuthenticationProvider | ip |
SASLAuthenticationProvider | sasl |
X509AuthenticationProvider | x509 |
当znode acl schema为world
时,是不需要经任何AuthenticationProvider
进行认证的,因此不需要任何实现类。
当znode acl schema为auth
时,代表着需要对请求上下文中的认证信息进行校验,在ServerCnxn
的authInfo
中保存了所有的已认证成功的Id
以及认证服务所关联的的schema
,由该schema
再去ProviderRegistry
中查找所关联的AuthenticationProvider
实现类来对认证信息进行校验。
除了上述已有的实现者以外,用户还可以自定义实现AuthenticationProvider
。自定义的实现类,需要设置到System Properties中,对应的Property Key
需以"zookeeper.authProvider."
开头。另外,自定义的AuthenticationProvider
的schema
名称不应与现有的重名,否则会覆盖现有的实现。
Reference
ZooKeeper安全认证机制:用户名密码认证
ZooKeeper提供了简单的基于用户名和密码的认证机制,即DIGEST-MD5认证机制。本文首先介绍使用该认证机制所涉及的一些配置细节,接下来介绍ZooKeeper内部关于DIGEST-MD5认证机制的一些实现细节。
如何使用
Client
系统属性配置:
// "zookeeper.sasl.clientconfig"如果不设置,默认值为"Client" System.setProperty("zookeeper.sasl.clientconfig", "Client"); System.setProperty("zookeeper.sasl.client", "true");
自定义一个JaasConf对象,继承自javax.security.auth.login.Configuration,目的是为了便于Configuration所需参数的配置:
public class JaasConf extends Configuration { private Map<String, AppConfigurationEntry[]> sections = new HashMap<String, AppConfigurationEntry[]>(); public void addSection(String name, String loginModuleName, String... args) { Map<String, String> options = new HashMap<String, String>(); for (int i = 0; i < args.length; i += 2) { options.put(args[i], args[i + 1]); } AppConfigurationEntry[] entries = new AppConfigurationEntry[]{ new AppConfigurationEntry(loginModuleName, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)}; this.sections.put(name, entries); } @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { return this.sections.get(name); } }
实例化JaasConf,设置LoginModuleName以及对应的username/password等信息:
JaasConf conf = new JaasConf(); // Section Name: "Client", 这里的名称与系统属性"zookeeper.sasl.clientconfig"保持一致 // LoginModule Name: "org.apache.zookeeper.server.auth.DigestLoginModule" // Options: // "username": "nosql" // "password": "nosql123" conf.addSection("Client", "org.apache.zookeeper.server.auth.DigestLoginModule", "username", "nosql", "password", "nosql123"); Configuration.setConfiguration(conf);
Server
系统属性配置:
System.setProperty("zookeeper.sasl.serverconfig", "Server"); System.setProperty("zookeeper.authProvider.sasl", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
实例化JaasConf,并在Server端配置所有允许访问的username/password信息:
JaasConf conf = new JaasConf(); // LoginModuleName: "org.apache.zookeeper.server.auth.DigestLoginModule" // Options: // "user_nosql: nosql123" conf.addSection("Server", "org.apache.zookeeper.server.auth.DigestLoginModule", "user_nosql", "nosql123"); Configuration.setConfiguration(conf);
可以看到,Client端与Server端配置username/password的参数名称是不同的:
- Client 用户名通过静态参数”username“指定,密码通过静态参数”password“指定
- Server 用户名直接配置在一个以”user_“开头的动态参数名中,参数值直接为对应的password
Client通过这种模式只能配置一个username/password,而Server端的动态参数则允许配置多个Client的username/password。原因在于,Client只需要配置一个username/password即可,而Server端则允许配置多个Client的username/password。
实现原理
整体思路
- Server端在初始化ServerCnxnFactory时,加载预先配置的允许访问的一个或多个username/password列表,并执行Login操作
- Client基于配置的username/password以及DigestLoginModule,执行Login操作
- Client请求与Server端建立Sasl连接,建立连接过程中,通过com.sun.security.sasl.digest.FactoryImpl提供的认证机制,完成对username/password的合法校验
Client初始化
ZooKeeperSaslClient初始化时:
if (login == null) { if (LOG.isDebugEnabled()) { LOG.debug("JAAS loginContext is: " + loginContext); } // 初始化Login对象,Login对象是static类型的,也就说,该对象在进程级别内 // 是共享的. Login对象利用Java JAAS机制执行login操作,具体的Login机制由 // 配置的LoginContext来实现. login = new Login(loginContext, new ClientCallbackHandler(null)); login.startThreadIfNeeded(); } Subject subject = login.getSubject(); SaslClient saslClient; // ZooKeeper支持的认证主要是GSSAPI(Kerberos)以及DIGEST-MD5. 如果基于GSSAPI, // 认证成功后会在Subject中添加对应的Principal信息. 如果Subject中的Principal // 信息为空,则认为要使用DIGEST-MD5认证(注: 这种设计并不太好) if (subject.getPrincipals().isEmpty()) { // no principals: must not be GSSAPI: use DIGEST-MD5 mechanism instead. LOG.info("Client will use DIGEST-MD5 as SASL mechanism."); String[] mechs = {"DIGEST-MD5"}; // 从subject中获取username与password信息 String username = (String)(subject.getPublicCredentials().toArray()[0]); String password = (String)(subject.getPrivateCredentials().toArray()[0]); // 初始化SaslClient时,将username传入,password在ClientCallbackHandler中. // "zk-sasl-md5" is a hard-wired 'domain' parameter shared with // zookeeper server code (see ServerCnxnFactory.java) saslClient = Sasl.createSaslClient(mechs, username, "zookeeper", "zk-sasl-md5", null, new ClientCallbackHandler(password)); return saslClient; }
关于如上源码的更多备注信息:
- Login阶段,已经配置了LoginModule为
org.apache.zookeeper.server.auth.DigestLoginModule
-
DigestLoginModule
中在初始化时已经将Client配置的username和password信息加载到subject中:public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String,?> sharedState, Map<String,?> options) { if (options.containsKey("username")) { // Zookeeper client: get username and password from JAAS conf // (only used if using DIGEST-MD5). this.subject = subject; String username = (String)options.get("username"); this.subject.getPublicCredentials().add((Object)username); String password = (String)options.get("password"); this.subject.getPrivateCredentials().add((Object)password); } return; }
- Sasl.createSaslClient的流程:
String mechFilter = "SaslClientFactory." + mechName; Provider[] provs = Security.getProviders(mechFilter); for (int j = 0; provs != null && j < provs.length; j++) { className = provs[j].getProperty(mechFilter); if (className == null) { // Case is ignored continue; } fac = (SaslClientFactory) loadFactory(provs[j], className); if (fac != null) { mech = fac.createSaslClient( new String[]{mechanisms[i]}, authorizationId, protocol, serverName, props, cbh); if (mech != null) { return mech; } } }
“SaslClientFactory.DEGIEST-MD5″所关联的SaslClientFactory实现为:
com.sun.security.sasl.digest.FactoryImpl
所有的SaslClientFactory的实现信息都被注册在java.security.Security中。
Security与ProviderRegistry:
java.security.Security: Java Security框架中的定义,用来注册SaslClientFactory. 每一个SaslClientFactory都关联着一个Name.
org.apache.zookeeper.server.auth.ProviderRegistry: ZooKeeper中自定义的用来注册所有的AuthenticationProvider的类,每一个AuthenticationProvider关联一个schema
Server端初始化
ServerCnxnFactory#configureSaslLogin中的一些关键源码:
String serverSection = System.getProperty("zookeeper.sasl.serverconfig", "Server"); // Note that 'Configuration' here refers to javax.security.auth.login.Configuration. AppConfigurationEntry entries[] = null; SecurityException securityException = null; try { entries = Configuration.getConfiguration().getAppConfigurationEntry(serverSection); } catch (SecurityException e) { // handle below: might be harmless if the user doesn't intend to use JAAS authentication. securityException = e; } // ...中间略去一下非关键源码.... try { // 初始化SaslServerCallbackHandler saslServerCallbackHandler = new SaslServerCallbackHandler(Configuration.getConfiguration()); // 初始化Login对象,利用配置的LoginModule执行login操作. login = new Login(serverSection, saslServerCallbackHandler); login.startThreadIfNeeded(); } catch (LoginException e) { // .... }
SaslServerCallbackHandler
初始化过程中,加载配置的一个或多个username/password信息:
public SaslServerCallbackHandler(Configuration configuration) throws IOException { String serverSection = System.getProperty("zookeeper.sasl.serverconfig", "Server"); AppConfigurationEntry configurationEntries[] = configuration.getAppConfigurationEntry(serverSection); if (configurationEntries == null) { String errorMessage = "Could not find a 'Server' entry in" + " this configuration: Server cannot start."; LOG.error(errorMessage); throw new IOException(errorMessage); } credentials.clear(); for(AppConfigurationEntry entry: configurationEntries) { Map<String,?> options = entry.getOptions(); // 所有的用户名都被配置在以"user_"为前缀的属性名中 for(Map.Entry<String, ?> pair : options.entrySet()) { String key = pair.getKey(); if (key.startsWith(USER_PREFIX)) { String userName = key.substring(USER_PREFIX.length()); credentials.put(userName,(String)pair.getValue()); } } } }
总结
该机制虽然实现了基于用户名和密码的简单认证机制,但所有的用户名和密码信息都是静态配置的,无法支持用户的动态增加,这是该方案的最大软肋。
ZooKeeper安全认证机制:SSL
本文探讨ZooKeeper的SSL安全机制。默认情形下,ZooKeeper的网络通信是没有加密的,但ZooKeeper提供了SSL特性,目前仅应用在Client与Server端之间的交互(Server与Server之间的交互尚不支持),且RPC通信协议基于Netty时(ZooKeeper内置的NIO实现中不支持)。
SSL简介
SSL全称为Secure Socket Layer
,它是一种介于传输层和应用层的协议,它通过”握手协议”和“传输协议”来解决信息传输的安全问题,它可以被建立在任何可靠的传输层协议之上(例如TCP,但不能是UDP)。SSL协议主要提供如下三方面的能力:
- 信息的加密传播
- 校验机制,数据一旦被篡改,通信双方均会立刻发现
- 身份证书,防止身份被冒充
SSL的基本设计思想:
- Client向Server端索要”公钥“
- Client对获取的”公钥“进行校验
- 双方协商生成“会话密钥”
- 双方基于”会话密钥“进行信息交换
前3步称之为”握手阶段”,”握手阶段”采用”非对称加密“算法。
第4步称之为”传输阶段”,基于”对称加密“算法,”对称加密”算法的性能是远高于”非对称加密”算法的,因此,更适用于大数据量的传输加密。
如何使用
Client端配置
ZooKeeper Client通过配置如下系统属性来启用基于Netty的RPC通信层:
zookeeper.clientCnxnSocket=”org.apache.zookeeper.ClientCnxnSocketNetty”
Client需要设置如下参数来启用安全通信:
zookeeper.client.secure=true
设置了zookeeper.client.secure
属性为true
以后,意味着Client与Server之间只能通过"secureClientPort"
所指定的端口进行交互。
最后,需要配置KeyStore与TrustStore的相关系统属性:
zookeeper.ssl.keyStore.location=”/path/to/your/keystore”
zookeeper.ssl.keyStore.password=”keystore_password”
zookeeper.ssl.trustStore.location=”/path/to/your/truststore”
zookeeper.ssl.trustStore.password=”truststore_password”
Server端配置
ZooKeeper Server通过配置如下系统属性来启用Netty:
zookeeper.serverCnxnFactory=”org.apache.zookeeper.server.NettyServerCnxnFactory”
在”zoo.cfg”中配置”secureClientPort”端口值,该端口值与原来的”clientPort”端口值应该区别开:
secureClientPort=2281
最后也需要设置KeyStore与TrustStore的配置,与Client端配置类似。
配置示例
“bin/zkServer.sh”的配置示例如下:
export SERVER_JVMFLAGS=”
-Dzookeeper.serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
-Dzookeeper.ssl.keyStore.location=/root/zookeeper/ssl/testKeyStore.jks
-Dzookeeper.ssl.keyStore.password=testpass
-Dzookeeper.ssl.trustStore.location=/root/zookeeper/ssl/testTrustStore.jks
-Dzookeeper.ssl.trustStore.password=testpass”
在 “zoo.cfg”中增加:
secureClientPort=2281
“bin/zkCli.sh”的配置为:
export CLIENT_JVMFLAGS=”
-Dzookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty
-Dzookeeper.client.secure=true
-Dzookeeper.ssl.keyStore.location=/root/zookeeper/ssl/testKeyStore.jks
-Dzookeeper.ssl.keyStore.password=testpass
-Dzookeeper.ssl.trustStore.location=/root/zookeeper/ssl/testTrustStore.jks
-Dzookeeper.ssl.trustStore.password=testpass”
X509AuthenticationProvider
默认情况下,SSL认证是由X509AuthenticationProvider
提供的,对应的schema为x509
。X509AuthenticationProvider
基于javax.net.ssl.X509KeyManager
与javax.net.ssl.X509TrustManager
提供Host
的证书认证机制。X509AuthenticationProvider仅仅当zookeeper.serverCnxnFactory
配置为NettyServerCnxnFactory
时才可使用,ZooKeeper内置的NIO实现类NIOServerCnxnFactory
并不支持SSL。
关键的配置项如下所示:
zookeeper.ssl.keyStore.location | KeyStore的路径 |
zookeeper.ssl.trustStore.location | TrustStore的路径 |
zookeeper.ssl.keyStore.password | KeyStore的访问密码 |
zookeeper.ssl.trustStore.password | TrustStore的访问密码 |
在KeyStore JKS文件中保存了Server的证书以及私钥信息,该证书需要由Client端信任,因此,该证书或CA(证书认证机构信息)也会被存储在Client端的TrustStore JKS文件中。同时,Server端的TrustStore JFS文件中存储了所信任的Client的证书/CA信息。
Client认证成功之后,会创建一个ZooKeeper Session,Client可以设置ACLs的schema为”x509″. “x509″使用Client认证成功后的X500 Principal作为ACL ID。 ACL信息中包含Client认证后的确切的X500 Principal名称。
关于X509与 X500:
X509: 一套数字证书体系标准
X500: 定义了一种区别命名规则,以命名树来确保用户名称的唯一性
与digest
认证类似,Server端可以配置一个X509的superUser
,对应的Property Key为:
zookeeper.X509AuthenticationProvider.superUser
superUser
可以绕过ACL配置从而拥有所有znodes的所有权限。
定制X509AuthenticationProvider
除了默认的X509AuthenticationProvider
以外,ZooKeeper允许自定义扩展实现X509的安全信任机制,尤其是Certificate Key Infrastructures不使用JKS时。
自定义实现X509AuthenticationProvider
应该遵循:
- 继承自
X509AuthenticationProvider
- KeyManager需要继承自
javax.net.ssl.X509ExtendedKeyManager
- TrustManager需要继承自
javax.net.ssl.X509ExtendedTrustManager
- 覆写
X509AuthenticationProvider
的getKeyManager
与getTrustManager
方法
这样,自定义的实现才会在SSLEngine中发挥作用。
自定义的AuthenticationProvider需要配置一个对应的schema
名称,并且通过系统属性"zookeeper.authProvider.[schema_name]"
来配置新定义的AuthenticationProvider实现类,这样在ProviderRegistry初始化时会自动加载。接下来,还需要设置系统属性"zookeeper.ssl.authProvider=[schema_name]"
,这样,新定义的AuthenticationProvider才可以被应用在安全认证中。
实现细节
NettyServerCnxnFactory
构造函数中初始化ChannelPipeline时调用初始化SSL的方法:NettyServerCnxnFactory#initSSL
方法的实现如下:CnxnChannelHandler#channelConnected
方法的定义如下:
当SslHandler中的handshake Future中的监听者被触发以后,由CertificateVerifier
来对证书的合法性进行校验,而CertificateVerifier
对证书进行校验的操作是由X509AuthenticationProvider
或者自定义的扩展实现类来完成:
Reference
- Client-Server Mutual Authentication
- ZOOKEEPER-938
- ZOOKEEPER-2125
- ZooKeeper SSL User Guide
- SSL/TLS协议运行机制的概述
http://www.nosqlnotes.com/technotes/zookeeper-acl/
http://www.nosqlnotes.com/technotes/zookeeper-digest-md5/
http://www.nosqlnotes.com/technotes/zookeeper-ssl/
相关推荐
在 Dubbo+Zookeeper 的安全认证中,需要使用 Zookeeper 的 ACL 机制来控制用户对 Zookeeper 节点的访问权限。可以通过设置 Zookeeper 的用户名和密码来实现身份验证,然后使用 digest 方式来设置 ACL,从而控制用户...
然而,在 ZooKeeper 中存在一些安全漏洞,例如未经授权的访问、数据泄露等。为了解决这些问题,本文将介绍如何在 ZooKeeper 中增加权限登录验证,以限制访问 IP 地址。 一、背景知识 ZooKeeper 是一个基于 Java 的...
- **安全增强**:支持 SASL 认证和 ACL 策略,提升了系统的安全性。 - **监控与日志**:提供了更详细的监控指标和日志记录,便于诊断和调试。 - **API 更新**:优化了 Java 和 C API,增强了易用性和兼容性。 ###...
总的来说,整合Spring Boot和Zookeeper的权限控制,需要理解Zookeeper的认证机制,通过Spring的`CuratorFramework`来实现连接和操作,并在业务层封装相关功能。同时,要注意处理可能出现的异常情况,确保服务的稳定...
- **安全性**:启用Zookeeper的认证和授权机制,防止未经授权的访问。 总结,Apache ZooKeeper 3.4.6是一个强大且灵活的分布式协调服务,其简洁的API和稳定的性能使得它在分布式环境中得到了广泛应用。正确理解和...
8. **安全性**:3.5.x版本中,ZooKeeper增强了安全性,可以配置SASL、SSL等安全认证方式,保护集群免受未授权访问。 9. **API使用**:ZooKeeper提供了Java和C语言的API,允许应用程序进行数据操作、监听数据变化等...
6.2 SASL认证:ZooKeeper支持SASL(Simple Authentication and Security Layer),可实现客户端和服务端的身份验证。 七、监控与维护 7.1 监控工具:使用JMX(Java Management Extensions)监控ZooKeeper的运行...
ZooKeeper 未授权访问修复建议 ...通过增加认证用户、设置权限和查看 Acl 设置,可以确保 ZooKeeper 节点的安全访问。在实际应用中,需要根据具体情况配置访问权限,以确保 ZooKeeper 节点的安全性和可靠性。
6. **安全设置**:配置认证机制,确保对Zookeeper的操作安全可靠。 ZkVisualizer不仅提供了基本的浏览和编辑功能,还支持高级操作,例如批量操作、搜索功能以及自定义视图。这些特性使得Zookeeper的管理工作变得...
3. **安全性提升**:增强了权限控制和认证机制,提供更安全的服务环境。 4. **兼容性**:3.4.6版本保持了与早期版本的兼容性,方便了升级和维护。 三、Zookeeper搭建与使用 1. **安装**:首先,下载Zookeeper ...
在某些情况下,比如使用Dubbo框架与ZooKeeper结合时,可能会遇到认证问题,这时需要确保认证机制正确实现,以避免认证失败导致的访问控制问题。 如果上述认证方式都无法满足需求,最后可以采用IP限制方式,即通过IP...
1. **安全性**:Zookeeper支持SASL和ACL,可以实现用户认证和访问控制,保护数据的安全。 2. **监控**:通过JMX接口,Zookeeper提供了丰富的监控指标,可以监控服务器状态、会话、操作等信息。 3. **日志与快照**...
2. **安全性**:支持SASL和Kerberos认证,保证了数据访问的安全性。 总结,Apache Zookeeper 3.4.12版本以其强大的功能和稳定性在分布式领域扮演着重要角色。理解并掌握Zookeeper的工作原理和使用方法,对于构建高...
此外,它还参与系统的安全管理,例如与Kerberos和LdapServer配合实现安全认证(在安全模式下)。 总结来说,ZooKeeper在华为大数据认证中是至关重要的,它的分布式协调服务解决了复杂分布式环境中的一致性、可靠性...
4. 安全性提升:加强了权限控制,支持更安全的认证和授权机制。 总结,Zookeeper 3.4.5是分布式协调服务的重要工具,它的强大功能和稳定性使其在各种分布式系统中得以广泛应用。理解并掌握Zookeeper的工作原理和...
2. 安全增强:该版本加强了安全性,支持SSL加密通信,增强了认证和授权机制,以保护敏感数据的安全。 3. 配置管理:新增了动态配置更新功能,允许在运行时修改服务器配置,无需重启服务,提高了运维灵活性。 4. ...
为了保障数据的安全性和完整性,Kafka 提供了多种安全认证机制,其中 Kerberos 认证是一种非常重要的安全手段。本文将详细介绍如何为 Kafka 配置 Kerberos 安全认证,包括安装 Kerberos 服务、配置 Zookeeper 的 ...
ZooKeeper 支持 SASL 认证和 ACL(Access Control Lists)权限控制,可以对不同用户或角色设置不同的访问权限,确保数据的安全。 8. **ZooKeeper 集群** 在实际应用中,ZooKeeper 通常运行在多个服务器组成的集群...
1. 安全性:Zookeeper支持认证和授权,可以通过ACL(Access Control List)设置权限,保护数据安全。 2. 性能调优:包括调整数据存储路径、优化网络参数、合理设置session超时时间、监控系统资源等。 3. 监控与...
4. 安全性提升:增强了安全认证机制,支持SASL和SSL加密,为数据传输提供了安全保障。 三、Zookeeper服务部署 在实际应用中,Zookeeper通常以集群模式运行,确保服务的高可用性。部署Zookeeper 3.4.6时,需要注意...