@codemowers/oidc-key-manager 0.1.0

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/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Codemowers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ oidc-key-manager
2
+ =================
3
+
4
+ CLI to manage secret keys required by oidc-gateway
5
+
6
+ <!-- toc -->
7
+ * [Usage](#usage)
8
+ * [Commands](#commands)
9
+ <!-- tocstop -->
10
+ * [Usage](#usage)
11
+ * [Commands](#commands)
12
+ <!-- tocstop -->
13
+ # Usage
14
+ <!-- usage -->
15
+ ```sh-session
16
+ $ npm install -g @codemowers/oidc-key-manager
17
+ $ key-manager COMMAND
18
+ running command...
19
+ $ key-manager (--version)
20
+ @codemowers/oidc-key-manager/0.1.0 linux-x64 node-v16.17.0
21
+ $ key-manager --help [COMMAND]
22
+ USAGE
23
+ $ key-manager COMMAND
24
+ ...
25
+ ```
26
+ <!-- usagestop -->
27
+ ```sh-session
28
+ $ npm install -g oidc-key-manager
29
+ $ key-manager COMMAND
30
+ running command...
31
+ $ key-manager (--version)
32
+ oidc-key-manager/0.0.0 linux-x64 node-v18.15.0
33
+ $ key-manager --help [COMMAND]
34
+ USAGE
35
+ $ key-manager COMMAND
36
+ ...
37
+ ```
38
+ <!-- usagestop -->
39
+ # Commands
40
+ <!-- commands -->
41
+ * [`key-manager initialize`](#key-manager-initialize)
42
+
43
+ ## `key-manager initialize`
44
+
45
+ Initialize the secret with initial keys
46
+
47
+ ```
48
+ USAGE
49
+ $ key-manager initialize -c local|cluster [--json] [-n <value>] [-s <value>] [--recreate]
50
+
51
+ FLAGS
52
+ -c, --config=<option> (required) use local or in-cluster Kubernetes config
53
+ <options: local|cluster>
54
+ -n, --namespace=<value> namespace, defaults to current namespace if service account is used
55
+ -s, --secret=<value> [default: oidc-keys] secret name
56
+ --recreate recreate the secret if it exists
57
+
58
+ GLOBAL FLAGS
59
+ --json Format output as json.
60
+
61
+ DESCRIPTION
62
+ Initialize the secret with initial keys
63
+
64
+ EXAMPLES
65
+ $ key-manager initialize
66
+
67
+ $ key-manager initialize
68
+
69
+ $ key-manager initialize -n <kube namespace> -s <secret name>
70
+
71
+ $ key-manager initialize --namespace <kube namespace> --secret <secret name> --recreate
72
+ ```
73
+
74
+ _See code: [dist/commands/initialize.ts](https://github.com/codemowers/oidc-key-manager/blob/v0.1.0/dist/commands/initialize.ts)_
75
+ <!-- commandsstop -->
76
+ * [`key-manager initialize`](#key-manager-initialize)
77
+
78
+ ## `key-manager initialize`
79
+
80
+ Initialize the secret with initial keys
81
+
82
+ ```
83
+ USAGE
84
+ $ key-manager initialize [-n <value>] [-s <value>] [--recreate]
85
+
86
+ FLAGS
87
+ -n, --namespace=<value> namespace, defaults to current namespace if service account is used
88
+ -s, --secret=<value> [default: oidc-keys] secret name
89
+ --recreate recreate the secret if it exists
90
+
91
+ DESCRIPTION
92
+ Initialize the secret with initial keys
93
+
94
+ EXAMPLES
95
+ $ key-manager initialize
96
+
97
+ $ key-manager initialize -n <kube namespace> -s <secret name>
98
+
99
+ $ key-manager initialize --namespace <kube namespace> --secret <secret name> --recreate
100
+ ```
101
+
102
+ _See code: [dist/commands/initialize.ts](https://github.com/codemowers/oidc-key-manager/blob/v0.0.0/dist/commands/initialize.ts)_
103
+ <!-- commandsstop -->
package/bin/dev ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+
3
+ const oclif = require('@oclif/core')
4
+
5
+ const path = require('path')
6
+ const project = path.join(__dirname, '..', 'tsconfig.json')
7
+
8
+ // In dev mode -> use ts-node and dev plugins
9
+ process.env.NODE_ENV = 'development'
10
+
11
+ require('ts-node').register({project})
12
+
13
+ // In dev mode, always show stack traces
14
+ oclif.settings.debug = true;
15
+
16
+ // Start the CLI
17
+ oclif.run().then(oclif.flush).catch(oclif.Errors.handle)
package/bin/dev.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\dev" %*
package/bin/run ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ const oclif = require('@oclif/core')
4
+
5
+ oclif.run().then(require('@oclif/core/flush')).catch(require('@oclif/core/handle'))
package/bin/run.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\run" %*
@@ -0,0 +1,14 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Initialize extends Command {
3
+ static description: string;
4
+ static enableJsonFlag: boolean;
5
+ static examples: string[];
6
+ static flags: {
7
+ namespace: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
8
+ secret: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
9
+ config: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
10
+ recreate: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ static args: {};
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const core_1 = require("@oclif/core");
5
+ const common_flags_1 = tslib_1.__importDefault(require("../helpers/common-flags"));
6
+ const kube_api_service_1 = require("../helpers/kube-api-service");
7
+ const secret_1 = require("../helpers/secret");
8
+ class Initialize extends core_1.Command {
9
+ async run() {
10
+ const { flags } = await this.parse(Initialize);
11
+ const kubeApiService = new kube_api_service_1.KubeApiService(this, flags);
12
+ kubeApiService.printConfiguration();
13
+ const exists = await kubeApiService.checkSecretExistence();
14
+ if (exists && !flags.recreate) {
15
+ this.exit(0);
16
+ }
17
+ if (exists) {
18
+ await kubeApiService.deleteSecret();
19
+ }
20
+ const secret = new secret_1.Secret();
21
+ this.log('Generating secret');
22
+ secret.generateNew();
23
+ await kubeApiService.createSecret(secret);
24
+ }
25
+ }
26
+ exports.default = Initialize;
27
+ Initialize.description = 'Initialize the secret with initial keys';
28
+ Initialize.enableJsonFlag = true;
29
+ Initialize.examples = [
30
+ '<%= config.bin %> <%= command.id %>',
31
+ '<%= config.bin %> <%= command.id %>',
32
+ '<%= config.bin %> <%= command.id %> -n <kube namespace> -s <secret name>',
33
+ '<%= config.bin %> <%= command.id %> --namespace <kube namespace> --secret <secret name> --recreate',
34
+ ];
35
+ Initialize.flags = {
36
+ ...common_flags_1.default,
37
+ };
38
+ Initialize.args = {};
@@ -0,0 +1,17 @@
1
+ export interface CommonFlagsInterface {
2
+ namespace: string | undefined;
3
+ secret: string;
4
+ recreate: boolean;
5
+ config: string;
6
+ }
7
+ export declare enum ConfigType {
8
+ Local = "local",
9
+ InCluster = "cluster"
10
+ }
11
+ declare const _default: {
12
+ namespace: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
13
+ secret: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
14
+ config: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
15
+ recreate: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
16
+ };
17
+ export default _default;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigType = void 0;
4
+ const core_1 = require("@oclif/core");
5
+ var ConfigType;
6
+ (function (ConfigType) {
7
+ ConfigType["Local"] = "local";
8
+ ConfigType["InCluster"] = "cluster";
9
+ })(ConfigType = exports.ConfigType || (exports.ConfigType = {}));
10
+ exports.default = {
11
+ namespace: core_1.Flags.string({ char: 'n', description: 'namespace, defaults to current namespace if service account is used', aliases: ['namespace'], required: false }),
12
+ secret: core_1.Flags.string({ char: 's', description: 'secret name', aliases: ['secret'], default: 'oidc-keys', required: false }),
13
+ config: core_1.Flags.string({ char: 'c', description: 'use local or in-cluster Kubernetes config', aliases: ['config'], required: true, options: [ConfigType.Local, ConfigType.InCluster] }),
14
+ recreate: core_1.Flags.boolean({ description: 'recreate the secret if it exists', aliases: ['recreate'], required: false }),
15
+ };
@@ -0,0 +1,16 @@
1
+ import { CommonFlagsInterface } from './common-flags';
2
+ import { Command } from '@oclif/core';
3
+ import { Secret } from './secret';
4
+ export declare class KubeApiService {
5
+ #private;
6
+ private kc;
7
+ private coreV1Api;
8
+ private namespace;
9
+ private command;
10
+ private secretName;
11
+ constructor(command: Command, flags: CommonFlagsInterface);
12
+ printConfiguration(): void;
13
+ checkSecretExistence(): Promise<boolean>;
14
+ deleteSecret(): Promise<void>;
15
+ createSecret(secret: Secret): Promise<void>;
16
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var _KubeApiService_instances, _KubeApiService_validate;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.KubeApiService = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const client_node_1 = require("@kubernetes/client-node");
7
+ const common_flags_1 = require("./common-flags");
8
+ const Undefined = 'undefined';
9
+ class KubeApiService {
10
+ constructor(command, flags) {
11
+ var _a, _b, _c;
12
+ _KubeApiService_instances.add(this);
13
+ this.command = command;
14
+ this.kc = new client_node_1.KubeConfig();
15
+ flags.config === common_flags_1.ConfigType.InCluster ? this.kc.loadFromCluster() : this.kc.loadFromDefault();
16
+ this.namespace = (_c = (_a = flags.namespace) !== null && _a !== void 0 ? _a : (_b = this.kc.getContextObject(this.kc.getCurrentContext())) === null || _b === void 0 ? void 0 : _b.namespace) !== null && _c !== void 0 ? _c : Undefined;
17
+ this.coreV1Api = this.kc.makeApiClient(client_node_1.CoreV1Api);
18
+ this.secretName = flags.secret;
19
+ tslib_1.__classPrivateFieldGet(this, _KubeApiService_instances, "m", _KubeApiService_validate).call(this);
20
+ }
21
+ printConfiguration() {
22
+ var _a;
23
+ this.command.log('Using Kubernetes parameters:', {
24
+ ...this.kc.getContextObject(this.kc.getCurrentContext()),
25
+ server: (_a = this.kc.getCurrentCluster()) === null || _a === void 0 ? void 0 : _a.server,
26
+ secretName: this.secretName,
27
+ });
28
+ }
29
+ async checkSecretExistence() {
30
+ this.command.log(`Checking if secret ${this.secretName} exists`);
31
+ const exists = await this.coreV1Api.readNamespacedSecret(this.secretName, this.namespace)
32
+ .then(() => true)
33
+ .catch(error => {
34
+ if (error.status === 404) {
35
+ return false;
36
+ }
37
+ });
38
+ this.command.log(exists ? `Secret ${this.secretName} already exists` : `Secret ${this.secretName} does not exist`);
39
+ return Boolean(exists);
40
+ }
41
+ async deleteSecret() {
42
+ this.command.log(`Deleting existing secret ${this.secretName}`);
43
+ await this.coreV1Api.deleteNamespacedSecret(this.secretName, this.namespace).then(() => true);
44
+ this.command.log(`Existing secret ${this.secretName} deleted`);
45
+ }
46
+ async createSecret(secret) {
47
+ this.command.log(`Creating secret ${this.secretName}`);
48
+ await this.coreV1Api.createNamespacedSecret(this.namespace, secret.toKubeSecret(this.secretName));
49
+ this.command.log(`Created secret ${this.secretName}`);
50
+ }
51
+ }
52
+ exports.KubeApiService = KubeApiService;
53
+ _KubeApiService_instances = new WeakSet(), _KubeApiService_validate = function _KubeApiService_validate() {
54
+ if (this.namespace === Undefined) {
55
+ this.command.error('namespace is undefined', {
56
+ suggestions: [
57
+ 'set namespace with -n',
58
+ 'configure service account for this deployment/job',
59
+ ],
60
+ });
61
+ }
62
+ };
@@ -0,0 +1,9 @@
1
+ import { V1Secret } from '@kubernetes/client-node';
2
+ export declare class Secret {
3
+ #private;
4
+ private JWKs;
5
+ private CookieKeys;
6
+ constructor();
7
+ generateNew(): void;
8
+ toKubeSecret(secretName: string): V1Secret;
9
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var _Secret_instances, _Secret_generateRSAJwk, _Secret_generateCookieKey, _Secret_arrayToB64String, _Secret_getKubeSecretMetadata;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.Secret = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const jsrsasign_1 = require("jsrsasign");
7
+ const client_node_1 = require("@kubernetes/client-node");
8
+ const node_crypto_1 = tslib_1.__importDefault(require("node:crypto"));
9
+ const JWKSKeyName = 'OIDC_JWKS';
10
+ const CookieKeysKeyName = 'OIDC_COOKIE_KEYS';
11
+ class Secret {
12
+ constructor() {
13
+ _Secret_instances.add(this);
14
+ this.JWKs = [];
15
+ this.CookieKeys = [];
16
+ }
17
+ generateNew() {
18
+ this.JWKs = [tslib_1.__classPrivateFieldGet(this, _Secret_instances, "m", _Secret_generateRSAJwk).call(this, 4096)];
19
+ this.CookieKeys = [tslib_1.__classPrivateFieldGet(this, _Secret_instances, "m", _Secret_generateCookieKey).call(this, 32)];
20
+ }
21
+ toKubeSecret(secretName) {
22
+ const secret = new client_node_1.V1Secret();
23
+ secret.metadata = tslib_1.__classPrivateFieldGet(this, _Secret_instances, "m", _Secret_getKubeSecretMetadata).call(this, secretName);
24
+ secret.data = {};
25
+ secret.data[JWKSKeyName] = tslib_1.__classPrivateFieldGet(this, _Secret_instances, "m", _Secret_arrayToB64String).call(this, this.JWKs);
26
+ secret.data[CookieKeysKeyName] = tslib_1.__classPrivateFieldGet(this, _Secret_instances, "m", _Secret_arrayToB64String).call(this, this.CookieKeys);
27
+ return secret;
28
+ }
29
+ }
30
+ exports.Secret = Secret;
31
+ _Secret_instances = new WeakSet(), _Secret_generateRSAJwk = function _Secret_generateRSAJwk(len) {
32
+ const keypair = jsrsasign_1.KEYUTIL.generateKeypair('RSA', len);
33
+ const jwk = jsrsasign_1.KEYUTIL.getJWK(keypair.prvKeyObj);
34
+ return {
35
+ ...jwk,
36
+ use: 'sig',
37
+ };
38
+ }, _Secret_generateCookieKey = function _Secret_generateCookieKey(size) {
39
+ return node_crypto_1.default.randomBytes(size).toString('hex');
40
+ }, _Secret_arrayToB64String = function _Secret_arrayToB64String(array) {
41
+ const b = Buffer.from(JSON.stringify(array));
42
+ return b.toString('base64');
43
+ }, _Secret_getKubeSecretMetadata = function _Secret_getKubeSecretMetadata(secretName) {
44
+ const metaData = new client_node_1.V1ObjectMeta();
45
+ metaData.name = secretName;
46
+ return metaData;
47
+ };
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = void 0;
4
+ var core_1 = require("@oclif/core");
5
+ Object.defineProperty(exports, "run", { enumerable: true, get: function () { return core_1.run; } });
@@ -0,0 +1,78 @@
1
+ {
2
+ "version": "0.1.0",
3
+ "commands": {
4
+ "initialize": {
5
+ "id": "initialize",
6
+ "description": "Initialize the secret with initial keys",
7
+ "strict": true,
8
+ "pluginName": "@codemowers/oidc-key-manager",
9
+ "pluginAlias": "@codemowers/oidc-key-manager",
10
+ "pluginType": "core",
11
+ "aliases": [],
12
+ "examples": [
13
+ "<%= config.bin %> <%= command.id %>",
14
+ "<%= config.bin %> <%= command.id %>",
15
+ "<%= config.bin %> <%= command.id %> -n <kube namespace> -s <secret name>",
16
+ "<%= config.bin %> <%= command.id %> --namespace <kube namespace> --secret <secret name> --recreate"
17
+ ],
18
+ "flags": {
19
+ "json": {
20
+ "name": "json",
21
+ "type": "boolean",
22
+ "description": "Format output as json.",
23
+ "helpGroup": "GLOBAL",
24
+ "allowNo": false
25
+ },
26
+ "namespace": {
27
+ "name": "namespace",
28
+ "type": "option",
29
+ "char": "n",
30
+ "description": "namespace, defaults to current namespace if service account is used",
31
+ "required": false,
32
+ "multiple": false,
33
+ "aliases": [
34
+ "namespace"
35
+ ]
36
+ },
37
+ "secret": {
38
+ "name": "secret",
39
+ "type": "option",
40
+ "char": "s",
41
+ "description": "secret name",
42
+ "required": false,
43
+ "multiple": false,
44
+ "default": "oidc-keys",
45
+ "aliases": [
46
+ "secret"
47
+ ]
48
+ },
49
+ "config": {
50
+ "name": "config",
51
+ "type": "option",
52
+ "char": "c",
53
+ "description": "use local or in-cluster Kubernetes config",
54
+ "required": true,
55
+ "multiple": false,
56
+ "options": [
57
+ "local",
58
+ "cluster"
59
+ ],
60
+ "aliases": [
61
+ "config"
62
+ ]
63
+ },
64
+ "recreate": {
65
+ "name": "recreate",
66
+ "type": "boolean",
67
+ "description": "recreate the secret if it exists",
68
+ "required": false,
69
+ "allowNo": false,
70
+ "aliases": [
71
+ "recreate"
72
+ ]
73
+ }
74
+ },
75
+ "args": {}
76
+ }
77
+ }
78
+ }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@codemowers/oidc-key-manager",
3
+ "version": "0.1.0",
4
+ "description": "CLI to manage secret keys required by oidc-gateway",
5
+ "author": "Erki Aas",
6
+ "bin": {
7
+ "key-manager": "./bin/run"
8
+ },
9
+ "homepage": "https://github.com/codemowers/oidc-key-manager",
10
+ "license": "MIT",
11
+ "main": "dist/index.js",
12
+ "repository": "codemowers/oidc-key-manager",
13
+ "files": [
14
+ "/bin",
15
+ "/dist",
16
+ "/npm-shrinkwrap.json",
17
+ "/oclif.manifest.json"
18
+ ],
19
+ "dependencies": {
20
+ "@kubernetes/client-node": "^0.18.1",
21
+ "@oclif/core": "^2.8.4",
22
+ "@oclif/plugin-help": "^5",
23
+ "@oclif/plugin-plugins": "^2.4.7",
24
+ "@types/jsrsasign": "^10.5.8",
25
+ "jsrsasign": "^10.8.6"
26
+ },
27
+ "devDependencies": {
28
+ "@oclif/test": "^2.3.17",
29
+ "@types/chai": "^4",
30
+ "@types/mocha": "^9.0.0",
31
+ "@types/node": "^16.18.25",
32
+ "chai": "^4",
33
+ "eslint": "^7.32.0",
34
+ "eslint-config-oclif": "^4",
35
+ "eslint-config-oclif-typescript": "^1.0.3",
36
+ "mocha": "^9",
37
+ "oclif": "^3",
38
+ "shx": "^0.3.3",
39
+ "ts-node": "^10.9.1",
40
+ "tslib": "^2.5.0",
41
+ "typescript": "^4.9.5"
42
+ },
43
+ "oclif": {
44
+ "bin": "key-manager",
45
+ "dirname": "key-manager",
46
+ "commands": "./dist/commands",
47
+ "topicSeparator": " ",
48
+ "topics": {
49
+ "hello": {
50
+ "description": "Say hello to the world and others"
51
+ }
52
+ }
53
+ },
54
+ "scripts": {
55
+ "build": "shx rm -rf dist && tsc -b",
56
+ "lint": "eslint . --ext .ts --config .eslintrc",
57
+ "postpack": "shx rm -f oclif.manifest.json",
58
+ "posttest": "npm run lint",
59
+ "prepack": "npm run build && oclif manifest && oclif readme",
60
+ "test": "mocha --forbid-only \"test/**/*.test.ts\"",
61
+ "version": "oclif readme && git add README.md"
62
+ },
63
+ "engines": {
64
+ "node": ">=12.0.0"
65
+ },
66
+ "bugs": "https://github.com/codemowers/oidc-key-manager/issues",
67
+ "types": "dist/index.d.ts"
68
+ }