osTicket / osTicket-1.7

osTicket-1.7
GNU General Public License v2.0
232 stars 178 forks source link

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

Open tomashastings opened 11 years ago

tomashastings commented 11 years ago

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

infectormp commented 7 years ago

@leandropc ok, checkphp-fpm pool run user = nginx run user = owner of /var/www/html/support.

lepazca commented 7 years ago

/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 commented 7 years ago

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

Raul-mz commented 7 years ago

@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 commented 7 years ago

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 commented 6 years ago

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 commented 5 years ago

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 commented 4 years ago

@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.

rlmuthukumar commented 3 years ago

Hi Guys, Kindly help me... how the nginx config for docker container. Let me know what will be the root path.

Thanks.

rlmuthukumar commented 3 years ago

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;
    }

Hi Tried in the below files location.

./public/include/class.osticket.php ./include/class.osticket.php

after changing also getting same popup message... Kindly help me for fix... my osticket runs as container Thanks.