分布式服务Dubbo+Zookeeper安全认证

前言

由于之前的服务都是在内网,Zookeeper集群配置都是走的内网IP,外网不开放相关端口。最近由于业务升级,购置了阿里云的服务,需要对外开放Zookeeper服务。

问题

Zookeeper+dubbo,如何设置安全认证?不想让其他服务连接Zookeeper,因为这个Zookeeper服务器在外网。

查询官方文档:

Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。

流程说明:

  • 服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
  • 服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
  • 监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址

支持以下功能:

  • 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息
  • 当注册中心重启时,能自动恢复注册数据,以及订阅请求
  • 当会话过期时,能自动恢复注册数据,以及订阅请求
  • 当设置 < dubbo:registry check="false" /> 时,记录失败注册和订阅请求,后台定时重试
  • 可通过 < dubbo:registry username="admin" password="1234" /> 设置 zookeeper 登录信息
  • 可通过 < dubbo:registry group="dubbo" /> 设置 zookeeper 的根节点,不设置将使用无根树
  • 支持 * 号通配符 < dubbo:reference group="" version="" />,可订阅服务的所有分组和所有版本的提供者

官网文档第五条,明确说明了可以通过username和 password字段设置zookeeper 登录信息。

以下是registry参数说明:

但是,如果在Zookeeper上通过digest方式设置ACL,然后在dubbo registry上配置相应的用户、密码,服务就注册不到Zookeeper上了,会报KeeperErrorCode = NoAuth错误。

但是查阅ZookeeperRegistry相关源码并没有发现相关认证的地方,搜遍全网很少有问类似的问题,这个问题似乎并没有多少人关注。

Zookeeper中的ACL

概述

传统的文件系统中,ACL分为两个维度,一个是属组,一个是权限,子目录/文件默认继承父目录的ACL。而在Zookeeper中,node的ACL是没有继承关系的,是独立控制的。Zookeeper的ACL,可以从三个维度来理解:一是scheme; 二是user; 三是permission,通常表示为

scheme:id:permissions

下面从这三个方面分别来介绍:

scheme: scheme对应于采用哪种方案来进行权限管理,zookeeper实现了一个pluggable的ACL方案,可以通过扩展scheme,来扩展ACL的机制。zookeeper-3.4.4缺省支持下面几种scheme:

  • world: 它下面只有一个id, 叫anyone, world:anyone代表任何人,zookeeper中对所有人有权限的结点就是属于world:anyone的
  • auth: 它不需要id, 只要是通过authentication的user都有权限(zookeeper支持通过kerberos来进行authencation, 也支持username/password形式的authentication)
  • digest: 它对应的id为username:BASE64(SHA1(password)),它需要先通过username:password形式的authentication
  • ip: 它对应的id为客户机的IP地址,设置的时候可以设置一个ip段,比如ip:192.168.1.0/16, 表示匹配前16个bit的IP段
  • super: 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)

permission: zookeeper目前支持下面一些权限:

  • CREATE(c): 创建权限,可以在在当前node下创建child node
  • DELETE(d): 删除权限,可以删除当前的node
  • READ(r): 读权限,可以获取当前node的数据,可以list当前node所有的child nodes
  • WRITE(w): 写权限,可以向当前node写数据
  • ADMIN(a): 管理权限,可以设置当前node的permission

客户端管理

我们可以通过以下命令连接客户端进行操作:

./zkCli.sh 

帮助

[zk: localhost:2181(CONNECTED) 2] help
ZooKeeper -server host:port cmd args
        connect host:port
        get path [watch]
        ls path [watch]
        set path data [version]
        rmr path
        delquota [-n|-b] path
        quit
        printwatches on|off
        create [-s] [-e] path data acl
        stat path [watch]
        close
        ls2 path [watch]
        history
        listquota path
        setAcl path acl
        getAcl path
        sync path
        redo cmdno
        addauth scheme auth
        delete path [version]
        setquota -n|-b val path

简单操作

[zk: localhost:2181(CONNECTED) 12] ls /
[dubbo, test, zookeeper]
[zk: localhost:2181(CONNECTED) 13] create /itstyle  data ip:192.168.1.190:cdrw
Created /itstyle
[zk: localhost:2181(CONNECTED) 14] getAcl /itstyle
‘ip,‘192.168.1.190
: cdrw

zkclient操作代码

import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;

public class Acl {
    private static final String zkAddress = "192.168.1.190:2181";
    private static final String testNode = "/dubbo";
    private static final String readAuth = "read-user:123456";
    private static final String writeAuth = "write-user:123456";
    private static final String deleteAuth = "delete-user:123456";
    private static final String allAuth = "super-user:123456";
    private static final String adminAuth = "admin-user:123456";
    private static final String digest = "digest";  

    private static void initNode() throws NoSuchAlgorithmException {
        ZkClient zkClient = new ZkClient(zkAddress);
        System.out.println(DigestAuthenticationProvider.generateDigest(allAuth));
        zkClient.addAuthInfo(digest, allAuth.getBytes());
        if (zkClient.exists(testNode)) {
            zkClient.delete(testNode);
            System.out.println("节点删除成功!");
        }  

        List<ACL> acls = new ArrayList<ACL>();
        acls.add(new ACL(ZooDefs.Perms.ALL, new Id(digest, DigestAuthenticationProvider.generateDigest(allAuth))));
        acls.add(new ACL(ZooDefs.Perms.ALL, new Id(digest, DigestAuthenticationProvider.generateDigest(allAuth))));
        acls.add(new ACL(ZooDefs.Perms.READ, new Id(digest, DigestAuthenticationProvider.generateDigest(readAuth))));
        acls.add(new ACL(ZooDefs.Perms.WRITE, new Id(digest, DigestAuthenticationProvider.generateDigest(writeAuth))));
        acls.add(new ACL(ZooDefs.Perms.DELETE, new Id(digest, DigestAuthenticationProvider.generateDigest(deleteAuth))));
        acls.add(new ACL(ZooDefs.Perms.ADMIN, new Id(digest, DigestAuthenticationProvider.generateDigest(adminAuth))));
        zkClient.createPersistent(testNode, testNode, acls);  

        System.out.println(zkClient.readData(testNode));
        System.out.println("节点创建成功!");
        zkClient.close();
    }  

    private static void readTest() {
        ZkClient zkClient = new ZkClient(zkAddress);
        try {
            System.out.println(zkClient.readData(testNode));//没有认证信息,读取会出错
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }  

        try {
            zkClient.addAuthInfo(digest, adminAuth.getBytes());
            System.out.println(zkClient.readData(testNode));//admin权限与read权限不匹配,读取也会出错
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }  

        try {
            zkClient.addAuthInfo(digest, readAuth.getBytes());
            System.out.println(zkClient.readData(testNode));//只有read权限的认证信息,才能正常读取
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }  

        zkClient.close();
    }  

    private static void writeTest() {
        ZkClient zkClient = new ZkClient(zkAddress);  

        try {
            zkClient.writeData(testNode, "new-data");//没有认证信息,写入会失败
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }  

        try {
            zkClient.addAuthInfo(digest, writeAuth.getBytes());
            zkClient.writeData(testNode, "new-data");//加入认证信息后,写入正常
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }  

        try {
            zkClient.addAuthInfo(digest, readAuth.getBytes());
            System.out.println(zkClient.readData(testNode));//读取新值验证
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }  

        zkClient.close();
    }  

    private static void deleteTest() {
        ZkClient zkClient = new ZkClient(zkAddress);
        zkClient.addAuthInfo(digest, deleteAuth.getBytes());
        try {
            System.out.println(zkClient.readData(testNode));
            zkClient.delete(testNode);
            System.out.println("节点删除成功!");
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        zkClient.close();
    }  

    private static void changeACLTest() {
        ZkClient zkClient = new ZkClient(zkAddress);
        //注:zkClient.setAcl方法查看源码可以发现,调用了readData、setAcl二个方法
        //所以要修改节点的ACL属性,必须同时具备read、admin二种权限
        zkClient.addAuthInfo(digest, adminAuth.getBytes());
        zkClient.addAuthInfo(digest, readAuth.getBytes());
        try {
            List<ACL> acls = new ArrayList<ACL>();
            acls.add(new ACL(ZooDefs.Perms.ALL, new Id(digest, DigestAuthenticationProvider.generateDigest(adminAuth))));
            zkClient.setAcl(testNode, acls);
            Map.Entry<List<ACL>, Stat> aclResult = zkClient.getAcl(testNode);
            System.out.println(aclResult.getKey());
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        zkClient.close();
    }  

    public static void main(String[] args) throws Exception {  

        initNode();  

        System.out.println("---------------------");  

        readTest();  

        System.out.println("---------------------");  

        writeTest();  

        System.out.println("---------------------");  

        changeACLTest();  

        System.out.println("---------------------");  

        deleteTest();
    }
}

总结

大部分服务大都是部署在内网的,基本很少对外网开放,然而Dubbo的zookeeper用户权限认证貌似真的不起作用,如果非要对外开放只能通过iptables或者firewall进行IP Access Control,如果是阿里云服务器的话安全组也是个不错的选择。

时间: 2024-10-12 16:54:34

分布式服务Dubbo+Zookeeper安全认证的相关文章

大数据 --&gt; 分布式服务框架Zookeeper

分布式服务框架 Zookeeper Zookeeper系列 分布式服务框架 Zookeeper -- 管理分布式环境中的数据

分布式服务管理框架-Zookeeper客户端zkCli.sh使用详解

原文链接:http://blog.csdn.net/xyang81/article/details/53053642 在学习zookeeper(下面简称zk)客户端之前,有必要先了解一下zk的数据模型.zk维护着一个逻辑上的树形层次结构,树中的节点称为znode,和Linux系统的文件系统结构非常相似,如下图所示: 这种数据结构有如下特点: 1> 每个znode都有唯一路径标识,最顶层的znode为/,比如p_2这个znode的路径标识为/app1/p_2,znode只支持绝对路径,不支持相对路

分布式服务框架 Zookeeper -- 管理分布式环境中的数据

安装和配置详解 Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务.状态同步服务.集群管理.分布式应用配置项的管理等.本文将 从使用者角度详细介绍 Zookeeper 的安装和配置文件中各个配置项的意义,以及分析 Zookeeper 的典型的应用场景(配置文件的管理.集群管理.同步锁.Leader 选举.队列管理等),用 Java 实现它们并给出示例代码. 单机模式 单 机安装非常简单,只要获取

【转】分布式服务框架 Zookeeper -- 管理分布式环境中的数据

Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务.状态同步服务.集群管理.分布式应用配置项的管理等.本文将从使用者角度详细介绍 Zookeeper 的安装和配置文件中各个配置项的意义,以及分析 Zookeeper 的典型的应用场景(配置文件的管理.集群管理.同步锁.Leader 选举.队列管理等),用 Java 实现它们并给出示例代码. 安装和配置详解 本文介绍的 Zookeeper 是以 3

分布式服务管理框架 ZooKeeper

核心功能 统一命名服务(Name Service) 通过有层次的目录结构产生唯一的名称,同时可以将名称关联到特定资源 配置管理(Configuration Management) 将公共的配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息 发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中 集群管理(Group Membership) 通过让集群中每个server监控

分布式服务框架 Zookeeper(一)介绍

一.概述 ZooKeeper(动物园管理员),顾名思义,是用来管理Hadoop(大象).Hive(蜜蜂).Pig(小猪)的管理员,同时Apache Hbase.Apache Solr.LinkedIn Sensei等众多项目中都采用了ZooKeeper. ZooKeeper曾是hadoop的正式子项目,后发展成为Apache顶级项目,与Hadoop密切相关但却没有任何依赖.它是一个针对大型应用提供高可用的数据管理.应用程序协调服务的分布式服务框架,基于对Paxos算法的实现,使该框架保证了分布式

【Zookeeper】分布式服务框架 Zookeeper -- 管理分布式环境中的数据

Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务.状态同步服务.集群管理.分布式应用配置项的管理等.本文将从使用者角度详细介绍 Zookeeper 的安装和配置文件中各个配置项的意义,以及分析 Zookeeper 的典型的应用场景(配置文件的管理.集群管理.同步锁.Leader 选举.队列管理等),用 Java 实现它们并给出示例代码. 安装和配置详解 本文介绍的 Zookeeper 是以 3

JEESZ 分布式架构--dubbo+zookeeper+springmvc+mybatis+shiro+redis

平台简介 Jeesz是一个分布式的框架,提供项目模块化.服务化.热插拔的思想,高度封装安全性的Java EE快速开发平台. Jeesz本身集成Dubbo服务管控.Zookeeper注册中心.Redis分布式缓存技术.FastDFS分布式文件系统.ActiveMQ异步消息中间件.Nginx负载均衡等分布式技术 使用Maven做项目管理,项目模块化,提高项目的易开发性.扩展性 以spring Framework为核心容器,Spring MVC为模型视图控制器,MyBatis为数据访问层, Apach

分布式服务协调员zookeeper - 应用场景和监控

zookeeper在分布式系统中作为协调员的角色,可应用于Leader选举.分布式锁.配置管理等服务的实现.以下我们从zookeeper提供的API.应用场景和监控三方面学习和了解zookeeper(以下简称ZK). ZK API ZK以Unix文件系统树结构的形式管理存储的数据,图示如下: 其中每个树节点被称为znode,每个znode类似一个文件,包含文件元信息(meta data)和数据. 以下我们用server表示ZK服务的提供方,client表示ZK服务的使用方,当client连接ZK