1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
#!/usr/bin/env ucode
'use strict';
import { stdin, access, chmod, dirname, basename, open, popen, glob, lsdir, readfile, readlink, error, mkdir } from 'fs';
import { cursor } from 'uci';
import { connect } from 'ubus';
const openvpn_dir = '/etc/openvpn';
function shellquote(s) {
return `'${replace(s, "'", "'\\''")}'`;
}
function command(cmd) {
return trim(popen(cmd)?.read?.('all'));
}
function makedirs(path) {
const parts = split(path, '/');
const grow = [];
for(let i = 0; i < length(parts); i++) {
push(grow, parts[i]);
mkdir(join('/', grow));
}
return join('/', grow);
}
function keyDir(ifname, kt) {
return `${openvpn_dir}/${ifname}/${kt}`;
}
const methods = {
generateKey: {
args: { keytype: 'keytype', ifname: 'ifname', server_key: 'server_key', cl_meta: 'c_metadata' },
call: function(req) {
const kt = req.args?.keytype;
const ifname = req.args?.ifname || 'unnamed';
const ts = time();
if (!kt) return { error: 'missing keytype' };
let dir;
let outfile = `${ifname}_${kt}_${ts}.key`;
let mkpath = makedirs(`${keyDir(ifname, kt)}`);
let path = `${mkpath}/${outfile}`;
// find openvpn binary
const openvpn = trim(popen('command -v openvpn 2>/dev/null')?.read?.('all'));
if (!length(openvpn)) return { error: 'openvpn binary not found' };
let cmd;
if (kt == 'tls-crypt-v2-client') {
// client generation needs server key
let server_key = req.args?.server_key;
if (!server_key) {
// try to pick latest server key for same interface
const serverDir = `${keyDir(ifname, 'tls-crypt-v2-server')}`;
const list = lsdir(serverDir);
if (length(list) > 0) server_key = serverDir + '/' + list[-1];
} else {
server_key = `${keyDir(ifname, 'tls-crypt-v2-server')}/${req.args?.server_key}`;
}
if (!server_key) return { error: 'missing server_key for tls-crypt-v2-client' };
// denote which server key this client key is derived from in the name
path = `${mkpath}/${ifname}_${kt}_${ts}-${req.args?.server_key}`;
cmd = `${openvpn} --tls-crypt-v2 ${shellquote(server_key)} --genkey tls-crypt-v2-client ${shellquote(path)} ${shellquote(req.args?.cl_meta)} 2>/dev/null`;
} else {
// basic genkey
cmd = `${openvpn} --genkey ${kt} ${shellquote(path)} 2>/dev/null`;
}
const out = popen(cmd)?.read?.('all') || '';
// ensure permissions
chmod(path, 0o600);
let content = '';
try { content = readfile(path); } catch (e) { /* ignore */ }
if (!length(content)) {
return { error: 'failed to generate key', cmd_output: out };
}
return { path, filename: outfile, content };
}
},
getSKeys: {
args: { ifname: 'ifname' },
call: function(req) {
const serverDir = `${keyDir(req.args?.ifname, 'tls-crypt-v2-server')}`;
const list = lsdir(serverDir);
return { skeys: list, path: serverDir };
}
}
};
return { 'luci.openvpn': methods };
|