Intercept all nginx requests to serve series of static content? - nginx

Question: Is there a way to 'intercept' more specific locations? Before many locations (e.g., location /activity, location = /search, location ~ "^/content/([a-f\d]{24})/$", etc.), I would like to do potentially serve html and then handle the original request.
Background: An nginx.conf I'm working with serves up a series of html pages (e.g., international agreement page, location page, supplier welcome page, etc.) following user SSO login and the user attempting to access a URL of our product.
The current nginx configuration works by adding a try_files $uri /<location that could serve .html page>?redirecturi=$redirecturi; to many location blocks. This is cumbersome because numerous locations point to the same location (e.g., /landing as seen below); it would be more maintainable to have a single location as an entry point to this 'flow' of html pages.
Example location block that refers to a location (e.g., /landing) that can server html
location /workitem {
...
auth_request /auth;
...
recursive_error_pages on;
try_files $uri /landing?redirecturi=$redirecturi;
}
Example location that can serve html
location = /landing {
internal;
...
proxy_method GET;
proxy_pass <scheme>://<host>/api/internationalAgreement?redirecturi=$redirecturi;
recursive_error_pages on;
error_page 378 =200 /internationalAgreement.html;
error_page 395 =200 /supplier?redirecturi=$redirecturi;
}
On the backend, we expose APIs to that update the user's session when they've seen a page:
router.get('/internationalAgreement',
(req, res) => {
const redirectUri = req.query.redirecturi;
if (!redirectUri) {
return res.status(403).end();
}
// International Agreement not posted yet
if (req.session.showContentLandingPage === 'true') {
return res.status(378).end();
}
...
});
Pending response code, the location block either serves the .html page or routes to a different block that might serve html (e.g., /supplier as referenced above in /landing). When the user clicks 'Next' on the html page, an HTTP request is made, the backend updates the user's session with the page they saw, then the request if 'forwarded' to another block that could serve html.
Example location arrived when user clicks next
location = /api/ge/itc {
limit_except POST { deny all; }
...
proxy_pass <scheme>://<host>/api/internationalAgreement?redirecturi=$arg_ouri;
recursive_error_pages on;
error_page 379 =200 /userLocation?redirecturi=$upstream_http_x_mbe_redirect;
}
Eventually, these locations just pass through requests after the users has stepped through the required pages.
I've tried searching for examples of locations that match all locations but generally can only find the '/' would match as a catch all and not intercept more specific locations.

Related

Extension realurl - display of SQL query parts in URL

First of all, I have to deal with a project-related upgrade of a TYPO3 version and am also relatively new to the Nginx web server.
I have upgraded a TYPO3 installation from version 7.6-LTS to 8.7-LTS and transferred the site from an IIS (Windows) server to an Ubuntu 18.04 system with Nginx.
I have now discovered the following: the first click on an internal link on the website in the menu e.g. domain.com/prices works correctly. The URL domain.com/prices is called and also shown in the URL. Now when the page has been reloaded, the same menu item link now looks like this...
domain.com/index.php?id=8&L=1%20or%20%281%2C2%29%3D%28select%2Afrom%28select%20name_const%28CHAR%28111%2C108%2C111%2C108%2C111%2C115%2C104%2C101%2C114%29%2C1%29%2Cname_const%28CHAR%28111%2C108%2C111%2C108%2C111%2C108%2C111%2C115%2C104%2C101%2C114%29%2C1%29%29a%29%20- -%20and%201%3D1
...instead of the usual domain.com/prices. The oontent of the page is still correctly, so the correct page is being loaded.
The Typo3 installation uses the extension real url, which is exactly for this feature. It translates domain.com/index.php?id=8&L=1... into a speaking url like domain.com/prices. So why is it working on the first load, but then no longer?
I also thought, it could be an issue with the configuration of Nginx, but now I think it is a different topic here, because if it is a webserver issue, it won't work the first time, will it?
What can cause this behaviour?
Update
I actually saw, that the English version of the website is working well. So here, the links are displayed correctly: domain.com/prices, domain.com/modules and not even not first page load.
Maybe it is helpful. This is the Nginx config, I currently use:
server {
listen 80;
server_name domain.com;
root /var/www/domain.com/public;
index index.php index.html index.htm index.nginx-debian.html;
listen 443 ssl;
ssl_certificate /var/www/domain.com/ssl/domain.com-2020.crt;
ssl_certificate_key /var/www/domain.com/ssl/domain.com-2020.rsa;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Special root site case. prevent "try_files $uri/" + "index" from skipping the cache
# by accessing /index.php directly
location =/ {
recursive_error_pages on;
error_page 405 = #sfc;
return 405;
}
location #t3frontend {
# Using try_files for ease of configuration demonstration here,
# you can also fastcgi_pass directly to php here
try_files $uri /index.php$is_args$args;
}
location #sfc {
# Perform an internal redirect to TYPO3 if any of the required
# conditions for StaticFileCache don't match
error_page 405 = #t3frontend;
# Query String needs to be empty
if ($args != '') {
return 405;
}
# We can't serve static files for logged-in BE/FE users
if ($cookie_staticfilecache = 'fe_typo_user_logged_in') {
return 405;
}
if ($cookie_be_typo_user != '') {
return 405;
}
# Ensure we redirect to TYPO3 for non GET/HEAD requests
if ($request_method !~ ^(GET|HEAD)$ ) {
return 405;
}
charset utf-8;
try_files /typo3temp/tx_staticfilecache/${scheme}/${host}/${server_port}${uri}/index.html
/typo3temp/tx_staticfilecache/${scheme}/${host}/${server_port}${uri}
=405;
}
location /typo3temp/tx_staticfilecache {
deny all;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
location ~ /\.ht {
deny all;
}
# Prevent clients from accessing hidden files (starting with a dot)
# This is particularly important if you store .htpasswd files in the site hierarchy
# Access to `/.well-known/` is allowed.
# https://www.mnot.net/blog/2010/04/07/well-known
# https://tools.ietf.org/html/rfc5785
location ~* /\.(?!well-known\/) {
deny all;
}
# Prevent clients from accessing to backup/config/source files
location ~* (?:\.(?:bak|conf|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$ {
deny all;
}
# TYPO3 - Block access to composer files
location ~* composer\.(?:json|lock) {
deny all;
}
# TYPO3 - Block access to flexform files
location ~* flexform[^.]*\.xml {
deny all;
}
# TYPO3 - Block access to language files
location ~* locallang[^.]*\.(?:xml|xlf)$ {
deny all;
}
# TYPO3 - Block access to static typoscript files
location ~* ext_conf_template\.txt|ext_typoscript_constants\.(?:txt|typoscript)|ext_typoscript_setup\.(?:txt|typoscript) {
deny all;
}
# TYPO3 - Block access to miscellaneous protected files
location ~* /.*\.(?:bak|co?nf|cfg|ya?ml|ts|typoscript|dist|fla|in[ci]|log|sh|sql)$ {
deny all;
}
# TYPO3 - Block access to recycler and temporary directories
location ~ _(?:recycler|temp)_/ {
deny all;
}
# TYPO3 - Block access to configuration files stored in fileadmin
location ~ fileadmin/(?:templates)/.*\.(?:txt|ts|typoscript)$ {
deny all;
}
# TYPO3 - Block access to libaries, source and temporary compiled data
location ~ ^(?:vendor|typo3_src|typo3temp/var) {
deny all;
}
# TYPO3 - Block access to protected extension directories
location ~ (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|Resources/Private|Tests?|Documentation|docs?)/ {
deny all;
}
}
server {
if ($host = domain.com) {
return 301 https://$host$request_uri;
}
listen 80;
server_name domain.com;
return 404;
}
And this is the config of realurl:
The template analysis does not show any error. This is the template analysis for realurl:
Update 2
I'm a step further in this issue. I've read the bug report https://github.com/dmitryd/typo3-realurl/issues/333 which is similar to my issue. I tried to switch the config.linkVars value from L to L(0,1). That helped in the URL issue. But now it switches always back to German language.
With the setting L in config.linkVars, it added the language in the url, if it was not German:
domain.com/en/prices
With the new value L(0,1) it removes the language in the URL
domain.com/prices
So it always shows the German version of the page. I even tried to set the value to L(0,2), because of German, English and French language is integrated (1=English, 2=French), but it does not help in this case. It switches always back to German because of the missing language code in the URL.
Ok. Now I found the solution.
As I wrote in the comments it was the config setting:
config.linkVars
which was set initially to
config.linkVars = L
After I found this bug report
https://github.com/dmitryd/typo3-realurl/issues/333
I saw that I need to limit the L-value for security reasons.
In our case 0 for german, 1 for english, 2 for french:
config.linkVars = L(0-2)
Then all worked fine.

How can I serve a particular file for root url in Nginx?

I need to serve file from my filesystem for GET / request. I tried the following:
location = / {
index page2257160.html;
root /var/www/site/;
}
The rest of requests are proxied to backend:
location / {
proxy_pass http://localhost:1234;
}
But when I do the request, instead of serving the file from filesystem, nginx asks backend about /page2257160.html, backend returns 404, nginx sends this 404 back to client.
How can I fix this?
The index directive performs an internal redirect, so you will need a second location to match the rewritten URI. For example:
root /var/www/site/;
location = / {
index page2257160.html;
}
location = /page2257160.html {
}
See this document for details.
You can achieve the same thing with one location block and a try_files directive. For example:
location = / {
root /var/www/site/;
try_files /page2257160.html =404;
}
See this document for more.

Nginx Locations and Connection Permissions

I'm suffering a brute force attack, I need to look the access log and get the routes to block:
location /foo/{ deny all;}
location /foo2/{deny all;}
Since there are infinite routes I would need to spend a good time looking all the access log to block connections at those specific routes.
Instead of denying specific routes could I configure nginx to block connections on all other routes that don't exists in my application?
[]'s
Pedro
Make a list of first level folders and then you can block access to all not of them with a access required
events {
worker_connections 1024;
}
http {
map $request_uri $allowed_routes {
~^/(css|assets|js)/ "off";
/ "off";
default "Not allowed";
}
server {
auth_basic $allowed_routes;
auth_basic_user_file /var/www/html/.htpasswd;
location / {
echo "You can reach API $uri and $args, $arg_hash_folder";
}
}
}
You might consider other approaches also, but this one works well for me

Nginx - custom login page

I'm trying to secure my webapp by using nginx base authentication.
What I'm looking for is a way to force the browser to show my custom html login page instead of the default login popup but still handle the authorization process.
I try to omit the 'WWW-Authenticate' header and the popup wasn't display but I've no idea how to force the browser to add the 'Authorization' header for each request.
hereby nginx.conf:
location /{
auth_basic "Restricted";
auth_basic_user_file htpasswd;
proxy_pass http://tomcat:8080/;
error_page 401 /login.html;
}
location = /login.html {
root html;
more_clear_headers 'WWW-Authenticate';
}
You need to add proxy_intercept_errors on; or you will not be able to define error_page. Otherwise NGINX just passes the HTTP 401 response back to the client.
location /{
auth_basic "Restricted";
auth_basic_user_file htpasswd;
proxy_pass http://tomcat:8080/;
proxy_intercept_errors on;
error_page 401 /login.html;
}
location = /login.html {
root html;
more_clear_headers 'WWW-Authenticate';
}
Syntax: proxy_intercept_errors on | off;
Default: proxy_intercept_errors off;
Context: http, server, location
Determines whether proxied responses with codes greater than or equal to 300 should be passed to a client or be intercepted and redirected to nginx for processing with the error_page directive.
*has* to be a better way to accomplish this, but i managed to do it with X-Accel-Redirect and php, here's how:
i wanted to have a custom login page for folder /foo/ (and recursively all its content)
... first i renamed the on-disk folder /var/www/html/foo to /var/www/html/internal_foo , then i added to nginx config:
location ~ /internal_foo {
internal;
# even if this is the default root, the internal-directive seems to prevent inheriting the root directive, so have to explicitly specify it here...
root /var/www/html;
}
location ~ /foo {
try_files "" /foo.php$is_args$args;
}
then i created a /var/www/html/foo.php with the contents:
<?php
// warning: script vulnerable to timing attack which can disclose existence of files. (realpath() is not constant-time)
if(is_authorised()){
$translated="/internal_foo/".urldecode(substr($_SERVER['REQUEST_URI'],strlen("/foo/")));
$file=__DIR__.$translated;
$file=realpath($file);
if(false===$file){http_response_code(404);exit();}
if(0!==strpos($file,__DIR__.DIRECTORY_SEPARATOR."internal_foo")){
// probably a hacker attempting ../../../etc/passwd schenanigans.
http_response_code(400);
exit();
}
header("X-accel-redirect: ".$translated);
}else{
show_loginpage();
}
and voila, if a user is authorized, the request is sent to nginx, otherwise show_loginpage(); is executed (where you can put your custom login page), mission accomplished :)
If you really want to do this. The easiest way is to put the username and password into the url. Basic Authentication supports this. Change all requests to the format below and it will work:
"http://" + username + ":" + password + "#example.com"

How to redirect invalid url to a valid url in nginx?

I have a valid url of the type http://example.com/valid/. Using nginx how do i redirect a url of type http://example.com/valid/dsdhshd to my valid url?
I tried:
location /valid/ {
resolver 8.8.8.8;
proxy_pass http://example.com/valid/;
proxy_redirect off;
}
But it gives a 500 internal server error.
I also tried location return 301 $scheme://example.com/valid/; but this just put me in an infinite redirection loop.
If you want to send the redirect to the client, don't proxy the request and simply send it.
server {
# Your server configuration ...
# Enclose regular expressions in default location.
location / {
location /valid {
location ~ /valid/.+ {
try_files $uri #invalid;
}
# Handle the request to the valid URL ...
}
}
location #invalid {
return 301 $scheme://$server_name/valid;
}
}
That should do the trick. You did get a redirect loop because your location block also matched the /valid/ URL itself, something you don't wanted to match. You only want to match URLs which have something after that string, e.g. /valid/foo. That is exactly what the regular expression in the location block above is ensuring.
here's a sample i could think about
server {
location /valid {
try_files $uri $uri/ #redirect_invalid;
}
location #redirect_invalid {
return 301 $scheme://$server_name/valid;
}
}
But this will be very specific and if there's many folders you'll need to add each separately, I can't think of a method to make this generic for all folders, maybe someone else could help me with this.

Resources