Nginx and Oauth2-proxy: After logging in with Google, redirects back to Oauth login page - nginx

I am trying to use Oauth2-proxy as a gateway to my web site with Google auth. The Oauth login page appears, and you can click "Sign In" which takes you to a Google login page, but after logging in it redirects straight back to the Oauth login page. I suspect it has something to do with cookies but I don't know where I'm going wrong.
NGINX config:
server {
listen 443;
server_name my.website.com;
ssl on;
ssl_certificate /etc/ssl/my.website.com.pem;
ssl_certificate_key /etc/ssl/my.website.com.key;
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $request_uri;
}
location = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}
location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
# if you enabled --pass-access-token, this will pass the token to the backend
auth_request_set $token $upstream_http_x_auth_request_access_token;
proxy_set_header X-Access-Token $token;
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
# When using the --set-authorization-header flag, some provider's cookies can exceed the 4kb
# limit and so the OAuth2 Proxy splits these into multiple parts.
# Nginx normally only copies the first `Set-Cookie` header from the auth_request to the respon$
# so if your cookies are larger than 4kb, you will need to extract additional cookies manually.
auth_request_set $auth_cookie_name_upstream_1 $upstream_cookie_auth_cookie_name_1;
# Extract the Cookie attributes from the first Set-Cookie header and append them
# to the second part ($upstream_cookie_* variables only contain the raw cookie content)
if ($auth_cookie ~* "(; .*)") {
set $auth_cookie_name_0 $auth_cookie;
set $auth_cookie_name_1 "auth_cookie_name_1=$auth_cookie_name_upstream_1$1";
}
# Send both Set-Cookie headers now if there was a second part
if ($auth_cookie_name_upstream_1) {
add_header Set-Cookie $auth_cookie_name_0;
add_header Set-Cookie $auth_cookie_name_1;
}
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass "http://127.0.0.1:8888"; # This is where my web server is hosted
}
}
Oauth2-proxy config:
cookie_secret="" # Filled in with a base64 string
provider="google"
email_domains="*"
client_secret="" # Filled in with Google auth client secret
client_id="" # Filled in with Google auth client ID
cookie_secure="false" # No idea what to set this as, tried both false and true, same result
redirect_url="https://my.website.com/oauth2/auth/"
upstreams="http://127.0.0.1:8888/" # My website server
Oauth2-proxy startup command:
~/oauth2-proxy-v6.1.1.linux-amd64/oauth2-proxy --config=/home/username/work/src/github.com/oauth2-proxy/oauth2-proxy/contrib/local-environment/oauth2-proxy.cfg
Any ideas would be greatly appreciated!

According to the documentation
--redirect-url is the OAuth Redirect URL, should be set to supported callback endpoint , e.g. "https://internalapp.yourcompany.com/oauth2/callback".
/oauth2/callback endpoint - the URL used at the end of the OAuth cycle. The oauth app will be configured with this as the callback url.
The actual generated url has a state parameter, that includes the original URL to return back.
As I understand, callback retrieves from the state the original (protected by the proxy) url and redirects browser to it.

The answer I accepted is correct, but I also had to make some cookie changes and figured I should just post my final, working code.
I was able to get it working with these changes:
Under location /oauth2/:
proxy_set_header X-Auth-Request-Redirect "https://my.website.com";
Under location /:
auth_request_set $auth_cookie $upstream_http_set_cookie;
In Oauth2-proxy config (additional or replacement lines):
redirect_url = "https://my.website.com/oauth2/callback"
set_xauthrequest = true
upstreams = ["file:///dev/null"]
cookie_domains = [".website.com"]
cookie_secure = false
cookie_samesite = "lax"
edit: final contents of the config files:
NGINX config:
server {
listen 443;
server_name my.website.com;
ssl on;
ssl_certificate /etc/ssl/my.website.com.pem;
ssl_certificate_key /etc/ssl/my.website.com.key;
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect "https://my.website.com";
}
location = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}
location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
auth_request_set $token $upstream_http_x_auth_request_access_token;
proxy_set_header X-Access-Token $token;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
auth_request_set $auth_cookie_name_upstream_1 $upstream_cookie_auth_cookie_name_1;
if ($auth_cookie ~* "(; .*)") {
set $auth_cookie_name_0 $auth_cookie;
set $auth_cookie_name_1 "auth_cookie_name_1=$auth_cookie_name_upstream_1$1";
}
if ($auth_cookie_name_upstream_1) {
add_header Set-Cookie $auth_cookie_name_0;
add_header Set-Cookie $auth_cookie_name_1;
}
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass "http://127.0.0.1:8888"; # This is where my web server is hosted
}
}
Oauth2-proxy config:
cookie_secret="" # Filled in with a base64 string
provider="google"
email_domains="*"
client_secret="" # Filled in with Google auth client secret
client_id="" # Filled in with Google auth client ID
cookie_domains=[".website.com"]
cookie_secure="false"
cookie_samesite="lax"
redirect_url="https://my.website.com/oauth2/callback"
upstreams="http://127.0.0.1:8888/" # My website server
set_xauthrequest=true
upstreams=["file:///dev/null"]

You are going back to the oauth page because you have set (in Oauth2-proxy config)
redirect_url="https://my.website.com/oauth2/auth/"
Change it to just https://my.website.com/ for example,to land on your website home screen.

Related

nginx authelia-authrequest.conf for subnet

I'm posting this topic because I have a problem with nginx configuration on authelia with ip filtering.
Here is my current configuration.
####BABBYBUDDY####
map $remote_addr $is_allowed {
192.168.1.120 1;
default 0;
}
include /etc/nginx/conf/user-agent/user-agent-babby.domain.tld.conf;
server {
listen 80;
listen 443 ssl http2;
server_name babby.domain.tld;
if ($scheme != "https") {
rewrite ^ https://$host$uri permanent;
}
include /etc/nginx/conf/ssl/ssl.conf;
include /etc/nginx/conf/ssl/domain/cert.domain.tld.conf;
include /etc/nginx/conf/error-page/error-page.conf;
charset utf-8;
include /etc/nginx/conf/auth/authelia/authelia-location.conf;
location / {
if ($user_agent = 0) { return 403; }
if ($is_allowed = 0) {
include /etc/nginx/conf/auth/authelia/authelia-authrequest.conf;
}
proxy_pass http://babybuddy:8000;
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 X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
}
}
authelia-authrequest.conf
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
auth_request /authelia;
## Set the $target_url variable based on the original request.
auth_request_set $target_url $scheme://$http_host$request_uri;
## Save the upstream response headers from Authelia to variables.
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
auth_request_set $name $upstream_http_remote_name;
auth_request_set $email $upstream_http_remote_email;
## Inject the response headers from the variables into the request made to the backend.
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
proxy_set_header Remote-Name $name;
proxy_set_header Remote-Email $email;
## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
error_page 401 =302 https://auth.domain.tld/?rd=$target_url;
I want to load the selement file based on the remote ip.
Error nginxnginx: [emerg] "auth_request" directive is not allowed here in /etc/nginx/conf/auth/authelia/authelia-authrequest.conf:2
Thank you in advance.
Transfer the config from authelia-authrequest.conf to the base file.

Docker login not passing basic authentication headers to nginx

I need to put a NGINX proxy between my registery and the web. This because I need to control the authentications with auth_request.
The program that is behind this auth_request can search in different databases for the user.
Users that need to login can be located in different databases. The program gives a 200 reply when he can find the user. This is already working for other set-ups that I needed to secure the same way.
## Set a variable to help us decide if we need to add the
## 'Docker-Distribution-Api-Version' header.
## The registry always sets this header.
## In the case of nginx performing auth, the header is unset
## since nginx is auth-ing before proxying.
map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
'' 'registry/2.0';
}
server {
include /etc/nginx/conf.d/certs/server.io/ssl.conf;
listen *:5000 ssl;
server_name docker.server.io;
access_log /etc/nginx/conf.d/logs/docker.server.io.access.log main;
# disable any limits to avoid HTTP 413 for large image uploads
client_max_body_size 0;
# required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
chunked_transfer_encoding on;
location /v2/ {
# Do not allow connections from docker 1.5 and earlier
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}
# To add basic authentication to v2.
auth_request /tb_int_auth;
auth_request_set $user $upstream_http_x_forwarded_user;
## If $docker_distribution_api_version is empty, the header is not added.
## See the map directive above where this variable is defined.
add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;
proxy_pass https://10.0.100.161:31500;
proxy_ssl_verify off;
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
location = /tb_int_auth {
internal;
proxy_pass https://internal_server/auth.php;
proxy_ssl_verify off;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Client-IP $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/local/openresty/nginx/html;
}
}
The headers that I receive during the request
"headers": {
"x-forwarded-proto": "https",
"QueryString": "",
"Method": "GET",
"x-forwarded-for": "127.0.0.1",
"Uri": "auth.php",
"x-real-ip": "127.0.0.1",
"RemoteUser": "",
"host": "127.0.0.1",
"PathInfo": "auth.php",
"connection": "close",
"x-forwarded-by": "127.0.0.1:443",
"front-end-https": "on",
"Principal": "",
"accept-encoding": "gzip",
"user-agent": "docker/18.09.0 go/go1.10.4 git-commit/4d60db4 kernel/3.10.0-862.14.4.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/18.09.0 \\(linux\\))"
}
In the other applications I receive the authentication header.

SSL not working on nginx 443 configuration

I am setting up the nginx for a website. i want to set only for some sublinks to ssl link user login, sign up .i create a lets encrypt ssl and the certificate is working fine. i checked on ssl shopper. I want to configure the ssl only for store not for all the site.so i redirect the store from 80 to 443 and only the store want to work ssl. but after i configure
on nginx some buttons (javascriptvoid) not working. its says mixed content , so when i check on view source its shows the url of the buttons are still http in store page.(it should be https) .
i check with everything, i reconfigure nginx, check the tomcat side, all are oky.i dont knwo what is the issue.
my nginx configuration is here for you
(The(/sub) sub location is the one which i want to work https)
NGINX configuration
upstream backend_front {
ip_hash;
server tomcat_serverip:8080;
}
server {
listen 80;
server_name www.domianname.com;
charset utf-8;
access_log /var/log/nginx/80access.log main;
location / {
proxy_pass http://backend_front;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /sub/ {
if ($http_x_forwarded_proto != 'https') {
return 301 https://$server_name$request_uri;
}
proxy_pass http://backend_front;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 443 ssl;
server_name www.domainname.my;
ssl on;
ssl_certificate /etc/letsencrypt/live/fullch.pem;
ssl_certificate_key /etc/letsencrypt/live/privkey.pem;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 15m;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
charset utf-8;
access_log /var/log/nginx/443access.log main;
add_header Strict-Transport-Security "max-age=31536000";
root /data/resources/;
location /sub/ {
proxy_pass http://backend_front;
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-Proto $scheme;
}
location / {
proxy_pass http://backend_front;
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-Proto $scheme;
}
# for static files caching
location ~ .*\.(html|jsp)?$ {
proxy_pass http://backend_front;
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-Proto $scheme;
}
location ~ .*\.(gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ {
root /data/resources/;
expires 20m;
} # for static files caching -- end
location ~ /favicon\.ico {
root html;
}
location ~ /\. {
deny all;
}
}
The view source result for the buttons
Manage Account
My Orders
When I click on the button this error message showing on google chrome element console (but for http its working fine.)
jquery-1.8.3.min.js:2 Mixed Content: The page at 'https://www.example.com/store/account.htm' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://www.example.com/isLogin.htm'. This request has been blocked; the content must be served over HTTPS.
send # jquery-1.8.3.min.js:2
ajax # jquery-1.8.3.min.js:2
tiaozhuan # account.htm:155
onclick # account.htm:77
please help me guys on this.i am stuck on thi for last 1 week to fix this.i am not a programmer i am a sys admin. and new for nginx.please help on this.
"Mixed content" usually refers to a page which, when loaded, then makes secondary requests under both HTTP and HTTPS (e.g., images, css, javascript).
When you use make a request under HTTPS, all subsequent requests must also be HTTPS. You need to convert the URL of the buttons to be "https://"
At last we find out the issue and resolved the issue after 3 weeks.
the issue is beacuse of the proxy ip.We use nginx server for redirection and for proxy. so we need to add aditional enty in server.xml in tomcat about the nginx server ip .here is the entry.
<Valve className="org.apache.catalina.valves.RemoteIpValve" internalProxies="111\.111\.111\.111" remoteIpHeader="X-Forwarded-For" protocolHeader="X-Forwarded-Proto" protocolHeaderHttpsValue="https" httpsServerPort="443" />
so the Internal proxy is the Nginx IP.

Issue a custom token from NGINX

I am new to NGINX. I'm wondering if it is possible to, with a single request to NGINX, make two proxy calls that affect the outcome of the response.
Specifically, I'm wanting to add a token to the response of an NGINX request where the token is given as a response header from a separate service.
Theoretically, it might look something like...
location / {
# Call to token service and set the response to a variable, maybe?
# proxy_pass Make the actual call
# Add token from step one to response headers
}
I don't know if this is supported by NGINX, if I need to delve into a custom module, or if this is just a bad idea.
Thanks.
Got it!
location / {
if ($http_x_entry_id = "") {
return 302 /entry;
}
auth_request /token/test-token;
auth_request_set $token $upstream_http_x_test_token;
set $test_ui test-ui;
proxy_pass http://$test_ui;
add_header X-My-Token "$token";
}
location /token/test-token {
internal;
set $token_api token-api;
error_page 500 =401 /error/401;
error_page 400 =401 /error/401;
proxy_method POST;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_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-Proto $scheme;
rewrite /token/(.*) "/$1/$http_x_entry_id" break;
proxy_pass http://$token_api:8080;
}

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

Resources