原文はこちら。
The original article was written by Tomáš Langer (Oracle).
https://medium.com/helidon/helidon-integration-with-vault-8b98641bc519
HashiCorp Cloud Platform (HCP) Vaultはマイクロサービスでよく使われるVault(機密情報を管理するためのソフトウェア)です。 APIはRESTベースで、Helidonはリアクティブクライアントを使ってVaultとの連携を実装しています。
HCP VaultとHelidonの統合は現在実験的な段階であり、本番運用には適していません。APIや機能がまだ完全にテストされておらず、変更の可能性があります。
Vault統合では以下の機能をサポートします。
シークレットエンジン | Key/Value version 1/2 Cubbyhole PKI Transit Database |
認証方法 | Token Kubernetes (k8s) AppRole |
他のシステム運用および構成 | シークレットエンジンや認証方法の有効化・無効化 |
各機能は個別のモジュールとして実装されており、Vault
クラスがそれぞれをバインドしています。Helidon MPでは、インジェクションを使ってこのバインドを自動的に実施するので、好きなシークレットエンジンをインジェクションするだけでよいのです。
これらの機能に加えて、Vaultを以下のように認証することもできます。
トークン認証 | Vaultと接続時にトークンを構成する |
AppRole認証 | AppRole IDとシークレットIDを構成し、Vaultと接続するために使われるテンポラリトークンのために これらのIDを交換する |
k8s認証 | k8sのJWTトークンを現在のノードで発見し、そのJWTトークンを使ってテンポラリトークンを取得し、Vaultに接続する |
Extensibility
統合は(ServiceLoader
を使う)サービスプロバイダーを基にしているため、新たなシークレットエンジンや認証方法を極めて簡単に実装できます。これにより、Vault
クラスに複数のメソッドをしなくても、新たなシークレットエンジンや認証方法を追加できます。
以下のSPIをご覧ください。
io.helidon.integrations.vault.spi.AuthMethodProvider
io.helidon.integrations.vault.spi.SecretsEngineProvider
io.helidon.integrations.vault.spi.SysProvider
io.helidon.integrations.vault.spi.VaultAuth
io.helidon.integrations.vault.spi.InjectionProvider
Maven Coordinates for Vault Modules
以下はHelidon 2.3.0で利用可能な全てのVaultモジュールのMavenの依存関係です。
<dependency>
<groupId>io.helidon.integrations.vault</groupId>
<artifactId>helidon-integrations-vault</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.integrations.vault.auths</groupId>
<artifactId>helidon-integrations-vault-auths-token</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.integrations.vault.auths</groupId>
<artifactId>helidon-integrations-vault-auths-approle</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.integrations.vault.auths</groupId>
<artifactId>helidon-integrations-vault-auths-k8s</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.integrations.vault.secrets</groupId>
<artifactId>helidon-integrations-vault-secrets-kv1</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.integrations.vault.secrets</groupId>
<artifactId>helidon-integrations-vault-secrets-kv2</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.integrations.vault.secrets</groupId>
<artifactId>helidon-integrations-vault-secrets-cubbyhole</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.integrations.vault.secrets</groupId>
<artifactId>helidon-integrations-vault-secrets-transit</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.integrations.vault.secrets</groupId>
<artifactId>helidon-integrations-vault-secrets-database</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.integrations.vault.sys</groupId>
<artifactId>helidon-integrations-vault-sys</artifactId>
</dependency>
Local Testing
VaultはDockerイメージとして利用できるため、ローカルでテストする場合は単純に以下を実行するだけです。
docker run -e VAULT_DEV_ROOT_TOKEN_ID=my-token -d --name=vault -p8200:8200 vault
上記コマンドでVaultのDockerイメージを作成し、バックグランドで開始し、localhost:8220
でカスタムルートトークンmy-token
を使い、vault
という名前を使ってオープンします。ルートトークンには多くの権限があるため、これはもちろんローカルテスト用途限定ではありますが、以下の例を使い簡単に利用できます。
Vault Integration with Helidon MP
Helidon MPでVault統合を利用するには、すべての機能のインジェクションを可能にするため、以下のモジュールを追加する必要があります。
<dependency>
<groupId>io.helidon.integrations.vault</groupId>
<artifactId>helidon-integrations-vault-cdi</artifactId>
</dependency>
Vault接続の構成は以下の通りです(トークン認証とAppRole認証を使う場合)
vault.default.address=http://localhost:8200
vault.default.token=my-token
vault.approle.address=http://localhost:8200
vault.approle.auth.app-role.role-id=app-role-id
vault.approle.auth.app-role.secret-id=app-role-secret-id
(適切なモジュールがクラスパスにある場合)以下のクラスを任意のCDI beanにインジェクトできます。
Kv2Secrets | Key/Value Version 2 Secrets (バージョン付きシークレット、デフォルト) |
Kv1Secrets | Key/Value Version 1 Secrets (バージョンなしシークレット、レガシー) |
CubbyholeSecrets | Cubbyhole secrets(トークンがバインドされたシークレット) |
DbSecrets | Database secrets(一時的なDBの資格証明生成用) |
PkiSecrets | PKI secrets(キーとX.509証明書生成用) |
TransitSecrets | Transit operations(暗号化、署名、HMAC) |
AppRoleAuth | AppRole認証(管理オペレーション) |
K8sAuth | Kubernetes認証(管理オペレーション) |
TokenAuth | トークン認証(管理オペレーション) |
Sys | システム運用(Vaultの管理。シークレットエンジンや認証方法の有効・無効化) |
*Rx | 上記のクラスに対応するリアクティブカウンターパート CDIがブロッキング環境であるため、通常はCDIでの利用は推奨されません。 |
以下は、(JAX-RSリソースにおいて)デフォルトのVault構成を使ってシークレットを暗号化する場合のVaultの利用例です。
private final TransitSecrets secrets;
@Inject
TransitResource(TransitSecrets secrets) {
this.secrets = secrets;
}
//...
@Path("/encrypt/{secret: .*}")
@GET
public String encrypt(@PathParam("secret") String secret) {
return secrets.encrypt(Encrypt.Request.builder()
.encryptionKeyName(ENCRYPTION_KEY)
.data(Base64Value.create(secret)))
.encrypted()
.cipherText();
}
そして以下はAppRole Vault構成を使ってシークレットを取得する場合のVaultの利用例です。
private final Kv2Secrets secrets;
@Inject
Kv2Resource(@VaultName("app-role") Kv2Secrets secrets) {
this.secrets = secrets;
}
@GET
@Path("/secrets/{path: .*}")
public Response getSecret(@PathParam("path") String path) {
Optional<Kv2Secret> secret = secrets.get(path);
if (secret.isPresent()) {
Kv2Secret kv2Secret = secret.get();
return Response.ok()
.entity("Version " + kv2Secret.metadata().version() + ", secret: " + kv2Secret.values().toString())
.build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
Advanced Configuration
エンジンに@VaultPath("path")
をインジェクトして正しいパスを取得することにより、カスタムパスでのシークレットエンジンを有効化できます。上記のサンプルコードにて @VaultName
の使い方を示していますが、これは設定を区別するために使用しています。
Vault Integration with Helidon SE
Helidon SEではインジェクションを使用しないため、リアクティブなコードではRx
という接尾辞のついたAPIを使用し、「手作業」で取得する必要があります。
Vaultに接続するための最小限の構成です。
vault:
token: "my-token"
address: "http://localhost:8200"
Vaultのセットアップおよび特定のシークレットエンジンを取得するためのコードです。
Vault vault = Vault.builder()
.config(config.get("vault"))
.build();
Kv2SecretsRx secrets = vault.secrets(Kv2SecretsRx.ENGINE);
利用可能な任意のシークレットエンジンで同様のコードを利用できます。
Kv2SecretsRx | Key/Value Version 2 Secrets (バージョン付きシークレット、デフォルト) |
Kv1SecretsRx | Key/Value Version 1 Secrets (バージョンなしシークレット、レガシー) |
CubbyholeSecretsRx | Cubbyhole secrets(トークンがバインドされたシークレット) |
DbSecretsRx | Database secrets(一時的なDBの資格証明生成用) |
PkiSecretsRx | PKI secrets(キーとX.509証明書生成用) |
TransitSecretsRx | Transit operations(暗号化、署名、HMAC) |
特定の認証方法を取得するコードです。
K8sAuthRx auth = vault.auth(K8sAuthRx.AUTH_METHOD)
同様のコードを任意の利用可能な認証方法で利用できます。
AppRoleAuthRx | AppRole認証(管理オペレーション) |
K8sAuthRx | Kubernetes認証(管理オペレーション) |
TokenAuthRx | トークン認証(管理オペレーション) |
最後にVaultのSys
オペレーションを取得するためのコードです。
SysRx sys = vault.sys(SysRx.API);
Vault with WebServer
それでは、エンドツーエンドのリアクティブな動作を使って、WebServer を使った例を作ってみましょう。ここでは Vault API に加え、ServerRequest (req)
と ServerResponse (res)
を使用します。
以下は、シークレットの取得やデータの暗号化/復号化を行うサーバーメソッドの例です。
private void getSecret(ServerRequest req, ServerResponse res) {
String path = req.path().param("path");
secrets.get(path)
.thenAccept(secret -> {
if (secret.isPresent()) {
// using toString so we do not need to depend on JSON-B
Kv2Secret kv2Secret = secret.get();
res.send("Version " + kv2Secret.metadata().version() + ", secret: " + kv2Secret.values().toString());
} else {
res.status(Http.Status.NOT_FOUND_404);
res.send();
}
})
.exceptionally(res::send);
}private void encryptSecret(ServerRequest req, ServerResponse res) {
String secret = req.path().param("text");
secrets.encrypt(Encrypt.Request.builder()
.encryptionKeyName(ENCRYPTION_KEY)
.data(Base64Value.create(secret)))
.forSingle(response -> res.send(response.encrypted().cipherText()))
.exceptionally(res::send);
}private void decryptSecret(ServerRequest req, ServerResponse res) {
String encrypted = req.path().param("text");
secrets.decrypt(Decrypt.Request.builder()
.encryptionKeyName(ENCRYPTION_KEY)
.cipherText(encrypted))
.forSingle(response -> res.send(String.valueOf(response.decrypted().toDecodedString())))
.exceptionally(res::send);
}
Learn More
Helidonの詳細は、プロジェクトのWebサイトやGitHubリポジトリをチェックしてください。
Project Helidon
https://helidon.io
Helidon: Java Libraries for Microservices
https://github.com/oracle/helidon