(原始内容存档于2018-09-21). Envilder Node.js SDK
安全装入环境变量 AWS SSM 参数存储 或者说 Azure 密钥断层 直接输入您的Node.js应用程序. 零卖家锁定:保密留入你云.
部分 诽谤者 项目.
先决条件
- 节点.js 22.12+
- AWS 提供者: 配置 AWS 证书( CLI, 环境变量, 或 IAM 角色)
- Azure 提供者: 通过
az login,管理身份,或环境变量
安装
npm install @envilder/sdk
快速启动
单线: 解析+注入
import { Envilder } from '@envilder/sdk';
// Resolve secrets and inject into process.env
await Envilder.load('envilder.json');
console.log('DB_PASSWORD loaded:', !!process.env.DB_PASSWORD);
解决不注射
import { Envilder } from '@envilder/sdk';
const secrets = await Envilder.resolveFile('envilder.json');
console.log(secrets.get('DB_PASSWORD')); // avoid logging secrets in production
Azure 密钥断层
在地图文件中设置提供者 $config; 值是密钥默认密名 :
{
"$schema": "https://envilder.com/schema/map-file.v1.json",
"$config": {
"provider": "azure",
"vaultUrl": "https://my-vault.vault.azure.net"
},
"DB_PASSWORD": "myapp-prod-db-password"
}
数值按所写发送到密钥默认值, 但斜线变为连字符: 路径样式值
例如 /myapp/prod/db-password 改为 myapp-prod-db-password,同名CLI推动
它在下面。 它在下面.
代码与AWS相同. 全权证书以下列方式解决: DefaultAzureCredential
(单位:千美元)az login,管理身份,或环境变量:
import { Envilder } from '@envilder/sdk';
await Envilder.load('envilder.json');
console.log('DB_PASSWORD loaded:', !!process.env.DB_PASSWORD);
流利的构建器( 有覆盖)
覆盖地图文件 $config 运行时间: 用于切换提供者,
配置文件或每个环境的库 URL :
import { Envilder, SecretProviderType } from '@envilder/sdk';
// Override provider + vault URL
const secrets = await Envilder.fromMapFile('envilder.json')
.withProvider(SecretProviderType.Azure)
.withVaultUrl('https://my-vault.vault.azure.net')
.resolve();
// Override AWS profile and inject
await Envilder.fromMapFile('envilder.json')
.withProfile('staging')
.inject();
基于环境的装载
基于您当前环境的路径秘密加载 。 每个环境
地图到自己的机密文件(或 null 以跳过加载 :
import { Envilder } from '@envilder/sdk';
const env = process.env.APP_ENV ?? 'development';
// Resolve + inject into process.env
await Envilder.load(env, {
production: 'prod-secrets.json',
development: 'dev-secrets.json',
test: null, // no secrets loaded
});
解决不注射:
const secrets = await Envilder.resolveFile(env, {
production: 'prod-secrets.json',
development: 'dev-secrets.json',
test: null,
});
行为:
- 如果环境映射到文件路径,则从该文件中载入机密.
- 如果环境映射到
null或不是在地图中,是空的Map默默返回. - 空或仅白空的环境名称
Error。 。 。 .
秘密验证
Opt- in 验证确保所有解密都有非空值 :
import { Envilder, validateSecrets } from '@envilder/sdk';
const secrets = await Envilder.resolveFile('envilder.json');
validateSecrets(secrets); // throws SecretValidationError if any value is empty
validateSecrets() 检查:
- 地图不是空的( 扔出)
SecretValidationError带空missingKeys(中文(简体) ) - 每个值都是非空( 扔出)
SecretValidationError列出失败的密钥) - 当所有值都存在时悄悄通过
import { SecretValidationError, validateSecrets } from '@envilder/sdk';
try {
validateSecrets(secrets);
} catch (err) {
if (err instanceof SecretValidationError) {
console.log(`Missing: ${err.missingKeys.join(', ')}`);
}
}
地图文件合同验证
在装入任何文件前, 请检查地图文件是否有效
与当前证书一起存在被宣布的秘密。 没有使用秘密值
或返回。 这是相同的支票 envilder validate 在CLI中.
import { Envilder } from '@envilder/sdk';
const result = await Envilder.fromMapFile('envilder.json').validateContract();
if (!result.isValid) {
for (const error of result.mapFileErrors) {
console.error(`${error.key}: ${error.message}`);
}
for (const key of result.keys.filter((k) => k.status !== 'exists')) {
console.error(`${key.variable} (${key.provider} ${key.path}): ${key.status}`);
}
}
validateContract() 从未为预期的失败投出
结果:
mapFileErrors: 地图文件不匹配 已出版计划 或编号 提供者配置无法工作( 例如 )vaultUrl与AWS). 何时 没有,没有提供商呼叫和keys为空.keys: 每个变量有一个条目path, (中文(简体) ),provider财务报告和审定财务报表status数字 :exists, (中文(简体) ),missing, (中文(简体) ),access_denied(身份无法检查秘密), 从未报告为missing, (中文(简体) ),authentication_failed或者说provider_error。 。 。 .reason当提供商给出一个时,解释最后三个.
在合同未履行时未能完成负载,请选择建筑商。 这个
在解析和丢弃前检查一次 ContractValidationError,其
消息命名失败的密钥和路径( 绝不是其值)
result 掌握全部结果:
await Envilder.fromMapFile('envilder.json')
.withContractValidation()
.inject(); // throws ContractValidationError if a key is missing
无 withContractValidation(),负载不会增加提供者呼叫和
不需要额外的许可 。 检查需要 ssm:GetParameters 关于AWS( 它
用途 GetParameters 分批 10 个 WithDecryption: false; 平面
String 值仍然可以在反应中返回并被丢弃
与 kms:Decrypt 相同的权限读取已解密的值, 所以范围
喜欢 ssm:GetParameter和, 打开
Azure 键断层 List 秘密许可(进入政策)或
Microsoft.KeyVault/vaults/secrets/readMetadata/action (Azure RBAC,列入
),因为它列出了秘密元数据而不是
读取值.
合同的审定和 validateSecrets() 回答不同的问题:
合同验证检查地图文件是否有效, 并宣布每个机密
在解决之前存在; validateSecrets() 检查已经存在的值
解决不是空的.
高级用途
执行 ISecretProvider 插入自定义后端的接口
(例如,HashiCorp Vault,GCP秘密经理):
import {
EnvilderClient,
MapFileParser,
type ISecretProvider,
} from '@envilder/sdk';
import { readFileSync } from 'node:fs';
class MyCustomProvider implements ISecretProvider {
async getSecrets(names: string[]): Promise<Map<string, string>> {
// fetch from your custom backend
return new Map();
}
}
const json = readFileSync('envilder.json', 'utf-8');
const mapFile = new MapFileParser().parse(json);
const provider = new MyCustomProvider();
const client = new EnvilderClient(provider);
const secrets = await client.resolveSecrets(mapFile);
EnvilderClient.injectIntoEnvironment(secrets);
在流畅的构建器中自定义提供者
withSecretProvider() 将构建者交给您自己的提供者实例而不是
它会建造 从 $config。地图文件仍然被验证为
常规,所以这也是测试您的启动代码与内存的方法
假,没有云账户、模拟器或环境变量:
import {
Envilder,
type ISecretExistenceChecker,
type ISecretProvider,
} from '@envilder/sdk';
const fakeSecrets = new Map([['/app/db-url', 'postgres://localhost/test']]);
class InMemoryProvider implements ISecretProvider, ISecretExistenceChecker {
async getSecrets(names: string[]): Promise<Map<string, string>> {
return new Map(
names.flatMap((name) => {
const value = fakeSecrets.get(name);
return value === undefined ? [] : [[name, value] as const];
}),
);
}
async checkSecretsExist(names: readonly string[]) {
return new Map(
names.map((name) => [
name,
{ status: fakeSecrets.has(name) ? 'exists' : 'missing' } as const,
]),
);
}
}
await Envilder.fromMapFile('envilder.json')
.withSecretProvider(new InMemoryProvider())
.withContractValidation()
.inject();
为了参与合同验证,供应商还执行
ISecretExistenceChecker。只执行的提供者 ISecretProvider
仍然可以解开秘密,但是 validateContract() 每个密钥报告为
provider_error这样 withContractValidation() 负载失败 。 这个
provider 每个密钥的字段保留所配置的类型(withProvider(), (中文(简体) ),
接下来 $config.provider,则 aws) (中文(简体) ).
对于一个完整的,可运行的走过,看
typescript-custom-provider
缩写中:一个本地AES-256-GCM加密的自定义供应商
文件,带有加载的测试,启动时失败于缺失的秘密,以及
验证合同而不解密任何东西.
API 参考
静态外观Envilder(中文(简体) )
| 方法 | 说明 |
|---|---|
load(path) | 解密并输入 process.env |
resolveFile(path) | 解密, 返回为 Map<string, string> |
load(env, mapping) | 以环境为基础的决心+注入 |
resolveFile(env, mapping) | 以环境为基础的决心 |
fromMapFile(path) | 返回配置的流利构建器 |
流利的建设者(通过) fromMapFile()(中文(简体) )
| 方法 | 说明 |
|---|---|
withProvider(type) | 覆盖秘密提供者(AWS/Azure) |
withProfile(name) | 覆盖命名为 AWS 的配置文件 |
withVaultUrl(url) | 覆盖 Azure 密钥断层 URL |
withSecretProvider(provider) | 自己来 ISecretProvider 而不是从 $config |
withContractValidation() | 在解决前检查一次地图文件合同; 失败 ContractValidationError |
validateContract() | 检查地图文件合同并返回 a ContractValidationResult未加载 |
resolve() | 解密, 返回为 Map |
inject() | 解析 + 注入 process.env |
验证
| 函数 | 说明 |
|---|---|
validateSecrets(map) | 掷出 SecretValidationError 如果任何值为空或映射为空 |
映射文件格式
{
"$schema": "https://envilder.com/schema/map-file.v1.json",
"$config": {
"provider": "aws",
"profile": "my-profile"
},
"DB_PASSWORD": "/app/prod/db-password",
"API_KEY": "/app/prod/api-key"
}
支助提供者: aws (违约), azure。 。 。 .
对Azure, 添加 vaultUrl数字 :
{
"$schema": "https://envilder.com/schema/map-file.v1.json",
"$config": {
"provider": "azure",
"vaultUrl": "https://my-vault.vault.azure.net"
},
"DB_PASSWORD": "myapp-prod-db-password",
"API_KEY": "myapp-prod-api-key"
}
关键和价值
密钥遵循与CLI和GitHub Action相同的规则,所以一个接受的密钥被大家接受:
- 密钥 可能只包含字母,数字,
_, (中文(简体) ),., (中文(简体) ),-财务报告和审定财务报表/(单位:千美元)^[A-Za-z0-9_./-]+$(三);__proto__也被拒绝.MapFileParser.parse掷出Error在获取任何秘密之前./允许使用等级名称(Database/Password),并按原样返回. - 数值 是提供者中的秘密标识符( SSM 参数名称或密钥默认密钥) 名字),必须是JSON字符串.
- 秘密内容 以提供者存储的方式返回.
resolveFile编号 :resolve让他们进去 记忆;load编号 :inject将其写入操作系统无法操作的进程环境 存储NUL(单位:千美元)U+0000)和Windows将变量限制为32,767个字符.
见 关键和价值:接受什么 充分参考和解释规则.
见 根读取 用于完整地图文件参考.
实例
可运行的项目 阴道模拟器 解析映射文件
与 AWS SSM 和 Azure 密钥 Vault 相对应(由 localStack 为 AWS 和 LocalStack 为 Azure 组成),并将数值传递到
容器,无 .env 文件 :