Discourse отображает IP-адрес сервера/localhost как IP-адрес пользователя

Привет! Я развернул Discourse на собственном сервере, всё работает нормально, кроме определения IP-адреса пользователя. Я даже написал PHP-скрипт (вне Docker-контейнера), чтобы получить все заголовки, в которых может содержаться IP:

REMOTE_ADDR: 212.58.xxx.xxx
SERVER_PORT: 80
SERVER_ADDR: 85.25.xxx.xxx
SERVER_SOFTWARE: Apache:
HTTP_CF_CONNECTING_IP: 212.58.xxx.xxx
HTTP_CDN_LOOP: cloudflare
HTTP_X_REAL_IP: 162.158.xxx.xxx

Детали:
На сервере установлена панель BrainyCP с Apache и Nginx (в данный момент сайт использует Nginx, который реверс-проксирует Docker-контейнер).
HTTP_CF_CONNECTING_IP внутри Docker-контейнера возвращает 127.0.0.1, но снаружи значения нормальные.
Без изменения заголовка с помощью пользовательских команд Discourse отображает IP-адрес сервера.

(Скоро добавлю больше деталей, так как мой экземпляр Discourse сейчас пересобирается)

Я не совсем уверен насчёт вашей конфигурации, но заметил Cloudflare в вашем сообщении. Добавили ли вы шаблон CloudFlare в файл app.yml?

  - "templates/cloudflare.template.yml"

РЕДАКТИРОВАНИЕ: использовал не те " "

Нет, я добавил его в список шаблонов, теперь ожидаю пересборки

Собрал заново, но всё ещё отображается IP-сервера (я закомментировал пользовательские команды

## Любые пользовательские команды для выполнения после сборки
run:
  - exec: echo "Начало пользовательских команд"
  ## Если хотите установить адрес электронной почты в поле 'От кого' для первой регистрации, раскомментируйте и измените:
  ## После получения первого письма о регистрации закомментируйте строку обратно. Выполнять нужно только один раз.
  - exec: rails r "SiteSetting.notification_email='noreply@zeronet.space'"
  #- replace:
  #   filename: /etc/nginx/conf.d/discourse.conf
  #   from: "types {"
  #   to: |
  #     set_real_ip_from 85.25.134.45;
  #     real_ip_header CF-Connection-IP;
  #     real_ip_recursive on;
  #     types {
  #- replace:
  #   filename: /etc/nginx/conf.d/discourse.conf
  #   from: $proxy_add_x_forwarded_for
  #   to: $send_http_cf_connection_ip;
  #   global: true
  - exec: echo "Конец пользовательских команд"

Мне нужно их раскомментировать? (также хочу отметить, что они не работали даже до того, как я добавил шаблон Cloudflare в app.yml))

Без комментария $sent_http_cf_connection_ip возвращает 127.0.0.1
image

Nginx, скорее всего, сообщает 127.0.0.1, а не сам Discourse. Вероятно, вам потребуется настроить realip в Nginx.

Я уже добавил cloudflare.template.yml, который добавляет директивы realip, но это всё ещё не работает.
Я даже удалил пользовательские команды, которые изменяли заголовок на пользовательский, и теперь Discourse отображает IP-адрес сервера для всех пользователей вместо localhost.

Также вот конфигурация Nginx для домена zeronet.space, сгенерированная самой панелью

server {
	listen 85.25.xxx.xx:443 ssl http2;
	server_name  zeronet.space www.zeronet.space;
	root  /home/ay0ks/workspace/sites/zeronet.space;
	
	# ssl on;
	ssl_certificate  /etc/certs/ay0ks/zeronet.space_1655753906.crt;
	ssl_certificate_key /etc/certs/ay0ks/zeronet.space_1655753906.key;
	#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	#ssl_ciphers  "HIGH:!RC4:!aNULL:!MD5:!kEDH";
	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
	ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-256-GCM-SHA384:ECDHE:!COMPLEMENTOFDEFAULT;
	ssl_prefer_server_ciphers on;
	
	add_header Strict-Transport-Security 'max-age=604800';
	
	access_log /etc/nginx/vhost_logs/zeronet.space_access;
	error_log /etc/nginx/vhost_logs/zeronet.space_error;
	
	location ~ /.well-known {
		allow all;
	}
	
	location ~ /\.ht {
		deny all;
		access_log off;
		log_not_found off;
	}

	location / {
		root /home/ay0ks/workspace/sites/zeronet.space;
		proxy_pass http://85.25.xxx.xx:31080; # Discourse развернут на портах 31080/31443
		proxy_redirect     off;
		proxy_force_ranges on;
		proxy_set_header   Host $host;
		proxy_set_header   X-Real-IP $remote_addr;
		proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header   X-Forwarded-Proto $scheme;
		proxy_set_header   HTTPS $scheme;
		
		proxy_cache off;
		proxy_cache_key "$request_method|$http_if_modified_since|$http_if_none_match|$host|$request_uri";
		#access_log /etc/nginx/vhost_logs//home/ay0ks/workspace/sites/zeronet.space;
		
		proxy_cache_valid 3s;
		proxy_cache_min_uses 2;
		# proxy_cache_lock on;
		# proxy_cache_use_stale error timeout;
		# proxy_cache_use_stale updating http_502 http_504;
		limit_conn lone 100;
		# limit_req zone=ltwo burst=10;
		
		client_body_buffer_size    128k;
		client_max_body_size       1024m;
		proxy_connect_timeout      180;
		proxy_send_timeout         180;
		proxy_read_timeout         180;
		send_timeout               180;
		
		proxy_buffer_size          4k;
		proxy_buffers              8 32k;
		proxy_busy_buffers_size    68k;
		proxy_temp_file_write_size 10m;
	}

	# error_page  404              /404.html;
	# error_page   500 502 503 504  /50x.html;
}

Также хочу отметить «путь» запроса:
Пользователь -> Cloudflare -> Сервер(Nginx -> Docker -> Discourse)
И отмечу, что IP-адрес пользователя виден снаружи Docker в заголовке Cloudflare CF-Connecting-IP

Я не очень хорошо разбираюсь в том, как работает механизм реального IP-адреса в Cloudflare, но подозреваю, что вашему nginx с Discourse потребуется директива set_real_ip_from, указывающая на IP-адрес, под которым ваш прокси-nginx виден изнутри. Это 127.0.0.1? Какой-то другой внутренний адрес? Публичный адрес? Не уверен, какой именно он увидит.

Как только вы определите этот адрес, я бы посоветовал оставить шаблон Cloudflare как есть, а затем добавить новое правило replace специально для set_real_ip_from.

Кроме того, ваш прокси-nginx должен быть настроен так, чтобы передавать заголовок CF-Connecting-IP, если это еще не сделано или не выполняется по умолчанию. С этим моментом я, к сожалению, помочь не смогу.

У меня та же проблема. В моём случае nginx не был настроен на использование IP-адреса Docker в качестве диапазона для установки realip.

set_real_ip_from 172.18.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
set_real_ip_from 172.18.0.0/16;

Поэтому я перешёл: Пользователь > Cloudflare > Сервер Nginx (SWAG Docker) > Discourse

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Real-IP $remote_addr;
templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Раскомментируйте эти две строки, если хотите добавить Lets Encrypt (https)
#  - "templates/web.ssl.template.yml"
#  - "templates/web.letsencrypt.ssl.template.yml"
#  - "templates/web.socketed.template.yml"
  - "templates/cloudflare.template.yml"

Это может быть причиной ваших проблем. Попробуйте установить Discourse на сервер без каких-либо панелей или обратных прокси и сообщите, столкнетесь ли вы с той же проблемой.

В качестве первого шага начните с корректировки блока location для Discourse в соответствии с деталями, приведенными здесь: Run other websites on the same machine as Discourse

Это не решение проблемы. По сути, покупка нового выделенного сервера за 30 € в месяц решает всё, лол (тогда этой темы бы не существовало).

Попробую и опубликую результаты здесь

Нужно ли добавлять заголовки также в конфигурацию Nginx для Discourse (внутри Docker)? Потому что он всё ещё показывает адрес сервера вместо имен пользователей.

Исходя из вашего пути, для меня это не имеет смысла.

Ваш nginx направляет запросы к Discourse в Docker, зачем тогда вашему Discourse нужен свой собственный nginx?

Не знаю, я добавил ваши директивы Nginx к своим через панель (также проверил через SSH), но всё равно отображается IP-адрес сервера.

Вот мой app.yml:

## это шаблон контейнера Docker Discourse «все-в-одном», автономный
##
## После внесения изменений в этот файл ВЫ ДОЛЖНЫ выполнить пересборку
## /var/discourse/launcher rebuild app
##
## БУДЬТЕ *ОЧЕНЬ* ОСТОРОЖНЫ ПРИ РЕДАКТИРОВАНИИ!
## YAML-ФАЙЛЫ ЧРЕЗВЫЧАЙНО ЧУВСТВИТЕЛЬНЫ К ОШИБКАМ В ПРОБЕЛАХ ИЛИ ВЫРАВНИВАНИИ!
## посетите http://www.yamllint.com/, чтобы проверить этот файл при необходимости

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
  - "templates/cloudflare.template.yml"
## Раскомментируйте эти две строки, если хотите добавить Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

## какие TCP/IP-порты должен открывать этот контейнер?
## Если вы хотите, чтобы Discourse использовал один порт с другим веб-сервером, например Apache или nginx,
## см. https://meta.discourse.org/t/17247 для деталей
expose:
  - "31080:80"   # http
  - "31443:443" # https

params:
  db_default_text_search_config: "pg_catalog.russian"

  ## Установите db_shared_buffers максимум в 25% от общего объема памяти.
  ## будет установлено автоматически при загрузке на основе обнаруженной оперативной памяти, или вы можете переопределить
  db_shared_buffers: "4096MB"

  ## может улучшить производительность сортировки, но увеличивает использование памяти на подключение
  #db_work_mem: "40MB"

  ## Какую ревизию Git должен использовать этот контейнер? (по умолчанию: tests-passed)
  #version: tests-passed

env:
  LC_ALL: ru_RU.UTF-8
  LANG: ru_RU.UTF-8
  LANGUAGE: ru_RU.UTF-8
  DISCOURSE_DEFAULT_LOCALE: ru

  ## Сколько одновременных веб-запросов поддерживается? Зависит от памяти и ядер CPU.
  ## будет установлено автоматически при загрузке на основе обнаруженных процессоров, или вы можете переопределить
  UNICORN_WORKERS: 8

  ## TODO: Доменное имя, на которое будет реагировать этот экземпляр Discourse
  ## Обязательно. Discourse не будет работать с чистым IP-адресом.
  DISCOURSE_HOSTNAME: 'zeronet.space'

  ## Раскомментируйте, если хотите, чтобы контейнер запускался с тем же
  ## именем хоста (опция -h), как указано выше (по умолчанию "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO: Список адресов электронной почты через запятую, которые станут администраторами и разработчиками
  ## при первоначальной регистрации, например 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'contact@zeronet.space'

  ## TODO: SMTP-сервер, используемый для проверки новых учетных записей и отправки уведомлений
  ## SMTP-адрес, имя пользователя и пароль обязательны
  ## ВНИМАНИЕ: символ '#' в пароле SMTP может вызвать проблемы!
  DISCOURSE_SMTP_ADDRESS: smtp.zeronet.space
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: noreply@zeronet.space
  DISCOURSE_SMTP_PASSWORD: "xxxxxxx"
  DISCOURSE_SMTP_ENABLE_START_TLS: true
  DISCOURSE_SMTP_AUTHENTICATION: login
  DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: none
  DISCOURSE_NOTIFICATION_EMAIL: "noreply@zeronet.space"
  #DISCOURSE_SMTP_DOMAIN: "zeronet.space"

  ## Если вы добавили шаблон Lets Encrypt, раскомментируйте ниже, чтобы получить бесплатный SSL-сертификат
  #LETSENCRYPT_ACCOUNT_EMAIL: me@example.com

  ## Адрес CDN http или https для этого экземпляра Discourse (настроен на получение)
  ## см. https://meta.discourse.org/t/14857 для деталей
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com

  ## Ключ IP-адреса геолокации maxmind для поиска по IP-адресу
  ## см. https://meta.discourse.org/t/-/137387/23 для деталей
  #DISCOURSE_MAXMIND_LICENSE_KEY: 1234567890123456

## Контейнер Docker не имеет состояния; все данные хранятся в /shared
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

## Плагины размещаются здесь
## см. https://meta.discourse.org/t/19157 для деталей
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## Любые пользовательские команды для выполнения после сборки
run:
  - exec: echo "Начало пользовательских команд"
  ## Если вы хотите установить адрес электронной почты в поле 'От' для первой регистрации, раскомментируйте и измените:
  ## После получения первого письма о регистрации закомментируйте строку обратно. Она должна выполниться только один раз.
  - exec: rails r "SiteSetting.notification_email='noreply@zeronet.space'"
  - exec: echo "Конец пользовательских команд"

Ответ был дан выше. Вам нужно что-то вроде этого.

Это устранило проблемы с определением IP-адреса (теперь всё работает нормально), но теперь некоторые изображения не загружаются.

РЕДАКТИРОВАНИЕ: это была проблема с кэшем, теперь всё работает! Спасибо всем!