MongoDB Atlas com Terraform: usuários do banco de dados e Vault
SM
Samuel Molling9 min read • Published Apr 15, 2024 • Updated Apr 15, 2024
APLICATIVO COMPLETO
Avalie esse Tutorial
Neste tutorial, mostrarei como criar um usuário para o MongoDB database no MongoDB Atlas usando o Terraform e como armazenar essa credencial com segurança no HashiCorp Vault. Vimos no artigo anterior, MongoDB Atlas With Terraform - Cluster and Backup Políticas, como criar um cluster com políticas de backup configuradas. Agora, Go e criaremos nosso primeiro usuário. Se você não leu os artigos anteriores, sugerimos que você procure para entender como começar.
Este artigo é para qualquer pessoa que pretende usar ou já usa infraestrutura como código (IaC) na plataforma MongoDB Atlas ou deseja saber mais sobre ela.
Tudo o que fazemos aqui está contido na documentação do provedor/recurso:
Neste ponto, criaremos nosso primeiro usuário usando Terraform no MongoDB Atlas e armazenaremos o URI para se conectar ao meu cluster no HashiCorp Vault. Para quem não conhece, o HashiCorp Vault é uma ferramenta de gerenciamento de segredos que permite armazenar, acessar e gerenciar com segurança credenciais confidenciais, como senhas, chaves de API, certificados e muito mais. Ele foi projetado para ajudar as organizações a proteger seus dados e infraestrutura em ambientes de Tl complexos e distribuídos. Nela, armazenaremos o URI de conexão do usuário que será criado com o cluster que criamos no último artigo.
Antes de começarmos, certifique-se de que todos os pré-requisitos mencionados no artigo anterior estejam configurados corretamente: Instalar o Terraform, criar uma chave de API no MongoDB Atlas e configurar um projeto e um cluster no Atlas. Estas etapas são essenciais para garantir o sucesso da criação do seu usuário do banco de dados.
O primeiro passo é executar o HashiCorp Vault para que possamos testar nosso módulo. É possível executar o Vault no Docker Local. Se você não tiver o Docker instalado, poderá baixá-lo. Depois de baixar o Docker, baixaremos a imagem que queremos executar — neste caso, do Vault. Para fazer isso, executaremos um comando no terminal
docker pull vault:1.13.3
ou fará o download usando o Docker Desktop.![Procurando a imagem no docker](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fblt3adf134a1cc654f8%2F661cefe94c473591d2ee4ca7%2Fimage2.png&w=3840&q=75)
Agora, criaremos um container a partir desta imagem. Clique na imagem e clique em Executar. Depois disso, uma caixa será aberta onde só precisamos mapear a porta do nosso computador para o container. Nesse caso, usarei a porta,8200 que é a porta padrão do Vault. Clique em Executar.
![Tela para configurar a porta do docker](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fblt01b4534800d306c0%2F661cefe912f2752a7aeff578%2Fimage8.png&w=3840&q=75)
O contêiner começará a ser executado. Se formos ao nosso navegador e inserirmos a URL
localhost:8200/
, a tela de login do Vault aparecerá.![Tela de login do Vault](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fbltabb003cbf7efb6fa%2F661cefe936f462858244ec50%2Fimage1.png&w=3840&q=75)
Para acessar o Vault, usaremos o Token Raiz que é gerado quando criarmos o container.
![Tela de registro contendo token](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fblt2c34530c41490c28%2F661cefe90aca6b12ed3273b3%2Fimage7.png&w=3840&q=75)
Agora, vamos fazer login. Após abrir, criaremos um novo mecanismo do tipo KV apenas para ignorá-lo um pouco melhor. Clique em Mecanismos secretos -> Habilitar novo mecanismo -> KV genérico e clique em Avançar.
![Tela de seleção do mecanismo de segredos](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fblt867d41655e363848%2F661cefe931ff3a1d35a41344%2Fimage9.png&w=3840&q=75)
Em Caminho,
kv/my_app
coloque e clique em Habilitar mecanismo. Agora, temos nosso Vault configurado e funcionando.A próxima etapa é configurar o provedor Terraform. Isso permitirá que o Terraform se comunique com a API MongoDB Atlas and Vault para gerenciar recursos. Adicione o seguinte bloco de código ao seu arquivo providers.tf:
1 provider "mongodbatlas" {} 2 provider "vault" { 3 address = "http://localhost:8200" 4 token = "hvs.brmNeZd31NwEmyky1uYI2wvY" 5 skip_child_token = true 6 }
No artigo anterior, configuramos o provedor Terraform colocando nossas chaves públicas e privadas em variáveis de ambiente. Continuaremos desta forma. Adicionaremos um novo provedor, o Vault. Nele, configuraremos o endereço do Vault, o token de autenticação e o parâmetro skip_child_token para que possamos nos autenticar no Vault.
Observação: não é aconselhável especificar o token de autenticação em um ambiente de produção. Use um dos métodos de autenticação recomendados pela HashiCorp, como app_role. Você pode avaliar as opções nos Docs do Terraform
O arquivo de versão continua tendo a mesma finalidade, conforme mencionado em outros artigos, mas adicionaremos a versão do provedor do Vault como algo novo.
1 terraform { 2 required_version = ">= 0.12" 3 required_providers { 4 mongodbatlas = { 5 source = "mongodb/mongodbatlas" 6 version = "1.14.0" 7 } 8 vault = { 9 source = "hashicorp/vault" 10 version = "4.0.0" 11 } 12 } 13 }
Depois de configurar o arquivo de versão e estabelecer as versões do Terraform e do provedor, a próxima etapa é definir o recurso do usuário no MongoDB Atlas. Isso é feito criando um arquivo .tf arquivo — por exemplo, main.tf — onde criaremos nosso módulo. Como criaremos um módulo que será reutilizável, usaremos variáveis e valores padrão para que outras chamadas possam criar usuários com permissões diferentes, sem precisar escrever um novo módulo.
1 # ------------------------------------------------------------------------------ 2 # RANDOM PASSWORD 3 # ------------------------------------------------------------------------------ 4 resource "random_password" "default" { 5 length = var.password_length 6 special = false 7 } 8 9 # ------------------------------------------------------------------------------ 10 # DATABASE USER 11 # ------------------------------------------------------------------------------ 12 resource "mongodbatlas_database_user" "default" { 13 project_id = data.mongodbatlas_project.default.id 14 username = var.username 15 password = random_password.default.result 16 auth_database_name = var.auth_database_name 17 18 dynamic "roles" { 19 for_each = var.roles 20 content { 21 role_name = try(roles.value["role_name"], null) 22 database_name = try(roles.value["database_name"], null) 23 collection_name = try(roles.value["collection_name"], null) 24 } 25 } 26 27 dynamic "scopes" { 28 for_each = var.scope 29 content { 30 name = scopes.value["name"] 31 type = scopes.value["type"] 32 } 33 } 34 35 36 dynamic "labels" { 37 for_each = local.tags 38 content { 39 key = labels.key 40 value = labels.value 41 } 42 } 43 } 44 45 resource "vault_kv_secret_v2" "default" { 46 mount = var.vault_mount 47 name = var.secret_name 48 data_json = jsonencode(local.secret) 49 }
No início do arquivo, temos o recurso random_password que é usado para gerar uma senha aleatória para o nosso usuário. No recurso mongodbatlas_database_user, especificaremos nossos detalhes de usuário. Estamos colocando alguns valores como variáveis, como feito em outros artigos, como nome e auth_database_name com um valor padrão de admin. Abaixo, criamos três blocos dinâmicos: funções, escopos e rótulos. Para roles, é uma lista de mapas que podem conter o nome da role (read, readWrite ou algum outro), o database_name e o collection_name. Esses valores podem ser opcionais se você criar um usuário com permissão atlasAdmin, como neste caso, não. É necessário especificar um banco de dados ou uma collection ou, se você quiser, especificar apenas o banco de dados e não uma collection específica. Faremos um exemplo. Para o bloco de escopos, o tipo é DATA_LAKE ou CLUSTER. No nosso caso, especificaremos um cluster, que é o nome do nosso cluster criado, o cluster de demonstração. E os rótulos servem como tags para o nosso usuário.
Finalmente, definimos o recurso vault_kv_secret_v2 que criará um segredo em nosso Vault. Ele recebe o monte onde será criado e o nome do segredo. O data_json é o valor do segredo; estamos criando-o no arquivo locals.tf que avaliaremos abaixo. É um valor JSON — é por isso que o estamos codificando.
No arquivo variable.tf, criamos variáveis com valores padrão:
1 variable "project_name" { 2 description = "The name of the Atlas project" 3 type = string 4 } 5 6 variable "cluster_name" { 7 description = "The name of the Atlas cluster" 8 type = string 9 } 10 11 variable "password_length" { 12 description = "The length of the password" 13 type = number 14 default = 20 15 } 16 17 variable "username" { 18 description = "The username of the database user" 19 type = string 20 } 21 22 variable "auth_database_name" { 23 description = "The name of the database in which the user is created" 24 type = string 25 default = "admin" 26 } 27 28 variable "roles" { 29 description = <<HEREDOC 30 Required - One or more user roles blocks. 31 HEREDOC 32 type = list(map(string)) 33 } 34 35 variable "scope" { 36 description = "The scopes to assign to the user" 37 type = list(object({ 38 name = string 39 type = string 40 })) 41 default = [] 42 } 43 44 variable "labels" { 45 type = map(any) 46 default = null 47 description = "A JSON containing additional labels" 48 } 49 50 variable "uri_options" { 51 type = string 52 default = "retryWrites=true&w=majority&readPreference=secondaryPreferred" 53 description = "A string containing additional URI configs" 54 } 55 56 variable "vault_mount" { 57 description = "The mount point for the Vault secret" 58 type = string 59 } 60 61 variable "secret_name" { 62 description = "The name of the Vault secret" 63 type = string 64 } 65 66 variable "application" { 67 description = <<HEREDOC 68 Optional - Key-value pairs that tag and categorize the cluster for billing and organizational purposes. 69 HEREDOC 70 type = string 71 } 72 73 variable "environment" { 74 description = <<HEREDOC 75 Optional - Key-value pairs that tag and categorize the cluster for billing and organizational purposes. 76 HEREDOC 77 type = string 78 }
Configuramos um arquivo chamado locals.tf com os valores do nosso Vault e as marcações que foram criadas, como o último artigo. O interessante aqui é que estamos definindo como a string de conexão do nosso usuário será montada e salva no Vault. Também não foi possível salvar o nome de usuário e a senha, mas eu pessoalmente preferi salvar o URI. Dessa forma, posso especificar algumas boas práticas, como definir tags de conexão, como readPreference, sem depender do desenvolvedor para colocá-las no aplicação. No código abaixo, existem alguns tratados de texto para que o URI esteja correto. No final, crio uma variável chamada segredo que possui uma chave de URI e recebe o valor do URI criado.
1 locals { 2 private_connection_srv = data.mongodbatlas_advanced_cluster.default.connection_strings.0.standard_srv 3 cluster_uri = trimprefix(local.private_connection_srv, "mongodb+srv://") 4 private_connection_string = "mongodb+srv://${mongodbatlas_database_user.default.username}:${random_password.default.result}@${local.cluster_uri}/${var.auth_database_name}?${var.uri_options}" 5 6 secret = { "URI" = local.private_connection_string } 7 8 tags = { 9 name = var.application 10 environment = var.environment 11 } 12 }
Neste artigo, adotamos o uso de fontes de dados no Terraform para estabelecer uma conexão dinâmica com os recursos existentes, como nosso projeto MongoDB Atlas e nosso cluster. Especificamente, no arquivo data.tf, definimos uma fonte de dados, mongodbotlas_project e mongodcatlas_advanced_cluster, para acessar informações sobre um projeto e cluster existentes com base em seu nome:
1 data "mongodbatlas_project" "default" { 2 name = var.project_name 3 } 4 5 6 data "mongodbatlas_advanced_cluster" "default" { 7 project_id = data.mongodbatlas_project.default.id 8 name = var.cluster_name 9 }
Finalmente, definimos nosso arquivo de variáveis, terraform.tfvars:
1 project_name = "project-test" 2 username = "usr_myapp" 3 application = "teste-cluster" 4 environment = "dev" 5 cluster_name = "cluster-demo" 6 7 roles = [{ 8 "role_name" = "readWrite", 9 "database_name" = "db1", 10 "collection_name" = "collection1" 11 }, { 12 "role_name" : "read", 13 "database_name" : "db2" 14 }] 15 16 scope = [{ 17 name = "cluster-demo", 18 type = "CLUSTER" 19 }] 20 21 secret_name = "MY_MONGODB_SECRET" 22 vault_mount = "kv/my_app"
Estes valores definidos em terraform.tfvars são utilizados pelo Terraform para preencher variáveis correspondentes em sua configuração. Nele, estamos especificando o escopo do usuário, os valores do Vault e as funções do usuário. O usuário terá permissão de leitura e gravação no banco de dados1 na collection1 e permissão de leitura no banco de dados2 em todas as collections do cluster de demonstração.
A estrutura do arquivo é a seguinte:
- main.tf: Neste arquivo, definiremos o recurso principal, o mongodbatlas_database_user e o vault_kv_secret_v2, junto com uma geração aleatória de senhas. Aqui, você configurou o cluster e as rotinas de backup.
- provider.tf: É neste arquivo que definimos o provedor que estamos usando, no nosso caso, mongodbotlas e Vault.
- terraform.tfvars: Este arquivo contém as variáveis que serão usadas em nosso módulo — por exemplo, o nome do usuário e as informações do Vault, entre outras.
- variable.tf: Aqui, definimos as variáveis mencionadas no arquivo terraform.tfvars, especificando o tipo e, opcionalmente, um valor padrão.
- version.tf: Este arquivo é usado para especificar a versão do Terraform e os fornecedores que estamos usando.
- data.tf: Aqui, especificamos uma fonte de dados que nos trará informações sobre nosso projeto e o cluster criado. Faremos a pesquisa do Atlas por seu nome e para nosso módulo, ele nos fornecerá o ID do projeto e as informações do cluster, como sua connection string.
- locals.tf: Especificamos tags de exemplo a serem usadas em nosso usuário e tratamento para criar o URI no Vault.
Agora é a hora de se inscrever. =D
Executamos um Terraform init no terminal na pasta onde os arquivos estão localizados para que ele baixe os provedores, módulos, etc...
Observação: lembre-se de exportar as variáveis de ambiente com a chave pública e privada.
1 export MONGODB_ATLAS_PUBLIC_KEY="your_public_key" 2 export MONGODB_ATLAS_PRIVATE_KEY=your_private_key"
Agora, executamos init e depois planejamos, como nos artigos anteriores.
Avaliamos que nosso plano é exatamente o que esperamos e executamos a aplicação para criá-lo.
Ao executar o comando
terraform apply
, você será solicitado para aprovação com yes
ou no
. Digite yes
.Agora, vejamos no Atlas se o usuário foi criado com sucesso...
![Usuário exibido no acesso ao banco de dados](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fbltcdaf7406e85f79d5%2F661cefe936f462543444ec54%2Fimage3.png&w=3840&q=75)
![Permissões de acesso exibidas](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fbltbb0d0b37cd3e7e23%2F661cefe91c390d5d3c98ec3d%2Fimage10.png&w=3840&q=75)
Vamos também dar uma olhada no Vault para ver se nosso segredo foi criado.
![URI secreto do MongoDB](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fblt0dc4c9ad575c4118%2F661cefe9ba18470cf69b8c14%2Fimage6.png&w=3840&q=75)
Foi criado com sucesso! Agora, vamos testar se o URI está funcionando perfeitamente.
Este é o formato do URI que é gerado:
mongosh "mongodb+srv://usr_myapp:<password>@<clusterEndpoint>/admin?retryWrites=true&majority&readPreference=secondaryPreferred"
![Login do Mongosh](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fbltc6c61799f656701f%2F661cf85d4c4735186bee4ce7%2Fimage5.png&w=3840&q=75)
Conectamos e faremos uma inserção para avaliar se as permissões são adequadas — inicialmente, em db1 em collection1.
![Comando a ser inserido no banco de dados e confirmado](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fbltdd01eaae2a3d9d24%2F661cefe936f462254644ec58%2Fimage11.png&w=3840&q=75)
Sucesso! Agora, no db3, verifique se ele não terá permissão em outro banco de dados.
![Acesso negado a collection não autorizada](/developer/_next/image/?url=https%3A%2F%2Fimages.contentstack.io%2Fv3%2Fassets%2Fblt39790b633ee0d5a7%2Fblt05fe248cb479b18a%2F661cf85d4c47359b89ee4ce5%2Fimage4.png&w=3840&q=75)
Chegamos ao final desta série de artigos sobre MongoDB. espero que tenham sido úteis para você e para você!
Para saber mais sobre o MongoDB e várias ferramentas, Convido você a visitar o Centro do Desenvolvedor para ler os outros artigos.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.
Relacionado
Tutorial
Tutorial do MongoDB Atlas Data Federation: consultas federadas e $out para AWS S3
Jan 23, 2024 | 7 min read
Tutorial
Como melhorar os aplicativos LLM com a recuperação de documentos principais usando MongoDB e LangChain
Feb 11, 2025 | 15 min read