diff --git a/abra.sh b/abra.sh new file mode 100644 index 0000000..c16890a --- /dev/null +++ b/abra.sh @@ -0,0 +1,3 @@ +export NGINX_CONF_VERSION=v5 +export ENTRYPOINT_APP_VERSION=v2 +export CONFIG_HJSON_VERSION=v4 diff --git a/compose.yml b/compose.yml index 384caee..9143d05 100644 --- a/compose.yml +++ b/compose.yml @@ -6,39 +6,44 @@ services: image: dessalines/lemmy:0.16.1 environment: - RUST_LOG="warn,lemmy_server=info,lemmy_api=info,lemmy_api_common=info,lemmy_api_crud=info,lemmy_apub=info,lemmy_db_schema=info,lemmy_db_views=info,lemmy_db_views_actor=info,lemmy_db_views_moderator=info,lemmy_routes=info,lemmy_utils=info,lemmy_websocket=info" + - DOMAIN + - STACK_NAME + - ADMIN_USERNAME + - SITE_NAME + - SLUR_FILTER volumes: - - ./lemmy.hjson:/config/config.hjson + - lemmy_config:/config/ + secrets: + - admin_password + entrypoint: ['/docker-entrypoint.sh'] + # entrypoint: ['tail', '-f', '/dev/null'] + configs: + - source: config_hjson_conf + target: /config/config.hjson.tmpl + - source: entrypoint_app_conf + target: /docker-entrypoint.sh + mode: 0555 depends_on: - postgres - pictrs + networks: + - backend deploy: restart_policy: condition: on-failure labels: - "coop-cloud.${STACK_NAME}.version=" - lemmy-ui: + ui: image: dessalines/lemmy-ui:0.16.1 environment: - - LEMMY_INTERNAL_HOST=lemmy:8536 - - LEMMY_EXTERNAL_HOST="${DOMAIN}" - - LEMMY_HTTPS=true + - "LEMMY_INTERNAL_HOST=${STACK_NAME}_app:8536" + - "LEMMY_EXTERNAL_HOST=${DOMAIN}" + # - LEMMY_HTTPS=true + networks: + - backend depends_on: - app - deploy: - restart_policy: - condition: on-failure - labels: - - "traefik.enable=true" - - "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=1235" - - "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})" - - "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure" - - "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}" - # Redirect from EXTRA_DOMAINS to DOMAIN - - "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect" - - "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLForceHost=true" - - "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLHost=${DOMAIN}" - - "coop-cloud.${STACK_NAME}.version=" db: image: postgres:12-alpine @@ -46,30 +51,71 @@ services: - POSTGRES_USER=lemmy - POSTGRES_PASSWORD=password - POSTGRES_DB=lemmy + networks: + - backend volumes: - - ./volumes/postgres:/var/lib/postgresql/data + - postgres_data:/var/lib/postgresql/data pictrs: image: asonix/pictrs:0.3.0-beta.12-r1 - ports: - - "127.0.0.1:8537:8080" - - "127.0.0.1:6670:6669" user: 991:991 + networks: + - backend volumes: - - ./volumes/pictrs:/mnt + - pictrs:/mnt web: image: nginx:1.20.0 networks: - proxy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost"] - interval: 30s - timeout: 10s - retries: 10 - start_period: 1m + - backend + environment: + - DOMAIN + - STACK_NAME + configs: + - source: nginx_conf + target: /etc/nginx/conf.d/default.conf + depends_on: + - app + deploy: + restart_policy: + condition: on-failure + labels: + - "traefik.enable=true" + - "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=80" + - "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})" + - "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure" + - "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}" + # Redirect from EXTRA_DOMAINS to DOMAIN + - "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect" + - "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLForceHost=true" + - "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLHost=${DOMAIN}" + +volumes: + lemmy_config: + postgres_data: + pictrs: + +configs: + entrypoint_app_conf: + name: ${STACK_NAME}_entrypoint_app_${ENTRYPOINT_APP_VERSION} + file: entrypoint.sh + nginx_conf: + name: ${STACK_NAME}_nginx_${NGINX_CONF_VERSION} + file: nginx.conf.tmpl + template_driver: golang + config_hjson_conf: + name: ${STACK_NAME}_config_hjson_${CONFIG_HJSON_VERSION} + file: config.hjson.tmpl + template_driver: golang + +secrets: + admin_password: + external: true + name: ${STACK_NAME}_admin_password_${SECRET_ADMIN_PASSWORD_VERSION} networks: + backend: proxy: external: true diff --git a/config.hjson b/config.hjson.tmpl similarity index 95% rename from config.hjson rename to config.hjson.tmpl index c2c1520..fec8efa 100644 --- a/config.hjson +++ b/config.hjson.tmpl @@ -21,7 +21,7 @@ port: 8536 # settings related to the postgresql database # address where pictrs is available - pictrs_url: "http://pictrs:8080" + pictrs_url: "http://{{ env "STACK_NAME" }}_pictrs:8080" database: { # name of the postgres database for lemmy database: "lemmy" @@ -30,7 +30,7 @@ # password to connect to postgres password: "password" # host where postgres is running - host: "postgres" + host: "db" # port where postgres can be accessed port: 5432 # maximum number of active sql connections diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..f91ea3a --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +if [ ! -f /config/config.hjson ]; then + cp /config/config.hjson.tmpl /config/config.hjson +fi + +# https://github.com/LemmyNet/lemmy/blob/main/docker/prod/Dockerfile +/app/lemmy diff --git a/nginx.conf.tmpl b/nginx.conf.tmpl new file mode 100644 index 0000000..ebced89 --- /dev/null +++ b/nginx.conf.tmpl @@ -0,0 +1,81 @@ +limit_req_zone $binary_remote_addr zone=lemmy_ratelimit:10m rate=1r/s; + +upstream backend { + server "{{ env "STACK_NAME" }}_app:8536"; +} + +upstream frontend { + server "{{ env "STACK_NAME" }}_ui:1234"; +} + +server { + listen 80; + listen [::]:80; + server_name {{ env "DOMAIN" }}; + + # Hide nginx version + server_tokens off; + + # Enable compression for JS/CSS/HTML bundle, for improved client load times. + # It might be nice to compress JSON, but leaving that out to protect against potential + # compression+encryption information leak attacks like BREACH. + gzip on; + gzip_types text/css application/javascript image/svg+xml; + gzip_vary on; + + # Upload limit for pictrs + client_max_body_size 20M; + + # frontend + location / { + set $proxpass "http://frontend"; + if ($http_accept ~ "^application/.*$") { + set $proxpass "http://backend"; + } + if ($request_method = POST) { + set $proxpass "http://backend"; + } + proxy_pass $proxpass; + + rewrite ^(.+)/+$ $1 permanent; + + # Send actual client IP upstream + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + # backend + location ~ ^/(api|pictrs|feeds|nodeinfo|.well-known) { + proxy_pass "http://{{ env "STACK_NAME" }}_app:8536"; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Rate limit + limit_req zone=lemmy_ratelimit burst=30 nodelay; + + # Add IP forwarding headers + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + + # Redirect pictshare images to pictrs + location ~ /pictshare/(.*)$ { + return 301 /pictrs/image/$1; + } + +} + +# Anonymize IP addresses +# https://www.supertechcrew.com/anonymizing-logs-nginx-apache/ +map $remote_addr $remote_addr_anon { + ~(?P\d+\.\d+\.\d+)\. $ip.0; + ~(?P[^:]+:[^:]+): $ip::; + 127.0.0.1 $remote_addr; + ::1 $remote_addr; + default 0.0.0.0; +} +access_log /var/log/nginx/access.log combined;