diff --git a/.env.sample b/.env.sample index cc02951..4534b75 100644 --- a/.env.sample +++ b/.env.sample @@ -14,4 +14,11 @@ SITE_NAME=LemmyCloud SLUR_FILTER='(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)' -FEDERATION_ENABLED=true \ No newline at end of file +FEDERATION_ENABLED=true +STRICT_ALLOWLIST=false +SMTP_HOST= +SMTP_NAME= +SMTP_PORT=587 +MAIL_FROM_ADDRESS= +MAIL_DOMAIN= +SECRET_SMTP_PASSWORD_VERSION=v1 diff --git a/compose.yml b/compose.yml index 7443745..c0f2e53 100644 --- a/compose.yml +++ b/compose.yml @@ -1,100 +1,29 @@ ---- -version: "3.8" +x-logging: &default-logging + driver: "json-file" + options: + max-size: "50m" + max-file: "4" services: - app: - user: 0:0 - image: dessalines/lemmy:0.18.3 - 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 - - FEDERATION_ENABLED - volumes: - - lemmy_config:/config/ - secrets: - - admin_password - - postgres_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 + proxy: + image: nginx:1-alpine networks: - backend - deploy: - restart_policy: - condition: on-failure - labels: - - "coop-cloud.${STACK_NAME}.version=0.2.0+0.18.3" - healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8536/api/v3/site"] - interval: 30s - timeout: 10s - retries: 10 - start_period: 5m - - ui: - image: dessalines/lemmy-ui:0.18.3 - environment: - - "LEMMY_UI_LEMMY_INTERNAL_HOST=${STACK_NAME}_app:8536" - - "LEMMY_UI_LEMMY_EXTERNAL_HOST=${DOMAIN}" - # - LEMMY_HTTPS=true - networks: - - backend - depends_on: - - app - healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:1234"] - interval: 30s - timeout: 10s - retries: 10 - start_period: 1m - - db: - image: postgres:15-alpine - environment: - - POSTGRES_USER=lemmy - - POSTGRES_DB=lemmy - - POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password - networks: - - backend - secrets: - - postgres_password - volumes: - - postgres_data:/var/lib/postgresql/data - - - pictrs: - image: asonix/pictrs:0.4.0-beta.19 - user: 991:991 - networks: - - backend - volumes: - - pictrs:/mnt - - web: - image: nginx:1.20.0 - networks: - proxy - - backend - environment: - - DOMAIN - - STACK_NAME + ports: + # actual and only port facing any connection from outside + # Note, change the left number if port 1236 is already in use on your system + # You could use port 80 if you won't use a reverse proxy + - "1236:1236" + - "8536:8536" + #restart: unless-stopped configs: - source: nginx_conf - target: /etc/nginx/conf.d/default.conf + target: /etc/nginx/nginx.conf depends_on: - - app + - pictrs + - lemmy-ui + logging: *default-logging deploy: restart_policy: condition: on-failure @@ -109,11 +38,145 @@ services: - "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLForceHost=true" - "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLHost=${DOMAIN}" + app: + networks: + - backend + image: dessalines/lemmy:0.19.5 + # this hostname is used in nginx reverse proxy and also for lemmy ui to connect to the backend + hostname: "${STACK_NAME}" + #restart: unless-stopped + secrets: + - admin_password + - postgres_password + - smtp_password + environment: + - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" + - RUST_BACKTRACE=full + - DOMAIN + - STACK_NAME + - ADMIN_USERNAME + - SITE_NAME + - SLUR_FILTER + - FEDERATION_ENABLED + - STRICT_ALLOWLIST + - SMTP_HOST + - SMTP_NAME + - SMTP_PORT=587 + - MAIL_FROM_ADDRESS + - MAIL_DOMAIN + ports: + # prometheus metrics can be enabled with the `prometheus` config option. they are available on + # port 10002, path /metrics by default + - "10002:10002" + volumes: + - lemmy_config:/config/config.hjson:Z + configs: + - source: config_hjson_conf + target: /config/config.hjson + depends_on: + - postgres + - pictrs + logging: *default-logging + + lemmy-ui: + image: dessalines/lemmy-ui:0.19.5 + hostname: "${STACK_NAME}_ui" + networks: + - backend + environment: + # this needs to match the hostname defined in the lemmy service + - "LEMMY_UI_LEMMY_INTERNAL_HOST=${STACK_NAME}:8536" + # set the outside hostname here + - "LEMMY_UI_LEMMY_EXTERNAL_HOST=${DOMAIN}" + - LEMMY_UI_HTTPS=false + - LEMMY_UI_DEBUG=true + depends_on: + - app + #restart: unless-stopped + logging: *default-logging + init: true + + pictrs: + image: asonix/pictrs:0.5.16 + # this needs to match the pictrs url in lemmy.hjson + hostname: "${STACK_NAME}_pictrs" + networks: + - backend + # we can set options to pictrs like this, here we set max. image size and forced format for conversion + # entrypoint: /sbin/tini -- /usr/local/bin/pict-rs -p /mnt -m 4 --image-format webp + environment: + - PICTRS_OPENTELEMETRY_URL=http://otel:4137 + - PICTRS__API_KEY=API_KEY + - RUST_LOG=debug + - RUST_BACKTRACE=full + - PICTRS__MEDIA__VIDEO_CODEC=vp9 + - PICTRS__MEDIA__GIF__MAX_WIDTH=256 + - PICTRS__MEDIA__GIF__MAX_HEIGHT=256 + - PICTRS__MEDIA__GIF__MAX_AREA=65536 + - PICTRS__MEDIA__GIF__MAX_FRAME_COUNT=400 + user: 991:991 + volumes: + - pictrs:/mnt:Z + #restart: unless-stopped + logging: *default-logging + + postgres: + image: pgautoupgrade/pgautoupgrade:16-alpine + # this needs to match the database host in lemmy.hson + # Tune your settings via + # https://pgtune.leopard.in.ua/#/ + # You can use this technique to add them here + # https://stackoverflow.com/a/30850095/1655478 + hostname: "${STACK_NAME}_postgres" + networks: + - backend + command: + [ + "postgres", + "-c", + "session_preload_libraries=auto_explain", + "-c", + "auto_explain.log_min_duration=5ms", + "-c", + "auto_explain.log_analyze=true", + "-c", + "auto_explain.log_triggers=true", + "-c", + "track_activity_query_size=1048576", + ] + ports: + # use a different port so it doesn't conflict with potential postgres db running on the host + - "5433:5432" + environment: + - POSTGRES_USER=lemmy + - POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password + - POSTGRES_DB=lemmy + volumes: + - postgres_data:/var/lib/postgresql/data:Z + networks: + - backend + secrets: + - postgres_password + #restart: unless-stopped + logging: *default-logging +networks: + backend: + proxy: + external: true volumes: lemmy_config: postgres_data: pictrs: - +secrets: + admin_password: + external: true + name: ${STACK_NAME}_admin_password_${SECRET_ADMIN_PASSWORD_VERSION} + postgres_password: + external: true + name: ${STACK_NAME}_postgres_password_${SECRET_POSTGRES_PASSWORD_VERSION} + smtp_password: + external: true + name: ${STACK_NAME}_smtp_password_${SECRET_SMTP_PASSWORD_VERSION} configs: entrypoint_app_conf: name: ${STACK_NAME}_entrypoint_app_${ENTRYPOINT_APP_VERSION} @@ -126,16 +189,3 @@ configs: 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} - postgres_password: - external: true - name: ${STACK_NAME}_postgres_password_${SECRET_POSTGRES_PASSWORD_VERSION} - -networks: - backend: - proxy: - external: true diff --git a/config.hjson.tmpl b/config.hjson.tmpl index dd6f0f3..c1e713d 100644 --- a/config.hjson.tmpl +++ b/config.hjson.tmpl @@ -2,62 +2,53 @@ # for more info about the config, check out the documentation # https://join-lemmy.org/docs/en/administration/configuration.html + # This is a minimal lemmy config for the dev / main branch. Do not use for a + # release / stable version. + setup: { - # username for the admin user admin_username: "{{ env "ADMIN_USERNAME" }}" - # password for the admin user admin_password: "{{ secret "admin_password" }}" - # name of the site (can be changed later) site_name: "{{ env "SITE_NAME" }}" } - - # opentelemetry_url: "http://otel:4137" - - # the domain name of your instance (eg "lemmy.ml") - hostname: "{{ env "DOMAIN" }}" - # address where lemmy should listen for incoming requests - bind: "0.0.0.0" - # port where lemmy should listen for incoming requests - port: 8536 - # settings related to the postgresql database - # address where pictrs is available - pictrs_url: "http://{{ env "STACK_NAME" }}_pictrs:8080" database: { - # name of the postgres database for lemmy + host: {{ env "STACK_NAME" }}_postgres database: "lemmy" - # username to connect to postgres user: "lemmy" - # password to connect to postgres password: "{{ secret "postgres_password" }}" - # host where postgres is running - host: "db" - # port where postgres can be accessed - port: 5432 - # maximum number of active sql connections - pool_size: 5 } + + hostname: "{{ env "DOMAIN" }}" + bind: "0.0.0.0" + port: 8536 + + pictrs: { + url: "http://{{ env "STACK_NAME" }}_pictrs:8080/" + # api_key: "API_KEY" + image_mode: None + } + + #opentelemetry_url: "http://otel:4137" slur_filter: ''' {{ env "SLUR_FILTER" }} ''' -# # optional: email sending configuration -# email: { -# # hostname and port of the smtp server -# smtp_server: "" -# # login name for smtp server -# smtp_login: "" -# # password to login to the smtp server -# smtp_password: "" -# # address to send emails from, eg "noreply@your-instance.com" -# smtp_from_address: "" -# # whether or not smtp connections should use tls -# use_tls: true -# } + email: { + # hostname and port of the smtp server + smtp_server: "{{ env "SMTP_HOST" }}:{{ env "SMTP_PORT" }}" + # login name for smtp server + smtp_login: "{{ env "SMTP_NAME" }}" + # password to login to the smtp server + smtp_password: "{{ secret "smtp_password" }}" + # address to send emails from, eg "noreply@your-instance.com" + smtp_from_address: "{{ env "MAIL_FROM_ADDRESS" }}" + # whether or not smtp connections should use tls + use_tls: true + } # Settings related to activitypub federation federation: { enabled: {{ env "FEDERATION_ENABLED" }} - strict_allowlist: false + strict_allowlist: {{ env "STRICT_ALLOWLIST" }} http_fetch_retry_limit: 25 worker_count: 64 debug: false diff --git a/nginx.conf.tmpl b/nginx.conf.tmpl index ebced89..63bb0fc 100644 --- a/nginx.conf.tmpl +++ b/nginx.conf.tmpl @@ -1,81 +1,72 @@ -limit_req_zone $binary_remote_addr zone=lemmy_ratelimit:10m rate=1r/s; - -upstream backend { - server "{{ env "STACK_NAME" }}_app:8536"; +worker_processes 1; +events { + worker_connections 1024; } - -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; +http { + upstream lemmy { + # this needs to map to the lemmy (server) docker service hostname + server "{{ env "STACK_NAME" }}:8536"; + } + upstream lemmy-ui { + # this needs to map to the lemmy-ui docker service hostname + server "{{ env "STACK_NAME" }}_ui:1234"; } - # 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"; + server { + # this is the port inside docker, not the public one yet + listen 1236; + listen 8536; + # change if needed, this is facing the public web + server_name {{ env "DOMAIN" }}; + server_tokens off; - # Rate limit - limit_req zone=lemmy_ratelimit burst=30 nodelay; + gzip on; + gzip_types text/css application/javascript image/svg+xml; + gzip_vary on; - # 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; + # Upload limit, relevant for pictrs + client_max_body_size 20M; + + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + + # frontend general requests + location / { + # distinguish between ui requests and backend + # don't change lemmy-ui or lemmy here, they refer to the upstream definitions on top + set $proxpass "http://lemmy-ui"; + + if ($http_accept = "application/activity+json") { + set $proxpass "http://lemmy"; + } + if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") { + set $proxpass "http://lemmy"; + } + if ($request_method = POST) { + set $proxpass "http://lemmy"; + } + 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|version|.well-known) { + proxy_pass "http://lemmy"; + # proxy common stuff + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # 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; + } } - - - # 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;