Vault のシークレットエンジンのサポート

Spring Vault には、Vault のさまざまなシークレットエンジンをサポートするためのいくつかの拡張機能が付属しています。

具体的には、Spring Vault には次の拡張機能が付属しています。

他のすべてのバックエンドは、VaultTemplate のメソッド (VaultTemplate.read(…)VaultTemplate.write(…)) を通じて直接使用できます。

キーと値のバージョン 1 (「バージョン管理されていないシークレット」)

kv シークレットエンジンは、Vault 用に構成された物理ストレージ内に任意のシークレットを保存するために使用されます。

kv シークレットエンジンをバージョン管理されていない方法で実行する場合、キーに対して最後に書き込まれた値のみが保持されます。バージョン管理されていない KV の利点は、追加のメタデータや履歴が保存されないため、各キーのストレージサイズが削減されることです。さらに、このように構成されたバックエンドに送信されるリクエストは、ストレージ呼び出しが少なく、特定のリクエストに対するロックがないため、パフォーマンスが向上します。

Spring Vault には、個々の Key-Value API 実装間の差異をカプセル化する専用の Key-Value API が付属しています。VaultKeyValueOperations は Vault CLI 設計に従っています。これは、vault kv getvault kv put などのコマンドを提供する Vault の主要なコマンドラインツールです。

バージョンとマウントパスを指定することで、この API を Key-Value エンジンの両方のバージョンで使用できます。次の例では、Key-Value バージョン 1 を使用します。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultKeyValueOperations keyValueOperations = operations.opsForKeyValue("secret",
							VaultKeyValueOperationsSupport.KeyValueBackend.KV_1);

keyValueOperations.put("elvis", Collections.singletonMap("password", "409-52-2002"));

VaultResponse read = keyValueOperations.get("elvis");
read.getRequiredData().get("social-security-number");

VaultKeyValueOperations は、putgetdeletelist などのすべての Key-Value 操作をサポートします。

あるいは、キーとレスポンスが入力キーと出力キーに直接マッピングされるため、API は直接マッピングされ、簡単に使用できるため、VaultTemplate を通じて使用することもできます。次の例は、mykey でのシークレットの書き込みと読み取りを示しています。kv シークレットエンジンは secret にマウントされます。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());

operations.write("secret/elvis", Collections.singletonMap("social-security-number", "409-52-2002"));

VaultResponse read = operations.read("secret/elvis");
read.getRequiredData().get("social-security-number");

Vault Key-Value バージョン 1 API (英語) の詳細については、Vault リファレンスドキュメントを参照してください。

キーと値のバージョン 2 (「バージョン管理されたシークレット」)

kv シークレットエンジンは 2 つのバージョンのいずれかで実行できます。このセクションでは、バージョン 2 の使用について説明します。kv バックエンドのバージョン 2 を実行する場合、キーは構成可能な数のバージョンを保持できます。古いバージョンのメタデータとデータを取得できます。さらに、チェックアンドセット操作を使用して、データが意図せず上書きされることを避けることができます。

キーと値のバージョン 1 (「バージョン管理されていないシークレット」) と同様に、Spring Vault には、個々の Key-Value API 実装間の差異をカプセル化する専用の Key-Value API が付属しています。Spring Vault には、個々の Key-Value API 実装間の差異をカプセル化する専用の Key-Value API が付属しています。VaultKeyValueOperations は Vault CLI 設計に従っています。これは Vault の主要なコマンドラインツールであり、vault kv getvault kv put などのコマンドを提供します。

バージョンとマウントパスを指定することで、この API を Key-Value エンジンの両方のバージョンで使用できます。次の例では、Key-Value バージョン 2 を使用します。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultKeyValueOperations keyValueOperations = operations.opsForKeyValue("secret",
							VaultKeyValueOperationsSupport.KeyValueBackend.KV_2);

keyValueOperations.put("elvis", Collections.singletonMap("social-security-number", "409-52-2002"));

VaultResponse read = keyValueOperations.get("elvis");
read.getRequiredData().get("social-security-number");

VaultKeyValueOperations は、putgetdeletelist などのすべての Key-Value 操作をサポートします。

バージョン管理された Key-Value API の詳細を操作することもできます。これは、特定のシークレットを取得したい場合、またはメタデータにアクセスする必要がある場合に便利です。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultVersionedKeyValueOperations versionedOperations = operations.opsForVersionedKeyValue("secret");

Versioned.Metadata metadata = versionedOperations.put("elvis",							(1)
					Collections.singletonMap("social-security-number", "409-52-2002"));

Version version = metadata.getVersion();												(2)

Versioned<Object> ssn = versionedOperations.get("elvis", Version.from(42));				(3)

Versioned<SocialSecurityNumber> mappedSsn = versionedOperations.get("elvis",			(4)
											Version.from(42), SocialSecurityNumber.class);

Versioned<Map<String,String>> versioned = Versioned.create(Collections					(5)
						.singletonMap("social-security-number", "409-52-2002"),
						Version.from(42));

versionedOperations.put("elvis", version);
1secret/ マウントで使用できる elvis にシークレットを保存します。
2 バージョン管理されたバックエンドにデータを保存すると、バージョン番号などのメタデータが返されます。
3 バージョン管理された Key-Value API を使用すると、バージョン番号で識別される特定のバージョンを取得できます。
4 バージョン管理されたキーと値のシークレットは、値オブジェクトにマッピングできます。
5CAS を使用してバージョン付きシークレットを更新する場合、入力は以前に取得したバージョンを参照する必要があります。

kv v2 シークレットエンジンを使用しながら、VaultTemplate を使用することが可能です。API はコンテキストパスと入出力の表現方法に対して異なるアプローチを提供するため、これは最も便利なアプローチではありません。具体的には、実際のシークレットとの対話には、データセクションのラップとラップ解除、およびマウントとシークレットキーの間に data/ パスセグメントの導入が必要です。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());

operations.write("secret/data/elvis", Collections.singletonMap("data",
			Collections.singletonMap("social-security-number", "409-52-2002")));

VaultResponse read = operations.read("secret/data/ykey");
Map<String,String> data = (Map<String, String>) read.getRequiredData().get("data");
data.get("social-security-number");

Vault Key-Value バージョン 2 API (英語) の詳細については、Vault リファレンスドキュメントを参照してください。

PKI (公開鍵インフラストラクチャ)

pki シークレットエンジンは、認証局の操作を実装することにより、証明書のバックエンドを表します。

PKI シークレットエンジンは、動的な X.509 証明書を生成します。このシークレットエンジンを使用すると、サービスは、秘密キーと CSR を生成し、CA に送信し、検証と署名のプロセスが完了するのを待つという通常の手動プロセスを経ることなく、証明書を取得できます。Vault の組み込みの認証および認可メカニズムは、検証機能を提供します。

Spring Vault は、VaultPkiOperations を介した証明書の発行、署名、取り消し、CRL の取得をサポートします。他のすべての PKI 機能は、VaultOperations を通じて使用できます。

次の例では、証明書を発行および取り消す方法の使用箇所を簡単に説明します。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultPkiOperations pkiOperations = operations.opsForPki("pki");

VaultCertificateRequest request = VaultCertificateRequest.builder()								(1)
			.ttl(Duration.ofHours(48))
			.altNames(Arrays.asList("prod.dc-1.example.com", "prod.dc-2.example.com"))
			.withIpSubjectAltName("1.2.3.4")
			.commonName("hello.example.com")
			.build();

VaultCertificateResponse response = pkiOperations.issueCertificate("production", request); 		(2)
CertificateBundle certificateBundle = response.getRequiredData();

KeyStore keyStore = certificateBundle.createKeyStore("my-keystore");							(3)

KeySpec privateKey = certificateBundle.getPrivateKeySpec();										(4)
X509Certificate certificate = certificateBundle.getX509Certificate();
X509Certificate caCertificate = certificateBundle.getX509IssuerCertificate();

pkiOperations.revoke(certificateBundle.getSerialNumber());										(5)
1VaultCertificateRequest ビルダーを使用して証明書リクエストを作成します。
2Vault に証明書をリクエストします。Vault は認証局として機能し、署名された X.509 証明書でレスポンスします。実際のレスポンスは CertificateBundle です。
3 生成された証明書は、公開キー、秘密キー、発行者証明書を含む Java KeyStore として直接取得できます。KeyStore には幅広い用途があるため、この形式は構成に適しています (HTTP クライアント、データベースドライバー、SSL で保護された HTTP サーバーなど)。
4CertificateBundle を使用すると、Java Cryptography Extension API を介して秘密キー、公開証明書、発行者証明書に直接アクセスできます。
5 証明書が使用されなくなった (または侵害された) 場合は、シリアル番号を使用して証明書を取り消すことができます。Vault の CRL には、失効した証明書が含まれています。

Vault PKI シークレット API (英語) の詳細については、Vault リファレンスドキュメントを参照してください。

トークン認証バックエンド

このバックエンドは、実際のシークレットと対話しない認証バックエンドです。むしろ、アクセストークン管理へのアクセスが可能になります。トークンベースの認証の詳細については、「認証方法」の章を参照してください。

token 認証方法は組み込まれており、/auth/token で自動的に使用可能になります。これにより、ユーザーはトークンを使用して認証できるほか、新しいトークンの作成、トークンによるシークレットの取り消しなどが可能になります。

他の認証メソッドが ID を返すと、Vault コアはトークンメソッドを呼び出して、その ID に対して新しい一意のトークンを作成します。

トークンストアを使用して、他の認証方法をバイパスすることもできます。トークンを直接作成したり、更新や失効など、トークンに対して他のさまざまな操作を実行したりできます。

Spring Vault は、このバックエンドを使用して、構成された認証方法によって提供されるセッショントークンを更新および取り消します。

次の例は、アプリケーション内から Vault トークンをリクエスト、更新、取り消す方法を示しています。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultTokenOperations tokenOperations = operations.opsForToken();

VaultTokenResponse tokenResponse = tokenOperations.create();                          (1)
VaultToken justAToken = tokenResponse.getToken();

VaultTokenRequest tokenRequest = VaultTokenRequest.builder().withPolicy("policy-for-myapp")
									.displayName("Access tokens for myapp")
									.renewable()
									.ttl(Duration.ofHours(1))
									.build();

VaultTokenResponse appTokenResponse = tokenOperations.create(tokenRequest);          (2)
VaultToken appToken = appTokenResponse.getToken();

tokenOperations.renew(appToken);                                                     (3)

tokenOperations.revoke(appToken);                                                    (4)
1 ロールのデフォルトを適用してトークンを作成します。
2 ビルダー API を使用すると、リクエストするトークンの詳細な設定を定義できます。トークンをリクエストすると、VaultToken が返されます。これは、Vault トークンの値オブジェクトとして使用されます。
3 トークン API を通じてトークンを更新できます。通常、これは Vault セッショントークンを追跡するために SessionManager によって行われます。
4 必要に応じて、トークン API を通じてトークンを取り消すことができます。通常、これは Vault セッショントークンを追跡するために SessionManager によって行われます。

Vault トークン認証メソッド API (英語) の詳細については、Vault リファレンスドキュメントを参照してください。

トランジットバックエンド

転送シークレットエンジンは、転送中のデータの暗号化機能を処理します。Vault は、このシークレットエンジンに送信されたデータを保存しません。「サービスとしての暗号化」または「サービスとしての暗号化」と見なすこともできます。トランジットシークレットエンジンは、データの署名と検証、データのハッシュと HMAC の生成、ランダムバイトソースとして機能することもできます。

転送の主な使用例は、アプリケーションからのデータを暗号化し、その暗号化されたデータをプライマリデータストアに保存することです。これにより、アプリケーション開発者からの適切な暗号化と復号化の負担が軽減され、Vault のオペレータに負担が課せられます。

Spring Vault は、幅広い交通機関の運用をサポートします。

  • キーの作成

  • キーの再構成

  • 暗号化 / 復号化 / 再折り返し

  • HMAC 計算

  • 署名と署名の検証

transit 内のすべての操作はキーを中心に行われます。Transit エンジンは、キーのバージョン管理とさまざまな種類の鍵 (英語) をサポートしています。キーの種類によっては、使用できる操作が制限される場合があることに注意してください。

次の例は、キーの作成方法とデータの暗号化と復号化の方法を示しています。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultTransitOperations transitOperations = operations.opsForTransit("transit");

transitOperations.createKey("my-aes-key", VaultTransitKeyCreationRequest.ofKeyType("aes128-gcm96"));	(1)

String ciphertext = transitOperations.encrypt("my-aes-key", "plaintext to encrypt");					(2)

String plaintext = transitOperations.decrypt("my-aes-key", ciphertext);									(3)
1 まず、最初にキーが必要です。各キーには型を指定する必要があります。aes128-gcm96 は、暗号化、復号化、キー導出、収束暗号化をサポートしています。この例では、そのうちの暗号化と復号化が必要です。
2 次に、暗号化する必要があるプレーンテキストを含む String を暗号化します。入力 String は、デフォルトの Charset を使用して文字列をバイナリ表現にエンコードします。トークンをリクエストすると、VaultToken が返されます。これは、Vault トークンの値オブジェクトとして使用されます。encrypt メソッドは、通常 vault: で始まる Base64 でエンコードされた暗号文を返します。
3 暗号文を平文に復号するには、decrypt メソッドを呼び出します。暗号文を復号化し、デフォルトの文字セットを使用してデコードされた String を返します。

前述の例では、暗号化操作に単純な文字列を使用しています。これは単純なアプローチではありますが、文字セットの設定ミスのリスクがあり、バイナリセーフではありません。バイナリセーフティは、プレーンテキストでイメージ、圧縮データ、バイナリデータ構造などのデータにバイナリ表現が使用されている場合に必要です。

バイナリデータを暗号化および復号化するには、バイナリ値を保持できる Plaintext および Ciphertext 値オブジェクトを使用します。

byte [] plaintext = "plaintext to encrypt".getBytes();

Ciphertext ciphertext = transitOperations.encrypt("my-aes-key", Plaintext.of(plaintext));			(1)

Plaintext decrypttedPlaintext = transitOperations.decrypt("my-aes-key", ciphertext);				(2)
1 キー my-aes-key がすでに存在していると仮定すると、Plaintext オブジェクトを暗号化します。これに対して、encrypt メソッドは Ciphertext オブジェクトを返します。
2Ciphertext オブジェクトは復号化に直接使用でき、Plaintext オブジェクトを返します。

Plaintext および Ciphertext には、コンテキストオブジェクト VaultTransitContext が付属しています。これは、収束暗号化 (英語) の nonce 値と、キー導出を利用するための context 値を提供するために使用されます。

Transit では、プレーンテキストに署名し、指定されたプレーンテキストの署名を検証できます。署名操作には非対称キーが必要で、通常は楕円曲線暗号 (RSA) を使用します。

署名では、公開鍵と秘密鍵の分割を使用して信頼性を確保します。
署名者は秘密キーを使用して署名を作成します。そうしないと、誰でもあなたの名前でメッセージに署名できてしまいます。検証者は公開鍵部分を使用して署名を検証します。実際の署名は通常、ハッシュ値です。

内部的には、秘密キーを使用してハッシュが計算され、暗号化され、最終的な署名が作成されます。検証では、署名メッセージを復号化し、プレーンテキストに対して独自のハッシュを計算し、両方のハッシュ値を比較して、署名が有効かどうかを確認します。
byte [] plaintext = "plaintext to sign".getBytes();

transitOperations.createKey("my-ed25519-key", VaultTransitKeyCreationRequest.ofKeyType("ed25519"));	(1)

Signature signature = transitOperations.sign("my-ed25519-key", Plaintext.of(plaintext));			(2)

boolean valid = transitOperations.verify("my-ed25519-key", Plaintext.of(plaintext), signature);		(3)
1 署名には非対称キーが必要です。任意の楕円曲線暗号または RSA キー型を使用できます。キーが作成されると、署名を作成するための前提条件がすべて整います。
2 署名はプレーンテキストメッセージに対して作成されます。返された Signature には、Base64 文字を使用する ASCII セーフな文字列が含まれています。
3 署名を検証するには、Signature オブジェクトとプレーンテキストメッセージが必要です。戻り値として署名が有効かどうかを取得します。

Vault トランジットバックエンド (英語) の詳細については、Vault リファレンスドキュメントを参照してください。