Nginx and thttpd + cgi, remote_addr = 127.0.0.1

Hello,

i have an issue with getting users’ real IP. I am using nginx as
frontend with thttpd as backend. I’ve added such options to my default
server in nginx config:

location ~ .cgi|pl$ {
proxy_pass http://127.0.0.1:8000;
include /etc/nginx/proxy.conf;
}

/etc/nginx/proxy.conf has such options among others:

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

and I have patched latest thttpd
(http://www.acme.com/software/thttpd/thttpd-2.25b.tar.gz) with this
patch: http://wiki.nginx.org/ThttpdRealIP
also i had to rename “getline” to “mygetline” in
thttpd-2.25b/extras/htpasswd.c because i was getting an error
“htpasswd.c:52: error: conflicting types for ‘getline’” during
compilation.

the whole thttpd config is:

host=127.0.0.1
port=8000
dir=/var/www/default
user=www-data
cgipat=.cgi|.pl
vhost
charset=utf-8
logfile=/var/log/thttpd.log
pidfile=/var/run/thttpd.pid

after installing and starting both webservers I saved this script as
index.cgi and opened it in browser(i’m using Opera):

#!/bin/sh
echo “Content-type: text/html; charset=UTF-8”;
echo;
echo “i am cgi script

”;
echo “env:

”;
env

when I opened it with Opera Turbo enabled (thus using Opera’s proxy
server), I see output like this:

. .
GATEWAY_INTERFACE=CGI/1.1 REMOTE_ADDR=127.0.0.1
HTTP_USER_AGENT=Opera/9.80 (X11; Linux i686; U; ru) Presto/2.9.168
Version/11.51 . . .

and when I open it without Opera Turbo (with my real IP), I’m getting my
real IP in the output (a part was hidden):

. .
GATEWAY_INTERFACE=CGI/1.1 REMOTE_ADDR=178.177..
HTTP_USER_AGENT=Opera/9.80 (X11; Linux i686; U; ru) Presto/2.9.168
Version/11.51 . . .

what could I have done wrong with configuration?

//Jef, sorry for CC, but I don’t know the cause of this issue, and it
still could be thttpd. though i hope it’s just my hands :slight_smile:

Cheers,

Vladimir

On Wed, Oct 26, 2011 at 01:11:40AM +0400, Кирюшкин Владимир wrote:

Hi there,

/etc/nginx/proxy.conf has such options among others:

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

That should cause two variables HTTP_X_REAL_IP and HTTP_X_FORWARDED_FOR
to be visible in the CGI output.

and I have patched latest thttpd
(http://www.acme.com/software/thttpd/thttpd-2.25b.tar.gz) with this patch:
http://wiki.nginx.org/ThttpdRealIP

That patch looks like it will write the value of the X-Forwarded-For
header into something internal to thttpd.

#!/bin/sh
echo “Content-type: text/html; charset=UTF-8”;
echo;
echo “i am cgi script

”;
echo “env:

”;
env

What are HTTP_X_REAL_IP and HTTP_X_FORWARDED_FOR in the output of this
cgi script?

If they are not what you expect, check what nginx is doing.

If they are what you expect, check what your patched thttpd is doing.

Good luck with it,

f

Francis D. [email protected]

Hello,

thanks for your answer.

26.10.2011, 02:10, “Francis D.” [email protected]:

On Wed, Oct 26, 2011 at 01:11:40AM +0400, wrote:

That should cause two variables HTTP_X_REAL_IP and HTTP_X_FORWARDED_FOR
to be visible in the CGI output.

alas, no. the full output is (when using proxy):

i am cgi script


env:


GATEWAY_INTERFACE=CGI/1.1
REMOTE_ADDR=127.0.0.1
HTTP_USER_AGENT=Opera/9.80 (X11; Linux i686; U; ru) Presto/2.9.168
Version/11.51
CGI_PATTERN=.cgi|.pl
HTTP_ACCEPT=text/html, application/xml;q=0.9, application/xhtml+xml,
image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, /;q=0.1
HTTP_HOST=hidden
SERVER_SOFTWARE=thttpd/2.25b 29dec2003
PATH=/usr/local/bin:/usr/ucb:/bin:/usr/bin
HTTP_ACCEPT_LANGUAGE=ru, en
SERVER_PROTOCOL=HTTP/1.0
REQUEST_METHOD=GET
PWD=/var/www/default
SERVER_PORT=8000
SCRIPT_NAME=/index.cgi
SERVER_NAME=127.0.0.1

That patch looks like it will write the value of the X-Forwarded-For
header into something internal to thttpd.

yes, it is intended to change X-Forwarded-For into Remote-Addr, but
seems that it doesn’t work.

What are HTTP_X_REAL_IP and HTTP_X_FORWARDED_FOR in the output of this
cgi script?

there are no such vars at all.

If they are not what you expect, check what nginx is doing.

how could i do that?

Cheers,

Vladimir

On Wed, Oct 26, 2011 at 02:17:37AM +0400, Кирюшкин Владимир wrote:

26.10.2011, 02:10, “Francis D.” [email protected]:

On Wed, Oct 26, 2011 at 01:11:40AM +0400, Кирюшкин Владимир wrote:

Hi there,

That should cause two variables HTTP_X_REAL_IP and HTTP_X_FORWARDED_FOR
to be visible in the CGI output.

alas, no. the full output is (when using proxy):

Ah, this thttpd doesn’t expose the full http header as HTTP_*
environment
variables. So scratch that as a test.

That patch looks like it will write the value of the X-Forwarded-For
header into something internal to thttpd.

yes, it is intended to change X-Forwarded-For into Remote-Addr, but seems that
it doesn’t work.

Test the thttpd side:

curl -H ‘X-Forwarded-For: 127.0.0.2’ http://[thttpd-server]/env.cgi

If you don’t see REMOTE_ADDR=127.0.0.2, then your thttpd is not acting
as you expect. Nothing nginx can do can help you in that case.

And note what

curl -H ‘X-Forwarded-For: 127.0.0.2, 127.0.0.3’
http://[thttpd-server]/env.cgi

shows that is different.

If thttpd directly works (as above) and through nginx doesn’t, then
check
the nginx debug log to see what exactly nginx is sending to thttpd –
it’ll be the line with

http script copy: "X-Forwarded-For: "

and the next line will show the value.

Test using

curl http://[nginx-server]/env.cgi

and possibly

curl -H ‘X-Forwarded-For: 127.0.0.2’ http://[nginx-server]/env.cgi

If they are not what you expect, check what nginx is doing.

how could i do that?

debug log.

Overall, when I test here, it all works as expected, by which I mean:

when I access nginx on 10.0.2.15 which proxy_pass’es to thttpd on
127.0.0.1, I see REMOTE_ADDR=10.0.2.15

when I access nginx on 10.0.2.15 through any other proxy,
so that X-Forwarded-For is set before it gets to nginx, I see
REMOTE_ADDR=127.0.0.1

This patch to thttpd seems to require that X-Forwarded-For contains
exactly one address, which means that you must not set it in nginx using

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

You’ll instead want something like

proxy_set_header X-Forwarded-For $remote_addr;

with some careful thought about what to do if X-Forwarded-For was set
in the request to nginx.

Good luck,

f

Francis D. [email protected]

On Wed, Oct 26, 2011 at 01:11:40AM +0400, wrote:

dir=/var/www/default
#!/bin/sh

and when I open it without Opera Turbo (with my real IP), I’m getting my real IP
in the output (a part was hidden):

. .
GATEWAY_INTERFACE=CGI/1.1 REMOTE_ADDR=178.177.. HTTP_USER_AGENT=Opera/9.80
(X11; Linux i686; U; ru) Presto/2.9.168 Version/11.51 . . .

what could I have done wrong with configuration?

//Jef, sorry for CC, but I don’t know the cause of this issue, and it still
could be thttpd. though i hope it’s just my hands :slight_smile:

You may want to use mini_httpd instead of thttpd.
There is patch to support “X-Real-IP” header in mini_httpd:
http://mailman.nginx.org/pipermail/nginx/2010-October/023292.html


Igor S.

Hello,

26.10.2011, 17:02, “Francis D.” [email protected]:

Test the thttpd side:

curl -H ‘X-Forwarded-For: 127.0.0.2’ http://[thttpd-server]/env.cgi

If you don’t see REMOTE_ADDR=127.0.0.2, then your thttpd is not acting
as you expect. Nothing nginx can do can help you in that case.

localhost> curl -H ‘X-Forwarded-For: 127.0.0.2’
http://myserver:8000/index.cgi | grep -i remote
% Total % Received % Xferd Average Speed Time Time Time
Current
Dload Upload Total Spent Left
Speed
100 471 0 471 0 0 3007 0 --:–:-- --:–:-- --:–:–
5962
REMOTE_ADDR=myrealip

myserver# grep -i x-forward /ramdisk/nginx_debug.log
2011/10/26 17:20:08 [debug] 11877#0: *3 http header: “X-Forwarded-For:
127.0.0.2”

well, seems that it’s thttpd issue.

26.10.2011, 18:26, “Igor S.” [email protected]:

You may want to use mini_httpd instead of thttpd.
There is patch to support “X-Real-IP” header in mini_httpd:
Mailman with nginx and thttpd

thanks, i’ll follow your advice.

Cheers,

Vladimir

Hello,

during applying the patch i’ve got these warnings:

patch mini_httpd.c -i realip.patch

patching file mini_httpd.c
patching file mini_httpd.c
Hunk #1 FAILED at 27.
Hunk #2 FAILED at 163.
2 out of 2 hunks FAILED – saving rejects to file mini_httpd.c.rej

so i’ve added failed hunks manually.
after installing and configuring i’ve checked the output from
env-script, but REMOTE_ADDR still was 127.0.0.1.
also when i’m requesting that script without any additional headers,
it’s ok, but when i add “X-Real-IP” it looks like mini-httpd just closes
the connection:

curl -H ‘X-Real-IP: 127.0.0.2’ http://127.0.0.1:8000/index.cgi

curl: (52) Empty reply from server

then i’ve changed “X-Real-IP” to “X-Forwarded-For” in mini_httpd.c:

else if ( strncasecmp( line, “X-Forwarded-For:”, 16 ) == 0 )
{
if ( strcmp(remote_addr, xrealip ) == 0 )
{
cp = &line[16];
cp += strspn( cp, " \t" );
remote_addr = cp;
}
}

and after compiling there is same result:

curl -H ‘X-Forwarded-For: 127.0.0.2’ http://127.0.0.1:8000/index.cgi

curl: (52) Empty reply from server

with no additional headers script works ok.

Could you please suggest me any other way to run cgi programs with
nginx, but with getting real IPs from clients, not “127.0.0.1”?
using apache httpd as backend will be huge performance impact.
Thanks in advance.

Cheers,

Vladimir

Hi,

On 26.10.2011 00:17, Кирюшкин Владимир wrote:

Hello,

thanks for your answer.

26.10.2011, 02:10, “Francis D.” [email protected]:

[snipp]

If they are not what you expect, check what nginx is doing.

how could i do that?

http://www.nginx.org/en/docs/debugging_log.html

Aleks

Hello,

26.10.2011, 19:50, “Aleksandar L.” [email protected]:

http://nginx.localdomain.pl/wiki/FcgiWrap

installed fcgiwrap, now everything works flawlessly. Thanks for your
advice.

Cheers,

Vladimir

On 26.10.2011 22:37, Кирюшкин Владимир wrote:

Hello,

26.10.2011, 19:50, “Aleksandar L.” [email protected]:

http://nginx.localdomain.pl/wiki/FcgiWrap

installed fcgiwrap, now everything works flawlessly. Thanks for your
advice.

your welcome.

On 25.10.2011 23:11, Кирюшкин Владимир wrote:

include /etc/nginx/proxy.conf;

}

Maybe you will need another cgi executer :wink:

http://nginx.localdomain.pl/wiki/FcgiWrap
or

Cheers
Aleks