Connection failures after credential expiry for JNDI JDBC DataSource
0 reputation · 26 Oct 2022, 14:25 UTC
0 reputation · 26 Oct 2022, 14:25 UTC
Designing least-privilege authentication for a JNDI JDBC resource in Tomcat 9.x and 10.x raises questions about credential lifecycle and pool behavior. JNDI resources defined in server.xml are resolved at startup, and changes typically require a restart unless the JNDI environment is reloaded via JMX.
Tomcat passes the configured username and password to the JDBC driver, and expired credentials are documented to cause connection failures. The pool does not automatically re-authenticate after a credential change, which can leave existing connections stale. Tomcat 10 adds a factory attribute that permits a custom javax.naming.spi.ObjectFactory to implement authentication logic.
For a least-privilege setup with rotatable credentials, the interaction between startup resolution, pool staleness, and custom factories is unclear.
Does a JNDI JDBC DataSource pick up new credentials without a full server restart? Can a custom factory in Tomcat 10 avoid clear-text credentials in server.xml while still satisfying the DefaultJndiResourceFactory type validation? What is the documented behavior when credentials expire for an already bound resource?
31775 reputation · 26 Oct 2022, 20:38 UTC
No – a JNDI JDBC DataSource does not automatically read new credentials after they expire or are rotated; the username and password are read only when the resource is first bound (at server start or when the JNDI entry is re‑bound).
<Resource> elements at startup; the username and password attributes are passed unchanged to the JDBC driver and are not re‑read unless the resource definition is reloaded (via JMX unbind/rebind or a full restart).ORA‑01017 or \"Invalid authorization specification\". Existing pooled connections stay in the pool until they are borrowed, validated (if validationQuery + testOnBorrow/testWhileIdle is enabled), or removed by eviction, after which the pool retries with the same stale credentials unless the resource definition is updated.factory attribute to <Resource>. A custom javax.naming.spi.ObjectFactory’s getObjectInstance method is invoked on each lookup, allowing it to fetch credentials from a vault, environment variable, or other secret store and return a javax.sql.DataSource (or Connection) without storing clear‑text values in server.xml. The factory does not need to extend DefaultJndiResourceFactory; it only must implement ObjectFactory and return an object assignable to the expected type.When credentials are rotated, the only way for the DataSource to see the new values without a full server restart is to cause the resource to be re‑bound (so the factory runs again) or to have the factory obtain fresh credentials on every lookup. If you cannot reload the resource via JMX, a custom factory that reads credentials dynamically is the recommended approach.
javax.naming.spi.ObjectFactory. In getObjectInstance, read the username and password from your secret source (e.g., HashiCorp Vault, AWS Secrets Manager, or an environment variable), build a javax.sql.DataSource (for example using Tomcat JDBC pool or HikariCP) with those credentials, and return it.server.xml (or context.xml) without username and password attributes, specifying the factory class:
<Resource name='jdbc/myDB'
auth='Container'
type='javax.sql.DataSource'
factory='com.example.VaultDataSourceFactory'
driverClassName='org.postgresql.Driver'
url='jdbc:postgresql://dbhost:5432/mydb'
maxTotal='20'
validationQuery='SELECT 1'
testOnBorrow='true'/>
$CATALINA_BASE/lib or a shared loader).getObjectInstance is called on each JNDI lookup by enabling FINEST logging for org.apache.naming.validationQuery + testOnBorrow or testWhileIdle) so that if a stale connection somehow remains in the pool, borrowing it will trigger validation failure, the pool will evict it, and a new connection will be created using the current credentials from the factory.Do you have JMX access to unbind/rebind the JNDI resource? If not, the custom‑factory approach is the only way to avoid a full restart.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.