How can I configure a client-cert + LDAP authentication in Elytron, considering the following details:
- the client-cert authentication must be done first and, only if it fails, should the user be presented with a login form that would authenticate him with LDAP
- alongside client-cert authentication, I need to use an internal lib that gets a specific field inside the x509 certificate that uniquely identifies the owner. This field is a specific OID one
# TrustStore
/subsystem=elytron/key-store=myTrustStore:add(path=server.truststore,relative-to=jboss.server.config.dir,type=JKS,credential-reference={clear-text=secret})
/subsystem=elytron/key-store=myTrustStore:import-certificate(alias=root-v5,path=root-v5.cer,credential-reference={clear-text=secret},trust-cacerts=true,validate=false,relative-to=jboss.server.config.dir)
## intermediate CA/subsystem=elytron/key-store=myTrustStore:import-certificate(alias=jus-v5,path=jus-v5.cer,credential-reference={clear-text=secret},trust-cacerts=true,validate=false,relative-to=jboss.server.config.dir)
## intermediate CA/subsystem=elytron/key-store=myTrustStore:import-certificate(alias=soluti-v5,path=soluti-v5.cer,credential-reference={clear-text=secret},trust-cacerts=true,validate=false,relative-to=jboss.server.config.dir)
/subsystem=elytron/key-store=myTrustStore:store()
# TrustManager
/subsystem=elytron/trust-manager=myTrustManager:add(key-store=myTrustStore)
# SSL Context
/subsystem=elytron/server-ssl-context=applicationSSC:write-attribute(name=trust-manager, value=myTrustManager)
/subsystem=elytron/server-ssl-context=applicationSSC:write-attribute(name=need-client-auth, value=true)
# (KeyStore) Realm
/subsystem=elytron/key-store-realm=myKeyStoreRealm:add(key-store=myTrustStore)
# Decoder
/subsystem=elytron/x500-attribute-principal-decoder=myDecoder:add(attribute-name=cn)
# Security Domain
/subsystem=elytron/security-domain=mySecurityDomain:add(default-realm=myKeyStoreRealm,permission-mapper=default-permission-mapper,principal-decoder=myDecoder,realms=[{realm=myKeyStoreRealm,role-decoder=groups-to-roles}])
# HTTP Authentication Factory
/subsystem=elytron/http-authentication-factory=myHttpAuthFactory:add(security-domain=mySecurityDomain,http-server-mechanism-factory=global,mechanism-configurations=[{mechanism-name=CLIENT_CERT}])
# Application Security Domain
/subsystem=undertow/application-security-domain=myAppSecurityDomain:add(http-authentication-factory=myHttpAuthFactory)
reload
--
You received this message because you are subscribed to the Google Groups "WildFly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wildfly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wildfly/a5903e56-cd75-4239-9a26-024da8d08484n%40googlegroups.com.
Hi Cameron,
I added my user using add-user:[$JBOSS_HOME\bin] add-user -a -u <number-part-of-my-CN> -p '<my-password>' -g JBossAdminAs the username has some restrictions and I still don't know how to get a specific OID attribute from the digital certificate, I chose a CN substring that matches the number part of my identification. For that, I had to use regex-principal-transformer and chained-principal-transformer:/subsystem=elytron/regex-principal-transformer=myRegexTransformer:add(pattern=".*:([0-9]+)", replacement="$1")/subsystem=elytron/chained-principal-transformer=myChainedTransformer:add(principal-transformers=[QuickstartDecoder, myRegexTransformer])And make the changes to the Security Domain, setting myChainedTransformer and unsetting principal-decoder:/subsystem=elytron/security-domain=QuickstartDomain:write-attribute(name=pre-realm-principal-transformer,value=myChainedTransformer)/subsystem=elytron/security-domain=QuickstartDomain:write-attribute(name=principal-decoder,value=undefined)Besides that, I had to import my certificate into the server truststore under <number-part-of-my-CN> alias. After theses steps, I've accessed the application.But as I said before, I can't add the users to the truststore (and to the application-users), because we rely on the client certificate being signed by a list of CAs certificates present in the server truststore. That is, if the user has a certificate signed by a CA certificate present in the server truststore, that user should be authenticated (of course, besides all the TLS communication that happens and certificate validations). How can I setup this configuration on Wildfly/Elytron?Best,Benito.Em sexta-feira, 24 de março de 2023 às 14:31:07 UTC-3, Benito Coelho escreveu:Hello Cameron,Maybe I didn't express myself well, sorry about that, but I managed to make the quickstart example work. When I changed the example to fit our needs it broke. I can try to add my user as you suggested just to see if it works, but in our case we don't have client certificates stored, only the CA's certificates in the server truststore. So, I can't add every user. We rely on the client certificate being signed by the CAs that we put in the server truststore. Probably the configuration I've made is still lacking something.Thank you once more for your support.Best,Benito
Hi Cameron, thanks again. I've decided to give you a quick feedback to show what I've done, while I analyse your answer.So, before your answer I've tried the following configuration, based on [1], using constant-principal-transformer, and succeeded on authentication and authorization:<constant-principal-transformer name="toTest" constant="test"/><http-authentication-factory name="clientCertAuth" security-domain="ApplicationDomain" http-server-mechanism-factory="configuredCert"><mechanism-configuration><mechanism mechanism-name="CLIENT_CERT" final-principal-transformer="toTest"/></mechanism-configuration></http-authentication-factory><application-security-domains><application-security-domain name="other" http-authentication-factory="clientCertAuth" override-deployment-config="true"/></application-security-domains><server name="default-server"><http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/><https-listener name="https" socket-binding="https" ssl-context="ssl-context-server.keystore" enable-http2="true"/><host name="default-host" alias="localhost"><location name="/" handler="welcome-content"/><http-invoker http-authentication-factory="application-http-authentication"/></host></server><tls><key-stores><key-store name="applicationKS"><credential-reference clear-text="password"/><implementation type="JKS"/><file path="application.keystore" relative-to="jboss.server.config.dir"/></key-store><key-store name="serverTS"><credential-reference clear-text="truststorepass"/><implementation type="JKS"/><file path="server.truststore" relative-to="jboss.server.config.dir"/></key-store><key-store name="server.keystore"><credential-reference clear-text="keystorepass"/><implementation type="JKS"/><file path="server.keystore" relative-to="jboss.server.config.dir"/></key-store></key-stores><key-managers><key-manager name="key-manager-server.keystore" key-store="server.keystore"><credential-reference clear-text="keystorepass"/></key-manager></key-managers><trust-managers><trust-manager name="key-manager-serverTS" key-store="serverTS"/></trust-managers><server-ssl-contexts><server-ssl-context name="applicationSSC" key-manager="applicationKM"/><server-ssl-context name="ssl-context-server.keystore" cipher-suite-filter="DEFAULT" protocols="TLSv1.2" want-client-auth="true" need-client-auth="true"authentication-optional="true" use-cipher-suites-order="false" key-manager="key-manager-server.keystore" trust-manager="key-manager-serverTS"/></server-ssl-contexts></tls>File application-users.properties content:testFile application-roles.properties content:test=UsersBut the principal value was the DN and I need the OID 2.16.76.1.3.1 value. I tried to use the OID value in the principal decoder, but it didn't work. I guess the reason is what you said about SAN decoder support.<x500-attribute-principal-decoder name="myDecoder" oid=" 2.16.76.1.3.1" maximum-segments="1"/>In the server.log part below, logger org.wildfly.security, it's possible to see that the OID is there, but somehow I couldn't access it.[8]: ObjectId: 2.5.29.17 Criticality=falseSubjectAlternativeName [RFC822Name: nitoc...@gmail.comOther-Name: Unrecognized ObjectIdentifier: 1.3.6.1.4.1.311.20.2.3Other-Name: Unrecognized ObjectIdentifier: 2.16.76.1.3.1Other-Name: Unrecognized ObjectIdentifier: 2.16.76.1.3.6Other-Name: Unrecognized ObjectIdentifier: 2.16.76.1.3.5]In our application that runs in the production environment and that is being replaced, we had to override the Seam Identity part to get the x509 certificate from the http request and use an internal library to extract the OID 2.16.76.1.3.1 value. I hope to do something like that with the Gist you sent.Best,Benito.On Thu, Mar 30, 2023 at 11:31 PM Cameron Rodriguez <caro...@redhat.com> wrote:Hey Benito, sorry for the confusion. I've talked with some of the other engineers to help clear things up for me.
PS: I didn't understand what evidence decoders are used for.
Evidence decoders select a section of the `Evidence` (an Elytron interface for items used in validation, like certificates) to operate on. You can use a principal decoder on "evidence" to select a principal.For example, an `x500-subject-evidence-decoder`[1] would retrieve the first certificate's subject from a certificate chain. You could then follow up with `x500-attribute-principal-decoder`[2] to select an attribute in that subject to use as the principal. Each of these would be associated with the security domain.
Is it possible not to have a configured realm?
Yes, instead of the filesystem realm, you can associate a `constant-role-mapper`[3], so that any authenticated users will automatically receive the authorization roles needed. It's also possible to use to set roles based on the principal, but that's a bit more complex.
That is, the user could be authenticated by the mutual TLS, based on the CAs truststore, and the principal should be the OID 2.16.76.1.3.1 value?
It seems that OID is part of the Subject Alternative Name, though correct me if I'm wrong. Elytron's SAN decoder doesn't support the OtherName attribute, so a custom EvidenceDecoder implementation would be needed.I've made a Gist with an example CLI file and custom class. You'll need to package the custom class into a JAR and load it as a server module, described in Custom Components.[4]Let me know if you have any questions.Best,[1]: https://docs.wildfly.org/27/wildscribe/subsystem/elytron/x500-subject-evidence-decoder/index.html[2]: https://docs.wildfly.org/27/wildscribe/subsystem/elytron/x500-attribute-principal-decoder/index.html
TRACE [org.wildfly.security] (default task-1) Handling AvailableRealmsCallback: realms = [ldap-realm]DEBUG [org.wildfly.security.http.password] (default task-1) Username authentication. Realm: [ldap-realm], Username: [myuser].TRACE [org.wildfly.security] (default task-1) Handling RealmCallback: selected = [ldap-realm]TRACE [org.wildfly.security] (default task-1) Handling NameCallback: authenticationName = myuser(...)
TRACE [org.wildfly.security] (default task-1) Principal assigning: [myuser], pre-realm rewritten: [myuser], realm name: [ApplicationRealm], post-realm rewritten: [myuser ], realm rewritten: [myuser ]TRACE [org.wildfly.security] (default task-1) PropertiesRealm: identity [myuser] does not existDEBUG [org.wildfly.security.http.basic] (default task-1) User myuser authentication failed.
/subsystem=elytron/ldap-realm=ldap-realm:read-identity(identity=myuser)Hey Cameron,I've made some progress and finally succeeded in extracting the value relative to the OID 2.16.76.1.3.1 from the digital certificate. I followed your suggestion and packaged a custom class in custom-wf-decoder.jar that depends on an internal lib (that deals with digital certificates). I struggled to make the internal lib available to the custom-wf-decoder.jar. I don't know if it's a good practice, but I packaged the internal lib with its dependencies in a .jar and set it in the <resource-root> inside the <resources> tag, in the module .xml definition I added.
Next I successfully configured a LDAP realm separate from the client-cert configuration, using BASIC auth-method in web.xml.At this moment, I'm trying to integrate the client-cert and LDAP mechanisms, making the latter the fallback mechanism. The client-cert part is working, but when I skip the client-cert (by not choosing any certificate offered by the browser), the LDAP credentials are asked, as expected, but as I see in server.log the Properties Realm ("ApplicationRealm") is incorrectly used instead of the LDAP Realm ("ldap-realm") and as so I can't authenticate using LDAP (because the used realm is wrong):
Yep, no issues with that configuration. If you need to use that library elsewhere, you can package it into a separate module, and then list it as <module name="com.company.app.internal-lib-module-name"> within the <dependency> section of the module.xml. More info on that is in the JBoss Modules manual: https://jboss-modules.github.io/jboss-modules/manual/
You'll need to make the LDAP realm the default for the security domain, and (optionally) remove the ApplicationRealm. Here are the CLI commands to do that:
/subsystem=elytron/security-domain=ApplicationDomain:write-attribute(name=default-realm,value=ldap-realm)
# Remove following line to keep ApplicationRealm
/subsystem=elytron/security-domain=ApplicationDomain:list-remove(name=realms,index=1)
reload
and the resulting configuration should look like this:
<security-domains>
<security-domain name="ApplicationDomain" default-realm="ldap-realm" permission-mapper="default-permission-mapper"
pre-realm-principal-transformer="principalDecoder" evidence-decoder="customDecoder">
<realm name="ldap-realm" role-decoder="from-roles-attribute" />
</security-domain>
</security-domains>
- In the JBoss CLI, check `/subsystem=elytron/key-store=server-trust-store-name:read-aliases()` to make sure the identities were loaded correctly
- Check if helloworld-mutual-ssl-secured[1] works on a fresh WildFly copy, without modification. If not, the issue is likely on your end and I might not be able to help much.
$ {jbossHomeName}/bin/add-user.sh -a -u quickstartUser -p 'quickstartPwd1!' -g JBossAdmin
--
You received this message because you are subscribed to a topic in the Google Groups "WildFly" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/wildfly/J4L5NPylHd4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to wildfly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wildfly/CAD2gQZo%3D652JegXOj5D03tyJ_Zu-Fd6u3bCqiJYKKb%2BVw%2B6pHg%40mail.gmail.com.
Apparently, the helloworld-mutual-ssl-secured example is missing the role configuration (in link https://github.com/wildfly/quickstart/blob/main/shared-doc/add-application-user.adoc). I guess it should be:$ {jbossHomeName}/bin/add-user.sh -a -u quickstartUser -p 'quickstartPwd1!' -g JBossAdmin
After that modification, the example worked out. Next, I checked the server.log file and I didn't find the certificate_unknown error. Then I changed the root logger level to DEBUG (because that level is the one I've been using):<root-logger><level name="DEBUG"/><handlers><handler name="CONSOLE"/><handler name="FILE"/></handlers></root-logger>And both certificate_unknown and java.nio.channels.ClosedChannelException errors were there in the server.log. As I was used to setting the DEBUG level for the root logger (and -Djavax.net.debug=ssl:handshake as JVM parameters), these exceptions were always there. So, why won't they show in default log level (INFO)? Are they expected and I don't have to deal with them?
After taking a look through the logs[1], the errors don't seem to be significant. It seems that the server rejects a few certificates and then accepts one, and X509 decoding appears to run properly from there on. I'm not sure what those errors are supposed to represent (a side-effect of multithreading?), so I'll check if someone else on the team is more familiar with it. In any case, I don't think they indicate larger issues, and likely would've been raised to WARN or a higher level if it affected the authentication process.
Otherwise, do you run into any failures when authenticating with the client-cert + LDAP fallback? If you're able to use it, you should be good to go! Otherwise, WARN or higher level logs from either `org.wildfly.security.*` or `io.undertow.*` would be things to look for. Let me know if you run into any issues while testing.
Everything was working fine until we deployed the application into an environment that has a reverse proxy. The client-cert part stopped working and we think that it's related to the way Wildfly gets the client certificate. In this environment, the reverse proxy (HAProxy) passes the client certificate through the "X-Client-Certificate" request header to the Wildfly (and to the application). Is there a way to configure Elytron where to look for the client certificate (in this case, in a request header), besides the default "place" which I guess is a request attribute?
I haven't been able to verify this myself, but you should be able to configure Undertow to use the Proxy Protocol v1[1]:/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=proxy-protocol,value=true)If that doesn't work, you can also try enabling support for standard `X-Forwarded-To` Headers:/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=proxy-address-forwarding,value=true)
Hey Cameron,I haven't been able to verify this myself, but you should be able to configure Undertow to use the Proxy Protocol v1[1]:/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=proxy-protocol,value=true)If that doesn't work, you can also try enabling support for standard `X-Forwarded-To` Headers:/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=proxy-address-forwarding,value=true)I've tried both, separately, but without success. Maybe the custom evidence decoder doesn't look for the client certificate inside those "X-Client..." headers. Is there a way for me to look for the client certificate myself, through the HttpServletRequest or something similar, using the Evidence Decoder or another component?Thanks again!
Just wanted to get some clarification, is the TLS handshake (not the client cert authentication) being made with the reverse proxy, or with WildFly itself? The CLIENT_CERT mechanism retrieves the certificate from Java's TLS session information, so if the connection is not with the server, then a different mechanism needs to be used.
Haven't looked at all of the details here but just wanted to quickly note that if the client cert is being verified externally, we also have the External HTTP mechanism that just relies on HttpServerRequest#getRemoteUser().
In case that helps at all, more details about the External mechanism can be found here.
--
You received this message because you are subscribed to a topic in the Google Groups "WildFly" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/wildfly/J4L5NPylHd4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to wildfly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wildfly/578de6af-2414-41ba-a7b5-b1547f1dbad6n%40googlegroups.com.
Hey Farah,Haven't looked at all of the details here but just wanted to quickly note that if the client cert is being verified externally, we also have the External HTTP mechanism that just relies on HttpServerRequest#getRemoteUser().
In case that helps at all, more details about the External mechanism can be found here.The External HTTP mechanism relies on AJP? If so, I think the reverse proxy we use (HAProxy) doesn't support it. Besides that, we used some specific wildfly components, like custom evidence decoders to extract specific information from the client certificate, and I really don't know if they would still work.
I was wondering if there is a way to make the wildfly consider the "X-Client..." header instead of the standard request attributes to get the client certificate (supposing that that is how it works). Or should we try another approach?