Papers
Zanzibar: Google's Consistent, Global Authorization System
Ruoming Pang, Ramon Caceres, Mike Burrows, Zhifeng Chen, Pratik Dave, Nathan Germer, Alexander Golynski, Kevin Graney, Nina Kang, Lea Kissner, Jeffrey L. Korn, Abhishek Parmar, Christina D. Richards, Mengzhi Wang · USENIX Annual Technical Conference (ATC) 2019, 2019
O problema: autorização única, global e consistente para centenas de serviços
Dentro do Google, produtos como Calendar, Drive, Maps, Photos e YouTube precisam responder continuamente à mesma pergunta: este usuário pode acessar este objeto? Se cada serviço implementasse suas próprias ACLs (listas de controle de acesso), a semântica de permissão ficaria inconsistente entre produtos, prejudicando a experiência do usuário, e seria difícil fazer aplicações interoperarem, por exemplo quando um objeto de um aplicativo é embutido em outro. O paper descreve o Zanzibar como o sistema global que o Google construiu para armazenar e avaliar ACLs de forma centralizada, oferecendo um modelo de dados uniforme e uma linguagem de configuração capaz de expressar políticas muito diferentes entre si, usados por centenas de serviços. Além de flexível, o sistema precisa ser correto: as decisões de autorização devem respeitar a ordem causal das ações dos usuários, de modo que continuem coerentes enquanto as ACLs e o conteúdo dos objetos mudam. E precisa ser rápido e disponível, já que a verificação de permissão está no caminho crítico de quase toda requisição. Segundo o abstract, o sistema lida com trilhões de ACLs e milhões de requisições de autorização por segundo, e em mais de três anos de produção manteve latência no percentil 95 abaixo de 10 ms e disponibilidade acima de 99,999%.
Modelo de dados: tuplas de relação, usersets e regras de reescrita
O Zanzibar armazena ACLs como tuplas de relação, no formato objeto#relação@usuário, por exemplo dizendo que determinado usuário tem a relação de leitor sobre um documento. Grupos não são uma entidade especial: são apenas objetos cujas relações representam participação. O ponto mais importante é o conceito de userset: em vez de apontar para um usuário individual, a tupla pode apontar para o conjunto de pessoas que possuem certa relação com outro objeto. É assim que se representam grupos aninhados, como dar acesso a todos os membros de um grupo. Cada namespace (tipo de objeto) tem uma configuração que define suas relações e regras de reescrita de userset. Essas regras permitem derivar uma relação a partir de outras, sem gravar tuplas redundantes. Uma relação pode ser definida diretamente pelas tuplas armazenadas, herdada de outra relação no mesmo objeto (por exemplo, quem é editor também conta como leitor) ou obtida seguindo um objeto pai (por exemplo, herdar os leitores da pasta que contém o documento). Assim, políticas complexas ficam em configuração, e o armazenamento guarda apenas fatos simples. A API principal inclui chamadas como Read, Write, Check, Expand e Watch.
Consistência: zookies e o problema do novo inimigo
O paper destaca um problema sutil, chamado de new enemy problem. Dois exemplos: um usuário é removido da ACL de uma pasta, mas, por leitura de ACL desatualizada, ainda enxerga um documento sensível adicionado logo depois à pasta; ou uma ACL antiga é aplicada a um conteúdo novo de um documento. Para evitar isso, o Zanzibar precisa respeitar a ordem causal entre mudanças de ACL e mudanças de conteúdo, o que ele faz com consistência externa: as ACLs ficam no Spanner, cujos timestamps de commit (via TrueTime) refletem a ordem causal das escritas. O mecanismo que expõe isso aos clientes é o zookie, um token opaco que codifica um timestamp globalmente significativo. Quando o cliente autoriza uma alteração de conteúdo, recebe um zookie e o guarda junto com aquela versão do conteúdo. Em verificações posteriores, envia o zookie, e o Zanzibar avalia a checagem num snapshot pelo menos tão recente quanto ele. Sem zookie, o sistema escolhe um snapshot recente que favorece a baixa latência. O trade-off é claro: o cliente só paga o custo de frescor onde precisa de garantia causal, e o restante usa dados levemente defasados, mais fáceis de cachear.
Arquitetura e desempenho: aclservers, Leopard, cache e hedging
As ACLs ficam no Spanner, e cada escrita também vai para um changelog que alimenta a API Watch. Os aclservers recebem as chamadas Check, Read, Expand e Write e distribuem o trabalho pelo cluster; watchservers acompanham o changelog para transmitir mudanças de tuplas; e pipelines offline geram snapshots e removem versões antigas de tuplas. Para grupos muito grandes e profundamente aninhados, onde percorrer a hierarquia seria lento, existe um sistema de indexação separado, o Leopard, que representa a participação em grupos como conjuntos para tornar rápidas as consultas de alcançabilidade: índices são construídos offline e uma camada incremental aplica as atualizações posteriores. Contra pontos quentes, os servidores formam um cache distribuído com hashing consistente, uma tabela de locks por servidor impede que várias requisições calculem o mesmo resultado ao mesmo tempo (cache stampede), e os timestamps de avaliação são arredondados em intervalos grossos para que mais requisições compartilhem entradas de cache. Para reduzir latência de cauda, chamadas ao Spanner e ao Leopard usam hedging: se a resposta demora além de um limiar, envia-se uma requisição de reserva e vale a primeira resposta. Entre servidores do próprio Zanzibar não se faz hedging, porque o custo de uma checagem varia muito e duplicar as caras pioraria a latência.
Legado: autorização baseada em relacionamentos fora do Google
O paper se tornou a referência para o que se chama de controle de acesso baseado em relacionamentos (ReBAC), no qual a decisão depende das relações entre entidades e não apenas de papéis fixos como no RBAC. Uma lição de design é a separação em camadas: um modelo de dados mínimo (tuplas), uma configuração declarativa que deriva permissões a partir delas e um serviço central que precisa tratar consistência causal, cache e latência de cauda como requisitos desde o início. Esse desenho inspirou sistemas de autorização de código aberto e comerciais que adotam o modelo de tuplas e namespaces, normalmente descritos como inspirados no Zanzibar. Para quem projeta sistemas, o paper ensina dois pontos. Primeiro, permissão não é só uma tabela de papéis: em produtos com compartilhamento, herança por pastas e grupos aninhados, ela é um grafo de relações. Segundo, a ordem entre mudanças de permissão e mudanças de conteúdo importa, e a solução do Zanzibar, entregar ao cliente um token de consistência (o zookie) em vez de forçar leituras sempre atuais, é um exemplo de como oferecer garantia forte sem pagar o custo máximo em toda requisição.
Veja as ideias em ação
Pratique na escola de System Design os desenhos que usam este paper.