| name | secret-adapters |
| description | Secret management integration (密鑰管理整合). Use when working with HashiCorp Vault, credential management, or secure configuration. Covers secret storage (密鑰儲存), key management (金鑰管理), NestJS integration, online/offline modes, and automatic token renewal. Keywords: 密鑰, 機密, 金鑰, 秘密管理, secret, vault, credential, key management, HashiCorp, token, 環境變數, configuration
|
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';
abstract class SecretManager {
constructor(project: string);
get project(): string;
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
安裝
npm install @rytass/secret-adapter-vault
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: () => {
const dbPassword = vault.get<string>('DATABASE_PASSWORD');
vault.set('API_KEY', 'new-value');
vault.delete('OLD_CONFIG');
vault.set('ANOTHER_KEY', 'value', true);
vault.delete('TEMP_KEY', true);
vault.sync();
vault.sync(true);
},
});
NestJS 整合
import { VaultModule } from '@rytass/secret-adapter-vault-nestjs';
@Module({
imports: [
VaultModule.forRoot({
path: '/secret/data/myapp',
fallbackFile: '.env.local',
}),
],
})
export class AppModule {}
@Injectable()
export class ConfigService {
constructor(private readonly vault: VaultService) {}
async getDatabaseUrl(): Promise<string> {
return this.vault.get('DATABASE_URL');
}
async updateConfig(key: string, value: string): Promise<void> {
await this.vault.set(key, value);
await this.vault.set(key, value, true);
}
async removeConfig(key: string): Promise<void> {
await this.vault.delete(key);
await this.vault.delete(key, 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',
TOKEN_RENEWED = 'TOKEN_RENEWED',
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;
auth: VaultAuthMethods;
online?: boolean;
tokenTTL?: number;
onError?: (error: string) => void;
onReady?: () => void;
}
interface VaultSecretOnlineOptions {
host: string;
auth: VaultAuthMethods;
online: true;
tokenTTL?: number;
onError?: (error: string) => void;
onReady?: () => void;
}
interface VaultSecretOfflineOptions {
host: string;
auth: VaultAuthMethods;
online?: false;
tokenTTL?: number;
onError?: (error: string) => void;
onReady?: () => void;
}
條件型別(根據模式決定返回值):
type VaultGetType<O extends VaultSecretOptions, T> = O extends VaultSecretOnlineOptions ? Promise<T> : T;
type VaultSetType<O extends VaultSecretOptions> = O extends VaultSecretOnlineOptions ? Promise<void> : void;
type VaultDeleteType<O extends VaultSecretOptions> = O extends VaultSecretOnlineOptions ? Promise<void> : void;
API 回應型別:
interface VaultAPIFailedResponse {
errors: string[];
}
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';
orphan: boolean;
mfa_requirement: null;
num_uses: number;
};
} & VaultAPIBaseInfo<null>;
type VaultTokenRetrieveResponse = VaultTokenRetrieveSuccessResponse | VaultAPIFailedResponse;
type VaultGetSecretSuccessResponse = VaultAPIBaseInfo<{
data: Record<string, unknown>;
metadata: {
created_time: string;
custom_metadata: null;
deletion_time: string;
destroyed: boolean;
version: number;
};
}>;
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',
},
});
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 {
await vault.sync();
console.log('Synced to Vault');
} catch (error) {
if (error.message.includes('version is not match')) {
await vault.sync(true);
console.log('Force synced to Vault');
} else {
console.warn('Sync failed:', error);
}
}
}, 300000);
set() / delete() 的 syncToOnline 參數
vault.set('KEY', 'value');
vault.delete('KEY');
vault.set('KEY', 'value', true);
vault.delete('KEY', true);
優雅關閉
process.on('SIGTERM', () => {
vault.terminate();
process.exit(0);
});
NestJS Module Reference
VaultModuleOptions
interface VaultModuleOptions {
path: string;
fallbackFile?: string;
}
VaultService 方法簽名
class VaultService {
async get<T = string>(key: string): Promise<T>;
async set<T = string>(key: string, value: T, syncToOnline = false): Promise<void>;
async delete(key: string, syncToOnline = false): Promise<void>;
}
Environment Variables (NestJS)
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) {
console.log('Vault 備用模式不支援寫入操作');
}