From 7d3cbff79475c7e32818e7c77a208c94c165a0dd Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Tue, 29 Mar 2022 02:24:10 +0800 Subject: [PATCH 1/6] [Cloudflared] Install into base docker --- docker/debian-base.dockerfile | 14 +++++++++++ extra/download-cloudflared.js | 44 +++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 59 insertions(+) create mode 100644 extra/download-cloudflared.js diff --git a/docker/debian-base.dockerfile b/docker/debian-base.dockerfile index 9a8c759b..62889dc9 100644 --- a/docker/debian-base.dockerfile +++ b/docker/debian-base.dockerfile @@ -1,8 +1,11 @@ # DON'T UPDATE TO node:14-bullseye-slim, see #372. # If the image changed, the second stage image should be changed too FROM node:16-buster-slim +ARG TARGETPLATFORM + WORKDIR /app +# Install Curl # Install Apprise, add sqlite3 cli for debugging in the future, iputils-ping for ping, util-linux for setpriv # Stupid python3 and python3-pip actually install a lot of useless things into Debian, specify --no-install-recommends to skip them, make the base even smaller than alpine! RUN apt update && \ @@ -10,3 +13,14 @@ RUN apt update && \ sqlite3 iputils-ping util-linux dumb-init && \ pip3 --no-cache-dir install apprise==0.9.7 && \ rm -rf /var/lib/apt/lists/* + +# Install cloudflared +# dpkg --add-architecture arm: cloudflared do not provide armhf, this is workaround. Read more: https://github.com/cloudflare/cloudflared/issues/583 +COPY extra/download-cloudflared.js ./extra/download-cloudflared.js +RUN node ./extra/download-cloudflared.js $TARGETPLATFORM && \ + dpkg --add-architecture arm && \ + apt update && \ + apt --yes --no-install-recommends install ./cloudflared.deb && \ + rm -rf /var/lib/apt/lists/* && \ + rm -f cloudflared.deb + diff --git a/extra/download-cloudflared.js b/extra/download-cloudflared.js new file mode 100644 index 00000000..41519b7c --- /dev/null +++ b/extra/download-cloudflared.js @@ -0,0 +1,44 @@ +// + +const http = require("https"); // or 'https' for https:// URLs +const fs = require("fs"); + +const platform = process.argv[2]; + +if (!platform) { + console.error("No platform??"); + process.exit(1); +} + +let arch = null; + +if (platform === "linux/amd64") { + arch = "amd64"; +} else if (platform === "linux/arm64") { + arch = "arm64"; +} else if (platform === "linux/arm/v7") { + arch = "arm"; +} else { + console.error("Invalid platform?? " + platform); +} + +const file = fs.createWriteStream("cloudflared.deb"); +get("https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-" + arch + ".deb"); + +function get(url) { + http.get(url, function (res) { + if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { + console.log("Redirect to " + res.headers.location); + get(res.headers.location); + } else if (res.statusCode >= 200 && res.statusCode < 300) { + res.pipe(file); + + res.on("end", function () { + console.log("Downloaded"); + }); + } else { + console.error(res.statusCode); + process.exit(1); + } + }); +} diff --git a/package.json b/package.json index 134271c0..24558dc5 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", "limiter": "^2.1.0", + "node-cloudflared-tunnel": "~1.0.0", "nodemailer": "~6.6.5", "notp": "~2.0.3", "password-hash": "~1.2.2", From 44fb2a88f290acec2ba750d678a6a45cee81d394 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Tue, 29 Mar 2022 14:48:02 +0800 Subject: [PATCH 2/6] Add cloudflared socket handler --- server/server.js | 2 ++ .../cloudflared-socket-handler.js | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 server/socket-handlers/cloudflared-socket-handler.js diff --git a/server/server.js b/server/server.js index 9a5e1028..602b5a86 100644 --- a/server/server.js +++ b/server/server.js @@ -133,6 +133,7 @@ const { statusPageSocketHandler } = require("./socket-handlers/status-page-socke const databaseSocketHandler = require("./socket-handlers/database-socket-handler"); const TwoFA = require("./2fa"); const StatusPage = require("./model/status_page"); +const { cloudflaredSocketHandler } = require("./socket-handlers/cloudflared-socket-handler"); app.use(express.json()); @@ -1319,6 +1320,7 @@ exports.entryPage = "dashboard"; // Status Page Socket Handler for admin only statusPageSocketHandler(socket); + cloudflaredSocketHandler(socket); databaseSocketHandler(socket); debug("added all socket handlers"); diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js new file mode 100644 index 00000000..95dd4d80 --- /dev/null +++ b/server/socket-handlers/cloudflared-socket-handler.js @@ -0,0 +1,19 @@ +const { checkLogin } = require("../util-server"); + +const prefix = "cloudflared_"; + +module.exports.cloudflaredSocketHandler = (socket) => { + + socket.on(prefix + "start", async (callback) => { + try { + checkLogin(socket); + + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + +}; From f1f4b3b377d6c00a720fee93bf4be8ed3ac675e6 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Wed, 30 Mar 2022 01:49:45 +0800 Subject: [PATCH 3/6] Add reverse proxy setting page for controlling cloudflared --- package.json | 2 +- .../cloudflared-socket-handler.js | 69 +++++++++-- src/components/settings/ReverseProxy.vue | 111 ++++++++++++++++++ src/mixins/socket.js | 13 ++ src/pages/Settings.vue | 3 + src/router.js | 5 + 6 files changed, 194 insertions(+), 9 deletions(-) create mode 100644 src/components/settings/ReverseProxy.vue diff --git a/package.json b/package.json index 24558dc5..bb7b1e57 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", "limiter": "^2.1.0", - "node-cloudflared-tunnel": "~1.0.0", + "node-cloudflared-tunnel": "~1.0.6", "nodemailer": "~6.6.5", "notp": "~2.0.3", "password-hash": "~1.2.2", diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js index 95dd4d80..f7c69ed2 100644 --- a/server/socket-handlers/cloudflared-socket-handler.js +++ b/server/socket-handlers/cloudflared-socket-handler.js @@ -1,19 +1,72 @@ -const { checkLogin } = require("../util-server"); +const { checkLogin, setSetting, setting } = require("../util-server"); +const { CloudflaredTunnel } = require("node-cloudflared-tunnel"); +const { io } = require("../server"); const prefix = "cloudflared_"; +const cloudflared = new CloudflaredTunnel(); + +let isRunning; + +cloudflared.change = (running, message) => { + io.to("cloudflared").emit(prefix + "running", running); + io.to("cloudflared").emit(prefix + "message", message); + isRunning = running; + +}; + +cloudflared.error = (errorMessage) => { + io.to("cloudflared").emit(prefix + "errorMessage", errorMessage); +}; module.exports.cloudflaredSocketHandler = (socket) => { - socket.on(prefix + "start", async (callback) => { + socket.on(prefix + "join", async () => { try { checkLogin(socket); + socket.join("cloudflared"); + io.to(socket.userID).emit(prefix + "installed", cloudflared.checkInstalled()); + io.to(socket.userID).emit(prefix + "running", isRunning); + io.to(socket.userID).emit(prefix + "token", await setting("cloudflaredTunnelToken")); + } catch (error) { } + }); - } catch (error) { - callback({ - ok: false, - msg: error.message, - }); - } + socket.on(prefix + "leave", async () => { + try { + checkLogin(socket); + socket.leave("cloudflared"); + } catch (error) { } + }); + + socket.on(prefix + "start", async (token) => { + try { + checkLogin(socket); + if (token && typeof token === "string") { + token = token.trim(); + + // try to strip out "cloudflared.exe service install" + let array = token.split(" "); + if (array.length > 1) { + for (let i = 0; i < array.length - 1; i++) { + if (array[i] === "install") { + token = array[i + 1]; + } + } + } + + await setSetting("cloudflaredTunnelToken", token); + cloudflared.token = token; + } else { + cloudflared.token = null; + } + cloudflared.start(); + } catch (error) { } + }); + + socket.on(prefix + "stop", async () => { + try { + checkLogin(socket); + cloudflared.stop(); + } catch (error) { } }); }; diff --git a/src/components/settings/ReverseProxy.vue b/src/components/settings/ReverseProxy.vue new file mode 100644 index 00000000..fe41644b --- /dev/null +++ b/src/components/settings/ReverseProxy.vue @@ -0,0 +1,111 @@ +<template> + <div> + <h4 class="mt-4">Cloudflare Tunnel</h4> + + <div class="my-3"> + <div> + cloudflared: + <span v-if="installed === true" class="text-primary">{{ $t("Installed") }}</span> + <span v-else-if="installed === false" class="text-danger">{{ $t("Not installed") }}</span> + </div> + + <div> + {{ $t("Status") }}: + <span v-if="running" class="text-primary">{{ $t("Running") }}</span> + <span v-else-if="!running" class="text-danger">{{ $t("Not running") }}</span> + </div> + + <div v-if="false"> + {{ message }} + </div> + + <div class="mt-3"> + Message: + <textarea v-model="errorMessage" class="form-control" readonly></textarea> + </div> + + <p v-if="installed === false">(Download cloudflared from <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/">Cloudflare Website</a>)</p> + </div> + + <!-- If installed show token input --> + <div v-if="installed" class="mb-2"> + <div class="mb-4"> + <label class="form-label" for="cloudflareTunnelToken"> + Cloudflare Tunnel {{ $t("Token") }} + </label> + <HiddenInput + id="cloudflareTunnelToken" + v-model="cloudflareTunnelToken" + autocomplete="one-time-code" + /> + <div class="form-text"> + Don't know how to get the token? Please read the guide:<br /> + <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel" target="_blank"> + https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel + </a> + </div> + </div> + + <!-- Save Button --> + <div> + <button v-if="!running" class="btn btn-primary" type="submit" @click="start"> + {{ $t("Start") }} cloudflared + </button> + + <button v-if="running" class="btn btn-danger" type="submit" @click="stop"> + {{ $t("Stop") }} cloudflared + </button> + </div> + </div> + + <h4 class="mt-4">Other Software</h4> + <div> + For example: nginx, Apache and Traefik. <br /> + Please read <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy</a>. + </div> + </div> +</template> + +<script> +import HiddenInput from "../../components/HiddenInput.vue"; + +const prefix = "cloudflared_"; + +export default { + components: { + HiddenInput, + }, + data() { + return this.$root.cloudflared; + }, + computed: { + + }, + watch: { + + }, + mounted() { + this.$root.getSocket().emit(prefix + "join"); + }, + unmounted() { + this.$root.getSocket().emit(prefix + "leave"); + }, + methods: { + start() { + this.$root.getSocket().emit(prefix + "start", this.cloudflareTunnelToken); + }, + stop() { + this.$root.getSocket().emit(prefix + "stop"); + }, + } +}; +</script> + +<style lang="scss" scoped> +.logo { + margin: 4em 1em; +} +.update-link { + font-size: 0.9em; +} +</style> diff --git a/src/mixins/socket.js b/src/mixins/socket.js index 7d1bbea5..f2c4ff6d 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -42,6 +42,13 @@ export default { statusPageList: [], connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...", showReverseProxyGuide: true, + cloudflared: { + cloudflareTunnelToken: "", + installed: null, + running: false, + message: "", + errorMessage: "", + } }; }, @@ -231,6 +238,12 @@ export default { this.socket.firstConnect = false; }); + // cloudflared + socket.on("cloudflared_installed", (res) => this.cloudflared.installed = res); + socket.on("cloudflared_running", (res) => this.cloudflared.running = res); + socket.on("cloudflared_message", (res) => this.cloudflared.message = res); + socket.on("cloudflared_errorMessage", (res) => this.cloudflared.errorMessage = res); + socket.on("cloudflared_token", (res) => this.cloudflared.cloudflareTunnelToken = res); }, storage() { diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index 5b54e424..bd8ade5a 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -75,6 +75,9 @@ export default { notifications: { title: this.$t("Notifications"), }, + "reverse-proxy": { + title: this.$t("Reverse Proxy"), + }, "monitor-history": { title: this.$t("Monitor History"), }, diff --git a/src/router.js b/src/router.js index f59192d3..7ab75939 100644 --- a/src/router.js +++ b/src/router.js @@ -14,6 +14,7 @@ import Entry from "./pages/Entry.vue"; import Appearance from "./components/settings/Appearance.vue"; import General from "./components/settings/General.vue"; import Notifications from "./components/settings/Notifications.vue"; +import ReverseProxy from "./components/settings/ReverseProxy.vue"; import MonitorHistory from "./components/settings/MonitorHistory.vue"; import Security from "./components/settings/Security.vue"; import Backup from "./components/settings/Backup.vue"; @@ -83,6 +84,10 @@ const routes = [ path: "notifications", component: Notifications, }, + { + path: "reverse-proxy", + component: ReverseProxy, + }, { path: "monitor-history", component: MonitorHistory, From 82ea896bbc340eec09adc80872762607bc38d276 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Wed, 30 Mar 2022 11:59:49 +0800 Subject: [PATCH 4/6] Improve the workflow of cloudflared --- package.json | 2 +- server/server.js | 5 +- .../cloudflared-socket-handler.js | 33 ++++++++++--- src/components/settings/ReverseProxy.vue | 48 +++++++++++++++---- src/mixins/socket.js | 1 + 5 files changed, 70 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index bb7b1e57..641bcace 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", "limiter": "^2.1.0", - "node-cloudflared-tunnel": "~1.0.6", + "node-cloudflared-tunnel": "~1.0.7", "nodemailer": "~6.6.5", "notp": "~2.0.3", "password-hash": "~1.2.2", diff --git a/server/server.js b/server/server.js index f3203545..0734f527 100644 --- a/server/server.js +++ b/server/server.js @@ -133,7 +133,7 @@ const { statusPageSocketHandler } = require("./socket-handlers/status-page-socke const databaseSocketHandler = require("./socket-handlers/database-socket-handler"); const TwoFA = require("./2fa"); const StatusPage = require("./model/status_page"); -const { cloudflaredSocketHandler } = require("./socket-handlers/cloudflared-socket-handler"); +const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart } = require("./socket-handlers/cloudflared-socket-handler"); app.use(express.json()); @@ -1406,6 +1406,9 @@ exports.entryPage = "dashboard"; initBackgroundJobs(args); + // Start cloudflared at the end if configured + await cloudflaredAutoStart(); + })(); async function updateMonitorNotification(monitorID, notificationIDList) { diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js index f7c69ed2..128c4788 100644 --- a/server/socket-handlers/cloudflared-socket-handler.js +++ b/server/socket-handlers/cloudflared-socket-handler.js @@ -1,17 +1,13 @@ -const { checkLogin, setSetting, setting } = require("../util-server"); +const { checkLogin, setSetting, setting, doubleCheckPassword } = require("../util-server"); const { CloudflaredTunnel } = require("node-cloudflared-tunnel"); const { io } = require("../server"); const prefix = "cloudflared_"; const cloudflared = new CloudflaredTunnel(); -let isRunning; - cloudflared.change = (running, message) => { io.to("cloudflared").emit(prefix + "running", running); io.to("cloudflared").emit(prefix + "message", message); - isRunning = running; - }; cloudflared.error = (errorMessage) => { @@ -25,7 +21,7 @@ module.exports.cloudflaredSocketHandler = (socket) => { checkLogin(socket); socket.join("cloudflared"); io.to(socket.userID).emit(prefix + "installed", cloudflared.checkInstalled()); - io.to(socket.userID).emit(prefix + "running", isRunning); + io.to(socket.userID).emit(prefix + "running", cloudflared.running); io.to(socket.userID).emit(prefix + "token", await setting("cloudflaredTunnelToken")); } catch (error) { } }); @@ -62,11 +58,34 @@ module.exports.cloudflaredSocketHandler = (socket) => { } catch (error) { } }); - socket.on(prefix + "stop", async () => { + socket.on(prefix + "stop", async (currentPassword, callback) => { try { checkLogin(socket); + await doubleCheckPassword(socket, currentPassword); cloudflared.stop(); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + + socket.on(prefix + "removeToken", async () => { + try { + checkLogin(socket); + await setSetting("cloudflaredTunnelToken", ""); } catch (error) { } }); }; + +module.exports.autoStart = async () => { + let token = await setting("cloudflaredTunnelToken"); + + if (token) { + console.log("Start cloudflared"); + cloudflared.token = token; + cloudflared.start(); + } +}; diff --git a/src/components/settings/ReverseProxy.vue b/src/components/settings/ReverseProxy.vue index fe41644b..2b5f65d8 100644 --- a/src/components/settings/ReverseProxy.vue +++ b/src/components/settings/ReverseProxy.vue @@ -19,7 +19,7 @@ {{ message }} </div> - <div class="mt-3"> + <div v-if="errorMessage" class="mt-3"> Message: <textarea v-model="errorMessage" class="form-control" readonly></textarea> </div> @@ -37,8 +37,13 @@ id="cloudflareTunnelToken" v-model="cloudflareTunnelToken" autocomplete="one-time-code" + :readonly="running" /> <div class="form-text"> + <div v-if="cloudflareTunnelToken" class="mb-3"> + <span v-if="!running" class="remove-token" @click="removeToken">{{ $t("Remove Token") }}</span> + </div> + Don't know how to get the token? Please read the guide:<br /> <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel" target="_blank"> https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel @@ -46,15 +51,31 @@ </div> </div> - <!-- Save Button --> <div> <button v-if="!running" class="btn btn-primary" type="submit" @click="start"> {{ $t("Start") }} cloudflared </button> - <button v-if="running" class="btn btn-danger" type="submit" @click="stop"> + <button v-if="running" class="btn btn-danger" type="submit" @click="$refs.confirmStop.show();"> {{ $t("Stop") }} cloudflared </button> + + <Confirm ref="confirmStop" btn-style="btn-danger" :yes-text="$t('Stop') + ' cloudflared'" :no-text="$t('Cancel')" @yes="stop"> + The current connection may be lost if you are connecting Cloudflare Tunnel. Are you sure want to stop it? Type your password to confirm it. + + <div class="mt-3"> + <label for="current-password2" class="form-label"> + {{ $t("Current Password") }} + </label> + <input + id="current-password2" + v-model="currentPassword" + type="password" + class="form-control" + required + /> + </div> + </Confirm> </div> </div> @@ -68,14 +89,17 @@ <script> import HiddenInput from "../../components/HiddenInput.vue"; +import Confirm from "../Confirm.vue"; const prefix = "cloudflared_"; export default { components: { HiddenInput, + Confirm }, data() { + // See /src/mixins/socket.js return this.$root.cloudflared; }, computed: { @@ -84,7 +108,7 @@ export default { watch: { }, - mounted() { + created() { this.$root.getSocket().emit(prefix + "join"); }, unmounted() { @@ -95,17 +119,21 @@ export default { this.$root.getSocket().emit(prefix + "start", this.cloudflareTunnelToken); }, stop() { - this.$root.getSocket().emit(prefix + "stop"); + this.$root.getSocket().emit(prefix + "stop", this.currentPassword, (res) => { + this.$root.toastRes(res); + }); }, + removeToken() { + this.$root.getSocket().emit(prefix + "removeToken"); + this.cloudflareTunnelToken = ""; + } } }; </script> <style lang="scss" scoped> -.logo { - margin: 4em 1em; -} -.update-link { - font-size: 0.9em; +.remove-token { + text-decoration: underline; + cursor: pointer; } </style> diff --git a/src/mixins/socket.js b/src/mixins/socket.js index f2c4ff6d..d8b1ad22 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -48,6 +48,7 @@ export default { running: false, message: "", errorMessage: "", + currentPassword: "", } }; }, From 71be030733bcbc582a1305ec8700cdc31571fb64 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Wed, 30 Mar 2022 18:52:10 +0800 Subject: [PATCH 5/6] Add package-lock.json and minor words --- package-lock.json | 21 +++++++++++++++++++-- src/components/settings/ReverseProxy.vue | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1d30ce07..da15c8c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.12.1", + "version": "1.13.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.12.1", + "version": "1.13.1", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-svg-core": "~1.2.36", @@ -36,6 +36,7 @@ "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", "limiter": "^2.1.0", + "node-cloudflared-tunnel": "~1.0.7", "nodemailer": "~6.6.5", "notp": "~2.0.3", "password-hash": "~1.2.2", @@ -11160,6 +11161,14 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, + "node_modules/node-cloudflared-tunnel": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/node-cloudflared-tunnel/-/node-cloudflared-tunnel-1.0.7.tgz", + "integrity": "sha512-2xKygxFNZZPktF73dvJTNPjFkK4ThOPMpsZf885Iqq5Eie/vxk5mFH8a8dLlDWZYYpGDc699qToJTOlrTqd3Eg==", + "dependencies": { + "command-exists": "^1.2.9" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -24071,6 +24080,14 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, + "node-cloudflared-tunnel": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/node-cloudflared-tunnel/-/node-cloudflared-tunnel-1.0.7.tgz", + "integrity": "sha512-2xKygxFNZZPktF73dvJTNPjFkK4ThOPMpsZf885Iqq5Eie/vxk5mFH8a8dLlDWZYYpGDc699qToJTOlrTqd3Eg==", + "requires": { + "command-exists": "^1.2.9" + } + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", diff --git a/src/components/settings/ReverseProxy.vue b/src/components/settings/ReverseProxy.vue index 2b5f65d8..d35d5353 100644 --- a/src/components/settings/ReverseProxy.vue +++ b/src/components/settings/ReverseProxy.vue @@ -61,7 +61,7 @@ </button> <Confirm ref="confirmStop" btn-style="btn-danger" :yes-text="$t('Stop') + ' cloudflared'" :no-text="$t('Cancel')" @yes="stop"> - The current connection may be lost if you are connecting Cloudflare Tunnel. Are you sure want to stop it? Type your password to confirm it. + The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it. <div class="mt-3"> <label for="current-password2" class="form-label"> From b72a2d350fa82b279182e08af33974c6414f58cb Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Wed, 30 Mar 2022 20:08:26 +0800 Subject: [PATCH 6/6] Set cloudflared token from env var or arg --- package-lock.json | 14 +++++------ package.json | 2 +- server/server.js | 3 ++- .../cloudflared-socket-handler.js | 23 +++++++------------ 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index da15c8c3..e4fa75ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", "limiter": "^2.1.0", - "node-cloudflared-tunnel": "~1.0.7", + "node-cloudflared-tunnel": "~1.0.9", "nodemailer": "~6.6.5", "notp": "~2.0.3", "password-hash": "~1.2.2", @@ -11162,9 +11162,9 @@ "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, "node_modules/node-cloudflared-tunnel": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/node-cloudflared-tunnel/-/node-cloudflared-tunnel-1.0.7.tgz", - "integrity": "sha512-2xKygxFNZZPktF73dvJTNPjFkK4ThOPMpsZf885Iqq5Eie/vxk5mFH8a8dLlDWZYYpGDc699qToJTOlrTqd3Eg==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/node-cloudflared-tunnel/-/node-cloudflared-tunnel-1.0.9.tgz", + "integrity": "sha512-d0mhIM5P2ldE2yHChehC6EvnpFCkifWRzWrW81gVWdcCWqNcyISXuDdOYzRW5mwmjWuT6WNtLJoGQ84uqS4EmA==", "dependencies": { "command-exists": "^1.2.9" } @@ -24081,9 +24081,9 @@ "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, "node-cloudflared-tunnel": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/node-cloudflared-tunnel/-/node-cloudflared-tunnel-1.0.7.tgz", - "integrity": "sha512-2xKygxFNZZPktF73dvJTNPjFkK4ThOPMpsZf885Iqq5Eie/vxk5mFH8a8dLlDWZYYpGDc699qToJTOlrTqd3Eg==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/node-cloudflared-tunnel/-/node-cloudflared-tunnel-1.0.9.tgz", + "integrity": "sha512-d0mhIM5P2ldE2yHChehC6EvnpFCkifWRzWrW81gVWdcCWqNcyISXuDdOYzRW5mwmjWuT6WNtLJoGQ84uqS4EmA==", "requires": { "command-exists": "^1.2.9" } diff --git a/package.json b/package.json index 641bcace..3857aaac 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", "limiter": "^2.1.0", - "node-cloudflared-tunnel": "~1.0.7", + "node-cloudflared-tunnel": "~1.0.9", "nodemailer": "~6.6.5", "notp": "~2.0.3", "password-hash": "~1.2.2", diff --git a/server/server.js b/server/server.js index 0734f527..a3777c8c 100644 --- a/server/server.js +++ b/server/server.js @@ -91,6 +91,7 @@ const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || args.p const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || args["ssl-key"] || undefined; const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || args["ssl-cert"] || undefined; const disableFrameSameOrigin = !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || args["disable-frame-sameorigin"] || false; +const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CLOUDFLARED_TOKEN || undefined; // 2FA / notp verification defaults const twofa_verification_opts = { @@ -1407,7 +1408,7 @@ exports.entryPage = "dashboard"; initBackgroundJobs(args); // Start cloudflared at the end if configured - await cloudflaredAutoStart(); + await cloudflaredAutoStart(cloudflaredToken); })(); diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js index 128c4788..3f4a26e5 100644 --- a/server/socket-handlers/cloudflared-socket-handler.js +++ b/server/socket-handlers/cloudflared-socket-handler.js @@ -37,19 +37,6 @@ module.exports.cloudflaredSocketHandler = (socket) => { try { checkLogin(socket); if (token && typeof token === "string") { - token = token.trim(); - - // try to strip out "cloudflared.exe service install" - let array = token.split(" "); - if (array.length > 1) { - for (let i = 0; i < array.length - 1; i++) { - if (array[i] === "install") { - token = array[i + 1]; - } - } - } - - await setSetting("cloudflaredTunnelToken", token); cloudflared.token = token; } else { cloudflared.token = null; @@ -80,8 +67,14 @@ module.exports.cloudflaredSocketHandler = (socket) => { }; -module.exports.autoStart = async () => { - let token = await setting("cloudflaredTunnelToken"); +module.exports.autoStart = async (token) => { + if (!token) { + token = await setting("cloudflaredTunnelToken"); + } else { + // Override the current token via args or env var + await setSetting("cloudflaredTunnelToken", token); + console.log("Use cloudflared token from args or env var"); + } if (token) { console.log("Start cloudflared");