---
title: SpringのVaultサポートその1 (Spring Vault)
tags: ["Spring Vault", "Spring Cloud", "Spring Boot", "Java", "Vault"]
categories: ["Programming", "Java", "org", "springframework", "vault"]
date: 2017-06-26T13:32:43Z
updated: 2017-06-26T13:56:45Z
---
**目次**
### SpringのVaultサポート
SpringのVaultサポートは次の3つある
| プロジェクト名 | 説明 |
| --- | --- |
| [Spring Vault](http://projects.spring.io/spring-vault/) | VaultサポートのCore機能。Vault APIを`RestTemplate`でラップしたものと、`ProperteySource`にマッピングするもの |
| [Spring Cloud Vault](http://cloud.spring.io/spring-cloud-vault/) | Spring Cloud ConfigのConfig Serverの代わりにVaultを使うもの。データベースのCredentialsの生成もできる。内部でSpring Vaultを使用している。 |
| [Spring Cloud Config](http://cloud.spring.io/spring-cloud-config/) | Spring Cloud ConfigのConfig Serverの[バックエンドにVault](http://cloud.spring.io/spring-cloud-static/spring-cloud-config/1.3.1.RELEASE/#_vault_backend)を使えるもの。 GitとVaultを組み合わせたConfig Serverを作成できる。Spring Vaultを使用していない。 |
この記事ではSpring Vaultについて簡単に紹介する。その他は別の記事にて。
### Vaultのセットアップ
ローカルで立ててもいいが、ここでは[こちら](https://blog.ik.am/entries/423)で立てたCloud Foundry上のVault Serverを使う。
### プロジェクトの作成
普通のSpring Bootプロジェクトに次の`dependency`を追加。
``` xml
4.0.0
com.example
demo-spring-vault
0.0.1-SNAPSHOT
jar
demo-spring-vault
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
1.5.4.RELEASE
UTF-8
UTF-8
1.8
Dalston.SR1
org.springframework.vault
spring-vault-core
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
```
### `VaultTemplate`
まずは`RestTemplate`でVault API(`vault`コマンド)にアクセスする処理をラップした`VaultTemplate`を使う。
``` java
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.AbstractVaultConfiguration;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultResponseSupport;
import com.fasterxml.jackson.annotation.JsonProperty;
@SpringBootApplication
public class DemoSpringVaultApplication {
public static void main(String[] args) {
SpringApplication.run(DemoSpringVaultApplication.class, args);
}
@Bean
CommandLineRunner commandLineRunner(VaultTemplate vaultTemplate) {
return x -> {
// vault write secret/hello value=world
vaultTemplate.write("secret/hello", new Hello("world"));
// vault read secret/hello (=> POJO)
VaultResponseSupport hello = vaultTemplate.read("secret/hello",
Hello.class);
System.out.println(hello.getData().getValue());
// vault read secret/hello (=> Map)
VaultResponse r = vaultTemplate.read("secret/hello");
System.out.println(r.getData());
};
}
public static class Hello {
final String value;
public Hello(@JsonProperty("value") String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
@Configuration
public static class VaultConfig extends AbstractVaultConfiguration {
@Value("${spring.cloud.vault.token}")
String token;
@Value("${spring.cloud.vault.host:localhost}")
String host;
@Value("${spring.cloud.vault.port:8200}")
int port;
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create(host, port);
}
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication(token);
}
}
}
```
`application.properties`に次の設定を
```
spring.cloud.vault.host=cf-vault.cfapps.io
spring.cloud.vault.port=443
spring.cloud.vault.token=
# following is not necessary
spring.main.web-environment=false
logging.level.org.springframework.web.client.RestTemplate=DEBUG
```
実行すると
```
(略)
2017-06-26 18:31:30.378 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : Created POST request for "https://cf-vault.cfapps.io:443/v1/secret/hello"
2017-06-26 18:31:30.415 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : Setting request Accept header to [application/json, application/*+json]
2017-06-26 18:31:30.421 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : Writing [com.example.demo.DemoSpringVaultApplication$Hello@52851b44] using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@584f54e6]
2017-06-26 18:31:35.764 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : POST request for "https://cf-vault.cfapps.io:443/v1/secret/hello" resulted in 204 (No Content)
2017-06-26 18:31:35.767 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : Created GET request for "https://cf-vault.cfapps.io:443/v1/secret/hello"
2017-06-26 18:31:35.770 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : Setting request Accept header to [application/json, application/*+json]
2017-06-26 18:31:35.942 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : GET request for "https://cf-vault.cfapps.io:443/v1/secret/hello" resulted in 200 (OK)
2017-06-26 18:31:35.943 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : Reading [org.springframework.vault.client.VaultResponses$1@269f4bad] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@584f54e6]
world
2017-06-26 18:31:35.950 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : Created GET request for "https://cf-vault.cfapps.io:443/v1/secret/hello"
2017-06-26 18:31:35.951 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : Setting request Accept header to [application/json, application/*+json]
2017-06-26 18:31:36.120 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : GET request for "https://cf-vault.cfapps.io:443/v1/secret/hello" resulted in 200 (OK)
2017-06-26 18:31:36.121 DEBUG 77397 --- [ main] o.s.web.client.RestTemplate : Reading [class org.springframework.vault.support.VaultResponse] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@584f54e6]
{value=world}
(略)
```
簡単。認証方式ごとに`ClientAuthentication`の以下の実装クラスが用意されている。
* `org.springframework.vault.authentication.AppIdAuthentication`
* `org.springframework.vault.authentication.AppRoleAuthentication`
* `org.springframework.vault.authentication.AwsEc2Authentication`
* `org.springframework.vault.authentication.ClientCertificateAuthentication`
* `org.springframework.vault.authentication.CubbyholeAuthentication`
* `org.springframework.vault.authentication.TokenAuthentication`
### `@VaultPropertySource`
`@ProprtySource`のVault版で`properties`ファイルの代わりにVaultから設定を読み込み、`Environment`オブジェクトや`@Vaule`で参照できる。
`vault`コマンドで次の設定をしておく。
```
$ vault write secret/myapp foo=100 bar=aaa
Success! Data written to: secret/myapp
$ vault read secret/myapp
Key Value
--- -----
refresh_interval 768h0m0s
bar aaa
foo 100
```
この`foo`と`bar`をアプリケーションに設定するようにすると、ソースコードは次のようになる。
``` java
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.vault.annotation.VaultPropertySource;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.AbstractVaultConfiguration;
@SpringBootApplication
@VaultPropertySource("secret/myapp")
public class DemoSpringVaultApplication {
public static void main(String[] args) {
SpringApplication.run(DemoSpringVaultApplication.class, args);
}
@Bean
CommandLineRunner commandLineRunner(MyApp myApp) {
return x -> {
myApp.hello();
};
}
@Configuration
public static class VaultConfig extends AbstractVaultConfiguration {
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("cf-vault.cfapps.io", 443);
}
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("");
}
}
@Component
public static class MyApp {
@Value("${foo}")
int foo;
@Value("${bar}")
String bar;
public void hello() {
System.out.println(foo + " " + bar);
}
}
}
```
実行すると、
```
(略)
100 aaa
(略)
```
出ました。
Renewalもサポートされているけれど、今回は紹介しない。
注意点は、Valutの接続情報は`PropertySource`ができる前に設定されていないといけない(`bootstrap.properties`など)。今回は`VaultConfig`クラスにハードコードした。
### 続く
次はSpring Cloud VaultかSpring Cloud ConfigのVaultバックエンドを紹介する。