Merge pull request #9 from 18F/jim/063022/protectbeta

set up basicauth config to protect staging site
This commit is contained in:
Jim Moffet
2022-07-19 19:02:40 -07:00
committed by GitHub
13 changed files with 76 additions and 49 deletions

15
.flake8
View File

@@ -1,15 +0,0 @@
[flake8]
# Rule definitions: http://flake8.pycqa.org/en/latest/user/error-codes.html
exclude = venv*,__pycache__,node_modules,cache
max-complexity = 14
max-line-length = 120
# B003 assigning to os.environ
# B306 BaseException.message has been deprecated
# W503: line break before binary operator
# W504 line break after binary operator
extend_ignore=
B003,
B306,
W503,
W504

2
.gitignore vendored
View File

@@ -109,6 +109,8 @@ environment.sh
.env
.env*
varsfile
*.log.json
logs/**
# CloudFoundry
.cf

View File

@@ -37,6 +37,7 @@ from werkzeug.local import LocalProxy
from app import proxy_fix, webauthn_server
from app.asset_fingerprinter import asset_fingerprinter
from app.config import configs
from app.custom_auth import CustomBasicAuth
from app.extensions import antivirus_client, redis_client, zendesk_client
from app.formatters import (
convert_to_boolean,
@@ -134,7 +135,7 @@ from app.url_converters import (
login_manager = LoginManager()
csrf = CSRFProtect()
metrics = GDSMetrics()
basic_auth = CustomBasicAuth()
# The current service attached to the request stack.
def _get_current_service():
@@ -221,6 +222,8 @@ def create_app(application):
login_manager.session_protection = None
login_manager.anonymous_user = AnonymousUser
setup_basic_auth(application)
# make sure we handle unicode correctly
redis_client.redis_store.decode_responses = True
@@ -586,3 +589,6 @@ def init_jinja(application):
]
jinja_loader = jinja2.FileSystemLoader(template_folders)
application.jinja_loader = jinja_loader
def setup_basic_auth(application):
application.basic_auth = CustomBasicAuth(application)

View File

@@ -4,13 +4,16 @@ import os
if os.environ.get('VCAP_APPLICATION'):
# on cloudfoundry, config is a json blob in VCAP_APPLICATION - unpack it, and populate
# standard environment variables from it
from app.cloudfoundry_config import extract_cloudfoundry_config
extract_cloudfoundry_config()
# from app.cloudfoundry_config import extract_cloudfoundry_config
# extract_cloudfoundry_config()
vcap_services = json.loads(os.environ['VCAP_SERVICES'])
os.environ['REDIS_URL'] = vcap_services['aws-elasticache-redis'][0]['credentials']['uri']
class Config(object):
ADMIN_CLIENT_SECRET = os.environ.get('ADMIN_CLIENT_SECRET')
API_HOST_NAME = os.environ.get('API_HOST_NAME')
ADMIN_CLIENT_USER_NAME = os.environ.get('ADMIN_CLIENT_USERNAME')
API_HOST_NAME = os.environ.get('API_HOST_NAME', 'localhost')
SECRET_KEY = os.environ.get('SECRET_KEY')
DANGEROUS_SALT = os.environ.get('DANGEROUS_SALT')
ZENDESK_API_KEY = os.environ.get('ZENDESK_API_KEY')
@@ -23,9 +26,7 @@ class Config(object):
# Logging
DEBUG = False
NOTIFY_LOG_PATH = os.getenv('NOTIFY_LOG_PATH')
ADMIN_CLIENT_USER_NAME = 'notify-admin'
NOTIFY_LOG_PATH = os.environ.get('NOTIFY_LOG_PATH', 'application.log')
ANTIVIRUS_API_HOST = os.environ.get('ANTIVIRUS_API_HOST')
ANTIVIRUS_API_KEY = os.environ.get('ANTIVIRUS_API_KEY')
@@ -40,7 +41,7 @@ class Config(object):
HEADER_COLOUR = '#81878b' # mix(govuk-colour("dark-grey"), govuk-colour("mid-grey"))
HTTP_PROTOCOL = 'http'
NOTIFY_APP_NAME = 'admin'
NOTIFY_LOG_LEVEL = 'DEBUG'
NOTIFY_LOG_LEVEL = os.environ.get('NOTIFY_LOG_LEVEL', 'DEBUG')
PERMANENT_SESSION_LIFETIME = 20 * 60 * 60 # 20 hours
SEND_FILE_MAX_AGE_DEFAULT = 365 * 24 * 60 * 60 # 1 year
SESSION_COOKIE_HTTPONLY = True
@@ -62,13 +63,17 @@ class Config(object):
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-local'
MOU_BUCKET_NAME = 'local-mou'
TRANSIENT_UPLOADED_LETTERS = 'local-transient-uploaded-letters'
ROUTE_SECRET_KEY_1 = os.environ.get('ROUTE_SECRET_KEY_1', '')
ROUTE_SECRET_KEY_2 = os.environ.get('ROUTE_SECRET_KEY_2', '')
ROUTE_SECRET_KEY_1 = os.environ.get('ROUTE_SECRET_KEY_1', 'dev-route-secret-key-1')
ROUTE_SECRET_KEY_2 = os.environ.get('ROUTE_SECRET_KEY_2', 'dev-route-secret-key-2')
CHECK_PROXY_HEADER = False
ANTIVIRUS_ENABLED = True
REDIS_URL = os.environ.get('REDIS_URL')
REDIS_ENABLED = True
BASIC_AUTH_USERNAME = os.environ.get('BASIC_AUTH_USERNAME')
BASIC_AUTH_PASSWORD = os.environ.get('BASIC_AUTH_PASSWORD')
BASIC_AUTH_FORCE = True
ASSET_DOMAIN = ''
ASSET_PATH = '/static/'
@@ -93,6 +98,7 @@ class Config(object):
class Development(Config):
BASIC_AUTH_FORCE = True
NOTIFY_LOG_PATH = 'application.log'
DEBUG = True
SESSION_COOKIE_SECURE = False
@@ -104,7 +110,7 @@ class Development(Config):
MOU_BUCKET_NAME = 'notify.tools-mou'
TRANSIENT_UPLOADED_LETTERS = 'development-transient-uploaded-letters'
PRECOMPILED_ORIGINALS_BACKUP_LETTERS = 'development-letters-precompiled-originals-backup'
ADMIN_CLIENT_SECRET = 'dev-notify-secret-key'
ADMIN_CLIENT_SECRET = os.environ.get('ADMIN_CLIENT_SECRET')
# check for local compose orchestration variable
API_HOST_NAME = os.environ.get('DEV_API_HOST_NAME', 'http://dev:6011')
DANGEROUS_SALT = 'dev-notify-salt'
@@ -116,11 +122,11 @@ class Development(Config):
ASSET_PATH = '/static/'
REDIS_URL = os.environ.get('DEV_REDIS_URL', 'http://redis:6379')
REDIS_ENABLED = os.environ.get('REDIS_ENABLED') == '1'
REDIS_ENABLED = True
class Test(Development):
BASIC_AUTH_FORCE = False
DEBUG = True
TESTING = True
WTF_CSRF_ENABLED = False
@@ -143,6 +149,7 @@ class Test(Development):
class Preview(Config):
BASIC_AUTH_FORCE = True
HTTP_PROTOCOL = 'https'
HEADER_COLOUR = '#F499BE' # $baby-pink
CSV_UPLOAD_BUCKET_NAME = 'preview-notifications-csv-upload'
@@ -162,6 +169,7 @@ class Preview(Config):
class Staging(Config):
BASIC_AUTH_FORCE = True
HTTP_PROTOCOL = 'https'
HEADER_COLOUR = '#6F72AF' # $mauve
CSV_UPLOAD_BUCKET_NAME = 'staging-notifications-csv-upload'
@@ -178,6 +186,7 @@ class Staging(Config):
class Live(Config):
BASIC_AUTH_FORCE = True
HEADER_COLOUR = '#005EA5' # $govuk-blue
HTTP_PROTOCOL = 'https'
CSV_UPLOAD_BUCKET_NAME = 'notifications.prototype.csv_upload'
@@ -191,6 +200,18 @@ class Live(Config):
CHECK_PROXY_HEADER = False
ASSET_DOMAIN = 'static.notifications.service.gov.uk'
ASSET_PATH = 'https://static.notifications.service.gov.uk/'
REDIS_URL = os.environ.get('REDIS_URL')
REDIS_ENABLED = True
ADMIN_CLIENT_SECRET = os.environ.get('ADMIN_CLIENT_SECRET')
ADMIN_CLIENT_USER_NAME = os.environ.get('ADMIN_CLIENT_USERNAME')
API_HOST_NAME = os.environ.get('API_HOST_NAME')
DANGEROUS_SALT = os.environ.get('DANGEROUS_SALT')
SECRET_KEY = os.environ.get('SECRET_KEY')
ANTIVIRUS_API_HOST = 'http://localhost:6016'
ANTIVIRUS_API_KEY = 'test-key'
ANTIVIRUS_ENABLED = False
class CloudFoundryConfig(Config):

15
app/custom_auth.py Normal file
View File

@@ -0,0 +1,15 @@
from flask_basicauth import BasicAuth
from flask import jsonify, request
class CustomBasicAuth(BasicAuth):
"""
Description:
Override BasicAuth to permit anonymous healthcheck at /_status?simple=true
"""
def challenge(self):
if "/_status" in request.url:
if request.args.get('elb', None) or request.args.get('simple', None):
return jsonify(status="ok"), 200
return super(CustomBasicAuth, self).challenge()
custom_basic_auth = CustomBasicAuth()

View File

@@ -1,6 +1,6 @@
import os
from app.notify_client import NotifyAdminAPIClient, cache
class StatusApiClient(NotifyAdminAPIClient):
def get_status(self, *params):

View File

@@ -1,12 +0,0 @@
Logging configured
Logging configured
Logging configured
Logging configured again
Logging configured
Logging configured again
Logging configured
Logging configured again
Logging configured
Logging configured again
Logging configured
Logging configured again

View File

@@ -23,16 +23,13 @@
}
},
"extensions": [
"ms-python.python", // "ms-python.black-formatter",
"donjayamanne.python-extension-pack",
"ms-azuretools.vscode-docker",
"ms-python.vscode-pylance",
"eamodio.gitlens",
"wholroyd.jinja",
"pmbenjamin.vscode-snyk",
"visualstudioexptteam.vscodeintellicode",
"yzhang.markdown-all-in-one",
"ms-ossdata.vscode-postgresql",
"GitHub.copilot"
],
"forwardPorts": [

View File

@@ -7,6 +7,8 @@ set -ex
# tools and the filesystem mount enabled should be located here.
###################################################################
echo "RUNNING ENTRYPOINT SCRIPT"
# Define aliases
echo -e "\n\n# User's Aliases" >> ~/.zshrc
echo -e "alias fd=fdfind" >> ~/.zshrc
@@ -29,6 +31,7 @@ pip3 install -r requirements_for_test.txt
make generate-version-file
# make babel
# npm ci install
if [ ! -d "/node_modules" ]; then
npm ci install
fi
@@ -36,4 +39,6 @@ fi
npm run build
# run flask
# make run
# make run
echo "FINISHED ENTRYPOINT SCRIPT"

View File

@@ -18,22 +18,25 @@ applications:
env:
NOTIFY_APP_NAME: admin
NOTIFY_LOG_PATH: /home/vcap/logs/app.log
NOTIFY_LOG_LEVEL: DEBUG
FLASK_APP: application.py
FLASK_ENV: production
REDIS_ENABLED: ((REDIS_ENABLED))
NOTIFY_ENVIRONMENT: live
# Credentials variables
ADMIN_CLIENT_SECRET: ((ADMIN_CLIENT_SECRET))
ADMIN_BASE_URL: notifications-admin.app.cloud.gov
API_HOST_NAME: notifications-api.app.cloud.gov
ADMIN_CLIENT_USERNAME: ((ADMIN_CLIENT_USERNAME))
ADMIN_BASE_URL: https://notifications-admin.app.cloud.gov
API_HOST_NAME: https://notifications-api.app.cloud.gov
DANGEROUS_SALT: ((DANGEROUS_SALT))
SECRET_KEY: ((SECRET_KEY))
ROUTE_SECRET_KEY_1: ((ROUTE_SECRET_KEY_1))
ROUTE_SECRET_KEY_2: ((ROUTE_SECRET_KEY_2))
AWS_REGION: us-west-2
AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID))
AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY))
BASIC_AUTH_USERNAME: ((BASIC_AUTH_USERNAME))
BASIC_AUTH_PASSWORD: ((BASIC_AUTH_PASSWORD))
NOTIFY_BILLING_DETAILS: []

View File

@@ -10,6 +10,7 @@ wtforms==3.0.1
Flask-Login==0.6.1
Werkzeug==2.1.2
jinja2==3.1.2
Flask-BasicAuth==0.2.0
blinker==1.4
pyexcel==0.7.0

View File

@@ -71,6 +71,8 @@ flask-redis==0.4.0
# via notifications-utils
flask-wtf==1.0.1
# via -r requirements.in
Flask-BasicAuth==0.2.0
# via -r requirements.in
gds-metrics @ git+https://github.com/alphagov/gds_metrics_python.git@6f1840a57b6fb1ee40b7e84f2f18ec229de8aa72
# via -r requirements.in
geojson==2.5.0

View File

@@ -5,3 +5,5 @@ ROUTE_SECRET_KEY_1: asdf
ROUTE_SECRET_KEY_2: asdf
AWS_ACCESS_KEY_ID: asdf
AWS_SECRET_ACCESS_KEY: asdf
BASIC_AUTH_USERNAME: asdf
BASIC_AUTH_PASSWORD: asdf