vvauth 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/.eslintignore +1 -0
- package/.eslintrc +6 -0
- package/.npmignore +4 -0
- package/README.md +6 -0
- package/index.js +161 -0
- package/package.json +33 -0
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
coverage
|
package/.eslintrc
ADDED
package/.npmignore
ADDED
package/README.md
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const url = require('url');
|
|
8
|
+
const net = require('net');
|
|
9
|
+
const {spawn} = require('child_process');
|
|
10
|
+
|
|
11
|
+
const {parse} = require('yaml');
|
|
12
|
+
const {args} = require('nyks/process/parseArgs')();
|
|
13
|
+
const SSHAgent = require('ssh-agent-js/client');
|
|
14
|
+
const trim = require('mout/string/trim');
|
|
15
|
+
const get = require('mout/object/get');
|
|
16
|
+
const eachLimit = require('nyks/async/eachLimit');
|
|
17
|
+
|
|
18
|
+
const request = require('nyks/http/request');
|
|
19
|
+
const drain = require('nyks/stream/drain');
|
|
20
|
+
|
|
21
|
+
const debug = require('debug');
|
|
22
|
+
|
|
23
|
+
const logger = {
|
|
24
|
+
debug : debug('dspp:secrets:debug'),
|
|
25
|
+
info : debug('dspp:secrets:info'),
|
|
26
|
+
error : debug('dspp:secrets:error'),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
const VCREDS_RC = ".vcredsrc";
|
|
31
|
+
|
|
32
|
+
class vcreds {
|
|
33
|
+
constructor(rc = null) {
|
|
34
|
+
this.rc = {};
|
|
35
|
+
if(rc) {
|
|
36
|
+
this.rc = rc;
|
|
37
|
+
} else {
|
|
38
|
+
if(fs.existsSync(VCREDS_RC)) {
|
|
39
|
+
let body = fs.readFileSync(VCREDS_RC, 'utf8');
|
|
40
|
+
this.rc = parse(body);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async _get_token() {
|
|
46
|
+
let {vault_addr, ssh_auth, jwt_auth} = this.rc;
|
|
47
|
+
|
|
48
|
+
let token = this.rc.VAULT_TOKEN;
|
|
49
|
+
if(!token && ssh_auth && process.env.SSH_AUTH_SOCK)
|
|
50
|
+
token = await this._login_vault_ssh({...ssh_auth, vault_addr});
|
|
51
|
+
|
|
52
|
+
if(!token && jwt_auth && jwt_auth.jwt) {
|
|
53
|
+
let {path, jwt, role} = jwt_auth, payload = {jwt, role};
|
|
54
|
+
token = await this._login_vault(vault_addr, path, payload);
|
|
55
|
+
}
|
|
56
|
+
return token;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async login(publish = true) {
|
|
60
|
+
let VAULT_TOKEN = await this._get_token();
|
|
61
|
+
if(publish) {
|
|
62
|
+
let env = {VAULT_TOKEN};
|
|
63
|
+
this._publish_env(env);
|
|
64
|
+
}
|
|
65
|
+
return VAULT_TOKEN;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_publish_env(env) {
|
|
69
|
+
let cmds = [];
|
|
70
|
+
for(let [k, v] of Object.entries(env)) {
|
|
71
|
+
cmds.push(`export ${k}=${v}`);
|
|
72
|
+
cmds.push(`echo export ${k}=[redacted] >&2`);
|
|
73
|
+
}
|
|
74
|
+
process.stdout.write(cmds.join("\n") + "\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async _login_vault_ssh({vault_addr, path = 'ssh', role}) {
|
|
78
|
+
let sock;
|
|
79
|
+
await new Promise(resolve => (sock = net.connect(process.env.SSH_AUTH_SOCK, resolve)));
|
|
80
|
+
let agent = new SSHAgent(sock);
|
|
81
|
+
let keys = Object.values(await agent.list_keys());
|
|
82
|
+
|
|
83
|
+
let token;
|
|
84
|
+
await eachLimit(keys, 1, async ({type, ssh_key, fingerprint, comment}) => {
|
|
85
|
+
if(token)
|
|
86
|
+
return;
|
|
87
|
+
|
|
88
|
+
let remote_url = `${trim(vault_addr, '/')}/v1/auth/${path}/nonce`;
|
|
89
|
+
let query = {...url.parse(remote_url), json : true};
|
|
90
|
+
let res = await request(query);
|
|
91
|
+
let {data : {nonce}} = JSON.parse(String(await drain(res)));
|
|
92
|
+
|
|
93
|
+
const public_key = `${type} ${ssh_key}`;
|
|
94
|
+
const {signature} = await agent.sign(fingerprint, Buffer.from(nonce));
|
|
95
|
+
|
|
96
|
+
const payload = {public_key, role, nonce : Buffer.from(nonce).toString('base64'), signature};
|
|
97
|
+
try {
|
|
98
|
+
token = await this._login_vault(vault_addr, path, payload);
|
|
99
|
+
} catch(err) {
|
|
100
|
+
logger.debug("ssh : invalid challenge for public key", comment);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
sock.destroy();
|
|
105
|
+
|
|
106
|
+
if(!token)
|
|
107
|
+
throw `Could not login to vault`;
|
|
108
|
+
|
|
109
|
+
return token;
|
|
110
|
+
}
|
|
111
|
+
async _alias_exists(alias) {
|
|
112
|
+
let child = spawn('bash', ["-lc", `alias ${alias}`]);
|
|
113
|
+
return new Promise(resolve => child.on('exit', resolve));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async install() {
|
|
117
|
+
const alias_name = "vauth";
|
|
118
|
+
const alias_value = "source <(vcreds login)";
|
|
119
|
+
const bashrc_path = path.resolve(os.homedir(), ".bashrc");
|
|
120
|
+
let bashrc = fs.existsSync(bashrc_path) ? fs.readFileSync(bashrc_path, 'utf-8').trim() : '';
|
|
121
|
+
let exists = await this._alias_exists(alias_name);
|
|
122
|
+
if(exists == 0) {
|
|
123
|
+
console.error("Alias %s already installed", alias_name);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
console.error("Alias %s not installed, pushing it to %s", alias_name, bashrc_path);
|
|
127
|
+
|
|
128
|
+
fs.writeFileSync(bashrc_path, [bashrc, `alias ${alias_name}="${alias_value}"`].join("\n"));
|
|
129
|
+
console.error(`Installation ok, please \nsource ${bashrc_path}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async _login_vault(vault_addr, path, payload) {
|
|
133
|
+
console.error("Connecting to %s", vault_addr);
|
|
134
|
+
let remote_url = `${trim(vault_addr, '/')}/v1/auth/${path}/login`;
|
|
135
|
+
let query = {...url.parse(remote_url), json : true};
|
|
136
|
+
let res = await request(query, payload);
|
|
137
|
+
let response = String(await drain(res));
|
|
138
|
+
|
|
139
|
+
if(res.statusCode !== 200)
|
|
140
|
+
throw `Could not login to vault : ${response}`;
|
|
141
|
+
|
|
142
|
+
response = JSON.parse(response);
|
|
143
|
+
//console.log(response);
|
|
144
|
+
let token = get(response, 'auth.client_token');
|
|
145
|
+
return token;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//ensure module is called directly, i.e. not required
|
|
154
|
+
if(module.parent === null) {
|
|
155
|
+
let cmd = args.shift();
|
|
156
|
+
let run = cmd ? [`--ir://raw`, `--ir://run=${cmd}`] : [];
|
|
157
|
+
require('cnyks/lib/bundle')(vcreds, null, run);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
module.exports = vcreds;
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vvauth",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vault Creds manager",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vvauth": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"eslint": "eslint ."
|
|
11
|
+
},
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"cnyks": "^3.0.6",
|
|
16
|
+
"debug": "^4.3.4",
|
|
17
|
+
"mout": "^1.0.0",
|
|
18
|
+
"nyks": "^6.9.1",
|
|
19
|
+
"ssh-agent-js": "^2.0.4",
|
|
20
|
+
"yaml": "^2.6.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"eslint": "^8.57.0",
|
|
24
|
+
"eslint-plugin-ivs": "^4.0.1",
|
|
25
|
+
"expect.js": "^0.3.1",
|
|
26
|
+
"mocha": "^3.1.2",
|
|
27
|
+
"nyc": "^15.0.1"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git@github.com:131/vcreds.git"
|
|
32
|
+
}
|
|
33
|
+
}
|