Nginx - custom login page - nginx

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"

Related

Intercept all nginx requests to serve series of static content?

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.

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.

NGINX keep URL at /location/some-path and serve /location/?

Is this possible?
User enters https://mywebsite.com/location/any-path
Nginx runs https://mywebsite.com/location/
URL displays https://mywebsite.com/location/any-path
Is there a particular name for this type of action also?
You can use nginx redirection here -
location = /location/any-path {
return 301 /location;
}
You want to proxy_pass the request to another url and then proxy_redirect to alter the url displayed to the client.
Something like this:
location ~ /location/(.+) {
resolver 8.8.8.8;
proxy_pass https://mywebsite.com/location/index.php;
proxy_redirect /location/ /location/$1/;
}

How to process response from www-authenticate in nginx?

I'm using openresty nginx v1.11.2.4. I wish to be able to authenticate users before they are given access to a resource or before they try to PUT something on the server. I am using the http_auth_request_module and the following is an except from my nginx.conf file:
location /video/ {
auth_request /auth;
root /usr/local/openresty/nginx/html;
}
location = /auth {
more_set_headers "WWW-Authenticate: Basic";
return 401;
}
This results in the browser asking for user credentials alright but now how do I get/process the user credentials from the client?
The ngx_http_auth_request_module implements client authorization based on the result of a subrequest.
If you want to use basic authentication you don't need to use ngx_http_auth_request_module. Use http://nginx.org/en/docs/http/ngx_http_auth_basic_module.html
following the answer to the question here: Nginx authentication with auth_request module
I was able to process the username and password by accessing the $http_authorization variable in my nginx.conf file. The following is an excerpt from my nginx.conf:
location /video {
satisfy any;
auth_basic "Private Property";
auth_basic_user_file /usr/local/openresty/nginx/conf/htpasswd;
auth_request /auth;
root /usr/local/openresty/nginx/html;
client_max_body_size 1000M;
if ($request_method != "GET"){
content_by_lua_file /root/Documents/contentbylua.lua;
}
}
location = /auth {
set $authHeader $http_authorization;
set $authUName $remote_user;
content_by_lua_file /root/Documents/restrict.lua;
}
The following conf allows me to authenticate a user whose credentials are stored in a redisDB in the restrict.lua file which returns a 200 or 401 code depending on the credentials of the user back to the /location block.
The response (username & password) is accessed in the restrict.lua file by ngx.var.authHeader. Some string processing is done to remove the 'Basic' then the remnant is base64 decoded and then some string processing is done on it to obtain the password. That's all

How to disable logging images in nginx but still allow the get request?

I'm trying to log only java-script files request in the nginx access_log.
I tried using the following code i found on this site:
location ~* ^.+.(jpg|jpeg|gif|css|png|html|htm|ico|xml|svg)$ {
access_log off;
}
the problem is it doesn't allow the get request at all and i get a 404 error when trying to run the html file that executes the js file in the browse.
I want everything to work just the same but for the access log to log only request for js files.
How do i do that?
Put it in the server block and make sure that the "root" is correctly set up. It does work
Working example:
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires +60d;
access_log off;
}
I have this in the server block and not a location block.
Alternatively you can keep all requests within single location but use access_log with condidional if operator to disable images logging:
map $request_uri $is_loggable {
~* ^.+\.(jpg|jpeg|gif|css|png|html|htm|ico|xml|svg)$ 0;
default 1;
}
server {
location / {
access_log /path/to/log/file combined if=$is_loggable;
...
}
}
Here combined is a name of default log format.
You say that you want to log only java-script files, so actually you can use even simplier solution:
map $request_uri $is_loggable {
~* ^.+\.js$ 1;
default 0;
}

Resources