VaultTemplate 簡介

位於 org.springframework.vault.core 套件的 VaultTemplate 類別是 Spring Vault 支援的核心類別,提供豐富的功能集來與 Vault 互動。此樣板提供便利的操作,以在 Vault 中讀取、寫入和刪除資料,並提供您的領域物件與 Vault 資料之間的對應。

一旦設定完成,VaultTemplate 即為執行緒安全,且可在多個執行個體之間重複使用。

Vault 文件與領域類別之間的對應是透過委派給 RestTemplate 來完成。Spring Web 支援提供對應基礎架構。

VaultTemplate 類別實作了 VaultOperations 介面。在盡可能範圍內,VaultOperations 上的方法會以 Vault API 上可用的方法命名,使 API 對於已習慣 API 和 CLI 的現有 Vault 開發人員來說感到熟悉。例如,您會找到諸如 "write"、"delete"、"read" 和 "revoke" 等方法。設計目標是盡可能輕鬆地在 Vault API 和 VaultOperations 的使用之間進行轉換。這兩個 API 之間的主要區別在於 VaultOperations 可以傳遞領域物件,而不是 JSON 鍵值組。

參考 VaultTemplate 執行個體上的操作的首選方式是透過其介面 VaultOperations

雖然 VaultTemplate 上有許多便利方法可協助您輕鬆執行常見任務,但如果您需要直接存取 Vault API 以存取 VaultTemplate 未明確公開的功能,您可以使用數個執行回呼方法之一來存取基礎 API。執行回呼將為您提供 RestOperations 物件的參考。請參閱 執行回呼 章節以取得更多資訊。

現在讓我們看看如何在 Spring 容器的環境中使用 Vault 的範例。

註冊和設定 Spring Vault Bean

使用 Spring Vault 不需要 Spring Context。但是,在受管理容器內註冊的 VaultTemplateSessionManager 執行個體將參與 Spring IoC 容器提供的 生命週期事件。這對於在應用程式關閉時處置作用中的 Vault 工作階段很有用。您還可以受益於在應用程式中重複使用相同的 VaultTemplate 執行個體。

Spring Vault 隨附一個支援組態類別,該類別提供在 Spring 容器內使用的 Bean 定義。應用程式組態類別通常從 AbstractVaultConfiguration 擴充,並且需要提供環境特定的其他詳細資訊。

AbstractVaultConfiguration 擴充需要實作 VaultEndpoint vaultEndpoint()ClientAuthentication clientAuthentication() 方法。

範例 1. 使用 Java 基礎 Bean Metadata 註冊 Spring Vault 物件
@Configuration
public class AppConfig extends AbstractVaultConfiguration {

    /**
     * Specify an endpoint for connecting to Vault.
     */
    @Override
    public VaultEndpoint vaultEndpoint() {
        return new VaultEndpoint();                            (1)
    }

    /**
     * Configure a client authentication.
     * Please consider a more secure authentication method
     * for production use.
     */
    @Override
    public ClientAuthentication clientAuthentication() {
        return new TokenAuthentication("…");                   (2)
    }
}
1 建立一個新的 VaultEndpoint,預設指向 https://127.0.0.1:8200
2 此範例使用 TokenAuthentication 快速開始使用。請參閱 [vault.core.authentication] 以取得有關支援的驗證方法的詳細資訊。
範例 2. 註冊 Spring Vault 並套用注入的屬性
@Configuration
public class AppConfig extends AbstractVaultConfiguration {

    @Value("${vault.uri}")
    URI vaultUri;

    /**
     * Specify an endpoint that was injected as URI.
     */
    @Override
    public VaultEndpoint vaultEndpoint() {
        return VaultEndpoint.from(vaultUri);                          (1)
    }

    /**
     * Configure a Client Certificate authentication.
     * {@link RestOperations} can be obtained from {@link #restOperations()}.
     */
    @Override
    public ClientAuthentication clientAuthentication() {
        return new ClientCertificateAuthentication(restOperations()); (2)
    }
}
1 VaultEndpoint 可以使用各種 Factory 方法建構,例如 from(URI uri)VaultEndpoint.create(String host, int port)
2 ClientAuthentication 方法的相依性可以從 AbstractVaultConfiguration 取得,或由您的組態提供。
在某些情況下,建立自訂組態類別可能很麻煩。請查看 EnvironmentVaultConfiguration,它允許使用現有屬性來源和 Spring 的 Environment 中的屬性進行組態。請在 使用 EnvironmentVaultConfiguration 中閱讀更多資訊。

工作階段管理

Spring Vault 需要 ClientAuthentication 才能登入和存取 Vault。請參閱 [vault.core.authentication] 以取得有關驗證的詳細資訊。Vault 登入不應在每次經過驗證的 Vault 互動時發生,而必須在整個工作階段中重複使用。此方面由 SessionManager 實作處理。SessionManager 決定取得 Token 的頻率、關於撤銷和續約。Spring Vault 隨附兩個實作

  • SimpleSessionManager:僅從提供的 ClientAuthentication 取得 Token,而無需重新整理和撤銷

  • LifecycleAwareSessionManager:如果 Token 可續約,則此 SessionManager 會排程 Token 續約,並在處置時撤銷登入 Token。續約會使用 AsyncTaskExecutor 排程。如果使用 AbstractVaultConfiguration,則預設會組態 LifecycleAwareSessionManager

使用 EnvironmentVaultConfiguration

Spring Vault 包含 EnvironmentVaultConfiguration,可從 Spring 的 Environment 和一組預先定義的屬性鍵組態 Vault 用戶端。EnvironmentVaultConfiguration 支援常用的組態。其他組態透過從最合適的組態類別衍生來支援。將 EnvironmentVaultConfiguration@Import(EnvironmentVaultConfiguration.class) 包含到現有的 Java 基礎組態類別中,並透過任何 Spring 的 PropertySource 提供組態屬性。

範例 3. 將 EnvironmentVaultConfiguration 與屬性檔案搭配使用
Java 基礎組態類別
@PropertySource("vault.properties")
@Import(EnvironmentVaultConfiguration.class)
public class MyConfiguration{
}
vault.properties
vault.uri=https://127.0.0.1:8200
vault.token=00000000-0000-0000-0000-000000000000

屬性鍵

  • Vault URI:vault.uri

  • SSL 設定

    • 金鑰儲存庫資源:vault.ssl.key-store (選用)

    • 金鑰儲存庫密碼:vault.ssl.key-store-password (選用)

    • 金鑰儲存庫類型:vault.ssl.key-store-type (選用,通常為 jks,也支援 pem)

    • 信任儲存庫資源:vault.ssl.trust-store (選用)

    • 信任儲存庫密碼:vault.ssl.trust-store-password (選用)

    • 信任儲存庫類型:vault.ssl.trust-store-type (選用,通常為 jks,也支援 pem)

    • 已啟用 SSL/TLS 通訊協定:vault.ssl.enabled-protocols (自 2.3.2 起,選用,通訊協定以逗號分隔)

    • 已啟用 SSL/TLS 密碼套件:vault.ssl.enabled-cipher-suites (自 2.3.2 起,選用,密碼套件以逗號分隔)

  • 驗證方法:vault.authentication (預設為 TOKEN,支援的驗證方法為:TOKENAPPIDAPPROLEAWS_EC2AWS_IAMAZURECERTCUBBYHOLEKUBERNETES)

驗證特定屬性鍵

  • Vault Token:vault.token

  • AppId 路徑:vault.app-id.app-id-path (預設為 app-id)

  • AppId:vault.app-id.app-id

  • UserId:vault.app-id.user-idMAC_ADDRESSIP_ADDRESS 使用 MacAddressUserId,分別為 IpAddressUserId 使用者 ID 機制。任何其他值都與 StaticUserId 一起使用。

  • AppRole 路徑:vault.app-role.app-role-path (預設為 approle)

  • RoleId:vault.app-role.role-id

  • SecretId:vault.app-role.secret-id (選用)

  • AWS EC2 路徑:vault.aws-ec2.aws-ec2-path (預設為 aws-ec2)

  • 角色:vault.aws-ec2.role

  • RoleId:vault.aws-ec2.role-id (已棄用: 請改用 vault.aws-ec2.role)

  • 身分文件 URL:vault.aws-ec2.identity-document (預設為 169.254.169.254/latest/dynamic/instance-identity/pkcs7)

  • 角色:vault.aws-iam.role

無設定選項。

  • 初始 Vault Token:vault.token

  • Kubernetes 路徑:vault.kubernetes.kubernetes-path (預設為 kubernetes)

  • 角色:vault.kubernetes.role

  • 服務帳戶 Token 檔案路徑:vault.kubernetes.service-account-token-file (預設為 /var/run/secrets/kubernetes.io/serviceaccount/token)

執行回呼

所有 Spring 樣板類別的一個常見設計特點是,所有功能都路由到樣板的執行回呼方法之一。這有助於確保例外狀況和可能需要的任何資源管理以一致的方式執行。雖然這在 JDBC 和 JMS 的情況下比 Vault 更為必要,但它仍然為存取和記錄提供單一位置。因此,使用執行回呼是存取 Vault API 以執行我們未在 VaultTemplate 上公開為方法的非常見操作的首選方式。

以下是執行回呼方法的清單。

  • <T> T doWithVault (RestOperationsCallback<T> callback) 執行給定的 RestOperationsCallback,允許使用 RestOperations 與 Vault 互動,而無需工作階段。

  • <T> T doWithSession (RestOperationsCallback<T> callback) 執行給定的 RestOperationsCallback,允許在經過驗證的工作階段中與 Vault 互動。

以下範例使用 ClientCallback 初始化 Vault

vaultOperations.doWithVault(new RestOperationsCallback<VaultInitializationResponse>() {

  @Override
  public VaultInitializationResponse doWithRestOperations(RestOperations restOperations) {

    ResponseEntity<VaultInitializationResponse> exchange = restOperations
                       .exchange("/sys/init", HttpMethod.PUT,
                                 new HttpEntity<Object>(request),
                                 VaultInitializationResponse.class);

    return exchange.getBody();
    }
});