Saturday, December 16. 2017
Adding javaee security to wildfly
This entry is the second part of the certificate security in wildfly series. The previous post was about setting a TLS mutual authentication in a jax-ws application, but without any javaee integration. The client had to send the certificate (and revoked certificates were not admitted at TLS level) but inside the java application no principal was assigned. This continuation entry is going to add this integration. In wildfly it is mainly a matter of configuration.
The new elytron subsystem is very convoluted and you need a lot of steps to add a standard CLIENT-CERT login. The following points were needed in my case:
The certificate login continues to be based in a certificate store. I am not very happy with this feature, I think that any certificate that is valid (dates, revocation and so on) should be directly passed. But default implementation also checks that the certificate is in a user key-store. So another users.jks was created with the client certificates:
keytool -import -keystore users.jks -file /etc/pki/client1.pem -alias "CN=SampleClient1,O=demo.kvm,C=ES" keytool -import -keystore users.jks -file /etc/pki/client2.pem -alias "CN=SampleClient2,O=demo.kvm,C=ES"
And finally the new store is added to the configuration:
/subsystem=elytron/key-store=users:add(type=jks, relative-to=jboss.server.config.dir, path=users.jks, credential-reference={clear-text=Kiosko_00})
Please note that the certificate alias should be the user name. (The full subject is used. As I comment in a later step, the CN decoder is not working for me, maybe with a working decoder the alias should be different. I do not really know.)
With the users key-store a certificate realm is created:
/subsystem=elytron/key-store-realm=users-realm:add(key-store=users)
I wanted to use a CN decoder (the logged username would be the common-name part of the subject) but it does not work. The openssl defaulted to UTF-8 strings and the default decoder just manages IA5 and Printable strings (I do not know why). So the decoder does not work with my certificates.
/subsystem=elytron/x500-attribute-principal-decoder=cn-decoder:add(oid="2.5.4.3", maximum-segments=1)
Finally a role decoder is used to auto-assign a Users role to the certificate.
/subsystem=elytron/constant-role-mapper=users-roles:add(roles=[Users])
With all the previous parts the security domain is created:
/subsystem=elytron/security-domain=certificate-domain:add(principal-decoder=cn-decoder, role-mapper=users-roles, realms=[{realm=users-realm}], default-realm=users-realm, permission-mapper=default-permission-mapper)
Now the security domain should be assigned to the web subsystem (undertow) using an authentication factory:
/subsystem=elytron/http-authentication-factory=certificate-auth-fact:add(http-server-mechanism-factory=global, security-domain=certificate-domain, mechanism-configurations=[{mechanism-name=CLIENT_CERT,mechanism-realm-configurations=[{realm-name=certificate-sec-domain}]}]) /subsystem=undertow/application-security-domain=certificate-sec-domain:add(http-authentication-factory=certificate-auth-fact)
And finally because the WS endpoint is an EJB the security domain is also assigned to the ejb3 subsystem (indeed this part is not needed):
/subsystem=ejb3/application-security-domain=certificate-sec-domain:add(security-domain=certificate-domain)
Now the server is ready for certificate login. The elytron subsystem can be configured to have a second method (BASIC for example) but more steps are needed. You can also configure an aggregate realm to assign roles using a second provider for roles (the key-store realm would validate the principal and a second one, ldap for example, would add the roles, instead of my fixed Users role used in this entry). But, as I said before, the configuration is very convoluted in my opinion. Besides I do not see any direct translation for the typical stacking of login modules of the previous and deprecated security system.
Finally the sample application should be modified to include the new certificate configuration. So the web.xml is modified to request CLIENT-CERT. (As the endpoint is an EJB the same solution can also be achieved using the jboss-ejb3.xml.)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<security-constraint>
<web-resource-collection>
<web-resource-name>Protect all application</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
<auth-constraint>
<role-name>Users</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>certificate-sec-domain</realm-name>
</login-config>
<security-role>
<description>Role required to log in to the Application</description>
<role-name>Users</role-name>
</security-role>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
And the jboss-web.xml is used to mark the certificate-sec-domain as the application security domain to use.
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>certificate-sec-domain</security-domain>
</jboss-web>
The WS endpoint is slightly modified to only let its use to the Users role. The RolesAllowed annotation is added.
@Stateless
@WebService(name = "echo",
targetNamespace = "http://es.rickyepoderi.sample/ws",
serviceName = "echo-service")
@DeclareRoles("Users")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class Echo {
@Resource SessionContext ctx;
@WebMethod
@RolesAllowed("Users")
public String echo(String input) {
return ctx.getCallerPrincipal() + " -> " + input;
}
}
And here it is the video. It is just the execution of the same WS client presented before using the valid client1.p12 key-store. The new thing is that now the endpoint returns the subject of the certificate as the logged user. Therefore, everything is working.
This second entry in the series integrates the previous solution into the javaee security framework. The certificate is validated using the key-store realm which checks that the certificate is inside the configured store. All the previous work is still in place (TLS mutual authentication ensures the certificate is valid and not revoked) but then the realm authenticates the user into the application. This way the WS endpoint can be configured with security and the caller is assigned properly. The new version of the application can be downloaded from here.
Comments