@govuk-pay/cli 0.0.53 → 0.0.55
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/package.json +1 -1
- package/readme.md +4 -3
- package/resources/pay-local/config/service_config.yaml +2 -0
- package/resources/pay-local/templates/docker-compose.hbs +15 -3
- package/src/commands/local/config/pay_local_cluster.js +2 -1
- package/src/commands/secrets/config/config.types.js +6 -1
- package/src/commands/secrets/config/secrets/bitwarden_cli.js +4 -0
- package/src/commands/secrets/config/secrets.js +3 -0
- package/src/commands/secrets/providers/bitwarden_cli.js +98 -0
- package/src/commands/secrets/providers/factory.js +7 -1
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -23,9 +23,10 @@ once it's installed you can run it using `pay [arguments]` or `payx [arguments]`
|
|
|
23
23
|
If you'd rather not install it globally you can run it using `npx @govuk-pay/cli`.
|
|
24
24
|
|
|
25
25
|
We're in the process of porting the existing Ruby CLI into typescript, we've tried to make that process
|
|
26
|
-
transparent to the users of the CLI.
|
|
27
|
-
|
|
28
|
-
if you want a different shell or `rbenv`
|
|
26
|
+
transparent to the users of the CLI. None of the commands now rely on the ruby implementation, but you can
|
|
27
|
+
(for now) still execute the ruby versions by using `pay legacy`. You'll need `rbenv` installed with `bundler
|
|
28
|
+
installed into it. When handing over to ruby we use `zsh` by default, if you want a different shell or `rbenv`
|
|
29
|
+
you can set the following environment variables:
|
|
29
30
|
|
|
30
31
|
- `PAY_CLI_RBENV_COMMAND` - defaults to `rbenv`
|
|
31
32
|
- `PAY_CLI_SHELL_COMMAND` - defaults to `zsh`
|
|
@@ -144,11 +144,13 @@ selfservice:
|
|
|
144
144
|
type: node
|
|
145
145
|
proxy: true
|
|
146
146
|
naxsi: true
|
|
147
|
+
is_bundled: true
|
|
147
148
|
db: false
|
|
148
149
|
port: 9400
|
|
149
150
|
debug_port: 9401
|
|
150
151
|
healthcheck: true
|
|
151
152
|
proxy_port: 39000
|
|
153
|
+
entrypoint_override_local: 'sh -c "npm ci && npm run dev"'
|
|
152
154
|
clusters:
|
|
153
155
|
- admin
|
|
154
156
|
- endtoend
|
|
@@ -138,7 +138,11 @@ services:
|
|
|
138
138
|
test: ["CMD", "wget", "-Y", "off", "-O", "/dev/null", "http://{{name}}:{{port}}/healthcheck"]
|
|
139
139
|
interval: 10s
|
|
140
140
|
timeout: 5s
|
|
141
|
+
{{#ifBoth isBundled localBuild}}
|
|
142
|
+
retries: 20
|
|
143
|
+
{{else}}
|
|
141
144
|
retries: 12
|
|
145
|
+
{{/ifBoth}}
|
|
142
146
|
env_file:
|
|
143
147
|
- {{../defaultServiceConfigsPath}}/services/{{name}}.env
|
|
144
148
|
{{#ifFileExists environmentOverrideFilePath}}
|
|
@@ -147,9 +151,12 @@ services:
|
|
|
147
151
|
{{#ifBoth ../mountLocalNodeApps localBuild}}
|
|
148
152
|
volumes:
|
|
149
153
|
- "$WORKSPACE/pay-{{../name}}:/app"
|
|
150
|
-
|
|
154
|
+
{{#if ../isBundled}}
|
|
155
|
+
- {{../name}}_node_modules:/app/node_modules
|
|
156
|
+
{{/if}}
|
|
157
|
+
{{#if ../../mountPayJSCommons}}
|
|
151
158
|
- "$WORKSPACE/pay-js-commons:/pay-js-commons"
|
|
152
|
-
|
|
159
|
+
{{/if}}
|
|
153
160
|
{{/ifBoth}}
|
|
154
161
|
environment:
|
|
155
162
|
- BIND_HOST=0.0.0.0
|
|
@@ -281,5 +288,10 @@ networks:
|
|
|
281
288
|
|
|
282
289
|
volumes:
|
|
283
290
|
{{#each dbServices}}
|
|
284
|
-
|
|
291
|
+
{{name}}:
|
|
292
|
+
{{/each}}
|
|
293
|
+
{{#each nodeApps}}
|
|
294
|
+
{{#if isBundled}}
|
|
295
|
+
{{name}}_node_modules:
|
|
296
|
+
{{/if}}
|
|
285
297
|
{{/each}}
|
|
@@ -180,7 +180,8 @@ function payServiceFromPayServiceConfig(config, upOptions, environmentOverridesP
|
|
|
180
180
|
imageTag: localBuild ? 'local' : 'latest-master',
|
|
181
181
|
requiresLocalStack: sqsQueues.length > 0 || snsTopics.length > 0,
|
|
182
182
|
entrypointOverrideLocal: config.entrypoint_override_local,
|
|
183
|
-
environmentOverrideFilePath: node_path_1.default.join(environmentOverridesPath, `${config.name}.env`)
|
|
183
|
+
environmentOverrideFilePath: node_path_1.default.join(environmentOverridesPath, `${config.name}.env`),
|
|
184
|
+
isBundled: config.is_bundled
|
|
184
185
|
};
|
|
185
186
|
}
|
|
186
187
|
function dbServiceFromPayServiceConfig(config) {
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SERVICE_NAMES = exports.ENVIRONMENT_NAMES = exports.SECRET_SOURCES = void 0;
|
|
4
|
-
exports.SECRET_SOURCES = [
|
|
4
|
+
exports.SECRET_SOURCES = [
|
|
5
|
+
'bitwarden-cli',
|
|
6
|
+
'pay-low-pass',
|
|
7
|
+
'ssm',
|
|
8
|
+
'value'
|
|
9
|
+
];
|
|
5
10
|
exports.ENVIRONMENT_NAMES = [
|
|
6
11
|
'deploy',
|
|
7
12
|
'deploy-7',
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getSecretConfig = exports.configuredSecretsForServiceInEnv = exports.SECRET_SOURCE_PRECEDENCE = exports.SECRETS = void 0;
|
|
4
|
+
const bitwarden_cli_1 = require("./secrets/bitwarden_cli");
|
|
4
5
|
const pay_low_pass_1 = require("./secrets/pay_low_pass");
|
|
5
6
|
const config_types_1 = require("./config.types");
|
|
6
7
|
const service_secrets_1 = require("./service_secrets");
|
|
7
8
|
const ssm_1 = require("./secrets/ssm");
|
|
8
9
|
const value_1 = require("./secrets/value");
|
|
9
10
|
exports.SECRETS = {
|
|
11
|
+
'bitwarden-cli': bitwarden_cli_1.BITWARDEN_CLI_CONFIG,
|
|
10
12
|
ssm: ssm_1.SSM_CONFIG,
|
|
11
13
|
'pay-low-pass': pay_low_pass_1.PAY_LOW_PASS_CONFIG,
|
|
12
14
|
value: value_1.VALUE_CONFIG
|
|
@@ -17,6 +19,7 @@ exports.SECRETS = {
|
|
|
17
19
|
*/
|
|
18
20
|
exports.SECRET_SOURCE_PRECEDENCE = [
|
|
19
21
|
'value',
|
|
22
|
+
'bitwarden-cli',
|
|
20
23
|
'pay-low-pass',
|
|
21
24
|
'ssm'
|
|
22
25
|
];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BitwardenCLIProvider = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const providers_types_1 = require("./providers.types");
|
|
6
|
+
class BitwardenCLIProvider extends providers_types_1.AbstractSecretProvider {
|
|
7
|
+
bitwardenUnlockToken;
|
|
8
|
+
constructor(env, secretSource) {
|
|
9
|
+
super(env, secretSource);
|
|
10
|
+
this.bitwardenUnlockToken = checkBitwardenStatus();
|
|
11
|
+
}
|
|
12
|
+
async get(secretConfig) {
|
|
13
|
+
const env = {
|
|
14
|
+
...process.env
|
|
15
|
+
};
|
|
16
|
+
if (this.bitwardenUnlockToken !== undefined) {
|
|
17
|
+
env.BW_SESSION = this.bitwardenUnlockToken;
|
|
18
|
+
}
|
|
19
|
+
const bwGetPasswordResult = (0, child_process_1.spawnSync)('bw', ['get', 'password', secretConfig.secretSourceValue], {
|
|
20
|
+
env
|
|
21
|
+
});
|
|
22
|
+
if (bwGetPasswordResult.status !== 0) {
|
|
23
|
+
if (bwGetPasswordResult.stderr.toString().includes('Not found.')) {
|
|
24
|
+
console.error(`Secret ${secretConfig.secretSourceValue} not found in bitwarden. This can either be because it doesn't exist, or you don't have access`);
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
console.error('Unknown error when trying to get the password from bitwarden, output from the get follows:');
|
|
28
|
+
console.error(bwGetPasswordResult.output.toString());
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
return bwGetPasswordResult.stdout.toString();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.BitwardenCLIProvider = BitwardenCLIProvider;
|
|
35
|
+
function checkBitwardenStatus() {
|
|
36
|
+
const bwAvailableResult = (0, child_process_1.spawnSync)('command', ['-v', 'bw'], { shell: true });
|
|
37
|
+
if (bwAvailableResult.status !== 0) {
|
|
38
|
+
console.error('You need to install the bitwarden CLI before you can source secrets from it: `brew install bitwarden-cli`');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const bwStatusResult = (0, child_process_1.spawnSync)('bw', ['status']);
|
|
42
|
+
if (bwStatusResult.status !== 0) {
|
|
43
|
+
console.error('Could not check the login status of the bitwarden cli with command `bw status`. Output from the command follows:');
|
|
44
|
+
console.error(bwStatusResult.output.toString());
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
let bwStatusJson;
|
|
48
|
+
try {
|
|
49
|
+
bwStatusJson = JSON.parse(bwStatusResult.stdout.toString());
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
console.error('An error occured when trying to parse the JSON output of `bw status`. The output of that command follows:');
|
|
53
|
+
console.error(bwStatusResult.output.toString());
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
if (!('status' in bwStatusJson)) {
|
|
57
|
+
console.error('The JSON output of `bw status` did not contain a \'status\' key. The output of that command follows:');
|
|
58
|
+
console.error(bwStatusResult.output.toString());
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
if (bwStatusJson.status === 'unauthenticated') {
|
|
62
|
+
console.error('Error: You are not authenticated in the bitwarden cli, you need to run `bw login` and login');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
else if (bwStatusJson.status === 'locked') {
|
|
66
|
+
console.warn('Your bitwarden cli is logged in, but locked. Executing `bw unlock`, please enter your master password when prompted');
|
|
67
|
+
return unlockBitWarden();
|
|
68
|
+
}
|
|
69
|
+
else if (bwStatusJson.status === 'unlocked') {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function unlockBitWarden() {
|
|
74
|
+
const bwUnlockResult = (0, child_process_1.spawnSync)('bw', ['unlock'], {
|
|
75
|
+
shell: true,
|
|
76
|
+
stdio: ['inherit', 'pipe', 'inherit']
|
|
77
|
+
});
|
|
78
|
+
if (bwUnlockResult.status !== 0) {
|
|
79
|
+
console.error('Failed to unlock your bitwarden vault.');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
const lineWithToken = bwUnlockResult.stdout.toString().split('\n').find((line) => {
|
|
83
|
+
return line.startsWith('$ export BW_SESSION=');
|
|
84
|
+
});
|
|
85
|
+
if (lineWithToken === undefined) {
|
|
86
|
+
console.error('Could not find the BW_SESSION token in the `bw unlock` output. Looking for a line that starts with `$ export BW_SESSION=`. Got:');
|
|
87
|
+
console.error(bwUnlockResult.output.toString());
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const sessionToken = lineWithToken.substring(lineWithToken.indexOf('"') + 1, lineWithToken.lastIndexOf('"'));
|
|
91
|
+
if (sessionToken.length === 0 || sessionToken.startsWith('$ export BW_SESSION=') || sessionToken.includes('"')) {
|
|
92
|
+
console.error('The session token we read from `bw unlock` does not look correct. Output from the command follows');
|
|
93
|
+
console.error(bwUnlockResult.output.toString());
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
console.warn('Bitwarden unlocked');
|
|
97
|
+
return sessionToken;
|
|
98
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.providerFor = void 0;
|
|
4
|
+
const bitwarden_cli_1 = require("./bitwarden_cli");
|
|
4
5
|
const pass_repo_1 = require("./pass_repo");
|
|
5
6
|
const ssm_1 = require("./ssm");
|
|
6
7
|
const value_1 = require("./value");
|
|
7
8
|
const providers = {
|
|
8
|
-
|
|
9
|
+
'bitwarden-cli': {},
|
|
9
10
|
'pay-low-pass': {},
|
|
11
|
+
ssm: {},
|
|
10
12
|
value: {}
|
|
11
13
|
};
|
|
12
14
|
function providerFor(secretConfig) {
|
|
@@ -25,6 +27,10 @@ function providerFor(secretConfig) {
|
|
|
25
27
|
memoisedProvider = new ssm_1.SSMProvider(secretConfig.environment, secretConfig.source);
|
|
26
28
|
break;
|
|
27
29
|
}
|
|
30
|
+
case 'bitwarden-cli': {
|
|
31
|
+
memoisedProvider = new bitwarden_cli_1.BitwardenCLIProvider(secretConfig.environment, secretConfig.source);
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
28
34
|
}
|
|
29
35
|
providers[secretConfig.source][secretConfig.environment] = memoisedProvider;
|
|
30
36
|
}
|