Hey Grace, Albert & Robert

It looks like your support for nginx_accel_redirect won't quite work as expected. There are two issues I've found so far.

#1) Your nginx_octopus_include.conf includes the following for private files

    ###
    ### deny direct access to private downloads
    ###
    location ~* ^/sites/.*/private/ {
        access_log  off;
        deny all;
    }

This won't work with the X-Accel-Redirect header because you have not specified it as internal. I have forked your files on github and I think you need this.

    ###
    ### deny direct access to private downloads
    ###
    location ~* ^/sites/.*/private/ {
		log_not_found off;
		internal;
    }

However, BEFORE YOU DO THAT.... We have issue...

#2) Your choice of a location won't work well with Drupal.

Using the location of ^/sites/.*/private/ puts the /private folder directly within the example.com (site folder). This means you would need to set your /admin/settings/file-system to sites/example.com and then the path on something like filefield to /private/whatever (mine is /private/videos).

So the full path ends up being sites

/sites/example.com/private/videos

Your included module of nginx_accel_redirect hijacks the /system/files menu path in order to inject the X-Accel-Redirect header.

This means if a user opts to use this module, they MUST set Drupal to use Private files.

The problem is, that the module uses Drupal's file_create_path() function, which in turn calls file_directory_path(). If you look at that function it looks like this.

function file_directory_path() {
  return variable_get('file_directory_path', conf_path() .'/files');
}

Which means that /files is implied and the call to file_create_path() within nginx_accel_redirect will always fail and the X-Accel-Redirect header will never get sent.

My suggestion would be to modify your nginx location config to the following.

###
### deny direct access to private downloads
### with support for x-accel-redirect headers
### http://drupal.org/project/nginx_accel_redirect
###
location ~* /files/private/ {
	log_not_found off;
	internal;
}

It would be optional to use perusio's config setting for
https://github.com/perusio/drupal-with-nginx/blob/master/sites-available...

location ~* /system/files/ {
    try_files $uri /index.php?q=$uri&$args;
    log_not_found off;
}

As I would personally prefer that you simply block /files/private and allow me to still use Drupal's public files setting - knowing that /files/private is blocked off. This will allow me to write my own modules and inject the x-accel-redirect when I see fit. Of course this also factors into my own file_access module which I will be using and would work with nginx_accel_redirect.

I've not been able to test this yet on my local machine as I just got nginx running today, but this is what I've found to be the case so far. Of course, with only 2 reported sites using nginx_accel_redirect they may not be on the servers Omega8 is running. ;)

Am I right on this one? Or do you have people on omega servers who are successfully using nginx_accel_redirect?

Comments

omega8cc’s picture

Status: Active » Needs review

Hey Matt!

The location with ~* ^/sites/.*/private/ { is still required, as it is used also for the built-in in Aegir support for sites/domain/private directory, by default created. And it *does work* also for sites/domain/files/private directory, thanks to the wildcard in the path.

Also, we don't use internal there, because some modules expect error 403 and not 404 - which is default when internal is used. At least this is how I remember why we did it that way there.

Note also that it is not *our* choice of the location - it is default in the standard Drupal multisite, which is the directory structure used in Aegir, and there are good reasons why the standard path to files is hardcoded in Aegir, so you can't change it and there will be always the /sites/domain in the (direct) URI.

We also have already location for system, which is always sent to drupal for processing.

So what can be wrong if we already have all required locations? I think we have them in the wrong order, as location for private is above/before location with system, so it never has a chance to hit the system location and is stopped completely earlier, at private location.

I'm not sure if changing deny all to internal would help here, probably not, so we probably only need to re-order those locations a bit.

I just did that and it is now in HEAD, could you test and let us know if that helped?

The fix: http://drupalcode.org/project/octopus.git/commit/f7bb289

Thanks.

mattman’s picture

Ok, I'll pull it down and give it a go. I am trying on my local dev enviroment so I'm just including the nginx_octopus_include.conf file into my server - I'm not running a virtual of a linux box (on OS X).

I was testing with persuio's configs just to see if I could get things going and so far I'm still having issues with replicating the setup. It may be a nginx issue or something else. I installed nginx via macports.

If I can't get much farther, then I'll have to shoot a video and get it to you guys.

Once I can get this going then I'll be an nginx/omega8 convert for life. ;)

Matt

mattman’s picture

Here's a screencast of my setup and the issue.

http://screencast.com/t/UxQpliAiC

Ok, I've symlinked the nginx_octopus_include.conf into my server and I'm getting the same issues I had with perusio's config.

The issue may either be me, my setup - or the universe. :) The issue I'm hitting is that the file_create_path() function is not returning a valid path. This means the nginx_accel_redirect_file_transfer() function is never getting to the point where it can send header('X-Accel-Redirect: ' . base_path() . file_create_path($filepath));

Even when I modify the code to make sure a valid path gets into the function. The header may be added but nginx does not pass the file through. It still hits return drupal_not_found(); or it's never even getting redirected to nginx.

You'll have to excuse any lack of knowledge. I've only just setup nginx for the first time yesterday. I've been beating on this for the past two days however. Maybe you can see something in my setup.

One interesting thing is that when I watch the headers using Live HTTP headers in Firefox, I only send the request to GET /system/files/private/files/KarateAppPart11.zip and the immediate response is a 404 from nginx. So it would seem that nginx isn't even pushing the request to drupal - yet the returned page is a drupal 'Page not found'. Very confusing right now.

mattman’s picture

Here's some additional info.

With everything in place here's some additional testing I've done. Here are the headers when the private files is turned on.

http://dev.mag6/system/files/private/files/KarateAppPart11.zip



GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1

Host: dev.mag6

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 FirePHP/0.5

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-us,en;q=0.5

Accept-Encoding: gzip, deflate

Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7

Keep-Alive: 115

Connection: keep-alive

Referer: http://dev.mag6/videos/the-separation-model-part-11-reporting-separated.html

Cookie: SESS81ee2fea142c4d3b030c6f2dd048d7bb=02vse9n0bg1js5aet5gqjpt9r5; has_js=1

x-insight: activate

If-Modified-Since: Wed, 22 Jun 2011 20:11:02 GMT



HTTP/1.1 404 Not Found

Server: nginx/1.0.0

Date: Wed, 22 Jun 2011 20:11:15 GMT

Content-Type: text/html; charset=utf-8

Transfer-Encoding: chunked

Connection: keep-alive

X-Powered-By: PHP/5.3.6

Expires: Sun, 19 Nov 1978 05:00:00 GMT

Last-Modified: Wed, 22 Jun 2011 20:11:15 GMT

Cache-Control: store, no-cache, must-revalidate, post-check=0, pre-check=0

Content-Encoding: gzip

----------------------------------------------------------

According to...

    ###
    ### imagecache and imagecache_external support
    ###
    location ~* /(?:external|system|files/imagecache|files/styles)/ {
        access_log         off;
        expires            30d;
        # fix common problems with old paths after import from standalone to Aegir multisite
        rewrite  ^/sites/(.*)/files/imagecache/(.*)/sites/default/files/(.*)$  /sites/$host/files/imagecache/$2/$3 last;
        rewrite  ^/files/imagecache/(.*)$                                      /sites/$host/files/imagecache/$1 last;
        rewrite  ^/files/styles/(.*)$                                          /sites/$host/files/styles/$1 last;
        add_header X-Header "IC Generator 1.0";
        try_files $uri @drupal;
    }

The try_files is pushing to @drupal, which is where the return drupal_not_found(); is being hit.

However, notice that if I disable the #try_files $uri @drupal; the return headers I get are of a nginx 404.

HTTP/1.1 404 Not Found

Server: nginx/1.0.0

Date: Wed, 22 Jun 2011 20:14:56 GMT

Content-Type: text/html

Transfer-Encoding: chunked

Connection: keep-alive

Content-Encoding: gzip

Should I not be seeing the add_header X-Header "IC Generator 1.0"; or is this because nginx moves on to the 404 and does not add the header due to the error?

I'm still coming up to speed on how you guys have this conf laid out.

mattman’s picture

Ok, making some progress. I think I found one issue. The file_create_path() was not working in Drupal because the path to my files was via a symlink.

I don't think php-fpm was able to stat the file so php's realpath() was likely failing within file_check_location().

It's odd because php-fpm is running as my login username and has permissions all the way down. Are there issues with nginx/php-fpm and symlinks?

At least I'm moving forward. It may not be your config at all - It will likely turn out to be me and my lack of knowledge about nginx/php-fpm (coming from an Apache guy :)

Matt

mattman’s picture

I'm at least now passing through the nginx_accel_redirect_file_transfer() function.

Adding a dd(headers_list()); is giving me this.

Array
(
    [0] => X-Powered-By: PHP/5.3.6
    [1] => Expires: Sun, 19 Nov 1978 05:00:00 GMT
    [2] => Last-Modified: Wed, 22 Jun 2011 21:24:04 GMT
    [3] => Cache-Control: post-check=0, pre-check=0
    [4] => Content-Type: application/zip; name="KarateAppPart11.zip"
    [5] => Content-Length: 284972
    [6] => Content-Disposition: attachment; filename="KarateAppPart11.zip"
    [7] => Cache-Control: private
    [8] => X-Accel-Redirect: /sites/dev.mag6/files/private/files/KarateAppPart11.zip
)

So at least the header is being added. However, the response I'm now getting is a 403 from nginx.

http://dev.mag6/system/files/private/files/KarateAppPart11.zip



GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1
Host: dev.mag6
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 FirePHP/0.5
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://dev.mag6/
Cookie: SESS81ee2fea142c4d3b030c6f2dd048d7bb=02vse9n0bg1js5aet5gqjpt9r5; has_js=1
x-insight: activate

HTTP/1.1 403 Forbidden
Server: nginx/1.0.0
Date: Wed, 22 Jun 2011 21:27:18 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Expires: Sun, 19 Nov 1978 05:00:00 GMT
Cache-Control: post-check=0, pre-check=0, private
Content-Disposition: attachment; filename="KarateAppPart11.zip"
Content-Encoding: gzip

Here's the result of the error.log debug

2011/06/22 14:32:20 [notice] 40718#0: *74 "(?:HTTrack|HTMLParser|libwww|PECL|AutomaticSiteMap|ClickSense|ValueClick|SiteBot|BuzzTracker)" does not match "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 FirePHP/0.5", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [notice] 40718#0: *74 "^(?:GET|HEAD|POST)$" matches "GET", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [notice] 40718#0: *74 "^/sites/(.*)/files/imagecache/(.*)/sites/default/files/(.*)$" does not match "/system/files/private/files/KarateAppPart11.zip", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [notice] 40718#0: *74 "^/files/imagecache/(.*)$" does not match "/system/files/private/files/KarateAppPart11.zip", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [notice] 40718#0: *74 "^/files/styles/(.*)$" does not match "/system/files/private/files/KarateAppPart11.zip", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [notice] 40718#0: *74 "^/(.*)$" matches "/system/files/private/files/KarateAppPart11.zip", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [notice] 40718#0: *74 rewritten data: "/index.php", args: "q=system/files/private/files/KarateAppPart11.zip", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [notice] 40718#0: *74 "^(?:HEAD|POST)$" does not match "GET", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [notice] 40718#0: *74 "(?:HTTrack|HTMLParser|libwww|PECL|AutomaticSiteMap|ClickSense|ValueClick|SiteBot|BuzzTracker)" does not match "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 FirePHP/0.5", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [notice] 40718#0: *74 "^(?:GET|HEAD|POST)$" matches "GET", client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "dev.mag6", referrer: "http://dev.mag6/"
2011/06/22 14:32:20 [error] 40718#0: *74 access forbidden by rule, client: 127.0.0.1, server: dev.mag6, request: "GET /system/files/private/files/KarateAppPart11.zip HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "dev.mag6", referrer: "http://dev.mag6/"

It looks like it's not matching anything and getting all the way back up to

    ###
    ### deny not compatible request methods without 405 response
    ###
    if ( $request_method !~ ^(?:GET|HEAD|POST)$ ) {
         return 444;
    }
omega8cc’s picture

So maybe we should really change deny all to internal at http://drupalcode.org/project/octopus.git/blob/HEAD:/aegir/conf/nginx_oc... ?

Could you try to change this there?

mattman’s picture

Yep, that's an affirmative. As soon as you go internal the file comes down. So there's definitely a relationship between x-accel and internal.

http://wiki.nginx.org/X-accel

With a 403 deny all it's likely saying "hey this is explicitly denied no matter what!" :)

If other modules expect 403s then you may need to create another location and work within /files and tell people they have to store things in /files/private

Now I have to figure out what the issue is with symlinks.

You guys are using symlinks right? The future issue I'm wondering about is in this particular site the /files/private would have around 13G of files. What would the impact be on a aegir clone on that one! If the files are copied when you make a clone that would suck. So it would be important for me to keep the files outside of the root but yet still support x-accel

On my local box here I can't get symlinks working. Of course, I'm going across two drives - but should that be an issue?

omega8cc’s picture

Thanks, you are right, as it is still using the same URI, just with special header and not like it is for other modules where requests always goes via some Drupal controlled gate like /admin. We just sent this also upstream to Provision.

As for symlinks - yes, we use them and with that big files directory you must move it away from the site's directory anyway, however on the server you will not cross filesystems/disks, so maybe it is some Mac specific issue?

mattman’s picture

Status: Needs review » Closed (fixed)

Yeah, been toying with php realpath() not being able to stat the file. Something is going on and I'll just have to move the site and files onto the same drive to test.

It's one of those issues that drives you bonkers until you break down and just change how you're doing things. You want to find the answer but you know that many hours wasted = many hours wasted. :)

I'm just glad to know that it will all work out. I'm looking forward to moving my sites to omega boxes. I wish I wasn't so busy right now so I could make some instructional videos about using your setup. At least I'm gaining a ton of knowledge about how things are working behind the scenes!

Closing the issue.

perusio’s picture

Hello,

There's no hardcoding of anything in the module. You can place the private files dir wherever you want. Of course the nginx config must be adjusted. Namely the protection of the direct access.

I haven't checked the code, but in the wiki it's explicit that the location where the special pipelline is active must be set internal. This returns a 404 when trying to access it directly. If you need the 403 then you can use error_page.

error_page 404 =403 /404.html;

in the internal location. Now the 403 status code will be returned. The 404.html can be created by just doing:

touch 404.html

in the site root.

mattman’s picture

Thanks António!

You're knowledge, insight and contributions are muito apreciado. I was able to get my nginx setup going quite quickly with the help of your configs!

omega8cc’s picture

Status: Closed (fixed) » Fixed

Thank you.

Now Octopus supports X-Accel-Redirect correctly. As I'm not sure if we need 403 there for other modules really, I will leave this with internal for now and will use perusio's tip from #11 if needed.

Thanks for all your help in debugging this!

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.