Skip to content

Doesn't work on NGINX due to use of .htaccess files and relying on PATH_INFO #538

Open
@tomashastings

Description

@tomashastings
Contributor

I was pleased to see 1.7 has finally been released, yet somewhat displeased to see the zip file contains a number of .htaccess files.

Installing went fine, but as soon as I wanted to set up the API to inject e-mails into the system I ran into a number of 'URL not supported' errors.

I had set up NGINX to handle the /api/* URLS by adding this line to my configuration:

location ~ ^/api/(tickets|tasks)(.*)$ {
try_files $uri $uri/ /api/http.php;
}

Yet the requests still failed, it turns out the function get_path_info returns $_SERVER['PATH_INFO'] or $_SERVER['ORIG_PATH_INFO'], both of which aren't set when using NGINX + PHP-FPM.

I had to rewrite get_path_info() to have it look at the REQUEST_URI, snip off the excess bits at the front and back and return the part of the url which is expected.

The function get_path_info contains the following comment: //TODO: conruct possible path info.

You might want to change that to 'TODO: don't rely on PATH_INFO as it's unreliable cross-platform

Activity

protich

protich commented on Apr 7, 2013

@protich
Member

@tomashastings - Thank you for the feedback.

Like any other software osTicket v1.7 is not perfect! In our defense the requirements clearly indicates Apache & IIS being the supported web servers. We were sort aware of possible issues related to reliance on AcceptPathInfo directive and mode_rewrite on api urls - e.g path info on nginx.

Since we couldn't possibly test all possible server configurations - relying on collaborative feedback from the field/users (like yourself) is the approach we took. After all, it's an open source project.

I don't mean to sound defensive - but the tone of your feedback came off as "Look at all the work you guys made me do". TODO comments on open source code base are open invites for others to help. It would have been nice to get a pull request on path info fix you implemented and may be a writeup on how you got the API to work on nginx web server.

It's the open source way - together we can make it better.

tomashastings

tomashastings commented on Apr 7, 2013

@tomashastings
ContributorAuthor

Thanks for the reply protich, I'm sorry if my message came off a little strong, English isn't my main language.

I also seem to have overlooked the Apache/IIS requirement. I've used Apache for years but have moved to NGINX for performance reasons, I'm used to having to jump through a few hoops to get things working so that's ok.

As far as path info on nginx goes, the fastcgi_split_path_info directive isn't the solution as it's intended use are urls like /foo/bar.php/some/path and the osTicket api URL's don't contain .php

The way to get php to handle a non-existing url is by using the try_files directive.

Also, I'm new to Github, all I've ever used it for is looking-at and downloading code, never contributing, if I knew what a pull request was, I might have used it ;)

As far as a solution goes, I'll gladly provide information on how to get everything to work, but it involves a little editing of class.osticket.php and I'm not quite sure if /everything/ works yet, haven't had the time to test and find out which parts of osTicket rely on the get_path_info() function.

I plan to install apache on another port to see if I didn't break anything and if both webservers act the same.

Going to read up on pull requests and how to help now.

protich

protich commented on Apr 7, 2013

@protich
Member

Thanks for the reply. No apology needed - all I wanted you to know is that, you can help make osTicket better. You're now chief NGINX resident expert and with your feedback/help you can help others in the osTicket community - who might not be as tech-savvy as you.

That said, we have 2 issues;

  • API's url rewrite

I'm assuming the sample code you posted (quoted below) rewrites the requested URL on-the-fly.

location ~ ^/api/(tickets|tasks)(.*)$ {
try_files $uri $uri/ /api/http.php;
}

Basically we need to rewrite request to /api/path.x as /api/http.php/path.x

  • Missing PATH_INFO

osTicket's url dispatcher for the API and AJAX calls relies on PATH_INFO to serve the request. Best approach, in my opinion, is to let the server set the info - hence the need to enable AcceptPathInfo directive in apache. For NGINX I'm assuming fastcgi_split_path_info accomplishes the same.

As for users who can't possibly change web server's configuration - we'll need to put together a fix in get_path_info() method - something you already implemented.

PS: It's totally worth it to learn git. Let me know if I can be of any help.

reopened this on Apr 7, 2013
tomashastings

tomashastings commented on Apr 7, 2013

@tomashastings
ContributorAuthor

I couldn't get it to work with fastcgi_split_path_info, I think that's meant for /file.php/extra/path type urls and not /fake/extra/path like osTicket is using (please correct me if i'm wrong).

I had a fix in which I modified class.osticket.php to chop up the request_uri and get the path_info from there, but that's far from ideal, so I decided to make nginx set the PATH_INFO by using set, if and a regular expression in the server-configuration.

This configuration file for NGINX seems to work, it set's PATH_INFO where needed, denies access to files which need not be accessed directly and serves the api/ajax requests

server {
        listen 80;
        server_name tickets.example.net;

        root /usr/home/tickets/html;
        access_log  /usr/home/tickets/logs/access.log main;

        set $path_info "";

        # Deny access to all files in the include directory
        location ~ ^/include {
                deny all;
                return 403;
        }

        # Deny access to apache .ht* files (nginx doesn't use these)
        location ~ /\.ht {
                deny all;
        }


        # Requests to /api/* need their PATH_INFO set, this does that
        if ($request_uri ~ "^/api(/[^\?]+)") {
                set $path_info $1;
        }

        # /api/*.* should be handled by /api/http.php if the requested file does not exist
        location ~ ^/api/(tickets|tasks)(.*)$ {
                try_files $uri $uri/ /api/http.php;
        }

        # /scp/ajax.php needs PATH_INFO too, possibly more files need it hence the .*\.php
        if ($request_uri ~ "^/scp/.*\.php(/[^\?]+)") {
                set $path_info $1;
        }

        # Make sure requests to /scp/ajax.php/some/path get handled by ajax.php
        location ~ ^/scp/ajax.php/(.*)$ {
                try_files $uri $uri/ /scp/ajax.php;
        }

        # Set index.php as our directoryindex
        location / {
                index index.php;
        }

        # Send php files off to the PHP-FPM listing on localhost:9000
        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_pass    127.0.0.1:9000;
                fastcgi_index   index.php;
                fastcgi_param   SCRIPT_FILENAME         $document_root$fastcgi_script_name;
                fastcgi_param   PATH_INFO               $path_info;
                include fastcgi_params;
        }

}
tomashastings

tomashastings commented on Apr 8, 2013

@tomashastings
ContributorAuthor

I slept on the above and came up with the following solution for people who cannot (or don't want to) modify their nginx config, the use of 'if' statements in nginx is discouraged ( see: http://wiki.nginx.org/IfIsEvil )

By modifying class.osticket.php's get_path_info function, the path info is determined by looking at the request_uri and script_name.

I tested it on the /api and /scp/ajax.php urls and it seems to work as expected:

    function get_path_info() {
        if(isset($_SERVER['PATH_INFO']) && !empty($_SERVER['PATH_INFO']))
            return $_SERVER['PATH_INFO'];

        if(isset($_SERVER['ORIG_PATH_INFO']) && !empty($_SERVER['ORIG_PATH_INFO']))
            return $_SERVER['ORIG_PATH_INFO'];

        $request_uri = preg_replace('@\?.*$@', '', $_SERVER['REQUEST_URI']);

        if (strpos($request_uri, $_SERVER['SCRIPT_NAME']) !== false) {
            $guessed_pathinfo = preg_replace('#^'.preg_quote($_SERVER['SCRIPT_NAME']).'#', '', $request_uri);
        } else {
            $guessed_pathinfo = preg_replace('#^'.preg_quote(preg_replace('@/([^/]+)$@', '', $_SERVER['SCRIPT_NAME'])).'#', '', $request_uri);
        }
        if (!empty($guessed_pathinfo))
            return $guessed_pathinfo;

        return null;
    }
deanet

deanet commented on Nov 9, 2013

@deanet

any update of this issue ? i'm currently using branch repo but it still doesnt work..

greezybacon

greezybacon commented on Nov 9, 2013

@greezybacon
Contributor

Sorry, we still haven't added NGINX as a supported platform yet. However, GoDadday hosting clients still have troubles with osTicket related to path parsing, so I think addressing this issue would be beneficial to a supported platform too.

SoulRaven

SoulRaven commented on Jan 8, 2014

@SoulRaven

i have a similar problem but wit ajax calls, osTicket/osTicket#382 (comment)

any ideea how to fix this?

greezybacon

greezybacon commented on Jan 8, 2014

@greezybacon
Contributor

Use the nginx configuration posted by @tomashastings above

SoulRaven

SoulRaven commented on Jan 8, 2014

@SoulRaven

this is my nginx config:

server {
listen xxx:80;
server_name xxxx;
root /var/www/support;
index index.php;
index index.php;
access_log /var/www/support/tmp/logs/access.log combined buffer=32k;
error_log /var/www/support/tmp/logs/error.log;

set $path_info "";

# Deny access to all files in the include directory
    location ~ ^/include {
            deny all;
            return 403;
   }

# Deny access to apache .ht* files (nginx doesn't use these)
    location ~ /\.ht {
            deny all;
   }

# Requests to /api/* need their PATH_INFO set, this does that
    if ($request_uri ~ "^/api(/[^\?]+)") {
            set $path_info $1;
   }

   # /api/*.* should be handled by /api/http.php if the requested file does not exist
    location ~ ^/api/(tickets|tasks)(.*)$ {
            try_files $uri $uri/ /api/http.php;
   }

# /scp/ajax.php needs PATH_INFO too, possibly more files need it hence the .*\.php
    if ($request_uri ~ "^/scp/.*\.php(/[^\?]+)") {
            set $path_info $1;
   }

   # Make sure requests to /scp/ajax.php/some/path get handled by ajax.php
   location ~ ^/scp/ajax.php/(.*)$ {
            try_files $uri $uri/ /scp/ajax.php;
   }

# Set index.php as our directoryindex
    location / {
            index index.php;
   }

# Send php files off to the PHP-FPM listing on localhost:9000
location ~ \.php$ {
    try_files $uri =404;
    #fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass 127.0.0.1:9000;
       fastcgi_index index.php;
    fastcgi_param   SCRIPT_FILENAME         $document_root$fastcgi_script_name;
          fastcgi_param   PATH_INFO               $path_info;
          include fastcgi_params;
}   
gzip on;
gzip_comp_level 9;

# static file 404's aren't logged and expires header is set to maximum age
location ~* \.(jpg|jpeg|gif|css|png|js|ico|html)$ {
    access_log off;
    expires max;
}

location ~ /(\.ht|\.git|\.svn) {
        deny all;
    }

}

still not working, the same error

38 remaining items

tomashastings

tomashastings commented on Dec 20, 2016

@tomashastings
ContributorAuthor
lepazca

lepazca commented on Dec 20, 2016

@lepazca

@infectormp @tomashastings I did the change in the fastcgi_params configuration file and delete the line fastcgi_param SCRIPT_FILENAME from the main one, trying to install now.

I keep getting the blank tooltip.

error.log
2016/12/20 13:34:51 [error] 63443#0: *4083 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: 192.168.20.4, server: support.example.com, request: "GET /setup/ajax.php/help/tips/install HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "support.example.com", referrer: "https://support.example.com/setup/install.php"

lepazca

lepazca commented on Dec 20, 2016

@lepazca

When I click the 'Install now' button almost instantly I get 502 BAD GATEWAY

error.log
2016/12/20 14:10:08 [error] 63590#0: *4436 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 192.168.20.4, server: support.example.com, request: "POST /setup/install.php HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "support.example.com", referrer: "https://support.example.com/setup/install.php"

infectormp

infectormp commented on Dec 20, 2016

@infectormp

@LeandroPC sorry my fault, i look to wrong conf. Please get back fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; to main conf and set in fastcgi_params fastcgi_param SCRIPT_NAME $fastcgi_script_name;

here is my

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

# httpoxy mitigation (https://httpoxy.org/ https://www.nginx.com/blog/?p=41962)
fastcgi_param  HTTP_PROXY         "";
lepazca

lepazca commented on Dec 20, 2016

@lepazca

@infectormp same outcome :(

/etc/nginx/sites-enabled/support

...
    location ~ \.php$ {
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params_ost;
            fastcgi_param  PATH_INFO        $path_info;
            fastcgi_pass        unix:/var/run/php5-fpm.sock;
    }

file /etc/nginx/fastcgi_params exactly like yours

infectormp

infectormp commented on Dec 20, 2016

@infectormp

@LeandroPC ok, check php-fpm pool run user = nginx run user = owner of /var/www/html/support.

lepazca

lepazca commented on Dec 20, 2016

@lepazca

/etc/php5/fpm/pool.d/www.conf

user = www-data
group = www-data

/etc/nginx/nginx.conf
user www-data www-data;

chown www-data.www-data /var/www/html/support -R
drwxr-xr-x 15 www-data www-data  4096 Dec 19 12:02 support

Folders have 755 and files 644

infectormp

infectormp commented on Dec 21, 2016

@infectormp

@LeandroPC sorry mate, not have any idea why this happens. All looks fine.

Raul-mz

Raul-mz commented on Jan 16, 2017

@Raul-mz

@LeandroPC Your problem is solved by modifying the method get_path_info in include/class.osticket.php.

function get_path_info() {
$request_uri = preg_replace('@?.*$@', '', $_SERVER['REQUEST_URI']);

if (strpos($request_uri, $_SERVER['SCRIPT_NAME']) !== false) {
    $guessed_pathinfo = preg_replace('#^'.preg_quote($_SERVER['SCRIPT_NAME']).'#', '', $request_uri);
} else {
    $guessed_pathinfo = preg_replace('#^'.preg_quote(preg_replace('@/([^/]+)$@', '', $_SERVER['SCRIPT_NAME'])).'#', '', $request_uri);
}    
if (!empty($guessed_pathinfo))
    return $guessed_pathinfo;

if(isset($_SERVER['PATH_INFO']))
return $_SERVER['PATH_INFO'];

if(isset($_SERVER['ORIG_PATH_INFO']))
    return $_SERVER['ORIG_PATH_INFO'];

return null;
}

nmindz

nmindz commented on Mar 8, 2017

@nmindz

I know I am replying on a topic that is almost 4 years old, but information here is still relevant today.

For those like me, who might have control of their web servers, and opt not to change ANY software code (that would eventually break in any future release or update, since it has not been merged into the project and might eventually cause all kinds of headaches), here's my experience with OSTicket and NGINX (with a reverse proxy too!).

If you feel more comfortable changing a server's configurations rather than OSTicket's internal functions, read on:

I am on OSTicket v1.9.15-git (latest commit 70898b3a9f984b500aa408c2bfe7b4ba26c1ce9c) and, since we also have a standard web server (and reverse proxy) of NGINX for all of our instances and applications, not surprisingly we had several problems with our setup.

However, with proper HTTP header settings and the configurations provided by @tomashastings , we were able to make OSTicket run properly and have all AJAX requests being properly forwarded.

Still following the security recommendations of using PHP's cgi.fix_pathinfo = 0 (having it set to 1 might be a problem, since that delegates to PHP the responsibility of "trying to find the best match", and a bad or maliciously crafted script could get executed in that way.), we were able to achieve such with the following:

server {
    listen   80;

    root "/var/www/osticket";

    index index.php index.html index.htm;

    server_name default_server;

    set $path_info "";

    # Deny access to all files in the include directory
    location ~ ^/include {
        deny all;
        return 403;
    }

    # Deny access to apache .ht* files (nginx doesn't use these)
    location ~ /\.ht {
        deny all;
    }

    # Requests to /api/* need their PATH_INFO set, this does that
    if ($request_uri ~ "^/api(/[^\?]+)") {
        set $path_info $1;
    }

    # /api/*.* should be handled by /api/http.php if the requested file does not exist
    location ~ ^/api/(tickets|tasks)(.*)$ {
        try_files $uri $uri/ /api/http.php;
    }

    # /scp/ajax.php needs PATH_INFO too, possibly more files need it hence the .*\.php
    if ($request_uri ~ "^/scp/.*\.php(/[^\?]+)") {
        set $path_info $1;
    }

    # Make sure requests to /scp/ajax.php/some/path get handled by ajax.php
    location ~ ^/scp/ajax.php/(.*)$ {
        try_files $uri $uri/ /scp/ajax.php;
    }

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

    location ~ \.php$ {
        try_files $uri =404;
        # fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass 10.127.46.10:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_param PATH_INFO $path_info;
    }

    location = /50x.html {
        root /var/www/html;
    }

    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;
}

I am providing my currently (so far) working dump of NGINX configurations for both the virtual host (serving OSTicket itself) and the reverse proxy corresponding part.

It is always good to remember however, that this is a dump of a running server. Therefore, it has very specific information, even though I have removed sensitive information and tried to replace hosts with more meaningful and clear examples.

Read it through and pay careful attention to comments such as:

# configuration file /etc/nginx/sites-enabled/osticket-domain:

Which should give you a hint and context on how to use those chunks of configuration.

(Again, thanks to @tomashastings and many others that donated time and effort both in support and in maintaining such a wonderful piece of open sourced software.)

==========================

PS: We are using apparently unnecessary variables such as $rootdir or $upstream but that's just so NGINX does not try to resolve those values while they still might not exist (we are running those instances inside Docker containers and we use docker-compose to manage them, therefore not necessarily every piece of the app will be up at the same time when NGINX would evaluate the host name, root directories and etc...)

mckaygerhard

mckaygerhard commented on Jan 1, 2018

@mckaygerhard

thanks @nmindz great work, at leas something made something, i already made some docs for install in hiawatta and lighty too due osticket have lack of right knowledge in real professional installation usage..

cheasingh

cheasingh commented on Aug 26, 2019

@cheasingh

I finally fixed the custom forms by adding the following two lines:

    if ($request_uri ~ "^/ajax.php(/[^\?]+)") {
        set $path_info $1;
    }

    location ~ ^/ajax.php/.*$ {
        try_files $uri $uri/ /ajax.php?$query_string;
    }

I hope that this will help other people that are struggling with this issue.

solved my problem,

Hatimtech

Hatimtech commented on Jun 29, 2020

@Hatimtech

@NCC-Lykos here is my fully working config

 server {
    listen 80;
    server_name xxx;

    access_log /var/log/nginx/xx.access_log;
    error_log /var/log/nginx/xx.error_log;

    return 301 https://$server_name$request_uri;  # enforce https
}

server {
    listen 443 ssl http2;
    server_name xxx;

    access_log /var/log/nginx/xxx.access.ssl_log;
    error_log /var/log/nginx/xxx.error.ssl_log;

    include /etc/nginx/default.d/ssl.conf;

    root /var/www/localhost/htdocs/xxx;

    set $path_info "";

    location ~ /include {
        deny all;
        return 403;
    }

    if ($request_uri ~ "^/api(/[^\?]+)") {
        set $path_info $1;
    }

    location ~ ^/api/(?:tickets|tasks).*$ {
        try_files $uri $uri/ /api/http.php?$query_string;
    }

    if ($request_uri ~ "^/scp/.*\.php(/[^\?]+)") {
        set $path_info $1;
    }

    location ~ ^/scp/ajax.php/.*$ {
        try_files $uri $uri/ /scp/ajax.php?$query_string;
    }

    location ~ ^/ajax.php/.*$ {
        try_files $uri $uri/ /ajax.php?$query_string;
    }

    location ~ ^/kb/ajax.php/.*$ {
        try_files $uri $uri/ /kb/ajax.php?$query_string;
    }

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

    location ~ \.php$ {
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
            fastcgi_param  PATH_INFO        $path_info;
            fastcgi_pass        unix:/run/php-fpm/www.sock;
    }
}

Worked Perfect. Thanks.

ghost
ghost
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @protich@deanet@danielbald@greezybacon@timwhite

        Issue actions

          Doesn't work on NGINX due to use of .htaccess files and relying on PATH_INFO · Issue #538 · osTicket/osTicket-1.7