Nginx Reverse Proxy Websocket Authentication - HTTP 403

I use Nginx as the reverse proxy for the Spring boot application. I also use Websockets with sockjs and stomp messages.

Here is the contextual 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 give 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 fine, expect when I run this application behind the 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 is always with HTTP code 403.

I am using version 1.9.7.

Do you have any ideas why the client is not receiving authentication?

I know similar questions like this one , but solutions do not work at all.

Update

I managed to run the application through HTTP. I need to pass the CSRF token in the Nginx configuration. New 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; # 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 there is no redirection over HTTPS. Spring logs display the following entry:

 oswssthDefaultSockJsService - Processing transport request: GET http://testsystem:80/localization/226/3mbmu212/websocket 

It seems that Nginx Proxy needs to be rewritten to the right port.

+5
source share
4 answers

I solved the problem myself. Basically, Nginx needs to pass some additional header values ​​if you want to use Websocket and Spring Security. The following lines should be added to the location section in your Nginx configuration:

  # 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"; 
+7
source

The accepted solution did not work for me, although I used the very classic 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 is checking the origin, and it is this code that causes me problems:

 // 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 this code, the scheme is "http" and the port is 8888, which is not discarded, since it is not a standard port.

However, the browser accesses https: // myserver / , and port 443 is omitted because it is HTTPS by default.

Therefore, the ports do not match (empty! = 8888), and the origin check is not performed.

Or you can turn off 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're interested, these headings read in

 org.springframework.web.util.UriComponentsBuilder.adaptFromForwardedHeaders(HttpHeaders) 
+1
source

I had a similar problem. I was unable to use Spring's basic security check using NGINX. In addition to setting 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/; } } 
0
source

I solved this problem without CSRF header in NGINX proxy.

My stack: spring-boot, spring-security (with redis session storage), spring-boot-websocket with standard STOMP implementation , NGINX to service the interface and proxy other services that use the interface.

The first time I use the default configuration on the NGINX blog here and here (copy and paste into the story):

 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 not working, still 403 Forbidden.

I fixed this problem with the configuration below (the real important part for fixing 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; } } } 
0
source

Source: https://habr.com/ru/post/1237582/


All Articles