-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP]: Harden images and reorg #647
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
FROM python:3.9-slim-buster | ||
FROM python:3.9-slim | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removing the |
||
ENV PYTHONUNBUFFERED 1 | ||
|
||
|
@@ -17,23 +17,16 @@ RUN addgroup --system django \ | |
&& adduser --system --ingroup django django | ||
|
||
# Requirements are installed here to ensure they will be cached. | ||
COPY ./requirements /requirements | ||
RUN pip3 install --no-cache-dir -r /requirements/production.txt \ | ||
&& rm -rf /requirements | ||
COPY requirements /requirements | ||
RUN pip3 install --no-cache-dir -r /requirements/local.txt | ||
|
||
COPY ./docker/production/django/entrypoint /entrypoint | ||
RUN sed -i 's/\r$//g' /entrypoint | ||
RUN chmod +x /entrypoint | ||
RUN chown django /entrypoint | ||
COPY . /app | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Files and directories owned by the running user are a security risk. Removing the entrypoint script in favor of running the gunicorn server directly allows avoiding the user chown |
||
|
||
COPY ./docker/production/django/start /start | ||
RUN sed -i 's/\r$//g' /start | ||
RUN chmod +x /start | ||
RUN chown django /start | ||
COPY --chown=django:django . /app | ||
|
||
USER django | ||
RUN python /app/manage.py collectstatic --noinput \ | ||
&& mkdir -p /app/staticfiles/.well-known \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Running |
||
&& cyclonedx-py requirements requirements/base.txt --output-format json --outfile /app/staticfiles/.well-known/bom | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Satisfies Executive Order 14028 |
||
WORKDIR /app | ||
|
||
ENTRYPOINT ["/entrypoint"] | ||
USER django | ||
EXPOSE 5000 | ||
CMD ["/usr/local/bin/gunicorn", "-c", "gunicorn_conf.py", "config.wsgi", "--chdir=/app"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
""" | ||
Base settings to build other settings files upon. | ||
""" | ||
|
||
from datetime import timedelta | ||
from typing import List # noqa | ||
|
||
|
@@ -21,7 +22,7 @@ | |
# ------------------------------------------------------------------------------ | ||
# Set DEBUG to False as a default for safety | ||
# https://docs.djangoproject.com/en/dev/ref/settings/#debug | ||
DEBUG = env.bool("DJANGO_DEBUG", default=False) | ||
DEBUG = env.bool("DJANGO_DEBUG", default=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Production config sets this back to |
||
APPEND_SLASH = False | ||
TIME_ZONE = "UTC" | ||
LANGUAGE_CODE = "en-us" | ||
|
@@ -51,6 +52,8 @@ | |
"django.contrib.sessions", | ||
"django.contrib.messages", | ||
"django.contrib.staticfiles", | ||
"whitenoise.runserver_nostatic", | ||
"django_extensions", | ||
# Third party apps | ||
"rest_framework", # utilities for rest apis | ||
"rest_framework.authtoken", | ||
|
@@ -63,6 +66,7 @@ | |
"dj_rest_auth", | ||
"drf_yasg", | ||
"social_django", | ||
"gunicorn", | ||
# Your apps | ||
"metagrid.api_proxy", | ||
"metagrid.users", | ||
|
@@ -98,12 +102,21 @@ | |
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"] | ||
ROOT_URLCONF = "config.urls" | ||
|
||
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key | ||
SECRET_KEY = env( | ||
"DJANGO_SECRET_KEY", | ||
default="7LynCTKfcjH6p2Nz77YM9XzSnTSvpPVUNz4bHEScGJ6flWcOslxGNMdAhsDioJFJ", | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Production config sets this empty so that it raises |
||
|
||
WSGI_APPLICATION = "config.wsgi.application" | ||
|
||
# ------------------------------------------------------------------------------ | ||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend | ||
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" | ||
EMAIL_BACKEND = env( | ||
"DJANGO_EMAIL_BACKEND", | ||
default="django.core.mail.backends.console.EmailBackend", | ||
) | ||
# https://docs.djangoproject.com/en/2.2/ref/settings/#email-timeout | ||
EMAIL_TIMEOUT = 5 | ||
|
||
|
@@ -116,11 +129,20 @@ | |
# https://docs.djangoproject.com/en/dev/ref/settings/#managers | ||
MANAGERS = ADMINS | ||
|
||
CORS_ORIGIN_ALLOW_ALL = True | ||
|
||
# DATABASES | ||
# ------------------------------------------------------------------------------ | ||
# https://docs.djangoproject.com/en/dev/ref/settings/#databases | ||
DATABASES = {"default": env.db("DATABASE_URL")} | ||
DATABASES["default"]["ATOMIC_REQUESTS"] = True | ||
# Default to allowing the standard Postgres variables to configure the default database | ||
# https://www.postgresql.org/docs/current/libpq-envars.html | ||
|
||
DATABASES = { | ||
"default": { | ||
"ATOMIC_REQUESTS": True, | ||
**env.db("DATABASE_URL", default="pgsql:///postgres"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting this as the database url allows configuration via the standard postgres defaults and env vars |
||
} | ||
} | ||
|
||
# STATIC | ||
# ------------------------------------------------------------------------------ | ||
|
@@ -184,7 +206,7 @@ | |
# https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators | ||
AUTH_PASSWORD_VALIDATORS = [ | ||
{ | ||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", | ||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" | ||
}, | ||
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, | ||
{ | ||
|
@@ -250,7 +272,10 @@ | |
|
||
# django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/ | ||
# ------------------------------------------------------------------------------- | ||
DEFAULT_RENDERER_CLASSES = ["rest_framework.renderers.JSONRenderer"] | ||
DEFAULT_RENDERER_CLASSES = [ | ||
"rest_framework.renderers.JSONRenderer", | ||
"rest_framework.renderers.BrowsableAPIRenderer", | ||
] | ||
REST_FRAMEWORK = { | ||
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", | ||
"PAGE_SIZE": int(env("DJANGO_PAGINATION_LIMIT", default=10)), | ||
|
@@ -281,15 +306,13 @@ | |
SOCIALACCOUNT_PROVIDERS = { | ||
"keycloak": { | ||
"KEYCLOAK_URL": env( | ||
"KEYCLOAK_URL", | ||
), | ||
"KEYCLOAK_REALM": env( | ||
"KEYCLOAK_REALM", | ||
"KEYCLOAK_URL", default="https://esgf-login.ceda.ac.uk/" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wasn't sure if this should be e.g. https://keycloak for the local docker compose instance and the CEDA instance set as default in the Production config? I'm not sure of the status of keycloak support |
||
), | ||
"KEYCLOAK_REALM": env("KEYCLOAK_REALM", default="esgf"), | ||
}, | ||
} | ||
# Used in data migration to register Keycloak social app | ||
KEYCLOAK_CLIENT_ID = env("KEYCLOAK_CLIENT_ID") | ||
KEYCLOAK_CLIENT_ID = env("KEYCLOAK_CLIENT_ID", default="metagrid-localhost") | ||
|
||
ACCOUNT_EMAIL_REQUIRED = True | ||
ACCOUNT_USER_MODEL_USERNAME_FIELD = None | ||
|
@@ -305,8 +328,12 @@ | |
# https://docs.djangoproject.com/en/3.2/ref/settings/#login-url | ||
LOGIN_URL = "/login/globus/" | ||
|
||
LOGIN_REDIRECT_URL = env("DJANGO_LOGIN_REDIRECT_URL") | ||
LOGOUT_REDIRECT_URL = env("DJANGO_LOGOUT_REDIRECT_URL") | ||
LOGIN_REDIRECT_URL = env( | ||
"DJANGO_LOGIN_REDIRECT_URL", default="http://localhost:8080/search" | ||
) | ||
LOGOUT_REDIRECT_URL = env( | ||
"DJANGO_LOGOUT_REDIRECT_URL", default="http://localhost:8080/search" | ||
) | ||
|
||
# This dictates which scopes will be requested on each user login | ||
SOCIAL_AUTH_GLOBUS_SCOPE = [ | ||
|
@@ -317,13 +344,14 @@ | |
"urn:globus:auth:scope:transfer.api.globus.org:all", | ||
] | ||
|
||
SOCIAL_AUTH_GLOBUS_KEY = env("GLOBUS_CLIENT_KEY") | ||
SOCIAL_AUTH_GLOBUS_SECRET = env("GLOBUS_CLIENT_SECRET") | ||
SOCIAL_AUTH_GLOBUS_KEY = env("GLOBUS_CLIENT_KEY", default=12345) | ||
SOCIAL_AUTH_GLOBUS_SECRET = env("GLOBUS_CLIENT_SECRET", default=12345) | ||
SOCIAL_AUTH_GLOBUS_AUTH_EXTRA_ARGUMENTS = { | ||
"requested_scopes": SOCIAL_AUTH_GLOBUS_SCOPE, | ||
"prompt": None, | ||
} | ||
SOCIAL_AUTH_JSONFIELD_ENABLED = True | ||
SOCIAL_AUTH_REDIRECT_IS_HTTPS = False | ||
|
||
# dj-rest-auth | ||
# ------------------------------------------------------------------------------- | ||
|
@@ -335,12 +363,71 @@ | |
# ------------------------------------------------------------------------------- | ||
# https://github.com/adamchainz/django-cors-headers#setup | ||
CORS_ALLOWED_ORIGINS = [ | ||
"http://localhost:3000", | ||
"http://localhost:8080", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Frontend now based on nginx unpriviliged which defaults to port 8080 |
||
] | ||
CORS_ALLOW_CREDENTIALS = True | ||
CORS_ORIGIN_WHITELIST = env.list("CORS_ORIGIN_WHITELIST") | ||
CORS_ORIGIN_WHITELIST = env.list( | ||
"CORS_ORIGIN_WHITELIST", default="http://localhost:8080" | ||
) | ||
CSRF_TRUSTED_ORIGINS = ["http://localhost:8080"] | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are no longer needed by the frontend but still used by the backend for proxying. Renaming them would make migration less straightforward, but seems like it should be worth it |
||
SEARCH_URL = env( | ||
"REACT_APP_SEARCH_URL", | ||
default="https://esgf-node.ornl.gov/esg-search/search", | ||
) | ||
WGET_URL = env( | ||
"REACT_APP_WGET_API_URL", | ||
default="https://esgf-node.ornl.gov/esg-search/wget", | ||
) | ||
STATUS_URL = env( | ||
"REACT_APP_ESGF_NODE_STATUS_URL", | ||
default="https://esgf-node.ornl.gov/proxy/status", | ||
) | ||
SOLR_URL = env( | ||
"REACT_APP_ESGF_SOLR_URL", default="https://esgf-node.ornl.gov/esg-search" | ||
) | ||
|
||
FRONTEND_SETTINGS = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All settings in the |
||
"REACT_APP_CLIENT_ID": env("REACT_APP_CLIENT_ID", default="frontend"), | ||
"REACT_APP_GOOGLE_ANALYTICS_TRACKING_ID": env( | ||
"REACT_APP_GOOGLE_ANALYTICS_TRACKING_ID", default="" | ||
), | ||
"REACT_APP_GLOBUS_NODES": env.list( | ||
"REACT_APP_GLOBUS_NODES", | ||
default=[ | ||
"aims3.llnl.gov", | ||
"esgf-data1.llnl.gov", | ||
"esgf-data2.llnl.gov", | ||
], | ||
), | ||
"REACT_APP_KEYCLOAK_REALM": env( | ||
"REACT_APP_KEYCLOAK_REALM", default="esgf" | ||
), | ||
"REACT_APP_KEYCLOAK_URL": env( | ||
"REACT_APP_KEYCLOAK_URL", default="https://esgf-login.ceda.ac.uk/" | ||
), | ||
"REACT_APP_KEYCLOAK_CLIENT_ID": env( | ||
"REACT_APP_KEYCLOAK_CLIENT_ID", default="frontend" | ||
), | ||
"REACT_APP_HOTJAR_ID": env("REACT_APP_HOTJAR_ID", default=1234), | ||
"REACT_APP_HOTJAR_SV": env("REACT_APP_HOTJAR_SV", default=1234), | ||
"REACT_APP_AUTHENTICATION_METHOD": env( | ||
"REACT_APP_AUTHENTICATION_METHOD", default="keycloak" | ||
), | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basic sanity checks around the shape of settings. Perhaps move to Pydantic? |
||
SEARCH_URL = env("REACT_APP_SEARCH_URL") | ||
WGET_URL = env("REACT_APP_WGET_API_URL") | ||
STATUS_URL = env("REACT_APP_ESGF_NODE_STATUS_URL") | ||
SOLR_URL = env("REACT_APP_ESGF_SOLR_URL") | ||
if not isinstance(FRONTEND_SETTINGS["REACT_APP_GLOBUS_NODES"], list): | ||
raise environ.ImproperlyConfigured( | ||
f"REACT_APP_GLOBUS_NODES must be of type list, not " | ||
f"{FRONTEND_SETTINGS['REACT_APP_GLOBUS_NODES']} of type " | ||
f"{type(FRONTEND_SETTINGS['REACT_APP_GLOBUS_NODES'])}" | ||
) | ||
|
||
if FRONTEND_SETTINGS["REACT_APP_AUTHENTICATION_METHOD"] not in ( | ||
"keycloak", | ||
"globus", | ||
): | ||
raise environ.ImproperlyConfigured( | ||
f"REACT_APP_AUTHENTICATION_METHOD must be one of keycloak or globus, " | ||
f"not {FRONTEND_SETTINGS['REACT_APP_AUTHENTICATION_METHOD']}" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The *.env files were removed in favor of ensuring that all settings had a sane default out of the box though each service still respects settings loaded from .env files as before in order to preserve backwards compatibility