Adds docker compose examples (#1113)

This adds three example docker-compose files:

- A basic Grist instance backed by sqlite, with no additional services.
- A Grist instance that uses Postgres, Redis and MinIO.
- A Grist instance that uses OIDC authentication and traefik.

These are intended to be customised by self-hosters for their own needs.

All examples should work without any additional configuration.
This commit is contained in:
Spoffy 2024-08-12 20:54:43 +01:00 committed by GitHub
parent 6bbcf9c1d7
commit dfb816888e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 1895 additions and 0 deletions

5
.gitignore vendored
View File

@ -83,3 +83,8 @@ xunit.xml
# ext directory can be overwritten # ext directory can be overwritten
ext/** ext/**
# Docker compose examples - persistent values and secrets
/docker-compose-examples/*/persist
/docker-compose-examples/*/secrets
/docker-compose-examples/grist-traefik-oidc-auth/.env

View File

@ -0,0 +1,12 @@
This is the simplest example that runs Grist, suitable for local testing.
It is STRONGLY RECOMMENDED not to use this container in a way that makes it accessible to the internet.
This setup lacks basic security or authentication.
Other examples demonstrate how to set up authentication and HTTPS.
See https://support.getgrist.com/self-managed for more information.
## How to run this example
This example can be run with `docker compose up`.

View File

@ -0,0 +1,8 @@
services:
grist:
image: gristlabs/grist:latest
volumes:
# Where to store persistent data, such as documents.
- ${PERSIST_DIR}/grist:/persist
ports:
- 8484:8484

View File

@ -0,0 +1,24 @@
This is the simplest example of Grist with authentication and HTTPS encryption.
It uses Traefik as:
- A reverse proxy to manage certificates and provide HTTPS support
- A basic authentication provided using Traefik's Basic Auth middleware.
This setup, after configuring HTTPS certificates correctly, should be acceptable on the public internet.
However, it doesn't allow a user to sign-out due to the way browsers handle basic authentication.
You may want to try a more secure authentication setup such Authelia, Authentik or traefik-forward-auth.
The OIDC auth example demonstrates a setup using Authelia.
See https://support.getgrist.com/self-managed for more information.
## How to run this example
This example can be run with `docker compose up`.
The default login is:
- Username: `test@example.org`
- Password: `test`
This can be changed in `./configs/traefik-dynamic-config.yaml`. Instructions on how to do this are available in that file.

View File

@ -0,0 +1,35 @@
providers:
# Enables reading docker label config values
docker: {}
# Read additional config from this file.
file:
directory: "/etc/traefik/dynamic"
entrypoints:
# Defines a secure entrypoint using TLS encryption
websecure:
address: ":443"
http:
tls: true
# Defines an insecure entrypoint that redirects to the secure one.
web:
address: ":80"
http:
# Redirects HTTP to HTTPS
redirections:
entrypoint:
to: "websecure"
scheme: "https"
# Enables automatic certificate renewal
certificatesResolvers:
letsencrypt:
acme:
email: "my_email@example.com"
storage: /acme/acme.json
tlschallenge: true
# Enables the web UI
# This is disabled by default for security, but can be useful to debugging traefik.
api:
# insecure: true

View File

@ -0,0 +1,13 @@
http:
# Declaring the user list
middlewares:
grist-basic-auth:
basicAuth:
# The header that Grist will listen for authenticated usernames on.
headerField: "X-Forwarded-User"
# This is the list of users, in the format username:password.
# Passwords can be created using `htpasswd`
# E.g: `htpasswd -nB test@example.org`
users:
# The default username is "test@example.org". The default password is "test".
- "test@example.org:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"

View File

@ -0,0 +1,44 @@
services:
grist:
image: gristlabs/grist:latest
environment:
# Sets the header to look at for authentication
GRIST_FORWARD_AUTH_HEADER: X-Forwarded-User
# Forces Grist to only use a single team called 'Example'
GRIST_SINGLE_ORG: my-grist-team # alternatively, GRIST_ORG_IN_PATH: "true" for multi-team operation
# Force users to login (disable anonymous access)
GRIST_FORCE_LOGIN: true
# Base URL Grist redirects to when navigating. Change this to your domain.
APP_HOME_URL: https://grist.localhost
# Default email for the "Admin" account
GRIST_DEFAULT_EMAIL: test@example.org
volumes:
# Where to store persistent data, such as documents.
- ${PERSIST_DIR}/grist:/persist
labels:
- "traefik.http.services.grist.loadbalancer.server.port=8484"
- "traefik.http.routers.grist.rule=Host(`grist.localhost`)"
- "traefik.http.routers.grist.tls.certresolver=letsencrypt"
- "traefik.http.routers.grist-auth.rule=Host(`grist.localhost`) && (PathPrefix(`/auth/login`) || PathPrefix(`/_oauth`))"
- "traefik.http.routers.grist-auth.middlewares=grist-basic-auth@file"
- "traefik.http.routers.grist-auth.tls.certresolver=letsencrypt"
traefik:
image: traefik:latest
ports:
# HTTP Ports
- "80:80"
- "443:443"
# The Web UI (enabled by --api.insecure=true)
# - "8080:8080"
volumes:
# Set the config file for traefik - this is loaded automatically.
- ./configs/traefik-config.yml:/etc/traefik/traefik.yml
# Set the config file for the dynamic config, such as middleware.
- ./configs/traefik-dynamic-config.yml:/etc/traefik/dynamic/dynamic-config.yml
# Certificate location, if automatic certificate setup is enabled.
- ./configs/acme:/acme
# Traefik needs docker access when configured via docker labels.
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- grist

View File

@ -0,0 +1,32 @@
This is an example of Grist with Authelia for OIDC authentication, and Traefik for HTTP encryption and routing.
OIDC enables authentication using many existing providers, including Google, Microsoft, Amazon and Okta.
This example uses Authelia, which is a locally hosted OIDC provider, so that it can work without further setup.
However, Authelia could be easily replaced by one of the providers listed above, or other self-hosted alternatives,
such as Authentik or Dex.
This example could be hosted on a dedicated server, with the following changes:
- DNS setup
- HTTPS / Certificate setup (e.g Let's encrypt)
See https://support.getgrist.com/install/oidc for more information on using Grist with OIDC.
## How to run this example
To run this example, you'll first need to generate several secrets needed by Authelia.
This is automated for you in `generateSecureSecrets.sh`, which uses Authelia's docker image to populate the `./secrets` directory.
This example can then be run with `docker compose up`. This will make Grist available on `https://grist.localhost` with a self-signed certificate (by default), after all the services have started. Note: it may take up to a minute for all of the services to start correctly.
The self-signed certificate will cause a security warning in the web browser when you try to visit Grist.
This is fine for local testing and can be bypassed, but correct certificates should be set up if Grist is being made
available on the internet.
### Users
The default username is `test`, with password `test`.
You can add or modify users in ./configs/authelia/user-database.yml. Additional instructions are provided in that file.

View File

@ -0,0 +1,14 @@
# Primary users file.
# Passwords are generated using 'authelia crypto hash generate argon2'
# E.g:
# docker run authelia/authelia:4 authelia crypto hash generate argon2 --password "test"
# See https://www.authelia.com/reference/guides/passwords/#yaml-format
users:
test:
disabled: false
displayname: 'Test'
password: '$argon2id$v=19$m=65536,t=3,p=4$j1Jub3z0jWBmXNOjNpRK5w$d5176FINCAuzdT3uehQqMS08FC4fadAGrqyZL+0W+p4'
email: 'test@example.org'
groups: []

View File

@ -0,0 +1,30 @@
providers:
# Enables reading docker label config values
docker: {}
entrypoints:
# Defines a secure entrypoint using TLS encryption
websecure:
address: ":443"
http:
tls: true
# Defines an insecure entrypoint that redirects to the secure one.
web:
address: ":80"
http:
# Redirects HTTP to HTTPS
redirections:
entrypoint:
to: "websecure"
scheme: "https"
# Enables automatic certificate renewal
certificatesResolvers:
letsencrypt:
acme:
email: "my_email@example.com"
storage: /acme/acme.json
tlschallenge: true
api:
insecure: true

View File

@ -0,0 +1,118 @@
secrets:
# These secrets are used by Authelia
JWT_SECRET:
file: ${SECRETS_DIR}/JWT_SECRET
SESSION_SECRET:
file: ${SECRETS_DIR}/SESSION_SECRET
STORAGE_ENCRYPTION_KEY:
file: ${SECRETS_DIR}/STORAGE_ENCRYPTION_KEY
# These secrets are for using Authelia as an OIDC provider
HMAC_SECRET:
file: ${SECRETS_DIR}/HMAC_SECRET
JWT_PRIVATE_KEY:
file: ${SECRETS_DIR}/certs/private.pem
GRIST_CLIENT_SECRET_DIGEST:
file: ${SECRETS_DIR}/GRIST_CLIENT_SECRET_DIGEST
services:
grist:
image: gristlabs/grist:latest
environment:
# The URL of given OIDC provider. Used for redirects, among other things.
GRIST_OIDC_IDP_ISSUER: https://${AUTHELIA_DOMAIN}
# Client ID, as configured with the OIDC provider.
GRIST_OIDC_IDP_CLIENT_ID: grist-local
# Client secret, as provided by the OIDC provider.
GRIST_OIDC_IDP_CLIENT_SECRET: ${GRIST_CLIENT_SECRET}
# The URL to redirect to with the OIDC provider to log out.
# Some OIDC providers will automatically configure this.
GRIST_OIDC_IDP_END_SESSION_ENDPOINT: https://${AUTHELIA_DOMAIN}/logout
# Allow self-signed certificates so this example behaves correctly.
# REMOVE THIS IF HOSTING ON THE INTERNET.
NODE_TLS_REJECT_UNAUTHORIZED: 0
# Forces Grist to only use a single team called 'Example'
GRIST_SINGLE_ORG: my-grist-team # alternatively, GRIST_ORG_IN_PATH: "true" for multi-team operation
# Force users to login (disable anonymous access)
GRIST_FORCE_LOGIN: true
# Base URL Grist redirects to when navigating. Change this to your domain.
APP_HOME_URL: https://${GRIST_DOMAIN}
# Default email for the "Admin" account
GRIST_DEFAULT_EMAIL: ${DEFAULT_EMAIL:-test@example.org}
restart: always
volumes:
# Where to store persistent data, such as documents.
- ${PERSIST_DIR}/grist:/persist
labels:
- "traefik.http.services.grist.loadbalancer.server.port=8484"
- "traefik.http.routers.grist.rule=Host(`${GRIST_DOMAIN}`)"
- "traefik.http.routers.grist.service=grist"
# Uncomment and configure in traefik-config.yml to enable automatic HTTPS certificate setup.
#- "traefik.http.routers.grist.tls.certresolver=letsencrypt"
depends_on:
# Grist attempts to setup OIDC when it starts, making a request to the OIDC service.
# This will fail if Authelia isn't ready and reachable.
# Traefik will only start routing to Authelia when it's registered as healthy.
# Making Grist wait for Authelia to be healthy should avoid this issue.
authelia:
condition: service_healthy
traefik:
condition: service_started
traefik:
image: traefik:latest
ports:
# HTTP Ports
- "80:80"
- "443:443"
# The Web UI (enabled by --api.insecure=true)
- "8080:8080"
- "8082:8082"
volumes:
# Set the config file for traefik - this is loaded automatically.
- ./configs/traefik/config.yml:/etc/traefik/traefik.yml
# Certificate location, if automatic certificate setup is enabled.
- ./secrets/acme_certificates:/acme
# Traefik needs docker access when configured via docker labels.
- /var/run/docker.sock:/var/run/docker.sock
networks:
default:
aliases:
# Enables Grist to resolve this domain to Traefik when doing OIDC setup.
- ${AUTHELIA_DOMAIN}
authelia:
image: authelia/authelia:4
secrets:
- HMAC_SECRET
- JWT_SECRET
- JWT_PRIVATE_KEY
- GRIST_CLIENT_SECRET_DIGEST
- SESSION_SECRET
- STORAGE_ENCRYPTION_KEY
environment:
AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET_FILE: '/run/secrets/JWT_SECRET'
AUTHELIA_SESSION_SECRET_FILE: '/run/secrets/SESSION_SECRET'
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: '/run/secrets/STORAGE_ENCRYPTION_KEY'
HMAC_SECRET_FILE: '/run/secrets/HMAC_SECRET'
JWT_PRIVATE_KEY_FILE: '/run/secrets/JWT_PRIVATE_KEY'
# Domain Grist is hosted at. Custom variable that's interpolated into the Authelia config
APP_DOMAIN: ${GRIST_DOMAIN}
# Where Authelia should redirect to after successful authentication.
GRIST_OAUTH_CALLBACK_URL: https://${GRIST_DOMAIN}/oauth2/callback
# Hash of the client secret provided to Grist.
GRIST_CLIENT_SECRET_DIGEST_FILE: "/run/secrets/GRIST_CLIENT_SECRET_DIGEST"
volumes:
- ./configs/authelia:/config
- ${PERSIST_DIR}/authelia:/persist
command:
- 'authelia'
- '--config=/config/configuration.yml'
# Enables templating in the config file
- '--config.experimental.filters=template'
labels:
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
- "traefik.http.routers.authelia.rule=Host(`${AUTHELIA_DOMAIN}`)"
- "traefik.http.routers.authelia.service=authelia"
# Uncomment and configure in traefik-config.yml to enable automatic HTTPS certificate setup.
#- "traefik.http.routers.authelia.tls.certresolver=letsencrypt"

View File

@ -0,0 +1,6 @@
GRIST_DOMAIN=grist.localhost
AUTHELIA_DOMAIN=auth.grist.localhost
DEFAULT_EMAIL=test@example.org
PERSIST_DIR=./persist
SECRETS_DIR=./secrets
GRIST_CLIENT_SECRET=

View File

@ -0,0 +1,32 @@
# Helper script to securely generate random secrets for Authelia.
SCRIPT_DIR=$(dirname $0)
# Copy over template files to final locations
cp -R "$SCRIPT_DIR/secrets_template" "$SCRIPT_DIR/secrets"
cp "$SCRIPT_DIR/env-template" "$SCRIPT_DIR/.env"
# Parses an Aurelia generated secret for the value
function getSecret {
cut -d ":" -f 2 <<< "$1" | tr -d '[:blank:]'
}
function generateSecureString {
getSecret "$(docker run authelia/authelia:4 authelia crypto rand --charset=rfc3986 --length="$1")"
}
generateSecureString 128 > "$SCRIPT_DIR/secrets/HMAC_SECRET"
generateSecureString 128 > "$SCRIPT_DIR/secrets/JWT_SECRET"
generateSecureString 128 > "$SCRIPT_DIR/secrets/SESSION_SECRET"
generateSecureString 128 > "$SCRIPT_DIR/secrets/STORAGE_ENCRYPTION_KEY"
# Generates the OIDC secret key for the Grist client
CLIENT_SECRET_OUTPUT="$(docker run authelia/authelia:4 authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986)"
CLIENT_SECRET=$(getSecret "$(grep 'Password' <<< $CLIENT_SECRET_OUTPUT)")
sed -i "/GRIST_CLIENT_SECRET=$/d" "$SCRIPT_DIR/.env"
echo "GRIST_CLIENT_SECRET=$CLIENT_SECRET" >> "$SCRIPT_DIR/.env"
getSecret "$(grep 'Digest' <<< $CLIENT_SECRET_OUTPUT)" >> "$SCRIPT_DIR/secrets/GRIST_CLIENT_SECRET_DIGEST"
# Generate JWT certificates Authelia needs for OIDC
docker run -v ./secrets/certs:/certs authelia/authelia:4 authelia crypto certificate rsa generate -d /certs

View File

@ -0,0 +1,3 @@
DATABASE_PASSWORD=CHANGE THIS PASSWORD
MINIO_PASSWORD=CHANGE THIS PASSWORD
PERSIST_DIR=./persist

View File

@ -0,0 +1,20 @@
This examples shows how to start up Grist that:
- Uses Postgres as a home database,
- Redis as a state store.
- MinIO for snapshot storage
It is STRONGLY RECOMMENDED not to use this container in a way that makes it accessible to the internet.
This setup lacks basic security or authentication.
Other examples demonstrate how to set up authentication and HTTPS.
See https://support.getgrist.com/self-managed for more information.
This setup is based on one provided by Akito (https://github.com/theAkito).
## How to run this example
Before running this example, it's very strongly recommended to update the `_PASSWORD` environment variables
in `.env` to be long, randomly generated passwords.
This example can be run with `docker compose up`.

View File

@ -0,0 +1,76 @@
services:
grist:
image: gristlabs/grist:latest
environment:
# Postgres database setup
TYPEORM_DATABASE: grist
TYPEORM_USERNAME: grist
TYPEORM_HOST: grist-db
TYPEORM_LOGGING: false
TYPEORM_PASSWORD: ${DATABASE_PASSWORD}
TYPEORM_PORT: 5432
TYPEORM_TYPE: postgres
# Redis setup
REDIS_URL: redis://grist-redis
# MinIO setup. This requires the bucket set up on the MinIO instance with versioning enabled.
GRIST_DOCS_MINIO_ACCESS_KEY: grist
GRIST_DOCS_MINIO_SECRET_KEY: ${MINIO_PASSWORD}
GRIST_DOCS_MINIO_USE_SSL: 0
GRIST_DOCS_MINIO_BUCKET: grist-docs
GRIST_DOCS_MINIO_ENDPOINT: grist-minio
GRIST_DOCS_MINIO_PORT: 9000
volumes:
# Where to store persistent data, such as documents.
- ${PERSIST_DIR}/grist:/persist
ports:
- 8484:8484
depends_on:
- grist-db
- grist-redis
- grist-minio
- minio-setup
grist-db:
image: postgres:alpine
environment:
POSTGRES_DB: grist
POSTGRES_USER: grist
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
volumes:
- ${PERSIST_DIR}/postgres:/var/lib/postgresql/data
grist-redis:
image: redis:alpine
volumes:
- ${PERSIST_DIR}/redis:/data
grist-minio:
image: minio/minio:latest
environment:
MINIO_ROOT_USER: grist
MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD}
volumes:
- ${PERSIST_DIR}/minio:/data
command:
server /data --console-address=":9001"
# This sets up the buckets required in MinIO. It is only needed to make this example work.
# It isn't necessary for deployment and can be safely removed.
minio-setup:
image: minio/mc
environment:
MINIO_PASSWORD: ${MINIO_PASSWORD}
depends_on:
grist-minio:
condition: service_started
restart: on-failure
entrypoint: >
/bin/sh -c "
/usr/bin/mc alias set myminio http://grist-minio:9000 grist '$MINIO_PASSWORD';
/usr/bin/mc mb myminio/grist-docs;
/usr/bin/mc anonymous set public myminio/grist-docs;
/usr/bin/mc version enable myminio/grist-docs;
"