One place for hosting & domains

      Proteger

      Cómo proteger MongoDB en Ubuntu 20.04


      Melissa Anderson escribió una versión anterior de este tutorial.

      Introducción

      MongoDB, también conocido como Mongo, es una base de datos de documentos de código abierto que se utiliza en muchas aplicaciones web modernas. Se clasifica como base de datos NoSQL porque no sigue la estructura de las bases de datos relacionales tradicionales basadas en tablas. En su lugar, utiliza documentos similares a JSON con esquemas dinámicos.

      En MongoDB, la autenticación no está habilitada por defecto, por lo tanto, cualquier usuario con acceso al servidor donde se haya instalado la base de datos puede agregar y eliminar datos sin restricciones. Este tutorial le servirá como guía para crear un usuario administrativo y habilitar la autenticación para brindar protección contra estas vulnerabilidades. Luego, realizará una prueba para confirmar que solo ese usuario administrativo tenga acceso a la base de datos.

      Requisitos previos

      Para completar este tutorial, necesitará lo siguiente:

      Paso 1: Añadir un usuario administrativo

      Desde el lanzamiento de la versión 3.0, el demonio de MongoDB está configurado para aceptar únicamente conexiones del socket local de Unix y no se abre automáticamente a Internet en general. Sin embargo, la autenticación se sigue manteniendo desactivada de forma predeterminada. Esto significa que cualquier usuario que tenga acceso al servidor donde se haya instalado MongoDB también tiene pleno acceso a las bases de datos.

      Como primer paso para brindar protección contra esta vulnerabilidad, creará un usuario administrativo. A continuación, habilitará la autenticación y se conectará como este usuario administrativo para acceder a la base de datos.

      Para agregar un usuario administrativo, primero, debe conectarse al shell de Mongo. Como la autenticación está desactivada, puede hacerlo con el comando mongo, sin ninguna otra opción:

      Verá un resultado arriba de la línea de comandos del shell de Mongo. Como todavía no habilitó la autenticación, incluirá una advertencia de que el control de acceso no está habilitado para la base de datos y que el acceso de lectura y escritura a los datos y a la configuración de la base de datos no está restringido:

      Output

      MongoDB shell version v4.4.0 . . . 2020-06-09T13:26:51.391+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database. 2020-06-09T13:26:51.391+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted. . . . >

      Estas advertencias desaparecerán una vez que habilite la autenticación; por ahora, indican que cualquier persona que pueda acceder a su servidor de Ubuntu también puede tomar control de su base de datos.

      Para demostrar esto, ejecute el comando show dbs de Mongo:

      Este comando devuelve una lista de todas las bases de datos del servidor. Sin embargo, cuando la autenticación está habilitada, la lista cambia según el rol del usuario de Mongo o el nivel de acceso que tenga a ciertas bases de datos. Pero, como la autenticación está desactivada, devolverá todas las bases de datos que se encuentren en el sistema sin restricciones:

      Output

      admin 0.000GB config 0.000GB local 0.000GB

      En este resultado de ejemplo, solo se muestran las bases de datos predeterminadas. Sin embargo, si tiene alguna base de datos que contenga datos confidenciales en su sistema, cualquier usuario podría encontrarla con este comando.

      Este paso se centra en agregar un usuario administrativo como parte de la mitigación de esta vulnerabilidad. Para hacerlo, primero, debe conectarse a la base de datos admin. Allí es donde se almacena la información sobre los usuarios, como sus nombres de usuario, contraseñas y roles:

      Output

      switched to db admin

      MongoDB viene instalado con varios métodos de shell basados en JavaScript que puede usar para administrar su base de datos. Uno de ellos, el método db.createUser, se utiliza para crear usuarios nuevos en la base de datos en la que se ejecuta el método.

      Inicie el método db.createUser:

      Este método requiere que especifique un nombre de usuario y una contraseña para el usuario, así como los roles que desee asignarle. Recuerde que MongoDB almacena sus datos en documentos similares a JSON. Por lo tanto, al crear un usuario nuevo, lo que hace es crear un documento para almacenar los datos correspondientes del usuario en campos individuales.

      Tal como sucede con los objetos en JSON, los documentos de MongoDB comienzan y terminan con llaves ({ y }). Para comenzar a agregar un usuario, ingrese una llave de apertura:

      Nota: Mongo no registrará el método db.createUser como completo hasta que ingrese un paréntesis de cierre. Hasta que lo haga, el símbolo del sistema cambiará del signo de mayor que (>) al de puntos suspensivos (...).

      A continuación, ingrese un campo user:, con el nombre de usuario que desee como valor entre comillas dobles seguido por un coma. En el siguiente ejemplo, se especifica el nombre de usuario AdminSammy, pero puede ingresar el que desee:

      Luego, ingrese un campo pwd con el método passwordPrompt() como valor. Cuando ejecute el método db.createUser, el método passwordPrompt() le presentará un mensaje para que ingrese su contraseña. Esta alternativa es más segura que escribir la contraseña en texto no cifrado, como hizo con el nombre de usuario.

      Nota: El método passwordPrompt() solo es compatible con MongoDB 4.2 y versiones posteriores. Si está utilizando una versión anterior de Mongo, deberá escribir la contraseña en texto no cifrado, como hizo al escribir el nombre de usuario:

      Asegúrese de también incluir una coma al final de este campo:

      A continuación, ingrese los roles que desea que tenga su usuario administrativo. Como está creando un usuario administrativo, como mínimo, debería concederle el rol userAdminAnyDatabase en la base de datos admin. Esto le permitirá crear y modificar usuarios nuevos y roles. Como el usuario administrativo tiene este rol en la base de datos admin, también tendrá acceso de superusuario a todo el clúster.

      Además, en el siguiente ejemplo, también se le otorga al usuario administrativo el rol readWriteAnyDatabase. Esto le brinda la posibilidad de leer y modificar datos en cualquier base de datos del clúster, a excepción de las bases de datos config y locales, que, en su mayoría, son de uso interno:

      • roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ]

      A continuación, ingrese una llave de cierre para indicar el final del documento:

      Luego, ingrese un paréntesis de cierre para cerrar y ejecutar el método db.createUser:

      En conjunto, así es como debería verse su método db.createUser:

      > db.createUser(
      ... {
      ... user: "AdminSammy",
      ... pwd: passwordPrompt(),
      ... roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ]
      ... }
      ... )
      

      Si la sintaxis de cada línea es correcta, el método se ejecutará de forma adecuada y se le solicitará ingresar una contraseña:

      Output

      Enter password:

      Ingrese una contraseña segura de su elección. A continuación, recibirá una confirmación de que el usuario se añadió:

      Output

      Successfully added user: { "user" : "AdminSammy", "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" }, "readWriteAnyDatabase" ] }

      Una vez que la reciba, puede salir del cliente MongoDB:

      En este punto, su usuario podrá ingresar credenciales. Sin embargo, no se le requerirá hacerlo hasta que habilite la autenticación y reinicie el demonio de MongoDB.

      Paso 2: Habilitar la autenticación

      Para habilitar la autenticación, debe editar mongod.conf, el archivo de configuración de MongoDB. Una vez que la habilite y reinicie el servicio de Mongo, los usuarios podrán seguir conectándose a la base de datos sin autenticación. Sin embargo, no podrán leer ni modificar datos sin proporcionar un nombre de usuario y una contraseña correctos.

      Abra el archivo de configuración en su editor de texto preferido. En este caso, utilizaremos nano:

      • sudo nano /etc/mongod.conf

      Desplácese hacia abajo hasta la sección security marcada con comentarios:

      /etc/mongod.conf

      . . .
      #security:
      
      #operationProfiling:
      
      . . .
      

      Quite la marca de comentario de esta línea eliminando el signo de numeral (#):

      /etc/mongod.conf

      . . .
      security:
      
      #operationProfiling:
      
      . . .
      

      A continuación, añada el parámetro authorization y establézcalo en "enabled". Cuando haya terminado, las líneas deberían tener el siguiente aspecto:

      /etc/mongod.conf

      . . .
      security:
        authorization: "enabled"
      . . .
      

      Tenga en cuenta que la línea security: no tiene espacios al principio, pero la línea authorization: tiene dos espacios de sangría.

      Después de agregar estas líneas, guarde y cierre el archivo. Si utilizó nano para abrir el archivo, hágalo pulsando CTRL + X, Y y, luego, ENTER.

      A continuación, reinicie el demonio para que estos cambios nuevos surtan efecto:

      • sudo systemctl restart mongod

      Luego, compruebe el estado del servicio para asegurarse de que se haya reiniciado correctamente:

      • sudo systemctl status mongod

      Si el comando restart se ejecutó correctamente, obtendrá un resultado que indica que el servicio mongod está activo y se ha iniciado recientemente:

      Output

      ● mongod.service - MongoDB Database Server Loaded: loaded (/lib/systemd/system/mongod.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2020-06-09 22:06:20 UTC; 7s ago Docs: https://docs.mongodb.org/manual Main PID: 15370 (mongod) Memory: 170.1M CGroup: /system.slice/mongod.service └─15370 /usr/bin/mongod --config /etc/mongod.conf Jun 09 22:06:20 your_host systemd[1]: Started MongoDB Database Server.

      Después de verificar que el demonio esté en funcionamiento, puede realizar una prueba para determinar si la configuración de autenticación que añadió funciona de la manera prevista.

      Paso 3: Probar la configuración de autenticación

      Para comenzar a probar si los requisitos de autenticación que añadió en el paso anterior funcionan correctamente, comience por conectarse sin especificar credenciales para verificar si sus acciones efectivamente están restringidas:

      Ahora que habilitó la autenticación, no se mostrará ninguna de las advertencias que se le presentaron anteriormente:

      Output

      MongoDB shell version v4.4.0 connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb Implicit session: session { "id" : UUID("5d50ed96-f7e1-493a-b4da-076067b2d898") } MongoDB server version: 4.4.0 >

      Confirme si su acceso está restringido al volver a ejecutar el comando show dbs:

      Recuerde que, como vio en el Paso 1, hay algunas bases de datos predeterminadas en su servidor. Sin embargo, en este caso, el comando no generará ningún resultado porque no se identificó como usuario con privilegios.

      Dado que este comando no devuelve ningún dato, se puede decir que la configuración de autenticación está funcionando de la manera prevista. Tampoco podrá crear usuarios ni realizar otras tareas que requieren privilegios sin autenticarse.

      Proceda a salir del shell de MongoDB:

      Nota: En lugar de ejecutar el siguiente comando exit, como hizo en el Paso 1, alternativamente, puede cerrar el shell presionando CTRL + C.

      A continuación, asegúrese de que su usuario administrativo pueda autenticarse correctamente ejecutando el siguiente comando mongo para conectarse con las credenciales de este usuario. Este comando incluye el indicador -u, que precede el nombre de usuario con el que se desea conectar. Asegúrese de sustituir AdminSammy por el nombre de usuario de su propio usuario administrativo. También incluye el indicador -p, que le solicitará la contraseña del usuario, y especifica admin como la base de datos de autenticación en la que se creó el nombre de usuario indicado:

      • mongo -u AdminSammy -p --authenticationDatabase admin

      Ingrese la contraseña del usuario cuando se le solicite hacerlo; luego, se lo dirigirá al shell . Una vez allí, intente volver a emitir el comando show dbs:

      Esta vez, dado que se autenticó de forma adecuada, el comando devolverá correctamente una lista de todas las bases de datos que se encuentran en el servidor:

      Output

      admin 0.000GB config 0.000GB local 0.000GB

      Esto confirma que la autenticación se habilitó de forma correcta.

      Conclusión

      Al completar esta guía, creó un usuario administrativo de MongoDB que puede utilizar tanto para crear y modificar nuevos usuarios y roles como para administrar su instancia de MongoDB. También configuró su instancia de MongoDB para exigir que los usuarios se autentiquen con un nombre de usuario y una contraseña válidos para poder interactuar con cualquier dato.

      Para obtener más información sobre cómo administrar usuarios en MongoDB, consulte la documentación oficial sobre el tema. También puede interesarle aprender más sobre el funcionamiento de la autenticación en MongoDB.

      Además, si planea interactuar de forma remota con su instancia de MongoDB, puede consultar nuestra guía Cómo configurar el acceso remoto para MongoDB en Ubuntu 20.04.



      Source link

      Como proteger o MongoDB no Ubuntu 20.04


      Uma versão anterior deste tutorial foi escrita por Melissa Anderson.

      Introdução

      O MongoDB, também conhecido como Mongo, é um banco de dados de documentos de código aberto usado em muitas aplicações Web modernas. Ele é classificado como um banco de dados NoSQL porque ele não depende de uma estrutura de banco de dados relacional tradicional baseada em tabela. Ao invés disso, ele usa documentos semelhantes ao JSON com esquemas dinâmicos.

      O MongoDB não possui a autenticação habilitada por padrão, o que significa que qualquer usuário com acesso ao servidor onde o banco de dados está instalado pode adicionar e excluir dados sem restrição. Para protegê-lo dessa vulnerabilidade, este tutorial irá guiá-lo na criação de um usuário administrativo e na ativação da autenticação. Em seguida, você fará um teste para confirmar se apenas esse usuário administrativo tem acesso ao banco de dados

      Pré-requisitos

      Para concluir este tutorial, você precisará do seguinte:

      • Um servidor executando o Ubuntu 20.04. Cada servidor deverá ter um non-root user administrativo e um firewall configurado com o UFW. Configure isso seguindo o nosso guia de configuração inicial de servidor para o Ubuntu 20.04.
      • O MongoDB instalado em seu servidor. Este tutorial foi validado usando a versão 4.4 do MongoDB. Apesar disso, ele também deve funcionar para versões mais antigas do MongoDB. Para instalar o Mongo em seu servidor, siga nosso tutorial sobre Como instalar o MongoDB no Ubuntu 20.04.

      Passo 1 — Adicionando um usuário administrativo

      Desde o lançamento da versão 3.0, o daemon do MongoDB vem configurado para aceitar apenas conexões partindo do soquete local do Unix, e ele não fica automaticamente aberto à Internet como um todo. No entanto, a autenticação ainda vem desativada por padrão. Isso significa que todos os usuários que têm acesso ao servidor onde o MongoDB está instalado também têm acesso completo aos bancos de dados.

      Como um primeiro passo para proteger essa vulnerabilidade, você irá criar um usuário administrativo. Mais tarde, você irá habilitar a autenticação e se conectar com esse usuário administrativo para acessar o banco de dados

      Para adicionar um usuário administrativo, é necessário conectar-se primeiro ao shell do Mongo. Como a autenticação está desativada, faça isso com o comando mongo, sem nenhuma outra opção:

      Haverá algum resultado acima do prompt do shell do Mongo. Como a autenticação ainda não foi ativada, ele irá incluir um aviso informando que o controle de acesso não está habilitado para o banco de dados e que o acesso à leitura e escrita dos dados e a configuração do banco de dados não têm restrição:

      Output

      MongoDB shell version v4.4.0 . . . 2020-06-09T13:26:51.391+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database. 2020-06-09T13:26:51.391+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted. . . . >

      Esses avisos irão desaparecer depois que você habilitar a autenticação, mas, por enquanto, eles informam que qualquer um que consiga acessar seu servidor Ubuntu também pode assumir o controle do seu banco de dados.

      Para ilustrar, execute o comando show dbs do Mongo:

      Esse comando retorna uma lista de todos os banco de dados presentes no servidor. No entanto, quando a autenticação está habilitada, a lista muda com base na função do usuário do Mongo, ou qual nível de acesso ele tem a determinados bancos de dados. No entanto, como a autenticação está desativada, ele irá retornar todos os banco de dados atualmente presentes no sistema sem restrições:

      Output

      admin 0.000GB config 0.000GB local 0.000GB

      Neste exemplo de resultado, apenas os bancos de dados padrão aparecem. Contudo, se houver algum banco de dados que contenha dados confidenciais em seu sistema, qualquer usuário pode encontrá-lo com este comando.

      Como forma de mitigar essa vulnerabilidade, esse passo tem o intuito de adicionar um usuário administrativo. Para fazer isso, é necessário primeiro conectar-se ao banco de dados admin. Lá é onde as informações sobre os usuários, como seus nomes, senhas e funções, são armazenados:

      Output

      switched to db admin

      O MongoDB vem instalado com alguns métodos de shell baseados em JavaScript que você pode usar para gerenciar seu banco de dados. Um deles, o método db.createUser, é usado para criar novos usuários no banco de dados no qual o método é executado.

      Inicie o método db.createUser :

      Esse método requer que seja especificado um nome de usuário e uma senha para o usuário, bem como todas as funções que você queira que o usuário tenha. Lembre-se que o MongoDB armazena seus dados em documentos semelhantes ao JSON. Sendo assim, ao criar um novo usuário, tudo o que você está fazendo é criar um documento para manter os dados de usuário apropriados como campos individuais.

      Assim como com objetos em JSON, os documentos no MongoDB começam e terminam com chaves ({ e }). Para começar a adicionar um usuário, insira uma chave de abertura:

      Nota: o Mongo não irá registrar o método db.createUser como completo até que você digite um parêntese de fechamento. Até que isso seja feito, o prompt irá mudar de um sinal maior que (>) para três pontos (...).

      Em seguida, insira um campo user: com o seu nome de usuário desejado como um valor entre aspas seguido de uma vírgula. O exemplo a seguir especifica o nome de usuário como AdminSammy, mas você pode digitar qualquer nome de usuário que quiser:

      Em seguida, insira um campo pwd com o método passwordPrompt() como valor. Ao executar o método db.createUser, o método passwordPrompt() fornecerá um prompt para que você digite sua senha. Isso é mais seguro do que o método alternativo, que é digitar sua senha em texto não criptografado como você fez para seu nome de usuário.

      Nota: o método passwordPrompt() é compatível apenas com as versões 4.2 ou mais recentes do MongoDB. Se estiver usando uma versão mais antiga do Mongo, então será necessário escrever sua senha em texto não criptografado, da mesma forma como você escreveu seu nome de usuário:

      Certifique-se de também adicionar uma vírgula após este campo:

      Em seguida, digite as funções que você deseja que seu usuário administrativo tenha. Como está criando um usuário administrativo, você deve no mínimo dar a ele a função userAdminAnyDatabase no banco de dados admin. Isso permitirá que o usuário administrativo crie e modifique novos usuários e funções. Como o usuário administrativo possui essa função no banco de dados admin, isso também lhe concederá acesso de superusuário a todo o cluster.

      Além disso, o exemplo a seguir também concede a função readWriteAnyDatabase ao usuário administrativo. Isso faz com que o usuário administrativo seja capaz de ler e modificar dados em qualquer banco de dados presentes no cluster, exceto pelos bancos de dados config e local, que existem principalmente para uso interno:

      • roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ]

      Depois disso, insira uma chave de fechamento para encerrar o documento:

      Em seguida, digite um parêntese de fechamento para fechar e executar o método db.createUser:

      Aqui está como seu método db.createUser deve se parecer, com todos os elementos presentes:

      > db.createUser(
      ... {
      ... user: "AdminSammy",
      ... pwd: passwordPrompt(),
      ... roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ]
      ... }
      ... )
      

      Se a sintaxe de todas as linhas estiver correta, o método será executado corretamente e você será solicitado a inserir uma senha:

      Output

      Enter password:

      Digite uma senha forte da sua escolha. Em seguida, você receberá uma confirmação de que o usuário foi adicionado:

      Output

      Successfully added user: { "user" : "AdminSammy", "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" }, "readWriteAnyDatabase" ] }

      Depois disso, saia do cliente MongoDB:

      Neste ponto, seu usuário será autorizado a inserir credenciais. No entanto, elas não serão necessárias até que você habilite a autenticação e reinicie o daemon do MongoDB.

      Passo 2 — Habilitando a autenticação

      Para habilitar a autenticação, é necessário editar o arquivo de configuração mongod.conf do MongoDB. Depois que você habilitar e reiniciar o serviço Mongo, os usuários ainda serão capazes de se conectar ao banco de dados sem autenticação. No entanto, eles não serão capazes de ler ou modificar dados até que forneçam um nome de usuário e uma senha corretos.

      Abra o arquivo de configuração com seu editor de texto preferido. Aqui, usaremos o nano:

      • sudo nano /etc/mongod.conf

      Role para baixo até encontrar a seção security transformada em comentário:

      /etc/mongod.conf

      . . .
      #security:
      
      #operationProfiling:
      
      . . .
      

      Descomente esta linha removendo a cerquilha (#):

      /etc/mongod.conf

      . . .
      security:
      
      #operationProfiling:
      
      . . .
      

      Em seguida, adicione o parâmetro authorization e defina ele como "enabled" (habilitado). Quando terminar, as linhas devem ficar assim:

      /etc/mongod.conf

      . . .
      security:
        authorization: "enabled"
      . . .
      

      Observe que a linha security: não possui espaços no início, enquanto que a linha authorization: é iniciada com dois espaços.

      Após adicionar essas linhas, salve e feche o arquivo. Se você usou o nano para abrir o arquivo, faça isso pressionando CTRL + X, Y, depois ENTER.

      Em seguida, reinicie o daemon para colocar essas novas alterações em vigor:

      • sudo systemctl restart mongod

      Em seguida, verifique o status do serviço para garantir que ele foi reiniciado corretamente:

      • sudo systemctl status mongod

      Se o comando restart tiver sido bem sucedido, você receberá um resultado que indica que o serviço mongod está ativo e foi iniciado recentemente:

      Output

      ● mongod.service - MongoDB Database Server Loaded: loaded (/lib/systemd/system/mongod.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2020-06-09 22:06:20 UTC; 7s ago Docs: https://docs.mongodb.org/manual Main PID: 15370 (mongod) Memory: 170.1M CGroup: /system.slice/mongod.service └─15370 /usr/bin/mongod --config /etc/mongod.conf Jun 09 22:06:20 your_host systemd[1]: Started MongoDB Database Server.

      Depois de verificar que o daemon está em operação, você pode testar se a configuração de autenticação adicionada funciona como esperado.

      Passo 3 — Testando as configurações de autenticação

      Para começar a testar se os requisitos de autenticação adicionados no passo anterior estão funcionando corretamente, comece fazendo a conexão sem especificar nenhuma credencial para verificar se suas ações estão realmente restritas:

      Agora que a autenticação foi habilitada, nenhum dos avisos que você encontrou anteriormente aparecerá:

      Output

      MongoDB shell version v4.4.0 connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb Implicit session: session { "id" : UUID("5d50ed96-f7e1-493a-b4da-076067b2d898") } MongoDB server version: 4.4.0 >

      Confirme se seu acesso está restrito executando o comando show dbs novamente:

      Lembre-se que no Passo 1 foi mostrado que existem pelo menos alguns bancos de dados padrão em seu servidor. No entanto, neste caso, o comando não mostrará nenhum resultado porque você não autenticou-se como um usuário privilegiado.

      Como esse comando não retorna nenhuma informação, é seguro dizer que a configuração de autenticação está funcionando como esperado. Você também não será capaz de criar usuários ou executar outras tarefas que requerem privilégios sem primeiro autenticar-se.

      Vá em frente e saia do shell do MongoDB:

      Nota: em vez de executar o comando exit a seguir como foi feito anteriormente no Passo 1, uma maneira alternativa de fechar o shell é apenas pressionar CTRL + C.

      Em seguida, certifique-se de que seu usuário administrativo seja capaz de autenticar-se corretamente executando o comando mongo a seguir para se conectar com este usuário. Este comando inclui o sinalizador -u, que precede o nome do usuário com o qual você deseja se conectar. Certifique-se de substituir AdminSammy pelo nome do seu próprio usuário administrativo. O comando também inclui o sinalizador -p, que irá solicitar-lhe a senha do usuário e especifica admin como o banco de dados de autenticação onde o nome de usuário especificado foi criado:

      • mongo -u AdminSammy -p --authenticationDatabase admin

      Digite a senha do usuário quando solicitado, e então você será colocado dentro do shell. Uma vez lá, tente emitir o comando show dbs novamente:

      Desta vez, como você autenticou-se corretamente, o comando retornará com sucesso uma lista de todos os bancos de dados atualmente presentes no servidor:

      Output

      admin 0.000GB config 0.000GB local 0.000GB

      Isso confirma que a autenticação foi habilitada com sucesso.

      Conclusão

      Ao completar este guia, você configurou um usuário administrativo do MongoDB que você pode empregar para criar e modificar novos usuários e funções, e gerenciar sua instância do MongoDB. Você também configurou sua instância do MongoDB para exigir que os usuários autentiquem-se com um nome de usuário e uma senha válidos antes que possam interagir com os dados.

      Para mais informações sobre como gerenciar usuários do MongoDB, confira a documentação oficial sobre o assunto. Você também pode estar interessado em aprender mais sobre como a autenticação funciona no MongoDB.

      Além disso, se você planeja interagir com sua instância do MongoDB remotamente, siga nosso guia Como configurar o acesso remoto para o MongoDB no Ubuntu 20.04.



      Source link

      Cómo configurar y proteger un clúster etcd con Ansible en Ubuntu 18.04


      El autor seleccionó a Wikimedia Foundation para recibir una donación como parte del programa Write for DOnations.

      Introducción

      etcd es un almacén de clave-valor distribuido en el que se basan muchas plataformas y herramientas, como Kubernetes, Vulcand y Doorman. En Kubernetes, etcd se utiliza como una tienda de configuración global que almacena el estado del clúster. Saber administrar etcd es esencial para gestionar clústeres de Kubernetes. Si bien hay muchas ofertas de Kubernetes gestionados, conocidos como Kubernetes como servicio, que eliminan esta carga administrativa, muchas empresas siguen prefiriendo ejecutar clústeres autogestionados de Kubernetes en sus instalaciones debido a la flexibilidad que ofrecen.

      La primera mitad de este artículo lo guiará en el proceso de configuración de un clúster etcd de 3 nodos en servidores de Ubuntu 18.04. La segunda mitad se centrará en la protección del clúster usando Transport Layer Security, o TLS. Para ejecutar cada configuración de forma automatizada, utilizaremos Ansible en todo momento. Ansible es una herramienta de administración de configuración similar a Puppet, Chef y SaltStack que nos permite definir cada paso de configuración de manera declarativa dentro de archivos denominados cuadernos de estrategias.

      Al final de este tutorial, tendrá un clúster etcd de 3 nodos en ejecución en sus servidores. También tendrá un cuaderno de estrategias de Ansible que le permite recrear de manera repetida y consistente la misma configuración en conjuntos de servidores nuevos.

      Requisitos previos

      Para completar esta guía, necesitará lo siguiente:

      Advertencia: Como el propósito de este artículo es proporcionar una introducción a los ajustes de un clúster etcd en una red privada, los tres servidores de Ubuntu 18.04 de esta configuración no se probaron con un firewall y se accedió a ellos con un usuario root. En una configuración de producción, todos los nodos expuestos a una red pública de Internet requerirían un firewall y un usuario sudo para respetar las prácticas recomendadas de seguridad. Para obtener más información, consulte el tutorial Configuración inicial para servidores con Ubuntu 18.04.

      Paso 1: Configurar Ansible para el nodo de control

      Ansible es una herramienta que se utiliza para administrar servidores. Los servidores que administra Ansible se denominan nodos administrados y el equipo que ejecuta Ansible, nodo de control. Ansible utiliza las claves SSH del nodo de control para obtener acceso a los nodos administrados. Una vez que se establezca una sesión SSH, Ansible ejecutará un conjunto de secuencias de comandos para proporcionar y configurar los nodos administrados. En este paso, comprobaremos que podemos usar Ansible para establecer conexión con nodos administrados y ejecutar el comando hostname.

      Un día típico de un administrador de sistemas puede implicar administrar diferentes conjuntos de nodos. Por ejemplo, puede usar Ansible para proporcionar servidores nuevos, pero utilizarlo más adelante para volver a configurar otro conjunto de servidores. Ansible proporciona el concepto de inventario de host (o inventario para abreviar) para permitir a los administradores organizar mejor el conjunto de nodos administrados. Puede definir todos los nodos que desee administrar con Ansible en un archivo de inventario y organizarlos en grupos. Luego, al ejecutar los comandos ansible y ansible-playbook, puede especificar los hosts o los grupos a los que se aplica el comando.

      Ansible lee el archivo de inventario de /etc/ansible/hosts de forma predeterminada; sin embargo, podemos especificar un archivo de inventario diferente utilizando el indicador --inventory (o -i para abreviar).

      Para comenzar, cree un directorio nuevo en su equipo local (el nodo de control) para alojar todos los archivos de este tutorial:

      • mkdir -p $HOME/playground/etcd-ansible

      Luego, ingrese el directorio que acaba de crear:

      • cd $HOME/playground/etcd-ansible

      Dentro del directorio, cree y abra un archivo de inventario en blanco denominado hosts con su editor:

      • nano $HOME/playground/etcd-ansible/hosts

      Dentro del archivo hosts, enumere cada uno de sus nodos administrados con el siguiente formato, sustituyendo las direcciones IP públicas resaltadas por las de sus servidores:

      ~/playground/etcd-ansible/hosts

      [etcd]
      etcd1 ansible_host=etcd1_public_ip  ansible_user=root
      etcd2 ansible_host=etcd2_public_ip  ansible_user=root
      etcd3 ansible_host=etcd3_public_ip  ansible_user=root
      

      La línea [etcd] define un grupo denominado etcd. En la definición del grupo, enumeramos todos nuestros nodos administrados. Cada línea comienza con un alias (por ejemplo, etcd1), lo que nos permite referirnos a cada host usando un nombre fácil de recordar en vez de una dirección IP larga. ansible_host y ansible_user son variables de Ansible. En este caso, se utilizan para proporcionarle a Ansible las direcciones IP públicas y los nombres de usuario de SSH que se deben utilizar para la conexión a través de SSH.

      Para asegurarnos de que Ansible pueda conectarse con nuestros nodos administrados, podemos probar la conectividad al utilizar Ansible para ejecutar el comando hostname en cada uno de los hosts del grupo etcd:

      • ansible etcd -i hosts -m command -a hostname

      Desglosemos este comando para aprender lo que significa cada sección:

      • etcd: especifica el patrón de host que se utiliza para determinar qué hosts del inventario se administran con este comando. Aquí, utilizamos el nombre del grupo como patrón de host.
      • -i hosts: especifica el archivo de inventario que se debe utilizar.
      • -m command: la funcionalidad detrás de Ansible la proporcionan los módulos. El módulo command toma el argumento pasado y lo ejecuta como comando en cada uno de los nodos administrados. Presentaremos algunos módulos más de Ansible más adelante en este tutorial.
      • -a hostname: es el argumento que se pasa al módulo. La cantidad y los tipos de argumentos dependen del módulo.

      Después de ejecutar el comando, obtendrá el siguiente resultado, que indica que Ansible está configurado correctamente:

      Output

      etcd2 | CHANGED | rc=0 >> etcd2 etcd3 | CHANGED | rc=0 >> etcd3 etcd1 | CHANGED | rc=0 >> etcd1

      Los comandos que ejecuta Ansible se denominan tareas. El uso de ansible en la línea de comandos para ejecutar tareas se denomina comandos ad-hoc. La ventaja de los comandos ad-hoc es que son rápidos y requieren poca configuración; la desventaja es que se ejecutan manualmente y, por lo tanto, no pueden utilizarse con sistemas de control de versiones como Git.

      Una leve mejora sería escribir una secuencia de comandos de shell y ejecutar nuestros comandos usando el módulo script de Ansible. Esto nos permitiría registrar los pasos de configuración que tomamos en un control de versiones. Sin embargo, las secuencias de comandos de shell son imperativas, lo que significa que debemos asumir la tarea de determinar los comandos que se deben ejecutar para configurar el sistema de acuerdo al estado deseado (es decir, la manera de hacerlo). Ansible, por otro lado, promueve un enfoque declarativo en el que nosotros definimos el estado deseado que debería tener nuestro servidor en los archivos de configuración y Ansible se encarga de que lo alcance.

      El enfoque declarativo es preferible porque la intención del archivo de configuración se transmite de forma inmediata, por lo que es más fácil de entender y mantener. También atribuye la manipulación de casos de borde a Ansible en lugar de al administrador, lo que nos ahorra mucho trabajo.

      Ahora que configuró el nodo de control de Ansible para establecer conexión con los nodos administrados, en el siguiente paso, le presentaremos los cuadernos de estrategias de Ansible, que permiten especificar tareas de forma declarativa.

      Paso 2: Obtener nombres de los host de nodos administrados con cuadernos de estrategias de Ansible

      En este paso, vamos a replicar lo que hicimos en el Paso 1, imprimir los nombres de host de los nodos administrados, pero, en vez de ejecutar tareas ad-hoc, definiremos todas las tareas de forma declarativa como cuadernos de estrategias de Ansible y las ejecutaremos. El propósito de este paso es demostrar cómo funcionan los cuadernos de estrategias de Ansible; realizaremos tareas mucho más importantes con ellos en pasos posteriores.

      Cree un archivo nuevo denominado playbook.yaml en el directorio de su proyecto con su editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada las siguientes líneas en playbook.yaml:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        tasks:
          - name: "Retrieve hostname"
            command: hostname
            register: output
          - name: "Print hostname"
            debug: var=output.stdout_lines
      

      Cierra y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y.

      El playbook contiene una lista de estrategias; y cada una de ellas contiene una lista de tareas que se deben ejecutar en todos los hosts que coincidan con el patrón de host que especifica la clave hosts. En este cuaderno de estrategias, tenemos una estrategia que contiene dos tareas. La primera tarea ejecuta el comando hostname utilizando el módulo command y registra el resultado en una variable denominada output. En la segunda tarea, usamos el módulo debug para imprimir la propiedad stdout_lines de la variable output.

      Ahora, podemos ejecutar este cuaderno de estrategias utilizando el comando ansible-playbook:

      • ansible-playbook -i hosts playbook.yaml

      Obtendrá el siguiente resultado, que indica que su cuaderno de estrategias está funcionando correctamente:

      Output

      PLAY [etcd] *********************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************ ok: [etcd2] ok: [etcd3] ok: [etcd1] TASK [Retrieve hostname] ********************************************************************************************************** changed: [etcd2] changed: [etcd3] changed: [etcd1] TASK [Print hostname] ************************************************************************************************************* ok: [etcd1] => { "output.stdout_lines": [ "etcd1" ] } ok: [etcd2] => { "output.stdout_lines": [ "etcd2" ] } ok: [etcd3] => { "output.stdout_lines": [ "etcd3" ] } PLAY RECAP ************************************************************************************************************************ etcd1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd2 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd3 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

      Nota: a veces, ansible-playbook utiliza cowsay como manera lúdica de imprimir los encabezamientos. Si encuentra muchos dibujos de una vaca en código ASCII en su terminal, ahora sabe el motivo. Para desactivar esta función, establezca la variable de entorno ANSIBLE_NOCOWS en 1 antes de ejecutar ansible-playbook ejecutando export ANSIBLE_NOCOWS=1 en su shell.

      En este paso, pasamos de ejecutar tareas ad-hoc imperativas a ejecutar cuadernos de estrategias declarativos. En el siguiente paso, sustituiremos estas dos tareas de demostración con tareas que configurarán nuestro clúster etcd.

      Paso 3: Instalar etcd en nodos administrados

      En este paso, le presentaremos los comandos necesarios para instalar etcd de forma manual y le demostraremos cómo convertir esos mismos comandos en tareas de nuestro cuaderno de estrategias de Ansible.

      etcd y su cliente etcdctl están disponibles como binarios, que descargaremos, extraeremos y colocaremos en un directorio que es parte de la variable de entorno PATH. Estos son los pasos que se deben llevar a cabo en cada uno de los nodos administrados al realizar la configuración de forma manual:

      • mkdir -p /opt/etcd/bin
      • cd /opt/etcd/bin
      • wget -qO- https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz | tar --extract --gzip --strip-components=1
      • echo 'export PATH="$PATH:/opt/etcd/bin"' >> ~/.profile
      • echo 'export ETCDCTL_API=3" >> ~/.profile

      Con los primeros cuatro comandos, se descargan los binarios y se extraen en el directorio /opt/etcd/bin/ . El cliente etcdctl usará API v2 para comunicarse con el servidor etcd de manera predeterminada. Como estamos ejecutando etcd v3.x, el último comando establece la variable de entorno ETCDCTL_API en 3.

      Nota: Aquí, utilizamos etcd v3.3.13 en un equipo con procesadores que utilizan el conjunto de instrucciones AMD64. Puede encontrar binarios de otros sistemas y otras versiones en la página oficial de Lanzamientos de GitHub.

      Para replicar los mismos pasos en un formato estandarizado, podemos agregar tareas a nuestro cuaderno de estrategias. Abra el archivo playbook.yaml en su editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Sustituya el archivo playbook.yaml en su totalidad por el siguiente contenido:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
            file:
              path: /opt/etcd/bin
              state: directory
              owner: root
              group: root
              mode: 0700
          - name: "Download the tarball into the /tmp directory"
            get_url:
              url: https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
              dest: /tmp/etcd.tar.gz
              owner: root
              group: root
              mode: 0600
              force: True
          - name: "Extract the contents of the tarball"
            unarchive:
              src: /tmp/etcd.tar.gz
              dest: /opt/etcd/bin/
              owner: root
              group: root
              mode: 0600
              extra_opts:
                - --strip-components=1
              decrypt: True
              remote_src: True
          - name: "Set permissions for etcd"
            file:
              path: /opt/etcd/bin/etcd
              state: file
              owner: root
              group: root
              mode: 0700
          - name: "Set permissions for etcdctl"
            file:
              path: /opt/etcd/bin/etcdctl
              state: file
              owner: root
              group: root
              mode: 0700
          - name: "Add /opt/etcd/bin/ to the $PATH environment variable"
            lineinfile:
              path: /etc/profile
              line: export PATH="$PATH:/opt/etcd/bin"
              state: present
              create: True
              insertafter: EOF
          - name: "Set the ETCDCTL_API environment variable to 3"
            lineinfile:
              path: /etc/profile
              line: export ETCDCTL_API=3
              state: present
              create: True
              insertafter: EOF
      

      Cada tarea utiliza un módulo; en este conjunto de tareas, estamos utilizando los siguientes:

      • file: para crear el directorio /opt/etcd/bin y, más adelante, establecer los permisos del archivo para los binarios etcd y etcdctl.
      • get_url: para descargar el paquete tarball comprimido con gzip en los nodos administrados.
      • unarchive: para extraer y desempaquetar los binarios etcd y etcdctl del paquete tarball comprimido con gzip.
      • lineinfile: para agregar una entrada en el archivo .profile.

      Para aplicar estos cambios, cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el mismo comando ansible-playbook en la terminal:

      • ansible-playbook -i hosts playbook.yaml

      La sección PLAY RECAP del resultado solo mostrará ok y changed:

      Output

      ... PLAY RECAP ************************************************************************************************************************ etcd1 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd2 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd3 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

      Para confirmar si etcd se instaló de forma correcta, ingrese con SSH de forma manual a uno de los nodos administrados y ejecute etcd y etcdctl:

      etcd1_public_ip es la dirección IP pública del servidor denominado etcd1. Una vez que haya obtenido acceso mediante SSH, ejecute etcd --version para imprimir la versión de etcd instalada:

      Obtendrá un resultado similar al siguiente, que indica que el binario etcd está instalado correctamente:

      Output

      etcd Version: 3.3.13 Git SHA: 98d3084 Go Version: go1.10.8 Go OS/Arch: linux/amd64

      Para confirmar que etcdctl se haya instalado correctamente, ejecute la versión etcdctl:

      Verá un resultado similar al siguiente:

      Output

      etcdctl version: 3.3.13 API version: 3.3

      Tenga en cuenta que el resultado dice API version: 3.3, lo que también confirma que nuestra variable de entorno ETCDCTL_API se estableció correctamente.

      Salga del servidor etcd1 para regresar a su entorno local.

      Con esto, instalamos etcd y etcdctl en todos nuestros nodos administrados correctamente. En el siguiente paso, añadiremos más tareas a nuestra estrategia para ejecutar etcd como servicio en segundo plano.

      Paso 4: Crear un archivo de unidad para etcd

      La manera más rápida de ejecutar etcd con Ansible parece ser ejecutar /opt/etcd/bin/etcd con el módulo command. Sin embargo, no funcionará, porque hará que etcd se ejecute como proceso en primer plano. El uso del módulo command hará que Ansible no responda mientras espera que se devuelva el comando etcd, lo que no sucederá en ningún momento. Por lo tanto, en este paso, vamos a actualizar nuestro cuaderno de estrategias para ejecutar nuestro binario etcd como servicio en segundo plano en su lugar.

      Ubuntu 18.04 utiliza systemd como sistema init, lo que significa que podemos crear servicios nuevos al escribir archivos de unidades y colocarlos en el directorio /etc/systemd/system/.

      Primero, cree un directorio nuevo denominado files/ en el directorio de nuestro proyecto:

      A continuación, cree un archivo nuevo denominado etcd.service en ese directorio con su editor:

      Luego, copie el siguiente bloque de código en el archivo files/etcd.service:

      ~/playground/etcd-ansible/files/etcd.service

      [Unit]
      Description=etcd distributed reliable key-value store
      
      [Service]
      Type=notify
      ExecStart=/opt/etcd/bin/etcd
      Restart=always
      

      Este archivo de unidad define un servicio que ejecuta el ejecutable en /opt/etcd/bin/etcd, notifica a systemd cuando termina de inicializarse y, si sale en algún momento, se reinicia.

      Nota: Si desea obtener más información sobre los archivos de unidades y systemd o adaptar el archivo de unidad a sus necesidades, consulte la guía Información sobre unidades y archivos de unidades de Systemd.

      Cierre y guarde el archivo files/etcd.service presionando CTRL+X y, luego, Y.

      A continuación, debemos agregar una tarea en nuestro cuaderno de estrategias para copiar el archivo local files/etcd.service al directorio /etc/systemd/system/etcd.service de cada nodo administrado. Podemos hacerlo usando el módulo copy.

      Abra su cuaderno de estrategias:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada la siguiente tarea resaltada al final de nuestras tareas existentes:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Set the ETCDCTL_API environment variable to 3"
            lineinfile:
              path: /etc/profile
              line: export ETCDCTL_API=3
              state: present
              create: True
              insertafter: EOF
          - name: "Create a etcd service"
            copy:
              src: files/etcd.service
              remote_src: False
              dest: /etc/systemd/system/etcd.service
              owner: root
              group: root
              mode: 0644
      

      Al copiar el archivo de unidad en /etc/systemd/system/etcd.service, definimos un servicio.

      Guarde y cierre el cuaderno de estrategias.

      Vuelva a ejecutar el mismo comando ansible-playbook para aplicar los cambios nuevos:

      • ansible-playbook -i hosts playbook.yaml

      Para confirmar que los cambios se hayan aplicado, primero, ingrese mediante SSH a uno de los nodos administrados:

      A continuación, ejecute systemctl status etcd para consultar a systemd el estado del servicio etcd:

      Obtendrá el siguiente resultado, que indica que el servicio está cargado:

      Output

      ● etcd.service - etcd distributed reliable key-value store Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled) Active: inactive (dead) ...

      Nota: La última línea (Active: inactive (dead)) del resultado indica que el servicio está inactivo, lo que significa que no se ejecutará de forma automática cuando el sistema se inicie. Esto es de esperar, no es un error.

      Presione q para regresar al shell y, luego, ejecute exit para salir del nodo administrado y volver a su shell local:

      En este paso, actualizamos nuestro cuaderno de estrategias para ejecutar el binario etcd como servicio de systemd. En el siguiente paso, continuaremos configurando etcd al proporcionarle espacio para almacenar sus datos.

      Paso 5: Configurar el directorio de datos

      etcd es un almacén de datos de valor clave, lo que significa que debemos proporcionarle espacio para almacenar sus datos. En este paso, vamos a actualizar nuestro cuaderno de estrategias para definir un directorio de datos específico para etcd.

      Abra su cuaderno de estrategias:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada la siguiente tarea al final de la lista de tareas:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create a etcd service"
            copy:
              src: files/etcd.service
              remote_src: False
              dest: /etc/systemd/system/etcd.service
              owner: root
              group: root
              mode: 0644
          - name: "Create a data directory"
            file:
              path: /var/lib/etcd/{{ inventory_hostname }}.etcd
              state: directory
              owner: root
              group: root
              mode: 0755
      

      En este caso, utilizamos /var/lib/etcd/hostname.etcd como directorio de datos, donde hostname es el nombre de host del nodo administrado actual. inventory_hostname es una variable que representa el nombre de host del nodo administrado actual; Ansible completa su valor de forma automática. La sintaxis de llaves (es decir, {{ inventory_hostname }}) se utiliza para sustituir variables con el motor de plantillas Jinja2, que es el motor predeterminado de Ansible.

      Cierre el editor de texto y guarde el archivo.

      A continuación, debemos indicarle a etcd que utilice este directorio de datos. Lo haremos pasando el parámetro data-dir a etcd. Para establecer los parámetros de etcd, podemos utilizar una combinación de variables de entorno, indicadores de la línea de comandos y archivos de configuración. En este tutorial, utilizaremos un archivo de configuración, dado que es mucho más fácil aislar todas las configuraciones en un archivo en lugar de tenerlas esparcidas por todo nuestro cuaderno de estrategias.

      Cree un directorio nuevo denominado templates/ en el directorio de su proyecto:

      A continuación, cree un archivo nuevo denominado etcd.conf.yaml.j2 en ese directorio con su editor:

      • nano templates/etcd.conf.yaml.j2

      Luego, copie la siguiente línea y péguela en el archivo:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      

      Este archivo utiliza la misma sintaxis de sustitución de variables de Jinja2 que nuestro cuaderno de estrategias. Podemos usar el módulo template para sustituir las variables y cargar el resultado en cada host administrado. Funciona de manera similar a copy, con la excepción de que realiza la sustitución de variables antes de la carga.

      Salga de etcd.conf.yaml.j2 y abra su cuaderno de estrategias:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada las siguientes tareas a la lista de tareas para crear un directorio y cargar el archivo de configuración basado en una plantilla en su interior:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create a data directory"
            file:
              ...
              mode: 0755
          - name: "Create directory for etcd configuration"
            file:
              path: /etc/etcd
              state: directory
              owner: root
              group: root
              mode: 0755
          - name: "Create configuration file for etcd"
            template:
              src: templates/etcd.conf.yaml.j2
              dest: /etc/etcd/etcd.conf.yaml
              owner: root
              group: root
              mode: 0600
      

      Guarde y cierre este archivo.

      Como realizamos este cambio, debemos actualizar el archivo de unidad de nuestro servicio para que pase la ubicación de nuestro archivo de configuración (es decir, /etc/etcd/etcd.conf.yaml).

      Abra el archivo del servicio etcd en su equipo local:

      Actualice el archivo files/etcd.service añadiendo el indicador --config-file resaltado en lo siguiente:

      ~/playground/etcd-ansible/files/etcd.service

      [Unit]
      Description=etcd distributed reliable key-value store
      
      [Service]
      Type=notify
      ExecStart=/opt/etcd/bin/etcd --config-file /etc/etcd/etcd.conf.yaml
      Restart=always
      

      Guarde y cierre este archivo.

      En este paso, utilizamos nuestro cuaderno de estrategias para proporcionar un directorio de datos para que etcd almacene sus datos. En el siguiente paso, añadiremos algunas tareas adicionales para reiniciar el servicio etcd y hacer que se ejecute en el inicio.

      Paso 6: Habilitar e iniciar el servicio etcd

      Siempre que realizamos cambios en el archivo de unidad de un servicio, debemos reiniciarlo para que surtan efecto. Podemos hacerlo ejecutando el comando systemctl restart etcd. Además, para que el servicio etcd se inicie de forma automática en el inicio del sistema, debemos ejecutar systemctl enabled etcd. En este paso, ejecutaremos esos dos comandos utilizando el cuaderno de estrategias.

      Podemos usar el módulo command para ejecutar comandos:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada las siguientes tareas al final de la lista de tareas:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Create configuration file for etcd"
            template:
              ...
              mode: 0600
          - name: "Enable the etcd service"
            command: systemctl enable etcd
          - name: "Start the etcd service"
            command: systemctl restart etcd
      

      Guarde y cierre el archivo.

      Vuelva a ejecutar ansible-playbook -i hosts playbook.yaml una vez más:

      • ansible-playbook -i hosts playbook.yaml

      Para verificar que el servicio etcd se haya reiniciado y habilitado, ingrese mediante SSH a uno de los nodos administrados:

      A continuación, ejecute systemctl status etcd para consultar el estado del servicio etcd:

      Encontrará enabled y active (running), como indica lo resaltado en el siguiente ejemplo, lo que significa que se implementaron los cambios que hicimos en nuestro cuaderno de estrategias:

      Output

      ● etcd.service - etcd distributed reliable key-value store Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled) Active: active (running) Main PID: 19085 (etcd) Tasks: 11 (limit: 2362)

      En este paso, utilizamos el módulo command para ejecutar comandos systemctl para reiniciar y habilitar el servicio etcd en nuestros nodos administrados. Ahora que configuramos una instalación de etcd, en el siguiente paso, vamos a probar su funcionalidad al llevar a cabo algunas operaciones básicas de creación, lectura, actualización y eliminación (CRUD).

      Paso 7: Probar etcd

      Si bien tenemos una instalación de etcd en funcionamiento, no es segura ni está listo para usarse en producción. Pero antes de proteger nuestra configuración de etcd en pasos posteriores, primero, vamos comprender lo que puede hacer etcd en términos de funcionalidad. En este paso, vamos a enviar solicitudes a etcd de forma manual para añadir, recuperar, actualizar y eliminar sus datos.

      Por defecto, etcd expone una API que escucha en el puerto 2379 para la comunicación con el cliente. Esto significa que podemos enviar solicitudes de API sin procesar a etcd utilizando un cliente HTTP. Sin embargo, es más rápido usar el cliente oficial de etcd, etcdctl, que permite crear/actualizar, recuperar y eliminar pares clave-valor usando los subcomandos put, get y del respectivamente.

      Asegúrese de seguir estando en el nodo administrado etcd1 y ejecute los siguientes comandos de etcdctl para confirmar que su instalación de etcd esté funcionando.

      Primero, cree una nueva entrada utilizando el subcomando put.

      El subcomando put tiene la siguiente sintaxis:

      etcdctl put key value
      

      En etcd1, ejecute el siguiente comando:

      El comando que acabamos de ejecutar le indica a etcd que escriba el valor "bar" en la clave foo del almacén.

      A continuación, encontrará OK impreso en el resultado, lo que indica que los datos se mantuvieron:

      Output

      OK

      Luego, podemos recuperar esta entrada utilizando el subcomando get, que tiene la sintaxis etcdctl get key:

      Obtendrá este resultado, que muestra la clave en la primera línea y el valor que insertó anteriormente en la segunda:

      Output

      foo bar

      Podemos eliminar la entrada utilizando el subcomando del, que tiene la sintaxis etcdctl del key:

      Obtendrá el siguiente resultado, que indica la cantidad de entradas eliminadas:

      Output

      1

      Ahora, vamos a ejecutar el subcomando get una vez más para intentar recuperar un par clave-valor eliminado:

      No obtendrá ningún resultado, lo que significa que etcdctl no puede recuperar el par clave-valor. Esto confirma que las entradas eliminadas no pueden recuperarse.

      Ahora que probó las operaciones básicas de etcd y etcdctl, salga del nodo administrado y regrese a su entorno local:

      En este paso, utilizamos el cliente etcdctl para enviar solicitudes a etcd. En este punto, estamos ejecutando tres instancias separadas de etcd que actúan de forma independiente unas de otras. Sin embargo, etcd se diseñó como un almacén de clave-valor distribuido, lo que significa que se pueden agrupar varias instancias de etcd para formar un clúster único; luego, cada instancia se convierte en miembro del clúster. Después de crear un clúster, podrá recuperar pares clave-valor insertados desde distintos miembro del clúster. En el siguiente paso, utilizaremos nuestro playbook para transformar nuestros tres clústeres de un solo nodo en un clúster único de tres nodos.

      Paso 8: Crear un clúster utilizando descubrimiento estático

      Para crear un clúster de tres nodos en lugar de tres de un nodo, debemos configurar estas instalaciones de etcd para que se comuniquen entre sí. Esto significa que cada una de ellas debe conocer las direcciones IP de las demás. Este proceso se denomina descubrimiento. El descubrimiento se puede realizar utilizando una configuración estática o un descubrimiento de servicio dinámico. En este paso, analizaremos la diferencia entre ambas y, también, actualizaremos nuestro cuaderno de estrategias para configurar un clúster de etcd con descubrimiento estático.

      El descubrimiento mediante configuración estática es el método que requiere menos configuración; los puntos de conexión de cada miembro se pasan al comando etcd antes de su ejecución. Para usar la configuración estática, se deben cumplir las siguientes condiciones antes de la inicialización del clúster:

      • se debe conocer la cantidad de miembros
      • se deben conocer los puntos de conexión de cada miembro
      • las direcciones IP de todos los puntos de conexión deben ser estáticas

      Si no se pueden cumplir estas condiciones, puede usar un servicio de descubrimiento dinámico. Con el descubrimiento de servicios dinámicos, todas las instancias se registran en el servicio de descubrimiento, lo que permite a cada miembro recuperar información sobre la ubicación de los demás.

      Como sabemos que queremos tener un clúster de etcd de tres nodos y todos nuestros servidores tienen direcciones IP estáticas, utilizaremos el descubrimiento estático. Para iniciar nuestro clúster utilizando descubrimiento estático, debemos agregar varios parámetros a nuestro archivo de configuración. Utilice un editor para abrir el archivo de plantilla templates/etcd.conf.yaml.j2 :

      • nano templates/etcd.conf.yaml.j2

      A continuación, añada las siguientes líneas resaltadas:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      name: {{ inventory_hostname }}
      initial-advertise-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
      listen-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,http://127.0.0.1:2380
      advertise-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
      listen-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,http://127.0.0.1:2379
      initial-cluster-state: new
      initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=http://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}
      

      Cierre y guarde el archivo templates/etcd.conf.yaml.j2  presionando CTRL+X y, luego, Y.

      A continuación, presentamos una breve explicación de cada parámetro:

      • name: el nombre del miembro en lenguaje natural. Por defecto, etcd utiliza un identificador único generado al azar para identificar cada miembro; sin embargo, un nombre en lenguaje natural nos permite referirnos a ellos con mayor facilidad tanto en los archivos de configuración como en la línea de comandos. Aquí, utilizaremos los nombres de host como nombres de los miembros (es decir, etcd1, etcd2 y etcd3).
      • initial-advertise-peer-urls: una lista de combinaciones de direcciones IP y puertos que pueden usar otros miembros para comunicarse con este. Además del puerto de la API (2379) de etcd también expone el puerto 2380 para la comunicación de punto a punto entre los miembros de etcd, lo que les permite enviar mensajes e intercambiar datos. Tenga en cuenta que estas URL deben poder ser accesibles para sus pares (y no deben ser una dirección IP local).
      • listen-peer-urls: una lista de combinaciones de direcciones IP y puertos que el miembro actual utilizará para escuchar comunicaciones de otros miembros. Debe incluir todas las URL del indicador --initial-advertise-peer-urls, así como las URL locales, como 127.0.0.1:2380. La dirección IP y el puerto de destino de los mensajes entrantes de pares deben coincidir con una de las URL que se enumeran aquí.
      • advertise-client-urls: una lista de combinaciones de direcciones IP y puertos que deben usar los clientes para comunicarse con este miembro. El cliente debe poder acceder a estas URL (y no deben ser una dirección local). Si el cliente está accediendo al clúster a través de una red pública de Internet, la dirección IP debe ser pública.
      • listen-client-urls: una lista de combinaciones de direcciones IP y puertos que el miembro actual utilizará para escuchar comunicaciones de los clientes. Debe incluir todas las URL del indicador --advertise-client-urls, así como las URL locales, como 127.0.0.1:2379. La dirección IP y el puerto de destino de los mensajes entrantes de clientes deben coincidir con una de las URL que se enumeran aquí.
      • initial-cluster: una lista de los puntos de conexión de cada miembro del clúster. Cada punto de conexión debe coincidir con una de las URL de initial-advertise-peer-urls del miembro correspondiente.
      • initial-cluster-state: puede ser new o existing.

      Para asegurar consistencia, etcd solo puede tomar decisiones cuando la mayoría de los nodos tienen un estado correcto. Esto se conoce como establecimiento de quorum. En otras palabras, en un clúster de tres miembros, hay quorum cuando dos o más de ellos tienen un estado correcto.

      Si el parámetro initial-cluster-state se establece en new, etcd sabrá que se trata de un clúster nuevo que se está iniciando y permitirá que los miembros se inicien en paralelo sin esperar a que se alcance el quorum. Más concretamente, una vez que se inicie el primer miembro, no habrá quorum porque un tercio (33,33 %) es menor o igual que el 50 %. Normalmente, etcd se detendría y se negaría a realizará más acciones, por lo que el clúster nunca se crearía. Sin embargo, con initial-cluster-state establecido en new, ignorará la falta de quorum inicial.

      Si se establece en existing, el miembro intentará unirse a un clúster existente, y esperará que el quorum ya esté establecido.

      Nota: Puede obtener más información sobre todos los indicadores de configuración compatibles en la sección Configuración de la documentación de etcd.

      En el archivo de plantilla templates/etcd.conf.yaml.j2 actualizado, hay algunas instancias de hostvars. Cuando Ansible se ejecute, recopilará variables de diversas fuentes. Ya hemos utilizado la variable inventory_hostname, pero hay mucho más disponible. Estas variables están disponibles en hostvars[inventory_hostname]['ansible_facts']. Aquí, estamos extrayendo las direcciones IP privadas de cada nodo y las estamos utilizando para crear el valor de nuestro parámetro.

      Nota: Como habilitamos la opción Redes privadas cuando creamos nuestros servidores, habrá tres direcciones IP asociadas a cada servidor:

      • Una dirección IP de bucle invertido: una dirección que solo es válida dentro del mismo equipo. Se utiliza para que el equipo haga referencia a sí mismo, por ejemplo, 127.0.0.1
      • Una dirección IP pública: una dirección que se puede enrutar mediante una red pública de Internet, por ejemplo, 178.128.169.51
      • Una dirección IP privada: una dirección que solo se puede enrutar mediante una red privada; en el caso de Droplets de DigitalOcean, hay una red privada en cada centro de datos, por ejemplo, 10.131.82.225

      Cada una de estas direcciones IP está asociada con una interfaz de red distinta: la dirección de bucle invertido se asocia con la interfaz lo, la dirección IP pública se asocia con la interfaz eth0 y la dirección IP privada con la interfaz eth1. Estamos utilizando la interfaz eth1 para que todo el tráfico se mantenga en la red privada, sin llegar a Internet.

      Para los fines de este artículo, no es necesario tener conocimientos sobre las interfaces de red, pero, si desea obtener más información, el artículo Introducción a terminología de redes, interfaces y protocolos es un buen punto de partida.

      La sintaxis {% %} de Jinja2 define la estructura de bucle for que se itera a través de cada nodo del grupo etcd para crear la cadena initial-cluster en un formato requerido por etcd.

      Para crear el clúster nuevo de tres miembros, debe detener el servicio etcd y borrar el directorio de datos antes de iniciar el clúster. Para hacerlo, utilice un editor para abrir el archivo playbook.yaml en su equipo local:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Luego, antes de la tarea "Create a data directory", añada una tarea para detener el servicio etcd:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
              group: root
              mode: 0644
          - name: "Stop the etcd service"
            command: systemctl stop etcd
          - name: "Create a data directory"
            file:
          ...
      

      A continuación, actualice la tarea "Create a data directory" para eliminar el directorio de datos y recrearlo:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        become: True
        tasks:
          ...
          - name: "Stop the etcd service"
            command: systemctl stop etcd
          - name: "Create a data directory"
            file:
              path: /var/lib/etcd/{{ inventory_hostname }}.etcd
              state: "{{ item }}"
              owner: root
              group: root
              mode: 0755
            with_items:
              - absent
              - directory
          - name: "Create directory for etcd configuration"
            file:
          ...
      

      La propiedad with_items define una lista de cadenas sobre la cual iterará en esta tarea. Es equivalente a repetir la misma tarea dos veces, pero con valores diferentes para la propiedad state. Aquí, estamos iterando sobre la lista con elementos absent y directory, lo que garantiza que el directorio de datos se elimine primero y, luego, se vuelva a crear.

      Cierra y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar ansible-playbook. Ahora, Ansible creará un clúster de etcd único, de 3 miembros:

      • ansible-playbook -i hosts playbook.yaml

      Puede verificarlo al ingresar mediante SSH en cualquier nodo miembro de etcd:

      A continuación, ejecute etcdctl endpoint health --cluster:

      • etcdctl endpoint health --cluster

      Esto enumerará el estado de cada miembro del clúster:

      Output

      http://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 2.517267ms http://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 2.153612ms http://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 2.639277ms

      Ahora, hemos creado correctamente un clúster de etcd de tres nodos. Podemos confirmar esto añadiendo una entrada a etcd en un nodo miembro y recuperándola de otro. En uno de los nodos miembro, ejecute etcdctl put:

      A continuación, utilice una terminal nueva para ingresar mediante SSH en un nodo miembro diferente:

      Luego, intente recuperar la misma entrada usando la clave:

      Podrá recuperar la entrada, lo que demuestra que el clúster está funcionando:

      Output

      foo bar

      Por último, salga de todos los nodos administrados y regrese a su equipo local:

      En este paso, proporcionamos un clúster de tres nodos. En este momento, las comunicaciones entre los miembros de etcd y sus pares y clientes se llevan a cabo mediante HTTP. Esto significa que la comunicación no está cifrada y cualquier parte que pueda interceptar el tráfico puede leer los mensajes. Esto no es un problema importante si el clúster y los clientes de etcd están implementados en una red privada o una red privada virtual (VPN) que controle por completo. Sin embargo, si algún tráfico debe transmitirse a través de una red compartida (privada o pública), deberá asegurarse de que ese tráfico esté cifrado. Además, se debe establecer un mecanismo para que un cliente o par compruebe la autenticidad del servidor.

      En el siguiente paso, veremos cómo proteger las comunicaciones de cliente a servidor y las de punto a punto utilizando TLS.

      Paso 9: Obtener direcciones IP privadas de nodos administrados

      Para cifrar mensajes entre nodos miembros, etcd utiliza el Protocolo seguro de transferencia de hipertexto, o HTTPS, que es una capa por encima de la Seguridad de la capa de transporte, o TLS. TLS utiliza un sistema de claves privadas, certificados y entidades de confianza denominadas Entidades de certificación (CA) para autenticarse y enviar mensajes cifrados.

      En este tutorial, cada nodo miembro debe generar un certificado para identificarse y este debe estar firmado por una CA. Configuraremos todos los nodos miembros para que confíen en esta CA y, por lo tanto, también en cualquier certificado firmado por ella. Esto les permite a los nodos miembro autenticarse mutuamente.

      El certificado que genere cada nodo miembro debe permitir que los demás lo identifiquen. Todos los certificados incluyen el Nombre común (CN) de la entidad a la que están asociados. Esto se suele utilizar como la identidad de la entidad. Sin embargo, al verificar un certificado, las implementaciones de los clientes pueden comparar si la información recopilada sobre la entidad concuerda con la proporcionada en el certificado. Por ejemplo, si un cliente descarga un certificado TLS con firmante CN=foo.bar.com, pero, en realidad, se está conectando al servidor usando una dirección IP (por ejemplo, 167.71.129.110), hay una incongruencia y es posible que el cliente no confíe en el certificado. Al especificar un nombre alternativo del firmante (SAN) en el certificado, le informa que ambos nombres pertenecen a la misma entidad.

      Como nuestros miembros de etcd se emparejan utilizando sus direcciones IP privadas, al definir nuestros certificados, tendremos que proporcionar estas direcciones IP privadas como nombres alternativos del firmante.

      Para obtener la dirección IP privada de un nodo administrado, ingrese a él mediante SSH:

      Luego, ejecute el siguiente comando:

      • ip -f inet addr show eth1

      Verá un resultado similar a las siguientes líneas:

      Output

      3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 inet 10.131.255.176/16 brd 10.131.255.255 scope global eth1 valid_lft forever preferred_lft forever

      En nuestro resultado ejemplo, 10.131.255.176 es la dirección IP privada del nodo administrado, y la única información que nos interesa. Para filtrar todo lo que no corresponde a la IP privada, podemos canalizar el resultado del comando ip a la utilidad sed, que se utiliza para filtrar y transformar texto.

      • ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'

      Ahora, el único resultado es la dirección IP privada:

      Output

      10.131.255.176

      Una vez que esté satisfecho con el funcionamiento del comando anterior, salga del nodo administrado:

      Para incorporar los comandos anteriores a nuestro playbook, abra el archivo playbook.yaml:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      A continuación, añada una nueva estrategia con una sola tarea antes de nuestra estrategia existente:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: etcd
        tasks:
          - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'
            register: privateIP
      - hosts: etcd
        tasks:
      ...
      

      La tarea utiliza el módulo shell para ejecutar los comandos ip y sed, con lo cual se obtiene la dirección IP privada del nodo administrado. A continuación, registra el valor que devolvió el comando shell dentro de una variable denominada privateIP, que utilizaremos más adelante.

      En este paso, añadimos una tarea al cuaderno de estrategias para obtener la dirección IP privada de los nodos administrados. En el siguiente paso, vamos a utilizar esta información para generar certificados para cada nodo miembro que firmaremos con una Entidad de certificación (CA).

      Paso 10: Generar claves privadas y CSR para los miembros de etcd

      Para que un nodo miembro pueda recibir tráfico cifrado, el remitente debe utilizar la clave pública del nodo miembro para cifrar los datos y el nodo miembro debe usar su clave privada para descifrar el texto cifrado y obtener los datos originales. La clave pública se empaqueta en un certificado y la firma una CA para garantizar que sea auténtica.

      Por lo tanto, debemos generar una clave privada y una solicitud de firma de certificado (CSR) para cada nodo miembro de etcd. Para facilitar la tarea, generaremos todos los pares de claves y firmaremos todos los certificados de forma local, en el nodo de control y, luego, copiaremos los archivos pertinentes a los hosts administrados.

      Primero, cree un directorio denominado artifacts/, donde colocaremos los archivos (claves y certificados) que se generarán durante el proceso. Abra el archivo playbook.yaml con un editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Utilice el módulo file para crear el directorio artifacts/ en su interior:

      ~/playground/etcd-ansible/playbook.yaml

      ...
          - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/1/p'
            register: privateIP
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
          - name: "Create ./artifacts directory to house keys and certificates"
            file:
              path: ./artifacts
              state: directory
      - hosts: etcd
        tasks:
      ...
      

      A continuación, añada otra tarea al final de la estrategia para generar la clave privada:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
              ...
          - name: "Generate private key for each member"
            openssl_privatekey:
              path: ./artifacts/{{item}}.key
              type: RSA
              size: 4096
              state: present
              force: True
            with_items: "{{ groups['etcd'] }}"
      - hosts: etcd
        tasks:
      ...
      

      La creación de claves privadas y CSR se puede llevar a cabo utilizando los módulos openssl_privatekey y openssl_csr respectivamente.

      El atributo force: True garantiza que siempre se genere una clave privada, incluso si ya existe.

      Del mismo modo, añada la siguiente tarea nueva al misma estrategia para generar las CSR para cada miembro utilizando el módulo openssl_csr:

      ~/playground/etcd-ansible/playbook.yaml

      ...
      - hosts: localhost
        gather_facts: False
        become: False
        tasks:
          ...
          - name: "Generate private key for each member"
            openssl_privatekey:
              ...
            with_items: "{{ groups['etcd'] }}"
          - name: "Generate CSR for each member"
            openssl_csr:
              path: ./artifacts/{{item}}.csr
              privatekey_path: ./artifacts/{{item}}.key
              common_name: "{{item}}"
              key_usage:
                - digitalSignature
              extended_key_usage:
                - serverAuth
              subject_alt_name:
                - IP:{{ hostvars[item]['privateIP']['stdout']}}
                - IP:127.0.0.1
              force: True
            with_items: "{{ groups['etcd'] }}"
      

      Estamos especificando que este certificado puede estar involucrado en un mecanismo de firma digital para la autenticación del servidor. Este certificado se asocia con el nombre de host (por ejemplo, etcd1), pero el verificador debe tratar las direcciones IP de bucle inverso privadas y locales de cada nodo como nombres alternativos. Tenga en cuenta que estamos utilizando la variable privateIP que registramos en la estrategia anterior.

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el playbook:

      • ansible-playbook -i hosts playbook.yaml

      Ahora, encontraremos un nuevo directorio denominado artifacts en el directorio de nuestro proyecto; usaremos ls para enumerar su contenido:

      Encontrará las claves privadas y las CSR de cada miembro de etcd:

      Output

      etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

      En este paso, usamos varios módulos de Ansible para generar claves privadas y certificados de clave pública para cada uno de los nodos miembro. En el siguiente paso, veremos cómo firmar una solicitud de firma de certificado (CSR).

      Paso 11: Generar certificados de CA

      Dentro de un clúster de etcd, los nodos miembro cifran los mensajes usando la clave pública del receptor. Para garantizar que la clave pública sea genuina, el receptor empaqueta la clave pública en una solicitud de firma de certificado (CSR) y hace que una entidad de confianza (es decir, la CA) lo firme. Como controlamos todos los nodos miembro y las CA en las que confían, no necesitamos usar una CA externa y podemos actuar como nuestra propia CA. En este paso, vamos a actuar como nuestra propia CA, lo que significa que debemos generar una clave privada y un certificado autofirmado para que funcione como CA.

      Primero, abra el archivo playbook.yaml con su editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      A continuación, al igual que en el paso anterior, añada una tarea a la estrategia localhost para generar una clave privada para la CA:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
        - name: "Generate CSR for each member"
          ...
          with_items: "{{ groups['etcd'] }}"
          - name: "Generate private key for CA"
            openssl_privatekey:
              path: ./artifacts/ca.key
              type: RSA
              size: 4096
              state: present
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      A continuación, utilice el módulo openssl_csr para generar una CSR nueva. Esto es similar a lo que hicimos en el paso anterior, con la diferencia de que, en esta CSR, estamos añadiendo la extensión de uso de la clave y la restricción básica para indicar que este certificado puede utilizarse como certificado de CA:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate private key for CA"
            openssl_privatekey:
              path: ./artifacts/ca.key
              type: RSA
              size: 4096
              state: present
              force: True
          - name: "Generate CSR for CA"
            openssl_csr:
              path: ./artifacts/ca.csr
              privatekey_path: ./artifacts/ca.key
              common_name: ca
              organization_name: "Etcd CA"
              basic_constraints:
                - CA:TRUE
                - pathlen:1
              basic_constraints_critical: True
              key_usage:
                - keyCertSign
                - digitalSignature
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Por último, utilice el módulo openssl_certificate para autofirmar la CSR:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate CSR for CA"
            openssl_csr:
              path: ./artifacts/ca.csr
              privatekey_path: ./artifacts/ca.key
              common_name: ca
              organization_name: "Etcd CA"
              basic_constraints:
                - CA:TRUE
                - pathlen:1
              basic_constraints_critical: True
              key_usage:
                - keyCertSign
                - digitalSignature
              force: True
          - name: "Generate self-signed CA certificate"
            openssl_certificate:
              path: ./artifacts/ca.crt
              privatekey_path: ./artifacts/ca.key
              csr_path: ./artifacts/ca.csr
              provider: selfsigned
              force: True
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el playbook para aplicar los cambios:

      • ansible-playbook -i hosts playbook.yaml

      También puede ejecutar ls para verificar el contenido del directorio artifacts/ :

      Ahora, encontrará el certificado de CA generado recientemente (ca.crt):

      Output

      ca.crt ca.csr ca.key etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

      En este paso, generamos una clave privada y un certificado autofirmado para la CA. En el siguiente paso, usaremos el certificado de CA para firmar la CSR de cada miembro.

      Paso 12: Firmar las CSR de los miembros de etcd

      En este paso, vamos a firmar la CSR de cada nodo miembro. Lo haremos de forma similar a la manera en que usamos el módulo openssl_certificate para firmar el certificado de CA, pero, en lugar de usar el proveedor self-signed, utilizaremos el proveedor ownca, que nos permite firmar con nuestro propio certificado de CA.

      Abra su cuaderno de estrategias:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Añada la siguiente tarea resaltada a la tarea "Generate self-signed CA certificate":

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: localhost
        ...
        tasks:
          ...
          - name: "Generate self-signed CA certificate"
            openssl_certificate:
              path: ./artifacts/ca.crt
              privatekey_path: ./artifacts/ca.key
              csr_path: ./artifacts/ca.csr
              provider: selfsigned
              force: True
          - name: "Generate an `etcd` member certificate signed with our own CA certificate"
            openssl_certificate:
              path: ./artifacts/{{item}}.crt
              csr_path: ./artifacts/{{item}}.csr
              ownca_path: ./artifacts/ca.crt
              ownca_privatekey_path: ./artifacts/ca.key
              provider: ownca
              force: True
            with_items: "{{ groups['etcd'] }}"
      - hosts: etcd
        become: True
        tasks:
          - name: "Create directory for etcd binaries"
      ...
      

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el cuaderno de estrategias para aplicar los cambios:

      • ansible-playbook -i hosts playbook.yaml

      Ahora, enumere el contenido del directorio artifacts/:

      Encontrará la clave privada, la CSR y el certificado de cada miembro de etcd y la CA:

      Output

      ca.crt ca.csr ca.key etcd1.crt etcd1.csr etcd1.key etcd2.crt etcd2.csr etcd2.key etcd3.crt etcd3.csr etcd3.key

      En este paso, firmamos las CSR de cada nodo miembro usando la clave de la CA. En el siguiente paso, vamos a copiar los archivos pertinentes en cada nodo administrado, para que etcd tenga acceso a las claves y los certificados correspondientes para configurar las conexiones de TLS.

      Paso 13: Copiar claves privadas y certificados

      Cada nodo debe tener una copia del certificado de la CA autofirmado (ca.crt). Cada nodo de miembro de etcd también debe tener su clave privada y certificado propios. En este paso, vamos a cargar estos archivos y los colocaremos en un directorio nuevo /etc/etcd/ssl/.

      Para comenzar, abra el archivo playbook.yaml con su editor:

      • nano $HOME/playground/etcd-ansible/playbook.yaml

      Para realizar estos cambios en nuestro playbook de Ansible, primero, actualice la propiedad path de la tarea Create directory for etcd configuration para crear el directorio /etc/etcd/ssl/:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        ...
        tasks:
          ...
            with_items:
              - absent
              - directory
          - name: "Create directory for etcd configuration"
            file:
              path: "{{ item }}"
              state: directory
              owner: root
              group: root
              mode: 0755
            with_items:
              - /etc/etcd
              - /etc/etcd/ssl
          - name: "Create configuration file for etcd"
            template:
      ...
      

      A continuación, detrás de la tarea modificada, añada tres tareas más para copiar los archivos:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        ...
        tasks:
          ...
          - name: "Copy over the CA certificate"
            copy:
              src: ./artifacts/ca.crt
              remote_src: False
              dest: /etc/etcd/ssl/ca.crt
              owner: root
              group: root
              mode: 0644
          - name: "Copy over the `etcd` member certificate"
            copy:
              src: ./artifacts/{{inventory_hostname}}.crt
              remote_src: False
              dest: /etc/etcd/ssl/server.crt
              owner: root
              group: root
              mode: 0644
          - name: "Copy over the `etcd` member key"
            copy:
              src: ./artifacts/{{inventory_hostname}}.key
              remote_src: False
              dest: /etc/etcd/ssl/server.key
              owner: root
              group: root
              mode: 0600
          - name: "Create configuration file for etcd"
            template:
      ...
      

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y.

      Vuelva a ejecutar ansible-playbook para realizar estos cambios:

      • ansible-playbook -i hosts playbook.yaml

      En este paso, cargamos correctamente las claves privadas y los certificados a los nodos administrados. Ahora que copiamos los archivos, debemos actualizar nuestro archivo de configuración para usarlos.

      Paso 14: Habilitar TLS en etcd

      En el último paso de este tutorial, vamos a actualizar algunas configuraciones de Ansible para habilitar TLS en un clúster de etcd.

      Primero, abra el archivo de plantillas templates/etcd.conf.yaml.j2 con su editor:

      • nano $HOME/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      En su interior, cambie todas las URL para usar https como protocolo en lugar de http. Adicionalmente, añada una sección al final de la plantilla para especificar la ubicación del certificado de CA, el certificado del servidor y la clave del servidor:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      name: {{ inventory_hostname }}
      initial-advertise-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
      listen-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,https://127.0.0.1:2380
      advertise-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
      listen-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,https://127.0.0.1:2379
      initial-cluster-state: new
      initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=https://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}
      
      client-transport-security:
        cert-file: /etc/etcd/ssl/server.crt
        key-file: /etc/etcd/ssl/server.key
        trusted-ca-file: /etc/etcd/ssl/ca.crt
      peer-transport-security:
        cert-file: /etc/etcd/ssl/server.crt
        key-file: /etc/etcd/ssl/server.key
        trusted-ca-file: /etc/etcd/ssl/ca.crt
      

      Cierre y guarde el archivo templates/etcd.conf.yaml.j2.

      A continuación, ejecute su playbook de Ansible:

      • ansible-playbook -i hosts playbook.yaml

      Luego, ingrese mediante SSH a uno de los nodos administrados:

      Cuando haya ingresado, ejecute el comando etcdctl endpoint health para verificar si los puntos de conexión están usando HTTPS y si el estado de todos los miembros es correcto:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt endpoint health --cluster

      Como nuestro certificado de CA no es, por defecto, un certificado root de CA de confianza instalado en el directorio /etc/ssl/certs/, debemos pasarlo a etcdctl usando el indicador --cacert.

      Esto generará el siguiente resultado:

      Output

      https://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 19.237262ms https://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 4.769088ms https://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 5.953599ms

      Para confirmar que el clúster etcd efectivamente esté funcionando, podemos crear una entrada en un nodo miembro y recuperarla desde otro:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt put foo "bar"

      A continuación, utilice una terminal nueva para ingresar mediante SSH en un nodo diferente:

      Ahora, recupere la misma entrada utilizando la clave foo:

      • etcdctl --cacert /etc/etcd/ssl/ca.crt get foo

      Esto devolverá la entrada y verá el siguiente resultado:

      Output

      foo bar

      Puede hacer lo mismo en el tercer nodo para garantizar que los tres miembros estén en funcionamiento.

      Conclusión

      Proporcionó correctamente un clúster de etcd de tres nodos, lo protegió con TLS y confirmó que esté funcionando.

      etcd es una herramienta creada originalmente por CoreOS. Para entender el uso de etcd en relación con CoreOS, puede consultar el artículo Cómo usar Etcdctl y Etcd, el almacén de clave-valor distribuido de CoreOS. El artículo también lo guía a través del proceso de configuración de un modelo de descubrimiento dinámico, algo que mencionamos, pero no demostramos en este tutorial.

      Como se mencionó al principio de este tutorial, etcd es un componente importante del ecosistema de Kubernetes. Para obtener más información sobre Kubernetes y la función que desempeña etcd en él, puede consultar el artículo Introducción a Kubernetes. Si está implementando etcd como parte de un clúster de Kubernetes, tenga en cuenta que puede utilizar otras herramientas, como kubespray y kubeadm. Para obtener más información al respecto, puede consultar el artículo Cómo crear un clúster de Kubernetes usando Kubeadm en Ubuntu 18.04.

      Por último, en este tutorial, se utilizaron muchas herramientas, pero no se pudo entrar en demasiado detalle en cada una de ellas. Encontrará enlaces que proporcionarán un análisis más detallado de cada herramienta aquí:



      Source link