quarkiverse / quarkus-vault

Quarkus HashiCorp Vault extension
Apache License 2.0
19 stars 24 forks source link

Vault dynamics DB credentials with reactive-mysql-client #222

Open Visserr2 opened 7 months ago

Visserr2 commented 7 months ago

Hi,

Is it possible to use Vault dynamic credentails with reactive reactive-mysql-client (and other reactive deps) ? I use two datasource (one default and one named). If I put the db-credentials in Vault KV stores it will connect to the database but if I use dynamic credentials it will give the following error:

(vert.x-eventloop-thread-1) Uncaught exception received by Vert.x: jakarta.enterprise.inject.CreationException: Error creating synthetic bean [PjUxn1Kb-s6i5R4wH8_xdk6nbwM]: java.lang.IllegalStateException: The current thread cannot be blocked: vert.x-eventloop-thread-1 at io.vertx.mysqlclient.MySQLPool_PjUxn1Kb-s6i5R4wH8_xdk6nbwM_Synthetic_Bean.doCreate(Unknown Source) at io.vertx.mysqlclient.MySQLPool_PjUxn1Kb-s6i5R4wH8_xdk6nbwM_Synthetic_Bean.create(Unknown Source) at io.vertx.mysqlclient.MySQLPool_PjUxn1Kb-s6i5R4wH8_xdk6nbwM_Synthetic_Bean.create(Unknown Source) at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35) at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.c54(Unknown Source) at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.computeIfAbsent(Unknown Source) at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35) at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:21) at io.vertx.mysqlclient.MySQLPool_PjUxn1Kb-s6i5R4wH8_xdk6nbwM_Synthetic_ClientProxy.arc$delegate(Unknown Source) at io.vertx.mysqlclient.MySQLPool_PjUxn1Kb-s6i5R4wH8_xdk6nbwM_Synthetic_ClientProxy.query(Unknown Source) at io.quarkus.reactive.datasource.runtime.ReactiveDatasourceHealthCheck.lambda$call$1(ReactiveDatasourceHealthCheck.java:60) at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:277) at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:259) at io.vertx.core.impl.EventLoopContext.lambda$runOnContext$0(EventLoopContext.java:43) at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173) at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:166) at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:1583) Caused by: java.lang.IllegalStateException: The current thread cannot be blocked: vert.x-eventloop-thread-1 at io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:30) at io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65) at io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46) at io.quarkus.vault.runtime.VaultCredentialsProvider.getCredentials(VaultCredentialsProvider.java:55) at io.quarkus.vault.runtime.VaultCredentialsProvider_ClientProxy.getCredentials(Unknown Source) at io.quarkus.reactive.mysql.client.runtime.MySQLPoolRecorder.lambda$toMySQLConnectOptions$1(MySQLPoolRecorder.java:187) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at io.quarkus.reactive.mysql.client.runtime.MySQLPoolRecorder.toMySQLConnectOptions(MySQLPoolRecorder.java:177) at io.quarkus.reactive.mysql.client.runtime.MySQLPoolRecorder.initialize(MySQLPoolRecorder.java:96) at io.quarkus.reactive.mysql.client.runtime.MySQLPoolRecorder$2.apply(MySQLPoolRecorder.java:62) at io.quarkus.reactive.mysql.client.runtime.MySQLPoolRecorder$2.apply(MySQLPoolRecorder.java:59) at io.vertx.mysqlclient.MySQLPool_PjUxn1Kb-s6i5R4wH8_xdk6nbwM_Synthetic_Bean.createSynthetic(Unknown Source) ... 24 more

It looks like retrieving the dynamics credentials from Vault are a blocking operation. But maybe i'm doing something wrong. I use the following properties to configure dynamics credentials: quarkus.vault.credentials-provider..credentials-mount=database quarkus.vault.credentials-provider..credentials-role= quarkus.datasource.credentials-provider=

Does anyone recognize this problem?

Visserr2 commented 1 week ago

When using the quarkus-reactive-mysql-client (and hibernate-reactive) and dynamic credentials with Vault it will throw an error when fetching the credentials on rule 207 in the io.quarkus.reactive.mysql.client.runtime.MySqlPoolRecord. {D674F60C-832F-4B25-B51E-8B691AD597C0}

To solve this I created an custom VaultCredentialProvider and retrieve the credentials as a Future via a virtual thread. My code looks like this:

    private Map<String, String> retrieveCredentialsByVt(CredentialsProviderConfig config){
        try(var executorService = Executors.newVirtualThreadPerTaskExecutor()) {
            return executorService
                    .submit(() -> vaultDynamicCredentialsManager.getDynamicCredentials(config.credentialsMount(), config.credentialsRequestPath(), config.credentialsRole().get())
                            .await()
                            .indefinitely())
                    .get();
        } catch (Exception e){
            throw new VaultException("Cannot retrieve Vault credentials " + e.getMessage());
        }
    }

Im calling this method in the getCredentials-method:

    if(config.credentialsRole().isPresent()){
        return retrieveCredentialsByVt(config);
    }

    if (config.kvPath().isPresent()) {
        String password = vaultKVSecretEngine.readSecret(config.kvPath().get()).get(config.kvKey());
        Map<String, String> result = new HashMap<>();
        result.put(PASSWORD_PROPERTY_NAME, password);
        return result;
    }

This way I can use reactive-mysql-client in combination with Vault Dynamic database.