¿Cómo puedo guardar mis claves secretas y mi contraseña de forma segura en mi sistema de control de versiones?

134

Mantengo configuraciones importantes como los nombres de host y los puertos de servidores de desarrollo y producción en mi sistema de control de versiones. Pero sé que es una mala práctica guardar secretos (como claves privadas y contraseñas de bases de datos) en un repositorio de VCS.

Pero las contraseñas, como cualquier otra configuración, parecen ser versionadas. Entonces, ¿cuál es la forma correcta de mantener controlada la versión de las contraseñas?

Me imagino que implicaría mantener los secretos en su propio archivo de "configuración de secretos" y tener ese archivo encriptado y controlado por la versión. ¿Pero qué tecnologías? ¿Y cómo hacer esto correctamente? ¿Hay una mejor manera de hacerlo?


En general, hago la pregunta, pero en mi caso específico me gustaría almacenar claves secretas y contraseñas para un sitio Django / Python usando git y github .

Además, una solución ideal haría algo mágico cuando empuje / tire con git; por ejemplo, si el archivo de contraseñas cifradas cambia, se ejecuta un script que solicita una contraseña y la descifra en su lugar.


EDITAR: para mayor claridad, estoy preguntando dónde almacenar los secretos de producción .

Chris W.
fuente
1
En realidad, ahorre algo de dinero para mantener privado todo el repositorio.
John Mee
29
@JohnMee De hecho, ya pago por un repositorio privado, pero el punto sigue siendo: no debes mantener información confidencial en tu repositorio.
Chris W.
1
Creo que gran parte de la razón por la que será difícil obtener respuestas satisfactorias es que la contraseña de texto sin formato para conectarse a una base de datos es una reliquia de una era menos hostil. La respuesta correcta es algo así como "su código no debería necesitar un secreto", pero los sistemas a los que está accediendo no le dan muchas opciones.
msw
44
¿Por qué? Hay un valor cero en la versión que controla las contraseñas para servicios externos. El principal valor del control de versiones es que puede inspeccionar revisiones históricas de su aplicación que se sabe que funcionan correctamente y ejecutarlas . Sin embargo, las contraseñas antiguas no te sirven de nada. Si han sido revocados, nunca volverán a funcionar.
Coronel Panic el

Respuestas:

100

Tiene toda la razón al querer cifrar su archivo de configuración confidencial mientras mantiene el archivo en control de versiones. Como mencionó, la mejor solución sería una en la que Git cifre de forma transparente ciertos archivos confidenciales cuando los presione para que localmente (es decir, en cualquier máquina que tenga su certificado) pueda usar el archivo de configuración, pero Git o Dropbox o quien sea almacenar sus archivos en VC no tiene la capacidad de leer la información en texto sin formato.

Tutorial sobre cifrado / descifrado transparente durante Push / Pull

Esta esencia https://gist.github.com/873637 muestra un tutorial sobre cómo usar el controlador de filtro de manchas / limpieza de Git con openssl para encriptar archivos empujados de forma transparente. Solo necesita hacer una configuración inicial.

Resumen de cómo funciona

Básicamente crearás una .gitencryptcarpeta que contiene 3 scripts de bash,

clean_filter_openssl 
smudge_filter_openssl 
diff_filter_openssl 

que Git usa para descifrar, cifrar y admitir Git diff. Una frase de contraseña maestra y una sal (¡arreglado!) Se definen dentro de estos scripts y DEBE asegurarse de que .gitencrypt nunca se envíe. clean_filter_opensslScript de ejemplo :

#!/bin/bash

SALT_FIXED=<your-salt> # 24 or less hex characters
PASS_FIXED=<your-passphrase>

openssl enc -base64 -aes-256-ecb -S $SALT_FIXED -k $PASS_FIXED

Similar para smudge_filter_open_ssly diff_filter_oepnssl. Ver Gist.

Su repositorio con información confidencial debe tener un archivo .gitattribute (sin cifrar e incluido en el repositorio) que hace referencia al directorio .gitencrypt (que contiene todo lo que Git necesita para cifrar / descifrar el proyecto de manera transparente) y que está presente en su máquina local.

.gitattribute contenido:

* filter=openssl diff=openssl
[merge]
    renormalize = true

Finalmente, también deberá agregar el siguiente contenido a su .git/configarchivo

[filter "openssl"]
    smudge = ~/.gitencrypt/smudge_filter_openssl
    clean = ~/.gitencrypt/clean_filter_openssl
[diff "openssl"]
    textconv = ~/.gitencrypt/diff_filter_openssl

Ahora, cuando inserta el repositorio que contiene su información confidencial en un repositorio remoto, los archivos se cifrarán de forma transparente. Cuando extrae de una máquina local que tiene el directorio .gitencrypt (que contiene su frase de contraseña), los archivos se descifrarán de forma transparente.

Notas

Debo señalar que este tutorial no describe una forma de cifrar solo su archivo de configuración confidencial. Esto cifrará de forma transparente todo el repositorio que se envía al host VC remoto y descifrará todo el repositorio para que se descifre por completo a nivel local. Para lograr el comportamiento que desea, puede colocar archivos confidenciales para uno o varios proyectos en una sensible_configuración_repo. Podría investigar cómo funciona esta técnica de cifrado transparente con los submódulos Git http://git-scm.com/book/en/Git-Tools-Submodules si realmente necesita que los archivos confidenciales estén en el mismo repositorio.

El uso de una frase de contraseña fija podría conducir teóricamente a vulnerabilidades de fuerza bruta si los atacantes tuvieran acceso a muchos repositorios / archivos cifrados. OMI, la probabilidad de esto es muy baja. Como se menciona en una nota al final de este tutorial, no usar una frase de contraseña fija dará como resultado versiones locales de un repositorio en diferentes máquinas que siempre muestran que se han producido cambios con el 'estado de git'.

dgh
fuente
1
Oh muy interesante Esto suena casi exactamente como lo que quiero (excepto que está encriptando todo el repositorio).
Chris W.
Puede mantener todos los archivos de configuración confidenciales para múltiples aplicaciones en un repositorio cifrado o agregar el repositorio cifrado con la configuración sensible a su proyecto como un submódulo Git como se describe aquí git-scm.com/book/en/Git-Tools-Submodules .
dgh
Almacenar contraseñas / configuraciones de producción en submódulos (encriptados) no es infrecuente. stackoverflow.com/questions/11207284/… . Incluso facilitaría la administración de la configuración en todos los proyectos.
dgh
Puede valer la pena consultar github.com/AGWA/git-crypt para obtener una solución actualizada. Tiene la ventaja de permitir que se encripten archivos individuales y afirma ser "probadamente semánticamente seguro". El propio autor de Gist sugirió que esta herramienta es mejor, en github.com/shadowhand/git-encrypt .
geekley
52

Heroku impulsa el uso de variables de entorno para configuraciones y claves secretas:

El enfoque tradicional para manejar tales variables de configuración es ponerlos bajo fuente, en un archivo de propiedades de algún tipo. Este es un proceso propenso a errores y es especialmente complicado para aplicaciones de código abierto que a menudo tienen que mantener ramas separadas (y privadas) con configuraciones específicas de la aplicación.

Una mejor solución es usar variables de entorno y mantener las claves fuera del código. En un host tradicional o trabajando localmente, puede establecer variables de entorno en su bashrc. En Heroku, usas config vars.

Con Foreman y los .envarchivos, Heroku proporciona una cadena de herramientas envidiable para exportar, importar y sincronizar variables de entorno.


Personalmente, creo que está mal guardar claves secretas junto con el código. Es fundamentalmente inconsistente con el control de código fuente, porque las claves son para servicios extrínsecos al código . El único beneficio sería que un desarrollador pueda clonar HEAD y ejecutar la aplicación sin ninguna configuración. Sin embargo, suponga que un desarrollador revisa una revisión histórica del código. Su copia incluirá la contraseña de la base de datos del año pasado, por lo que la aplicación fallará en la base de datos de hoy.

Con el método Heroku anterior, un desarrollador puede verificar la aplicación del año pasado, configurarla con las claves de hoy y ejecutarla con éxito en la base de datos de hoy.

Coronel Panic
fuente
1
Esta respuesta no tiene suficiente atención, pero coincide con la forma de Linux.
Nikolay Fominyh
11
Entonces, si los vars de entorno están configurados en su bashrc y está implementando un nuevo servidor, entonces, ¿qué crea el bashrc? ¿No solo mueve las contraseñas de su repositorio de código fuente a su configuración de implementación? (que presumiblemente también está en el repositorio de código fuente, o en un repositorio propio)
Jonathan Hartley
@JonathanHartley su .bashrc no debe estar en el repositorio de código para su aplicación Django.
Steve
44
Lo siento, mi comentario es ambiguo, pero eso es porque estoy realmente confundido. Me encanta el sonido del punto de vista de esta respuesta, pero nunca lo he entendido completamente. Si estoy implementando en varios entornos diferentes, cada uno de los cuales contiene varios hosts, y quizás varios tipos de hosts, entonces obviamente necesito automatizar la creación de los archivos .bashrc que existirán en cada host para establecer sus variables de entorno. Entonces, ¿la respuesta dice que debería tener un segundo repositorio, separado de mi fuente, que contiene todas las configuraciones que se convertirán en variables de entorno en .bashrc en la implementación?
Jonathan Hartley
1
Solo deben configurarse una vez por cada máquina en la que se implemente. Si su proceso de implementación es "poner en marcha una nueva máquina y probar que está bien antes de redirigir el tráfico a ella y luego dispararle a la vieja", lo cual en mi humilde opinión es la mejor práctica, entonces realmente necesita automatizar la creación de lo que establezca env vars.
Jonathan Hartley
16

La forma más limpia en mi opinión es usar variables de entorno. No tendrá que lidiar con archivos .dist , por ejemplo, y el estado del proyecto en el entorno de producción sería el mismo que el de su máquina local.

Recomiendo leer el capítulo de configuración de la aplicación The Twelve-Factor , los otros también si está interesado.

Samy Dindane
fuente
66
Parece que las variables de entorno son una buena manera de ejecutar la aplicación con la configuración secreta ... pero aún no responde la pregunta de dónde guardar esa configuración.
Chris W.
2
Por lo general, debe tener un archivo README para cada una de sus aplicaciones. Allí, especifique qué variables de entorno deben establecerse, y cada vez que implemente un proyecto, simplemente siga los pasos y configure cada una de ellas. También puede crear un script de shell con muchos export MY_ENV_VAR=, y cuando lo implemente, simplemente complételo con los valores correctos source. Si por mantener quiere decir la versión de la configuración, no debería estar haciendo esto en primer lugar.
Samy Dindane
Además, vota a favor de la aplicación The Twelve-Factor : cosas realmente geniales.
Chris W.
44
@Samy: ¿Y si ha automatizado la implementación?
Jonathan Hartley
3
@Samy Todavía no entiendo cómo se establecerían las variables de entorno. La página de la aplicación de 12 factores tampoco deja eso claro (a menos que esté en Heroku, que no es mi proyecto actual). ¿Estamos diciendo que un script generador debe preguntar a una tienda de configuración central "Soy la máquina X, por favor dame mis datos de configuración ", y eso responde con los valores de las variables de entorno que deben establecerse. En ese caso, no creo que necesite más un script generado. Estoy especulando salvajemente aquí, ¿estoy ladrando el árbol correcto?
Jonathan Hartley
10

Una opción sería colocar las credenciales vinculadas al proyecto en un contenedor cifrado (TrueCrypt o Keepass) y enviarlo.

Actualización como respuesta de mi comentario a continuación:

Pregunta interesante por cierto. Acabo de encontrar esto: github.com/shadowhand/git-encrypt que parece muy prometedor para el cifrado automático

Schneck
fuente
Sería bueno tener algo que pudiera automatizar. De modo que si mi archivo de contraseña encriptada cambia, desencripta automáticamente el nuevo archivo.
Chris W.
77
Pregunta interesante por cierto. Acabo de encontrar esto: github.com/shadowhand/git-encrypt, que parece muy prometedor para el cifrado automático.
schneck
1
Wow asombroso. La descripción de git-encryptsonidos suena exactamente como la que estoy buscando "Al trabajar con un repositorio git remoto que está alojado en un servidor de almacenamiento de terceros, la confidencialidad de los datos a veces se convierte en una preocupación. Este artículo lo guía a través de los procedimientos para configurar repositorios git para el cual sus directorios de trabajo locales son normales (sin cifrar) pero el contenido comprometido está cifrado ". (Por supuesto, solo quiero un subconjunto de mi contenido encriptado ...)
Chris W.
@schneck publica tu comentario como respuesta para que Chris pueda aceptarlo, parece que es lo que está buscando.
Tony Abou-Assaleh
9

Sugiero usar archivos de configuración para eso y no versionarlos.

Sin embargo, puede ver ejemplos de versiones de los archivos.

No veo ningún problema para compartir la configuración de desarrollo. Por definición, no debe contener datos valiosos.

tiktak
fuente
1
Pero entonces, ¿dónde almacenar los registros de contraseña canónica? Me pondría nervioso tener esos datos solo en un archivo de configuración en una máquina que podría explotar algún día.
Chris W.
@ChrisW. Si la máquina explota, ya no necesita la contraseña ... Sin embargo, si solo tiene una copia de los datos en su máquina de producción, eso debería levantar una bandera roja. Pero eso no significa que deba estar en VCS. Debe haber RAID, copias de seguridad completas complementadas por copias de seguridad incrementales en medios magnéticos y ópticos. Muchas corporaciones tienen un procedimiento de control de cambios que puede determinar cómo y dónde almacenar contraseñas y otros materiales confidenciales en papel también.
Steve Buzonas
@ChrisW No quiero ser rudo, pero parece que no nos dices la verdad y las contraseñas que quieres almacenar no se usan en Desarrollo sino en producción. ¿No es esto cierto? De lo contrario, ¿por qué le importa una máquina de desarrollo o prueba y las contraseñas de desarrollo? Nadie haría eso.
tiktak
Por cierto, en nuestra empresa, todas las contraseñas de desarrollo están disponibles en papel y en la intranet. Porque no tienen valor. Están allí porque el software que desarrollamos necesita autenticación.
tiktak
@tiktak, tienes razón: mi pregunta es sobre qué hacer con respecto a las contraseñas de producción. No me importa particularmente el almacenamiento de contraseñas de desarrollo en A VCS en claro. Lo siento si no lo he dejado lo suficientemente claro.
Chris W.
7

BlackBox fue lanzado recientemente por StackExchange y aunque todavía tengo que usarlo, parece abordar exactamente los problemas y respaldar las funciones solicitadas en esta pregunta.

De la descripción en https://github.com/StackExchange/blackbox :

Almacene con seguridad los secretos en un repositorio VCS (es decir, Git o Mercurial). Estos comandos facilitan que GPG encripte archivos específicos en un repositorio para que estén "encriptados en reposo" en su repositorio. Sin embargo, los scripts facilitan descifrarlos cuando necesita verlos o editarlos, y descifrarlos para usarlos en la producción.

Chris W.
fuente
7

Desde que hice esta pregunta, me decidí por una solución, que uso cuando desarrollo pequeñas aplicaciones con un pequeño equipo de personas.

cripta git

git-crypt usa GPG para cifrar archivos de forma transparente cuando sus nombres coinciden con ciertos patrones. Por ejemplo, si agrega a su .gitattributesarchivo ...

*.secret.* filter=git-crypt diff=git-crypt

... entonces un archivo como config.secret.jsonsiempre se enviará a repositorios remotos con cifrado, pero permanecerá sin cifrar en su sistema de archivos local.

Si quiero agregar una nueva clave GPG (una persona) a su repositorio, que puede descifrar los archivos protegidos y luego ejecutarlos git-crypt add-gpg-user <gpg_user_key>. Esto crea una nueva confirmación. El nuevo usuario podrá descifrar las confirmaciones posteriores.

Chris W.
fuente
5

En general, hago la pregunta, pero en mi caso específico me gustaría almacenar claves secretas y contraseñas para un sitio Django / Python usando git y github.

No, simplemente no lo hagas, incluso si es tu repositorio privado y nunca tienes la intención de compartirlo, no lo hagas.

Debe crear un local_settings.py, ponerlo en VCS ignorar y en su settings.py hacer algo como

from local_settings import DATABASES, SECRET_KEY
DATABASES = DATABASES

SECRET_KEY = SECRET_KEY

Si la configuración de tus secretos es tan versátil, estoy ansioso por decirte que estás haciendo algo mal

Hedde van der Heide
fuente
9
Pero aún tendré que seguir esos secretos en alguna parte . Por ejemplo, keypass o algo por el estilo, ¿verdad?
Chris W.
La regulación y la implementación del almacenamiento de datos privados depende de la política de la empresa para la que se realiza el proyecto. Dudo mucho que el código fuente del proyecto sea el lugar adecuado, ya que cualquier probador o programador
externo
4

EDITAR: supongo que desea realizar un seguimiento de sus versiones de contraseñas anteriores, por ejemplo, para un script que evitaría la reutilización de contraseñas, etc.

Creo que GnuPG es la mejor manera de hacerlo: ya se usa en un proyecto relacionado con git (git-annex) para cifrar el contenido del repositorio almacenado en los servicios en la nube. GnuPG (gnu pgp) proporciona un cifrado basado en claves muy fuerte.

  1. Mantiene una clave en su máquina local.
  2. Agrega 'mypassword' a los archivos ignorados.
  3. En el enlace previo al compromiso, encripta el archivo mypassword en el archivo mypassword.gpg seguido por git y lo agrega al commit.
  4. En el enlace posterior a la fusión, simplemente descifra mypassword.gpg en mypassword.

Ahora, si su archivo 'mypassword' no cambió, el cifrado tendrá el mismo texto cifrado y no se agregará al índice (sin redundancia). La más mínima modificación de mypassword resulta en texto cifrado radicalmente diferente y mypassword.gpg en el área de preparación difiere mucho del que está en el repositorio, por lo que se agregará a la confirmación. Incluso si el atacante obtiene su clave gpg, todavía necesita forzar la contraseña. Si el atacante obtiene acceso al repositorio remoto con texto cifrado, puede comparar un montón de textos cifrados, pero su número no será suficiente para darle una ventaja no despreciable.

Más adelante puede usar .gitattributes para proporcionar un descifrado sobre la marcha para salir de su contraseña.

También puede tener claves separadas para diferentes tipos de contraseñas, etc.

pielgrzym
fuente
3

Por lo general, separo la contraseña como un archivo de configuración. y hacerlos dist.

/yourapp
    main.py
    default.cfg.dist

Y cuando corro main.py, pon la contraseña real en default.cfgla copiada.

PD. cuando trabajas con git o hg. puede ignorar *.cfgarchivos para hacer .gitignoreo.hgignore

admirar
fuente
De lo que estaba hablando es de los archivos .dist: ejemplos de archivos de configuración reales. Una buena práctica es que debería ser posible ejecutar el software solo cambiando el nombre eliminando la extensión ".dist" (o mejor: copiando), es decir, debería poder probar el software en segundos, sin tener que configurarlo durante todo el día.
tiktak
3

Proporcionar una forma de anular la configuración

Esta es la mejor manera de administrar un conjunto de valores predeterminados razonables para la configuración que registra sin requerir que la configuración esté completa o que contenga cosas como nombres de host y credenciales. Hay algunas formas de anular las configuraciones predeterminadas.

Las variables de entorno (como ya han mencionado otros) son una forma de hacerlo.

La mejor manera es buscar un archivo de configuración externo que anule los valores de configuración predeterminados. Esto le permite administrar las configuraciones externas a través de un sistema de administración de configuración como Chef, Puppet o Cfengine. La gestión de la configuración es la respuesta estándar para la gestión de configuraciones separadas de la base de código, por lo que no tiene que hacer una versión para actualizar la configuración en un solo host o grupo de hosts.

FYI: Cifrar créditos no siempre es una buena práctica, especialmente en un lugar con recursos limitados. Puede ser que el cifrado de créditos no le permita mitigar riesgos adicionales y simplemente agregará una capa innecesaria de complejidad. Asegúrese de hacer el análisis adecuado antes de tomar una decisión.

dietbuddha
fuente
2

Cifre el archivo de contraseñas, utilizando, por ejemplo, GPG. Agregue las claves en su máquina local y en su servidor. Descifre el archivo y colóquelo fuera de sus carpetas de repositorio.

Yo uso passwords.conf, ubicado en mi carpeta de inicio. En cada implementación, este archivo se actualiza.

Willian
fuente
Entonces el software necesita descifrar el archivo de contraseña.
tiktak
Bueno, solo cuando se implementa el sitio, la contraseña se descifra y se escribe en un archivo de contraseña de texto sin formato
Willian
2

No, las claves privadas y las contraseñas no están bajo control de revisión. No hay ninguna razón para abrumar a todos con acceso de lectura a su repositorio con el conocimiento de las credenciales de servicio confidenciales utilizadas en la producción, cuando lo más probable es que no todas tengan acceso a esos servicios.

Comenzando con Django 1.4, sus proyectos de Django ahora se envían con un project.wsgimódulo que define el applicationobjeto y es un lugar perfecto para comenzar a aplicar el uso de un project.localmódulo de configuración que contiene configuraciones específicas del sitio.

Este módulo de configuración se ignora desde el control de revisión, pero se requiere presencia cuando se ejecuta la instancia de su proyecto como una aplicación WSGI, típica de los entornos de producción. Así es como debería verse:

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.local")

# This application object is used by the development server
# as well as any WSGI server configured to use this file.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

Ahora puede tener un local.pymódulo cuyo propietario y grupo se pueden configurar para que solo el personal autorizado y los procesos de Django puedan leer el contenido del archivo.

Filip Dupanović
fuente
2

Si necesita VCS para sus secretos, al menos debe mantenerlos en un segundo repositorio separado de su código real. Por lo tanto, puede dar acceso a los miembros de su equipo al repositorio de código fuente y no verán sus credenciales. Además, aloje este repositorio en otro lugar (por ejemplo, en su propio servidor con un sistema de archivos encriptado, no en github) y para verificarlo en el sistema de producción podría usar algo como git-submodule .

Bernhard Vallant
fuente
1

Otro enfoque podría ser evitar por completo guardar secretos en los sistemas de control de versiones y, en su lugar, utilizar una herramienta como la bóveda de hashicorp , un almacenamiento secreto con rodadura de claves y auditoría, con una API y cifrado integrado.

Kai Peters
fuente
1

Esto es lo que hago:

  • Guarde todos los secretos como envoltorios en $ HOME / .secrets (permisos de go-r) que $ HOME / .bashrc obtiene (de esta manera si abre .bashrc frente a alguien, no verán los secretos)
  • Los archivos de configuración se almacenan en VCS como plantillas, como config.properties almacenadas como config.properties.tmpl
  • Los archivos de plantilla contienen un marcador de posición para el secreto, como:

    my.password = ## MY_PASSWORD ##

  • En la implementación de la aplicación, se ejecuta un script que transforma el archivo de plantilla en el archivo de destino, reemplazando los marcadores de posición con valores de variables de entorno, como cambiar ## MY_PASSWORD ## al valor de $ MY_PASSWORD.

Pavel Chernikov
fuente
0

Puede usar EncFS si su sistema lo proporciona. Por lo tanto, podría mantener sus datos cifrados como una subcarpeta de su repositorio, mientras proporciona a su aplicación una vista descifrada de los datos montados a un lado. Como el cifrado es transparente, no se necesitan operaciones especiales en pull o push.

Sin embargo, necesitaría montar las carpetas EncFS, lo que podría hacer su aplicación en función de una contraseña almacenada en otro lugar fuera de las carpetas versionadas (por ejemplo, variables de entorno).

Dronus
fuente