We are currently using Spring x509 support for client-auth. The app server is JBoss in which the verify-client="want" has been set.
However what I observe is once I add the x509 element to the spring configuration file, it will try to load a digital certificate for every http request. However I dont want it to do that for the login page. Unless the user clicks on a specific button (say 'Login with Certificate') on the login page, I dont want the certificate to be loaded and used.
Is there a way to exclude any particular URLs? Or once you have an x509 element in the element for Spring configuration, it will try to load the certificate for every http request?
Here is part of my spring configuation:
<security:http create-session="always">
<x509 subject-principal-regex="CN=(.*?)," user-service-ref="myUserDetailsService" />
<intercept-url pattern= ..... // not relevant to this question
<intercept-url pattern= ..... // not relevant to this question
<security:form-login
login-page="myLoginPage"
authentication-failure-url="/myErrorPage"
default-target-url="/mySuccessPage"
always-use-default-target="true"/>
</security:http>
Also if not in Spring, is there a way to specify an exclude URL on JBOSS:
Here is the element configured in the JBOSS standalone.xml
<connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true">
<ssl name="ssl" key-alias="alias" certificate-key-file="keystore" verify-client="want" ca-certificate-file="truststore"/>
</connector>
Related
I have a spring app.
It is consistently giving me this error in websphere liberty. This is my login settings .
in web.xml for spring security.
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- ===== SECURITY CONFIGURATION ===== -->
<!-- All requests matching pattern below will bypass the security filter chain completely -->
<security:http pattern="/image/**" security="none"/>
<!-- security:http pattern="/login.jsp*" security="none" / -->
<!-- Defines who can access each URL. -->
<!--
Spring Security 3.0 introduced the ability to use Spring EL expressions as an authorization mechanism in addition to the simple use
of configuration attributes and access-decision voters which have seen before. Expression-based access control is built on the same
architecture but allows complicated boolean logic to be encapsulated in a single expression.
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/el-access.html
-->
<security:http auto-config="true" use-expressions="true">
<!-- URL restrictions (order is important!) Most specific matches should be at top -->
<!-- Don't set any role restrictions on login.jsp. Any requests for the login page should be available for anonymous users -->
<security:intercept-url pattern="/login.jsp*" access="isAuthenticated()" />
...
Anonymous access to the login page doesn't appear to be enabled. This is almost certainly an error. Please check your configuration allows unauthenticated access to the configured login page. (Simulated access was rejected: org.springframework.security.access.AccessDeniedException: Access is denied)
I have configured LDAP but I do not know how to tie LDAP settings to server authentication as similar to WAS 7.0 global security activation so the application is not able to authenticate .
Can someone give me further infomation as how the access-id in security settings relates to LDAP Realm.
<jaasLoginContextEntry id="system.WEB_INBOUND" loginModuleRef="HashLogin, certificate, hashtable, token, userNameAndPassword" name="system.WEB_INBOUND"/>
<jaasLoginContextEntry id="WSLogin" loginModuleRef="WSLoginId, certificate, hashtable, token, userNameAndPassword" name="WSLoginId" />
<jaasLoginModule id="WSLoginId" className="com.ibm.ws.security.common.auth.module.WSLoginModuleImpl" libraryRef="${com.ibm.ws.security.wim.*}"></jaasLoginModule>
</server>
I have looked at the Liberty profile documents so I would appreciate a more detailed information then linking me to IBM documents because I have read those and several information out in internet a lot and have exhausted all resources that I can do look up on so I would really appreciate a more detailed explanation which would explain how to implement global security and application security enablement as WAS 7.0 does when we configure LDAP repository in WAS . My LDAP is Microsoft Active Directory. And my application security is handled by spring container.
As resource I looked at this but this did not seem to help.
How to map security role to ldap group in websphere liberty profile
Here is how access-id in the Liberty profile can be defined assuming the LDAP server definition has realm name as ldapRealm in server.xml.
<!- Sample LDAP definition -->
<ldapRegistry id="TivoliLdap" host="myHost.rtp.raleigh.ibm.com" realm="ldapRealm" port="389" ldapType="IBM Tivoli Directory Server" ignoreCase="false" baseDN="o=mycompany,c=us">
</ldapRegistry>
<!-- Application binding sample for using access-id attribute for user or group element -->
<application-bnd>
<security-role name="Employee">
<user name="Bob" access-id="user:ldapRealm/Bob"/>
<group ame="developers" access-id="group:ldapRealm/developers"/>
</security-role>
</application-bnd>
I have implemented resource owner flow with spring oauth2 based on spring's sparklr sample application and a couple of samples I found online. I tested the token request part with curl like this in order to provide both client and user credentials:
curl -v --data "username=user1&password=user1&client_id=client1&client_secret=client1&grant_type=password" -X POST "http://localhost:8080/samplerestspringoauth2/oauth/token"
and it works correctly, however I have made the following observation:
Although according to the examples I saw, I make use of the BasicAuthentication filter, this is not really used in the security process. Since the token request does not contain an Authentication header, the BasicAuthentication filter just skips doing any checks. ClientCredentialsTokenEndpointFilter and authentication-server are the only ones performing security checks during the token request. After noticing this and verifying it via debugging, I tried to remove completely the following part:
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
from the configuration. But then I got the warning:
"No AuthenticationEntryPoint could be established. Please make sure
you have a login mechanism configured through the namespace (such as
form-login) or specify a custom AuthenticationEntryPoint with the
'entry-point-ref' attribute".
As a next step, I added the entry-point-ref="clientAuthenticationEntryPoint in the http namespace, and got rid of the warning. I tested the app and played correctly.
However, in addition to the above, I have also made the following observation during debugging:
The ClientCredentialsTokenEndpointFilter, contains its own OAuth2AuthenticationEntryPoint entry point inside a private variable, and uses that when failing due to wrong client credentials.
Therefore, it does not matter what entry point I specify either in the basic filter, or in the http namespace. At the end ClientCredentialsTokenEndpointFilter will use its own private OAuth2AuthenticationEntryPoint.
To summarize my conclusions seem to be the following:
The basic filter is not used and can be removed, if we specify the
endpoint in the http namespace instead.
Specifying either a basic
filter,or an endpoint in http namespace is needed only for the
compiler to stop the warning. They have no practical use, and the
endpoint used is hardcoded inside
ClientCredentialsTokenEndpointFilter.
Below I put the http and endpoint configuration for the token request for your reference. I skip the rest of configuration for keeping the post easy to read:
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<custom-filter ref="clientCredentialsTokenEndpointFilter"
before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client" />
<property name="typeName" value="Basic" />
</bean>
I also assume that the same issue also occurs in the original sparklr application (which is spring oauth2 sample app) configuration for token request which is very similar. That can be found in https://github.com/spring-projects/spring-security-oauth/blob/master/samples/oauth2/sparklr/src/main/webapp/WEB-INF/spring-servlet.xml, and the related part is below:
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/**" method="GET" access="ROLE_DENY" />
<intercept-url pattern="/**" method="PUT" access="ROLE_DENY" />
<intercept-url pattern="/**" method="DELETE" access="ROLE_DENY" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request
parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
I would expect spring oauth2 to more appropriately interact with spring security instead of having to put unnecessary and misleading configuration, and that makes me think that I may have missed something. Since security is a sensitive aspect I wanted to share that with you and ask if my conclusion correct.
The /oauth/token provides two different ways to authenticate clients which are requesting tokens:
Using HTTP-Basic authentication (when "http-basic" element is present)
The authentication is handled with org.springframework.security.web.authentication.www.BasicAuthenticationFilter and processes the "Authorization" HTTP header which contains base64 encoded credentials of the client. The filter only performs processing when the Authorization header is present. This method is always tried first. The entry point defined on http-basic will only be invoked when user has supplied an "Authorization" header with invalid content - that's why you don't see the entry point invoked in your debugger, try to set an Authorization HTTP header and your breakpoint will get a hit.
As defined in the OAuth standard using client_id and client_secret HTTP paremeters
This is handled using org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter and by default uses entry point which sends back WWW-Authenticate header to the client. The default entry point can be customized (there's a setAuthenticationEntryPoint method). The entry point is only used when you supply client_id parameter.
Both of these methods use different ways to obtain client's username+password, but verify it against the same authentication manager.
The "No AuthenticationEntryPoint could be established" error which you observe when taking out the <http-basic> element is coming from Spring Security itself, not from the OAuth Extension. The reason is that Spring Security is not able to tell that there's a default entry point already configured inside of the custom filter ClientCredentialsTokenEndpointFilter. And the HTTP configuration of Spring Security always must have at least one entry point available.
So, the complete logic goes as follows:
when you include "Authorization" header with invalid credentials and <http-basic> element is present , system will use entry point defined on the <http-basic> element. If none is specified (attribute entry-point-ref is missing), system will create a default instance of BasicAuthenticationEntryPoint automatically for you and use it.
when you include HTTP parameter "client_id" and "client_secret" with invalid credentials and custom filter clientCredentialsTokenEndpointFilter is present, system will use entry point defined in the clientCredentialsTokenEndpointFilter bean (which is by default instance of OAuth2AuthenticationEntryPoint)
in case neither "Authorization" header nor "client_id" parameter are present and the endpoint requires authentication ("IS_AUTHENTICATED_FULLY"), system will use the entry point defined on the <http entry-point-ref="">, if present, otherwise it will use the entry point defined on the http-basic (as above)
in case you don't specify neither http-basic (or other default authentication method which Spring recognizes), nor default entry point using the <http entry-point-ref="">, system will fail with "No AuthenticationEntryPoint could be established", because it requires at least one entry point and it doesn't understand that there's one available inside the clientCredentialsTokenEndpointFilter.
Regarding your observations:
>> The basic filter is not used and can be removed, if we specify the
endpoint in the http namespace instead.
> This is true in case you are authentication your clients using client_id + client_secret
>> Specifying either a basic filter,or an endpoint in http namespace is
needed only for the compiler to stop the warning. They have no
practical use, and the endpoint used is hardcoded inside
ClientCredentialsTokenEndpointFilter.
> Partly true, as the entry point will be used in case client_id is missing.
The configuration is indeed confusing (which is partly caused by the fact that OAuth isn't a native part of Spring Security, but an extension), but all of those settings make sense and are used in specific situations.
The changes you made have no security implications.
please take a look at a part of my security-config.xml:
<http use-expressions="true">
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/home" access="permitAll" />
[other stuff here]
<intercept-url pattern="/**" access="denyAll" />
</http>
Let's suppose that the context name of my application is koko.
This works fine in Tomcat: When I visit http://tomcat-url:8080/koko/ or http://tomcat-url:8080/koko/home I see a home page asking me to login - after I login I am redirected to http://tomcat-url:8080/koko/and I can see link to the other stuff.
When I try to do the same in JBoss, I visit http://jboss-url:8080/koko/ and I immediately get a login page ! If I visit the http://jboss-url:8080/koko/home I see the homepage asking me to login. Now, after I login I am redirected to the http://jboss-url:8080/koko/ again and I get an Access Denied!! If I manually go to http://jboss-url:8080/koko/home after I've logged in I see the other staff and I am able to navigate normally.
So the problem seems to be that JBoss does not understand the line
<intercept-url pattern="/" access="permitAll" />
or it is overriden by the /** line that follows. However tomcats works fine with at. This shouldn't be dependent on the Application Server since it is completely spring related and the same spring is used for both applications.
Update - solution: Based on #M.Deinum 's answer I added the a new url request mapping named /index.html to my homepage and a permitAll line to that url - and worked fine !
Tomcat and JBoss have their own implementations (customizations) to the Servlet API.
I would guess that JBoss does some pre processing on the URL and instead of / tries to resolve /index.html. This is probaly by default (in JBoss) and based on the welcome-file list defined in the web.xml (or to some default).
It's been a while that I worked with JBoss but maybe you can configure this processing somewhere. Historically JBoss used Tomcat as there provider and customized some the the Valves Tomcat uses (to extends/modify behavior).
Here's the scenario. I have a WAR that is connected to using SSL from an Eclipse RCP client using an X.509 certificate. After I verified that I have the certificate I want to retrieve the user details (ie. implement the loadUserByUsername) and read the userId from the request header (supplied by the client) and NOT use the supplied DN from the certificate. Is this possible? Essentially I trust the caller to supply me with the userId that I should use in my Spring security context.
My Spring configuration currently looks like this and works for the standard case of extracting the DN from the X.509 cert and loading the user.
<security:http>
<security:intercept-url pattern="/**" requires-channel="https" />
<security:x509 subject-principal-regex="^(.*?)$" />
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="myUserDetailsService" />
</security:authentication-manager>
I need this to support the scenario of PKI client (user1) -> PKI service A -> PKI service B (run service B as user1).*
I have a Spring web app, secured with Spring Security, running on EC2. In front of the EC2 instance is an Elastic Load Balancer with an SSL cert (https terminates at the load balancer ie. port 443 -> port 80), so from Tomcat's perspective, inbound requests are HTTP.
My login form submits to https, however the subsequent redirect goes to http (success or fail). The authentication was successful, and I can go back to https and I'm logged in.
My login configuration looks like so:
<security:form-login
default-target-url="/home"
login-page="/"
login-processing-url="/processlogin"
authentication-failure-url="/?login_error=1"/>
What do I need to change to make default-target-url and authentication-failure-url go to https?
Tomcat 6
Spring Security 3.0.x
Your spring configuration should be agnostic to the used protocol. If you use something like "requires-channel", you'll run into problems sooner or later, especially if you want to deploy the same application to a development environment without https.
Instead, consider to configure your tomcat properly. You can do this with RemoteIpValve. Depending on which headers the loadbalancer sends, your server.xml configuration needs to contain something like this:
<Valve
className="org.apache.catalina.valves.RemoteIpValve"
internalProxies=".*"
protocolHeader="X-Forwarded-Proto"
httpsServerPort="443"
/>
Spring will determine the absolute redirect address based on the ServletRequest, so change the httpsServerPort if you are using something else than 443:
The httpsServerPort is the port returned by
ServletRequest.getServerPort() when the protocolHeader indicates https
protocol
If it is a Spring Boot application (I use currently the 2.0.0 release), the following configuration within the application.properties file should be enough:
server.tomcat.protocol-header=x-forwarded-proto
This worked for me on AWS with an load balancer at the front.
For Spring Boot < 2.0.0 it should also work (not tested)
I had the same problem with Spring Boot behind Google Kubernetes. Adding these two lines to application.properties did it for me
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
Source: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-security.html#howto-enable-https
Solution was two fold
(1) application.yml
server:
use-forward-headers: true
(2) in servers /etc/apache2/sites-enabled/oow.com-le-ssl.conf
RequestHeader set X-Forwarded-Proto https
RequestHeader set X-Forwarded-Port 443
(2.1) and enabled the apache module with
sudo a2enmod headers
Put it together with the help of this and this
One way I got this working is by adding the following config
<http auto-config="true" use-expressions="true" entry-point-ref="authenticationEntryPoint" >
<form-login login-page="/login.jsf" authentication-failure-url="/login.jsf?login_error=t" always-use-default-target="true" default-target-url="xxxxx" />
<logout logout-url="/logout" logout-success-url="/logoutSuccess.jsf" />
...
</http>
Had to add always-use-default-target="true" and default-target-url="https://....". Not the ideal way as you need to hard code the url in the config.
I set requires-channel="any" on all intercept-urls. This allows it to still work in my dev environment where I don't use SSL.
<intercept-url pattern="/createUser" access="permitAll" requires-channel="any"/>
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" requires-channel="any"/>
<intercept-url pattern="/**" access="isAuthenticated()" requires-channel="any"/>
Then, create an apache virtual host that redirects all traffic to the HTTPS version.
<VirtualHost *:80>
ServerName www.mywebsite.com
Redirect permanent / https://www.mywebsite.com/
</VirtualHost>
I am also facing exactly same problem and till the time I get proper solution I am redirecting my requests from proxy server to tomcat server over AJP instead of HTTP.
Below is my apache configuration
ProxyPass /myproject ajp://localhost:8009/myproject
ProxyPassReverse /myproject ajp://localhost:8009/myproject
use below lines of code in web.xml
<security-constraint>
<web-resource-collection>
<web-resource-name>Login and Restricted Space URLs</web-resource-name>
<url-pattern>/j_security_check</url-pattern>
<url-pattern>/loginpage.rose</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
it makes forced to use HTTPS.
In my case, I had to REMOVE the property server.use-forward-headers=true.
This is my setup:
Digital Ocean LB --> Kubernetes cluster with Ingress --> Spring boot Application
Tried everything mentioned above for my k8's to spring boot application, problem was k8 was secured and ssl was handled by SSL accelerator sitting in front of ingress. The application only received http requests and spring security also forwarded to http, which was never found. The solution that worked for me:
nginx.ingress.kubernetes.io/proxy-redirect-from: http://$http_host/
nginx.ingress.kubernetes.io/proxy-redirect-to: https://$http_host/$namespace/helloworld-service/