HashiCorp Vault 客戶端程式開發 - 使用 VaultSharp
§ 引言
此文章紀錄取用 HashiCorp Vault(簡稱 Vault)的客方端程式碼開發基本範例。 本例只有取用秘密,一般來說也只會開放讀取給應用端。管理端肯定是另一條通道。
在 client 端取得保險箱(vault)中的秘密(secrets)。 有二種方式,一、經由 Web API;二、經由專用函式庫。 本例用官方建議的函式庫之一 VaultShare 取用秘密(secrets)。
§ 參考文件
HashiCorp Vault 開發環境部署Vault,HashiCorp Vault 入門練習Vault, 保險庫§ 開發環境
IDE: Visual Studio 2022
平台: .NET8
程式語言: C# 12
框架: Blazer Web App (server mode)
§ 本例簡述
Vault 提供多種方法多種通道存取秘密。 本例只練習三種:
root token。
userpass 使用者帳號密碼。
cert 檢驗客戶端憑證 。
§ 前端程式碼
@using System.Security.Cryptography.X509Certificates
@using VaultSharp
@using VaultSharp.V1.AuthMethods
@using VaultSharp.V1.AuthMethods.Token
@using VaultSharp.V1.Commons
@using VaultSharp.V1.SecretsEngines
@using System.Text.Json
@page "/vault-lab"
<PageTitle>VaultLab</PageTitle>
<h3>_VaultLab</h3>
<p>@message</p>
<p>connStr1: @connStr1<br/>connStr2: @connStr2</p>
<pre>
@if (secret != null)
{
@JsonSerializer.Serialize(secret, new JsonSerializerOptions { WriteIndented = true })
}
</pre>
<button class="btn btn-primary" @onclick=GetVaultSecret>Get Vault with token</button>
<button class="btn btn-primary" @onclick=GetVaultSecret2>Get Vault with user/password</button>
<button class="btn btn-primary" @onclick=GetVaultSecret3>Get Vault with cert</button>
@code {
//## Resource
//const string vaultAddress = "https://127.0.0.1:8200";
const string vaultAddress = "https://localhost:8200";
//## State
string message = "INIT";
Secret<SecretData>? secret = null;
string connStr1 = string.Empty;
string connStr2 = string.Empty;
/// <summary>
/// 用 root token 取得 Vault 的 secret。
/// </summary>
async Task GetVaultSecret()
{
try
{
message = "START TokenAuthMethodInfo";
const string rootToken = "hvs.AYTJgwte3oScGHIQX2mzeQCI";
IAuthMethodInfo authMethod = new TokenAuthMethodInfo(rootToken);
var vaultClientSettings = new VaultClientSettings(vaultAddress, authMethod);
IVaultClient vaultClient = new VaultClient(vaultClientSettings);
const string path = "myAwesomeApp/creds";
const string mountPoint = "secret";
secret = await vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(path, mountPoint: mountPoint);
IDictionary<string, object> result = secret.Data.Data;
connStr1 = Convert.ToString(result["conn_str1"]) ?? string.Empty;
connStr2 = Convert.ToString(result["conn_str2"]) ?? string.Empty;
message = "SUCCESS";
}
catch (Exception ex)
{
message = "出現例外!" + ex.Message;
}
}
/// <summary>
/// 用 user/password 取得 Vault 的 secret。
/// </summary>
async Task GetVaultSecret2()
{
try
{
message = "START UserPassAuthMethodInfo";
const string username = "my_user";
const string password = "my_password";
IAuthMethodInfo authMethod = new VaultSharp.V1.AuthMethods.UserPass.UserPassAuthMethodInfo(username, password);
var vaultClientSettings = new VaultClientSettings(vaultAddress, authMethod);
IVaultClient vaultClient = new VaultClient(vaultClientSettings);
const string path = "myAwesomeApp/creds";
const string mountPoint = "secret";
secret = await vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(path, mountPoint: mountPoint);
IDictionary<string, object> result = secret.Data.Data;
connStr1 = Convert.ToString(result["conn_str1"]) ?? string.Empty;
connStr2 = Convert.ToString(result["conn_str2"]) ?? string.Empty;
message = "SUCCESS";
}
catch (Exception ex)
{
message = "出現例外!" + ex.Message;
}
}
/// <summary>
/// 用憑證取得 Vault 的 secret。
/// </summary>
async Task GetVaultSecret3()
{
try
{
message = "START CertAuthMethodInfo";
using var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);
var cert = certStore.Certificates.Find(X509FindType.FindByThumbprint, "27e630b830d9af8c9b459ab84e0715b9c09b4392", false).First();
// 該憑證必需有私鑰且可匯出。(可私錀原則上不可匯出才對吧!)
//byte[] certBlob = System.IO.File.ReadAllBytes("D:\\Temp\\my127002.pfx");
//X509Certificate2 cert = new X509Certificate2(certBlob, "12345678");
IAuthMethodInfo authMethod = new VaultSharp.V1.AuthMethods.Cert.CertAuthMethodInfo(cert);
var vaultClientSettings = new VaultClientSettings(vaultAddress, authMethod);
IVaultClient vaultClient = new VaultClient(vaultClientSettings);
const string path = "myAwesomeApp/creds";
const string mountPoint = "secret";
secret = await vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(path, mountPoint: mountPoint);
IDictionary<string, object> result = secret.Data.Data;
connStr1 = Convert.ToString(result["conn_str1"]) ?? string.Empty;
connStr2 = Convert.ToString(result["conn_str2"]) ?? string.Empty;
message = "SUCCESS";
}
catch (Exception ex)
{
message = "出現例外!" + ex.Message;
Exception? innerEx = ex.InnerException;
while (innerEx != null)
{
message += innerEx.Message;
innerEx = innerEx?.InnerException;
}
}
}
}
§ 後端相應設定
1) 當然要先存入秘密。
利用Vaualt管理介面存入模擬的連線字串。名為 conn_str1, conn_str2。
存取路徑(path):secret/mySwesomeApp/creds

2) 透過 policy 授權。
依照 Vault 的規則需經由 policy 授權存取對象。我們設定了my_policy
取存 secret 底下全部資源。

3) 設定存取方法。
存取方法此例有三種: token, userpass, cert。如圖

一、token,即 root token 在服務第一次啟用時就可拿到。
二、userpass 使用者帳號密碼。 新增時指定 username, password 外,還需指定前面設定好的 my_policy 以授權存取對象。

三、cert 客戶端憑證。 經測試此功能需主機端啟動TSL才有效用。 需上傳客戶端憑證(無金錀的公開憑證)並指定授權policy即可。


§ 沒圖沒真象
由程式拿回來的秘密(secret)畫面。

(EOF)
Last updated