Nginx authenticate against backend server with j_security_check - nginx

I need a reverse proxy for a backend Rest API to avoid CORS and debugging issues while developing a Svelte App. I've tried a few options like Node JS Express with http-proxy-middleware, Nginx, etc. The problem is that the backend JAX-RS Rest API requires j_security_check authentication. My modest network protocol skills run out trying to configure that in the proxy...
How would I make the following work:
Svelte App (http:5000) -> Proxy (http:3333) -> Rest API (http:8080 w/j_security_check)
So for example:
Svelte:
const ping = async () => {
const response = await fetch('http://localhost:3333/api/ping', {
method: 'GET',
headers: {
Accept: 'application/json',
},
});
return await response.json();
};
Nginx so far:
location /api/ {
add_header Access-Control-Allow-Origin "http://localhost:5000";
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH";
proxy_pass http://backend:8080/;
proxy_set_header Host $host;
}
I guess I'm missing a lot of header config from the above. If it's even possible for j_security_check.
Since this is local development proxy only, it would be optimal if the proxy could handle the authentication and leave those details hidden from the Svelte App.

Related

CORS configuration OK on the server but client thinks it's not

I've a client on a server (https://example.com) and an API on another server (https://api.myapis.com/thisapi)
When my client calls the API server I have this in my NGINX log:
XXX.XXX.XXX.XXX - - [03/Jan/2021:19:07:03 +0100] "OPTIONS /thisapi/particular/route/ HTTP/1.1" 200 13 "https://example.com/app/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0"
So I suppose it's OK for the NGINX server, which has this conf:
location /thisapi/ {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For$remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:1234/;
add_header 'Access-Control-Allow-Origin' 'https://example.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
}
Here is how I make the API call with axios:
axios.post(`${process.env.VUE_APP_API_URL}/particular/route/`, {
text: text.value,
},
{
headers: {
'Content-Type': 'application/json',
},
})
.then((response) => {
console.log(response.data);
link.value = `${process.env.VUE_APP_URL}${process.env.VUE_APP_PUBLICPATH}${response.data}`;
})
.catch((error) => {
console.log(error);
})
.then(() => {
loading.value = false;
});
return true;
}
And I have this in my Firefox console:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.myapis.com/thisapi/particular/route/. (Reason: Multiple CORS header ‘Access-Control-Allow-Origin’ not allowed).
I really don't see where I messed up... If anybody could give me a hand, it would be great. Thanks in advance.
I usually don't handle this in Nginx, but rather in the backend itself.
Try:
add_header 'Access-Control-Allow-Origin' '*' always;
If it now works like you expect it, that means 'https://example.com' is not correct.
(Btw you shouldn't use * in your code, it's just for demonstration purpose.)
Origin is consisted of 3 parts:
Protocol
https
http
Domain Name
(like www.example.com)
Port
Usually this is either 80 or 443
Make sure that the origin you are adding to the ‘Access-Control-Allow-Origin’ list, is exactly what it should be.
Check whether it is https or http.
EDITED:
Check the request and response headers.
The bluish parts should be the same.
If they are not the same for you, that means whatever you are doing, isn't working like it should be.
OK I understood what went wrong, I was mistaken by another answer here: https://stackoverflow.com/a/65532334/1098303
This experimented user told CORS was to lift restrictions, so that's what I did in my nodeJS app. I added another "Origin", like this:
res.setHeader('Access-Control-Allow-Origin', 'myothersource');
That is what made the error.
Now that I removed the header addition in my NodeJS app, everything is find, but I still wonder how you can "lift restrictions" in a NodeJS app compared to what is set on the nginx server, it seems impossible and you might have to choose between an nginx conf or a NodeJS one.

ASP.NET 4.5 Rest API's work in Unity Android/iOS build but fails with "Unknown error" in Unity WebGL build

I have scoured every possible forum for this and somehow have not gotten my WebGL to consume my ASP.NET 4.5 REST API's.
From what I can tell it is possibly related to WebGL requiring CORS, but even enabling this I cannot get the game to communicate with my API's
So either there's something wrong with the way I have implemented global CORS settings in ASP.NET or something else is breaking.
To be clear these API's are running perfectly well on Android/iOS/Windows builds and even in the editor.
What I have done so far:
Installed the Microsoft CORS build as recommended by Microsoft's documentation relating to it, then added the following code to the WebAPIConfig class in Visual Studio:
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
////new code
config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
This is also in my web.config:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
</customHeaders>
</httpProtocol>
I need these settings global so I used the "*" as indicated by the documentation to include all domains, method types, and headers because I use ASP.NET token authentication for my API.
Here is a code snippet that gets the token in the Unity project (just to be clear, this works on other platforms, only throws an error in a WebGL build)
public IEnumerator login()
{
string url = API.ROUTEPATH + API.TOKEN;
WWWForm form = new WWWForm();
form.AddField("grant_type", "password");
form.AddField("username", API.APIUSERNAME);
form.AddField("password", API.APIPASSWORD);
UnityWebRequest uwr = UnityWebRequest.Post(url, form);
uwr.SetRequestHeader("Content-Type", "application/json");
yield return uwr.SendWebRequest();
try
{
if (uwr.isNetworkError)
{
Debug.Log(uwr.error);
}
else
{
APIAuthToken returnauth = JsonUtility.FromJson<APIAuthToken>(uwr.downloadHandler.text);
if (!string.IsNullOrEmpty(returnauth.access_token))
{
API.hasAuth = true;
API.token = returnauth.access_token;
Debug.Log(returnauth.access_token);
}
}
}
catch
{
}
}
uwr.error produces the following, very helpful error: Unknown Error So I'm not even sure if it is CORS related, it's just my best guess based on the research I have done, but even with multiple different implementations of it I still sit with the same error. So if it's not a problem with the API's and with my Unity code please just ignore the ASP.NET code snippet.
cURL - A simple curl -I <endpoint> or curl -X OPTIONS -v <endpoint> can reveal a ton of information about what is happening related to CORS. It can allow you to set different origins, check preflight responses, and more.
"Let's say you have a backend API that uses cookies for session management. Your game works great when testing on your own domain, but breaks horribly once you host the files on Kongregate due to the fact that your API requests are now cross-domain and subject to strict CORS rules."
Is this your problem?
Problably on both sides if things are not set up properly will refuse to send cookies, but its good, its mean you have the control to allow what domains your sessions cookies will be sent to.
So probably you need first to configure the server to allow multiplies origins but make sure to validate the value against a whitelist so that you aren't just enabling your session cookies to be sent to any origin domain.
Example on a Node Express with CORS middleware(game ID 12345) and an origin whitelist below:
express = require('express')
var cors = require('cors')
var app = express()
var whitelist = ['https://game12345.konggames.com'];
var corsOptions = {
credentials: true,
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
};
app.use(cors(corsOptions));
app.options('*', cors(corsOptions)); // Enable options for preflight
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(8080, () => console.log(`Example app listening on port 8080!`))
cURL command to check the headers for an OPTIONS preflight request from an origin in the whitelist array:
curl -X OPTIONS -H"Origin: https://game12345.konggames.com" -v http://localhost:8080/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> OPTIONS / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Origin: https://game12345.konggames.com
>
< HTTP/1.1 204 No Content
< X-Powered-By: Express
< Access-Control-Allow-Origin: https://game12345.konggames.com
< Vary: Origin, Access-Control-Request-Headers
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
< Content-Length: 0
< Date: Tue, 24 Sep 2019 22:04:08 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
instruct the client to include cookies when it makes a cross-domain request,If the preflight response did not include Access-Control-Allow-Credentials: true, or if your Access-Control-Allow-Access is set to a wildcard (*) then the cookies will not be sent and you are likely to see errors in your browser's Javascript console:
Access to XMLHttpRequest at 'https://api.mygamebackend.com' from origin 'https://game54321.konggames.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Unity's UnityWebRequest and the older WWW classes use XMLHttpRequest under the hood to fetch data from remote servers. Since there is no option to set the withCredentials flag to true, we have to perform a pretty dirty hack when initializing our application in order to turn that on for the appropriate requests.
In your WebGL template or generated index.html:
<script>
XMLHttpRequest.prototype.originalOpen = XMLHttpRequest.prototype.open;
var newOpen = function(_, url) {
var original = this.originalOpen.apply(this, arguments);
if (url.indexOf('https://api.mygamebackend.com') === 0) {
this.withCredentials = true;
}
return original;
}
XMLHttpRequest.prototype.open = newOpen;
</script>
This snippet of code overrides the open method of XMLHttpRequest so that we can conditionally set withCredentials equal to true when desired. Once this is in place, cross-origin cookies should begin working between the Kongregate-hosted iframe domain and the game's backend servers!
info taken from here
also looks nice for this

Nginx - How can I create a custom request that will be used with the auth_request module

To start with: I am NOT an nginx expert. Very much a newbie to it.
I am attempting to protect a 3rd party piece of software with nginx doing the authentication (really - just verifying that the request has a valid OAuth2 Bearer token)
The HTTP request will have an OAuth2 bearer token in the Authentication header.
e.g. Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZ....H5w
I have an OAuth2 server (UAA) that has an api where I can call http://myuaa/check_token?token=eyJhbGciOiJSUzI1NiIsImtpZ....H5w to get back a 2XX or a 4XX if the token is valid. A complication is that this server does require basic auth to call the /check_token endpoint.
I have tried using a map to parse the token from the authorization header, but with no luck.
Just kind of at a loss.
Perhaps this isn't a good fit for Nginx?
relevant pieces of the nginx.conf
# this map isnt working as I thought it might
http {
...
map $http_authorization $token {
~Bearer(?<token>abc) $token;
}
...
# test just to see if the authorization header is being parsed and passed - no luck
location /oauth {
proxy_set_header X-my-header $token;
proxy_set_header X-another-header value;
proxy_set_header Authorization "Basic basdasdfasdf";
proxy_pass http://localhost:8080;
}
Expected request to the 3rd party server that nginx is protecting:
<GET|POST|PUT|DELETE> /anyurl HTTP1/1.1
..
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZ....H5w
..
Expected request forwarded to the UAA server to validate token
GET /check_token?token=eyJhbGciOiJSUzI1NiIsImtpZ....H5w
..
Authorization Basic asfasdfdf
..
Your map directive isn't working, named group token somehow interfere with the $token variable, any of these definitions would work:
map $http_authorization $token {
~^Bearer\s+([\S]+)$ $1;
}
or
map $http_authorization $token {
~^Bearer\s+(?<bearer>[\S]+)$ $bearer;
}
Full working config will be looking like this:
map $http_authorization $token {
~^Bearer\s+(?<bearer>[\S]+)$ $bearer;
}
server {
...
location / {
auth_request /uaa;
...
}
location /uaa {
internal;
proxy_pass_request_body off;
proxy_set_header Authorization "Basic your_base64_auth_string";
proxy_set_header Content-Length "";
proxy_pass http://localhost:8080/check_token?token=$token;
}
}

Fetch information from an API before sending the request upstream

Is it possible to send a http subrequest in a location block and use the response in the proxy_pass directive?
use case
My upstream application needs some additional information from an API.
I've written a location block that proxies request with the proxy_pass directive.
Before nginx sends the request to my application. I'd like to send an HTTP request to my API and use several response headers
as request headers to my application.
This is the outline of what I want to achieve:
server {
server_name ...;
location {
# perform subrequest to fetch additional information from an api
proxy_pass myapplication;
proxy_set_header X-Additional-Info "some information from the subrequest";
}
}
The behaviour is similar to the auth_request module. However, I can't find documentation of sending an additional blocking HTTP request before inside a location block using standard nginx configuration.
You can't do it using regular nginx directives but it's quite easy using lua-nginx-module.
This module embeds Lua, via the standard Lua 5.1 interpreter or LuaJIT
2.0/2.1, into Nginx and by leveraging Nginx's subrequests, allows the integration of the powerful Lua threads (Lua coroutines) into the
Nginx event model.
Here's how to accomplish what you need:
create a directory conf.d/
put 2 files test.conf and header.lua into it (see the contents below)
docker run -p8080:8080 -v your_path/conf.d:/etc/nginx/conf.d openresty/openresty:alpine
curl http://localhost:8080/
test.conf
server {
listen 8080;
location /fetch_api {
# this is a service echoing your IP address
proxy_pass http://api.ipify.org/;
}
location / {
set $api_result "";
access_by_lua_file /etc/nginx/conf.d/header.lua;
proxy_set_header X-Additional-Info $api_result;
# this service just prints out your request headers
proxy_pass http://scooterlabs.com/echo;
}
}
header.lua
local res = ngx.location.capture('/fetch_api', { method = ngx.HTTP_GET, args = {} });
ngx.log(ngx.ERR, res.status);
if res.status == ngx.HTTP_OK then
ngx.var.api_result = res.body;
else
ngx.exit(403);
end
results
curl http://localhost:8080/
Simple webservice echo test: make a request to this endpoint to return the HTTP request parameters and headers. Results available in plain text, JSON, or XML formats. See http://www.cantoni.org/2012/01/08/simple-webservice-echo-test for more details, or https://github.com/bcantoni/echotest for source code.
Array
(
[method] => GET
[headers] => Array
(
[X-Additional-Info] => my-ip-address
[Host] => scooterlabs.com
[Connection] => close
[User-Agent] => curl/7.43.0
[Accept] => */*
)
[request] => Array
(
)
[client_ip] => my-ip-address
[time_utc] => 2018-01-23T19:25:56+0000
[info] => Echo service from Scooterlabs (http://www.scooterlabs.com)
)
Notice the X-Additional-Info header populated with the data obtained in /fetch_api handler

How should I configure Nginx to proxy to a URL passed by parameter?

I'm trying to get access to media files (images, videos) sitting behind an OAuth2 authentication.
In order to access the resource I need to add a custom Authorization Bearer token to the request, so I can't use a simple rewrite (well, as far as I know at least).
It cannot be done via plain HTML (say img or video tag) so I'm considering to have Nginx proxying the queries to the final server.
Each of the media resources would be loaded via a /proxy path, with a token parameter (for authentication) and url for the actual resource to load.
Sample URL:
http://myserver.com/proxy/?token=12345&url=http://protectedserver.com/custompath/asset
This is what I came up with but I am not quite sure how to configure the proxy_pass directive since I need it to proxy to the $url variable specifically. I do not need to proxy the path (which would be empty anyway).
location /proxy/ {
if ($arg_token ~ "^$") { return 404; }
if ($arg_url ~ "^$") { return 404; }
set $url $arg_url;
proxy_set_header Authorization "Bearer $arg_token";
set $args "";
#proxy_pass $url;
}
Note: this will be run in a closed environment and only specific machines (kiosks with limited interaction) will be able to access the page so I'm not concerned about a potential leak of the auth token.
I noticed a similar question on ServerFault, but no one had an answer to that:
https://serverfault.com/questions/671991/nginx-proxy-pass-url-from-get-argument
I'm looking for a config setting to make it work or a viable alternative solution.
Here is a correct configuration for my problem:
location /proxy/ {
if ($arg_token ~ "^$") { return 404; }
if ($arg_url ~ "^$") { return 404; }
set $url $arg_url;
set $token $arg_token;
set $args "";
# IMPORTANT, this is required when using dynamic proxy pass
# You can alternatively use any DNS resolver under your control
resolver 8.8.8.8;
proxy_pass $url;
proxy_set_header Authorization "Bearer $token";
proxy_redirect off;
}

Resources