diff --git a/Caddyfile b/Caddyfile deleted file mode 100644 index 23268c6..0000000 --- a/Caddyfile +++ /dev/null @@ -1,77 +0,0 @@ -# Example Caddyfile for InvenTree -# The following environment variables may be used: -# - INVENTREE_SITE_URL: The upstream URL of the InvenTree site (default: inventree.localhost) -# - INVENTREE_SERVER: The internal URL of the InvenTree container (default: http://inventree-server:8000) -# -# Note that while this file is a good starting point, it may need to be modified to suit your specific requirements -# -# Ref to the Caddyfile documentation: https://caddyserver.com/docs/caddyfile - - -# Logging configuration for Caddy -(log_common) { - log { - output file /var/log/caddy/{args[0]}.access.log - } -} - -# CORS headers control (used for static and media files) -(cors-headers) { - header Allow GET,HEAD,OPTIONS - header Access-Control-Allow-Origin * - header Access-Control-Allow-Methods GET,HEAD,OPTIONS - header Access-Control-Allow-Headers Authorization,Content-Type,User-Agent - - @cors_preflight{args[0]} method OPTIONS - - handle @cors_preflight{args[0]} { - respond "" 204 - } -} - -# The default server address is configured in the .env file -# If not specified, the default address is used - http://inventree.localhost -# If you need to listen on multiple addresses, or use a different port, you can modify this section directly -http://inventree.ctbk.de { - import log_common inventree - - encode gzip - - request_body { - max_size 100MB - } - - # Handle static request files - handle_path /static/* { - import cors-headers static - - root * /var/www/static - file_server - } - - # Handle media request files - handle_path /media/* { - import cors-headers media - - root * /var/www/media - file_server - - # Force download of media files (for security) - # Comment out this line if you do not want to force download - header Content-Disposition attachment - - # Authentication is handled by the forward_auth directive - # This is required to ensure that media files are only accessible to authenticated users - forward_auth {$INVENTREE_SERVER:"http://inventree-server:8000"} { - uri /auth/ - } - } - - # All other requests are proxied to the InvenTree server - reverse_proxy {$INVENTREE_SERVER:"http://inventree-server:8000"} { - - # If you are running behind another proxy, you may need to specify 'trusted_proxies' - # Ref: https://caddyserver.com/docs/json/apps/http/servers/trusted_proxies/ - # trusted_proxies ... - } -} diff --git a/Makefile b/Makefile index 11ba6f3..a152205 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,9 @@ psql: update: read -p "Update will cause downtime of the server. Are you sure you want to continue? Press Ctrl+c to abort!" _ - $(COMPOSE) pull $(COMPOSE) down - $(COMPOSE) run --rm inventree-server invoke update + $(COMPOSE) pull + $(COMPOSE) run inventree-server invoke update $(COMPOSE) up -d data: # podman does not autocreate data folder diff --git a/README.md b/README.md index 7b98dfd..ca162cf 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ Copy the `sample.env` into a file named `.env` and make sure to adapt all values In order to run invoke an update or complete the first setup, the data folder must be created (`mkdir data`) and `make update` is used to pull the latest images and apply all database migrations. +`make update` involves the command `docker compose run --rm inventree-server invoke update` which is performs the initial database setup. + +To create an admin account, the command `docker compose run inventree-server invoke superuser` can be run. + The command `make up` can be used to run the setup as a foreground service, `make "up -d"` can be used to run the setup in detached mode. > Warning: @@ -30,7 +34,7 @@ This can be required because Docker does not like ZFS and might have issues to s ### SSO -The following is an example on configuring SSO using OIDC and Keycloak as IdP. See the [InvenTree SSO docs](https://docs.inventree.org/en/latest/settings/SSO) as well as the [AllAuth social providers](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/index.html) for more details. Note that Keycloak is not a valid provider anymore any OIDC [should be used](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/keycloak.html). +The following is an example on configuring SSO using OIDC and Keycloak as IdP. See the [InvenTree SSO docs](https://docs.inventree.org/en/latest/settings/SSO) as well as the [AllAuth social providers](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/index.html) for more details. Note that Keycloak is not a valid provider anymore and OIDC [should be used](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/keycloak.html). #### Keycloak diff --git a/backup.sh b/backup.sh deleted file mode 100644 index 81ad1e5..0000000 --- a/backup.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -time=$(date +"%Y-%m-%dT%H:%M:%S%z") -dir=backup -[ -d "${dir}" ] || mkdir -p "${dir}" -docker compose run --rm -u postgres inventree-db sh -c 'PGPASSWORD=$POSTGRES_PASSWORD pg_dump -h inventree-db -p 5432 -U $POSTGRES_USER inventree' > "${dir}/${time}.sql" -# to restore: pg_restore -d newdb db.dump diff --git a/compose.yml b/compose.yml index 8f80693..dc7908c 100644 --- a/compose.yml +++ b/compose.yml @@ -1,8 +1,10 @@ +version: "3.8" + # Docker compose recipe for a production-ready InvenTree setup, with the following containers: # - PostgreSQL as the database backend # - gunicorn as the InvenTree web server # - django-q as the InvenTree background worker process -# - Caddy as a reverse proxy +# - nginx as a reverse proxy # - redis as the cache manager (optional, disabled by default) # --------------------- @@ -32,20 +34,16 @@ # INVENTREE_TAG=0.7.5 # -# ---------------------------- -# Docker compose customization -# ---------------------------- -# If you wish to customize the docker-compose script, you should only do so if you understand the stack! -# Do not expect support for customizations that are not part of the standard InvenTree setup! - services: # Database service # Use PostgreSQL as the database backend inventree-db: - image: postgres:17-alpine container_name: inventree-db + image: ${POSTGRES_IMAGE:?You must provide the 'POSTGRES_IMAGE' variable in the .env file} expose: - ${INVENTREE_DB_PORT:-5432}/tcp + env_file: + - .env environment: - PGDATA=/var/lib/postgresql/data/pgdb - POSTGRES_USER=${INVENTREE_DB_USER:?You must provide the 'INVENTREE_DB_USER' variable in the .env file} @@ -53,43 +51,62 @@ services: - POSTGRES_DB=${INVENTREE_DB_NAME:?You must provide the 'INVENTREE_DB_NAME' variable in the .env file} volumes: # Map 'data' volume such that postgres database is stored externally - - ${INVENTREE_EXT_VOLUME:?You must specify the 'INVENTREE_EXT_VOLUME' variable in the .env file!}:/var/lib/postgresql/data/:z + - inventree_data:/var/lib/postgresql/data/:z restart: unless-stopped # redis acts as database cache manager + # only runs under the "redis" profile : https://docs.docker.com/compose/profiles/ inventree-cache: - image: redis:7-alpine container_name: inventree-cache + image: ${REDIS_IMAGE:?You must provide the 'REDIS_IMAGE' variable in the .env file} + depends_on: + - inventree-db env_file: - .env + profiles: + - redis expose: - - ${INVENTREE_CACHE_PORT:-6379} + - ${INVENTREE_CACHE_PORT:-6379} restart: always # InvenTree web server service # Uses gunicorn as the web server inventree-server: - # If you wish to specify a particular InvenTree version, do so here - image: inventree/inventree:${INVENTREE_TAG:-stable} container_name: inventree-server - # Only change this port if you understand the stack. + # If you wish to specify a particular InvenTree version, do so here + image: ${INVENTREE_IMAGE:?You must provide the 'INVENTREE_IMAGE' variable in the .env file} expose: - - 8000 + - 8000 + env_file: + - .env + environment: + INVENTREE_SOCIAL_PROVIDERS: | + { + "openid_connect": { + "SERVERS": [{ + "id": "oidc", + "name": "Hacknang SSO", + "server_url": "${HKNG_OIDC_URL:?You must provide the 'HKNG_OIDC_URL' variable in the .env file}", + "APP": { + "client_id": "${HKNG_OIDC_CLIENT_ID:?You must provide the 'HKNG_OIDC_CLIENT_ID' variable in the .env file}", + "secret": "${HKNG_OIDC_CLIENT_SECRET:?You must provide the 'HKNG_OIDC_CLIENT_SECRET' variable in the .env file}" + } + }] + } + } depends_on: - inventree-db - - inventree-cache - env_file: - - .env volumes: # Data volume must map to /home/inventree/data - - ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z + - inventree_data:/home/inventree/data:z + - ./plugins:/home/inventree/InvenTree/plugins:z restart: unless-stopped # Background worker process handles long-running or periodic tasks inventree-worker: - # If you wish to specify a particular InvenTree version, do so here - image: inventree/inventree:${INVENTREE_TAG:-stable} container_name: inventree-worker + # If you wish to specify a particular InvenTree version, do so here + image: ${INVENTREE_IMAGE:?You must provide the 'INVENTREE_IMAGE' variable in the .env file} command: invoke worker depends_on: - inventree-server @@ -97,26 +114,36 @@ services: - .env volumes: # Data volume must map to /home/inventree/data - - ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z + - inventree_data:/home/inventree/data:z restart: unless-stopped - # caddy acts as reverse proxy and static file server - # https://hub.docker.com/_/caddy + # nginx acts as a reverse proxy + # static files are served directly by nginx + # media files are served by nginx, although authentication is redirected to inventree-server + # web requests are redirected to gunicorn + # NOTE: You will need to provide a working nginx.conf file! inventree-proxy: container_name: inventree-proxy - image: caddy:alpine - restart: always + image: ${NGINX_IMAGE:?You must provide the 'NGINX_IMAGE' variable in the .env file} depends_on: - inventree-server ports: - - ${INVENTREE_WEB_PORT:-80}:80 - - 443:443 - env_file: - - .env + # Default web port is 1337 (can be changed in the .env file) + - ${INVENTREE_WEB_PORT:-1337}:8080 volumes: - - ./Caddyfile:/etc/caddy/Caddyfile:ro,z - - ${INVENTREE_EXT_VOLUME}/static:/var/www/static:z - - ${INVENTREE_EXT_VOLUME}/media:/var/www/media:z - - ${INVENTREE_EXT_VOLUME}:/var/log:z - - ${INVENTREE_EXT_VOLUME}:/data:z - - ${INVENTREE_EXT_VOLUME}:/config:z + # Provide nginx configuration file to the container + # Refer to the provided example file as a starting point + - ./nginx.prod.conf:/etc/nginx/conf.d/default.conf:ro,Z + # nginx proxy needs access to static and media files + - inventree_data:/var/www:z + restart: unless-stopped + +volumes: + # Persistent data, stored external to the container(s) + inventree_data: + driver: local + driver_opts: + type: none + o: bind + # This directory specified where InvenTree data are stored "outside" the docker containers + device: ${INVENTREE_EXT_VOLUME:?You must specify the 'INVENTREE_EXT_VOLUME' variable in the .env file!} diff --git a/nginx.prod.conf b/nginx.prod.conf new file mode 100644 index 0000000..1ebdcd2 --- /dev/null +++ b/nginx.prod.conf @@ -0,0 +1,64 @@ +server { + + # Listen for connection on (internal) port 8080 (unprivileged nginx) + listen 8080; + + real_ip_header proxy_protocol; + + location / { + + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-By $server_addr:$server_port; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header CLIENT_IP $remote_addr; + + proxy_pass_request_headers on; + + proxy_redirect off; + + client_max_body_size 100M; + + proxy_buffering off; + proxy_request_buffering off; + + # Change 'inventree-server' to the name of the inventree server container, + # and '8000' to the INVENTREE_WEB_PORT (if not default) + proxy_pass http://inventree-server:8000; + } + + # Redirect any requests for static files + location /static/ { + alias /var/www/static/; + autoindex on; + + # Caching settings + expires 30d; + add_header Pragma public; + add_header Cache-Control "public"; + } + + # Redirect any requests for media files + location /media/ { + alias /var/www/media/; + + # Media files require user authentication + auth_request /auth; + + # Content header to force download + add_header Content-disposition "attachment"; + } + + # Use the 'user' API endpoint for auth + location /auth { + internal; + + proxy_pass http://inventree-server:8000/auth/; + + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + } + +} diff --git a/sample.env b/sample.env index 859f58e..b22915b 100644 --- a/sample.env +++ b/sample.env @@ -1,55 +1,67 @@ -# InvenTree environment variables for docker compose deployment -# For a full list of the available configuration options, refer to the InvenTree documentation: -# https://docs.inventree.org/en/stable/start/config/ - -# Specify the name of the docker-compose project +# InvenTree environment variables for a postgresql production setup COMPOSE_PROJECT_NAME=inventree -# InvenTree version tag (e.g. 'stable' / 'latest' / 'x.x.x') -INVENTREE_TAG=stable +# Location of persistent database data (stored external to the docker containers) +# Note: You *must* un-comment this line, and point it to a path on your local machine -# InvenTree server URL - update this to match your server URL -INVENTREE_SITE_URL="http://inventree.localhost" -#INVENTREE_SITE_URL="http://192.168.1.2" # You can specify a local IP address here -#INVENTREE_SITE_URL="https://inventree.my-domain.com" # Or a public domain name (which you control) +# e.g. Linux +INVENTREE_EXT_VOLUME=data -# Specify the location of the external data volume -# By default, placed in local directory 'inventree-data' -INVENTREE_EXT_VOLUME=./inventree-data +# e.g. Windows (docker desktop) +#INVENTREE_EXT_VOLUME=c:/Users/me/inventree-data + +# Default web port for the InvenTree server +INVENTREE_WEB_PORT=8080 # Ensure debug is false for a production setup +INVENTREE_DEBUG=False INVENTREE_LOG_LEVEL=WARNING -# Enable custom plugins? -INVENTREE_PLUGINS_ENABLED=True - -# Run migrations automatically? -INVENTREE_AUTO_UPDATE=True - -# InvenTree superuser account details -# Un-comment (and complete) these lines to auto-create an admin account -#INVENTREE_ADMIN_USER= -#INVENTREE_ADMIN_PASSWORD= -#INVENTREE_ADMIN_EMAIL= - # Database configuration options -# DO NOT CHANGE THESE SETTINGS (unless you really know what you are doing) +# Note: The example setup is for a PostgreSQL database INVENTREE_DB_ENGINE=postgresql INVENTREE_DB_NAME=inventree INVENTREE_DB_HOST=inventree-db INVENTREE_DB_PORT=5432 -# Database credentials - These should be changed from the default values! -# Note: These are *NOT* the InvenTree server login credentials, -# they are the credentials for the PostgreSQL database -INVENTREE_DB_USER=pguser -INVENTREE_DB_PASSWORD=pgpassword - -# Redis cache setup -# Refer to the documentation for other cache options -INVENTREE_CACHE_ENABLED=True -INVENTREE_CACHE_HOST=inventree-cache -INVENTREE_CACHE_PORT=6379 +# Redis cache setup (disabled by default) +# Un-comment the following lines to enable Redis cache +# Note that you will also have to run docker-compose with the --profile redis command +# Refer to settings.py for other cache options +#INVENTREE_CACHE_HOST=inventree-cache +#INVENTREE_CACHE_PORT=6379 # Options for gunicorn server -INVENTREE_GUNICORN_TIMEOUT=90 +INVENTREE_GUNICORN_TIMEOUT=30 + +# Enable custom plugins? +INVENTREE_PLUGINS_ENABLED=False + +# Image tag that should be used +INVENTREE_IMAGE=inventree/inventree:0.11.3 +REDIS_IMAGE=redis:7.0-alpine +NGINX_IMAGE=nginxinc/nginx-unprivileged:stable-alpine +# Postgres image must match version of pgdump in inventree image +POSTGRES_IMAGE=postgres:13-alpine + +# InvenTree admin account details +# make sure to use secure credentials these lines to auto-create an admin acount +INVENTREE_ADMIN_USER=admin +INVENTREE_ADMIN_PASSWORD=password +INVENTREE_ADMIN_EMAIL=admin@inventree.example + +# Database credentials - These must be configured before running +# Change from the default values! +INVENTREE_DB_USER=inventree +INVENTREE_DB_PASSWORD=password + +# Django configuration +INVENTREE_SECRET_KEY=some-secret-key +ALLOWED_HOSTS=inventree.example.com,www.inventree.example.com + +# SSO Config +INVENTREE_SOCIAL_BACKENDS=allauth.socialaccount.providers.openid_connect + +HKNG_OIDC_URL=https://keycloak.example.com/realms/master/.well-known/openid-configuration +HKNG_OIDC_CLIENT_ID=example-client +HKNG_OIDC_SECRET=example-secret