Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

My developers want to allow download some files only to users in lan. I said ok it's quite simple and I wrote changes to nginx config like this :



location /restricteddir/download/file {                                                             
        allow   192.168.0.0/16;                                                              
        allow   10.0.0.0/8;                                                                  
        deny all;                                                                            
    }

Ok from outside I'm getting 403 so thats good but from inside (LAN) gives me 404. Why?

I have checked, the location of file doesn't exist on disk. The developer told me that the location is dynamically generated by php so files are in tmp directory but after you click link like:

https://example.com/report/download/file/id/somefile.txt it should started download but it gives 404 error.

The last physical loaction is /restricteddir/download so file/id/somefile.txt is generated by php.

For testing I changed the location to /restricteddir/download and after that hiting https://example.com/restricteddir/download gave me 404 to.

I'm confused after disabling restriction to that dir everythings works fine, I can download file from https://example.com/report/download/file/id/somefile.txt and hit https://example.com/report/download/ without 404. So where is the bug ? What should I add to my nginx configuration to make it work?

EDIT1 This is full config for this virtualhost:


#example.com:443
server {
    listen 443 default;
    ssl on;
    ssl_certificate example.crt;
    ssl_certificate_key example.key;
    ssl_session_timeout  15m;
    ssl_protocols  SSLv2 SSLv3 TLSv1;
    ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_prefer_server_ciphers   on;                  
    server_name example.com;
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;



location / {
        root   /sites/public;
        index  index.php;
        if (!-f $request_filename) {
                rewrite ^.*$ /index.php last;
                break;
        }
    }
location /restricteddir/download/file {
       allow   192.168.0.0/16;
       allow   10.0.0.0/8;
       deny all;
    }

#PHP
location ~ \.php$ {        
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;      
        include /fastcgi_params;        
        fastcgi_param  SCRIPT_FILENAME /sites/public$fastcgi_script_name;
    }
}

server {
        listen   80 default;
        server_name example.com;
        access_log off;
        rewrite ^(.*)$ https://example.com$1 permanent;
        }

And I founded this in my logs:


2012/08/07 11:55:43 [error] 11121#0: *90 open() "/usr/share/nginx/html/restricteddir/download/file/id/somefile.txt" failed (2: No such file or directory), client: 10.200.2.70, server: example.com, request: "GET /restricteddir/download/file/id/somefile.txt HTTP/1.1", host: "example.com"

So now I know that nginx is looking this location in wrong root /usr/share/nginx/html/ instead in /sites/public

So maybe i should write this like :


location /sites/public/restricteddir/download/file {                                                             
        allow   192.168.0.0/16;                                                              
        allow   10.0.0.0/8;                                                                  
        deny all;                                                                            
    }

EDIT2

I moved root directive to server block and now in logs after enabling restriction for location /restricteddir/download/file I have:


2012/08/09 10:43:12 [error] 32120#0: *71 open() "/site/public/restricteddir/download/file/id/somefile.txt" failed (2: No such file or directory), client: 10.2.1.120, server: example.com, request: "GET /restricteddir/download/file/id/somefile.txt HTTP/1.1", host: "example.com"
So now nginx is looking in right place but doesn't find enything. Like this file was not generated by php? I'm strugling with it and have no idea what is wront... Thats a simple task restrcit location why this doesn't work ?

EDIT3

This is how looks my virtualhost config now:


#example.com:443
server {
    listen 443 default;
    ssl on;
    ssl_certificate example.crt;
    ssl_certificate_key example.key;
    ssl_session_timeout  15m;
    ssl_protocols  SSLv2 SSLv3 TLSv1;
    ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_prefer_server_ciphers   on;                  
    server_name example.com;
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;
    root   /sites/public;


location / {
        index  index.php;
        if (!-f $request_filename) {
                rewrite ^.*$ /index.php last;
                break;
        }
    }
location /restricteddir/download/file {
       allow   192.168.0.0/16;
       allow   10.0.0.0/8;
       deny all;
    }

#PHP
location ~ \.php$ {        
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;      
        include /fastcgi_params;        
        fastcgi_param  SCRIPT_FILENAME /sites/public$fastcgi_script_name;
    }
}

server {
        listen   80 default;
        server_name example.com;
        access_log off;
        rewrite ^(.*)$ https://example.com$1 permanent;
        }

So I only moved root directive into server block, after that nginx is looking in good place but still geting 404. I'm confused beacuse after I comment out location /restricteddir/download/file block everything is working fine and I can download the file.

For me it't very strange because its only simple restriction ... why is it that after enabling it nginx could not find that file ...

share|improve this question
1  
what is your full config part for main backend? You'll probably need to add proxy_pass backend/restricteddir/download/file inside your new restricted location. –  Andrei Mikhaltsov Aug 7 '12 at 9:47
1  
@Andrei Mikhaltsov I added this to my question in edit –  B14D3 Aug 7 '12 at 10:24
    
@MarkStosberg I made a typo when was converting from orginal configuration(Another mis-match I see is that your log entry refers to "restricteddir", but your "full config" post does not mention this. It mentions a "report" dir instead). –  B14D3 Aug 9 '12 at 9:01

2 Answers 2

Two suggestions:

  1. Move the root directive into the server block, so it will apply to all location blocks that follow it. That appears to be your intent.
  2. Keep in mind that the location directive applies to URIs not *file paths. So for example, the idea of location /sites/public doesn't make sense.

And some other feedback:

The question of whether the files physically exist or not does not make a difference when processing location directives, they will match regardless.

Another mis-match I see is that your log entry refers to "restricteddir", but your "full config" post does not mention this. It mentions a "report" dir instead.

share|improve this answer

Firstly, I'd avoid the following if block if possible because if is evil. Use try_files instead:

location / {
    root   /sites/public;
    index  index.php;
    if (!-f $request_filename) {
            rewrite ^.*$ /index.php last;
            break;
    }
}

This is better:

location / {
    try_files $uri $uri/ index.php?$args;
}

For clarity, I'd move root and index out of location / and into the server block:

server {
    …
    server_name example.com;
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    root   /sites/public;
    index  index.php;

Are the files completely dynamically generated by PHP, so don't exist on the filesystem directly and therefore can't be served by nginx without passing the request on to PHP? If so, you need to pass things with fastcgi_pass to fastcgi so PHP can create & serve the file, rather than nginx trying to serve something that doesn't exist. So:

location /report/download/ {
   allow   192.168.0.0/16;
   allow   10.0.0.0/8;
   deny all;
   fastcgi_pass   127.0.0.1:9000;
   fastcgi_index  index.php;      
   include /fastcgi_params;        
   fastcgi_param  SCRIPT_FILENAME /sites/public$fastcgi_script_name;
}

If the files actually exist on-disk, but are in a different location, i.e. the request comes in as https://example.com/report/download/file/id/somefile.txt which actually points to the on-disk location of /sites/public/restricteddir/download/file/id/somefile.txt and you want nginx to serve it directly without handing over to fastcgi then you want the alias directive.

location /report/download/ {
   allow   192.168.0.0/16;
   allow   10.0.0.0/8;
   deny all;
   # The actual physical path on disk -
   alias /sites/public/restricteddir/download/;
   # or should this be the following? -
   # alias /restricteddir/download/;
}

One final thing to check: if nginx and fastcgi are running as different users, make sure that both have the correct OS file permissions to read the files in question. If nginx is looking in the correct place but still can't find the file, perhaps its owner and group are fastcgi:fastcgi with 660 permissions? Either that or there's some sort of caching going on, such as open_file_cache in nginx.conf

share|improve this answer
    
I have already defined that all php files should be proxied to fast cgi in location ~ \.php$ block. Is it not enought? I should define new location block for php files in my restricted location ? –  B14D3 Aug 10 '12 at 8:15
1  
I'd fix your location / block to use try_files first, then see how it works. I've had weirdness when using allow and deny where nginx doesn't continue to follow the location matching chain, so it certainly won't hurt to try the code blocks I've listed above. –  Stef Pause Aug 10 '12 at 16:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.