天天看點

Dubbo+Zookeeper ACL+ElasticJob

一. 問題背景

當Zookeeper開啟ACL後,Dubbo以及Elastic-Job需要如何配置來連接配接Zookeeper。

Zookeeper ACL介紹參考文章: https://cloud.tencent.com/developer/article/1414462

二. 元件版本

  1. zookeeper: 3.5.8
  2. dubbo: 2.6.6
  3. elastic-job: 1.0.6

三. ZK設定ACL

3.1 dubbo

dubbo預設是注冊在/dubbo目錄,下面以/dubbo目錄為例說明。

1. 進入Zookeeper用戶端,建立/dubbo目錄及設定ACL權限。

$ create /dubbo 'data'
$ addauth digest hamawhite:123456

$ setAcl /dubbo auth:hamawhite:123456:cdwra
$ getAcl /dubbo

'digest,'hamawhite:YfjnOH3mtKecIPZ5prmNB7B6lA4=
: cdrwa
           

2.   修改SpringBoot的配置檔案,增加duboo registry的username和password。

dubbo.registry.username=hamawhite
dubbo.registry.password=123456
           

如果Zookeeper管理者配置設定給指定的目錄來注冊,例如 /gou,則需要在dubbo的配置檔案中還需增加 dubbo.registry.group=/shuqi 。

完整配置如下:

dubbo.registry.group=shuqi
dubbo.registry.username=hamawhite
dubbo.registry.password=123456
           

3.2 elastic-job

推薦把elastic-job的namespace配置為dubbo.registry.group的值,否則Zookeeper管理者還需要給elastic-job額外配置設定目錄。

完整配置如下:

<!--配置作業注冊中心 -->
    <reg:zookeeper id="regCenter"
                   serverLists="${dubbo.registry.address}"
                   namespace="${dubbo.registry.group}"
                   digest="${dubbo.registry.username}:${dubbo.registry.password}"
                   baseSleepTimeMilliseconds="1000"
                   maxSleepTimeMilliseconds="3000"
                   maxRetries="0"/>
           

注意,在SpringBoot的配置中一定要定義dubbo.registry.group,如果是預設值,也要寫dubbo.registry.group=dubbo。

四. ZK目錄探索

4.1 dubbo registry目錄

dubbo在/dubbo目錄下注冊的service都未設定ACL,權限都是world:anyone:cdrwa。這樣其實任意使用者能通路此service目錄,仍有安全風險。

[zk: localhost:2181(CONNECTED) 64] getAcl /dubbo/com.dtwave.ai.api.service.ModelService
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 65] getAcl /dubbo/com.dtwave.ai.api.service.NotebookService
'world,'anyone
: cdrwa
           

4.2 dubbo 源碼分析

dubbo2.6版本後Zookeeper用戶端預設是Curator,是以檢視com.alibaba.dubbo.remoting.zookeeper.curator.CuratorZookeeperClient的代碼。

建立client代碼如下:

public CuratorZookeeperClient(URL url) {
        super(url);
        try {
            CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
                    .connectString(url.getBackupAddress())
                    .retryPolicy(new RetryNTimes(1, 1000))
                    .connectionTimeoutMs(5000);
            String authority = url.getAuthority();
            if (authority != null && authority.length() > 0) {
                builder = builder.authorization("digest", authority.getBytes());
            }
            client = builder.build();
            client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
                @Override
                public void stateChanged(CuratorFramework client, ConnectionState state) {
                    if (state == ConnectionState.LOST) {
                        CuratorZookeeperClient.this.stateChanged(StateListener.DISCONNECTED);
                    } else if (state == ConnectionState.CONNECTED) {
                        CuratorZookeeperClient.this.stateChanged(StateListener.CONNECTED);
                    } else if (state == ConnectionState.RECONNECTED) {
                        CuratorZookeeperClient.this.stateChanged(StateListener.RECONNECTED);
                    }
                }
            });
            client.start();
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
           

可以看到,建立client的時候有處理授權,builder = builder.authorization("digest", authority.getBytes()) ,也未設定aclProvider。

在建立目錄時候,也未設定ACL。 是以子目錄權限都是預設的world:anyone:cdrwa。

@Override
    public void createPersistent(String path) {
        try {
            client.create().forPath(path);
        } catch (NodeExistsException e) {
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    @Override
    public void createEphemeral(String path) {
        try {
            client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
        } catch (NodeExistsException e) {
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
           

補充建立目錄可以通過配置withACL來設定ACL,示例代碼如下:

具體可參考 https://blog.csdn.net/liuxiao723846/article/details/85303602

client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(nodePath, nodeData);
           

4.3 elastic-job zookeeper目錄

elastic-job在/dubbo目錄下建立的子目錄使用者名和密碼和配置項保持一緻,permission權限都是cdrwa。

[zk: localhost:2181(CONNECTED) 66] getAcl /dubbo/updateNotebookStatusJob
'digest,'hamawhite:YfjnOH3mtKecIPZ5prmNB7B6lA4=
: cdrwa
[zk: localhost:2181(CONNECTED) 67] ls /dubbo/updateNotebookStatusJob
[config, execution, leader, servers]
[zk: localhost:2181(CONNECTED) 68] getAcl /dubbo/updateNotebookStatusJob/leader
'digest,'hamawhite:YfjnOH3mtKecIPZ5prmNB7B6lA4=
: cdrwa
           

4.4 elastic-job Zookeeper用戶端源碼分析

檢視com.dangdang.ddframe.reg.zookeeper.ZookeeperRegistryCenter的代碼。

建立client 核心代碼如下:

public void init() {
        ......

        if (!Strings.isNullOrEmpty(this.zkConfig.getDigest())) {
            builder.authorization("digest", this.zkConfig.getDigest().getBytes(Charset.forName("UTF-8"))).aclProvider(new ACLProvider() {
                public List<ACL> getDefaultAcl() {
                    return Ids.CREATOR_ALL_ACL;
                }

                public List<ACL> getAclForPath(String path) {
                    return Ids.CREATOR_ALL_ACL;
                }
            });
        }

        this.client = builder.build();
        this.client.start();

        try {
            this.client.blockUntilConnected();
            if (!Strings.isNullOrEmpty(this.zkConfig.getLocalPropertiesPath())) {
                this.fillData();
            }
        } catch (Exception var3) {
            RegExceptionHandler.handleException(var3);
        }

    }
           

可以看到,建立client的時候有處理授權,builder = builder.authorization("digest", authority.getBytes()) ,設定了aclProvider。

其中perms設定為全部權限,即cdrwa。Ids設定為AUTH_IDS,表示會繼承用戶端的身份鑒權資訊。

/**
         * This ACL gives the creators authentication id's all permissions.
         */
        public final ArrayList<ACL> CREATOR_ALL_ACL = new ArrayList<ACL>(
                Collections.singletonList(new ACL(Perms.ALL, AUTH_IDS)));

       /**
         * This Id is only usable to set ACLs. It will get substituted with the
         * Id's the client authenticated with.
         */
        public final Id AUTH_IDS = new Id("auth", "");
           

這樣CreateBuilderImpl裡的 acling = new ACLing(client.getAclProvider()),後續用client建立的子目錄使用者名和密碼和配置項保持一緻,permission權限都是cdrwa。

繼續閱讀