Revert "cloud: revert duckdns + letsencrypt"

This reverts commit 7d022548b9.
This commit is contained in:
Koushik Dutta
2023-08-15 21:29:30 -07:00
parent c1f1e96109
commit aff1e86d6f
4 changed files with 6131 additions and 1817 deletions

View File

@@ -2,3 +2,4 @@
out/
node_modules/
dist/
external

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,7 @@
"scrypted": {
"name": "Scrypted Cloud",
"type": "API",
"realfs": true,
"interfaces": [
"SystemSettings",
"BufferConverter",
@@ -36,6 +37,12 @@
"HttpRequestHandler"
]
},
"optionalDependencies": {
"@greenlock/manager": "^3.1.0",
"@koush/greenlock": "^4.0.9",
"acme-dns-01-duckdns": "^3.0.1",
"greenlock-store-fs": "^3.2.2"
},
"dependencies": {
"@eneris/push-receiver": "^3.1.4",
"@scrypted/common": "file:../../common",
@@ -50,5 +57,5 @@
"@types/nat-upnp": "^1.1.2",
"@types/node": "^20.4.5"
},
"version": "0.1.24"
"version": "0.1.27"
}

View File

@@ -18,6 +18,9 @@ import { PushManager } from './push';
import { readLine } from '../../../common/src/read-stream';
import { qsparse, qsstringify } from "./qs";
console.warn('webpack', require('@greenlock/manager'), require('greenlock-store-fs'), require('acme-dns-01-duckdns'));
const { deviceManager, endpointManager, systemManager } = sdk;
export const DEFAULT_SENDER_ID = '827888101440';
@@ -34,9 +37,9 @@ class ScryptedPush extends ScryptedDeviceBase implements BufferConverter {
}
async convert(data: Buffer | string, fromMimeType: string): Promise<Buffer> {
if (this.cloud.storageSettings.values.forwardingMode === 'Custom Domain' && this.cloud.storageSettings.values.hostname) {
return Buffer.from(`https://${this.cloud.getHostname()}${await this.cloud.getCloudMessagePath()}/${data}`);
}
const validDomain = this.cloud.getSSLHostname();
if (validDomain)
return Buffer.from(`https://${validDomain}${await this.cloud.getCloudMessagePath()}/${data}`);
const url = `http://127.0.0.1/push/${data}`;
return this.cloud.whitelist(url, 10 * 365 * 24 * 60 * 60 * 1000, `https://${this.cloud.getHostname()}${SCRYPTED_CLOUD_MESSAGE_PATH}`);
@@ -83,6 +86,26 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
placeholder: 'my-server.dyndns.com',
onPut: () => this.scheduleRefreshPortForward(),
},
duckDnsToken: {
title: 'Duck DNS Token',
placeholder: 'xxxxx123456',
onPut: () => {
this.storageSettings.values.duckDnsCertValid = false;
this.log.a('Reload the Scrypted Cloud Plugin to apply the Duck DNS change.');
}
},
duckDnsHostname: {
title: 'Duck DNS Hostname',
placeholder: 'my-scrypted.duckdns.org',
onPut: () => {
this.storageSettings.values.duckDnsCertValid = false;
this.log.a('Reload the Scrypted Cloud Plugin to apply the Duck DNS change.');
}
},
duckDnsCertValid: {
type: 'boolean',
hide: true,
},
securePort: {
title: 'Local HTTPS Port',
description: 'The Scrypted Cloud plugin listens on this port for for cloud connections. The router must use UPNP, port forwarding, or a reverse proxy to send requests to this port.',
@@ -182,6 +205,20 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
}
};
this.storageSettings.settings.duckDnsToken.onGet = async () => {
return {
hide: this.storageSettings.values.forwardingMode === 'Custom Domain'
|| this.storageSettings.values.forwardingMode === 'Disabled',
}
};
this.storageSettings.settings.duckDnsHostname.onGet = async () => {
return {
hide: this.storageSettings.values.forwardingMode === 'Custom Domain'
|| this.storageSettings.values.forwardingMode === 'Disabled',
}
};
this.log.clearAlerts();
this.storageSettings.settings.securePort.onPut = (ov, nv) => {
@@ -230,12 +267,96 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
this.storageSettings.values.upnpPort = upnpPort;
// scrypted cloud will replace localhost with requesting ip.
const ip = this.storageSettings.values.forwardingMode === 'Custom Domain'
? this.storageSettings.values.hostname?.toString()
: (await axios(`https://${SCRYPTED_SERVER}/_punch/ip`)).data.ip;
let ip: string;
if (this.storageSettings.values.forwardingMode === 'Custom Domain') {
ip = this.storageSettings.values.hostname?.toString();
if (!ip)
throw new Error('Hostname is required for port Custom Domain setup.');
}
else if (this.storageSettings.values.duckDnsHostname && this.storageSettings.values.duckDnsToken) {
try {
const url = new URL('https://www.duckdns.org/update');
url.searchParams.set('domains', this.storageSettings.values.duckDnsHostname);
url.searchParams.set('token', this.storageSettings.values.duckDnsToken);
await axios(url.toString());
}
catch (e) {
this.console.error('Duck DNS Erorr', e);
throw new Error('Duck DNS Error. See Console Logs.');
}
try {
const pluginVolume = process.env.SCRYPTED_PLUGIN_VOLUME;
const greenlockD = path.join(pluginVolume, 'greenlock.d');
if (!ip)
throw new Error('Hostname is required for port Custom Domain setup.');
// const dns01 = require('acme-dns-01-duckdns').create({
// baseUrl: 'https://www.duckdns.org/update',
// token: 'abcd',// this.storageSettings.values.duckDnsToken,
// });
// hack
// dns01.module = 'acme-dns-01-duckdns';
// dns01.token = this.storageSettings.values.duckDnsToken;
const Greenlock = require('@koush/greenlock');
const greenlock = Greenlock.create({
packageRoot: process.env.NODE_PATH,
configDir: greenlockD,
packageAgent: 'Scrypted/1.0',
maintainerEmail: 'koushd@gmail.com',
staging: true,
notify: function (event, details) {
if ('error' === event) {
// `details` is an error object in this case
console.error(details);
}
}
});
await greenlock.manager
.defaults({
challenges: {
'dns-01': {
module: 'acme-dns-01-duckdns',
token: this.storageSettings.values.duckDnsToken,
},
},
agreeToTerms: true,
subscriberEmail: 'koushd@gmail.com',
});
const altnames = [this.storageSettings.values.duckDnsHostname];
const r = await greenlock
.add({
subject: altnames[0],
altnames: altnames
});
const result = await greenlock
.get({ servername: this.storageSettings.values.duckDnsHostname });
this.storageSettings.values.duckDnsCertValid = true;
const { pems } = result;
const certificate = this.storageSettings.values.certificate;
if (certificate.certificate !== pems.cert || certificate.serviceKey !== pems.privkey) {
certificate.certificate = pems.cert;
certificate.serviceKey = pems.privkey;
this.storageSettings.values.certificate = certificate;
deviceManager.requestRestart();
}
this.console.log(r);
}
catch (e) {
this.console.error("Let's Encrypt Error", e);
throw new Error("Let's Encrypt Error. See Console Logs.");
}
ip = this.storageSettings.values.duckDnsHostname;
}
else {
ip = (await axios(`https://${SCRYPTED_SERVER}/_punch/ip`)).data.ip;
}
if (this.storageSettings.values.forwardingMode === 'Custom Domain')
upnpPort = 443;
@@ -344,7 +465,7 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
async whitelist(localUrl: string, ttl: number, baseUrl: string): Promise<Buffer> {
const local = new URL(localUrl);
if (this.storageSettings.values.forwardingMode === 'Custom Domain' && this.storageSettings.values.hostname) {
if (this.getSSLHostname()) {
return Buffer.from(`${baseUrl}${local.pathname}`);
}
@@ -394,7 +515,9 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
getAuthority() {
const upnp_port = this.storageSettings.values.forwardingMode === 'Custom Domain' ? 443 : this.storageSettings.values.upnpPort;
const hostname = this.storageSettings.values.forwardingMode === 'Custom Domain' ? this.storageSettings.values.hostname : undefined;
const hostname = this.storageSettings.values.forwardingMode === 'Custom Domain'
? this.storageSettings.values.hostname
: this.storageSettings.values.duckDnsToken && this.storageSettings.values.duckDnsHostname;
if (upnp_port === 443 && !hostname) {
const error = this.storageSettings.values.forwardingMode === 'Custom Domain'
@@ -480,10 +603,14 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
async releaseDevice(id: string, nativeId: string): Promise<void> {
}
getSSLHostname() {
const validDomain = (this.storageSettings.values.forwardingMode === 'Custom Domain' && this.storageSettings.values.hostname)
|| (this.storageSettings.values.duckDnsCertValid && this.storageSettings.values.duckDnsHostname && this.storageSettings.values.upnpPort && `${this.storageSettings.values.duckDnsHostname}:${this.storageSettings.values.upnpPort}`);
return validDomain;
}
getHostname() {
if (this.storageSettings.values.forwardingMode === 'Custom Domain' && this.storageSettings.values.hostname)
return this.storageSettings.values.hostname;
return SCRYPTED_SERVER;
return this.getSSLHostname() || SCRYPTED_SERVER;
}
async convert(data: Buffer, fromMimeType: string): Promise<Buffer> {
@@ -571,10 +698,13 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
}
}
else if (url.pathname === '/web/') {
if (this.storageSettings.values.forwardingMode === 'Custom Domain' && this.storageSettings.values.hostname)
res.setHeader('Location', `https://${this.storageSettings.values.hostname}/endpoint/@scrypted/core/public/`);
else
const validDomain = this.getSSLHostname();
if (validDomain) {
res.setHeader('Location', `https://${validDomain}/endpoint/@scrypted/core/public/`);
}
else {
res.setHeader('Location', '/endpoint/@scrypted/core/public/');
}
res.writeHead(302);
res.end();
return;