Вот финальное решение, из минусов:
1) Опирается на CF-Connecting-IP (это из Cloudlflare)
2) В access_log будет логгироваться IP внешнего Nginx, хотя в конце будет виден реальный клиентский IP ($http_x_forwarded_for), можно заменить log_format
3) Багофича: если вынести все proxy_set_header за пределы вирт.хоста (server {}), то они НЕ применяются в proxy_pass. Так как не обрабатываются в его контексте. Причина: map это динамическая штука согласно документации (Since variables are evaluated only when they are used, the mere declaration even of a large number of “map” variables does not add any extra costs to request processing) и она запускается на обработку только если "нужна"... Почему Nginx считает что она не нужна если описана в nginx.conf (и даже корректно используется в log_format), а не в server {}? Видимо на то есть причины.
Итак, вначале общие настройки:
===== nginx.conf OR conf.d/000-realip.conf =====
set_real_ip_from 1.1.1.1/24; # haproxies internal
set_real_ip_from 2.2.2.xxx; # caravan
real_ip_header proxy_protocol; # using proxy_protocol as header, it cannot be dynamically set
# check CF-Connecting-IP, use $remote_addr as fallback
map $http_cf_connecting_ip $cf_ip_addr {
"" $remote_addr;
default $http_cf_connecting_ip;
}
# dynamic address for further X-Real-IP header passed to backend
map $proxy_protocol_addr $real_ip_addr {
"" $cf_ip_addr;
default $proxy_protocol_addr;
}
# updated log_format, make sure to have it before using in access_log
log_format main '$real_ip_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
===== nginx.conf OR conf.d/000-realip.conf =====
===== nginx.conf OR conf.d/001-vhost.conf =====
server {
listen 80;
listen 81 proxy_protocol;
server_name DOMAIN www.DOMAIN;
location /
{
proxy_pass
http://DOMAIN_UPSTREAM;
proxy_set_header Connection "";
proxy_pass_header Set-Cookie;
proxy_set_header Host $host;
proxy_set_header Cookie $http_cookie;
proxy_set_header X-Real-IP $real_ip_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
===== nginx.conf OR conf.d/001-vhost.conf =====
Результат (по логам Nginx):
HAProxy->Nginx: 3.3.3.3 - - [22/Feb/2017:23:37:35 +0300] "GET /p.php HTTP/1.1" 200 1340 "-" "curl/7.38.0" "-"
Nginx->Nginx: 2.2.2.2 - - [22/Feb/2017:23:37:49 +0300] "GET /p.php HTTP/1.1" 200 1400 "-" "curl/7.38.0" "3.3.3.3"
Direct to Nginx: 3.3.3.3 - - [22/Feb/2017:23:37:58 +0300] "GET /p.php HTTP/1.1" 200 1340 "-" "curl/7.38.0" "-"
3.3.3.3 - это реальный IP клиента, 2.2.2.2 это IP внешнего Nginx.
Как видно, только при использовании внешнего Nginx вы не можем переопределить $remote_addr и не можем его заменить путем использования realip модуля т.к. он у нас настроен на proxy_protocol. Но даже в этом случае у нас остается бекап в виде $http_x_forwarded_for. Это что касается логов (тем более что там можно тоже заюзать $real_ip_addr что я в итоге и сделал). Сам по себе бекенд теперь всегда получает нужный X-Real-IP и его гео-модуль также работает правильно.
Очень надеюсь что такой странный кейс поможет еще кому-то ;)