Implementação de melhores práticas das funções do Postgres

21

Pessoal,

Eu poderia usar sua ajuda para tornar meu design de controle de acesso de usuário do Postgres melhor e mais alinhado às práticas recomendadas. Estou ajudando a implantar um pequeno servidor Postgres de produção, mas não sou um administrador de banco de dados, portanto sei o suficiente para ser perigoso.

Há um servidor com uma instalação do Postgres v9.2. Esta instalação hospeda vários bancos de dados, cada um atendendo totalmente a um "cliente" diferente. Em outras palavras, o cliente1 não utilizará o banco de dados2 e assim por diante. Durante as operações normais, os bancos de dados são acessados ​​por uma instância correspondente do CakePHP, todos localizados no mesmo servidor do Postgres. Embora possa haver possíveis otimizações nessa implantação, estou principalmente interessado nas funções do Psql.

Com base no que li, parece que três tipos de papéis fariam sentido:

  • Postgres de superusuário com senha não padrão
  • Uma função de administrador que não possui privilégios de superusuário para manutenção de rotina, criação de banco de dados, backup e restauração. Deve ser capaz de fazer qualquer coisa com todos os bancos de dados do cliente.
  • Funções de usuário com apenas a capacidade de CRUD em seu respectivo banco de dados. Mais direitos por conta própria poderão ser tolerados se limpar a implementação.

Implementar esse design é onde estou muito menos confiante. Propriedade do banco de dados versus a tabela e quem deve herdar de quem é um pouco enlameado. Abaixo estão meus bancos de dados e meus usuários. São informações suficientes para avaliar a implementação?

     Role name |                   Attributes                   |     Member of     
    -----------+------------------------------------------------+-------------------
     admin     | Create role, Create DB                         | {user1, user2}
     postgres  | Superuser, Create role, Create DB              | {}
     user1     |                                                | {}
     user2     |                                                | {}

    postgres=# \l
                                 List of databases
       Name    |  Owner   | Encoding | Collate | Ctype |   Access privileges   
    -----------+----------+----------+---------+-------+-----------------------
     admin     | postgres | UTF8     | en_US   | en_US | =Tc/postgres         +
               |          |          |         |       | postgres=CTc/postgres+
               |          |          |         |       | admin=CTc/postgres
     postgres  | postgres | UTF8     | en_US   | en_US | 
     template0 | postgres | UTF8     | en_US   | en_US | =c/postgres          +
               |          |          |         |       | postgres=CTc/postgres
     template1 | postgres | UTF8     | en_US   | en_US | =c/postgres          +
               |          |          |         |       | postgres=CTc/postgres
     user1     | admin    | UTF8     | en_US   | en_US | =Tc/admin            +
               |          |          |         |       | admin=CTc/admin      +
               |          |          |         |       | user1=CTc/admin
     user2     | admin    | UTF8     | en_US   | en_US | =Tc/admin            +
               |          |          |         |       | admin=CTc/admin      +
               |          |          |         |       | user2=CTc/admin

Para evitar conexões e senhas externas em claro, o pg_hba.conf é o seguinte:

local   all             all                                     md5
host    all             all             127.0.0.1/32            md5
host    all             all             ::1/128                 md5
JP Beaudry
fonte
1
Na minha experiência, a melhor separação que também traz uma enorme quantidade de outras vantagens é executar clusters PostGreSQL separados (por exemplo, serviços) para cada cliente. É o que atualmente fazemos para um grande ambiente de produção no momento e eu não faria isso de maneira diferente, a menos que o número de DBs ficasse realmente grande e cada um deles fosse realmente pequeno. Obviamente, o aplicativo também precisa saber como se conectar a uma fonte de dados diferente para cada inquilino (cliente).
Florin Asăvoaie
Ao lado de FlorinAsăvoaie, sua observação. Todo banco de dados não deveria ter seu próprio usuário proprietário e usuário de consulta? Isso tornaria mais fácil colocar determinados usuários em um cofre de senhas para fins de manutenção.
Hspaans

Respostas:

5

Sei que essa é uma pergunta antiga, mas tentarei responder ainda agora, pois fiz algumas pesquisas relacionadas a isso.

O que você está tentando fazer é chamado de multilocação no nível do banco de dados. Isso pode ser alcançado de duas maneiras:

  1. Em um único cluster de banco de dados, de alguma maneira como o OP descreveu, no entanto, minha escolha pessoal seria esta:

    • O usuário do postgres usa autenticação de mesmo nível e não tem conexões de senha permitidas. A autenticação MD5, na minha opinião, é uma má prática. Se você se deparar com algum tipo de problema com a consistência do banco de dados ou esse tipo de coisa, ainda poderá fazer login se permitir que o postgres use a autenticação por pares.
    • Cada cliente deve obter seu próprio esquema e não o banco de dados. Há múltiplas razões para isto:
      • Possuir o banco de dados inteiro concederia muitos privilégios.
      • Possuir apenas tabelas específicas traria problemas para os desenvolvedores e sempre exigiria que os administradores adicionassem permissões e outras coisas.
      • Assim, em uma configuração normal, cada um deles teria acesso para criar coisas dentro de seu esquema, incluindo tabelas, visualizações, gatilhos, etc.
      • Todos eles usam a mesma cadeia de conexão, exceto o nome de usuário. No postgres, por padrão, se você tiver um esquema com o nome do seu usuário, ele estará automaticamente no seu caminho de pesquisa.
    • Eu optaria por não ter um usuário administrador capaz de acessar cada esquema, como uma medida de segurança. Você deve fazer backup despejando cada esquema com seu próprio usuário ou usando a técnica PITR do PostgreSQL. Você ainda precisaria usar o usuário do postgres para criar novos esquemas, eu usaria uma regra sudo e um script para isso.
    • Muitas boas práticas de segurança recomendam descartar o esquema padrão, então - lá vamos nós.
    • Essa solução é extremamente adequada se o banco de dados de cada cliente for pequeno e você tiver muitos clientes.
    • Se o seu aplicativo manipular a multilocação, ele poderá usar um único conjunto de conexões para todos os clientes. Obviamente, isso elimina muitos dos aprimoramentos de segurança acima, mas pode ter benefícios de desempenho, especialmente quando você tem um grande número de clientes (se você tiver entre 500 e 1.000 fontes de dados separadas e usar o pool de conexões, será bastante impressionante).
  2. Cada cliente obtém seu próprio cluster de banco de dados. Essa é a minha solução preferida, especialmente porque geralmente trabalho com aplicativos que possuem grandes bancos de dados por cada cliente.

    • Este traz uma separação de dados muito boa. Você pode usar volumes de armazenamento separados para cada cliente, alocar limitações de CPU e memória (usando a janela de encaixe?).
    • Muito boa flexibilidade sobre o que cada cliente precisa em sua instância. Eles podem ser semelhantes ou ter características distintas.
    • Muito fácil de escalar nas duas direções (para cima e para fora).
    • Eu também uso IPs virtuais separados em que cada cluster escuta conexões, fazendo com que o dimensionamento não exija a reconfiguração da fonte de dados.
    • Os backups de PITR são por cliente, portanto, será mais fácil restaurar um único cliente em comparação com a multilocação por esquema.
    • Em configurações complexas, cada cliente pode precisar de vários bancos de dados, esquemas, usuários e funções, etc., portanto, essa é uma solução muito melhor nesses casos.

Você também pode usar uma combinação dos itens acima e usar o pgBouncer como um roteador.

Florin Asăvoaie
fonte