Checking headers

Is there any way with nginx to check a request’s headers and send back
a 401 if the headers are not proper?

On Sat, May 28, 2016 at 12:48 PM, Larry M.
[email protected]
wrote:

Is there any way with nginx to check a request’s headers and send back
a 401 if the headers are not proper?

Yes, you can do with this via the ‘map’ and ‘if’ directives. A trivial
example:

http {
# if the “X-Foo” request header contains the phrase ‘data’, set
$bar to 1; otherwise, set it to 0
map $http_x_foo $bar {
default 0;
“~data” 1;
}

server {
    location /t {
            if ($bar) {
                    return 401;
            }
    }

}

See also Module ngx_http_map_module and
Module ngx_http_rewrite_module

On Mon, May 30, 2016 at 2:19 PM, Robert P.
[email protected] wrote:

Yes, you can do with this via the ‘map’ and ‘if’ directives. A trivial
server {
location /t {
if ($bar) {
return 401;
}
}
}

See also Module ngx_http_map_module and
Module ngx_http_rewrite_module

I added this to the http section:

map $http_x_capdata_auth $not_auth {
    default 1;
    "authorized" 0;
}

and this to the location section:

        if ($not_auth) {
            return 401;
        }

and it’s always returning a 401, even if there is a header:

X-Capdata-Auth: authorized

And I doing something wrong here? How can I debug this?

On Tue, May 31, 2016 at 7:55 AM, Larry M. [email protected]
wrote:

            "~data" 1;

See also Module ngx_http_map_module and

Looking with tcpdump I do not see that header field set. The request
is coming from a django app which is doing a redirect and I set the
header before the redirect. Guess I have to debug from that side.

I traced the django code all the way through to when the response is
going out and I see this:

(Pdb) response._headers
{‘x-capdata-auth’: (‘X-Capdata-Auth’, ‘authorized’), ‘content-type’:
(‘Content-Type’, ‘text/html; charset=utf-8’), ‘location’: (‘Location’,
http://foo.bar.com:8000/workitem/12345’), ‘vary’: (‘Vary’, ‘Cookie’)}

Any one have any ideas as to why it doesn’t seem to make it over to
nginx?

On Tue, May 31, 2016 at 7:41 AM, Larry M. [email protected]
wrote:

    }

Module ngx_http_rewrite_module
if ($not_auth) {
return 401;
}

and it’s always returning a 401, even if there is a header:

X-Capdata-Auth: authorized

And I doing something wrong here? How can I debug this?

Looking with tcpdump I do not see that header field set. The request
is coming from a django app which is doing a redirect and I set the
header before the redirect. Guess I have to debug from that side.

On Tue, May 31, 2016 at 09:23:36AM -0400, Larry M. wrote:

On Tue, May 31, 2016 at 7:55 AM, Larry M. [email protected] wrote:

On Sat, May 28, 2016 at 12:48 PM, Larry M. [email protected]
wrote:

Hi there,

Is there any way with nginx to check a request’s headers and send back
a 401 if the headers are not proper?

http://foo.bar.com:8000/workitem/12345’), ‘vary’: (‘Vary’, ‘Cookie’)}

Any one have any ideas as to why it doesn’t seem to make it over to nginx?

There is a request from the client to nginx.

There is a response from nginx to the client.

There can be a request from nginx to its upstream, and a response from
upstream to nginx.

Any of those requests and responses can include headers.

In your architecture, what “header” do you care about?

That should tell you which variable value to check.

http://nginx.org/r/$http_

http://nginx.org/r/$sent_http_

http://nginx.org/r/$upstream_http_

are three different families of variables set within nginx.

Possibly one of them covers what you want?

f

Francis D. [email protected]

On Tue, May 31, 2016 at 10:26:26AM -0400, Larry M. wrote:

On Tue, May 31, 2016 at 9:45 AM, Francis D. [email protected] wrote:

Hi there,

neither case does my config pick that up. This is what I have:

map $http_x_capdata_auth $not_auth {
default 1;
“authorized” 0;
}

Is that the correct way to check for that header value?

Yes. That checks for the request header from the client.

It works for me:

==
http {
map $http_x_capdata_auth $not_auth {
default 1;
“authorized” 0;
}

server {
listen 8080;
location / {
if ($not_auth) { return 401 “$http_x_capdata_auth, $not_auth\n”; }
return 200 “$http_x_capdata_auth, $not_auth\n”;
}
}
}

curl -v -H ‘X-CapData-Auth: authorized’ http://127.0.0.1:8080/test
→ HTTP/1.1 200 OK; authorized, 0

curl -v http://127.0.0.1:8080/test
→ HTTP/1.1 401 Unauthorized: , 1

Is there a way for me to dump the headers that it sees on requests to port 8000?

Within nginx, probably the debug log is simplest.

Outside nginx: tcpdump, or ask the client what it sends.

f

Francis D. [email protected]

On Tue, May 31, 2016 at 9:45 AM, Francis D. [email protected]
wrote:

Looking with tcpdump I do not see that header field set. The request

are three different families of variables set within nginx.

Possibly one of them covers what you want?

There are 2 ways requests get to port 8000, which is the port I want
to check headers on.

One is via a C++ Qt app, and the other is from a python django app.

The C++ app sends the request directly to port 8000. With the django
app a request is sent to port 8004 and django sends a 301 redirect to
8000. In both cases the header field X-Capdata-Auth is set. And in
neither case does my config pick that up. This is what I have:

map $http_x_capdata_auth $not_auth {
default 1;
“authorized” 0;
}

Is that the correct way to check for that header value?

Is there a way for me to dump the headers that it sees on requests to
port 8000?

On Tue, May 31, 2016 at 11:38 AM, Francis D. [email protected]
wrote:

One is via a C++ Qt app, and the other is from a python django app.

"authorized" 0;

==

Outside nginx: tcpdump, or ask the client what it sends.

Using curl I can see that ngixn is doing the right thing. Looking at
the request coming out of the clients show the header being there.
Using tcpdump I do not see the header. I know this is no longer an
nginx question, but anyone know why that header would get dropped
along the way?

On Tue, May 31, 2016 at 12:33:56PM -0400, Larry M. wrote:

On Tue, May 31, 2016 at 11:38 AM, Francis D. [email protected] wrote:

On Tue, May 31, 2016 at 10:26:26AM -0400, Larry M. wrote:

Hi there,

The C++ app sends the request directly to port 8000. With the django
app a request is sent to port 8004 and django sends a 301 redirect to
8000. In both cases the header field X-Capdata-Auth is set. And in
neither case does my config pick that up. This is what I have:

Using curl I can see that ngixn is doing the right thing. Looking at
the request coming out of the clients show the header being there.
Using tcpdump I do not see the header. I know this is no longer an
nginx question, but anyone know why that header would get dropped
along the way?

It sounds like your design is that your client sends a http request to
port 8004; the http service there returns a 301 redirect to a url on
port
8000 and includes a particular response header; and you want your client
to follow the redirect by making a new request to port 8000 and include
a
request header that mirrors the particular response header that you
sent.

If you are using the client that you wrote, then you can make sure that
it does that.

If you are using a general http client, it is unlikely to do that.

Perhaps an alternate design involving reverse-proxying would be valid?

Cheers,

f

Francis D. [email protected]

On Tue, May 31, 2016 at 4:19 PM, Francis D. [email protected]
wrote:

request header that mirrors the particular response header that you sent.
With the django app, what you are saying is correct.

If you are using the client that you wrote, then you can make sure that
it does that.

If you are using a general http client, it is unlikely to do that.

I am knida new to all this. The apps were written by someone who quit
and then this was all dropped in my lap. I thought I was clear on what
a client and server were in life, in this app it’s somewhat screwy.
What is behind port 8000 is nginx routing to some Angular code that
sends a request out. So the Angular code, although client side, is
acting like a server in that it is invoked in response to a request.
Then it turns about and acts like a client and sends a request out.
So, who’s the server here? nginx?

There are 2 approved ways to send a request to port 8000. One is from
an app we wrote that is in C++ and it directly sends the request to
port 8000. These requests are always previously authenticated and are
good to go. The second is from a django endpoint listening on 8004. It
does some authentication and if all is good, redirects to 8000. So
with both of these cases I want to request to port 8000 to go through.

Then, of course, there are myriad other ways for a request to get port
8000 - from a browser, curl, wget, etc. In all of these cases I want
the request to be blocked and return a 401.

I was hoping to do this with a custom header, but that appears not to
work. Can anyone recommend another way to achieve this?

Perhaps an alternate design involving reverse-proxying would be valid?

How would that help me?

Thanks!

On Tue, May 31, 2016 at 04:48:19PM -0400, Larry M. wrote:

On Tue, May 31, 2016 at 4:19 PM, Francis D. [email protected] wrote:

On Tue, May 31, 2016 at 12:33:56PM -0400, Larry M. wrote:

Hi there,

If you are using a general http client, it is unlikely to do that.

I am knida new to all this. The apps were written by someone who quit
and then this was all dropped in my lap.

It might be instructive for you to find out why they quit.

If it was related to them being required to implement a design which
they
knew can’t possibly work, it would be good for you to learn that early.

But that’s beside the point, here.

I thought I was clear on what
a client and server were in life, in this app it’s somewhat screwy.

Probably a good thing you can do for you, is take a pencil and paper
and write down the intended data flow of this application.

Until you are happy that the design is right, you probably can’t accept
responsibility for implementing it.

In general, the app is a chain of events. At each point, one client is
making one request of one server.

When you can see the data flow design, it will be clearer to you.

What is behind port 8000 is nginx routing to some Angular code that
sends a request out. So the Angular code, although client side, is
acting like a server in that it is invoked in response to a request.

I don’t follow all of those words, but that’s ok: I don’t have to.

Then it turns about and acts like a client and sends a request out.
So, who’s the server here? nginx?

Whatever is making the request is the client at this instant; whatever
is receiving the request is the server at this instant.

There are 2 approved ways to send a request to port 8000. One is from
an app we wrote that is in C++ and it directly sends the request to
port 8000. These requests are always previously authenticated and are
good to go. The second is from a django endpoint listening on 8004. It
does some authentication and if all is good, redirects to 8000. So
with both of these cases I want to request to port 8000 to go through.

Then, of course, there are myriad other ways for a request to get port
8000 - from a browser, curl, wget, etc. In all of these cases I want
the request to be blocked and return a 401.

nginx on port 8000 does not care whether the request came from your C++
app or from my curl command.

All it cares about is what it is configured to do: which is to accept a
http request that includes the “I promise I am authorised” token.

browser, curl, wget, etc, can all include that token, without touching
your django app or your C++ program.

If that is your design, and the authorisation actually matters for
anything, then your design is broken and you need to re-design.

I was hoping to do this with a custom header, but that appears not to
work. Can anyone recommend another way to achieve this?

One possibility might be to use auth_request
(Module ngx_http_auth_request_module) within nginx to authorise-and-return
the content in one step (as far as the client is concerned) in nginx.

Another possibility might be to use “X-Accel-Redirect” from the
reverse-proxied authorisation-checker. Again, from the client
perspective,
the request with credentials results in the desired response directly.

The current two-step process of one request with credentials which are
checked, returning a “I am authorised” token; followed by another
request
with that token which the second server does not authenticate at all;
leads to you being able to use “curl” to pretend to be authorised.

Perhaps an alternate design involving reverse-proxying would be valid?

How would that help me?

As above, nginx could reverse-proxy to the authorisation checker; or
alternatively the django app could reverse-proxy to nginx; and then you
could put in external (firewall?) rules which mean that only your C++
app and your django app can get to nginx on port 8000.

Good luck with it,

f

Francis D. [email protected]

On Tue, May 31, 2016 at 7:06 PM, Francis D. [email protected]
wrote:

request header that mirrors the particular response header that you sent.

With the django app, what you are saying is correct.

If you are using the client that you wrote, then you can make sure that
it does that.

If you are using a general http client, it is unlikely to do that.

I am knida new to all this. The apps were written by someone who quit
and then this was all dropped in my lap.

Francis, I really appreciate the time you took to send such a complete
and thoughtful reply.

It might be instructive for you to find out why they quit.

I’ll never know for sure, as he was gone before I got there. But I
have some pretty good guesses.

If it was related to them being required to implement a design which they
knew can’t possibly work, it would be good for you to learn that early.

He was the one who designed and built it, and it seems overly
complicated to me. But I like to give people the benefit of the doubt
and think he had his reasons.

But that’s beside the point, here.

I thought I was clear on what
a client and server were in life, in this app it’s somewhat screwy.

Probably a good thing you can do for you, is take a pencil and paper
and write down the intended data flow of this application.

Yes, that was one of the first things I did when I took the job. I
understand the flow, but I don’t understand why it was done in the way
it was.

Until you are happy that the design is right, you probably can’t accept
responsibility for implementing it.

It’s implemented already and it functionally works, but it has no
security or authentication at all. That is what I was asked to add.
And they wanted it done ASAP since they were hanging out naked.

In general, the app is a chain of events. At each point, one client is
making one request of one server.

When you can see the data flow design, it will be clearer to you.

What is behind port 8000 is nginx routing to some Angular code that
sends a request out. So the Angular code, although client side, is
acting like a server in that it is invoked in response to a request.

I don’t follow all of those words, but that’s ok: I don’t have to.

Initially I didn’t find it confusing, but perhaps that is from my
ignorance. But when I talked to people about it they were always
confused. I didn’t see why there were so confused, but then it struck
me - the Angular code acts like a server and in everyone’s mind, that
is not what client side code does. I was trying in the Angular code to
get info on the request it was serving. But from Angular’s point of
view, it wasn’t serving a request - it was just being invoked and it
sends out a request. The fact that it was invoked because nginx got a
request on port 8000 was not known to Angular. So me trying to do
server type things (like look a the request headers) made no sense in
client side code.

does some authentication and if all is good, redirects to 8000. So
http request that includes the “I promise I am authorised” token.

browser, curl, wget, etc, can all include that token, without touching
your django app or your C++ program.

Yes, I realize all that. I was going for some short term security by
obscurity. Anything is better then the nothing they had.

If that is your design, and the authorisation actually matters for
anything, then your design is broken and you need to re-design.

What I ended up doing was to embed the auth info in the URL parameters
and then encrypt that. Got me by for the moment. Down the road I’ll do
something better.

I was hoping to do this with a custom header, but that appears not to
work. Can anyone recommend another way to achieve this?

One possibility might be to use auth_request
(Module ngx_http_auth_request_module) within nginx to authorise-and-return
the content in one step (as far as the client is concerned) in nginx.

Another possibility might be to use “X-Accel-Redirect” from the
reverse-proxied authorisation-checker. Again, from the client perspective,
the request with credentials results in the desired response directly.

Thanks I will look into those solutions.

The current two-step process of one request with credentials which are
checked, returning a “I am authorised” token; followed by another request
with that token which the second server does not authenticate at all;
leads to you being able to use “curl” to pretend to be authorised.

Yes, if someone knew what header field and value to use.

Perhaps an alternate design involving reverse-proxying would be valid?

How would that help me?

As above, nginx could reverse-proxy to the authorisation checker; or
alternatively the django app could reverse-proxy to nginx; and then you
could put in external (firewall?) rules which mean that only your C++
app and your django app can get to nginx on port 8000.

Good luck with it,

Thanks again!

-Larry