Reputation: 3114
In my web application, I need to make calls to to a different web service (developed/managed by me) to start/manage resources through rest APIs. Web service runs on tomcat6. I can see from the browser logs that POST requests are getting through but GET requests are being forbidden. If I make the same calls from the web service itself, then I am not seeing any issues. I have defined cross origin filter for tomcat6 and mentioned GET in supported methods too but still problem persists..
I have defined the cross origin filters this way in the web.xml at the application server level itself. I am using CORS filter libraries from http://software.dzhuvinov.com/cors-filter.html. This is a tomcat6 server and filter has been defined at ($TOMCAT6_HOME/conf/web.xml) as follows
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.allowOrigin</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.supportedMethods</param-name>
<param-value>GET, POST, HEAD, PUT, DELETE, OPTIONS</param-value>
</init-param>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>*</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Strangely, webservice is accepting POST calls but for GET calls, its throwing 403 - Forbidden error telling "Access to the specified resource has been forbidden".
Headers for the GET call is as follows
Request URL:https://remote.vm.mycompany.com/remote/tunnel?read:c2faeb31-4147-49e8-b8d3-53d89496e5ca:0
Request Method:GET
Status Code:403 Forbidden
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Host:remote.vm.mycompany.com
Origin:https://ec2-184-72-200-91.compute-1.amazonaws.com
Referer:https://ec2-184-72-200-91.compute-1.amazonaws.com/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36
Query String Parametersview sourceview URL encoded
read:c2faeb31-4147-49e8-b8d3-53d89496e5ca:0:
Response Headersview source
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:https://ec2-184-72-200-91.compute-1.amazonaws.com
Content-Length:961
Content-Type:text/html;charset=utf-8
Date:Sun, 21 Jul 2013 17:17:37 GMT
Server:Apache-Coyote/1.1
Tomcat access logs also reveal GET request has been forbidden but does't give any clue in any of the logs
- - - [21/Jul/2013:17:17:37 +0000] POST /remote/tunnel?connect HTTP/1.1 200 -
- - - [21/Jul/2013:17:17:37 +0000] GET /remote/tunnel?read:c2faeb31-4147-49e8-b8d3-53d89496e5ca:0 HTTP/1.1 403 -
Here is my servlet code. I am trying to integrate guacamole (HTML5 VNC client as webservice )
package com.mycompany.html5remote;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.guacamole.GuacamoleException;
import net.sourceforge.guacamole.net.GuacamoleSocket;
import net.sourceforge.guacamole.net.GuacamoleTunnel;
import net.sourceforge.guacamole.net.InetGuacamoleSocket;
import net.sourceforge.guacamole.protocol.ConfiguredGuacamoleSocket;
import net.sourceforge.guacamole.protocol.GuacamoleClientInformation;
import net.sourceforge.guacamole.protocol.GuacamoleConfiguration;
import net.sourceforge.guacamole.servlet.GuacamoleHTTPTunnelServlet;
import net.sourceforge.guacamole.servlet.GuacamoleSession;
public class HttpTunnel extends GuacamoleHTTPTunnelServlet {
private Logger logger = LoggerFactory.getLogger(HttpTunnel.class);
@Override
protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
HttpSession httpSession = request.getSession(true);
logger.info("Inside doConnect Method.");
GuacamoleClientInformation info = new GuacamoleClientInformation();
String hostname = request.getParameter("hostname");
String protocol = request.getParameter("protocol");
// Create socket
GuacamoleConfiguration config = new GuacamoleConfiguration();
config.setProtocol(protocol);
config.setParameter("hostname", hostname);
//config.setParameter("hostname", "ec2-184-73-104-108.compute-1.amazonaws.com");
if("vnc".equals(protocol)){
config.setParameter("port", "5901");
}else if ("rdp".equals(protocol)){
config.setParameter("port", "3389");
}else{
config.setParameter("port", "22");
}
logger.info("Set the configuration. Creating the socket connection now..");
// Return connected socket
GuacamoleSocket socket = new ConfiguredGuacamoleSocket(
new InetGuacamoleSocket("localhost", 4822),
config, info
);
logger.info("Successfully created socket connection.");
// Create tunnel from now-configured socket
GuacamoleTunnel tunnel = new GuacamoleTunnel(socket);
// Attach tunnel
GuacamoleSession session = new GuacamoleSession(httpSession);
session.attachTunnel(tunnel);
logger.info("Done");
return tunnel;
}
}
Documentation for GuacamoleHTTPTunnelServlet (GPL Licenced) is here
What could possibly am I missing? Are there any other places I can look for clues? Please help
Upvotes: 1
Views: 2002
Reputation: 11
I've spent days trying to hook up my Spring Boot with Apache Guacamole backend to my NextJS frontend, and here's the solution that works for me (with reference to this issue on StackOverflow):
There are two main parts to the problem, CORS and CSRF. Firstly, create a new Java class and setup a CORS configuration with the stuff like headers and methods that Guacamole needs, as well as the origin of the frontend server. This ensures that the Spring Boot server will respond to a request from the frontend with the proper CORS headers so that it doesn't get blocked by the browser.
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setExposedHeaders(Arrays.asList("Guacamole-Status-Code", "Guacamole-Error-Message", "Guacamole-Tunnel-Token"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Secondly, with the proper CORS configuration, the POST request ending with "/tunnel?connect" will be allowed, but subsequent GET requests ending with "/tunnel?read:some-numbers-and-text:0" will still be blocked. This is where CSRF comes into play, as the requests were probably rejected due to Spring Security's default CSRF protection. As such, simply create a new SecurityFilterChain and specify the line "http.cors().and().csrf().disable();", to disable CSRF (you can configure this later). The POST requests will then be accepted, and an Apache Guacamole tunnel can be initiated.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Adds a CorsFilter to be used. If a bean by the name of corsFilter is provided, that CorsFilter is used.
// Else if corsConfigurationSource is defined, then that CorsConfiguration is used.
http.cors().and().csrf().disable();
return http.build();
}
Here's the full Java Class:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Adds a CorsFilter to be used. If a bean by the name of corsFilter is provided, that CorsFilter is used.
// Else if corsConfigurationSource is defined, then that CorsConfiguration is used.
http.cors().and().csrf().disable();
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setExposedHeaders(Arrays.asList("Guacamole-Status-Code", "Guacamole-Error-Message", "Guacamole-Tunnel-Token"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Upvotes: 1
Reputation: 91
do you have any security constraints related to http methods configured in web.xml? i am not sure that why you go for a seperate api for filtering your request?
Upvotes: 0