目次
SpringのVaultサポート
SpringのVaultサポートは次の3つある
プロジェクト名 | 説明 |
---|---|
Spring Vault | VaultサポートのCore機能。Vault APIをRestTemplate でラップしたものと、ProperteySource にマッピングするもの |
Spring Cloud Vault | Spring Cloud ConfigのConfig Serverの代わりにVaultを使うもの。データベースのCredentialsの生成もできる。内部でSpring Vaultを使用している。 |
Spring Cloud Config | Spring Cloud ConfigのConfig ServerのバックエンドにVaultを使えるもの。 GitとVaultを組み合わせたConfig Serverを作成できる。Spring Vaultを使用していない。 |
この記事ではSpring Vaultについて簡単に紹介する。その他は別の記事にて。
Vaultのセットアップ
ローカルで立ててもいいが、ここではこちらで立てたCloud Foundry上のVault Serverを使う。
プロジェクトの作成
普通のSpring Bootプロジェクトに次のdependency
を追加。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo-spring-vault</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo-spring-vault</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
VaultTemplate
まずはRestTemplate
でVault API(vault
コマンド)にアクセスする処理をラップしたVaultTemplate
を使う。
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> hello = vaultTemplate.read("secret/hello",
Hello.class);
System.out.println(hello.getData().getValue());
// vault read secret/hello (=> Map<String, Object>)
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=<Set your 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
をアプリケーションに設定するようにすると、ソースコードは次のようになる。
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("<Set your Vault token>");
}
}
@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バックエンドを紹介する。