--- 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バックエンドを紹介する。