Muszę przechowywać klucze API i inne poufne informacje app.yaml
jako zmienne środowiskowe do wdrożenia w GAE. Problem z tym polega na tym, że jeśli app.yaml
wrzucę do GitHub, ta informacja stanie się publiczna (niedobra). Nie chcę przechowywać informacji w magazynie danych, ponieważ nie pasują one do projektu. Chciałbym raczej zamienić wartości z pliku, który jest wymieniony w .gitignore
każdym wdrożeniu aplikacji.
Oto mój plik app.yaml:
application: myapp
version: 3
runtime: python27
api_version: 1
threadsafe: true
libraries:
- name: webapp2
version: latest
- name: jinja2
version: latest
handlers:
- url: /static
static_dir: static
- url: /.*
script: main.application
login: required
secure: always
# auth_fail_action: unauthorized
env_variables:
CLIENT_ID: ${CLIENT_ID}
CLIENT_SECRET: ${CLIENT_SECRET}
ORG: ${ORG}
ACCESS_TOKEN: ${ACCESS_TOKEN}
SESSION_SECRET: ${SESSION_SECRET}
Jakieś pomysły?
Odpowiedzi:
Jeśli są to dane wrażliwe, nie należy ich przechowywać w kodzie źródłowym, ponieważ zostaną one wpisane do kontroli źródła. Mogą go tam znaleźć niewłaściwe osoby (z Twojej organizacji lub spoza niej). Ponadto środowisko programistyczne prawdopodobnie używa innych wartości konfiguracyjnych niż środowisko produkcyjne. Jeśli te wartości są przechowywane w kodzie, będziesz musiał uruchamiać inny kod podczas programowania i produkcji, co jest niechlujnym i złym rozwiązaniem.
W moich projektach umieszczam dane konfiguracyjne w datastore przy użyciu tej klasy:
from google.appengine.ext import ndb class Settings(ndb.Model): name = ndb.StringProperty() value = ndb.StringProperty() @staticmethod def get(name): NOT_SET_VALUE = "NOT SET" retval = Settings.query(Settings.name == name).get() if not retval: retval = Settings() retval.name = name retval.value = NOT_SET_VALUE retval.put() if retval.value == NOT_SET_VALUE: raise Exception(('Setting %s not found in the database. A placeholder ' + 'record has been created. Go to the Developers Console for your app ' + 'in App Engine, look up the Settings record with name=%s and enter ' + 'its value in that record\'s value field.') % (name, name)) return retval.value
Twoja aplikacja zrobiłaby to, aby uzyskać wartość:
API_KEY = Settings.get('API_KEY')
Jeśli istnieje wartość dla tego klucza w magazynie danych, otrzymasz ją. Jeśli go nie ma, zostanie utworzony rekord zastępczy i zostanie zgłoszony wyjątek. Wyjątek przypomni Ci, aby przejść do Developers Console i zaktualizować rekord zastępczy.
Uważam, że to eliminuje zgadywanie z ustawiania wartości konfiguracyjnych. Jeśli nie jesteś pewien, jakie wartości konfiguracyjne ustawić, po prostu uruchom kod, a on Ci powie!
Powyższy kod wykorzystuje bibliotekę ndb, która używa memcache i datastore pod maską, więc jest szybki.
Aktualizacja:
jelder zapytał, jak znaleźć wartości Datastore w konsoli App Engine i je ustawić. Oto jak:
Wejdź na https://console.cloud.google.com/datastore/
Wybierz swój projekt u góry strony, jeśli nie jest jeszcze wybrany.
W menu rozwijanym Rodzaj wybierz opcję Ustawienia .
Jeśli uruchomiłeś powyższy kod, twoje klucze pojawią się. Wszystkie będą miały wartość NIE USTAWIONĄ . Kliknij każdy z nich i ustaw jego wartość.
Mam nadzieję że to pomoże!
źródło
To rozwiązanie jest proste, ale może nie pasować do wszystkich różnych zespołów.
Najpierw umieść zmienne środowiskowe w pliku env_variables.yaml , np.
env_variables: SECRET: 'my_secret'
Następnie umieść to
env_variables.yaml
wapp.yaml
Na koniec dodaj
env_variables.yaml
to.gitignore
, aby tajne zmienne nie istniały w repozytorium.W takim przypadku
env_variables.yaml
potrzeby należy podzielić między menedżerów wdrażania.źródło
process.env.MY_SECRET_KEY
i jeśli potrzebujesz tych zmiennych środowiskowych w lokalnym środowisku deweloperskim, możesz użyćdotenv
pakietu węzłówenv_variables.yaml
do wszystkich instancji, to brakujący element układanki.gcloud app deploy
tak, jak zwykle do wdrażania w Google Cloud. 2. Jak lokalnie ustawić tajne zmienne środowiskowe? Jest wiele sposobów. Możesz po prostu użyćexport
w wierszu polecenia lub użyć dowolnych narzędzi, takich jak sugerowany @DaveKiss.os.environ.get('SECRET')
.Moje podejście polega na przechowywaniu kluczy klienta tylko w samej aplikacji App Engine. Klucze tajne klienta nie znajdują się w kontroli źródła ani na żadnych komputerach lokalnych. Ma to tę zaletę, że każdy współpracownik App Engine może wdrażać zmiany w kodzie bez martwienia się o sekrety klienta.
Przechowuję sekrety klienta bezpośrednio w Datastore i używam Memcache w celu zwiększenia opóźnienia dostępu do sekretów. Encje Datastore muszą zostać utworzone tylko raz i będą istnieć w przyszłych wdrożeniach. Oczywiście konsola App Engine może być używana do aktualizacji tych jednostek w dowolnym momencie.
Istnieją dwie możliwości wykonania jednorazowego tworzenia encji:
źródło
To nie istniało, kiedy publikowałeś, ale dla każdego, kto się tu potknie, Google oferuje teraz usługę o nazwie Secret Manager .
To prosta usługa REST (oczywiście z pakietami SDK) do przechowywania Twoich sekretów w bezpiecznej lokalizacji na platformie Google Cloud. Jest to lepsze podejście niż magazyn danych, wymagające dodatkowych kroków w celu wyświetlenia przechowywanych sekretów i posiadające bardziej szczegółowy model uprawnień - w razie potrzeby możesz zabezpieczyć poszczególne sekrety w inny sposób dla różnych aspektów projektu.
Oferuje przechowywanie wersji, dzięki czemu można ze względną łatwością obsługiwać zmiany haseł, a także solidną warstwę zapytań i zarządzania, umożliwiającą w razie potrzeby odkrywanie i tworzenie sekretów w czasie wykonywania.
Python SDK
Przykładowe użycie:
from google.cloud import secretmanager_v1beta1 as secretmanager secret_id = 'my_secret_key' project_id = 'my_project' version = 1 # use the management tools to determine version at runtime client = secretmanager.SecretManagerServiceClient() secret_path = client.secret_verion_path(project_id, secret_id, version) response = client.access_secret_version(secret_path) password_string = response.payload.data.decode('UTF-8') # use password_string -- set up database connection, call third party service, whatever
źródło
os.getenv('ENV_VAR')
?SECRET_KEY = env('SECRET_KEY', default=access_secret_version(GOOGLE_CLOUD_PROJECT_ID, 'SECRET_KEY', 1))
. Ustawienie domyślneaccess_secret_version
secret_id = 'my_secret_key'
nie jest wtedy obecne w twojej kontroli wersji?Najlepszym sposobem, aby to zrobić, jest przechowywanie kluczy w pliku client_secrets.json i wykluczenie ich z przesyłania do git, umieszczając je w pliku .gitignore. Jeśli masz różne klucze dla różnych środowisk, możesz użyć interfejsu API app_identity, aby określić identyfikator aplikacji i odpowiednio załadować.
Jest tutaj dość obszerny przykład -> https://developers.google.com/api-client-library/python/guide/aaa_client_secrets .
Oto przykładowy kod:
# declare your app ids as globals ... APPID_LIVE = 'awesomeapp' APPID_DEV = 'awesomeapp-dev' APPID_PILOT = 'awesomeapp-pilot' # create a dictionary mapping the app_ids to the filepaths ... client_secrets_map = {APPID_LIVE:'client_secrets_live.json', APPID_DEV:'client_secrets_dev.json', APPID_PILOT:'client_secrets_pilot.json'} # get the filename based on the current app_id ... client_secrets_filename = client_secrets_map.get( app_identity.get_application_id(), APPID_DEV # fall back to dev ) # use the filename to construct the flow ... flow = flow_from_clientsecrets(filename=client_secrets_filename, scope=scope, redirect_uri=redirect_uri) # or, you could load up the json file manually if you need more control ... f = open(client_secrets_filename, 'r') client_secrets = json.loads(f.read()) f.close()
źródło
app.yaml
podczas wdrażania aplikacji. Jakieś pomysły?app.yaml
) z tajnymi kluczami i poufnymi informacjami, a naprawdę podoba mi się to, że używasz przepływu pracy Google do wykonania zadania. Dzięki @GwynHowell. =)To rozwiązanie opiera się na przestarzałym pliku appcfg.py
Możesz użyć opcji wiersza poleceń -E w appcfg.py, aby skonfigurować zmienne środowiskowe podczas wdrażania aplikacji w GAE (aktualizacja appcfg.py)
$ appcfg.py ... -E NAME:VALUE, --env_variable=NAME:VALUE Set an environment variable, potentially overriding an env_variable value from app.yaml file (flag may be repeated to set multiple variables). ...
źródło
gcloud
narzędzia?Większość odpowiedzi jest nieaktualna. Korzystanie z Google Cloud Datastore jest teraz nieco inne. https://cloud.google.com/python/getting-started/using-cloud-datastore
Oto przykład:
from google.cloud import datastore client = datastore.Client() datastore_entity = client.get(client.key('settings', 'TWITTER_APP_KEY')) connection_string_prod = datastore_entity.get('value')
Zakłada się, że nazwa jednostki to „TWITTER_APP_KEY”, rodzaj to „ustawienia”, a „wartość” jest właściwością jednostki TWITTER_APP_KEY.
źródło
Wygląda na to, że możesz zrobić kilka podejść. Mamy podobny problem i wykonujemy następujące czynności (dostosowane do Twojego przypadku użycia):
Najłatwiej to zrobić, używając serwera ciągłej integracji, takiego jak Hudson , Bamboo lub Jenkins . Po prostu dodaj jakąś wtyczkę, krok skryptu lub przepływ pracy, który wykonuje wszystkie powyższe elementy, o których wspomniałem. Na przykład można przekazać zmienne środowiskowe skonfigurowane w samym Bamboo.
Podsumowując, wystarczy wcisnąć wartości podczas procesu kompilacji w środowisku, do którego masz tylko dostęp. Jeśli jeszcze nie automatyzujesz swoich kompilacji, powinieneś.
Inną opcją jest to, co powiedziałeś, umieść to w bazie danych. Jeśli powodem, dla którego tego nie robisz, jest to, że wszystko przebiega zbyt wolno, po prostu umieść wartości w memcache jako pamięć podręczną drugiej warstwy i przypnij wartości do instancji jako pamięć podręczną pierwszej warstwy. Jeśli wartości mogą się zmienić i musisz zaktualizować instancje bez ich ponownego uruchamiania, po prostu zachowaj skrót, aby wiedzieć, kiedy się zmieniają, lub w jakiś sposób wyzwalają, gdy coś, co robisz, zmienia wartości. To powinno być to.
źródło
Powinieneś zaszyfrować zmienne za pomocą google kms i osadzić je w kodzie źródłowym. ( https://cloud.google.com/kms/ )
umieść zaszyfrowaną (zaszyfrowaną i zakodowaną w formacie base64) wartość w zmiennej środowiskowej (w pliku yaml).
Trochę kodu w języku Python, aby rozpocząć odszyfrowywanie.
kms_client = kms_v1.KeyManagementServiceClient() name = kms_client.crypto_key_path_path("project", "global", "THEKEYRING", "THECRYPTOKEY") twitter_app_key = kms_client.decrypt(name, base64.b64decode(os.environ.get("TWITTER_APP_KEY"))).plaintext
źródło
@Jason F za odpowiedź opiera się na wykorzystaniu Google DataStore jest blisko, ale kod jest nieco nieaktualne na podstawie przykładowego wykorzystania na docs bibliotecznych . Oto fragment, który zadziałał dla mnie:
from google.cloud import datastore client = datastore.Client('<your project id>') key = client.key('<kind e.g settings>', '<entity name>') # note: entity name not property # get by key for this entity result = client.get(key) print(result) # prints all the properties ( a dict). index a specific value like result['MY_SECRET_KEY'])
Częściowo zainspirowany tym postem na Medium
źródło
Chciałem tylko zauważyć, jak rozwiązałem ten problem w javascript / nodejs. Do lokalnego rozwoju użyłem pakietu npm dotenv, który ładuje zmienne środowiskowe z pliku .env do process.env. Kiedy zacząłem używać GAE, dowiedziałem się, że zmienne środowiskowe muszą być ustawione w pliku „app.yaml”. Cóż, nie chciałem używać „dotenv” do lokalnego programowania i „app.yaml” do GAE (i powielać moje zmienne środowiskowe między dwoma plikami), więc napisałem mały skrypt, który ładuje zmienne środowiskowe app.yaml do procesu .env do lokalnego rozwoju. Mam nadzieję, że to komuś pomoże:
yaml_env.js:
(function () { const yaml = require('js-yaml'); const fs = require('fs'); const isObject = require('lodash.isobject') var doc = yaml.safeLoad( fs.readFileSync('app.yaml', 'utf8'), { json: true } ); // The .env file will take precedence over the settings the app.yaml file // which allows me to override stuff in app.yaml (the database connection string (DATABASE_URL), for example) // This is optional of course. If you don't use dotenv then remove this line: require('dotenv/config'); if(isObject(doc) && isObject(doc.env_variables)) { Object.keys(doc.env_variables).forEach(function (key) { // Dont set environment with the yaml file value if it's already set process.env[key] = process.env[key] || doc.env_variables[key] }) } })()
Teraz umieść ten plik tak wcześnie, jak to możliwe w kodzie i gotowe:
require('../yaml_env')
źródło
.env
pliku z tajnymi zmiennymi. Nie powielam ich w moimapp.yaml
pliku, a wdrożony kod nadal działa. Martwię się jednak, co stanie się z.env
plikiem w chmurze. Czy zostanie zaszyfrowany czy coś? Jak mogę się upewnić, że nikt nie będzie miał dostępu do.env
zmiennych pliku gcloud po jego wdrożeniu?Rozszerzam odpowiedź Martina
from google.appengine.ext import ndb class Settings(ndb.Model): """ Get sensitive data setting from DataStore. key:String -> value:String key:String -> Exception Thanks to: Martin Omander @ Stackoverflow https://stackoverflow.com/a/35261091/1463812 """ name = ndb.StringProperty() value = ndb.StringProperty() @staticmethod def get(name): retval = Settings.query(Settings.name == name).get() if not retval: raise Exception(('Setting %s not found in the database. A placeholder ' + 'record has been created. Go to the Developers Console for your app ' + 'in App Engine, look up the Settings record with name=%s and enter ' + 'its value in that record\'s value field.') % (name, name)) return retval.value @staticmethod def set(name, value): exists = Settings.query(Settings.name == name).get() if not exists: s = Settings(name=name, value=value) s.put() else: exists.value = value exists.put() return True
źródło
Istnieje pakiet pypi o nazwie gae_env, który umożliwia zapisywanie zmiennych środowiskowych appengine w Cloud Datastore. Pod maską wykorzystuje również Memcache, dzięki czemu jest szybki
Stosowanie:
import gae_env API_KEY = gae_env.get('API_KEY')
Jeśli istnieje wartość dla tego klucza w magazynie danych, zostanie ona zwrócona. Jeśli go nie ma,
__NOT_SET__
zostanie utworzony rekord zastępczy iValueNotSetError
zostanie wyrzucony znak zapytania. Wyjątek przypomni Ci, aby przejść do Developers Console i zaktualizować rekord zastępczy.Podobnie jak odpowiedź Martina, oto jak zaktualizować wartość klucza w Datastore:
Przejdź do sekcji Datastore w konsoli programistów
Wybierz swój projekt u góry strony, jeśli nie jest jeszcze wybrany.
W menu rozwijanym Rodzaj wybierz
GaeEnvSettings
.Klucze, dla których zgłoszono wyjątek, będą miały wartość
__NOT_SET__
.Przejdź do strony pakietu GitHub, aby uzyskać więcej informacji na temat użycia / konfiguracji
źródło