ClickHouse上一般使用XML文件来定义配置,其中包括用户配置。users.xml
文件或在/etc/clickhouse-server/users.d
目录中的配置文件都可用于定义用户及其相关属性,例如profile
、network restriction
、quota
和password
。这种方案在集群规模较小时比较有效,但是用来管理大规模集群却很费劲。
主要存在三个主要问题:首先每次必须添加、修改或删除用户时,管理员必须手动编辑这些配置文件;其次,将密码和哈希值直接存储在用户配置文件中存在安全隐患;第三,为用户分配和管理角色是非常繁琐的。
ClickHouse现在已经解决了以上三个问题。通过引入对RBAC的支持,ClickHouse允许使用SQL语句管理用户和角色,而无需修改配置文件。但是即使使用RBAC
,您仍然需要与组织内的其他服务分别管理用户和角色,相互之间无法打通,且密码仍然存储在文件系统中。
自2020年中以来,Altinity一直在努力添加ClickHouse对轻型目录访问协议(也称为LDAP)的支持。LDAP集中存储用户、密码及其在组织中角色,从而使得管理用户和权限的功能集中到一个地方。我们很高兴地宣布,ClickHouse支持LDAP身份验证和用户目录的特性已被合入社区,目前运行稳定且通用。
我们已使用大多数Linux发行版中提供的常用OpenLDAP软件包对其进行了仔细的测试。
在这一系列博客文章中,我们将描述对LDAP集成的新支持如何帮助中大型组织将ClickHouse用户角色管理与其现有服务集成在一起。我们将介绍LDAP与Clickhouse的几种不同程度的集成,从使用LDAP验证ClickHouse用户,到通过外部用户目录完全集成LDAP并将选定的LDAP组映射到RBAC中的角色。
测试环境
我们使用docker-compose集群来展示LDAP如何与ClickHouse集成。
您可以在https://gitlab.com/altinity-public/blogs/ldap-integration-with-clickhouse上找到docker-compose环境,该环境中使用了yandex/clickhouse-server:21.1.2.15镜像
要使用该环境,需要在系统上安装git
,docker
和docker-compose
。然后将包含测试环境的代码库克隆到本地。
git clone https://gitlab.com/altinity-public/blogs/ldap-integration-with-clickhouse.git
检查是否可以启动docker-compose集群。
cd ldap-integration-with-clickhouse cd docker-compose/ docker-compose up -d
Creating network "docker-compose_default" with the default driver Creating docker-compose_openldap1_1 ... done Creating docker-compose_zookeeper_1 ... done Creating docker-compose_clickhouse1_1 ... done Creating docker-compose_clickhouse3_1 ... done Creating docker-compose_clickhouse2_1 ... done Creating phpldapadmin ... done Creating docker-compose_all_services_ready_1 ... done
如果启动成功会看到上面的日志,表明openldap1
,zookeeper
,clickhouse1
,clickhouse2
,clickhouse3
和phpldapadmin
这些服务已经正常运行。我们还可检查所有服务是否健康。
请注意,您必须执行
docker-compose
目录下的所有docker-compose命令
docker-compose ps
Name Command State Ports ------------------------------------------------------------------------------------------------------------------- docker-compose_all_services_ready_1 /hello Exit 0 docker-compose_clickhouse1_1 bash -c clickhouse server ... Up (healthy) 8123/tcp, 9000/tcp, 9009/tcp docker-compose_clickhouse2_1 bash -c clickhouse server ... Up (healthy) 8123/tcp, 9000/tcp, 9009/tcp docker-compose_clickhouse3_1 bash -c clickhouse server ... Up (healthy) 8123/tcp, 9000/tcp, 9009/tcp docker-compose_openldap1_1 /container/tool/run --copy ... Up (healthy) 389/tcp, 636/tcp docker-compose_zookeeper_1 /docker-entrypoint.sh zkSe ... Up (healthy) 2181/tcp, 2888/tcp, 3888/tcp phpldapadmin /container/tool/run Up (healthy) 443/tcp, 0.0.0.0:8080->80/tcp
健全性检查
在继续之前我们做一些必要的健全性检查。首先,确保我们的OpenLDAP Server已启动并正在运行。我们可以执行搜索操作以查看在LDAP Server上定义好的用户
再次注意,您必须执行
docker-compose
目录下的所有docker-compose命令。
docker-compose exec openldap1 bash -c 'ldapsearch -x -H ldap://localhost -b "ou=users,dc=company,dc=com" -D "cn=admin,dc=company,dc=com" -w admin'
# extended LDIF # # LDAPv3 # base <ou=users,dc=company,dc=com> with scope subtree # filter: (objectclass=*) # requesting: ALL # # users, company.com dn: ou=users,dc=company,dc=com objectClass: organizationalUnit objectClass: top ou: users # ldapuser, users, company.com dn: cn=ldapuser,ou=users,dc=company,dc=com cn: ldapuser gidNumber: 501 givenName: John homeDirectory: /home/users/ldapuser objectClass: inetOrgPerson objectClass: posixAccount objectClass: top sn: User uid: ldapuser uidNumber: 1002 userPassword:: bGRhcHVzZXI= # search result search: 2 result: 0 Success # numResponses: 3 # numEntries: 2
可以看到,我们已经定义了一个用户:cn=ldapuser,ou=users,dc=company,dc=com
。ldapsearch
返回的数据中,ldapuser
为用户名,密码设置为与用户名相同(仅用于测试)。
其次,确保ClickHouse服务能够正常执行查询。
docker-compose exec clickhouse1 bash -c 'clickhouse-client -q "SELECT version()"' docker-compose exec clickhouse2 bash -c 'clickhouse-client -q "SELECT version()"' docker-compose exec clickhouse3 bash -c 'clickhouse-client -q "SELECT version()"'
在ClickHouse中配置LDAP服务器
在将LDAP与ClickHouse进行集成之前,需要让ClickHouse知道哪里有可用的LDAP服务器以及如何连接它,Clickhouse甚至还可以添加多个LDAP Server的配置。为简单起见,我们将仅使用在docker-compose集群中的openldap1
环境。接下来将配置文件添加到/etc/clickhouse-server/config.d
目录中,以配置LDAP Server。
docker-compose exec clickhouse1 bash -c 'cat <<HEREDOC > /etc/clickhouse-server/config.d/ldap_servers.xml <?xml version="1.0" encoding="utf-8"?> <yandex> <ldap_servers> <!--LDAP servers b9f1f80c_6598_11eb_80c1_39d7fbdc1e26--> <openldap1> <host>openldap1</host> <port>636</port> <enable_tls>yes</enable_tls> <auth_dn_prefix>cn=</auth_dn_prefix> <auth_dn_suffix>,ou=users,dc=company,dc=com</auth_dn_suffix> <tls_require_cert>never</tls_require_cert> </openldap1> </ldap_servers> </yandex> HEREDOC'
接着检查ldap_servers.xml
中的配置是否已合并到预处理的config.xml
文件中。
docker-compose exec clickhouse1 bash -c 'cat /var/lib/clickhouse/preprocessed_configs/config.xml | grep LDAP'
<!--LDAP servers b9f1f80c_6598_11eb_80c1_39d7fbdc1e26-->
可以看到唯一标识字符串已经在预处理的config.xml
文件中,说明LDAP Server配置已被Clickhouse成功加载。
使用LDAP验证ClickHouse用户
ClickHouse与LDAP的第一级集成是允许使用LDAP Server对ClickHouse用户进行身份验证。这样我们就不必在Clickhouse中为用户明确指定密码,而是让ClickHouse请求LDAP Server进行用户身份验证。
在users.xml中定义经过LDAP Server验证的用户
我们可将对应的配置文件添加到/etc/clickhouse-server/users.d
目录中以新建Clickhouse用户,而不需要在配置中为其指定密码,转而用LDAP Server配置代替之。
docker-compose exec clickhouse1 bash -c 'cat <<HEREDOC > /etc/clickhouse-server/users.d/ldapuser.xml <?xml version="1.0" encoding="utf-8"?> <yandex> <users> <!--LDAP users bb6f3d71_6598_11eb_80c1_39d7fbdc1e26--> <ldapuser> <ldap> <server>openldap1</server> </ldap> </ldapuser> </users> </yandex> HEREDOC'
确认新增用户配置已合并到预处理的users.xml
中。
$ docker-compose exec clickhouse1 bash -c 'cat /var/lib/clickhouse/preprocessed_configs/users.xml | grep bb6f3d71_6598_11eb_80c1_39d7fbdc1e26'
<!--LDAP users bb6f3d71_6598_11eb_80c1_39d7fbdc1e26-->
现在我们使用ldapuser
用户执行查询。
docker-compose exec clickhouse1 bash -c 'clickhouse-client -n --user "ldapuser" --password "ldapuser" -q "SELECT user()"'
ldapuser
有效了!现在我们试试用错误密码登录会不会验证失败?
docker-compose exec clickhouse1 bash -c 'clickhouse-client -n --user "ldapuser" --password "ldapuser2" -q "SELECT user()"'
Code: 516. DB::Exception: Received from localhost:9000. DB::Exception: ldapuser: Authentication failed: password is incorrect or there is no user with such name.
删除用户的配置文件,接下来我们改用RBAC定义Clickhouse用户
docker-compose exec clickhouse1 bash -c 'rm -rf /etc/clickhouse-server/users.d/ldapuser.xml'
使用RBAC定义经过LDAP Server验证的用户
在Clickhouse中,除XML配置文件之外,我们还可使用RBAC命令以更简单的方式定义用户。我们建议使用RBAC命令,因为您可使用ON CLUSTER
语句在整个Clickhouse集群上创建或删除用户。
CREATE USER命令的语法定义如下。
CREATE USER [IF NOT EXISTS | OR REPLACE] name1 [ON CLUSTER cluster_name1] [, name2 [ON CLUSTER cluster_name2] ...] [IDENTIFIED [WITH {NO_PASSWORD|PLAINTEXT_PASSWORD|SHA256_PASSWORD|SHA256_HASH|DOUBLE_SHA1_PASSWORD|DOUBLE_SHA1_HASH}] BY {'password'|'hash'}] [HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE] [DEFAULT ROLE role [,...]] [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
现在您还可以使用IDENTIFIED WITH LDAP_SERVER
语句,绑定LDAP Server对该用户进行身份验证。
docker-compose exec clickhouse1 bash -c 'clickhouse-client -q "CREATE USER ldapuser IDENTIFIED WITH LDAP_SERVER BY '\''openldap1'\''"'
检查用户是否创建成功
docker-compose exec clickhouse1 bash -c 'clickhouse-client -q "SHOW USERS"'
default ldapuser
使用ldapuser
用户执行查询。
docker-compose exec clickhouse1 bash -c 'clickhouse-client -n --user "ldapuser" --password "ldapuser" -q "SELECT user()"'
ldapuser
使用错误密码登录看看会不会报错
docker-compose exec clickhouse1 bash -c 'clickhouse-client -n --user "ldapuser" --password "ldapuser2" -q "SELECT user()"'
Code: 516. DB::Exception: Received from localhost:9000. DB::Exception: ldapuser: Authentication failed: password is incorrect or there is no user with such name.
在集群中使用RBAC定义经过LDAP Server验证的用户
RBAC命令允许在群集上创建或删除用户。现在我们将LDAP Server配置添加到其他两个ClickHouse节点:clickhouse2
和clickhouse3
中
首先,将LDAP Server配置添加到clickhouse2
中
docker-compose exec clickhouse2 bash -c 'cat <<HEREDOC > /etc/clickhouse-server/config.d/ldap_servers.xml <?xml version="1.0" encoding="utf-8"?> <yandex> <ldap_servers> <!--LDAP servers b9f1f80c_6598_11eb_80c1_39d7fbdc1e26--> <openldap1> <host>openldap1</host> <port>636</port> <enable_tls>yes</enable_tls> <auth_dn_prefix>cn=</auth_dn_prefix> <auth_dn_suffix>,ou=users,dc=company,dc=com</auth_dn_suffix> <tls_require_cert>never</tls_require_cert> </openldap1> </ldap_servers> </yandex> HEREDOC'
对clickhouse3
也进行同样的操作
docker-compose exec clickhouse3 bash -c 'cat <<HEREDOC > /etc/clickhouse-server/config.d/ldap_servers.xml <?xml version="1.0" encoding="utf-8"?> <yandex> <ldap_servers> <!--LDAP servers b9f1f80c_6598_11eb_80c1_39d7fbdc1e26--> <openldap1> <host>openldap1</host> <port>636</port> <enable_tls>yes</enable_tls> <auth_dn_prefix>cn=</auth_dn_prefix> <auth_dn_suffix>,ou=users,dc=company,dc=com</auth_dn_suffix> <tls_require_cert>never</tls_require_cert> </openldap1> </ldap_servers> </yandex> HEREDOC'
现在,我们将ON CLUSTER
子句添加到CREATE USER
命令中,在集群上创建ldapuser
用户
docker-compose exec clickhouse1 bash -c 'clickhouse-client -q "CREATE USER IF NOT EXISTS ldapuser IDENTIFIED WITH LDAP_SERVER BY '\''openldap1'\'' ON CLUSTER '\''replicated_cluster'\''"'
clickhouse2 9000 0 2 0 clickhouse1 9000 0 1 0 clickhouse3 9000 0 0 0
现在,我们以ldapuser
用户登录到集群的每个节点上执行命令。
docker-compose exec clickhouse1 bash -c 'clickhouse-client -n --user "ldapuser" --password "ldapuser" -q "SELECT user()"' docker-compose exec clickhouse2 bash -c 'clickhouse-client -n --user "ldapuser" --password "ldapuser" -q "SELECT user()"' docker-compose exec clickhouse3 bash -c 'clickhouse-client -n --user "ldapuser" --password "ldapuser" -q "SELECT user()"'
优化LDAP身份验证用户的登录
在使用LDAP Server对用户进行身份验证后,每次用户登录Clickhouse时,ClickHouse都必须请求LDAP Server来对该用户进行验证。当许多用户都需要身份验证时,这可能不是最佳选择。
为了解决此问题,我们可在Clickhouse的LDAP Server配置中使用<verification_cooldown>
参数,该参数指定一次成功登录之后的时间(单位:秒):在此期间,对于连续请求中的用户,我们将假定其已被成功验证, 这样就无需每次都请求LDAP Server。
默认情况下,此参数为0,表示禁用缓存,即每次用户登录时Clickhouse都会请求LDAP Server进行身份验证。
当前缓存已被禁用。我们在clickhouse1
上进行基准测试:
docker-compose exec clickhouse1 bash -c 'time for n in {1..1000}; do clickhouse-client -n --user "ldapuser" --password "ldapuser" -q "SELECT 1" > /dev/null; done'
real 0m30.189s user 0m14.492s sys 0m9.847s
接着我们在clickhouse1
上更改<verification_cooldown>
参数,改成5分钟
docker-compose exec clickhouse1 bash -c 'cat <<HEREDOC > /etc/clickhouse-server/config.d/ldap_servers.xml <?xml version="1.0" encoding="utf-8"?> <yandex> <ldap_servers> <!--LDAP servers b9f1f80c_6598_11eb_80c1_39d7fbdc1e26--> <openldap1> <host>openldap1</host> <port>636</port> <enable_tls>yes</enable_tls> <verification_cooldown>300</verification_cooldown> <auth_dn_prefix>cn=</auth_dn_prefix> <auth_dn_suffix>,ou=users,dc=company,dc=com</auth_dn_suffix> <tls_require_cert>never</tls_require_cert> </openldap1> </ldap_servers> </yandex> HEREDOC'
启用缓存之后,基准测试结果如下:
docker-compose exec clickhouse1 bash -c 'time for n in {1..1000}; do clickhouse-client -n --user "ldapuser" --password "ldapuser" -q "SELECT 1" > /dev/null; done'
real 0m22.472s user 0m12.000s sys 0m8.894s
如上所示,通过降低Clickhouse重复请求LDAP Server进行身份验证的开销,我们使用户登录性能提高了约26%。
结论
在本文中,我们介绍了对LDAP与ClickHouse集成的支持。我们研究了如何将LDAP Server配置添加到ClickHouse中。
我们还研究了最简单的情况,即使用LDAP Sever对Clickhouse中定义的用户进行身份验证,无论用户是管理员通过XML文件或RBAC命令配置的。RBAC命令提供对ON CLUSTER
语句的支持,有了ON CLUSTER
可在整个集群上创建或删除用户,而无需修改集群中每个节点的配置文件。
最后,我们研究了如何启用LDAP用户身份验证的缓存以优化用户重复登录Clickhouse的性能。