Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreIn my previous post about Managing Secrets with Vault, I introduced you to Vault and how to store arbitrary secrets using the generic secret backend. Vault can manage more than just secret data like API keys, passwords, and other sensitive string-like data. Today we’re taking a look at Vault’s integration with databases, services, and certificates.
When it comes to databases, the regular workflow of getting credentials applying for a database is asking some operator or a self-service tool to give you credentials so your application can log into the database. At this point, credentials are considered static. Credentials get usually changed in case the database is migrated or if there’s a security breach.
With Spring Cloud Vault you can store username and password inside Vault instead your application configuration. Spring Config Server is also a good choice; it allows you to store encrypted secrets in your config repository. Your credentials are protected.
There's one caveat: Long-lived credentials are a good target for leakage. Leaked credentials can give access to an unintended party. A few databases implement restrictions on source hosts. In some cases, a database user can be restricted to a group of hosts. That restriction prevents access from other hosts. Still, every user and process that has access to a permitted machine can use the leaked credentials. But how do you discover that leakage? You might find the leak if your data was leaked to the public or the internet but that’s not always the case. For other cases, the unintended party may read or change your data, and it’s fairly sure the leak remains undiscovered for quite a while.
Let’s make credentials short-lived.
Vault comes with a variety of integrations to different systems. Some of them integrate with PostgreSQL and MySQL as secret backend. A secret backend can provide secrets. In this case, secret backend does not mean that secret data is stored in PosgreSQL/MySQL. It means that Vault can create (and revoke) users for databases on demand.
To generate database credentials, you need to set up a role first. Roles control the permission context for database credentials generation. A role defines the permissions that are associated with the credentials. That concept maps well if you run different applications and each application requires different database permissions.
A role also defines the max lease time for obtained credentials. The lease is in Vault-speak the duration the credentials are valid. Vault revokes credentials from the database system once they are expired. Some databases, like PostgreSQL, have built-in support for password expiry with the CREATE ROLE … VALID UNTIL …
clause.
Now how to get started with Vault and MySQL?
You need an initialized and unsealed Vault server and a running MySQL server. See the previous post how to setup Vault.
Then you can set up the MySQL backend along the connection details and role declaration:
First, mount the mysql
secret backend with
$ vault mount mysql
Successfully mounted 'mysql' at 'mysql'!
Each mount correlates to one MySQL server. If you want to generate credentials for multiple servers, then you need to mount the backend multiple times using different paths.
Once the backend is mounted, you need to supply the control connection details that consist of a username/password combination and the host and port. The control connection is used to create and revoke users.
$ vault write mysql/config/connection \
connection_url="root:root@tcp(127.0.0.1:3306)/"
Success! Data written to: mysql/config/connection
The role contains the scripts for user creation and the authorization. You can include multiple commands if you wish to do so. Vault will execute these commands on each credential generation:
$ vault write mysql/roles/readonly \
sql="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT ON *.* TO '{{name}}'@'%';"
Success! Data written to: mysql/roles/readonly
Vault is now ready to generate credentials for the readonly
role. You can obtain credentials with
$ vault read mysql/creds/readonly
Key Value
--- -----
lease_id mysql/creds/readonly/2e7cd1d0-e313-158e-c9a4-1dd3c3277642
lease_duration 2592000
lease_renewable true
password 04cf512a-57f8-d146-9cf5-ec2bd829ca8c
username token-10a8b69f-a
and verify they work using the mysql
console application:
$ mysql -h 127.0.0.1 -utoken-10a8b69f-a -p04cf512a-57f8-d146-9cf5-ec2bd829ca8c
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5
...
Now that we know our Vault/MySQL integration is working we can use it inside a Spring Boot application with Spring Cloud Vault.
Grab a Spring Boot project. start.spring.io is a good starting point.
Include the Spring Cloud Vault Starter, the Database dependency, spring-jdbc
and the MySQL driver in your project.
Add the following code to your build configuration file. These lines include all required dependencies.
Maven
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-starter-config</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-config-databases</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/libs-snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
Gradle
repositories {
maven {
url 'https://repo.spring.io/libs-snapshot'
}
}
dependencies {
compile("org.springframework.cloud:spring-cloud-vault-starter-config:1.0.0.BUILD-SNAPSHOT")
compile("org.springframework.cloud:spring-cloud-vault-config-databases:1.0.0.BUILD-SNAPSHOT")
compile("org.springframework:spring-jdbc:4.3.1.RELEASE")
compile("mysql:mysql-connector-java:5.1.39")
}
Spring Cloud Vault uses by default the generic secret backend. In our case we don’t need the generic backend. So we need to disable the generic
backend and enable the MySQL backend. All configuration needs to be specified in the bootstrap configuration. For this example we use bootstrap.yml
in src/main/resources
:
spring.cloud.vault:
token: 9a63de21-8af7-311a-9a5a-151b6a0d4795
scheme: http
generic:
enabled: false
mysql:
enabled: true
role: readonly
spring.datasource.url: jdbc:mysql://127.0.0.1:3306
We don’t add any database credentials to the config file. These would usually be spring.datasource.username
and spring.datasource.password
.
The spring.cloud.vault.scheme
is set to http
because we’ve started Vault in plaintext HTTP mode (spring.cloud.vault.scheme
defaults to https
). Don’t do this in production. Plaintext makes the whole secret story useless as all listeners on the network can see your secrets
Please note that the token used in the example is the root token.
You can create new tokens with:
$ vault token-create
Key Value
--- -----
token 728d26ae-53a6-d8b6-d7a0-c5f62238ea55
token_accessor 2fd7dcba-39d0-04d3-8d6b-096c3529cf14
token_duration 0
token_renewable true
token_policies [root]
That’s apparently all you need to do for the Vault MySQL integration with your Spring Boot application.
Let’s quickly add some code to your application so you can test the integration with real code.
@SpringBootApplication
public class MySqlApplication {
public static void main(String[] args) {
SpringApplication.run(MySqlApplication.class, args);
}
@Autowired
DataSource dataSource;
@PostConstruct
private void postConstruct() throws Exception {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
ResultSet resultSet = statement.executeQuery("SELECT CURRENT_USER();");
resultSet.next();
System.out.println("Connection works with User: " + resultSet.getString(1));
resultSet.close();
}
}
Then start your application. The code produces an output like:
Connection works with User: token-...
Spring Cloud Vault obtains database credentials at startup and stores those using the default property names so Spring Boot can pick these.
Vault supports several other integrations. We’ve built support for integrations we also support in Spring Boot.
You can use the following services and databases:
You can view a complete example in our examples repository at https://github.com/mp911de/spring-cloud-vault-config-samples.