Provide a hook into the AWS Secret Manager for the flask secret key (#1398)

Also, the secret manager required a region name, so there was some
refactoring around how regions are handled.

Fixes #1239
This commit is contained in:
bmccandless
2020-04-15 14:33:40 -07:00
committed by GitHub
parent 95ade476e8
commit 7e7ed74b92
7 changed files with 102 additions and 28 deletions
+21 -4
View File
@@ -93,8 +93,23 @@ There are many more options to these commands that may be important or necessary
```
$ make build
```
6. Flask secret key
The application requires as secret key to be provided to flask, the web framework used by cellxgene.
There are three ways to provide the secret key:
6. Create an environment
- In the configuration file: update the server/flask_secret_key attribute.
- An environment variable: CXG_SECRET_KEY
- Managed by the AWS Secret Manager
If using the AWS Secret Manager, then the secret name is passed as an environment variable: CXG_AWS_SECRET_NAME.
The secret must contain a key with the name "flask_secret_key".
Likely you have located the AWS Secret Manager in the same AWS region as the dataroot. If that is not the case
then the AWS Secret Manager region name can be specified in an environment variable: CXG_AWS_SECRET_REGION_NAME.
7. Create an environment
```
# name of the environment
@@ -106,23 +121,25 @@ There are many more options to these commands that may be important or necessary
# One or both of the following environment variables needs to be set
$ CXG_DATAROOT=<location to your S3 bucket>
$ CXG_CONFIG_FILE=<location to your config file>
# Potentially also set envvars for the sercret key.
$ eb create $EB_ENV --instance-type $EB_INSTANCE \
--envvars CXG_DATAROOT=$CXG_DATAROOT,CXG_CONFIG_FILE=$CXG_CONFIG_FILE
```
7. Give the elastic beanstalk environment access to the S3 bucket.
8. Give the elastic beanstalk environment access to the S3 bucket.
This link may provide some useful information:
https://aws.amazon.com/premiumsupport/knowledge-center/elastic-beanstalk-s3-bucket-instance/
8. Deploy the application
9. Deploy the application
```
$ eb deploy $EB_ENV
```
9. Open the application in a browser
10. Open the application in a browser
```
$ eb open $EB_ENV
+40 -3
View File
@@ -4,6 +4,8 @@ import sys
import os
import logging
from flask_talisman import Talisman
import boto3
import json
if os.path.isdir("/opt/python/log"):
# This is the standard location where Amazon EC2 instances store the application logs.
@@ -23,12 +25,34 @@ sys.path.append(SERVERDIR)
try:
from server.common.app_config import AppConfig
from server.app.app import Server
from server.common.data_locator import DataLocator
from server.common.data_locator import DataLocator, discover_s3_region_name
except Exception:
logging.critical("Exception importing server modules", exc_info=True)
sys.exit(1)
def get_flask_secret_key(region_name, secret_name):
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
try:
get_secret_value_response = client.get_secret_value(
SecretId=secret_name
)
if 'SecretString' in get_secret_value_response:
var = get_secret_value_response['SecretString']
secret = json.loads(var)
return secret.get("flask_secret_key")
except Exception:
logging.critical("Caught exception during get_secret_key", exc_info=True)
sys.exit(1)
return None
class WSGIServer(Server):
def __init__(self, app_config):
super().__init__(app_config)
@@ -44,8 +68,12 @@ try:
dataroot = os.getenv("CXG_DATAROOT")
config_file = os.getenv("CXG_CONFIG_FILE")
secret_name = os.getenv("CXG_AWS_SECRET_NAME")
secret_region_name = os.getenv("CXG_AWS_SECRET_REGION_NAME")
if config_file:
config_location = DataLocator(config_file)
region_name = discover_s3_region_name(config_file)
config_location = DataLocator(config_file, region_name)
if config_location.exists():
with config_location.local_handle() as lh:
logging.info(f"Configuration from {config_file}")
@@ -66,6 +94,14 @@ try:
logging.info(f"Configuration from CXG_DATAROOT")
app_config.update(multi_dataset__dataroot=dataroot)
if secret_name:
if secret_region_name is None:
secret_region_name = discover_s3_region_name(app_config.multi_dataset__dataroot)
if not secret_region_name:
logging.error(f"Expected to discover the s3 region name from {app_config.multi_dataset__dataroot}")
flask_secret_key = get_flask_secret_key(secret_region_name, secret_name)
app_config.update(server__flask_secret_key=flask_secret_key)
# features are unsupported in the current hosted server
app_config.update(
user_annotations__enable=False,
@@ -77,7 +113,8 @@ try:
if not app_config.server__flask_secret_key:
logging.critical(
f"flask_secret_key is not provided. Either set in config file, or in CXG_SECRET_KEY environment variable"
f"flask_secret_key is not provided. Either set in config file, CXG_SECRET_KEY environment variable, "
"or in AWS Secret Manager"
)
sys.exit(1)