Wildfly / Elytron and jdbc-realm with digest

340 views Asked by At

I am migrating an application from Wildfly 16 to 30 and have a hard time finding the right security configuration.

What I have in Wildfly 16: (1) a jdbc-based authentication with base64'ed SHA-256 passwords / authorization like this:

                <security-domain name="j-lawyer-security" cache-type="default">
                    <authentication>
                        <login-module code="Database" flag="required">
                            <module-option name="dsJndiName" value="java:/jlawyerdb"/>
                            <module-option name="principalsQuery" value="select password from security_users where principalId=?"/>
                            <module-option name="rolesQuery" value="select role, 'Roles' from security_roles where principalId=?"/>
                            <module-option name="unauthenticatedIdentity" value="anonymous"/>
                            <module-option name="hashAlgorithm" value="SHA-256"/>
                            <module-option name="hashEncoding" value="base64"/>
                            <module-option name="hashUserPassword" value="true"/>
                            <module-option name="hashStorePassword" value="false"/>
                        </login-module>
                    </authentication>
                </security-domain>
            </security-domains>

The application code refers to this security domain.

(2) a remote EJB client that looks like this:

Properties properties = new Properties();
            properties.put("jboss.naming.client.ejb.context", true);

            // begin: for JMS only
            properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
            properties.put(Context.PROVIDER_URL, "http-remoting://localhost:8080");
            properties.put(Context.SECURITY_PRINCIPAL, "admin");
            properties.put(Context.SECURITY_CREDENTIALS, pwString);
            properties.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
            properties.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
            properties.put("jboss.naming.client.connect.options.org.xnio.Options.SSL_ENABLED", "false");
            properties.put("jboss.naming.client.connect.options.org.xnio.Options.SSL_STARTTLS", "false");

            InitialContext ic = new InitialContext(properties);
            SecurityServiceRemote remote = (SecurityServiceRemote) ic.lookup("ejb:j-lawyer-server/j-lawyer-server-ejb//SecurityService!com.jdimension.jlawyer.services.SecurityServiceRemote");
            remote.dummy();

(3) an REST API that uses HTTP basis authentication

Both the EJB client and HTTP BASIC work well together.

Wildfly 30

Now, in Wildfly 30, I am struggling to set up Elytron to achieve the same thing.

What I have in Wildfly standalone.xml:

(1) I changed the security realms to support database-based auth:

            <security-realms>
                <identity-realm name="local" identity="$local"/>
                <jdbc-realm name="ApplicationRealm">
                    <principal-query sql="select password from security_users where principalId=?" data-source="somedb">
                        <clear-password-mapper password-index="1"/>
                    </principal-query>
                    <principal-query sql="select role, 'Roles' from security_roles where principalId=?" data-source="jlawyerdb">
                        <attribute-mapping>
                            <attribute to="Roles" index="1"/>
                        </attribute-mapping>
                    </principal-query>
                </jdbc-realm>
            </security-realms>

Within the Elytron module, I have

            <http>
                <http-authentication-factory name="application-http-authentication" security-domain="ApplicationDomain" http-server-mechanism-factory="global">
                    <mechanism-configuration>
                        <mechanism mechanism-name="BASIC">
                            <mechanism-realm realm-name="ApplicationRealm"/>
                        </mechanism>
                    </mechanism-configuration>
                </http-authentication-factory>
                <provider-http-server-mechanism-factory name="global"/>
            </http>
            <sasl>
                <sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
                    <mechanism-configuration>
                        <mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
                        <mechanism mechanism-name="DIGEST-SHA-256">
                            <mechanism-realm realm-name="ApplicationRealm"/>
                        </mechanism>
                    </mechanism-configuration>
                </sasl-authentication-factory>

In the undertow module:

            <application-security-domains>
                <application-security-domain name="other" security-domain="ApplicationDomain"/>
            </application-security-domains>

For the remote EJB client, the lookup code successfully performs a lookup, but when calling some business logic it fails. Strangely, when I create a SHA-256 and Base64 it and pass that value as a password in the lookup properties, it is authenticating successfully!

That does not make sense to me, because it is essentially the same as storing clear text passwords on the server side, and in Wildfly 16 no such thing was needed.

The HTTP client (I try to open the web app that also hosts the REST backend) asks for credentials (browser popup) but then nothing works, not plain text password, not the hashed value.

I set security to TRACE level logging and this is what I get when authenticating from within the browser:

2023-11-24 21:26:31,706 TRACE [org.wildfly.security.http.servlet] (default task-1) Created ServletSecurityContextImpl enableJapi=true, integratedJaspi=true, applicationContext=default-host /j-lawyer-io
2023-11-24 21:26:31,706 TRACE [org.wildfly.security.http.servlet] (default task-1) No AuthConfigProvider for layer=HttpServlet, appContext=default-host /j-lawyer-io
2023-11-24 21:26:31,706 TRACE [org.wildfly.security.http.servlet] (default task-1) JASPIC Unavailable, using HTTP authentication.
2023-11-24 21:26:31,706 TRACE [org.wildfly.security] (default task-1) No CachedIdentity to restore.
2023-11-24 21:26:31,706 TRACE [org.wildfly.security] (default task-1) Created HttpServerAuthenticationMechanism [org.wildfly.security.auth.server.SecurityIdentityServerMechanismFactory$1@6c3c7599] for mechanism [BASIC]
2023-11-24 21:26:31,706 TRACE [org.wildfly.security] (default task-1) Handling AvailableRealmsCallback: realms = [j-lawyer-security-application]
2023-11-24 21:26:31,706 DEBUG [org.wildfly.security.http.password] (default task-1) Username authentication. Realm: [j-lawyer-security-application], Username: [admin].
2023-11-24 21:26:31,706 TRACE [org.wildfly.security] (default task-1) Handling RealmCallback: selected = [j-lawyer-security-application]
2023-11-24 21:26:31,706 TRACE [org.wildfly.security] (default task-1) Handling NameCallback: authenticationName = admin
2023-11-24 21:26:31,706 TRACE [org.wildfly.security] (default task-1) Principal assigning: [admin], pre-realm rewritten: [admin], realm name: [ApplicationRealm], post-realm rewritten: [admin], realm rewritten: [admin]
2023-11-24 21:26:31,706 TRACE [org.wildfly.security] (default task-1) Executing principalQuery select password from security_users where principalId=? with value admin
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Key Mapper: Password credential created using algorithm column value [clear]
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Executing principalQuery select role, 'Roles' from security_roles where principalId=? with value admin
2023-11-24 21:26:31,707 TRACE [org.wildfly.security.http.basic] (default task-1) User admin authenticated successfully!
2023-11-24 21:26:31,707 DEBUG [org.wildfly.security.http.password] (default task-1) Username authorization. Username: [admin].
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Role mapping: principal [admin] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Authorizing principal admin.
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Authorizing against the following attributes: [Roles] => [loginRole, writeAddressRole, createAddressRole, readArchiveFileRole, createOptionGroupRole, commonReportRole, createArchiveFileRole, writeOptionGroupRole, removeAddressRole, confidentialReportRole, readAddressRole, adminRole, writeArchiveFileRole, removeArchiveFileRole, deleteOptionGroupRole, importRole]
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Authorizing against the following runtime attributes: [] => []
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Permission mapping: identity [admin] with roles [] implies ("org.wildfly.security.auth.permission.LoginPermission" "") = true
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Authorization succeed
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) RunAs authorization succeed - the same identity
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Handling AuthorizeCallback: authenticationID = admin  authorizationID = admin  authorized = true
2023-11-24 21:26:31,707 DEBUG [org.wildfly.security.http.basic] (default task-1) User admin authorization succeeded!
2023-11-24 21:26:31,707 TRACE [org.wildfly.security] (default task-1) Handling AuthenticationCompleteCallback: succeed
2023-11-24 21:26:31,708 TRACE [org.wildfly.security] (default task-1) Handling SecurityIdentityCallback: identity = SecurityIdentity{principal=admin, securityDomain=org.wildfly.security.auth.server.SecurityDomain@3ea8f016, authorizationIdentity=EMPTY, realmInfo=RealmInfo{name='ApplicationRealm', securityRealm=org.wildfly.security.auth.realm.jdbc.JdbcSecurityRealm@51bd3c04}, creationTime=2023-11-24T20:26:31.707702901Z}
2023-11-24 21:26:31,708 TRACE [org.wildfly.security] (default task-1) Role mapping: principal [admin] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
2023-11-24 21:26:31,708 TRACE [org.wildfly.security] (default task-1) Role mapping: principal [admin] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
2023-11-24 21:26:31,708 TRACE [org.wildfly.security] (default task-1) Permission mapping: identity [admin] with roles [] implies ("jakarta.security.jacc.WebResourcePermission" "/swagger-ui/" "GET") = false

Questions:

  • At a conceptual level, am I doing something wrong?
  • is HTTP basic for a web app and hashed passwords working in combination with remote EJB clients?
  • When using hashing, is the remote client now supposed to provide the hashed value to the server in the lookup properties? If yes, what would the browser do? It cannot create such values.
  • As the Elytron doc seem either outdated or incomplete: anyone aware of a comparable example?

Thanks!

1

There are 1 answers

0
j-dimension On

Answering my own question here, just in case anyone else runs into this issue:

Use a

<simple-digest-mapper algorithm="simple-digest-sha-256" password-index="1"/>

at the jdbc-realm and a "PLAIN" mechanism at the SASL auth factory:

        <sasl-authentication-factory name="jlawyer-sasl-authentication-factory" sasl-server-factory="configured" security-domain="jlawyer-security-domain">
            <mechanism-configuration>
                <mechanism mechanism-name="PLAIN">
                    <mechanism-realm realm-name="jlawyer-jdbc-realm"/>
                </mechanism>
            </mechanism-configuration>
        </sasl-authentication-factory>

This did the trick.