Skip to content

Finish multi environment encryption #221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9e2025a
feat(#105): starts on creating environmentOptions for encryption team…
joelgallant Jun 19, 2021
f748f61
feat: environment asliases for env-specific encryption
joelgallant Jun 19, 2021
2ebb3bc
fix: addForEnvironment should respect replace when using array input
joelgallant Jun 19, 2021
155908a
Merge branch 'master' into multi-env-encryption
joelgallant Jun 27, 2021
315e6e3
feat: use meta file environmentOptions when looking up environment fo…
joelgallant Jun 27, 2021
1c21d95
fix: lint
joelgallant Jun 27, 2021
6b7575a
feat: uses environmentOptionsFromContext for decryptValue encryption …
joelgallant Jun 30, 2021
d08f16f
feat: support env suffixed encryption env var keys
johnb8 May 21, 2023
a8ea8c6
chore: tests for env secret key
johnb8 May 21, 2023
534596d
feat: encryption key revision includes environment
johnb8 May 22, 2023
0d5c00a
fix: add environment team members properly
johnb8 May 22, 2023
2598c16
test: add environments to encrypton tests
johnb8 May 22, 2023
bd8e1e9
feat: override env with whats in the key revision to allow for key reuse
johnb8 May 22, 2023
af20cd2
docs: add multi environment encryption
johnb8 May 22, 2023
4620708
fix: init-key creates the right key revision
johnb8 May 22, 2023
ac2d077
fix: encryption environment adding and reading old key revisions
johnb8 Jun 5, 2023
3cf016d
fix: select all symmetric keys when no environment is selected
johnb8 Jun 6, 2023
fb54e0b
fix: only send the decryption env if one is specified
johnb8 Jun 6, 2023
ac9cecd
chore: encryption docs
johnb8 Jun 6, 2023
b1a285e
Merge remote-tracking branch 'upstream/master' into finish-multi-env-…
johnb8 Jun 6, 2023
c22424c
chore: readd key file env var support
johnb8 Jun 7, 2023
0d8cf89
fix: prioritize default keys when no env is selected
johnb8 Jun 8, 2023
f01a273
chore: fix lint
johnb8 Jun 8, 2023
22d0f46
chore: release v2.9.0-beta.1
johnb8 Jun 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions app-config-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@app-config/cli",
"description": "CLI for @app-config",
"version": "2.8.7",
"version": "2.9.0-beta.1",
"license": "MPL-2.0",
"author": {
"name": "Launchcode",
Expand Down Expand Up @@ -35,14 +35,14 @@
"prepublishOnly": "yarn clean && yarn build && yarn build:es"
},
"dependencies": {
"@app-config/config": "^2.8.7",
"@app-config/core": "^2.8.7",
"@app-config/encryption": "^2.8.7",
"@app-config/generate": "^2.8.7",
"@app-config/logging": "^2.8.7",
"@app-config/node": "^2.8.7",
"@app-config/schema": "^2.8.7",
"@app-config/utils": "^2.8.7",
"@app-config/config": "^2.9.0-beta.1",
"@app-config/core": "^2.9.0-beta.1",
"@app-config/encryption": "^2.9.0-beta.1",
"@app-config/generate": "^2.9.0-beta.1",
"@app-config/logging": "^2.9.0-beta.1",
"@app-config/node": "^2.9.0-beta.1",
"@app-config/schema": "^2.9.0-beta.1",
"@app-config/utils": "^2.9.0-beta.1",
"ajv": "7",
"clipboardy": "2",
"common-tags": "1",
Expand All @@ -52,7 +52,7 @@
"yargs": "16"
},
"devDependencies": {
"@app-config/test-utils": "^2.8.7",
"@app-config/test-utils": "^2.9.0-beta.1",
"@types/common-tags": "1",
"@types/fs-extra": "9"
},
Expand Down
121 changes: 99 additions & 22 deletions app-config-cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
FailedToSelectSubObject,
EmptyStdinOrPromptResponse,
} from '@app-config/core';
import { promptUser, consumeStdin } from '@app-config/node';
import { promptUser, consumeStdin, asEnvOptions, currentEnvironment } from '@app-config/node';
import { checkTTY, LogLevel, logger } from '@app-config/logging';
import {
LoadedConfiguration,
Expand Down Expand Up @@ -44,9 +44,11 @@ import {
shouldUseSecretAgent,
startAgent,
disconnectAgents,
getRevisionNumber,
} from '@app-config/encryption';
import { loadSchema, JSONSchema } from '@app-config/schema';
import { generateTypeFiles } from '@app-config/generate';
import { loadMetaConfigLazy } from '@app-config/meta';
import { validateAllConfigVariants } from './validation';

enum OptionGroups {
Expand Down Expand Up @@ -319,6 +321,21 @@ function fileTypeForFormatOption(option: string): FileType {
}
}

async function loadEnvironmentOptions(opts: {
environmentOverride?: string;
environmentVariableName?: string;
}) {
const {
value: { environmentAliases, environmentSourceNames },
} = await loadMetaConfigLazy();

return asEnvOptions(
opts.environmentOverride,
environmentAliases,
opts.environmentVariableName ?? environmentSourceNames,
);
}

export const cli = yargs
.scriptName('app-config')
.wrap(Math.max(yargs.terminalWidth() - 5, 80))
Expand Down Expand Up @@ -579,13 +596,18 @@ export const cli = yargs
'Creates properties in meta file, making you the first trusted user',
],
],
options: {
environmentOverride: environmentOverrideOption,
},
},
async () => {
const myKey = await loadPublicKeyLazy();
const privateKey = await loadPrivateKeyLazy();
async (opts) => {
const environmentOptions = await loadEnvironmentOptions(opts);

const myKey = await loadPublicKeyLazy(environmentOptions);
const privateKey = await loadPrivateKeyLazy(environmentOptions);

// we trust ourselves, essentially
await trustTeamMember(myKey, privateKey);
await trustTeamMember(myKey, privateKey, environmentOptions);
logger.info('Initialized team members and a symmetric key');
},
),
Expand All @@ -601,20 +623,40 @@ export const cli = yargs
'Sets up a new symmetric key with the latest revision number',
],
],
options: {
environmentOverride: environmentOverrideOption,
},
},
async () => {
const keys = await loadSymmetricKeys();
const teamMembers = await loadTeamMembersLazy();
async (opts) => {
const environmentOptions = await loadEnvironmentOptions(opts);
const environment = currentEnvironment(environmentOptions);

const keys = await loadSymmetricKeys(undefined, environmentOptions);
const teamMembers = await loadTeamMembersLazy(environmentOptions);

let revision: number;
let revision: string;

if (keys.length > 0) {
revision = latestSymmetricKeyRevision(keys) + 1;
const latestRevison = latestSymmetricKeyRevision(keys);
const revNumber = getRevisionNumber(latestRevison);

if (environment) {
revision = `${environment}-${revNumber + 1}`;
} else {
revision = `${revNumber + 1}`;
}
} else if (environment) {
revision = `${environment}-1`;
} else {
revision = 1;
revision = '1';
}

await saveNewSymmetricKey(await generateSymmetricKey(revision), teamMembers);
await saveNewSymmetricKey(
await generateSymmetricKey(revision),
teamMembers,
environmentOptions,
);

logger.info(`Saved a new symmetric key, revision ${revision}`);
},
),
Expand Down Expand Up @@ -672,12 +714,22 @@ export const cli = yargs
name: 'ci',
description:
'Creates an encryption key that can be used without a passphrase (useful for CI)',
options: {
environmentOverride: environmentOverrideOption,
},
},
async () => {
async (opts) => {
const environmentOptions = await loadEnvironmentOptions(opts);

logger.info('Creating a new trusted CI encryption key');

const { privateKeyArmored, publicKeyArmored } = await initializeKeys(false);
await trustTeamMember(await loadKey(publicKeyArmored), await loadPrivateKeyLazy());

await trustTeamMember(
await loadKey(publicKeyArmored),
await loadPrivateKeyLazy(environmentOptions),
environmentOptions,
);

process.stdout.write(`\n${publicKeyArmored}\n\n${privateKeyArmored}\n\n`);

Expand Down Expand Up @@ -710,11 +762,16 @@ export const cli = yargs
description: 'Filepath of public key',
},
},
options: {
environmentOverride: environmentOverrideOption,
},
},
async (opts) => {
const environmentOptions = await loadEnvironmentOptions(opts);

const key = await loadKey(await readFile(opts.keyPath));
const privateKey = await loadPrivateKeyLazy();
await trustTeamMember(key, privateKey);
const privateKey = await loadPrivateKeyLazy(environmentOptions);
await trustTeamMember(key, privateKey, environmentOptions);

logger.info(`Trusted ${key.getUserIds().join(', ')}`);
},
Expand All @@ -738,10 +795,16 @@ export const cli = yargs
description: 'User ID email address',
},
},
options: {
environmentOverride: environmentOverrideOption,
},
},
async (opts) => {
const privateKey = await loadPrivateKeyLazy();
await untrustTeamMember(opts.email, privateKey);
const environmentOptions = await loadEnvironmentOptions(opts);
const privateKey = await loadPrivateKeyLazy(environmentOptions);

// TODO: by default, untrust for all envs?
await untrustTeamMember(opts.email, privateKey, environmentOptions);
},
),
)
Expand All @@ -763,13 +826,16 @@ export const cli = yargs
options: {
clipboard: clipboardOption,
agent: secretAgentOption,
environmentOverride: environmentOverrideOption,
},
},
async (opts) => {
const environmentOptions = await loadEnvironmentOptions(opts);

shouldUseSecretAgent(opts.agent);

// load these right away, so user unlocks asap
if (!shouldUseSecretAgent()) await loadPrivateKeyLazy();
if (!shouldUseSecretAgent()) await loadPrivateKeyLazy(environmentOptions);

let { secretValue }: { secretValue?: Json } = opts;

Expand Down Expand Up @@ -799,7 +865,7 @@ export const cli = yargs
}
}

const encrypted = await encryptValue(secretValue);
const encrypted = await encryptValue(secretValue, undefined, environmentOptions);

if (opts.clipboard) {
await clipboardy.write(encrypted);
Expand Down Expand Up @@ -827,13 +893,17 @@ export const cli = yargs
options: {
clipboard: clipboardOption,
agent: secretAgentOption,
environmentOverride: environmentOverrideOption,
},
},
async (opts) => {
const environmentOptions = await loadEnvironmentOptions(opts);
const environment = currentEnvironment(environmentOptions);

shouldUseSecretAgent(opts.agent);

// load these right away, so user unlocks asap
if (!shouldUseSecretAgent()) await loadPrivateKeyLazy();
if (!shouldUseSecretAgent()) await loadPrivateKeyLazy(environmentOptions);

let { encryptedText } = opts;

Expand All @@ -857,7 +927,14 @@ export const cli = yargs
throw new EmptyStdinOrPromptResponse('Failed to read from stdin or prompt');
}

process.stdout.write(JSON.stringify(await decryptValue(encryptedText)));
// only use an environment if one was provided - otherwise just find the key to use based on the revision
const decrypted = await decryptValue(
encryptedText,
undefined,
environment ? environmentOptions : undefined,
);

process.stdout.write(JSON.stringify(decrypted));
process.stdout.write('\n');
},
),
Expand Down
20 changes: 10 additions & 10 deletions app-config-config/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@app-config/config",
"description": "The config in @app-config",
"version": "2.8.7",
"version": "2.9.0-beta.1",
"license": "MPL-2.0",
"author": {
"name": "Launchcode",
Expand Down Expand Up @@ -30,17 +30,17 @@
"prepublishOnly": "yarn clean && yarn build && yarn build:es"
},
"dependencies": {
"@app-config/core": "^2.8.7",
"@app-config/default-extensions": "^2.8.7",
"@app-config/extensions": "^2.8.7",
"@app-config/logging": "^2.8.7",
"@app-config/meta": "^2.8.7",
"@app-config/node": "^2.8.7",
"@app-config/schema": "^2.8.7",
"@app-config/utils": "^2.8.7"
"@app-config/core": "^2.9.0-beta.1",
"@app-config/default-extensions": "^2.9.0-beta.1",
"@app-config/extensions": "^2.9.0-beta.1",
"@app-config/logging": "^2.9.0-beta.1",
"@app-config/meta": "^2.9.0-beta.1",
"@app-config/node": "^2.9.0-beta.1",
"@app-config/schema": "^2.9.0-beta.1",
"@app-config/utils": "^2.9.0-beta.1"
},
"devDependencies": {
"@app-config/test-utils": "^2.8.7"
"@app-config/test-utils": "^2.9.0-beta.1"
},
"prettier": "@lcdev/prettier",
"jest": {
Expand Down
8 changes: 4 additions & 4 deletions app-config-core/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@app-config/core",
"description": "Core logic for App Config",
"version": "2.8.7",
"version": "2.9.0-beta.1",
"license": "MPL-2.0",
"author": {
"name": "Launchcode",
Expand Down Expand Up @@ -30,15 +30,15 @@
"prepublishOnly": "yarn clean && yarn build && yarn build:es"
},
"dependencies": {
"@app-config/logging": "^2.8.7",
"@app-config/utils": "^2.8.7",
"@app-config/logging": "^2.9.0-beta.1",
"@app-config/utils": "^2.9.0-beta.1",
"@iarna/toml": "3",
"js-yaml": "^3.13.1",
"json5": "2",
"lodash.merge": "^4.6.2"
},
"devDependencies": {
"@app-config/test-utils": "^2.8.7",
"@app-config/test-utils": "^2.9.0-beta.1",
"@types/js-yaml": "3",
"@types/lodash.merge": "4"
},
Expand Down
6 changes: 3 additions & 3 deletions app-config-cypress/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@app-config/cypress",
"description": "Cypress testing plugin for @app-config",
"version": "2.8.7",
"version": "2.9.0-beta.1",
"license": "MPL-2.0",
"author": {
"name": "Launchcode",
Expand Down Expand Up @@ -31,11 +31,11 @@
},
"dependencies": {},
"peerDependencies": {
"@app-config/main": "^2.8.7",
"@app-config/main": "^2.9.0-beta.1",
"cypress": "6"
},
"devDependencies": {
"@app-config/main": "^2.8.7",
"@app-config/main": "^2.9.0-beta.1",
"cypress": "6"
},
"prettier": "@lcdev/prettier",
Expand Down
10 changes: 5 additions & 5 deletions app-config-default-extensions/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@app-config/default-extensions",
"description": "Default parsing extensions for @app-config",
"version": "2.8.7",
"version": "2.9.0-beta.1",
"license": "MPL-2.0",
"author": {
"name": "Launchcode",
Expand All @@ -22,10 +22,10 @@
"test": "jest"
},
"dependencies": {
"@app-config/core": "^2.8.7",
"@app-config/encryption": "^2.8.7",
"@app-config/extensions": "^2.8.7",
"@app-config/git": "^2.8.7",
"@app-config/core": "^2.9.0-beta.1",
"@app-config/encryption": "^2.9.0-beta.1",
"@app-config/extensions": "^2.9.0-beta.1",
"@app-config/git": "^2.9.0-beta.1",
"@app-config/v1-compat": "^2.1.4"
},
"devDependencies": {},
Expand Down
2 changes: 1 addition & 1 deletion app-config-electron/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@app-config/electron",
"description": "Exposes app-config values to Electron render processes",
"version": "2.8.7",
"version": "2.9.0-beta.1",
"license": "MPL-2.0",
"author": {
"name": "Launchcode",
Expand Down
Loading