updates
This commit is contained in:
parent
d6f517ed2b
commit
0013930dbd
|
@ -13,3 +13,7 @@ greenlock:
|
||||||
strategy: le-store-certbot # certificate storage plugin
|
strategy: le-store-certbot # certificate storage plugin
|
||||||
config_dir: /etc/acme # directory for ssl certificates
|
config_dir: /etc/acme # directory for ssl certificates
|
||||||
secret: '' # generate with node -e "console.log(crypto.randomBytes(16).toString('hex'))"
|
secret: '' # generate with node -e "console.log(crypto.randomBytes(16).toString('hex'))"
|
||||||
|
mailer:
|
||||||
|
url: 'https://api.mailgun.net/v3/EXAMPLE.COM/messages'
|
||||||
|
api_key: 'key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||||
|
from: 'Example Mailer <MALIER@EXAMPLE.COM>'
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!--meta http-equiv="refresh" content="5;url=https://' + tokenData.domains.join(',') + '/?serviceport=' + tokenData.ports.join(',')" /-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>document.body.hidden = true;</script>
|
||||||
|
|
||||||
|
<div class="js-error" hidden>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="js-magic" hidden>
|
||||||
|
<h1>Give us about 30 seconds...</h1>
|
||||||
|
We're initializing our connection, redirecting you to your device at
|
||||||
|
<a class="js-new-href">{{js-new-href}}</a>
|
||||||
|
which will then take a few seconds to initialize as it gets your https certificates for peer-to-peer, end-to-end encryption
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<small><pre><code class="js-token-data">{{js-token-data}}</code></pre></small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
res.send("<h1>Invalid Magic Link</h1>"
|
||||||
|
+ "<pre><code>'" + magic + "' isn't a valid link.\nLinks are only good for 5 minutes, so act fast.\n"
|
||||||
|
+ "(" + new Date(1000*((_auths[magic]||{}).dt||0)).toISOString() + ")</code></pre>\n"
|
||||||
|
);
|
|
@ -0,0 +1,23 @@
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var magic = (window.location.hash || '').substr(2).replace(/magic=/, '');
|
||||||
|
|
||||||
|
if (magic) {
|
||||||
|
window.fetch('https://api.' + location.hostname + '/api/telebit.cloud/magic/' + magic, {
|
||||||
|
method: 'GET'
|
||||||
|
, cors: true
|
||||||
|
}).then(function (resp) {
|
||||||
|
return resp.json().then(function (json) {
|
||||||
|
document.querySelector('body').hidden = false;
|
||||||
|
document.querySelector('js-magic').hidden = false;
|
||||||
|
document.querySelector('js-token-data').innerText = JSON.stringify(json, null, 2);
|
||||||
|
document.querySelector('js-new-href').href = json.domains[0];
|
||||||
|
document.querySelector('js-new-href').innerText = json.domains[0];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
document.querySelector('body').hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}());
|
|
@ -1,3 +1,4 @@
|
||||||
|
'use strict';
|
||||||
/*
|
/*
|
||||||
curl -s --user 'api:YOUR_API_KEY' \
|
curl -s --user 'api:YOUR_API_KEY' \
|
||||||
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
|
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
|
||||||
|
@ -8,6 +9,7 @@ curl -s --user 'api:YOUR_API_KEY' \
|
||||||
-F text='Testing some Mailgun awesomeness!'
|
-F text='Testing some Mailgun awesomeness!'
|
||||||
*/
|
*/
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
var escapeHtml = require('escape-html');
|
||||||
var _auths = module.exports._auths = {};
|
var _auths = module.exports._auths = {};
|
||||||
module.exports.authenticate = function (opts) {
|
module.exports.authenticate = function (opts) {
|
||||||
console.log("It's auth'n time!");
|
console.log("It's auth'n time!");
|
||||||
|
@ -16,41 +18,48 @@ module.exports.authenticate = function (opts) {
|
||||||
var state = opts.state;
|
var state = opts.state;
|
||||||
var jwtoken = opts.auth;
|
var jwtoken = opts.auth;
|
||||||
var auth;
|
var auth;
|
||||||
var mailer = {
|
|
||||||
user: 'wizard@telebit.cloud'
|
|
||||||
, secret: 'fbbf21d73c9d2f480bd0e71f5f18494e'
|
|
||||||
};
|
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
|
||||||
console.log('[DEBUG] ext auth', jwtoken);
|
console.log('[DEBUG] ext auth', jwtoken);
|
||||||
auth = jwtoken;
|
auth = jwtoken;
|
||||||
if ('object' === typeof auth && /^.+@.+\..+$/.test(auth.subject)) {
|
if ('object' === typeof auth && /^.+@.+\..+$/.test(auth.subject)) {
|
||||||
console.log('parsed');
|
|
||||||
var id = crypto.randomBytes(12).toString('hex');
|
|
||||||
//var id = crypto.randomBytes(16).toString('base64').replace(/\+/g,'-').replace(/\//g,'_').replace(/=/g,'');
|
|
||||||
console.log("[DEBUG] gonna send email");
|
console.log("[DEBUG] gonna send email");
|
||||||
|
auth.id = crypto.randomBytes(12).toString('hex');
|
||||||
|
//var id = crypto.randomBytes(16).toString('base64').replace(/\+/g,'-').replace(/\//g,'_').replace(/=/g,'');
|
||||||
|
var subj = 'Confirm New Device Connection';
|
||||||
|
var text = "You tried connecting with '{{hostname}}' for the first time. Confirm to continue connecting:\n"
|
||||||
|
+ '\n'
|
||||||
|
+ ' https://' + state.config.webminDomain + '/login/#/magic={{id}}\n'
|
||||||
|
+ '\n'
|
||||||
|
+ "({{os_arch}} {{os_platform}} {{os_release}})\n"
|
||||||
|
+ '\n'
|
||||||
|
;
|
||||||
|
var html = "You tried connecting with '{{hostname}}' for the first time. Confirm to continue connecting:<br>"
|
||||||
|
+ '<br>'
|
||||||
|
+ ' <a href="https://' + state.config.webminDomain + '/login/#/magic={{id}}">Confirm Device</a><br>'
|
||||||
|
+ '<br>'
|
||||||
|
+ ' <small>or copy and paste this link:</small><br>'
|
||||||
|
+ ' <small>https://' + state.config.webminDomain + '/login/#/magic={{id}}</small><br>'
|
||||||
|
+ '<br>'
|
||||||
|
+ "({{os_arch}} {{os_platform}} {{os_release}})<br>"
|
||||||
|
+ '<br>'
|
||||||
|
;
|
||||||
|
[ 'id', 'hostname', 'os_arch', 'os_platform', 'os_release' ].forEach(function (key) {
|
||||||
|
var val = escapeHtml(auth[key]);
|
||||||
|
subj = subj.replace(new RegExp('{{' + key + '}}', 'g'), val);
|
||||||
|
text = text.replace(new RegExp('{{' + key + '}}', 'g'), val);
|
||||||
|
html = html.replace(new RegExp('{{' + key + '}}', 'g'), val);
|
||||||
|
});
|
||||||
return requestAsync({
|
return requestAsync({
|
||||||
url: 'https://api.mailgun.net/v3/telebit.cloud/messages'
|
url: state.config.mailer.url
|
||||||
, method: 'POST'
|
, method: 'POST'
|
||||||
, auth: { user: 'api', pass: 'key-70ef48178081df19783ecfbe6fed5e9a' }
|
, auth: { user: 'api', pass: state.config.mailer.apiKey }
|
||||||
, formData: {
|
, formData: {
|
||||||
from: 'Telebit Wizard <wizard@telebit.cloud>'
|
from: state.config.mailer.from
|
||||||
, to: auth.subject
|
, to: auth.subject
|
||||||
, subject: 'Confirm New Device Connection'
|
, subject: subj
|
||||||
, text: "You tried connecting with '" + auth.hostname + "' for the first time. Confirm to continue connecting:\n"
|
, text: text
|
||||||
+ '\n'
|
, html: html
|
||||||
+ ' https://www.telebit.cloud/login/?magic=' + id + '\n'
|
|
||||||
+ '\n'
|
|
||||||
+ "(" + auth.os_arch + " " + auth.os_platform + " " + auth.os_release + ")\n"
|
|
||||||
+ '\n'
|
|
||||||
, html: "You tried connecting with '" + auth.hostname + "' for the first time. Confirm to continue connecting:<br>"
|
|
||||||
+ '<br>'
|
|
||||||
+ ' <a href="https://www.telebit.cloud/login/?magic=' + id + '">Confirm Device</a><br>'
|
|
||||||
+ '<br>'
|
|
||||||
+ ' <small>or copy and paste this link:<br>https://www.telebit.cloud/login/?magic=' + id + '</small><br>'
|
|
||||||
+ '<br>'
|
|
||||||
+ "(" + auth.os_arch + " " + auth.os_platform + " " + auth.os_release + ")<br>"
|
|
||||||
+ '<br>'
|
|
||||||
}
|
}
|
||||||
}).then(function (resp) {
|
}).then(function (resp) {
|
||||||
console.log("[DEBUG] email was sent, or so they say");
|
console.log("[DEBUG] email was sent, or so they say");
|
||||||
|
@ -76,12 +85,12 @@ module.exports.authenticate = function (opts) {
|
||||||
clearTimeout(t);
|
clearTimeout(t);
|
||||||
delete _auths[id];
|
delete _auths[id];
|
||||||
var hri = require('human-readable-ids').hri;
|
var hri = require('human-readable-ids').hri;
|
||||||
var hrname = hri.random() + '.telebit.cloud';
|
var hrname = hri.random() + '.' + state.config.sharedDomain;
|
||||||
var jwt = require('jsonwebtoken');
|
var jwt = require('jsonwebtoken');
|
||||||
var tokenData = {
|
var tokenData = {
|
||||||
domains: [ hrname ]
|
domains: [ hrname ]
|
||||||
, ports: [ 1024 + Math.round(Math.random() * 6300) ]
|
, ports: [ 1024 + Math.round(Math.random() * 6300) ]
|
||||||
, aud: 'telebit.cloud'
|
, aud: state.config.webminDomain
|
||||||
, iss: Math.round(Date.now() / 1000)
|
, iss: Math.round(Date.now() / 1000)
|
||||||
, id: id
|
, id: id
|
||||||
, hostname: auth.hostname
|
, hostname: auth.hostname
|
||||||
|
@ -108,6 +117,7 @@ module.exports.authenticate = function (opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("just trying a normal token...");
|
console.log("just trying a normal token...");
|
||||||
|
var decoded;
|
||||||
try {
|
try {
|
||||||
decoded = jwt.decode(jwtoken, { complete: true });
|
decoded = jwt.decode(jwtoken, { complete: true });
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
@ -120,35 +130,45 @@ module.exports.authenticate = function (opts) {
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var app = express();
|
var app = express();
|
||||||
app.use('/', express.static(path.join(__dirname, 'extensions/admin')));
|
var staticApp = express();
|
||||||
app.use('/login', function (req, res) {
|
var nowww = require('nowww')();
|
||||||
|
var CORS = require('connect-cors');
|
||||||
|
staticApp.use('/', express.static(path.join(__dirname, 'admin')));
|
||||||
|
app.use('/api', CORS({}));
|
||||||
|
app.get('/api/telebit.cloud/magic/:magic', function (req, res) {
|
||||||
|
console.log("DEBUG telebit.cloud magic");
|
||||||
var tokenData;
|
var tokenData;
|
||||||
var magic = req.query.magic;
|
var magic = req.params.magic || req.query.magic;
|
||||||
|
console.log("DEBUG telebit.cloud magic 1a");
|
||||||
if (_auths[magic]) {
|
if (_auths[magic]) {
|
||||||
|
console.log("DEBUG telebit.cloud magic 1b");
|
||||||
tokenData = _auths[magic].resolve();
|
tokenData = _auths[magic].resolve();
|
||||||
res.send('<!DOCTYPE html><html>'
|
console.log("DEBUG telebit.cloud magic 1c");
|
||||||
+ '<head><meta http-equiv="refresh" content="5;url='
|
res.send(tokenData);
|
||||||
+ 'https://' + tokenData.domains.join(',') + '/?serviceport=' + tokenData.ports.join(',')
|
|
||||||
+ '" /></head>'
|
|
||||||
+ '<body>'
|
|
||||||
+ '<h1>Give us about 30 seconds...</h1>'
|
|
||||||
+ 'We\'re initializing our connection, redirecting you to your device at <a href="https://' + tokenData.domains[0] + '">'
|
|
||||||
+ tokenData.domains[0]
|
|
||||||
+ '</a>, which will then take a few seconds to initialize as it gets your https certificates for peer-to-peer, end-to-end encryption'
|
|
||||||
+ '<br>'
|
|
||||||
+ '<br>'
|
|
||||||
+ '<small><pre><code>' + JSON.stringify(tokenData, null, 2) + '</code></pre></small>'
|
|
||||||
+ '</body></html>'
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
res.send("<h1>Invalid Magic Link</h1>"
|
console.log("DEBUG telebit.cloud magic 2");
|
||||||
+ "<pre><code>'" + magic + "' isn't a valid link.\nLinks are only good for 5 minutes, so act fast.\n"
|
res.send({ error: { code: "E_TOKEN", message: "Invalid or expired magic link." } });
|
||||||
+ "(" + new Date(1000*((_auths[magic]||{}).dt||0)).toISOString() + ")</code></pre>\n"
|
console.log("DEBUG telebit.cloud magic 2b");
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
module.exports.webadmin = function (state, req, res) {
|
module.exports.webadmin = function (state, req, res) {
|
||||||
//if (!loaded) { loaded = true; app.use('/', state.defaults.webadmin); }
|
//if (!loaded) { loaded = true; app.use('/', state.defaults.webadmin); }
|
||||||
console.log('[DEBUG] extensions webadmin');
|
console.log('[DEBUG] extensions webadmin');
|
||||||
|
var host = (req.headers.host || '').toLowerCase().split(':')[0];
|
||||||
|
if (state.config.webminDomain === host) {
|
||||||
|
console.log("DEBUG going to static");
|
||||||
|
staticApp(req, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ('api.' + state.config.webminDomain === host) {
|
||||||
|
console.log("DEBUG going to api");
|
||||||
app(req, res);
|
app(req, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ('www.' + state.config.webminDomain === host) {
|
||||||
|
console.log("DEBUG going to www");
|
||||||
|
nowww(req, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.end("Didn't recognize '" + escapeHtml(host) + "'. Not sure what to do.");
|
||||||
};
|
};
|
|
@ -39,12 +39,15 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bluebird": "^3.5.1",
|
"bluebird": "^3.5.1",
|
||||||
"cluster-store": "^2.0.8",
|
"cluster-store": "^2.0.8",
|
||||||
|
"connect-cors": "^0.5.6",
|
||||||
|
"escape-html": "^1.0.3",
|
||||||
"express": "^4.16.3",
|
"express": "^4.16.3",
|
||||||
"finalhandler": "^1.1.1",
|
"finalhandler": "^1.1.1",
|
||||||
"greenlock": "^2.2.4",
|
"greenlock": "^2.2.4",
|
||||||
"human-readable-ids": "^1.0.4",
|
"human-readable-ids": "^1.0.4",
|
||||||
"js-yaml": "^3.11.0",
|
"js-yaml": "^3.11.0",
|
||||||
"jsonwebtoken": "^8.2.1",
|
"jsonwebtoken": "^8.2.1",
|
||||||
|
"nowww": "^1.2.1",
|
||||||
"proxy-packer": "^1.4.3",
|
"proxy-packer": "^1.4.3",
|
||||||
"recase": "^1.0.4",
|
"recase": "^1.0.4",
|
||||||
"redirect-https": "^1.1.5",
|
"redirect-https": "^1.1.5",
|
||||||
|
|
Loading…
Reference in New Issue