更多精彩内容,请关注微信公众号:后端技术小屋

原文:https://altinity.com/blog/integrating-clickhouse-with-ldap-part-one
作者:Vitaliy Zakaznikov

ClickHouse上一般使用XML文件来定义配置,其中包括用户配置。users.xml文件或在/etc/clickhouse-server/users.d目录中的配置文件都可用于定义用户及其相关属性,例如profilenetwork restrictionquotapassword。这种方案在集群规模较小时比较有效,但是用来管理大规模集群却很费劲。主要存在三个主要问题:首先每次必须添加、修改或删除用户时,管理员必须手动编辑这些配置文件;其次,将密码和哈希值直接存储在用户配置文件中存在安全隐患;第三,为用户分配和管理角色是非常繁琐的。

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镜像

要使用该环境,需要在系统上安装gitdockerdocker-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  

如果启动成功会看到上面的日志,表明openldap1zookeeperclickhouse1clickhouse2clickhouse3phpldapadmin这些服务已经正常运行。我们还可检查所有服务是否健康。

请注意,您必须执行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=comldapsearch返回的数据中,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节点:clickhouse2clickhouse3

首先,将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的性能。

请继续关注下一部分,我们将讨论如何将LDAP Server用作外部用户目录,从而无需在ClickHouse中定义用户。如果您还有其他疑问或想讨论您的安全需求,请随时通过info@altinity.com与我们联系。我们很乐意为您提供帮助!

推荐阅读

更多精彩内容,请扫码关注微信公众号:后端技术小屋。如果觉得文章对你有帮助的话,请多多分享、转发、在看。

二维码