@kibibit/configit 1.0.0-beta.26 → 1.0.0-beta.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +419 -0
- package/lib/scripts/test-vault-comprehensive.d.ts +2 -0
- package/lib/scripts/test-vault-comprehensive.d.ts.map +1 -0
- package/lib/scripts/test-vault-comprehensive.js +422 -0
- package/lib/scripts/test-vault-comprehensive.js.map +1 -0
- package/lib/scripts/test-vault-dynamic.d.ts +2 -0
- package/lib/scripts/test-vault-dynamic.d.ts.map +1 -0
- package/lib/scripts/test-vault-dynamic.js +193 -0
- package/lib/scripts/test-vault-dynamic.js.map +1 -0
- package/lib/scripts/test-vault-gcp-ttl.d.ts +3 -0
- package/lib/scripts/test-vault-gcp-ttl.d.ts.map +1 -0
- package/lib/scripts/test-vault-gcp-ttl.js +218 -0
- package/lib/scripts/test-vault-gcp-ttl.js.map +1 -0
- package/lib/scripts/test-vault.d.ts +2 -0
- package/lib/scripts/test-vault.d.ts.map +1 -0
- package/lib/scripts/test-vault.js +167 -0
- package/lib/scripts/test-vault.js.map +1 -0
- package/lib/src/config.errors.d.ts.map +1 -0
- package/lib/src/config.errors.js.map +1 -0
- package/lib/src/config.model.d.ts.map +1 -0
- package/lib/src/config.model.js.map +1 -0
- package/lib/{config.service.d.ts → src/config.service.d.ts} +10 -1
- package/lib/src/config.service.d.ts.map +1 -0
- package/lib/{config.service.js → src/config.service.js} +75 -9
- package/lib/src/config.service.js.map +1 -0
- package/lib/src/environment.service.d.ts.map +1 -0
- package/lib/src/environment.service.js.map +1 -0
- package/lib/{index.d.ts → src/index.d.ts} +1 -0
- package/lib/src/index.d.ts.map +1 -0
- package/lib/{index.js → src/index.js} +1 -0
- package/lib/src/index.js.map +1 -0
- package/lib/src/json-schema.validator.d.ts.map +1 -0
- package/lib/src/json-schema.validator.js.map +1 -0
- package/lib/src/vault/__tests__/vault-integration.test.d.ts +2 -0
- package/lib/src/vault/__tests__/vault-integration.test.d.ts.map +1 -0
- package/lib/src/vault/__tests__/vault-integration.test.js +190 -0
- package/lib/src/vault/__tests__/vault-integration.test.js.map +1 -0
- package/lib/src/vault/decorators.d.ts +17 -0
- package/lib/src/vault/decorators.d.ts.map +1 -0
- package/lib/src/vault/decorators.js +149 -0
- package/lib/src/vault/decorators.js.map +1 -0
- package/lib/src/vault/index.d.ts +7 -0
- package/lib/src/vault/index.d.ts.map +1 -0
- package/lib/src/vault/index.js +42 -0
- package/lib/src/vault/index.js.map +1 -0
- package/lib/src/vault/secret-refresh-manager.d.ts +23 -0
- package/lib/src/vault/secret-refresh-manager.d.ts.map +1 -0
- package/lib/src/vault/secret-refresh-manager.js +149 -0
- package/lib/src/vault/secret-refresh-manager.js.map +1 -0
- package/lib/src/vault/types.d.ts +149 -0
- package/lib/src/vault/types.d.ts.map +1 -0
- package/lib/src/vault/types.js +4 -0
- package/lib/src/vault/types.js.map +1 -0
- package/lib/src/vault/vault-cache.d.ts +20 -0
- package/lib/src/vault/vault-cache.d.ts.map +1 -0
- package/lib/src/vault/vault-cache.js +139 -0
- package/lib/src/vault/vault-cache.js.map +1 -0
- package/lib/src/vault/vault-integration.d.ts +27 -0
- package/lib/src/vault/vault-integration.d.ts.map +1 -0
- package/lib/src/vault/vault-integration.js +211 -0
- package/lib/src/vault/vault-integration.js.map +1 -0
- package/lib/src/vault/vault-provider.d.ts +37 -0
- package/lib/src/vault/vault-provider.d.ts.map +1 -0
- package/lib/src/vault/vault-provider.js +354 -0
- package/lib/src/vault/vault-provider.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -65
- package/src/config.service.ts +155 -10
- package/src/config.service.vault.spec.ts +859 -0
- package/src/index.ts +1 -0
- package/src/vault/__tests__/vault-integration.test.ts +226 -0
- package/src/vault/decorators.ts +228 -0
- package/src/vault/index.ts +31 -0
- package/src/vault/secret-refresh-manager.ts +241 -0
- package/src/vault/types.ts +487 -0
- package/src/vault/vault-cache.ts +240 -0
- package/src/vault/vault-integration.ts +332 -0
- package/src/vault/vault-provider.ts +576 -0
- package/lib/config.errors.d.ts.map +0 -1
- package/lib/config.errors.js.map +0 -1
- package/lib/config.model.d.ts.map +0 -1
- package/lib/config.model.js.map +0 -1
- package/lib/config.service.d.ts.map +0 -1
- package/lib/config.service.js.map +0 -1
- package/lib/environment.service.d.ts.map +0 -1
- package/lib/environment.service.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/json-schema.validator.d.ts.map +0 -1
- package/lib/json-schema.validator.js.map +0 -1
- /package/lib/{config.errors.d.ts → src/config.errors.d.ts} +0 -0
- /package/lib/{config.errors.js → src/config.errors.js} +0 -0
- /package/lib/{config.model.d.ts → src/config.model.d.ts} +0 -0
- /package/lib/{config.model.js → src/config.model.js} +0 -0
- /package/lib/{environment.service.d.ts → src/environment.service.d.ts} +0 -0
- /package/lib/{environment.service.js → src/environment.service.js} +0 -0
- /package/lib/{json-schema.validator.d.ts → src/json-schema.validator.d.ts} +0 -0
- /package/lib/{json-schema.validator.js → src/json-schema.validator.js} +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecretRefreshManager
|
|
3
|
+
* Manages background refresh of Vault secrets based on TTL
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IRefreshStatus, VaultPropertyMetadata } from './types';
|
|
7
|
+
import { VaultCache } from './vault-cache';
|
|
8
|
+
import { VaultProvider } from './vault-provider';
|
|
9
|
+
|
|
10
|
+
interface IRefreshContext {
|
|
11
|
+
metadata: VaultPropertyMetadata;
|
|
12
|
+
targetInstance?: object;
|
|
13
|
+
fullPath: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* SecretRefreshManager - Handles TTL-based secret refresh scheduling
|
|
18
|
+
*/
|
|
19
|
+
export class SecretRefreshManager {
|
|
20
|
+
private refreshTimers: Map<string, NodeJS.Timeout> = new Map();
|
|
21
|
+
private refreshLocks: Map<string, Promise<void>> = new Map();
|
|
22
|
+
private refreshCounts: Map<string, number> = new Map();
|
|
23
|
+
private lastRefreshTimes: Map<string, number> = new Map();
|
|
24
|
+
private refreshContexts: Map<string, IRefreshContext> = new Map();
|
|
25
|
+
private vaultProvider: VaultProvider;
|
|
26
|
+
private cache: VaultCache;
|
|
27
|
+
private refreshBuffer: number; // Default refresh buffer in seconds
|
|
28
|
+
|
|
29
|
+
constructor(provider: VaultProvider, cache: VaultCache, refreshBuffer?: number) {
|
|
30
|
+
this.vaultProvider = provider;
|
|
31
|
+
this.cache = cache;
|
|
32
|
+
// Default: min(10% of TTL, 300s) - but we'll use a fixed buffer per secret
|
|
33
|
+
this.refreshBuffer = refreshBuffer || 300; // 5 minutes default
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Schedule refresh for a secret based on TTL
|
|
38
|
+
*/
|
|
39
|
+
scheduleRefresh(propertyName: string, metadata: VaultPropertyMetadata, targetInstance?: object): void {
|
|
40
|
+
const entry = this.cache.getEntry(propertyName);
|
|
41
|
+
if (!entry) {
|
|
42
|
+
return; // No cache entry to refresh
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Store context for refresh
|
|
46
|
+
this.refreshContexts.set(propertyName, {
|
|
47
|
+
metadata,
|
|
48
|
+
targetInstance,
|
|
49
|
+
fullPath: entry.vaultPath
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Cancel existing refresh if any
|
|
53
|
+
this.cancelRefresh(propertyName);
|
|
54
|
+
|
|
55
|
+
// Calculate refresh time
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const timeUntilRefresh = Math.max(0, entry.refreshAt - now);
|
|
58
|
+
|
|
59
|
+
if (timeUntilRefresh <= 0) {
|
|
60
|
+
// Already past refresh time, refresh immediately
|
|
61
|
+
this.executeRefresh(propertyName);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Schedule refresh
|
|
66
|
+
const timer = setTimeout(() => {
|
|
67
|
+
this.executeRefresh(propertyName);
|
|
68
|
+
}, timeUntilRefresh);
|
|
69
|
+
|
|
70
|
+
this.refreshTimers.set(propertyName, timer);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Cancel scheduled refresh for a property
|
|
75
|
+
*/
|
|
76
|
+
cancelRefresh(propertyName: string): void {
|
|
77
|
+
const timer = this.refreshTimers.get(propertyName);
|
|
78
|
+
if (timer) {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
this.refreshTimers.delete(propertyName);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Execute refresh for a secret (with locking to prevent concurrent refreshes)
|
|
86
|
+
*/
|
|
87
|
+
private async executeRefresh(propertyName: string): Promise<void> {
|
|
88
|
+
// Check if refresh is already in progress
|
|
89
|
+
const existingLock = this.refreshLocks.get(propertyName);
|
|
90
|
+
if (existingLock) {
|
|
91
|
+
await existingLock;
|
|
92
|
+
return; // Refresh already completed
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Create refresh lock
|
|
96
|
+
const refreshPromise = this.performRefresh(propertyName)
|
|
97
|
+
.finally(() => {
|
|
98
|
+
this.refreshLocks.delete(propertyName);
|
|
99
|
+
this.refreshTimers.delete(propertyName);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this.refreshLocks.set(propertyName, refreshPromise);
|
|
103
|
+
await refreshPromise;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Perform the actual refresh operation
|
|
108
|
+
*/
|
|
109
|
+
private async performRefresh(propertyName: string): Promise<void> {
|
|
110
|
+
const context = this.refreshContexts.get(propertyName);
|
|
111
|
+
if (!context) {
|
|
112
|
+
console.error(`No refresh context for ${ propertyName }`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { metadata, targetInstance, fullPath } = context;
|
|
117
|
+
const refreshCount = (this.refreshCounts.get(propertyName) || 0) + 1;
|
|
118
|
+
this.refreshCounts.set(propertyName, refreshCount);
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
// Read fresh secret from Vault (using full path)
|
|
122
|
+
const secret = await this.vaultProvider.read(fullPath);
|
|
123
|
+
|
|
124
|
+
// Update cache
|
|
125
|
+
this.cache.set(propertyName, fullPath, secret, metadata);
|
|
126
|
+
|
|
127
|
+
// Update target instance if provided
|
|
128
|
+
if (targetInstance) {
|
|
129
|
+
const key = metadata.key || metadata.propertyName;
|
|
130
|
+
const value = secret.data[key];
|
|
131
|
+
(targetInstance as any)[propertyName] = value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Record refresh time
|
|
135
|
+
this.lastRefreshTimes.set(propertyName, Date.now());
|
|
136
|
+
|
|
137
|
+
// Reschedule next refresh
|
|
138
|
+
this.scheduleRefresh(propertyName, metadata, targetInstance);
|
|
139
|
+
} catch (error: any) {
|
|
140
|
+
// Log error (sanitized)
|
|
141
|
+
const errorMessage = error?.message || 'Unknown error';
|
|
142
|
+
console.error(`Failed to refresh secret for ${ propertyName }: ${ this.sanitizeError(errorMessage) }`);
|
|
143
|
+
|
|
144
|
+
// Retry with exponential backoff
|
|
145
|
+
const retryDelay = Math.min(1000 * Math.pow(2, refreshCount - 1), 30000); // Max 30s
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
this.scheduleRefresh(propertyName, metadata, targetInstance);
|
|
148
|
+
}, retryDelay);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get refresh status for all secrets
|
|
154
|
+
*/
|
|
155
|
+
getRefreshStatus(): IRefreshStatus[] {
|
|
156
|
+
const statuses: IRefreshStatus[] = [];
|
|
157
|
+
const cachedProperties = this.cache.getCachedProperties();
|
|
158
|
+
|
|
159
|
+
for (const propertyName of cachedProperties) {
|
|
160
|
+
const entry = this.cache.getEntry(propertyName);
|
|
161
|
+
if (!entry) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const timer = this.refreshTimers.get(propertyName);
|
|
166
|
+
const now = Date.now();
|
|
167
|
+
const refreshAt = entry.refreshAt;
|
|
168
|
+
const timeUntilRefresh = Math.max(0, refreshAt - now);
|
|
169
|
+
|
|
170
|
+
statuses.push({
|
|
171
|
+
propertyName,
|
|
172
|
+
vaultPath: entry.vaultPath,
|
|
173
|
+
scheduled: timer !== undefined,
|
|
174
|
+
refreshAt,
|
|
175
|
+
timeUntilRefresh,
|
|
176
|
+
lastRefresh: this.lastRefreshTimes.get(propertyName) || entry.cachedAt,
|
|
177
|
+
refreshCount: this.refreshCounts.get(propertyName) || 0
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return statuses;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get refresh status for a specific property
|
|
186
|
+
*/
|
|
187
|
+
getRefreshStatusForProperty(propertyName: string): IRefreshStatus | null {
|
|
188
|
+
const entry = this.cache.getEntry(propertyName);
|
|
189
|
+
if (!entry) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const timer = this.refreshTimers.get(propertyName);
|
|
194
|
+
const now = Date.now();
|
|
195
|
+
const refreshAt = entry.refreshAt;
|
|
196
|
+
const timeUntilRefresh = Math.max(0, refreshAt - now);
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
propertyName,
|
|
200
|
+
vaultPath: entry.vaultPath,
|
|
201
|
+
scheduled: timer !== undefined,
|
|
202
|
+
refreshAt,
|
|
203
|
+
timeUntilRefresh,
|
|
204
|
+
lastRefresh: this.lastRefreshTimes.get(propertyName) || entry.cachedAt,
|
|
205
|
+
refreshCount: this.refreshCounts.get(propertyName) || 0
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Stop all refresh workers
|
|
211
|
+
*/
|
|
212
|
+
shutdown(): void {
|
|
213
|
+
// Cancel all timers
|
|
214
|
+
for (const [ propertyName, timer ] of this.refreshTimers.entries()) {
|
|
215
|
+
clearTimeout(timer);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this.refreshTimers.clear();
|
|
219
|
+
this.refreshLocks.clear();
|
|
220
|
+
this.refreshCounts.clear();
|
|
221
|
+
this.lastRefreshTimes.clear();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Sanitize error message (remove potential secret values)
|
|
226
|
+
*/
|
|
227
|
+
private sanitizeError(message: string): string {
|
|
228
|
+
// Remove potential secret values from error messages
|
|
229
|
+
const sensitivePatterns = [ /password/i, /secret/i, /key/i, /token/i, /credential/i ];
|
|
230
|
+
let sanitized = message;
|
|
231
|
+
|
|
232
|
+
sensitivePatterns.forEach((pattern) => {
|
|
233
|
+
sanitized = sanitized.replace(
|
|
234
|
+
new RegExp(`${ pattern.source }[:=]\\s*[^\\s,}]+`, 'gi'),
|
|
235
|
+
`${ pattern.source }: ***`
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return sanitized;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault Integration Types
|
|
3
|
+
* TypeScript interfaces and types for HashiCorp Vault integration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import 'reflect-metadata';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Vault secrets engine types
|
|
10
|
+
* - kv1/kv-v1: Key-Value v1 (simple)
|
|
11
|
+
* - kv2/kv-v2: Key-Value v2 (versioned, default)
|
|
12
|
+
* - database: Database secrets engine (dynamic credentials)
|
|
13
|
+
* - aws: AWS secrets engine
|
|
14
|
+
* - azure: Azure secrets engine
|
|
15
|
+
* - gcp: GCP secrets engine
|
|
16
|
+
* - transit: Transit secrets engine (encryption)
|
|
17
|
+
* - pki: PKI secrets engine
|
|
18
|
+
* - custom: Custom engine (requires custom extraction logic)
|
|
19
|
+
*/
|
|
20
|
+
export type VaultEngineType =
|
|
21
|
+
| 'kv1'
|
|
22
|
+
| 'kv-v1'
|
|
23
|
+
| 'kv2'
|
|
24
|
+
| 'kv-v2'
|
|
25
|
+
| 'database'
|
|
26
|
+
| 'aws'
|
|
27
|
+
| 'azure'
|
|
28
|
+
| 'gcp'
|
|
29
|
+
| 'transit'
|
|
30
|
+
| 'pki'
|
|
31
|
+
| 'custom';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Vault configuration options for ConfigService
|
|
35
|
+
*/
|
|
36
|
+
export interface IVaultConfigOptions {
|
|
37
|
+
/**
|
|
38
|
+
* Vault server endpoint (must be HTTPS in production)
|
|
39
|
+
*/
|
|
40
|
+
endpoint: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Authentication configuration
|
|
44
|
+
*/
|
|
45
|
+
auth: IVaultAuthConfig;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* TLS configuration
|
|
49
|
+
*/
|
|
50
|
+
tls?: IVaultTLSConfig;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Refresh buffer (seconds) - default: min(10% of TTL, 300s)
|
|
54
|
+
*/
|
|
55
|
+
refreshBuffer?: number;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fallback configuration
|
|
59
|
+
*/
|
|
60
|
+
fallback?: IVaultFallbackConfig;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Retry configuration
|
|
64
|
+
*/
|
|
65
|
+
retry?: IRetryPolicy;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Circuit breaker configuration
|
|
69
|
+
*/
|
|
70
|
+
circuitBreaker?: ICircuitBreakerConfig;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Authentication configuration with priority
|
|
75
|
+
* Supports both simple single-method and array-based multi-method configs
|
|
76
|
+
*/
|
|
77
|
+
export type IVaultAuthConfig = IVaultAuthConfigSimple | IVaultAuthConfigMethods;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Simple single-method auth config (for convenience)
|
|
81
|
+
*/
|
|
82
|
+
export type IVaultAuthConfigSimple =
|
|
83
|
+
| ({ method: 'gcp' } & IGCPAuthConfig)
|
|
84
|
+
| ({ method: 'aws' } & IAWSAuthConfig)
|
|
85
|
+
| ({ method: 'approle' } & IAppRoleAuthConfig)
|
|
86
|
+
| ({ method: 'token' } & ITokenAuthConfig);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Multi-method auth config (for fallback chains)
|
|
90
|
+
*/
|
|
91
|
+
export interface IVaultAuthConfigMethods {
|
|
92
|
+
/**
|
|
93
|
+
* Authentication methods in priority order
|
|
94
|
+
* Tried sequentially until one succeeds
|
|
95
|
+
*/
|
|
96
|
+
methods: IVaultAuthMethod[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Individual authentication method
|
|
101
|
+
*/
|
|
102
|
+
export interface IVaultAuthMethod {
|
|
103
|
+
type: 'gcp' | 'aws' | 'approle' | 'token';
|
|
104
|
+
config: IGCPAuthConfig | IAWSAuthConfig | IAppRoleAuthConfig | ITokenAuthConfig;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* GCP IAM authentication
|
|
109
|
+
*/
|
|
110
|
+
export interface IGCPAuthConfig {
|
|
111
|
+
type?: 'gcp';
|
|
112
|
+
/** Vault role name configured for GCP auth */
|
|
113
|
+
role: string;
|
|
114
|
+
/** Path to service account key file (JSON). If not provided, uses ADC */
|
|
115
|
+
serviceAccountKeyFile?: string;
|
|
116
|
+
/** Service account email. If not provided, derived from key file or metadata */
|
|
117
|
+
serviceAccountEmail?: string;
|
|
118
|
+
/** JWT expiration in seconds (default: 900 = 15 minutes) */
|
|
119
|
+
jwtExpiration?: number;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* AWS IAM authentication
|
|
124
|
+
*/
|
|
125
|
+
export interface IAWSAuthConfig {
|
|
126
|
+
type?: 'aws';
|
|
127
|
+
role: string;
|
|
128
|
+
// Uses instance profile or environment credentials
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* AppRole authentication
|
|
133
|
+
*/
|
|
134
|
+
export interface IAppRoleAuthConfig {
|
|
135
|
+
type?: 'approle';
|
|
136
|
+
/** Can be in config */
|
|
137
|
+
roleId: string;
|
|
138
|
+
/** MUST come from secure source */
|
|
139
|
+
secretId: string;
|
|
140
|
+
/** Default: 'approle' */
|
|
141
|
+
mountPath?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Token authentication (dev only)
|
|
146
|
+
*/
|
|
147
|
+
export interface ITokenAuthConfig {
|
|
148
|
+
type?: 'token';
|
|
149
|
+
/** MUST come from secure source */
|
|
150
|
+
token: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* TLS configuration
|
|
155
|
+
*/
|
|
156
|
+
export interface IVaultTLSConfig {
|
|
157
|
+
/**
|
|
158
|
+
* Require TLS (default: true, cannot be disabled)
|
|
159
|
+
*/
|
|
160
|
+
enabled: boolean;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Verify server certificate (default: true)
|
|
164
|
+
*/
|
|
165
|
+
verifyCertificate: boolean;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Certificate fingerprint for pinning (optional)
|
|
169
|
+
*/
|
|
170
|
+
certificateFingerprint?: string;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Custom CA certificate (optional)
|
|
174
|
+
*/
|
|
175
|
+
caCert?: string | Buffer;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Minimum TLS version (default: 'TLSv1.2')
|
|
179
|
+
*/
|
|
180
|
+
minVersion?: 'TLSv1.2' | 'TLSv1.3';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Fallback configuration
|
|
185
|
+
*/
|
|
186
|
+
export interface IVaultFallbackConfig {
|
|
187
|
+
/**
|
|
188
|
+
* Whether Vault is required (default: true)
|
|
189
|
+
* If false, falls back to env/file on initialization failure
|
|
190
|
+
*/
|
|
191
|
+
required: boolean;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Use cached secrets on failure (default: true)
|
|
195
|
+
*/
|
|
196
|
+
useCacheOnFailure: boolean;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Maximum cache age for fallback (ms) - default: TTL or 1 hour
|
|
200
|
+
*/
|
|
201
|
+
maxCacheAge: number;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Fail fast on refresh failure (default: true for required secrets)
|
|
205
|
+
*/
|
|
206
|
+
failFast: boolean;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Retry policy
|
|
211
|
+
*/
|
|
212
|
+
export interface IRetryPolicy {
|
|
213
|
+
/** Default: 3 */
|
|
214
|
+
maxAttempts: number;
|
|
215
|
+
backoff: {
|
|
216
|
+
strategy: 'exponential' | 'linear' | 'fixed';
|
|
217
|
+
/** Default: 1000ms */
|
|
218
|
+
initial: number;
|
|
219
|
+
/** Default: 10000ms */
|
|
220
|
+
max: number;
|
|
221
|
+
/** Default: 2 */
|
|
222
|
+
multiplier: number;
|
|
223
|
+
};
|
|
224
|
+
/** Default: ['ECONNREFUSED', 'ETIMEDOUT', '5xx'] */
|
|
225
|
+
retryableErrors: string[];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Circuit breaker configuration
|
|
230
|
+
*/
|
|
231
|
+
export interface ICircuitBreakerConfig {
|
|
232
|
+
/** Default: true */
|
|
233
|
+
enabled: boolean;
|
|
234
|
+
/** Default: 5 */
|
|
235
|
+
failureThreshold: number;
|
|
236
|
+
/** Default: 60000ms */
|
|
237
|
+
resetTimeout: number;
|
|
238
|
+
/** Default: 1 */
|
|
239
|
+
halfOpenMaxRequests: number;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Vault secret response structure
|
|
244
|
+
*/
|
|
245
|
+
export interface IVaultSecretResponse {
|
|
246
|
+
/**
|
|
247
|
+
* Secret data (structure varies by engine)
|
|
248
|
+
*/
|
|
249
|
+
data: Record<string, any>;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Lease ID (for dynamic secrets)
|
|
253
|
+
*/
|
|
254
|
+
lease_id?: string;
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Lease duration in seconds (for dynamic secrets)
|
|
258
|
+
*/
|
|
259
|
+
lease_duration?: number;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Whether the lease can be renewed
|
|
263
|
+
*/
|
|
264
|
+
renewable?: boolean;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Request ID for tracing
|
|
268
|
+
*/
|
|
269
|
+
request_id?: string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Normalized Vault secret (internal representation)
|
|
274
|
+
*/
|
|
275
|
+
export interface IVaultSecret {
|
|
276
|
+
/**
|
|
277
|
+
* Secret data (engine-specific structure)
|
|
278
|
+
*/
|
|
279
|
+
data: Record<string, any>;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Lease ID (for dynamic secrets)
|
|
283
|
+
*/
|
|
284
|
+
leaseId?: string;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Lease duration in seconds
|
|
288
|
+
*/
|
|
289
|
+
leaseDuration: number;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Whether lease is renewable
|
|
293
|
+
*/
|
|
294
|
+
renewable: boolean;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Metadata (for KV v2)
|
|
298
|
+
*/
|
|
299
|
+
metadata?: {
|
|
300
|
+
createdTime: string;
|
|
301
|
+
deletionTime: string;
|
|
302
|
+
destroyed: boolean;
|
|
303
|
+
version: number;
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Vault property metadata (from decorators)
|
|
309
|
+
*/
|
|
310
|
+
export interface VaultPropertyMetadata {
|
|
311
|
+
/**
|
|
312
|
+
* Vault path (from @VaultPath decorator)
|
|
313
|
+
*/
|
|
314
|
+
path: string;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Engine type (from @VaultEngine decorator, or auto-detected)
|
|
318
|
+
*/
|
|
319
|
+
engine: VaultEngineType;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Key name (from @VaultKey decorator, or defaults to property name)
|
|
323
|
+
*/
|
|
324
|
+
key?: string;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Refresh buffer override (from @VaultRefreshBuffer decorator)
|
|
328
|
+
*/
|
|
329
|
+
refreshBuffer?: number;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Whether required (from @VaultOptional decorator - false if present, true otherwise)
|
|
333
|
+
*/
|
|
334
|
+
required: boolean;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Property name
|
|
338
|
+
*/
|
|
339
|
+
propertyName: string;
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Property type (for validation)
|
|
343
|
+
*/
|
|
344
|
+
propertyType: string;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Vault cache entry
|
|
349
|
+
*/
|
|
350
|
+
export interface VaultCacheEntry {
|
|
351
|
+
/**
|
|
352
|
+
* The secret value (extracted from Vault response)
|
|
353
|
+
*/
|
|
354
|
+
value: any;
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Original Vault secret
|
|
358
|
+
*/
|
|
359
|
+
secret: IVaultSecret;
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* When this secret was cached
|
|
363
|
+
*/
|
|
364
|
+
cachedAt: number;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* When this secret expires (timestamp)
|
|
368
|
+
*/
|
|
369
|
+
expiresAt: number;
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* When to refresh this secret (timestamp)
|
|
373
|
+
*/
|
|
374
|
+
refreshAt: number;
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Property name this secret is mapped to
|
|
378
|
+
*/
|
|
379
|
+
propertyName: string;
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Vault path
|
|
383
|
+
*/
|
|
384
|
+
vaultPath: string;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Vault health status
|
|
389
|
+
*/
|
|
390
|
+
export interface VaultHealth {
|
|
391
|
+
connected: boolean;
|
|
392
|
+
authenticated: boolean;
|
|
393
|
+
cacheSize: number;
|
|
394
|
+
refreshQueueSize: number;
|
|
395
|
+
lastRefreshTime: number;
|
|
396
|
+
errors: VaultError[];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Vault error (sanitized)
|
|
401
|
+
*/
|
|
402
|
+
export interface VaultError {
|
|
403
|
+
timestamp: number;
|
|
404
|
+
path: string;
|
|
405
|
+
error: string; // Sanitized
|
|
406
|
+
retryable: boolean;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Refresh status for a secret
|
|
411
|
+
*/
|
|
412
|
+
export interface IRefreshStatus {
|
|
413
|
+
/**
|
|
414
|
+
* Property name
|
|
415
|
+
*/
|
|
416
|
+
propertyName: string;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Vault path
|
|
420
|
+
*/
|
|
421
|
+
vaultPath: string;
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Whether refresh is scheduled
|
|
425
|
+
*/
|
|
426
|
+
scheduled: boolean;
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* When refresh is scheduled (timestamp)
|
|
430
|
+
*/
|
|
431
|
+
refreshAt: number;
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Time until refresh (ms)
|
|
435
|
+
*/
|
|
436
|
+
timeUntilRefresh: number;
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Last refresh time (timestamp)
|
|
440
|
+
*/
|
|
441
|
+
lastRefresh: number;
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Number of refresh attempts
|
|
445
|
+
*/
|
|
446
|
+
refreshCount: number;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Detailed Vault health information
|
|
451
|
+
*/
|
|
452
|
+
export interface IVaultHealthDetails {
|
|
453
|
+
/**
|
|
454
|
+
* Whether connected to Vault
|
|
455
|
+
*/
|
|
456
|
+
connected: boolean;
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Whether authenticated
|
|
460
|
+
*/
|
|
461
|
+
authenticated: boolean;
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Cache size (number of cached secrets)
|
|
465
|
+
*/
|
|
466
|
+
cacheSize: number;
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Refresh queue size (number of scheduled refreshes)
|
|
470
|
+
*/
|
|
471
|
+
refreshQueueSize: number;
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Last refresh time (timestamp)
|
|
475
|
+
*/
|
|
476
|
+
lastRefreshTime: number;
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Recent errors
|
|
480
|
+
*/
|
|
481
|
+
errors: VaultError[];
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Refresh status for all secrets
|
|
485
|
+
*/
|
|
486
|
+
refreshStatus: IRefreshStatus[];
|
|
487
|
+
}
|