Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failure during connection to MYSQL on FIPS-compliant host #32910

Closed
fedinskiy opened this issue Apr 26, 2023 · 41 comments
Closed

Failure during connection to MYSQL on FIPS-compliant host #32910

fedinskiy opened this issue Apr 26, 2023 · 41 comments
Labels
area/agroal area/securepipeline issues related to ensure Quarkus can be used in a secure pipeline setups like FIPS or similar area/security kind/bug Something isn't working

Comments

@fedinskiy
Copy link
Contributor

fedinskiy commented Apr 26, 2023

Describe the bug

I have an application, which uses quarkus-hibernate-orm-panache and quarkus-jdbc-mysql to connect to MYSQL instance. When I run the application ( or integration test) on a RHEL machine with enabled FIPS mode, connection fails.

Expected behavior

Application should establish the connection successfully.

Actual behavior

WARN  [org.hib.eng.jdb.env.int.JdbcEnvironmentInitiator] (JPA Startup Thread) HHH000342: Could not obtain connection to query metadata: java.sql.SQLException: Cannot find any provider supporting RSA/ECB/OAEPWithSHA-1AndMGF1Padding
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:828)
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:448)
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241)
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198)
	at io.agroal.pool.ConnectionFactory.createConnection(ConnectionFactory.java:226)
	at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:536)
	at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:517)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at io.agroal.pool.util.PriorityScheduledExecutor.beforeExecute(PriorityScheduledExecutor.java:75)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1126)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: com.mysql.cj.exceptions.RSAException: Cannot find any provider supporting RSA/ECB/OAEPWithSHA-1AndMGF1Padding
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)
	at com.mysql.cj.protocol.ExportControlled.encryptWithRSAPublicKey(ExportControlled.java:691)
	at com.mysql.cj.protocol.a.authentication.Sha256PasswordPlugin.encryptPassword(Sha256PasswordPlugin.java:181)
	at com.mysql.cj.protocol.a.authentication.Sha256PasswordPlugin.encryptPassword(Sha256PasswordPlugin.java:171)
	at com.mysql.cj.protocol.a.authentication.CachingSha2PasswordPlugin.encryptPassword(CachingSha2PasswordPlugin.java:162)
	at com.mysql.cj.protocol.a.authentication.CachingSha2PasswordPlugin.nextAuthenticationStep(CachingSha2PasswordPlugin.java:142)
	at com.mysql.cj.protocol.a.authentication.CachingSha2PasswordPlugin.nextAuthenticationStep(CachingSha2PasswordPlugin.java:49)
	at com.mysql.cj.protocol.a.NativeAuthenticationProvider.proceedHandshakeWithPluggableAuthentication(NativeAuthenticationProvider.java:447)
	at com.mysql.cj.protocol.a.NativeAuthenticationProvider.connect(NativeAuthenticationProvider.java:212)
	at com.mysql.cj.protocol.a.NativeProtocol.connect(NativeProtocol.java:1433)
	at com.mysql.cj.NativeSession.connect(NativeSession.java:133)
	at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:948)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:818)
	... 11 more
Caused by: java.security.NoSuchAlgorithmException: Cannot find any provider supporting RSA/ECB/OAEPWithSHA-1AndMGF1Padding
	at java.base/javax.crypto.Cipher.getInstance(Cipher.java:565)
	at com.mysql.cj.protocol.ExportControlled.encryptWithRSAPublicKey(ExportControlled.java:687)
	... 22 more
Caused by: javax.crypto.NoSuchPaddingException: Unsupported padding OAEPWithSHA-1AndMGF1Padding
	at jdk.crypto.cryptoki/sun.security.pkcs11.P11RSACipher.engineSetPadding(P11RSACipher.java:137)
	at java.base/javax.crypto.Cipher$Transform.setModePadding(Cipher.java:391)
	at java.base/javax.crypto.Cipher.getInstance(Cipher.java:558)
	... 23 more

How to Reproduce?

  1. git clone [email protected]:fedinskiy/reproducer.git -b mysql-fips
  2. in a separate console:
docker run \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=user \
-e MYSQL_ROOT_PASSWORD=user \
-e MYSQL_DATABASE=mydb \
-e MARIADB_USER=user \
-e MARIADB_PASSWORD=user \
-e MARIADB_ROOT_PASSWORD=user \
-e MARIADB_DATABASE=mydb \
-e ssl_fips_mode=ON \
-p 3306:3306 \
registry.access.redhat.com/rhscl/mysql-80-rhel7:latest
  1. mvn clean package -Dquarkus.platform.version=3.0.1.Final -DskipTests
  2. cp -r target/quarkus-app/ .
  3. java -jar quarkus-app/quarkus-run.jar — this robustly fails.
    The same can be reproduced for Native mode:
  4. mvn clean package -Pnative -DskipTests -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=registry.access.redhat.com/quarkus/mandrel-22-rhel8:22.3
  5. target/code-with-quarkus-1.0.0-SNAPSHOT-runner
    Surprisingly, if we run native binary, which was built without support for native mode, then the connection starts working:
  6. mvn clean package -Dquarkus.platform.version=3.0.1.Final -DskipTests -Pnative
  7. target/code-with-quarkus-1.0.0-SNAPSHOT-runner — this succeeds
  8. java -jar quarkus-app/quarkus-run.jar — now this also succeeds, unless we restart the db

Output of uname -a or ver

4.18.0-425.19.2.el8_7.x86_64

Output of java -version

11.0.18, vendor: Red Hat, Inc

GraalVM version (if different from Java)

No response

Quarkus version or git rev

3.0.1.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63)

Additional information

$ cat /proc/sys/crypto/fips_enabled
1
@fedinskiy fedinskiy added the kind/bug Something isn't working label Apr 26, 2023
@quarkus-bot quarkus-bot bot added the area/securepipeline issues related to ensure Quarkus can be used in a secure pipeline setups like FIPS or similar label Apr 26, 2023
@quarkus-bot
Copy link

quarkus-bot bot commented Apr 26, 2023

/cc @Karm (securepipeline), @jerboaa (securepipeline)

@fedinskiy fedinskiy changed the title Failure during connection to MYSQL ion jvm mode Failure during connection to MYSQL in jvm mode Apr 26, 2023
@jerboaa
Copy link
Contributor

jerboaa commented Apr 26, 2023

Are you sure this works in native when native got built on a FIPS enabled host (that's a prerequisite for a FIPS enabled native build)? Otherwise the native build is equivalent to a JVM mode run with -Dcom.redhat.fips=false

@fedinskiy
Copy link
Contributor Author

@jerboaa all commands from the reproducer (including the ones for building) were run on a host with fips enabled.

@fedinskiy fedinskiy changed the title Failure during connection to MYSQL in jvm mode Failure during connection to MYSQL in JVM mode on FIPS-compliant host Apr 26, 2023
@jerboaa
Copy link
Contributor

jerboaa commented Apr 26, 2023

@fedinskiy Sure, but Mandrel in use when generating the the app matters too. Which was used? Only the quarkus/mandrel-22-rhel8:22.3 and quarkus/mandrel-21-rhel8:21.3 images have FIPS support for native. The ticket doesn't specify which native-image version was used for generating the runner binary.

@fedinskiy
Copy link
Contributor Author

@jerboaa I build native via quakrus profile and docker:

[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner] Using docker to run the native image builder
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner] Checking image status quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17

@jerboaa
Copy link
Contributor

jerboaa commented Apr 26, 2023

@jerboaa I build native via quarkus profile and docker:

[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner] Using docker to run the native image builder
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner] Checking image status quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17

Please build with registry.access.redhat.com/quarkus/mandrel-22-rhel8:22.3 and then compare JVM mode and native again. I'd expect for both JVM and native to fail similarly once you do that.

@jerboaa
Copy link
Contributor

jerboaa commented Apr 26, 2023

target/code-with-quarkus-1.0.0-SNAPSHOT-runner — this succeeds

Quick check that a native image is actually FIPS enabled is this (returns isFIPS if yes, nothing otherwise):

$ strings target/code-with-quarkus-1.0.0-SNAPSHOT-runner | grep isFIPS
isFIPS

@jerboaa
Copy link
Contributor

jerboaa commented Apr 26, 2023

As to the failure it seems to be caused by this:

return encryptPassword("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); see:
https://github.com/mysql/mysql-connector-j/blob/release/8.0/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/Sha256PasswordPlugin.java#L171

which seems to then delegate to:

Cipher cipher = Cipher.getInstance(transformation); here:
https://github.com/mysql/mysql-connector-j/blob/7d6b0800528b6b25c68b52dc10d6c1c8429c100c/src/main/core-impl/java/com/mysql/cj/protocol/ExportControlled.java#L687

That cipher is not supported by the NSS FIPS provider by RH OpenJDK:

$ sudo fips-mode-setup --check
FIPS mode is enabled.
Initramfs fips module is enabled.
The current crypto policy (FIPS) is based on the FIPS policy.
$ cat CipherTest.java 
import javax.crypto.Cipher;
public class CipherTest {

    public static void main(String[] args) throws Exception {
        // This transformation would work in FIPS/non FIPS: "RSA/ECB/PKCS1Padding"
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
        System.out.println(cipher.getProvider());
    }

}
$ javac CipherTest.java
$ java CipherTest
Exception in thread "main" java.security.NoSuchAlgorithmException: Cannot find any provider supporting RSA/ECB/OAEPWithSHA-1AndMGF1Padding
	at java.base/javax.crypto.Cipher.getInstance(Cipher.java:571)
	at CipherTest.main(CipherTest.java:6)
Caused by: javax.crypto.NoSuchPaddingException: Unsupported padding OAEPWithSHA-1AndMGF1Padding
	at jdk.crypto.cryptoki/sun.security.pkcs11.P11RSACipher.engineSetPadding(P11RSACipher.java:137)
	at java.base/javax.crypto.Cipher$Transform.setModePadding(Cipher.java:388)
	at java.base/javax.crypto.Cipher.getInstance(Cipher.java:564)
	... 1 more
$ java -Dcom.redhat.fips=false CipherTest 
SunJCE version 17

So it's the SunJCE provider that supplies this particular transformation in non-fips mode. If RSA/ECB/PKCS1Padding was used instead, it would work in both cases.

Is there any particular reason why RSA/ECB/OAEPWithSHA-1AndMGF1Padding is chosen by the driver? Perhaps worth raising an issue. Failing that, there may be a way to use a different auth scheme by the driver which can be supported in FIPS mode.

@fedinskiy
Copy link
Contributor Author

strings target/code-with-quarkus-1.0.0-SNAPSHOT-runner | grep isFips returns nothing ( the artifact was built with mvn clean package -Pnative -DskipTest). How should FIPS be enabled for the build?

Also, I see message Unable to build Hibernate SessionFactoryUnable to enable SSL support. You might be in the case where you used the quarkus.ssl.native=false configuration and SSL was not disabled automatically for your driver. in logs, is it possible, that Quarkus/Hibernate are still using encrypted connection, despite ?useSSL=false in connection string?

@fedinskiy
Copy link
Contributor Author

Also, it looks like[1] RSA/ECB/OAEPWithSHA-1AndMGF1Padding is a default option and can be overridden. Is it possible to tune Hibernate/Quarkus to do that?

[1] https://github.com/mysql/mysql-connector-j/blob/release/8.0/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/Sha256PasswordPlugin.java#L174

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

strings target/code-with-quarkus-1.0.0-SNAPSHOT-runner | grep isFips returns nothing ( the artifact was built with mvn clean package -Pnative -DskipTest). How should FIPS be enabled for the build?

  1. It should be strings target/code-with-quarkus-1.0.0-SNAPSHOT-runner | grep isFIPS or use case-insensitive in grep.
  2. How to enable? mvn clean package -Pnative -DskipTest -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=registry.access.redhat.com/quarkus/mandrel-22-rhel8:22.3. The pre-requisite for native enabled FIPS are 1.) have a GraalVM build with the FIPS patches (the said image has that) 2.) build on a fips enabled host 3.) The base JDK in use needs to have the FIPS patches as well (the said image is based on RH build of OpenJDK which has that).

Also, I see message Unable to build Hibernate SessionFactoryUnable to enable SSL support. You might be in the case where you used the quarkus.ssl.native=false configuration and SSL was not disabled automatically for your driver. in logs, is it possible, that Quarkus/Hibernate are still using encrypted connection, despite ?useSSL=false in connection string?

This is for some Quarkus dev to answer. I don't know.

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

Also, it looks like[1] RSA/ECB/OAEPWithSHA-1AndMGF1Padding is a default option and can be overridden. Is it possible to tune Hibernate/Quarkus to do that?

[1] https://github.com/mysql/mysql-connector-j/blob/release/8.0/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/Sha256PasswordPlugin.java#L174

Ideally the mysql Java driver would allow for a config to select which cipher to use. Currently it looks hard-coded: https://github.com/mysql/mysql-connector-j/blob/release/8.0/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/Sha256PasswordPlugin.java#L171

@Sanne Would you know if that driver supports it? If not, an issue should probably filed for this (not sure where they have the bug tracker).

@fedinskiy fedinskiy reopened this Apr 27, 2023
@fedinskiy
Copy link
Contributor Author

mvn clean package -Pnative -DskipTests -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=registry.access.redhat.com/quarkus/mandrel-22-rhel8:22.3
strings target/code-with-quarkus-1.0.0-SNAPSHOT-runner | grep -i isFIPS
#empty 

More general grep:

$ strings target/code-with-quarkus-1.0.0-SNAPSHOT-runner | grep -i fips
name = NSS-FIPS
nssSecmodDirectory = ${fips.nssdb.path}
nssModule = fips
name = NSS-FIPS
nssSecmodDirectory = ${fips.nssdb.path}
nssModule = fips
/usr/lib/jvm/java-17-openjdk-17.0.7.0.7-1.el8_7.x86_64/conf/security/nss.fips.cfg
name = "NSS FIPS SoftToken"
sun.security.pkcs11.wrapper.PKCS11$SynchronizedFIPSPKCS11
Private or Secret key will be imported in system FIPS mode.
Private or Secret key will be exported in system FIPS mode.
FIPS: error during the Token login required for the 
SunPKCS11 ${java.home}/conf/security/nss.fips.cfg
sun.security.pkcs11.wrapper.PKCS11$FIPSPKCS11Helper
sun.security.pkcs11.wrapper.PKCS11$FIPSPKCS11
sun.security.pkcs11.FIPSTokenLoginHandler
NETSCAPE_RSA_FIPS_WITH_3DES_EDE_CBC_SHA
NETSCAPE_RSA_FIPS_WITH_DES_CBC_SHA
SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA
sun.security.pkcs11.FIPSKeyImporter
Error reading fips.nssdb.pin from the '
Unsupported prefix for fips.nssdb.pin.
Invalid fips.nssdb.pin property value.
SSL_RSA_FIPS_WITH_DES_CBC_SHA
CKR_FIPS_SELF_TEST_FAILED
FIPSTokenLoginHandler.java
SynchronizedFIPSPKCS11
FIPSKeyImporter.java
 fips key importer
 fips key exporter
fips.keystore.type
getSystemFIPSEnabled
isSystemFipsEnabled
systemFipsEnabled
getFipsJSSEProvider
CKM_DSA_FIPS_G_GEN
SunPKCS11-NSS-FIPS
FIPSPKCS11
fips.nssdb.path
fips.nssdb.pin
fips.nssdb.pin
fips.provider.6
fips.provider.5
fips.provider.7
fips.nssdb.path
fips.provider.2
fips.provider.1
fips.provider.4
fips.provider.3
fdudinsk-fips
nss.fips.cfg
/nss.fips.cfg
fipsLoggedIn
fipsExportKey
fipsImportKey
fipsKeyExporter
fipsKeyImporter
getFipsNssdbPin
FIPSPKCS11Helper
fips
NSS-FIPS
FIPS
SunPKCS11-NSS-FIPS
fips

Also it seems weird, that running native seems to be "curing" the JVM mode.

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

Sorry, my bad for the wrong instructions on how to determine after-the-fact whether or not it's a fips enabled image or not. This is still to be determined.

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

Also it seems weird, that running native seems to be "curing" the JVM mode.

I cannot parse this, sorry. What do you mean? Could you reword, please?

@fedinskiy
Copy link
Contributor Author

See step 8 of the reproducer. We run jar, it fails, we run binary, it succeeds, we run jar again, it now also succeeds

@machi1990
Copy link
Member

machi1990 commented Apr 27, 2023

@fedinskiy it is possible to supply your own custom authentication plugin: which has to implement the com.mysql.cj.protocol.AuthenticationPlugin interface.
And then load it via the authenticationPlugins property in the jdbc url, which is the comma-delimited list of classes that implement the interface 'com.mysql.cj.protocol.AuthenticationPlugin`.

The default authenticaton plugin is sha256_password which is implemented in: https://github.com/mysql/mysql-connector-j/blob/release/8.0/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/Sha256PasswordPlugin.java#L59

You could build off your custom authentication plugin based on this and override the method which is at fault.
If you leave the name of the plugin as is i.e sha256_password then the default implementation will also be overridden once you register your custom implementation (via the authenticationPlugins property). Otherwise, if you use a different plugin name, be sure to also change the default authentication plugin by using the property defaultAuthenticationPlugin: The default authentication plugin client-side protocol name or a fully qualified name of a class that implements the interface 'com.mysql.cj.protocol.AuthenticationPlugin`.

Worth noting that, If you are running in native mode, make sure that the class implementing the custom authentication logic is registered for reflection.

@fedinskiy
Copy link
Contributor Author

@machi1990 I thought, that Quarkus supports FIPS out-of-the-box. Can I consider this an official recommendation? Is it documented anywhere?

For context: I am a member of Quarkus QE team, so my goal is less about "make the app work" and more about "make sure, that there are no nasty surprises for users"

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

See step 8 of the reproducer. We run jar, it fails, we run binary, it succeeds, we run jar again, it now also succeeds

Thanks. I'd attribute this behaviour to some caching mechanism of the driver.

Aside: @fedinskiy Are you saying that even a FIPS enabled native image (as produced here) works? That'd be a surprise and would have to get investigated.

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

For context: I am a member of Quarkus QE team, so my goal is less about "make the app work" and more about "make sure, that there are no nasty surprises for users"

+1 to "make sure, that there are no nasty surprises for users". The goal should be that it works smoothly in the default case (in FIPS).

@michalvavrik
Copy link
Member

For context: I am a member of Quarkus QE team, so my goal is less about "make the app work" and more about "make sure, that there are no nasty surprises for users"

+1 I am also worried that if we follow @machi1990 suggestion (thanks for taking your time to answer!) customers lose support (MySQL is officially supported with RHBQ 2.13 https://code.quarkus.redhat.com/?extension-search=origin:platform%20mysql) Do you by chance know what is supported solution?

@Sanne
Copy link
Member

Sanne commented Apr 27, 2023

I don't know much about FIPS specifics, but I see several comments referring to Sha256PasswordPlugin and wondering if it can be overriden.

Yes it can, MySQL supports several authentication plugins and that is just one of the many ones; e.g. see
https://github.com/mysql/mysql-connector-j/blob/2ad747a23dfc610b49be19088c643bc8c9524ac5/src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java#L178-L203

For example CachingSha2PasswordPlugin is also included in the MySQL JDBC driver and it does override the password encryption cypher to use RSA/ECB/PKCS1Padding ; would that suffice?

@Sanne
Copy link
Member

Sanne commented Apr 27, 2023

And according the these comments, not only could we extend the set of plugins with a custom one, but we could also override the default ones it's providing:

https://github.com/mysql/mysql-connector-j/blob/2ad747a23dfc610b49be19088c643bc8c9524ac5/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeAuthenticationProvider.java#L215-L225

(that seems like a last resort though, I'd expect the driver to be able to work with FIPS out of the box, but it might be useful if we wanted to "decorate" some of the basic ones for improved user experience)

@Sanne
Copy link
Member

Sanne commented Apr 27, 2023

(I only see now that @machi1990 had provided very similar information, sorry for the noise!)

In regards to support concerns of such an approach... yes I wouldn't expect end users to need to provide a custom plugin.
I expect choosing one of the provided ones should work, failing that we could provide a custom plugin to be included within Quarkus and supported (but I'd be very surprised for this to be necessary).

@fedinskiy
Copy link
Contributor Author

@jerboaa no, the native image, built with custom mandrel doesn't work, thank for the info.

javax.crypto.NoSuchPaddingException: Unsupported padding OAEPWithSHA-1AndMGF1Padding
	at [email protected]/sun.security.pkcs11.P11RSACipher.engineSetPadding(P11RSACipher.java:137)

So, for native image to support fips, it should be built with -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=registry.access.redhat.com/quarkus/mandrel-22-rhel8:22.3. Is this documented somewhere?

@Sanne as I understand, this plugin requires changes in jdbc connection string[1]. However, if the ssl is enabled ( eg quarkus.datasource.jdbc.url=jdbc:mysql://localhost:3306/mydb?useSSL=true&allowPublicKeyRetrieval=True ) there is another failure:

<...>
Caused by: org.hibernate.exception.JDBCConnectionException: Unable to open JDBC Connection for DDL execution [Cannot open file:NONE [NONE (No such file or directory)]] [n/a]
	at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:49)
	at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:108)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:94)
	at org.hibernate.resource.transaction.backend.jta.internal.DdlTransactionIsolatorJtaImpl.getIsolatedConnection(DdlTransactionIsolatorJtaImpl.java:77)
	at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.getIsolatedConnection(GenerationTargetToDatabase.java:60)
	at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.jdbcStatement(GenerationTargetToDatabase.java:110)
	at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:77)
	at org.hibernate.tool.schema.internal.Helper.applySqlString(Helper.java:235)
	at org.hibernate.tool.schema.internal.Helper.applySqlStrings(Helper.java:219)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.dropTables(SchemaDropperImpl.java:362)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.dropConstraintsTablesSequences(SchemaDropperImpl.java:253)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.dropFromMetadata(SchemaDropperImpl.java:215)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.performDrop(SchemaDropperImpl.java:185)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:155)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:115)
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:242)
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:143)
	at java.base/java.util.HashMap.forEach(HashMap.java:1337)
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:140)
	at io.quarkus.hibernate.orm.runtime.observers.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:21)
	at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:291)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:200)
	at io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder.build(FastBootEntityManagerFactoryBuilder.java:79)
	... 6 more
Caused by: java.sql.SQLNonTransientConnectionException: Cannot open file:NONE [NONE (No such file or directory)]
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:110)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:70)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:828)
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:448)
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241)
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198)
	at io.agroal.pool.ConnectionFactory.createConnection(ConnectionFactory.java:226)
	at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:536)
	at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:517)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at io.agroal.pool.util.PriorityScheduledExecutor.beforeExecute(PriorityScheduledExecutor.java:75)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1126)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	... 1 more
Caused by: com.mysql.cj.exceptions.SSLParamsException: Cannot open file:NONE [NONE (No such file or directory)]
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:151)
	at com.mysql.cj.protocol.ExportControlled.getSSLContext(ExportControlled.java:579)
	at com.mysql.cj.protocol.ExportControlled.performTlsHandshake(ExportControlled.java:333)
	at com.mysql.cj.protocol.StandardSocketFactory.performTlsHandshake(StandardSocketFactory.java:191)
	at com.mysql.cj.protocol.a.NativeSocketConnection.performTlsHandshake(NativeSocketConnection.java:101)
	at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:369)
	at com.mysql.cj.protocol.a.NativeAuthenticationProvider.connect(NativeAuthenticationProvider.java:205)
	at com.mysql.cj.protocol.a.NativeProtocol.connect(NativeProtocol.java:1433)
	at com.mysql.cj.NativeSession.connect(NativeSession.java:133)
	at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:948)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:818)
	... 11 more
Caused by: java.io.FileNotFoundException: NONE (No such file or directory)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
	at java.base/sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:86)
	at java.base/sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:184)
	at java.base/java.net.URL.openStream(URL.java:1165)
	at com.mysql.cj.protocol.ExportControlled.getSSLContext(ExportControlled.java:558)
	... 20 more

[1] https://stackoverflow.com/questions/48688140/mysql-jdbc-encryption-using-ssl

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

@jerboaa no, the native image, built with custom mandrel doesn't work, thank for the info.

javax.crypto.NoSuchPaddingException: Unsupported padding OAEPWithSHA-1AndMGF1Padding
	at [email protected]/sun.security.pkcs11.P11RSACipher.engineSetPadding(P11RSACipher.java:137)

We are finally on the same page. Thanks.

So, for native image to support fips, it should be built with -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=registry.access.redhat.com/quarkus/mandrel-22-rhel8:22.3. Is this documented somewhere?

Yes, for native fips support it has to be the product build of Mandrel (which RHBQ 2.13 uses, btw). That's the default image if you are on RHBQ 2.13 (so in a way you don't need to do anything other than build on a fips enabled host). There will need to be some documentation at some point. First step: Support FIPS in JVM mode, document that, then native documentation will follow. I'm the one responsible for FIPS support in Mandrel, so I might be able to help with certain JVM mode cases too.

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

For example CachingSha2PasswordPlugin is also included in the MySQL JDBC driver and it does override the password encryption cypher to use RSA/ECB/PKCS1Padding ; would that suffice?

@Sanne yes. RSA/ECB/PKCS1Padding is known to work on FIPS as well as non-fips. So it would be a good choice to use by default (or document to use that one over the upstream default). See #32910 (comment)

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

@fedinskiy Please update the description of this issue that it doesn't matter whether or not it's JVM or native (as we've shown that a fips enabled native image fails the same way as JVM mode).

@fedinskiy fedinskiy changed the title Failure during connection to MYSQL in JVM mode on FIPS-compliant host Failure during connection to MYSQL on FIPS-compliant host Apr 27, 2023
@Sanne
Copy link
Member

Sanne commented Apr 27, 2023

For example CachingSha2PasswordPlugin is also included in the MySQL JDBC driver and it does override the password encryption cypher to use RSA/ECB/PKCS1Padding ; would that suffice?

@Sanne yes. RSA/ECB/PKCS1Padding is known to work on FIPS as well as non-fips. So it would be a good choice to use by default (or document to use that one over the upstream default). See #32910 (comment)

ok great. We shouldn't change the default, this is a matter of documentation and testing the configurations.

@machi1990
Copy link
Member

For example CachingSha2PasswordPlugin is also included in the MySQL JDBC driver and it does override the password encryption cypher to use RSA/ECB/PKCS1Padding ; would that suffice?

@Sanne yes. RSA/ECB/PKCS1Padding is known to work on FIPS as well as non-fips. So it would be a good choice to use by default (or document to use that one over the upstream default). See #32910 (comment)

ok great. We shouldn't change the default, this is a matter of documentation and testing the configurations.

+1

@jerboaa
Copy link
Contributor

jerboaa commented Apr 27, 2023

ok great. We shouldn't change the default, this is a matter of documentation and testing the configurations.

@fedinskiy Could you give CachingSha2PasswordPlugin a try in FIPS mode, please?

@fedinskiy
Copy link
Contributor Author

@jerboaa according to the manual[1], it should be enabled by default. Are there any additional actions to be done?

[1] https://dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html

@jerboaa
Copy link
Contributor

jerboaa commented Apr 28, 2023

@fedinskiy Sorry, I have no idea about how to use the one over another. @Sanne would probably be your best bet. Though, the original stacktrace suggests it goes from CachingSha2PasswordPlugin => Sha256PasswordPlugin which then fails due to the padding algo not available in FIPS issue.

@Sanne
Copy link
Member

Sanne commented Apr 28, 2023

It should work, if you could share some way to reproduce it (how did you setup the db exactly?) I can try debugging it.

@jerboaa
Copy link
Contributor

jerboaa commented Apr 28, 2023

@Sanne I think it's just the docker run in the original reproduction steps using mysql-80-rhel7:latest container.

@jerboaa
Copy link
Contributor

jerboaa commented Apr 28, 2023

But I guess you'd have to have a FIPS enabled system fips-mode-setup --enable (on a RHEL 8 system).

@Sanne
Copy link
Member

Sanne commented Apr 28, 2023

sorry I might try to help with that but I'm travelling & fully scheduled for the upcoming two weeks, I don't think I'll have time to setup a FIPS server on RHEL soon.

But also for sake of future maintenance, could we find a way to have integration tests upstream? No container images freely available?

@jerboaa
Copy link
Contributor

jerboaa commented Apr 28, 2023

@Sanne All RH UBI openjdk images have FIPS support. But enabling of it is a kernel feature. So you'd need to run the container on a FIPS enabled system. E.g. ubi8/openjdk-17 image.

@fedinskiy
Copy link
Contributor Author

I tried to follow this manual[1] to use RSA over unencrypted connection First using the plugin with disabled ssl:
quarkus.datasource.jdbc.url=jdbc:mysql://localhost:3306/mydb?sslMode=DISABLED&allowPublicKeyRetrieval=True&defaultAuthenticationPlugin=caching_sha2_password without any change. It looks like this was somehow ignored by Hibernate/Quarkus/driver, since I see this in logs: Caused by: jakarta.persistence.PersistenceException: [PersistenceUnit: <default>] Unable to build Hibernate SessionFactoryUnable to enable SSL support. You might be in the case where you used the quarkus.ssl.native=false configuration and SSL was not disabled automatically for your driver., so it looks like something still tries to create an ssl connection

I also tried to use secure connection

quarkus.datasource.jdbc.url=jdbc:mysql://localhost:3306/mydb?sslMode=PREFERRED&allowPublicKeyRetrieval=True&trustCertificateStoreUrl=file:///home/hudson/reproducer/certs/client-truststore.jks&trustCertificateKeyStorePassword=password&trustCertificateKeyStoreType=BCFKS&defaultAuthenticationPlugin=caching_sha2_password

with a different error:

	... 53 more
Caused by: jakarta.persistence.PersistenceException: [PersistenceUnit: <default>] Unable to build Hibernate SessionFactory
	at io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder.persistenceException(FastBootEntityManagerFactoryBuilder.java:121)
	at io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder.build(FastBootEntityManagerFactoryBuilder.java:81)
	at io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider.createEntityManagerFactory(FastBootHibernatePersistenceProvider.java:74)
	at jakarta.persistence.Persistence.createEntityManagerFactory(Persistence.java:80)
	at jakarta.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
	at io.quarkus.hibernate.orm.runtime.JPAConfig$LazyPersistenceUnit.get(JPAConfig.java:156)
	at io.quarkus.hibernate.orm.runtime.JPAConfig$1.run(JPAConfig.java:64)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: org.hibernate.exception.JDBCConnectionException: Unable to open JDBC Connection for DDL execution [Cannot open file:NONE [NONE (No such file or directory)]] [n/a]
	at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:49)
	at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:108)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:94)
	at org.hibernate.resource.transaction.backend.jta.internal.DdlTransactionIsolatorJtaImpl.getIsolatedConnection(DdlTransactionIsolatorJtaImpl.java:77)
	at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.getIsolatedConnection(GenerationTargetToDatabase.java:60)
	at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.jdbcStatement(GenerationTargetToDatabase.java:110)
	at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:77)
	at org.hibernate.tool.schema.internal.Helper.applySqlString(Helper.java:235)
	at org.hibernate.tool.schema.internal.Helper.applySqlStrings(Helper.java:219)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.dropTables(SchemaDropperImpl.java:362)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.dropConstraintsTablesSequences(SchemaDropperImpl.java:253)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.dropFromMetadata(SchemaDropperImpl.java:215)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.performDrop(SchemaDropperImpl.java:185)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:155)
	at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:115)
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:242)
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:143)
	at java.base/java.util.HashMap.forEach(HashMap.java:1337)
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:140)
	at io.quarkus.hibernate.orm.runtime.observers.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:21)
	at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:291)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:200)
	at io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder.build(FastBootEntityManagerFactoryBuilder.java:79)
	... 6 more
Caused by: java.sql.SQLNonTransientConnectionException: Cannot open file:NONE [NONE (No such file or directory)]
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:110)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:70)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:828)
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:448)
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241)
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198)
	at io.agroal.pool.ConnectionFactory.createConnection(ConnectionFactory.java:226)
	at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:536)
	at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:517)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at io.agroal.pool.util.PriorityScheduledExecutor.beforeExecute(PriorityScheduledExecutor.java:75)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1126)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	... 1 more
Caused by: com.mysql.cj.exceptions.SSLParamsException: Cannot open file:NONE [NONE (No such file or directory)]
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:151)
	at com.mysql.cj.protocol.ExportControlled.getSSLContext(ExportControlled.java:579)
	at com.mysql.cj.protocol.ExportControlled.performTlsHandshake(ExportControlled.java:333)
	at com.mysql.cj.protocol.StandardSocketFactory.performTlsHandshake(StandardSocketFactory.java:191)
	at com.mysql.cj.protocol.a.NativeSocketConnection.performTlsHandshake(NativeSocketConnection.java:101)
	at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:369)
	at com.mysql.cj.protocol.a.NativeAuthenticationProvider.connect(NativeAuthenticationProvider.java:205)
	at com.mysql.cj.protocol.a.NativeProtocol.connect(NativeProtocol.java:1433)
	at com.mysql.cj.NativeSession.connect(NativeSession.java:133)
	at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:948)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:818)
	... 11 more
Caused by: java.io.FileNotFoundException: NONE (No such file or directory)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
	at java.base/sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:86)
	at java.base/sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:184)
	at java.base/java.net.URL.openStream(URL.java:1165)
	at com.mysql.cj.protocol.ExportControlled.getSSLContext(ExportControlled.java:558)
	... 20 more

[1] https://dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html

@michalvavrik
Copy link
Member

@fedinskiy can this be closed now?

@fedinskiy
Copy link
Contributor Author

I the app is built with mvn clean package -Dquarkus.platform.version=3.10.0 -DskipTests, than the reproducer works, so I consider this to be fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/agroal area/securepipeline issues related to ensure Quarkus can be used in a secure pipeline setups like FIPS or similar area/security kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants