Here is my problem:
Context :
-Windows Server 2012 with ActiveDirectory
-Tomcat
-Rest API (Spring)
I'm currently trying to restrict REST request. I want that only specific groups of the AD could access to specific resources. I'm restricted to Kerberos authentication.
System configuration
Create a user in domain "Tomcat"
setspn -a HTTP/apirest.domain#DOMAIN
Generate a tomcat.keytab using ktpass
API rest configuration
I'm using the spring security sample on github that you can find here :
https://github.com/spring-projects/spring-security-kerberos/tree/master/spring-security-kerberos-samples/sec-server-win-auth
I know that there is an EntryPoint and this is not needed in my context (API Rest). I've chosen this sample because it seems to use the windows authentication context and use it to automatically authenticate me in the spring security context. Right after, an ldap request is send to extract all information about the user logged. In my case, I need to extract the group.
I'm also using :
https://github.com/GyllingSW/kerberos-demo
To extract the role of the user with the class "RoleStrippingLdapUserDetailsMapper.java" instead of the "ActiveDirectoryLdapAuthoritiesPopulator". This implementation also offers localhost authentication but the issue with the NTLM token seems to be fixed in last commit of spring security.
I'm not really sure if this is the right way to do what I want.
My authentication seems to fail and I only have one things going wrong in my logs..
"Property 'userDn' not set - anonymous context will be used for read-write operations"
Questions
Do I have to run my tomcat service using the tomcat account ? (Seems to be, yes)
Am I doing the right things with Kerberos security ?
How can I get rid of the anonymous context?
The anonymous context seems to be set just right after Tomcat start. I want to get a context just after that my user (For instance, user1) requests the rest API (EntryPoint or whatever)
If there is something unclear let me know, I will try to reformulate!
Thanks,
You do not need to query LDAP to get information about which groups does user belong to. Active Directory already adds this information to the Kerberos ticket which is sent from browser to Tomcat.
You just need to extract this information from the token for example using Kerb4J library. It comes with Spring integration inspired by spring-security-kerberos project so it should be pretty easy to switch to it.
If you still want to query LDAP you need to authenticate in LDAP before you can make any queries. Again there's no need to use end-user accounts for it - you can use the keytab file for Kerberos authentication in LDAP and query groups using "Tomcat" account
I found a way to fix my issue.
In a REST API context, you have no entry point. I tried to set my entry point to an unmapped URL, just to do the negociation. By doing this, you will receive an HTTP response with the error code 404 (Not found) but with the right header was added by spring security (WWW-Authenticate).
The web browser will not send the ticket service if the error code is not 401.
To solve this problem, you have to create a CustomEntryPoint class (implements AuthenticationEntryPoint) and you need to override the "commence" method to return a 401 HTTP code with the right header.
I hope that could help. If there is a better way, let me know !
Related
I 'm trying to create a secure spring rest api using pre-authentication security for siteminder.
I 've tried a solution where I 'm getting SM_USER and when I tested it in postman by adding new header SM_USER with random value it seems to work fine.
If you don't provide header I'm getting an error org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException: SM_USER header not found in request which is valid.
But how can I be sure that this is secure? if someone knows the URL of my rest api could call this without problem. Should I check something else in spring or only siteminder offers user authentication?
The front-end SiteMinder web agent is the guaranty that the session is valid - you must make sure via server/network configuration that your application cannot be accessed directly without first passing through the SiteMinder web agent.
Also, SiteMinder asserts multiple headers. SM_USER should not be used alone because it can be asserted by the web agent in some circumstances when the user does not actually have a valid session. Instead, you should first look for the existence (non-blank) of SM_SERVERSESSIONID, which only exists if the session is valid.
Lastly, I generally try to avoid SM_USER at all - because SM_USER is actually not a user attribute at all, but rather is "the login identifier used for authentication". If SiteMinder authenticates users via federation (e.g. SAML) or x509 authentication, SM_USER will be rather different than if a login form was used. Instead, its better in SiteMinder to set a "universal id" that is a user attribute, and appears in the headers as SM_UNIVERSALID. Your SiteMinder administrators will know how to do this (and may already have - look to see if you have a SM_UNIVERSALID header available already).
One other caution, in some SiteMinder configurations, the underscore will not be in the header name (use of the underscore is called "legacy" header mode in SiteMinder), so you might want to make your app configurable with respect to the header names, e.g. SMSERVERSESSIONID, SMUSER, SMUNIVERSALID etc.
If you want to programmatically re-validate a session, you can use the SiteMinder Agent API or REST API, or look at my company's product "SSO/Rest" which provides a comprehensive set of uniform REST interfaces to SiteMinder and also other SSO providers (http://www.idfconnect.com).
HTH!
-Richard
I’ve got a question which seems popular, but I couldn’t find the answer. Well there’s a lot of information about it but I’m not sure what the best way is. So here’s the scenario.
We have a Single Page Application (SPA) and a RESTful Web Service (API). We use an external authentication/authorization service provider via OAuth2/JWT. But I need to persist the user ID (provided by the external authentication provider) on the database on the server side after successful login. And also I need to enrich the Authentication/Principal object in security context after successful login (for example by adding email).
There's a lot on the web about this scenario. But we have SDK for authentication/authorization already and it works perfectly (no custom code, etc). I just need to add something to the authentication object. What is the correct way to do it? Thanks.
For the record, this is what we did:
As I said there's already a SDK doing all the heavy lifting of authentication mechanics. We just need to enrich the authentication object after successful authentication. So we wrapped the AuthenticationProvider (implemented in the SDK) in our implementation (inspired by PreAuthenticatedAuthenticationProvider) and after successful authentication, we enriched the result using our UserDetails implementation (inspired by PreAuthenticatedGrantedAuthoritiesUserDetailsService). The rest was straight forward.
PS: please let me know if you don't like the idea.
TL;DR
Objective: managing api permissions:
OIDC authorization direct grant flow
User federation and authentication source : LDAP
Permissions store : legacy database
Client management and authentication: Keycloak
Question: What are the best practices for managing user permissions on Keycloak and rest api?
Context
We are implementing a rest API with spring to be used by a mobile application and an SPA. Our users accounts, permissions, rules… and all data are stored in a custom database used by different monolithic applications. To secure our api we have decided to use Keycloak.
The keycloak server is configured with an existing LDAP for user federation and ‘Direct grand flow’ for the mobile client application. For the first use case (authentication) everything is working fine.
Now we have to manage users permissions as follow :
The client applications should know user permissions to display/hide functionalities
The api should be able to validate user permissions to use different endpoints
Users permissions are based on some rules in the database and change frequently
In my understanding keycloak can handle authorization and fine grained permissions using hardcoded or user based policies but can’t be plugged to a different authorization source natively. As a consequence, I thought of building a custom role mapper using Keycloak SPI, retrieve user permissions from a custom api that I will develop, then map them to the access token.
As a result, my access token should look like:
"resource_access": {
“My-client”: {
“permissions”: [
“Show-products”,
“Buy-something”,
“Display-prices”
]
}
},
"username": “myUser”
Then the mobile application should be able to know user permissions based on the token, and my stateless server side (API) should be able to access user permissions on every call to check them using spring annotation :
#PreAuthorize("hasRole('Show-products')")
Problem
After first experimenting my solution seem to work fine, but I still have some security concerns about this choice since it’s out of the keycloak standard and includes rest calls to a different backend inside keycloak mappers.
So I was wondering :
Is it secure to put user permissions on the access token claims?
How to secure keycloak access to an external system (rest calls) to
retrieve permissions?
Should I rely on token claims to verify user permissions on each
request in my resource server?
Is there any other clean solution / best practices to handle user
permissions from external source in keycloak ?
Complimentary Informations
I’m using :
Springboot 1.5.13.RELEASE
Keycloak-adapter-bom 3.4.3.Final
Standalone keycloak server 3.4.3.Final
regarding your questions:
- Is it secure to put user permissions on the access token claims?
Yes, capabilities can (and should be) on the access-token, and with that you can take some decisions in your business layer (based on the roles/access claims). Remember nevertheless that a token is only base 64 encoded, and could be copied by other person and looked into, so it shouldn't contain secret or particularly confidential information, usually you put there enough info regarding the user, and some of its current permissions/capabilities/claims.
How to secure keycloak access to an external system (rest calls) to retrieve permissions?
It depends if it needs to be accessed from outside your network. If not, you can leave it unprotected (and unavailbale from outside/or only available for some specific IPs). If it is going to be available from outside/or you want to protect it with keycloack anyway, you can have either a "confidential" or a "bearer only" type of client. I'd suggest you to look into CORS and token sharing, so that you can reuse your already created "access-token" for your other endpoints without the need to authenticate again.
Should I rely on token claims to verify user permissions on each request in my resource server?
Not exactly sure what you mean. In keycloak the resource server isn't doing extra resource authorization like in a typical oAuth2 dance (unless your policy enforcer is activated but I believe you didn't go with this approach, but rather a mapper SPI #auth server for getting your roles right?)
In oAuth2 the "resource server" has 2 responsibilities: 1-providing the resource and 2-doing an extra authorization step. In the keycloak world those 2 steps are done by different actors. Step 1 is done by your application, and step2 is only done when policy enforcing is activated by keycloak also (that means Keycloak is the auth server and also part of the "resource server" from the oAuth2 perspective)
Now back to your question, if by resource server you just meant your application providing the content, then yes you can use the claims there, remember that the claims (and the whole access-token) was generated and digitaly-signed by the auth server, so you can use those claims in your app with no problem (and wouldn't know how to do it otherwise either).
Is there any other clean solution / best practices to handle user permissions from external source in keycloak ?
Hard to say, as you probably noticed; documentation in the web for your specific usecase is very limited; so not a lot of work of best practices exist there, you only real alternative would have been using policies with a custom Policy SPI, and that would have brought in other challenges. I'd say your solution is fine.
Best regards.
I am using Spring-Boot-Admin in order to monitor a SpringBoot app whose actuator endpoints are secured using basic auth. The required credentials are transferred to Spring-Boot-Admin (SBA) like described in the documentation. SBA itself is also secured using spring-boot-admin-server-ui-login and the provided SecurityConfiguration (based on the documentation & sample apps, see Github Repo for code).
Both the app to be monitored and SBA are deployed via docker.
Logging in to SBA works fine and I can see the application state as well as the health results. For some content I see a nested login mask though. When I click on "Logging" or "JMX" I am redirected to the login mask:
In the browsers network tab I can see that a 401 is returned for the /jolokia endpoint. All requests after that seem to be forwarded to the login page.
I have the following questions:
Why am I logged out if one request to the application fails? Is that a bug?
What is the source of the 401? SBA or my app? I know that SBA proxies requests to the app. According to the access logs from my app no request to /jolokia is done when I login to SBA. Does this mean that the 401 is returned by SBA directly? Its logs contain nothing of relevance though.
Accessing the /jolokia endpoint directly works fine. It even works when I use the same (proxied) URL that SBA uses (e.g. http://XXX:8090/api/applications/XXX/jolokia/). What is different when this is executed from within SBA?
I've tried to find more error details in SBA but so far failed to find the proper logging options. They either contain nothing relevant or way too much information (e.g. Spring Security) that doesn't seem to be relevant. Logging the full response would probably help...
Edit: I just realized, that the request to the /jolokia endpoint actually contains a different cookie (Cookie:JSESSIONID=4E51B84AE15A6890500F967B23EB92AC) than the requests to the working endpoints (e.g. /metrics). Thats weird, but probably explains why the /jolokia endpoint returns a 401. Now the question is: Why does it send a different cookie`?
I tried various things, but in the end couldn't solve this.
I instead ended up with a different configuration: No security at all for the endpoints (management.security.enabled=false), but exposing them on a different port (management.port=8081). This management endpoint is blocked for external access to the system completely.
With that, SBA behaves nicely and the application is still secure. In the end, its a much simpler setup which is good, too.
I had a similar problem.
I could access all actuator endpoints, but everything with /jolokia would not be authorized. Although my security setup is a different to yours, i assume the problem is the same.
When i digged into this I found out that jolokia runs in its own servlet separately from the spring boot application. Maybe therefore you have different sessions!?
My Security Config tries to match this expression
.mvcMatchers("/actuator/**").access("hasAnyRole('ADMIN','SBA')"),
but the SBA user role was not able to access Jolokia. It always matched the last fallback-rule where only Admin was allowed.
In my case the context- and servlet-path of the jolokia servlet would be stripped away from the request path before trying to match it to my security config. Then it would only match "/list" against my MVC matchers instead of "/actuator/jolokia/list" which would then not fit as expected.
Unfortunately I do not have a fix for your problem. For my case i added a requestMatchers rule which is a little bit less opinionated and would still match:
.requestMatchers(request -> request.getRequestURI().contains("/actuator/jolokia/")).access("hasAnyRole('ADMIN','SBA')")
instead of
.mvcMatchers("/actuator/**").access("hasAnyRole('ADMIN','SBA')")
So we have an authentication server where the UI application gets the access token and then it communicate with API server, it's all good. Now we are building a third application which needs SSO to authenticate the same user and that is fine too.
However, there are scenarios where this third application needs to use some resources on the API server which, from my understanding, we need to get a token from auth server using client-id/secret and then send the request with the access token. This seems ok too, however I am not sure how API server is going to authorise that token (a hint on this would be great).
But the main problem is we want this request to be sent on behalf of the user. This is because API server needs to audit all user's activities. How can we achieve this using Spring Boot/OAuth2 and JWT Token?
I went through documentation and I know about #EnableOauth2Sso #EnableAuthorisationServer etc. but this scenario is not clear and I'm not even sure it's been implemented in Spring or not.
If there is no implementation for this scenario, what do you recommend? Any experience you have had on this, can you please share?
Your API server plays the role of a Resource Server. There is an annotation designed for that purpose: #EnableResourceServer. Your client app then will consume this resource using the handy OAuth2RestTemplate.
There are two approaches to properly configure the Resource Server and get this working:
Have the public key directly in your resource server app: this way when the client app try to use a token provided by the authorization server to get a resource from the Resource Server, this will verify if the token is valid by itself.
Configure the resource server to ask the authorization server if a given access token is valid and depending of the response it will allow or decline to get the resource.
I have posted a sample app on github using the first approach. There you can see the interaction between the Authorization Server, the Client and the Resource Server, as well as all the configurations you need for this implementation. Hope it helps you.