diff --git a/.env.sample b/.env.sample index 20e3ac2..600c819 100644 --- a/.env.sample +++ b/.env.sample @@ -8,12 +8,25 @@ DOMAIN=lasuite-docs.example.com LETS_ENCRYPT_ENV=production ############################################################################## -# BASIC SETTINGS +# SECRETS ############################################################################## -# FIXME: Move to Docker secret -DJANGO_SECRET_KEY=ThisIsAnExampleKeyForDevPurposeOnly -# FIXME: Move to docker secret -DJANGO_SUPERUSER_PASSWORD=admin +# abbreviations are to fit abra 12 char secret recommendation +# DJANGO_SECRET_KEY +SECRET_DJANGO_SK_VERSION=v1 +# ODIC_RP_CLIENT_SECRET +SECRET_OIDC_RPCS_VERSION=v1 +# DJANGO_SUPERUSER_PASSWORD +SECRET_DJANGO_SP_VERSION=v1 +# MINIO_ROOT_PASSWORD +SECRET_MINIO_RP_VERSION=v1 +# MINIO_ROOT_USER +SECRET_MINIO_RU_VERSION=v1 +# COLLABORATION_SERVER_SECRET +SECRET_COLLAB_SS_VERSION=v1 +# POSTGRES_PASSWORD +SECRET_POSTGRES_P_VERSION=v1 +# Y_PROVIDER_API_KEY +SECRET_Y_API_KEY_VERSION=v1 ############################################################################## # EMAIL @@ -27,13 +40,12 @@ DJANGO_EMAIL_PORT=1025 # SINGLE SIGN ON ############################################################################## # NOTE: OpenID Connect (OIDC) single sign-on is **required**, see recipe README -OIDC_OP_JWKS_ENDPOINT=https://auth.${DOMAIN}/realms/impress/protocol/openid-connect/certs -OIDC_OP_AUTHORIZATION_ENDPOINT=https://auth.${DOMAIN}/realms/impress/protocol/openid-connect/auth -OIDC_OP_TOKEN_ENDPOINT=https://auth.${DOMAIN}/realms/impress/protocol/openid-connect/token -OIDC_OP_USER_ENDPOINT=https://auth.${DOMAIN}/realms/impress/protocol/openid-connect/userinfo -OIDC_RP_CLIENT_ID=impress -# FIXME: Move to docker secret -OIDC_RP_CLIENT_SECRET=example +OIDC_REALM=yourkeycloakrealm +OIDC_OP_JWKS_ENDPOINT=https://auth.${DOMAIN}/realms/${OIDC_REALM}/protocol/openid-connect/certs +OIDC_OP_AUTHORIZATION_ENDPOINT=https://auth.${DOMAIN}/realms/${OIDC_REALM}/protocol/openid-connect/auth +OIDC_OP_TOKEN_ENDPOINT=https://auth.${DOMAIN}/realms/${OIDC_REALM}/protocol/openid-connect/token +OIDC_OP_USER_ENDPOINT=https://auth.${DOMAIN}/realms/${OIDC_REALM}/protocol/openid-connect/userinfo +OIDC_RP_CLIENT_ID=yourkeycloakclientid OIDC_RP_SIGN_ALGO=RS256 OIDC_RP_SCOPES="openid email" LOGIN_REDIRECT_URL=https://${DOMAIN} diff --git a/abra-entrypoint.sh b/abra-entrypoint.sh new file mode 100644 index 0000000..6366227 --- /dev/null +++ b/abra-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -e + +[ -f /run/secrets/postgres_p ] && export DB_PASSWORD="$(cat /run/secrets/postgres_p)" +[ -f /run/secrets/django_sk ] && export DJANGO_SECRET_KEY="$(cat /run/secrets/django_sk)" +[ -f /run/secrets/minio_rp ] && export AWS_S3_SECRET_ACCESS_KEY="$(cat /run/secrets/minio_rp)" +[ -f /run/secrets/minio_ru ] && export AWS_S3_ACCESS_KEY_ID="$(cat /run/secrets/minio_ru)" +[ -f /run/secrets/django_sp ] && export DJANGO_SUPERUSER_PASSWORD="$(cat /run/secrets/django_sp)" +[ -f /run/secrets/oidc_rpcs ] && export OIDC_RP_CLIENT_SECRET="$(cat /run/secrets/oidc_rpcs)" +[ -f /run/secrets/collab_ss ] && export COLLABORATION_SERVER_SECRET="$(cat /run/secrets/collab_ss)" +[ -f /run/secrets/y_api_key ] && export Y_PROVIDER_API_KEY="$(cat /run/secrets/y_api_key)" + +# if not in "env" mode, then execute the original entrypoint and command +if [ ! "$1" = "-e" ]; then + exec "$@" +fi \ No newline at end of file diff --git a/abra.sh b/abra.sh index f2314ad..7880bff 100755 --- a/abra.sh +++ b/abra.sh @@ -1,12 +1,15 @@ # Set any config versions here # Docs: https://docs.coopcloud.tech/maintainers/handbook/#manage-configs -export NGINX_CONF_VERSION=v2 +export ABRA_ENTRYPOINT_VERSION=v4 +export NGINX_CONF_VERSION=v3 export PG_BACKUP_VERSION=v3 -# environment() { -# # TODO: Add file_env here -# } +environment() { + # this exports all the secrets as environment variables + source /abra-entrypoint.sh -e +} migrate() { + environment python manage.py migrate --noinput } diff --git a/compose.yml b/compose.yml index 67b493f..c65eb69 100644 --- a/compose.yml +++ b/compose.yml @@ -5,9 +5,9 @@ x-common-env: &common-env DJANGO_CONFIGURATION: Production DJANGO_ALLOWED_HOSTS: "*" - DJANGO_SECRET_KEY: + # DJANGO_SECRET_KEY supplied via secrets DJANGO_SETTINGS_MODULE: impress.settings - DJANGO_SUPERUSER_PASSWORD: + # DJANGO_SUPERUSER_PASSWORD supplied via secrets # Logging # Set to DEBUG level for dev only LOGGING_LEVEL_HANDLERS_CONSOLE: @@ -27,9 +27,8 @@ x-common-env: &common-env # Media STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage AWS_S3_ENDPOINT_URL: http://minio:9000 - AWS_S3_ACCESS_KEY_ID: user - # FIXME: Move to docker secret - AWS_S3_SECRET_ACCESS_KEY: password + # AWS_S3_ACCESS_KEY_ID supplied via secret (this is same MINIO_ROOT_USER) + # AWS_S3_SECRET_ACCESS_KEY supplied via secret (this is same as MINIO_ROOT_PASSWORD) MEDIA_BASE_URL: https://${DOMAIN} AWS_STORAGE_BUCKET_NAME: docs-media-storage # OIDC - settings from .env, see .env.sample @@ -38,7 +37,7 @@ x-common-env: &common-env OIDC_OP_TOKEN_ENDPOINT: OIDC_OP_USER_ENDPOINT: OIDC_RP_CLIENT_ID: - OIDC_RP_CLIENT_SECRET: + # OIDC_RP_CLIENT_SECRET supplied via secrets OIDC_RP_SIGN_ALGO: OIDC_RP_SCOPES: LOGIN_REDIRECT_URL: @@ -46,7 +45,7 @@ x-common-env: &common-env LOGOUT_REDIRECT_URL: OIDC_REDIRECT_ALLOWED_HOSTS: OIDC_AUTH_REQUEST_EXTRA_PARAMS: - # AI + # AI (Fixme: remove?) AI_FEATURE_ENABLED: "false" AI_BASE_URL: https://openaiendpoint.com AI_API_KEY: password @@ -58,35 +57,32 @@ x-postgres-env: &postgres-env # Postgresql db container configuration POSTGRES_DB: docs POSTGRES_USER: docs - # FIXME: Move to docker secret - POSTGRES_PASSWORD: password + POSTGRES_PASSWORD_FILE: /run/secrets/postgres_p # App database configuration DB_HOST: db DB_NAME: docs DB_USER: docs - # FIXME: Move to docker secret - DB_PASSWORD: password DB_PORT: 5432 + # DB_PASSWORD supplied via secrets (this is same as POSTGRES_PASSWORD) x-yprovider-env: &yprovider-env COLLABORATION_LOGGING: "true" - Y_PROVIDER_API_KEY: foobar + # Y_PROVIDER_API_KEY supplied via secrets COLLABORATION_API_URL: http://y-provider:4444/api/ COLLABORATION_SERVER_ORIGIN: https://${DOMAIN} - COLLABORATION_SERVER_SECRET: my-secret + # COLLABORATION_SERVER_SECRET supplied via secrets COLLABORATION_BACKEND_BASE_URL: https://${DOMAIN} COLLABORATION_WS_URL: wss://${DOMAIN}/collaboration/ws/ x-minio-env: &minio-env - MINIO_ROOT_USER: user - # FIXME: Move to docker secret - MINIO_ROOT_PASSWORD: password + MINIO_ROOT_USER_FILE: /run/secrets/minio_ru + MINIO_ROOT_PASSWORD_FILE: /run/secrets/minio_rp services: app: image: lasuite/impress-frontend:v3.4.2 networks: - - backend + - backend deploy: labels: - "traefik.enable=false" @@ -106,11 +102,26 @@ services: environment: <<: [*common-env, *postgres-env, *yprovider-env] healthcheck: - test: ["CMD", "python", "manage.py", "check"] + test: ["CMD", "/abra-entrypoint.sh", "python", "manage.py", "check"] interval: 15s timeout: 30s retries: 20 start_period: 10s + command: ["gunicorn", "-c", "/usr/local/etc/gunicorn/impress.py", "impress.wsgi:application"] + entrypoint: ["/abra-entrypoint.sh", "/usr/local/bin/entrypoint"] + configs: + - source: abra_entrypoint + target: /abra-entrypoint.sh + mode: 0555 + secrets: + - django_sk + - django_sp + - oidc_rpcs + - collab_ss + - minio_rp + - minio_ru + - postgres_p + - y_api_key celery: image: lasuite/impress-backend:v3.4.2 @@ -119,13 +130,32 @@ services: command: ["celery", "-A", "impress.celery_app", "worker", "-l", "INFO"] environment: <<: [*common-env, *postgres-env, *yprovider-env] + entrypoint: ["/abra-entrypoint.sh", "/usr/local/bin/entrypoint"] + configs: + - source: abra_entrypoint + target: /abra-entrypoint.sh + mode: 0555 + secrets: + - django_sk + - django_sp + - oidc_rpcs + - collab_ss + - minio_rp + - postgres_p + - y_api_key + y-provider: image: lasuite/impress-y-provider:v3.4.2 networks: - backend environment: *yprovider-env + entrypoint: > + sh -c "export Y_PROVIDER_API_KEY=\"$$(cat /run/secrets/y_api_key)\" && exec /usr/local/bin/entrypoint \"$$@\"" -- + command: ["yarn", "start"] # NOTE: healthcheck - `wget` is available in the container, but `wget http://localhost:4444` gives a 403 + secrets: + - y_api_key db: image: postgres:16 @@ -151,6 +181,8 @@ services: - source: pg_backup target: /pg_backup.sh mode: 0555 + secrets: + - postgres_p redis: image: redis:8 @@ -162,18 +194,23 @@ services: image: minio/mc:RELEASE.2025-05-21T01-59-54Z environment: *minio-env networks: - - backend + - backend entrypoint: > sh -c " - /usr/bin/mc alias set docs http://minio:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD} && \ - /usr/bin/mc mb --ignore-existing docs/docs-media-storage && \ - /usr/bin/mc version enable docs/docs-media-storage && \ - exit 0;" + MINIO_ROOT_USER=\"\$$(cat /run/secrets/minio_ru)\" && + MINIO_ROOT_PASSWORD=\"\$$(cat /run/secrets/minio_rp)\" && + /usr/bin/mc alias set docs http://minio:9000 \$${MINIO_ROOT_USER} \"\$${MINIO_ROOT_PASSWORD}\" && + /usr/bin/mc mb --ignore-existing docs/docs-media-storage && + /usr/bin/mc version enable docs/docs-media-storage && + exit 0" deploy: mode: replicated replicas: 0 restart_policy: condition: none + secrets: + - minio_rp + - minio_ru minio: image: minio/minio:RELEASE.2025-05-24T17-08-30Z @@ -183,15 +220,23 @@ services: interval: 1s timeout: 20s retries: 300 - entrypoint: "" networks: - backend command: minio server /data + entrypoint: ["/usr/bin/docker-entrypoint.sh"] volumes: - minio:/data deploy: labels: backupbot.backup: "${ENABLE_BACKUPS:-true}" + entrypoint: /abra-entrypoint.sh + configs: + - source: abra_entrypoint + target: /abra-entrypoint.sh + mode: 0555 + secrets: + - minio_rp + - minio_ru web: image: nginx:1.29 @@ -201,6 +246,11 @@ services: networks: proxy: backend: + depends_on: + - backend + - app + environment: + - STACK_NAME deploy: labels: - "traefik.enable=true" @@ -223,7 +273,37 @@ volumes: configs: nginx_conf: name: ${STACK_NAME}_nginx_conf_${NGINX_CONF_VERSION} - file: nginx.conf + file: nginx.conf.tmpl + template_driver: golang pg_backup: name: ${STACK_NAME}_pg_backup_${PG_BACKUP_VERSION} file: pg_backup.sh + abra_entrypoint: + name: ${STACK_NAME}_entrypoint_${ABRA_ENTRYPOINT_VERSION} + file: abra-entrypoint.sh + +secrets: + django_sk: + external: true + name: ${STACK_NAME}_django_sk_${SECRET_DJANGO_SK_VERSION} + oidc_rpcs: + external: true + name: ${STACK_NAME}_oidc_rpcs_${SECRET_OIDC_RPCS_VERSION} + django_sp: + external: true + name: ${STACK_NAME}_django_sp_${SECRET_DJANGO_SP_VERSION} + postgres_p: + external: true + name: ${STACK_NAME}_postgres_p_${SECRET_POSTGRES_P_VERSION} + collab_ss: + external: true + name: ${STACK_NAME}_collab_ss_${SECRET_COLLAB_SS_VERSION} + minio_rp: + external: true + name: ${STACK_NAME}_minio_rp_${SECRET_MINIO_RP_VERSION} + minio_ru: + external: true + name: ${STACK_NAME}_minio_ru_${SECRET_MINIO_RP_VERSION} + y_api_key: + external: true + name: ${STACK_NAME}_y_api_key_${SECRET_Y_API_KEY_VERSION} \ No newline at end of file diff --git a/nginx.conf b/nginx.conf.tmpl similarity index 96% rename from nginx.conf rename to nginx.conf.tmpl index efa1b30..07d5d07 100644 --- a/nginx.conf +++ b/nginx.conf.tmpl @@ -1,9 +1,9 @@ upstream docs_backend { - server backend:8000 fail_timeout=0; + server {{ env "STACK_NAME" }}_backend:8000 fail_timeout=0; } upstream docs_frontend { - server app:8080 fail_timeout=0; + server {{ env "STACK_NAME" }}_app:8080 fail_timeout=0; } server { diff --git a/pg_backup.sh b/pg_backup.sh index 2ab1ea2..1fb7892 100644 --- a/pg_backup.sh +++ b/pg_backup.sh @@ -5,8 +5,7 @@ set -e BACKUP_FILE='/var/lib/postgresql/data/pgdata/backup.sql' function backup { - # export PGPASSWORD=$(cat $POSTGRES_PASSWORD_FILE) - export PGPASSWORD="$POSTGRES_PASSWORD" + export PGPASSWORD=$(cat $POSTGRES_PASSWORD_FILE) pg_dump -U ${POSTGRES_USER} ${POSTGRES_DB} > $BACKUP_FILE }