Dotnet core identityserver4 reverse proxy - nginx

Hi everybody I have a problem with my reverse proxy configuration for my dotnet core application. My problem is that when I am using identityserver discovery endpoint the port number is missing from the end of my url.
I have a request for the discorvery document to
https://:8421/.well-known/openid-configuration
and the response is the following and the problem here is that the clients of the authentication service are using the document as a base for verification, using jwks_uri property from the response, the url listed in that property has the missing port number, so the clients cant call the openId configuration.
{
"issuer": "https://<servername>",
"jwks_uri": "https://<servername>/.well-known/openid-configuration/jwks",
"authorization_endpoint": "https://<servername>/connect/authorize",
"token_endpoint": "https://<servername>/connect/token",
"userinfo_endpoint": "https://<servername>/connect/userinfo",
"end_session_endpoint": "https://<servername>/connect/endsession",
"check_session_iframe": "https://<servername>/connect/checksession",
"revocation_endpoint": "https://<servername>/connect/revocation",
"introspection_endpoint": "https://<servername>/connect/introspect",
"device_authorization_endpoint": "https://<servername>/connect/deviceauthorization"
}
expected result for endpoint config:
{
"jwks_uri": "https://<servername>:<port>/.well-known/openid-configuration/jwks",
}
In my dotnet app I setup the reverse proxy settings
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedHost |
ForwardedHeaders.XForwardedProto;
options.ForwardLimit = 2; //Limit number of proxy hops trusted
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
app.UseForwardedHeaders();
nginx config is the following
server {
listen <ip>:8421 ssl;
server_name _;
ssl_certificate <certPath> ;
ssl_certificate_key <certPath>;
location / {
proxy_pass http://127.0.0.1:8421;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 8421;
}
}
Before updateing to Identityserver 4 4.0 it had the Public origin property but it became deprecated with 4.0. Now instead it is using the dotnet baseurl.
In the http pipeline I also logged all the headers from the request and got this result:
2020-07-03 11:40:15.109 +00:00;[INF];Cache-Control--no-cache Connection--upgrade Accept--*/* Accept-Encoding--gzip, deflate, br Host--<hostName> User-Agent--PostmanRuntime/7.26.1 X-Real-IP--<ip> X-Original-Proto--http X-Forwarded-Host--<domainName with port> X-Forwarded-Port--8421 Postman-Token--44dc6573-71eb-4b36-8b2a-9768d71e5b64 X-Original-For--<ip address> ;;

I had to add the port also to the nginx configuration. Setting the X-Forwarded-Host to $host was not enough also had to add the $proxy_port as well
The working confiugration for proxy
proxy_set_header X-Forwarded-Host $host:$proxy_port;

If the host is listening on non-default ports (not 80 for http and not 443 for https), use $http_host instead of $host to set the Host as shown below.
Change
change proxy_set_header Host $host;
to
proxy_set_header Host $http_host;

Related

wso2 api manager 3.2.0 nginx load balancing this site can’t be reached error?

I want to use loadbalancing for wso2 api manager 3.2.0 using Nginx. when call https://localhsot:443 in nginx server,
it redirects to https://api.am.wso2.com/publisher, but can not reach this site error occurs.
could you please me guide, what is wrong?
Nginx config:
user nginx;
worker_processes auto;
events {
worker_connections 1024;
}
http {
upstream sslapi.am.wso2.com {
server 172.24.64.114:9443;
server 172.24.64.114:9443;
}
upstream sslgw.am.wso2.com {
server 172.24.64.114:8243;
server 172.24.64.114:8243;
}
server {
listen 80;
server_name api.am.wso2.com;
rewrite ^/(.*) https://api.am.wso2.com/$1 permanent;
}
server {
listen 443 ssl;
server_name api.am.wso2.com;
proxy_set_header X-Forwarded-Port 443;
ssl_certificate /etc/nginx/ssl/apimanager.crt;
ssl_certificate_key /etc/nginx/ssl/apimanager.key;
location / {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
proxy_pass https://sslapi.am.wso2.com;
}
}
server {
listen 443 ssl;
server_name gw.am.wso2.com;
proxy_set_header X-Forwarded-Port 443;
ssl_certificate /etc/nginx/ssl/apimanager.crt;
ssl_certificate_key /etc/nginx/ssl/apimanager.key;
location / {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
proxy_pass https://sslgw.am.wso2.com;
}
}
}
and deployment.toml config in server(172.24.64.114):
[transport.https.properties]
proxyPort = 443
[server]
hostname = "api.am.wso2.com"
node_ip = "172.24.64.114"
#offset=0
mode = "single" #single or ha
base_path = "${carbon.protocol}://${carbon.host}:${carbon.management.port}"
#discard_empty_caches = false
server_role = "default"
and hosts config in (172.16.11.239) server:
172.0.0.1 localhost
172.24.64.114 api.am.wso2.com
and hosts config in (172.24.64.114) server:
172.24.64.114 api.am.wso2.com
After invoke nginx url (172.24.64.116) it redirects to 172.24.64.114 that is site is not reachable!
When you configure the API Manager with Proxy Port configurations, it is required to specify a Hostname as well. The same Hostname needs to be configured in the Nginx under server configurations. Further, under upstream, you have to configure the IP address of the API Manager nodes to direct the requests.
Since you are having a dedicated Nginx server (.116) in the middle, configure the Nginx server's IP address (.116) and the Hostname of the API Manager (api.am.wso2.com) in the Client node's (.239) Hosts entry. This will make sure that when you type the Hostname: api.am.wso2.com in the Client's node, the request will be dispatched to the Nginx server and then the Nginx will make the communication with the Upstream servers that have been configured.
Try out configuring the Hosts entries correctly in the Client's node and verify the behavior. A sample entry in the Client's Hosts will be as following
172.24.64.116 api.am.wso2.com

Daphne websocket erro r 200 when used with nginx

im trying to implement a system that comprise of nginx , daphne and gunicorn. So far , i have gotten gunicorn to work , however im facing an issue connecting the websockets for daphne. This issue does not arise when im in developing mode , only when i utilize nginx. This is the error code from my console :
reconnecting-websocket.js:199 WebSocket connection to 'ws://192.168.8.31/' failed: Error during WebSocket handshake: Unexpected response code: 200
This is my nginx config file :
upstream crm_server { server unix:/home/user/project/venv/run/gunicorn.sock fail_timeout=0;
}
upstream channels-backend {
server localhost:8001;
}
server {
listen 80;
# add here the ip address of your server
# or a domain pointing to that ip (like example.com or www.example.com)
server_name 192.168.8.31;
keepalive_timeout 5;
client_max_body_size 4G;
access_log /home/user/project/venv/logs/nginx-access.log;
error_log /home/user/project/venv/logs/nginx-error.log;
location /staticfiles/ {
root /home/user/roject/crm/staticfiles/;
}
# checks for static file, if not found proxy to app
location / {
try_files $uri #proxy_to_app; }
location /ws/ {
try_files $uri #proxy_to_ws;
}
location #proxy_to_ws {
include proxy_params;
proxy_pass http://channels-backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location #proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_buffering off;
proxy_redirect off;
proxy_pass http://crm_server;
}
This is my front end thats generating the socket
<script type="text/javascript">
var loc = window.location
var wsStart = 'ws://'
if (loc.protocol == 'https'){
wsStart = 'wss://' } var endpoint = wsStart + loc.host + loc.pathname var socket = new ReconnectingWebSocket(endpoint)
This is my routing:
from channels.routing import ProtocolTypeRouter , URLRouter
from django.urls import path
from rnd.consumers import NotificationConsumer
from django.conf.urls import url
from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator, OriginValidator
application = ProtocolTypeRouter({
"websocket": AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter(
[
path('',NotificationConsumer),
] ) )
) })
I would greatly appreciate any form of feed back!
When you proxy pass to open a Web-socket connection you need to proxy pass all of the web-socket http headers. https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism#WebSocket-specific_headers you will also need to pass the Origin header since you are using AllowedHostsOriginValidator.
--
also to debug these I suggest using a api client directly rather than the browser. Paw or Insomnia can be used to hit api to check that your Nginx config is routing to channels and not to Gunicorn.

get Client IP in a flask based application

I have Flask application deployed in server. and we are using Nginx. nginx setup is as below:
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_read_timeout 25s;
proxy_pass http://127.0.0.1:8000;
add_header X-Cache $upstream_cache_status;
In Flask setup I have done the following :
app = Flask(__name__, static_folder=None)
app.wsgi_app = ProxyFix(app.wsgi_app)
Now, whenever user visits a site, I want a real ip. Currently I am getting
127.0.0.1
I have tried like below:
if request.headers.getlist("X-Forwarded-For"):
ip = request.environ['HTTP_X_FORWARDED_FOR']
else:
ip = request.remote_addr
Could anybody guide me here please.
Use request.access_route
https://github.com/pallets/werkzeug/blob/master/werkzeug/wrappers.py
#cached_property
def access_route(self):
"""If a forwarded header exists this is a list of all ip addresses
from the client ip to the last proxy server.
"""
if 'HTTP_X_FORWARDED_FOR' in self.environ:
addr = self.environ['HTTP_X_FORWARDED_FOR'].split(',')
return self.list_storage_class([x.strip() for x in addr])
elif 'REMOTE_ADDR' in self.environ:
return self.list_storage_class([self.environ['REMOTE_ADDR']])
return self.list_storage_class()
Example Nginx config:
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Protocol https;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_pass http://127.0.0.1:9000;
}
You should just have:
ip = request.access_route[-1]

Nginx Reverse Proxy Websocket Authentication - HTTP 403

I'm using Nginx as a reverse proxy of a Spring boot application. I also use Websockets with sockjs and stomp messages.
Here is the context configuration.
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/localization" >
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic" />
</websocket:message-broker>
Here is the client code:
var socket = new SockJS(entryPointUrl);
var stompClient = Stomp.over(socket);
var _this = this;
stompClient.connect({}, function () {
stompClient.subscribe('/app/some-url', function (message) {
// do some stuff
});
});
I also you Spring Security to protect some content.
#Configuration
#Order(4)
public static class FrontendSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/js/**", "/css/**", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll();
}
}
Everything works great, expect when I run this application behind a Nginx reverse proxy. Here is the reverse configuration:
proxy_pass http://testsysten:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket support (nginx 1.4)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
# Max body size
client_max_body_size 10M;
The connection always fails with a HTTP 403 code.
I'm using version 1.9.7.
Do you have any idea, why the client does not gets authenticated?
I know similar questions, like this one but the solutions do not work at all.
Update
I managed to run the application over HTTP. I need to pass the CSRF token in the Nginx configuration. New configuration is:
proxy_pass http://testsysten:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Pass the csrf token (see https://de.wikipedia.org/wiki/Cross-Site-Request-Forgery)
# Default in Spring Boot
proxy_pass_header X-XSRF-TOKEN;
# WebSocket support (nginx 1.4)
proxy_http_version 1.1;
Only missing is redirect over HTTPS. In the Spring logs is see following entry:
o.s.w.s.s.t.h.DefaultSockJsService - Processing transport request: GET http://testsystem:80/localization/226/3mbmu212/websocket
Seems like Nginx Proxy needs to rewrite the to the right port.
I solved the problem by myself. Basically, Nginx needs to pass some additional header values if you want to use Websocket and Spring Security. The following lines need to be added to location section in your Nginx config:
# Pass the csrf token (see https://de.wikipedia.org/wiki/Cross-Site-Request-Forgery)
# Default in Spring Boot and required. Without it nginx suppresses the value
proxy_pass_header X-XSRF-TOKEN;
# Set origin to the real instance, otherwise a of Spring security check will fail
# Same value as defined in proxy_pass
proxy_set_header Origin "http://testsysten:8080";
The accepted solution did not work for me although I was using a very classical HTTPS configuration:
server {
listen 443 ssl;
location /ws {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8888;
}
...
The problem is that Spring checks the origin and specifically that code was causing me trouble:
// in org.springframework.web.util.UriComponentsBuilder.adaptFromForwardedHeaders(HttpHeaders):
if ((this.scheme.equals("http") && "80".equals(this.port)) ||
(this.scheme.equals("https") && "443".equals(this.port))) {
this.port = null;
}
In that code the scheme is 'http' and the port is 8888, which is not discarded because it is not the standard port.
The browser however hits https://myserver/ and the 443 port is omitted because it is the default HTTPS one.
Therefore the ports do not match (empty != 8888) and origin check fails.
Either you can disable origin checks in Spring WebSockets:
registry.addHandler( resgisterHandler(), "/ws" ).setAllowedOrigins( "*" );
or (probably safer) you can add the scheme and port to the NGINX proxy configuration:
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
If you are interested, those headers are read in
org.springframework.web.util.UriComponentsBuilder.adaptFromForwardedHeaders(HttpHeaders)
For Spring Boot 2.2.2+
Starting with Spring Boot version 2.2.2 you should be adding following setting for these X-Forwarded-* headers to be taken into account:
server.forward-headers-strategy=native
(in application.properties for instance)
I had faced a similar problem. I was unable to use the basic Spring Security authentication with NGINX. Apart from setting the proxy_pass_header X-XSRF-TOKEN;, I also had to set underscores_in_headers on;, since NGINX by default does not allow headers with underscores and the CSRF token is named _csrf.
So my final configuration file looked like this:
server {
underscores_in_headers on;
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm;
# Make site accessible from http://localhost/
server_name localhost;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
# Uncomment to enable naxsi on this location
# include /etc/nginx/naxsi.rules
}
location /example/ {
proxy_pass_header X-XSRF-TOKEN;
proxy_pass http://localhost:8080/;
}
}
I solved this problem without CSRF header in NGINX proxy.
My stack: spring-boot, spring-security (with redis session store), spring-boot-websocket with default STOMP implementation, NGINX to serve frontend and proxied to another services that frontend consume.
In first time I use the default configuration show in the NGINX Blog here and here (copy and paste for history):
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websocket {
server 192.168.100.10:8010;
}
server {
listen 8020;
location / {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
}
But dont work, still 403 Forbidden.
I fixed this issue with the configuration below (the real important part to fix websocket is # WebSocket Proxy):
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 30010;
server_name localhost;
client_max_body_size 10M;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# Backend API Proxy
location /api {
proxy_pass http://192.168.0.100:30080;
proxy_set_header Host $http_host;
proxy_set_header Access-Control-Allow-Origin 192.168.0.100;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
rewrite ^/api/?(.*) /$1 break;
proxy_redirect off;
}
# CDN Proxy
location ~ ^/cdn/(.*) {
proxy_pass http://192.168.0.110:9000;
rewrite ^/cdn/(.*) /$1 break;
}
# This is the configuration that fix the problem with WebSocket
# WebSocket Proxy
location /ws {
proxy_pass http://192.168.0.120:30090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header Access-Control-Allow-Origin 192.168.0.120;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
}
}
}
In my case (Spring Boot app), in addition to setting the Origin header as specified in the accepted answer, I had to set the Host header to match the ip:port of the Origin header, or to get rid of it altogether.
This is my working vhost config:
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/ssl/certs/<your-cert-file>.pem;
ssl_certificate_key /etc/ssl/private/<your-key-file>.key;
server_name <your-server-fqdn>;
access_log /var/log/nginx/<your-server-fqdn>.access.log;
error_log /var/log/nginx/<your-server-fqdn>.error.log error;
root /srv/www/<your-server-fqdn>;
index index.html index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:8080/v1;
}
location /async-api {
proxy_pass http://127.0.0.1:8080/stomp;
proxy_http_version 1.1;
# either set Host header as follows or get rid of the directive altogether
#proxy_set_header Host "127.0.0.1:8080";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
# Set origin to the real instance, otherwise a of Spring security check will fail
# Same value as defined in proxy_pass
proxy_set_header Origin "http://127.0.0.1:8080";
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /admin-api {
proxy_pass http://127.0.0.1:8080/api;
}
}

Nginx wildcard proxy, pass subdomain to the server (upstream proxy)

I would like to be able to pass subdomain.domain.com to .domain.com apache server, with subdomain info too.
I would like to make a nginx cache for domain, acting like wildcard, but passing subdomain to the destination (there is apache witch wildcard too). Up to now, I pass the info via proxy_set_header Host $host; but I would like to have request with subdomain at the apache server.
upstream domain.com {
server 172.1.1.1:80 weight=50 fail_timeout=30s;
}
server {
server_name *.domain.com;
location / {
proxy_pass http://domain.com;
#proxy_pass $request;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
location ~* ^.+. (jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|swf)$ {
proxy_pass http://topmanagergame.com;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache my-cache;
proxy_cache_valid 200 302 30m;
proxy_cache_valid 404 1m;
}
access_log /var/log/nginx/domain.com.log main;
error_log off;
}
Do you think I can use proxy_pass with upstream ?
Nginx (*wildcard_domain.com) --(cache)--> Apache (*wildcard_domain.com)
Nginx (anything.domain.com) --(cache)--> Apache (anything.domain.com)
upstream somestring {
server domain2.com:80 weight=50 fail_timeout=30s;
}
server {
listen 80;
server_name *.domain.com;
server_name ~^(?<subdomain>.+)\.domain\.com$;
location / {
proxy_pass http://somestring;
proxy_set_header Host $subdomain.domain2.com;
}
}
So I was trying to find the answer to this problem and kept finding this post. But I think dmytrivv answer is out of date. In our scenario, we have both wildcard domains (e.g. *.mydomain.com) and custom domains (e.g. fullycustomdomain.com). But you can solve both by using proxy_set_header Host $host; and having default at the end of your listen.
upstream qaweb {
# Servers in the web farm
server ip-notreal-name.ec2.internal:80;
}
server {
listen 443 ssl default;
ssl_certificate certs/mydomain.com.crt;
ssl_certificate_key certs/mydomain.com.key;
# Support for wildcard domains
server_name admin.mydomain.com *.mydomain.com "";
location / {
# Turn off access logging so we don't fill the hardrive
access_log off;
proxy_pass http://qaweb;
proxy_set_header Host $host;
# So that the correct IP shows up in the log once libapache2-mod-rpaf is installed
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Note, we are also using it as a TLS termination proxy.
You can also find more examples on how to use proxy_pass here https://www.liaohuqiu.net/posts/nginx-proxy-pass/

Resources