secret-adapters
Installation
SKILL.md
Secret Management Adapters (密鑰管理適配器)
Overview
@rytass/secret 系列套件提供統一的密鑰管理介面,目前支援 HashiCorp Vault 作為後端儲存。
套件清單
| 套件 | 說明 | 用途 |
|---|---|---|
@rytass/secret |
基礎介面 | 定義 SecretManager 抽象類別 |
@rytass/secret-adapter-vault |
Vault 適配器 | HashiCorp Vault 完整實現 |
@rytass/secret-adapter-vault-nestjs |
NestJS 模組 | Vault 的 NestJS 依賴注入整合 |
SecretManager 抽象類別
import { SecretManager } from '@rytass/secret';
// 所有 Secret Adapter 都繼承此抽象類別
abstract class SecretManager {
constructor(project: string);
get project(): string; // 取得初始化時的 path
abstract get<T>(key: string): Promise<T> | T;
abstract set<T>(key: string, value: T): Promise<void> | void;
abstract delete(key: string): Promise<void> | void;
}
Quick Start
安裝
# 直接使用 Vault
npm install @rytass/secret-adapter-vault
# NestJS 專案
npm install @rytass/secret-adapter-vault-nestjs
基本使用(離線模式)
import { VaultSecret } from '@rytass/secret-adapter-vault';
const vault = new VaultSecret('apps/myapp/config', {
host: 'https://vault.company.com',
online: false, // 離線模式(預設)
auth: {
account: 'myapp-service',
password: 'secure-password',
},
onReady: () => {
// 同步操作,無需 await
const dbPassword = vault.get<string>('DATABASE_PASSWORD');
// 修改本地快取(不立即同步到 Vault)
vault.set('API_KEY', 'new-value');
vault.delete('OLD_CONFIG');
// 或者立即同步到 Vault(syncToOnline = true)
vault.set('ANOTHER_KEY', 'value', true); // 立即同步
vault.delete('TEMP_KEY', true); // 立即同步
// 批次同步變更到 Vault
vault.sync(); // 如果版本不符會拋錯
vault.sync(true); // force = true,強制覆蓋
},
});
NestJS 整合
// app.module.ts
import { VaultModule } from '@rytass/secret-adapter-vault-nestjs';
@Module({
imports: [
VaultModule.forRoot({
path: '/secret/data/myapp',
fallbackFile: '.env.local', // Vault 不可用時的備用
}),
],
})
export class AppModule {}
// config.service.ts
@Injectable()
export class ConfigService {
constructor(private readonly vault: VaultService) {}
// get() 預設泛型為 string,可省略
async getDatabaseUrl(): Promise<string> {
return this.vault.get('DATABASE_URL'); // 等同 get<string>()
}
// set() 和 delete() 也支援 syncToOnline 參數
async updateConfig(key: string, value: string): Promise<void> {
await this.vault.set(key, value); // syncToOnline = false(預設)
await this.vault.set(key, value, true); // syncToOnline = true,立即同步
}
async removeConfig(key: string): Promise<void> {
await this.vault.delete(key); // syncToOnline = false(預設)
await this.vault.delete(key, true); // syncToOnline = true,立即同步
}
}
Core Concepts
線上模式 vs 離線模式
| 特性 | 線上模式 | 離線模式(預設) |
|---|---|---|
| 操作方式 | 即時連接 Vault | 使用本地快取 |
get() 返回 |
Promise<T> |
T(同步) |
set()/delete() 返回 |
Promise<void> |
Promise<void>(syncToOnline 時)或同步操作本地快取 |
| 效能 | 有網路延遲 | 極快 |
| 適用場景 | 需即時更新 | 頻繁讀取 |
| Token 管理 | 自動續期(預設 TTL 約 32 天) | 初始化時取得 |
狀態管理
enum VaultSecretState {
INIT = 'INIT', // 初始化中
READY = 'READY', // 可用
TERMINATED = 'TERMINATED', // 已終止
}
// 檢查狀態
if (vault.state === VaultSecretState.READY) {
// 可以安全操作
}
事件驅動
enum VaultEvents {
INITED = 'INITED', // 初始化開始
READY = 'READY', // Vault 準備就緒
TOKEN_RENEWED = 'TOKEN_RENEWED', // Token 已續期
TERMINATED = 'TERMINATED', // 連線已終止
ERROR = 'ERROR', // 發生錯誤
}
完整型別定義
以下所有型別皆從 @rytass/secret-adapter-vault 導出:
import {
VaultSecret,
VaultSecretState,
VaultEvents,
VaultAuthMethods,
VaultAuthMethodAccountPassword,
VaultSecretOptions,
VaultSecretOnlineOptions,
VaultSecretOfflineOptions,
VaultGetType,
VaultSetType,
VaultDeleteType,
VaultTokenRetrieveSuccessResponse,
VaultAPIFailedResponse,
VaultTokenRetrieveResponse,
VaultGetSecretSuccessResponse,
VaultGetSecretResponse,
} from '@rytass/secret-adapter-vault';
認證型別:
// 帳號密碼認證
interface VaultAuthMethodAccountPassword {
account: string;
password: string;
}
// 支援的認證方式(目前僅支援帳號密碼)
type VaultAuthMethods = VaultAuthMethodAccountPassword;
選項型別:
// 通用選項
interface VaultSecretOptions {
host: string; // Vault 伺服器位址
auth: VaultAuthMethods; // 認證方式
online?: boolean; // 是否使用線上模式(預設 false)
tokenTTL?: number; // Token TTL(毫秒),預設 2764724(約 32 天)
onError?: (error: string) => void; // 錯誤回調
onReady?: () => void; // 準備就緒回調
}
// 線上模式選項(online: true)
// 注意:獨立定義,非繼承自 VaultSecretOptions
interface VaultSecretOnlineOptions {
host: string;
auth: VaultAuthMethods;
online: true;
tokenTTL?: number;
onError?: (error: string) => void;
onReady?: () => void;
}
// 離線模式選項(online: false 或省略)
// 注意:獨立定義,非繼承自 VaultSecretOptions
interface VaultSecretOfflineOptions {
host: string;
auth: VaultAuthMethods;
online?: false;
tokenTTL?: number;
onError?: (error: string) => void;
onReady?: () => void;
}
條件型別(根據模式決定返回值):
// get() 返回型別:線上模式為 Promise<T>,離線模式為 T
type VaultGetType<O extends VaultSecretOptions, T> = O extends VaultSecretOnlineOptions ? Promise<T> : T;
// set() 返回型別
type VaultSetType<O extends VaultSecretOptions> = O extends VaultSecretOnlineOptions ? Promise<void> : void;
// delete() 返回型別
type VaultDeleteType<O extends VaultSecretOptions> = O extends VaultSecretOnlineOptions ? Promise<void> : void;
API 回應型別:
// API 失敗回應
interface VaultAPIFailedResponse {
errors: string[];
}
// Token 取得成功回應
// 注意:token_type 使用內部 enum VaultTokenType(未導出)
type VaultTokenRetrieveSuccessResponse = {
auth: {
client_token: string;
accessor: string;
policies: string[];
token_policies: string[];
metadata: Record<string, string> | null;
lease_duration: number;
renewable: boolean;
entity_id: string;
token_type: 'service' | 'batch'; // VaultTokenType enum(未導出)
orphan: boolean;
mfa_requirement: null;
num_uses: number;
};
} & VaultAPIBaseInfo<null>;
// Token 取得回應(成功或失敗)
type VaultTokenRetrieveResponse = VaultTokenRetrieveSuccessResponse | VaultAPIFailedResponse;
// Secret 取得成功回應
type VaultGetSecretSuccessResponse = VaultAPIBaseInfo<{
data: Record<string, unknown>;
metadata: {
created_time: string;
custom_metadata: null;
deletion_time: string;
destroyed: boolean;
version: number;
};
}>;
// Secret 取得回應(成功或失敗)
type VaultGetSecretResponse = VaultGetSecretSuccessResponse | VaultAPIFailedResponse;
Common Patterns
線上模式使用
const vault = new VaultSecret('apps/myapp/config', {
host: 'https://vault.company.com',
online: true,
auth: {
account: 'service-account',
password: 'password',
},
});
// 需要 await
const secret = await vault.get<string>('SECRET_KEY');
await vault.set('NEW_KEY', 'value');
await vault.delete('OLD_KEY');
TypeORM 整合
@Module({
imports: [
VaultModule.forRoot({ path: '/secret/data/database' }),
TypeOrmModule.forRootAsync({
imports: [VaultModule],
inject: [VaultService],
useFactory: async (vault: VaultService) => ({
type: 'postgres',
host: await vault.get<string>('DB_HOST'),
port: await vault.get<number>('DB_PORT'),
username: await vault.get<string>('DB_USERNAME'),
password: await vault.get<string>('DB_PASSWORD'),
database: await vault.get<string>('DB_NAME'),
}),
}),
],
})
export class DatabaseModule {}
JWT 模組整合
@Module({
imports: [
VaultModule.forRoot({ path: '/secret/data/auth' }),
JwtModule.registerAsync({
imports: [VaultModule],
inject: [VaultService],
useFactory: async (vault: VaultService) => ({
secret: await vault.get<string>('JWT_SECRET'),
signOptions: {
expiresIn: await vault.get<string>('JWT_EXPIRY') || '1h',
},
}),
}),
],
})
export class AuthModule {}
錯誤處理與備用機制
const vault = new VaultSecret('apps/myapp/config', {
host: 'https://vault.company.com',
online: false,
auth: { account: 'user', password: 'pass' },
onError: (error) => {
console.error('Vault error:', error);
// 可在此切換到備用配置
},
onReady: () => {
console.log('Vault ready');
},
});
定期同步(離線模式)
// 離線模式下定期同步變更
setInterval(async () => {
try {
// sync() 會檢查版本,如果 Vault 上的版本與快取不符會拋出錯誤
await vault.sync();
console.log('Synced to Vault');
} catch (error) {
if (error.message.includes('version is not match')) {
// 版本不符時,可選擇強制覆蓋
await vault.sync(true); // force = true
console.log('Force synced to Vault');
} else {
console.warn('Sync failed:', error);
}
}
}, 300000); // 每 5 分鐘
set() / delete() 的 syncToOnline 參數
// 離線模式下,set() 和 delete() 預設只修改本地快取
vault.set('KEY', 'value'); // syncToOnline = false(預設)
vault.delete('KEY'); // syncToOnline = false(預設)
// 如果需要立即同步到 Vault,傳入 syncToOnline = true
vault.set('KEY', 'value', true); // 立即同步到 Vault
vault.delete('KEY', true); // 立即同步到 Vault
優雅關閉
process.on('SIGTERM', () => {
vault.terminate();
process.exit(0);
});
NestJS Module Reference
VaultModuleOptions
// NestJS 模組配置選項
interface VaultModuleOptions {
path: string; // Vault secret path(預設 '/')
fallbackFile?: string; // Vault 不可用時的備用 .env 檔案
}
VaultService 方法簽名
class VaultService {
// get() 預設泛型為 string,可省略
async get<T = string>(key: string): Promise<T>;
// set() 支援 syncToOnline 參數
async set<T = string>(key: string, value: T, syncToOnline = false): Promise<void>;
// delete() 支援 syncToOnline 參數
async delete(key: string, syncToOnline = false): Promise<void>;
}
Environment Variables (NestJS)
# 必需(若未設置 VAULT_HOST,會自動切換至備用模式)
VAULT_HOST=https://vault.example.com:8200
VAULT_ACCOUNT=your-username
VAULT_PASSWORD=your-password
注意:
path不是透過環境變數設定,而是在VaultModule.forRoot({ path: '...' })中指定。
API Reference
詳細 API 文件請參閱 reference.md。
Troubleshooting
連線失敗
- 確認
VAULT_HOST使用 HTTPS - 檢查網路連線和防火牆
- 驗證帳號密碼正確
Token 過期
線上模式會自動續期。如果仍然過期:
- 檢查
tokenTTL設定 - 確認 Vault 伺服器時間同步
- 查看 Vault Token 政策設定
NestJS 備用模式
當 VAULT_HOST 未設置時或連線失敗時,VaultService 會切換至備用模式:
get()從 ConfigService(環境變數)讀取set()和delete()會拋出錯誤:"Cannot set/delete value when fallback to env file is enabled."
// 備用模式下的行為
try {
await vaultService.set('KEY', 'value');
} catch (error) {
// Error: Cannot set value when fallback to env file is enabled.
console.log('Vault 備用模式不支援寫入操作');
}
Related skills
More from rytass/utils
quadrats-module
|
7storage-development
Development guide for @rytass/storages base package (儲存基底套件開發指南). Use when creating new storage adapters (新增儲存 adapter), understanding base interfaces, or extending storage functionality. Covers StorageInterface, Storage class, file converters (檔案轉換器), hash algorithms (雜湊演算法), and implementation patterns.
7wms-react-components
|
7cms-modules
|
6