You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Graham Leggett <gr...@dsn.ericsson.se> on 1999/04/29 14:03:47 UTC

Help with reverse proxy and authentication

Hi all,

I need a bit of insight into the way Apache handles proxy requests
internally.

>From what I can see if request_rec->proxyreq is non zero, it means that
the request was handled by a proxy module, correct?

When request_rec->proxyreq is non zero and authentication is requested,
a "Proxy-authenticate" is returned instead of a "WWW-authenticate"
header. Trouble is, in the case of a *reverse* proxy
"Proxy-authenticate" is sent in error, and the browser ignores the
request for authentication.

What is the politically correct way of determining given the request_req
structure whether the request is being reverse proxied, rather than just
forward proxied?

Some insight would be appreciated :)

Regards,
Graham
-- 

Graham Leggett
Ericsson Business Consulting Netherlands B.V.
P.O. Box 209, 5120 AE Rijen
The Netherlands

Tel.+31-161-247311, Fax. +31-161-246612
E-Mail: graham.leggett@dsn.ericsson.se,
Homepage:  http://www.ericsson.se

Re: [PATCH] Reverse proxy and authentication

Posted by Graham Leggett <mi...@sharp.fm>.
Dean Gaudet wrote:

> [Hey Graham, it's more convenient if you send one attachment with the
> whole patch rather than an individual attachments each with one-file
> attachments, thanks.]

Sorry about that - I created a single file patch, then tried to test it
and it wouldn't work. Turned out to be a problem with Solaris patch...

> As Roy might say, "there is no such thing as a reverse proxy".  He's
> referring to the fact that if you read the rfc you won't find mention of a
> "reverse proxy", only of a proxy.  And strictly speaking, a proxy requires
> absolute URIs, for example, so this application is definately not an
> *http* proxy.  The core's concept of proxy is closer to that of an http
> proxy...

Reverse proxy isn't mentioned in any proxy rfc because on the frontend a
reverse proxy as actually a normal website. There is no way the browser
can tell that the URL it tried to fetch is proxied in any way, only the
laws of HTTP apply here.

This is where the problem lies. When authentication is added to a
reverse proxied URL using the <Location> braces, mod_proxy starts
talking proxy-speak, because proxy-speak is the only language it has
been programmed to speak. The browser receives "Proxy-Authentication:",
but because the browser is speaking HTTP to the website, and not Proxy,
it disregards the response as an error, and authentication doesn't work.

> The problem I see here is that really what the proxy module is being used
> as here is as a special purpose database for fetching response objects.

This is exactly how it's behaving, yes.

> Why does it set proxyreq = 1?  If there were a module which was fetching
> objects from a db and caching them locally we wouldn't be setting proxyreq
> = 1 for that module... how is this "reverse proxy" different?

I don't know - I don't fully understand how the rest of Apache responds
to proxy requests, and my approach was to keep the behavior the same as
it was, just change the authentication.

Basically Apache seems to be using the proxyreq variable to answer the
question "Is this request a proxy request?". Trouble is, it's too vague
a question, because in reality a proxy request can take two forms:

Proxy frontend <--> Proxy backend (normal proxy)
HTTP frontend <--> Proxy backend (reverse proxy)

What the patch tries to do is turn this two-state variable (proxy/no
proxy) into a three state variable (reverse/normal/none) in a way that's
backward compatible with the rest of the code. Ideally there should be a
simple way of determining whether the frontend is Proxy or HTTP so that
we can speak the correct language to the browser when it submits a
request.

Is there a better way of doing this?

Regards,
Graham
-- 
-----------------------------------------
minfrin@sharp.fm		"There's a moon
					over Bourbon Street
						tonight...

Re: [PATCH] Reverse proxy and authentication

Posted by Graham Leggett <mi...@sharp.fm>.
[I'm sending this message again because I didn't see it come through the
list...]

Dean Gaudet wrote:

> [Hey Graham, it's more convenient if you send one attachment with the
> whole patch rather than an individual attachments each with one-file
> attachments, thanks.]

Sorry about that - I created a single file patch, then tried to test it
and it wouldn't work. Turned out to be a problem with Solaris patch...

> As Roy might say, "there is no such thing as a reverse proxy".  He's
> referring to the fact that if you read the rfc you won't find mention of a
> "reverse proxy", only of a proxy.  And strictly speaking, a proxy requires
> absolute URIs, for example, so this application is definately not an
> *http* proxy.  The core's concept of proxy is closer to that of an http
> proxy...

Reverse proxy isn't mentioned in any proxy rfc because on the frontend a
reverse proxy as actually a normal website. There is no way the browser
can tell that the URL it tried to fetch is proxied in any way, only the
laws of HTTP apply here.

This is where the problem lies. When authentication is added to a
reverse proxied URL using the <Location> braces, mod_proxy starts
talking proxy-speak, because proxy-speak is the only language it has
been programmed to speak. The browser receives "Proxy-Authentication:",
but because the browser is speaking HTTP to the website, and not Proxy,
it disregards the response as an error, and authentication doesn't work.

> The problem I see here is that really what the proxy module is being used
> as here is as a special purpose database for fetching response objects.

This is exactly how it's behaving, yes.

> Why does it set proxyreq = 1?  If there were a module which was fetching
> objects from a db and caching them locally we wouldn't be setting proxyreq
> = 1 for that module... how is this "reverse proxy" different?

I don't know - I don't fully understand how the rest of Apache responds
to proxy requests, and my approach was to keep the behavior the same as
it was, just change the authentication.

Basically Apache seems to be using the proxyreq variable to answer the
question "Is this request a proxy request?". Trouble is, it's too vague
a question, because in reality a proxy request can take two forms:

Proxy frontend <--> Proxy backend (normal proxy)
HTTP frontend <--> Proxy backend (reverse proxy)

What the patch tries to do is turn this two-state variable (proxy/no
proxy) into a three state variable (reverse/normal/none) in a way that's
backward compatible with the rest of the code. Ideally there should be a
simple way of determining whether the frontend is Proxy or HTTP so that
we can speak the correct language to the browser when it submits a
request.

Is there a better way of doing this?

Regards,
Graham
-- 
-----------------------------------------
minfrin@sharp.fm		"There's a moon
					over Bourbon Street
						tonight...

Re: [PATCH] Reverse proxy and authentication

Posted by Doug MacEachern <do...@cp.net>.
On Sun, 16 May 1999, Dean Gaudet wrote:

> p.s. I think the problem is that r->proxyreq is overloaded -- it's being
> used as both a note to the core that the request is being handled by an
> HTTP/1.0 proxy, *and* being used as a note from mod_proxy to itself that
> it is in fact handling the request.  The second can be solved some other
> way -- no other module gets its own private field in request_rec. 

well, mod_perl uses it too, proxy modules written in Perl can decide
to handle a proxy request if $r->proxyreq() returns true.

-Doug


Re: [PATCH] Reverse proxy and authentication

Posted by Dean Gaudet <dg...@arctic.org>.
p.s. I think the problem is that r->proxyreq is overloaded -- it's being
used as both a note to the core that the request is being handled by an
HTTP/1.0 proxy, *and* being used as a note from mod_proxy to itself that
it is in fact handling the request.  The second can be solved some other
way -- no other module gets its own private field in request_rec. 

Dean


Re: [PATCH] Reverse proxy and authentication

Posted by Dean Gaudet <dg...@arctic.org>.
[Hey Graham, it's more convenient if you send one attachment with the
whole patch rather than an individual attachments each with one-file
attachments, thanks.]

As Roy might say, "there is no such thing as a reverse proxy".  He's
referring to the fact that if you read the rfc you won't find mention of a
"reverse proxy", only of a proxy.  And strictly speaking, a proxy requires
absolute URIs, for example, so this application is definately not an
*http* proxy.  The core's concept of proxy is closer to that of an http
proxy...

The problem I see here is that really what the proxy module is being used
as here is as a special purpose database for fetching response objects. 
Why does it set proxyreq = 1?  If there were a module which was fetching
objects from a db and caching them locally we wouldn't be setting proxyreq
= 1 for that module... how is this "reverse proxy" different? 

Dean

[PATCH] Reverse proxy and authentication

Posted by Graham Leggett <gr...@dsn.ericsson.se>.
Hi all,

This is the patch that fixes authetication when used with a reverse
proxy.

Currently if an attempt is made using a <Location> structure to password
protect a URL that has been reverse proxied, Apache sends a
"Proxy-authenticate" header instead of an "WWW-authenticate" header.
This confuses the browser, which thinks the Apache webserver is a
website and not a proxy, and the authentication fails.

In the code currently a variable request_rec->proxyreq is set non-zero
if the request is a proxy request. This patch extends that variable to
three states, NONE, PROXY and REVERSE with the values 0, 1 and 2
respectively. This is backward compatible with the "non-zero" behaviour,
and allows the authenticate code to distinguish between the requests.

This patch does not yet fix the case where a website is reverse proxied
through another proxy, still working on it.

The src/CHANGES file:

  *) Fixed proxy/www authentication mixup when authenticating reverse
proxied
     URLs. [Graham Leggett <mi...@sharp.fm>]

No PR's outstanding.

Regards,
Graham
-- 
-----------------------------------------
minfrin@sharp.fm		"There's a moon
					over Bourbon Street
						tonight...

Re: [PATCH] Re: Help with reverse proxy and authentication

Posted by "Dirk-Willem van Gulik (kim)" <di...@webweaving.org>.
Graham Leggett wrote:
 
> I decided to investigate where Apache decided that the request was to be
> handled by the proxy, and change this from a simple noproxy / proxy
> arrangement to three states - NONE, PROXY and REVERSE with the values 0,
> 1 and 2 respectively. Most of the code tests proxyreq for non zero
> status, only the authenticate code cares whether it's REVERSE or PROXY.
> 
> The patches are attached.
> 
> Can you tell me if my solution is too simplistic? I have tested it, and
> it seems to work, but a second opinion would be great.

I have to try this a lot better; and check for the chained case of having
more than one proxy in a row. But the principle is most certainly sound
and certainly the way to go. And I like how general this is; as you could
also use it in the future for things like the FTP proxying.

My compliments. Just submit it I'd say !

Dw.

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> 2a3,5
> >   *) Fixed proxy/www authentication mixup when authenticating reverse proxied
> >      URLs. [Graham Leggett <mi...@sharp.fm>]
> >
> 
>   ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> *** src/include/httpd-old.h     Thu Apr 29 15:31:08 1999
> --- src/include/httpd.h Thu Apr 29 15:04:59 1999
> ***************
> *** 653,659 ****
>       char *the_request;                /* First line of request, so we can log it */
>       int assbackwards;         /* HTTP/0.9, "simple" request */
>       int proxyreq;             /* A proxy request (calculated during
> !                                * post_read_request or translate_name) */
>       int header_only;          /* HEAD request, as opposed to GET */
>       char *protocol;           /* Protocol, as given to us, or HTTP/0.9 */
>       int proto_num;            /* Number version of protocol; 1.1 = 1001 */
> --- 653,661 ----
>       char *the_request;                /* First line of request, so we can log it */
>       int assbackwards;         /* HTTP/0.9, "simple" request */
>       int proxyreq;             /* A proxy request (calculated during
> !                                * post_read_request or translate_name)
> !                                * possible values PROXYREQ_NONE,
> !                                * PROXYREQ_PROXY, PROXYREQ_REVERSE */
>       int header_only;          /* HEAD request, as opposed to GET */
>       char *protocol;           /* Protocol, as given to us, or HTTP/0.9 */
>       int proto_num;            /* Number version of protocol; 1.1 = 1001 */
> ***************
> *** 780,786 ****
> --- 782,797 ----
>    */
>   };
> 
> + /* Possible values of request_rec->proxyreq. A request could be normal,
> +  * proxied or reverse proxied. Normally proxied and reverse proxied are
> +  * grouped together as just "proxied", but sometimes it's necessary to
> +  * tell the difference between the two, such as for authentication.
> +  */
> 
> + #define PROXYREQ_NONE 0
> + #define PROXYREQ_PROXY 1
> + #define PROXYREQ_REVERSE 2
> +
>   /* Things which are per connection
>    */
> 
> 
>   ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> *** src/modules/standard/mod_digest-old.c       Thu Apr 29 15:29:16 1999
> --- src/modules/standard/mod_digest.c   Thu Apr 29 15:19:23 1999
> ***************
> *** 137,143 ****
>   static int get_digest_rec(request_rec *r, digest_header_rec * response)
>   {
>       const char *auth_line = ap_table_get(r->headers_in,
> !                                     r->proxyreq ? "Proxy-Authorization"
>                                       : "Authorization");
>       int l;
>       int s, vk = 0, vv = 0;
> --- 137,143 ----
>   static int get_digest_rec(request_rec *r, digest_header_rec * response)
>   {
>       const char *auth_line = ap_table_get(r->headers_in,
> !                                     (r->proxyreq == PROXYREQ_PROXY) ? "Proxy-Authorization"
>                                       : "Authorization");
>       int l;
>       int s, vk = 0, vv = 0;
> 
>   ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> *** src/modules/standard/mod_rewrite-old.c      Thu Apr 29 15:29:44 1999
> --- src/modules/standard/mod_rewrite.c  Thu Apr 29 15:22:29 1999
> ***************
> *** 1125,1131 ****
>               }
> 
>               /* now make sure the request gets handled by the proxy handler */
> !             r->proxyreq = 1;
>               r->handler  = "proxy-server";
> 
>               rewritelog(r, 1, "go-ahead with proxy request %s [OK]",
> --- 1125,1131 ----
>               }
> 
>               /* now make sure the request gets handled by the proxy handler */
> !             r->proxyreq = PROXYREQ_REVERSE;
>               r->handler  = "proxy-server";
> 
>               rewritelog(r, 1, "go-ahead with proxy request %s [OK]",
> ***************
> *** 1385,1391 ****
>               }
> 
>               /* now make sure the request gets handled by the proxy handler */
> !             r->proxyreq = 1;
>               r->handler  = "proxy-server";
> 
>               rewritelog(r, 1, "[per-dir %s] go-ahead with proxy request "
> --- 1385,1391 ----
>               }
> 
>               /* now make sure the request gets handled by the proxy handler */
> !             r->proxyreq = PROXYREQ_REVERSE;
>               r->handler  = "proxy-server";
> 
>               rewritelog(r, 1, "[per-dir %s] go-ahead with proxy request "
> 
>   ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> *** src/modules/proxy/mod_proxy-old.c   Thu Apr 29 15:28:12 1999
> --- src/modules/proxy/mod_proxy.c       Thu Apr 29 15:25:04 1999
> ***************
> *** 153,159 ****
>             && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
>             && ap_matches_request_vhost(r, r->parsed_uri.hostname,
>                  r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) {
> !           r->proxyreq = 1;
>             r->uri = r->unparsed_uri;
>             r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
>             r->handler = "proxy-server";
> --- 153,159 ----
>             && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
>             && ap_matches_request_vhost(r, r->parsed_uri.hostname,
>                  r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) {
> !           r->proxyreq = PROXYREQ_PROXY;
>             r->uri = r->unparsed_uri;
>             r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
>             r->handler = "proxy-server";
> ***************
> *** 163,169 ****
>       else if (conf->req && r->method_number == M_CONNECT
>              && r->parsed_uri.hostname
>              && r->parsed_uri.port_str) {
> !           r->proxyreq = 1;
>             r->uri = r->unparsed_uri;
>             r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
>             r->handler = "proxy-server";
> --- 163,169 ----
>       else if (conf->req && r->method_number == M_CONNECT
>              && r->parsed_uri.hostname
>              && r->parsed_uri.port_str) {
> !           r->proxyreq = PROXYREQ_PROXY;
>             r->uri = r->unparsed_uri;
>             r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
>             r->handler = "proxy-server";
> ***************
> *** 198,204 ****
>              r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real,
>                                    r->uri + len, NULL);
>              r->handler = "proxy-server";
> !            r->proxyreq = 1;
>              return OK;
>         }
>       }
> --- 198,204 ----
>              r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real,
>                                    r->uri + len, NULL);
>              r->handler = "proxy-server";
> !            r->proxyreq = PROXYREQ_REVERSE;
>              return OK;
>         }
>       }
> ***************
> *** 304,310 ****
>         int maxfwd = strtol(maxfwd_str, NULL, 10);
>         if (maxfwd < 1) {
>             int access_status;
> !           r->proxyreq = 0;
>             if ((access_status = ap_send_http_trace(r)))
>                 ap_die(access_status, r);
>             else
> --- 304,310 ----
>         int maxfwd = strtol(maxfwd_str, NULL, 10);
>         if (maxfwd < 1) {
>             int access_status;
> !           r->proxyreq = PROXYREQ_NONE;
>             if ((access_status = ap_send_http_trace(r)))
>                 ap_die(access_status, r);
>             else
> 
>   ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> *** src/main/http_protocol-old.c        Thu Apr 29 15:27:46 1999
> --- src/main/http_protocol.c    Thu Apr 29 15:19:49 1999
> ***************
> *** 1109,1115 ****
>           ap_note_auth_failure(r);
>       else
>           ap_table_setn(r->err_headers_out,
> !                   r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
>                     ap_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"",
>                             NULL));
>   }
> --- 1109,1115 ----
>           ap_note_auth_failure(r);
>       else
>           ap_table_setn(r->err_headers_out,
> !                   (r->proxyreq == PROXYREQ_PROXY) ? "Proxy-Authenticate" : "WWW-Authenticate",
>                     ap_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"",
>                             NULL));
>   }
> ***************
> *** 1117,1123 ****
>   API_EXPORT(void) ap_note_digest_auth_failure(request_rec *r)
>   {
>       ap_table_setn(r->err_headers_out,
> !           r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
>             ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%lu\"",
>                 ap_auth_name(r), r->request_time));
>   }
> --- 1117,1123 ----
>   API_EXPORT(void) ap_note_digest_auth_failure(request_rec *r)
>   {
>       ap_table_setn(r->err_headers_out,
> !           (r->proxyreq == PROXYREQ_PROXY) ? "Proxy-Authenticate" : "WWW-Authenticate",
>             ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%lu\"",
>                 ap_auth_name(r), r->request_time));
>   }
> ***************
> *** 1125,1131 ****
>   API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
>   {
>       const char *auth_line = ap_table_get(r->headers_in,
> !                                       r->proxyreq ? "Proxy-Authorization"
>                                                     : "Authorization");
>       const char *t;
> 
> --- 1125,1131 ----
>   API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
>   {
>       const char *auth_line = ap_table_get(r->headers_in,
> !                                       (r->proxyreq == PROXYREQ_PROXY) ? "Proxy-Authorization"
>                                                     : "Authorization");
>       const char *t;
> 
> 
>   ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> *** src/modules/proxy/proxy_ftp-old.c   Thu Apr 29 15:28:26 1999
> --- src/modules/proxy/proxy_ftp.c       Thu Apr 29 15:25:40 1999
> ***************
> *** 419,425 ****
>    */
>   static int ftp_unauthorized (request_rec *r, int log_it)
>   {
> !     r->proxyreq = 0;
>       /* Log failed requests if they supplied a password
>        * (log username/password guessing attempts)
>        */
> --- 419,425 ----
>    */
>   static int ftp_unauthorized (request_rec *r, int log_it)
>   {
> !     r->proxyreq = PROXYREQ_NONE;
>       /* Log failed requests if they supplied a password
>        * (log username/password guessing attempts)
>        */
> 
>   ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> *** src/main/http_request-old.c Thu Apr 29 15:27:59 1999
> --- src/main/http_request.c     Thu Apr 29 15:18:03 1999
> ***************
> *** 981,987 ****
>        * about proxy authentication.  They treat it like normal auth, and then
>        * we tweak the status.
>        */
> !     if (r->status == AUTH_REQUIRED && r->proxyreq) {
>           r->status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
>       }
> 
> --- 981,987 ----
>        * about proxy authentication.  They treat it like normal auth, and then
>        * we tweak the status.
>        */
> !     if (r->status == AUTH_REQUIRED && r->proxyreq == PROXYREQ_PROXY) {
>           r->status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
>       }
>

[PATCH] Re: Help with reverse proxy and authentication

Posted by Graham Leggett <mi...@sharp.fm>.
Dirk-Willem van Gulik wrote:

> Unfortunately the answer is not soo simple. Are you in a great hurry ?
> if not, I am currently completing a consultancy, which includes a so-
> lution for part of this problem. I intend to silently put this into
> apache  two weeks after the last deliverable is accepted, and I
> think it would be a good idea to do the reverse proxy as well.

I decided to investigate where Apache decided that the request was to be
handled by the proxy, and change this from a simple noproxy / proxy
arrangement to three states - NONE, PROXY and REVERSE with the values 0,
1 and 2 respectively. Most of the code tests proxyreq for non zero
status, only the authenticate code cares whether it's REVERSE or PROXY.

The patches are attached.

Can you tell me if my solution is too simplistic? I have tested it, and
it seems to work, but a second opinion would be great.

Regards,
Graham
-- 
-----------------------------------------
minfrin@sharp.fm		"There's a moon
					over Bourbon Street
						tonight...