1. 程式人生 > >《ZooKeeper官方指南》ZooKeeper 使用 ACL 進行訪問控制

《ZooKeeper官方指南》ZooKeeper 使用 ACL 進行訪問控制

原文連結 譯者:Thor_liu

ZooKeeper 使用 ACL 進行訪問控制

ZooKeeper使用ACL來控制訪問其znode(ZooKeeper的資料樹的資料節點)。ACL的實現方式非常類似於UNIX檔案的訪問許可權:它採用訪問許可權位 允許/禁止 對節點的各種操作以及能進行操作的範圍。不同於UNIX許可權的是,ZooKeeper的節點不侷限於 使用者(檔案的擁有者),組和其他人(其它)這三個標準範圍。ZooKeeper不具有znode的擁有者的概念。相反,ACL指定id集以及與之對應的許可權。

還要注意的是一條ACL僅針對於一個特定的節點。尤其不適用於子節點。例如,如果/app 只對IP:172.16.16.1可讀 而 / APP/status 是對任何人可讀的,ACL不是遞迴的。

ZooKeeper支援可插拔的身份驗證方案。 id使用如下形式  scheme:id,其中 scheme 是 id 所對應一個認證方案。例如,IP:172.16.16.1,id為主機的地址 172.16.16.1。

當客戶端連線到ZooKeeper驗證自己時,ZooKeeper將有關該客戶端的所有Id與客戶連線關聯。客戶端試圖訪問一個節點時,這些ID與該znodes的ACL驗證。 ACL是由(scheme:expression, perms)對構成。其中expression的格式指定為scheme。例如,(IP:19.22.0.0/16,READ)表示對所有起始IP為19.22的客戶端具有讀許可權。

ACL 許可權

ZooKeeper 支援以下許可權:

  • CREATE: 能建立子節點
  • READ:能獲取節點資料和列出其子節點
  • WRITE: 能設定節點資料
  • DELETE: 能刪除子節點
  • ADMIN: 能設定許可權

CREATE許可權和DELETE許可權從WRITE許可權中分離出來,是為了獲得更好的訪問控制。使用CREATE和DELETE許可權的場景如下:

你想讓A使用者能夠設定節點資料,但不允許建立或刪除子節點。

有CREATE但無DELETE許可權:客戶端發出一個在父目錄下建立節點的請求。你想讓所有客戶端能夠新增,但是隻有建立者能夠刪除。(這類似於檔案的APPEND許可權)。

內建的 ACL schemes

ZooKeeper有如下內建的schemes

  • world 有個唯一的id, anyone ,代表所有人。
  • auth 不使用任何id,代表任何已認證的使用者。
  • digestusername:password 字串來產生一個MD5串,然後該串被用來作為ACL ID。認證是通過明文傳送username:password 來進行的,當用在ACL時,表示式為username:base64 ,base64是password的SHA1摘要的編碼。
  • ip 使用客戶端的主機IP作為ACL ID 。這個ACL表示式的格式為addr/bits ,此時addr中的有效位與客戶端addr中的有效位進行比對。

ZooKeeper C 客戶端 API

如下的常量是由ZooKeeper C語言庫中提供的:

  • const int ZOO_PERM_READ;  //能讀節點的值及列出其子節點
  • const int ZOO_PERM_WRITE; //能設定節點的值
  • const int ZOO_PERM_CREATE;  //能建立子節點
  • const int ZOO_PERM_DELETE; // 能刪除子節點
  • const int ZOO_PERM_ADMIN;  //能執行set_acl()
  • const int ZOO_PERM_ALL; // 上面所有值的OR
  • struct Id ZOO_ANYONE_ID_UNSAFE; //(‘world’,’anyone’)
  • struct Id ZOO_AUTH_IDS;// (‘auth’,’’)

ZOO_AUTH_IDS 為空時,應被理解成“建立者的Id”

ZooKeeper 客戶端有3種標準的ACL:

  • struct ACL_vector ZOO_OPEN_ACL_UNSAFE; //(ZOO_PERM_ALL,ZOO_ANYONE_ID_UNSAFE)
  • struct ACL_vector ZOO_READ_ACL_UNSAFE;// (ZOO_PERM_READ, ZOO_ANYONE_ID_UNSAFE)
  • struct ACL_vector ZOO_CREATOR_ALL_ACL; //(ZOO_PERM_ALL,ZOO_AUTH_IDS)

ZOO_OPEN_ACL_UNSAFE使所有ACL都“開放”了:任何應用程式在節點上可進行任何操作,能建立、列出和刪除它的子節點。對任何應用程式,ZOO_READ_ACL_UNSAFE是隻讀的。CREATE_ALL_ACL賦予了節點的建立者所有的許可權,在建立者採用此ACL建立節點之前,已經被伺服器所認證(例如,採用 “ digest”方案)。

以下ZooKeeper方法處理ACL:

  • int zoo_add_auth (zhandle_t *zh,const char* scheme,const char* cert, int certLen, void_completion_t completion, const void *data);

The application uses the zoo_add_auth function to authenticate itself to the server. The function can be called multiple times if the application wants to authenticate using different schemes and/or identities.

應用程式使用zoo_add_auth方法來向伺服器認證自己,如果想用不同的方案來認證,這個方法可以被呼叫多次。

  • int zoo_create (zhandle_t *zh, const char *path, const char *value,int valuelen, const struct ACL_vector *acl, int flags,char *realpath, int max_realpath_len);

zoo_create(…)方法建立一個新節點。acl 引數是一個與這個節點關聯的ACL列表,父節點許可權項的CREATE位已被設(set,即由許可權)。

  • int zoo_get_acl (zhandle_t *zh, const char *path,struct ACL_vector *acl, struct Stat *stat);

這個方法返回這個節點的ACL資訊。

  • int zoo_set_acl (zhandle_t *zh, const char *path, int version,const struct ACL_vector *acl);

這個方法用新的節點的ACL列表替換老的,這個節點的ADMIN位必須被設定(set,即具有ADMIN許可權)。

這有一個使用上面API的例子,採用”foo”方案認證,建立一個“/xyz”的暫態節點,設定其為”只建立“許可權。

這是一個非常簡單的例子,它的目的是展示如何與ZooKeeper ACL交換。C客戶端的實現,參考…/trunk/src/c/src/cli.c .

#include <errno.h>

#include “zookeeper.h”

static zhandle_t *zh;

/**
* In this example this method gets the cert for your
* environment — you must provide
*/
char *foo_get_cert_once(char* id) { return 0; }

/** Watcher function — empty for this example, not something you should
* do in real code */
void watcher(zhandle_t *zzh, int type, int state, const char *path,
void *watcherCtx) {}

int main(int argc, char argv) {
char buffer[512];
char p[2048];
char *cert=0;
char appId[64];

strcpy(appId, “example.foo_test”);
cert = foo_get_cert_once(appId);
if(cert!=0) {
fprintf(stderr,
“Certificate for appid [%s] is [%s]\n”,appId,cert);
strncpy(p,cert, sizeof(p)-1);
free(cert);
} else {
fprintf(stderr, “Certificate for appid [%s] not found\n”,appId);
strcpy(p, “dummy”);
}

zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);

zh = zookeeper_init(“localhost:3181″, watcher, 10000, 0, 0, 0);
if (!zh) {
return errno;
}
if(zoo_add_auth(zh,”foo”,p,strlen(p),0,0)!=ZOK)
return 2;

struct ACL CREATE_ONLY_ACL[] = {{ZOO_PERM_CREATE, ZOO_AUTH_IDS}};
struct ACL_vector CREATE_ONLY = {1, CREATE_ONLY_ACL};
int rc = zoo_create(zh,”/xyz”,”value”, 5, &CREATE_ONLY, ZOO_EPHEMERAL,
buffer, sizeof(buffer)-1);

/** this operation will fail with a ZNOAUTH error */
int buflen= sizeof(buffer);
struct Stat stat;
rc = zoo_get(zh, “/xyz”, 0, buffer, &buflen, &stat);
if (rc) {
fprintf(stderr, “Error %d for %s\n”, rc, __LINE__);
}

zookeeper_close(zh);
return 0;
}