Database Security Configuration and Connectivity for Java Applications

2024.11.20 · 5 minute read

I have an enterprise Java application and want to securely manage database connection configuration.

To address this requirement, this article will briefly describe how to securely manage database credentials with HashiCorp Vault and how to configure JPA for database connectivity in a Kubernetes environment.

Key management

Key storage

In a Kubernetes environment, we use HashiCorp Vault as the key management tool. The configuration is stored in the `values.yaml’ file, which uses the Helm configuration values:

# values.yaml
secretList:
  - name: database-credentials
    vaultPath: /secret/database/prod

Key acquisition process

  1. store sensitive information (e.g. database username, password) in the Vault
  2. retrieve credentials information from the Vault via vaultPath
  3. use name as a reference identifier (e.g. database-credentials.username)

Configuration File Management

When the application is deployed, Kubernetes injects the values from values.yaml into the application configuration file, which mainly consists of:

  • context.xml (Tomcat data source configuration)
  • persistence.xml (JPA configuration)
  • application.properties/yml (Spring configuration)

In the configuration file, we use the placeholder format: ^CFG:DATABASE_PASSWORD^.

Tomcat data source configuration

context.xml is Tomcat’s context configuration file for creating and managing data sources and other container-level resources.

Example:

<Context>
    <Resource
        name="jdbc/MainDatabase"
        type="javax.sql.DataSource"
        driverClassName="com.mysql.cj.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/mydb"
        username="^CFG:DATABASE_USERNAME^"
        password="^CFG:DATABASE_PASSWORD^"
        maxTotal="20"
        maxIdle="10"
        maxWaitMillis="10000"/>
</Context>

Detailed reference: Apache Tomcat 9 (9.0.97) - JNDI Resources How-To

JPA configuration

JPA (Java Persistence API) is a specification/interface, the most common JPA implementation is Hibernate.

persistance.xml is the configuration file for jpa:

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
    <persistence-unit name="mainPU" transaction-type="RESOURCE_LOCAL">
        <non-jta-data-source>java:comp/env/jdbc/MainDatabase</non-jta-data-source>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.show_sql" value="false"/>
        </properties>
    </persistence-unit>
</persistence>

where java:comp/env/ is the standard prefix for JNDI namespaces, as explained here: jakarta ee - what is java:comp/env? - Stack Overflow

You can find the corresponding Resource by using it.

Application startup process

Now let’s string together the above configuration files.

  1. Kubernetes initialization
  • Reading values.yaml
  • Get keys from Vault
  • Replace the placeholders in the configuration file
  1. **Tomcat startup
  • Load the context.xml
  • Initialize the data source connection pool
  1. **Application deployment
  • Reads persistence.xml.
  • Hibernate initialization
  • Create database connection

Code example

By importing the JPA specification and its implementation, Hibernate, as well as the Spring framework, we can properly manipulate the data in the database.

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String username;

    // getters and setters
}

@Service
@Transactional
public class UserService {
    // Specify the persistence-unit with the name attribute.
    @PersistenceContext(unitName = "mainPU")
    private EntityManager entityManager;

    public User findById(Long id) {
        return entityManager.find(User.class, id);
    }

    public void save(User user) {
        entityManager.persist(user);
    }
}

Summary

We have simply implemented a complete database security configuration scheme:

  1. key management with HashiCorp Vault
  2. configuration injection via Kubernetes
  3. Combining Tomcat data sources and JPA for database connectivity.

This solution ensures security and maintains good maintainability, which is suitable for the development and deployment of enterprise-level Java applications.

Thank you for reading! Your support is appreciated.

If you enjoyed this, consider buying me a coffee. ☕️