This directory contains Dockerfiles for AWS environments.
All cloud environments are intended to be as identical as possible, using thesame Docker images and Django settings, except for environment-specific parameters.
The cloud docker images are designed to run on AWS, using S3 and SQS, and run within any container scheduler platform, Kubernetes, ECS, ECS/Fargate... (Docker Compose is not intended for production use).
- Base image Dockerfile
- Web Dockerfile, starts the Django application with Gunicorn
- Worker Dockerfile, starts Celery
Docker Compose and Makefile in this directory are for troubleshooting docker images only. They are not supposed to be used in any environments.
The Django settings module for these environments is
panelapp.settings.docker-aws
.
The same Django settings module is used for all environments. All environment-specific parameters are passed as environment variables (not by switching Django setting module).
All of the following environment variables must be set:
AWS_S3_STATICFILES_BUCKET_NAME
- Name of the S3 bucket for storing static filesAWS_S3_MEDIAFILES_BUCKET_NAME
- Name of the S3 bucket for storing media (uploaded) filesAWS_REGION
- AWS RegionAWS_S3_STATICFILES_CUSTOM_DOMAIN
- Custom CDN domain to serve static files from (e.g.cdn.mydomain.com
- no trailing/
)AWS_S3_MEDIAFILES_CUSTOM_DOMAIN
- Custom CDN domain to serve media files from (e.g.cdn.mydomain.com
- no trailing/
)ALLOWED_HOSTS
- whitelisted hostnames, if user tries to access website which isn't here Django will throw 500 errorDEFAULT_FROM_EMAIL
- we send emails as this addressPANEL_APP_EMAIL
- PanelApp email addressEMAIL_HOST
- SMTP server hostnameEMAIL_PORT
- SMTP server portPANEL_APP_BASE_URL
- Public URL of the web application
DATABASE_HOST
- PostgreSQL hostnameDATABASE_PORT
- (default: 5432) PostgreSQL portDATABASE_NAME
- (default: "panelapp") db nameDATABASE_USER
- PostgreSQL usernameDATABASE_PASSWORD
- PostgreSQL passwordEMAIL_HOST_USER
- SMTP username (no SMTP authentication if omitted)EMAIL_HOST_PASSWORD
- SMTP password (no SMTP authentication if omitted)SECRET_KEY
- Secret for encrypting cookies
DJANGO_LOG_LEVEL
- to override Django log-level (default=INFO
). This also controls Gunicorn and Celery log level.EMAIL_USE_TLS
- Set toFalse
to prevent SMTP from using TLSSQS_QUEUE_VISIBILITY_TIMEOUT
- SQS topic Visibility Timeout. Must be identical to the Visibility Timeout of the SQS queueTASK_QUEUE_NAME
- Name of the SQS queue (default:panelapp
)AWS_STATICFILES_LOCATION
- specify it to change the path static files are located within their S3 bucket (default:static
)AWS_MEDIAFILES_LOCATION
- specify it to change the path media files are located within their S3 bucket (default:media
)
DATABASE_URL
- (alternative to passing separateDATABASE_*
parameters) database config URL, in the format:postgresql://{username}:{password}@{host}:{port}/{database_name}
DJANGO_ADMIN_URL
- change admin URL to something more secure (by obscurity) than the default/admin
AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
- required if not using IAM Roles to authenticate access to S3 bucketsCELERY_BROKER_URL
- Only required if not using IAM Roles for SQS authentication. It must be in the formatsqs://{aws_access_key}:{aws_secret_key}@
.
AWS_USE_COGNITO
- (Optional) Boolean setting whether to use Cognito as backend user authentication. Default toFalse
.AWS_COGNITO_DOMAIN_PREFIX
- Required iffAWS_USE_COGNITO
is true. Cognito hosted domain prefix in a form ofpanelapp-prod
or what has set in Cognito User Pool domain name setting. Note that currently only Hosted UI mode is tested and supported.AWS_COGNITO_USER_POOL_CLIENT_ID
- Required iffAWS_USE_COGNITO
is true. Cognito User Pool client ID.AWS_COGNITO_HOSTED_AUTH_BASE
- (Optional) Well known Cognito Hosted UI domain auth base URL.AWS_COGNITO_IDP_LOGOUT_ENDPOINT
- (Optional) Well known Cognito logout endpoint URL.AWS_ELB_SESSION_COOKIE_PREFIX
- (Optional) Well known AWS ELB auth session cookie. Default toAWSELBAuthSessionCookie
.AWS_ELB_PUBLIC_KEY_ENDPOINT
- (Optional) Well known region specific ELB public key endpoint URL.AWS_JWT_SECTIONS
- (Optional) Well known number of AWS JWT sections. Default to3
.AWS_JWT_SIGNATURE_ALGORITHM
- (Optional) Well known AWS JWT algorithm. Default toES256
.AWS_AMZN_OIDC_ACCESS_TOKEN
- (Optional) Well known AWS OIDC access token header. Default toHTTP_X_AMZN_OIDC_ACCESSTOKEN
.AWS_AMZN_OIDC_IDENTITY
- (Optional) Well known AWS OIDC identity header. Default toHTTP_X_AMZN_OIDC_IDENTITY
.AWS_AMZN_OIDC_DATA
- (Optional) Well known AWS OIDC data header. Default toHTTP_X_AMZN_OIDC_DATA
.
All Gunicorn settings may be overridden by an environment variable
named GUNICORN_<UPPERCASE-SETTING-NAME>
(e.g. GUNICORN_WORKERS
overrides workers
)
Defaults:
GUNICORN_WORKERS
(workers
): 8GUNICORN_TIMEOUT
(timeout
, in seconds): 300
Web and Worker are completely stateless. They may scale out for HA as required.
Two S3 buckets are used for storing files:
- Media (uploaded) files
- Static files (images, css, js...)
The Media bucket must be accessible for read+write by both Web and Worker.
The Static bucket must be accessible for read+write by Web only
The Static bucket must also be publicly accessible, either directly (not-recommended) or through CloudFront CDN (recommended).
AWS_S3_STATICFILES_CUSTOM_DOMAIN
defines the public DNS domain to access the Static bucket (e.g. the CloudFront domain).
By default, static files are stored in a
/static
"subdirectory" of the bucket and are expected to be served fromhttps://<AWS_S3_STATICFILES_CUSTOM_DOMAIN>/static/
base URL.
The application uses an SQS queue named panelapp
to schedule jobs picked up by Worker.
It is recommended to create the SQS queue beforehand. Infrastructure should be managed securely outside of the running application. You should not give the application permissions to create queues at runtime.
The queue Visibility Timeout must be 360
(seconds).
If you use a different value, do not forget to override SQS_QUEUE_VISIBILITY_TIMEOUT
to match the queue timeout.
If the Visibility Timeout does not match what the application is expecting, Celery will try to create a new queue.
Using AWS Cognito for user and authentication purpose is totally optional. This is set by AWS_USE_COGNITO
setting. The implementation is based on the following articles, therefore, required to provision Cognito User Pool and Application Load Balancer as described in articles.
- https://aws.amazon.com/blogs/aws/built-in-authentication-in-alb/
- https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html
- https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html
Example Terraform coded Cognito User Pool and Application Load Balancer deployment can be found in the Panelapp Cloud Infrastructure repo.
The application expects a PostgreSQL-compatible DB.
The application has been tested with AWS Aurora, but PostgreSQL RDS should also work.
Authentication with the database uses username and password (part of DATABASE_URL
).
Access SQS and S3 should be authorised using IAM Policy.
No AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
must be explicitly passed to the application (this is not secure!)
This should be the Least-Privilege IAM Policy to access the SQS Queue.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:DeleteMessage",
"sqs:GetQueueUrl",
"sqs:ChangeMessageVisibility",
"sqs:DeleteMessageBatch",
"sqs:SendMessageBatch",
"sqs:ReceiveMessage",
"sqs:SendMessage",
"sqs:GetQueueAttributes",
"sqs:ChangeMessageVisibilityBatch"
],
"Resource": "arn:aws:sqs:<REGION>:<ACCOUNT_ID>:<QUEUE_NAME>"
},
{
"Effect": "Allow",
"Action": "sqs:ListQueues",
"Resource": "*"
}
]
}
Note the default queue name is panelapp
, unless overridden.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1559654322774",
"Action": [
"s3:DeleteObject",
"s3:PutObjectAcl",
"s3:GetObjectAcl",
"s3:GetObject",
"s3:HeadBucket",
"s3:ListBucket",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::<STATIC-BUCKET-NAME>/*",
"arn:aws:s3:::<MEDIA-BUCKET-NAME>/*"
]
}
]
}
All application components logs to stdout in JSON, using python3-json-log-formatter==1.6.1
as log formatter, for
easier log aggregation and indexing.
Logging level is controlled by DJANGO_LOG_LEVEL
(default = INFO
). Note that this does not only control Django, but also
Celery and Gunicorn logging.