Dynamic Credentials 每一次產生出來的憑證,都是變動且有時效性的,這跟固定的 Key/Value,有很大的不一樣,用這樣的機制就可以保護真正的資訊,更有效的隱藏機敏性資料
 

開發環境
- Windows 11 Home
 - Windows Terminal 1.20.11781.0
 - Vault 1.17.6
 - Rider 2024.2
 
續上篇,保護你的機敏性資料,通過 VaultSharp 訪問 Hashicorp Vault - 快速入門 | 余小章 @ 大內殿堂 - 點部落 (dotblogs.com.tw)
直接進入重點,建立 Vault Server 開發環境
vault server -dev設定環境變數
$Env:VAULT_ADDR = "http://127.0.0.1:8200"建立資料庫執行個體
這裡我用 docker
docker run --name postgres.12 -e POSTGRES_USER=user -e POSTGRES_PASSWORD=password -e POSTGRES_DB=mydatabase -p 5432:5432 -d postgres:12-alpineVault CLI
啟用 Database Secrets
PS C:\Users\yao> vault secrets enable database
Success! Enabled the database secrets engine at: database/
設定 Valut 訪問資料庫
vault write database/config/my-postgresql-database `
    plugin_name=postgresql-database-plugin `
    allowed_roles="my-db-role" `
   connection_url="postgresql://{{username}}:{{password}}@localhost:5432/postgres?sslmode=disable" `
    username="user" `
    password="password"PS C:\Users\yao> vault write database/config/my-postgresql-database `
>>     plugin_name=postgresql-database-plugin `
>>     allowed_roles="my-db-role" `
>>    connection_url="postgresql://{{username}}:{{password}}@localhost:5432/postgres?sslmode=disable" `
>>     username="user" `
>>     password="password"
Success! Data written to: database/config/my-postgresql-database
設定 Vault 角色
當配置建立憑證時,建立一個用戶
$creationStatements = @"
CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "{{name}}";
"@
vault write database/roles/my-db-role `
    db_name=my-postgresql-database `
    creation_statements=$creationStatements `
    default_ttl="1h" `
    max_ttl="24h"PS C:\Users\yao> vault write database/roles/my-db-role `
>>     db_name=my-postgresql-database `
>>     creation_statements=$creationStatements `
>>     default_ttl="1h" `
>>     max_ttl="24h"
Success! Data written to: database/roles/my-db-role
讀取 Role
PS C:\Users\yao> vault read database/roles/my-db-role
Key                      Value
---                      -----
creation_statements      [CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "{{name}}";]
credential_type          password
db_name                  my-postgresql-database
default_ttl              1h
max_ttl                  24h
renew_statements         []
revocation_statements    []
rollback_statements      []
取得動態憑證 Dynamic Credentials
每執行就會產生一個憑證 Credential
PS C:\Users\yao> vault read database/creds/my-db-role
Key                Value
---                -----
lease_id           database/creds/my-db-role/WJ8TsZNAG0qot301urG7xbdI
lease_duration     1h
lease_renewable    true
password           7FiylxeZDVsY-M21X6FY
username          v-root-my-db-ro-iYq58EshiZiF0ljRp3h3-1728312012
欄位解釋
- lease_id: 此次請求的憑證 ID,用於後續操作(如續租或撤銷)。
 - lease_duration: 憑證的有效時間(例如 1 小時)。
 - username: Vault 動態生成的臨時使用者名稱。
 - password: 與生成的使用者對應的密碼。
 - lease_renewable: 如果為 true,表示這個憑證可以續租。
 
續約憑證
PS C:\Users\yao> vault lease renew database/creds/my-db-role/WJ8TsZNAG0qot301urG7xbdI
Key                Value
---                -----
lease_id          database/creds/my-db-role/WJ8TsZNAG0qot301urG7xbdI
lease_duration     1h
lease_renewable    true
撤銷憑證
PS C:\Users\yao> vault lease revoke database/creds/my-db-role/WJ8TsZNAG0qot301urG7xbdI
All revocation operations queued successfully!
列出所有的租約(leases)
PS C:\Users\yao> vault list sys/leases/lookup/database/creds/my-db-role
Keys
----
7DQgKfYbJl5XJ660yCLSl4Su
Dny8L1FByuYddxYnAWyYMdsl
IQERRbcVznmbN8xkaRch1niB
使用動態憑證登入 PostgreSQL
隨便一個租約都是可以登入大象

檢視租約
PS C:\Users\yao> vault lease lookup database/creds/my-db-role/h0N2j19tAx0Viog8aMMIIYuj
Key             Value
---             -----
expire_time     2024-10-07T23:20:15.4764045+08:00
id             database/creds/my-db-role/h0N2j19tAx0Viog8aMMIIYuj
issue_time      2024-10-07T22:20:15.4764045+08:00
last_renewal    <nil>
renewable       true
ttl             34m55s
名詞解釋
- lease_id: 該租約的唯一識別碼。
 - lease_duration: 憑證的有效時間。
 - lease_renewable: 是否允許續租。
 - lease_ttl: 憑證的剩餘存活時間(以秒為單位)。
 
C# VaultSharp
啟用 Database Secrets
[TestMethod]
public async Task _01_啟用資料庫()
{
    var vaultClient = this.CreateVaultClient();
    var enableSecretsEngine = new SecretsEngine
    {
        Type = new SecretsEngineType("database"),
        Path = "database",
        Description = "Database Secrets Engine"
    };
    await vaultClient.V1.System.MountSecretBackendAsync(enableSecretsEngine);
    Console.WriteLine("Secrets 已成功寫入!");
}
設定 Valut 訪問資料庫
[TestMethod]
public async Task _02_配置資料庫連線()
{
    var vaultClient = this.CreateVaultClient();
    // 寫入配置到 Vault
    var config = new PostgreSQLConnectionConfigModel
    {
        PluginName = "postgresql-database-plugin",
        AllowedRoles = new List<string> { "my-db-role" },
        Username = "user",
        Password = "password",
        ConnectionUrl = "postgresql://{{username}}:{{password}}@localhost:5432/postgres?sslmode=disable",
        // 正確的 username_template 使用有效的模板函數
        UsernameTemplate = "{{uuid}}",
    };
    var connectionName = "my-postgresql-database";
    await vaultClient.V1.Secrets.Database.ConfigureConnectionAsync(connectionName, config);
    Console.WriteLine("已成功寫入 PostgreSQL 配置!");
}
設定 Vault 角色
    [TestMethod]
    public async Task _03_建立角色()
    {
        var vaultClient = this.CreateVaultClient();
        // 定義創建語句
        var creationStatements = @"
CREATE ROLE ""{{name}}"" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ""{{name}}"";
";
        var connectionName = "my-postgresql-database";
        var role = new Role()
        {
            DatabaseProviderType = new DatabaseProviderType(connectionName),
            DefaultTimeToLive = "1h",
            MaximumTimeToLive = "24h",
            CreationStatements = [creationStatements],
            RevocationStatements = null,
            RollbackStatements = null,
            RenewStatements = null,
        };
        var roleName = "my-db-role";
        await vaultClient.V1.Secrets.Database.CreateRoleAsync(roleName, role);
        Console.WriteLine("已成功寫入資料庫角色配置!");
    }
讀取 Role
[TestMethod]
public async Task _04_取得角色資訊()
{
    var vaultClient = this.CreateVaultClient();
    // 讀取資料庫角色的詳細資訊
    var roleName = "my-db-role";
    var roleInfo = await vaultClient.V1.Secrets.Database.ReadRoleAsync(roleName);
    // 輸出角色的詳細資訊
    var roleJson = JsonSerializer.Serialize(roleInfo);
    Console.WriteLine(roleJson);
}
取得動態憑證 Dynamic Credentials
[TestMethod]
public async Task _05_建立憑證()
{
    var vaultClient = this.CreateVaultClient();
    // 讀取資料庫角色的憑證
    var roleName = "my-db-role";
    var credentials = await vaultClient.V1.Secrets.Database.GetCredentialsAsync(roleName);
    // 輸出角色的詳細資訊
    var roleJson = JsonSerializer.Serialize(credentials);
    Console.WriteLine(roleJson);
}
續約憑證
[TestMethod]
public async Task _06_續約憑證()
{
    var vaultClient = this.CreateVaultClient();
    var leaseId = "database/creds/my-db-role/RhazfRMw84PiOJfFZD5QAEez";
    // 續期租約
    var renewedLease = await vaultClient.V1.System.RenewLeaseAsync(leaseId, 3600);
    Console.WriteLine("租約已成功續期!");
    var roleJson = JsonSerializer.Serialize(renewedLease);
    Console.WriteLine(roleJson);
}
撤憑證證
[TestMethod]
public async Task _07_撤銷憑證()
{
    var vaultClient = this.CreateVaultClient();
    var leaseId = "database/creds/my-db-role/RhazfRMw84PiOJfFZD5QAEez";
    // 續期租約
    await vaultClient.V1.System.RevokeLeaseAsync(leaseId);
    Console.WriteLine("租約已成功撤銷!");
}
列出所有的租約 (leases)
[TestMethod]
public async Task 讀取所有租約()
{
    // 初始化 Vault Client
    var vaultClient = this.CreateVaultClient();
    // 指定要查詢的角色名稱
    var roleName = "my-db-role";
    try
    {
        var leases = await vaultClient.V1.System.GetAllLeasesAsync("database/creds/" + roleName);
        var json = JsonSerializer.Serialize(leases);
        Console.WriteLine(json);
    }
    catch (VaultApiException e)
    {
        Console.WriteLine(e.ToString());
    }
}心得
透過上面的演練,真的可以隱藏真實的帳號/密碼了,提升了安全性;考慮到 Vault Server 的負荷,應用程式跟 Vault Server 中間,可以考慮再墊一層快取。
後話:本來想著已經用 Vault CLI 實作 Dyncmic Credentials,換成用 VaultSharp 實現,透過 AI 應該可以很快速的轉換吧,沒想到沒有一個 AI 吐出來的答案是對的 QQ,花了一些時間在除錯,估計 VaultSharp 還沒有太多的資源。
範例位置
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET