My application sits behind a caching server that acts as a proxy. I'd like to limit access to the application so that it can only be accessed via the proxy. I first tried using a RemoteAddrFilter, however this filters based on the X-Forwarded-For header which is the IP address of the user connecting to the proxy, not the IP address of the proxy (This header is forwarded instead of being overwritten by the proxy). I decided to write my own filter based off of the RemoteAddrFilter:
public class IpFilter extends RequestFilter {
#Override
protected Log getLogger() {
return null;
}
private void sendErrorWhenNotHttp(ServletResponse response) throws IOException {
response.setContentType("text/plain");
response.getWriter().write(sm.getString("http.403"));
response.getWriter().flush();
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (/*check IP*/) {
filterChain.doFilter(servletRequest, servletResponse);
} else if (servletResponse instanceof HttpServletResponse) {
((HttpServletResponse) servletResponse).sendError(denyStatus);
} else {
sendErrorWhenNotHttp(servletResponse);
}
}
}
This gives me much more control over what can be accessed, however I'm unsure what to put into this if statement.
From what I can tell, given the parameters exposed here, the only accessible IP addresses are via headers, which do not represent the IP address of the proxy.
How can I get the IP address of the proxy?
Related
I'm working on springboot project and we are using openId keycloak for authentication. I'm delaing with Multitenancy concept too. I want to sent custom header as request or either response and the same should be captured in APM as metadata. My current approach is as follows:
public class PreAuthFilter extends KeycloakPreAuthActionsFilter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
((HttpServletResponse) response).addHeader("X-Realm",realm);
super.doFilter(request, response, chain);
}
But with above code i'm getting multiple response metatdata in APM
http.response.headers.X-Realm.0
http.response.headers.X-Realm.1
http.response.headers.X-Realm.2
http.response.headers.X-Realm.3
My expectation was single realm in APM Metadata
http.response.headers.X-Realm = "value"
I think SimpleHttpFacade is getting intialized during resolving deployment multiple times hence adding the response.
Need Suggestion
Thanx.
It appears this could be that the issue is more likely related to your application context spring and filters.
Since it's spring could you try OncePerRequestFilter ?
import org.springframework.web.filter.OncePerRequestFilter;
#Named
public class ApmFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// do apm things
filterChain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
I am playing around with a simple Spring Boot webapp which gets called by some software which does basic file download/upload tasks.
The software sending the Requests to my app can not be changed/modified and I came across following request being sent to my webapp:
DEBUG Received [
MOVE /database/1.tmp HTTP/1.1
Destination: http://localhost:8080/database/1
Host: localhost:8080
]
which results in
WARN Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'MOVE' not supported]
As I found out MOVE is not an enum in RequestMethod so I can not simply annotate my controller with method = RequestMethod.MOVE.
How can I handle this request?
First, you have to override Spring Boot's default firewall to allow MOVE methods:
#Bean
public HttpFirewall defaultHttpFirewall() {
final StrictHttpFirewall firewall = new StrictHttpFirewall();
Set<String> allowedHttpMethods = new HashSet<>();
allowedHttpMethods.add(HttpMethod.DELETE.name());
allowedHttpMethods.add(HttpMethod.GET.name());
allowedHttpMethods.add(HttpMethod.POST.name());
allowedHttpMethods.add(HttpMethod.PUT.name());
allowedHttpMethods.add("MOVE");
firewall.setAllowedHttpMethods(allowedHttpMethods);
return firewall;
}
Now that MOVE requests are handed over to your application, your only way (as I found out there are no controller mappings for custom methods) is to manually handle the requests in a filter:
#Component
#Slf4j
public class NonRESTFulHttpMethodRequestFilter implements Filter {
#Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
if ("MOVE".equals(httpServletRequest.getMethod())) {
final HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
log.trace("Ignoring 'MOVE {}' request with 200 OK", httpServletRequest.getRequestURI());
httpServletResponse.setStatus(HttpStatus.OK.value());
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
}
I am trying to implement a browser based single sign on using SPNEGO with Tomcat.
I have followed all the instructions on these two pages:
http://spnego.sourceforge.net/pre_flight.html
http://spnego.sourceforge.net/spnego_tomcat.html
When I accessed hello_spnego.jsp work fine and return user
but I need to add configuration in spring boot standalone application here is class configuration
#WebFilter("/*")
public class ExampleSpnegoAuthenricatorValue implements Filter {
private final Logger LOGGER = Logger.getLogger(ExampleSpnegoAuthenricatorValue.class.getName());
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
LOGGER.info("start Here -------------------------------------");
LOGGER.info(httpServletRequest.getRemoteUser());
LOGGER.info(httpServletRequest.getSession().getId());
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
System.out.println("Header: " + httpServletRequest.getHeader(headerNames.nextElement()));
}
}
LOGGER.info(httpServletRequest.getRequestedSessionId());
LOGGER.info("end Here -------------------------------------");
// Get the IP address of client machine.
// Log the IP address and current timestamp.
// Pass request back down the filter chain
filterChain.doFilter(servletRequest,servletResponse);
}
#Override
public void destroy() {
}
}
RemoteUser is null ?? and this error showing :
HTTP Status 500 - GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
type Exception report
message GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
I have verified that the account used to talk to the KDC is working correctly. Also, I have the username and password specified in the web.xml file, so I don't have a separate KeyTab file.
For diagnosis purposes I'm add two files krb5.conf and login.conf files:
Since I don't have a keytab file, it's not mentioned in the login.conf file.
FYI, I'm using Tomcat 8 and JDK 1.8.
I would really appreciate some insight on what's happening here. If you need more information, please let me know. Thanks in advance!
i have a few java micro services deployed on open shift . all of them are protected by a api-gateway application which uses keycloak for authentication & Authorization.
Down stream services need to log which user perform certain actions.
in my api-gateway application properties i have already set zuul.sensitiveHeaders to empty
zuul.sensitiveHeaders:
i can see bearer token in the downstream applications .
but how do i get the principal/user from token as downstream applications don't have keycloak dependency in gradle. ( if i add the dependency , i need to reconfigure realm and other properties ) .. is this the right way to do ?
i also tried adding a filter in api-gateway to separately set the user_name in header
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
System.out.println(" Filter doFilter "+req.getUserPrincipal());
if(req.getUserPrincipal() != null ){
res.setHeader("MYUSER",req.getUserPrincipal()==null?"NULL":req.getUserPrincipal().getName());
}
chain.doFilter(request, response);
}
But when i try to get the header in downstream microservices is null.
I wouldn't recommend doing this, or assuming that your non-web facing apps are completely secure. Realistically you should be re-validating the bearer token.
What you need is a zuul filter to add a header to the request. This is mostly from memory and you could update the filter to check if it should filter or not, that the request doesn't already contain an expected header etc.
#Component
public class AddUserHeader extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(AddUserHeader.class);
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter{
return true;
}
#Override
public Object run() {
RequestContext.getCurrentContext().addZuulRequestHeader("MYUSER", SecurityContextHolder.getAuthentication().getPrincipal().getName());
return null;
}
I have an application, where user is pre-authorized by SSO and lands to my page, now I need to make a call to another rest api to get some data, which is running on another server, but it will be use the same authentication. So I just wanted to know, how I can provide the authentication process? Do I need to set the cookie what I am getting from the incoming request.
When the request lands on your page it should have a token or key, in the http AUTHORIZATION header, this should be used with a filter
public class AuthFilter extends OncePerRequestFilter {
private String failureUrl;
private SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
try {
// check your SSO token here
chain.doFilter(request, response);
} catch (OnlineDriverEnquiryException ode) {
failureHandler.setDefaultFailureUrl(failureUrl);
failureHandler.onAuthenticationFailure(request, response, new BadCredentialsException("Captcha invalid!"));
}
}
public String getFailureUrl() {
return failureUrl;
}
public void setFailureUrl(String failureUrl) {
this.failureUrl = failureUrl;
}
}
Also read this post on how to set up the auto config. Spring security without form login