From d2b48a648f0a253f23b03ee11029e41c97225da9 Mon Sep 17 00:00:00 2001 From: Gabriel Ngandu-Biseba Date: Fri, 29 Nov 2024 10:46:27 +0100 Subject: [PATCH 01/14] Add support for user provided CA certificate to establish secure connections with a mysql/mariadb server --- server/config.js | 6 ++++++ server/database.js | 14 ++++++++++++++ server/setup-database.js | 13 +++++++++++++ 3 files changed, 33 insertions(+) diff --git a/server/config.js b/server/config.js index 515b90465f..ce1e35293b 100644 --- a/server/config.js +++ b/server/config.js @@ -1,3 +1,4 @@ +/* eslint-disable linebreak-style */ const isFreeBSD = /^freebsd/.test(process.platform); // Interop with browser @@ -19,6 +20,9 @@ const sslKeyPassphrase = args["ssl-key-passphrase"] || process.env.UPTIME_KUMA_S const isSSL = sslKey && sslCert; +const mariaDbSslCert = args["UPTIME_KUMA_DB_SSL_CERT"] || process.env.UPTIME_KUMA_DB_SSL_CERT || process.env.MARIADB_SSL_CERT || undefined; +const mariaDbUseSSL = mariaDbSslCert ? "true" : "false"; + /** * Get the local WebSocket URL * @returns {string} The local WebSocket URL @@ -43,4 +47,6 @@ module.exports = { isSSL, localWebSocketURL, demoMode, + mariaDbSslCert, + mariaDbUseSSL }; diff --git a/server/database.js b/server/database.js index 3b7646de8c..55141faee3 100644 --- a/server/database.js +++ b/server/database.js @@ -1,3 +1,4 @@ +/* eslint-disable linebreak-style */ const fs = require("fs"); const { R } = require("redbean-node"); const { setSetting, setting } = require("./util-server"); @@ -11,6 +12,7 @@ const { UptimeCalculator } = require("./uptime-calculator"); const dayjs = require("dayjs"); const { SimpleMigrationServer } = require("./utils/simple-migration-server"); const KumaColumnCompiler = require("./utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler"); +const { mariaDbSslCert, mariaDbUseSSL } = require("./config"); /** * Database & App Data Folder @@ -259,11 +261,22 @@ class Database { throw Error("Invalid database name. A database name can only consist of letters, numbers and underscores"); } + let sslConfig = null; + let serverCa = undefined; + if (mariaDbUseSSL) { + serverCa = [ fs.readFileSync(mariaDbSslCert, "utf8") ]; + sslConfig = { + rejectUnauthorized: true, + ca: serverCa + }; + } + const connection = await mysql.createConnection({ host: dbConfig.hostname, port: dbConfig.port, user: dbConfig.username, password: dbConfig.password, + ssl: sslConfig }); await connection.execute("CREATE DATABASE IF NOT EXISTS " + dbConfig.dbName + " CHARACTER SET utf8mb4"); @@ -278,6 +291,7 @@ class Database { password: dbConfig.password, database: dbConfig.dbName, timezone: "Z", + ssl: sslConfig, typeCast: function (field, next) { if (field.type === "DATETIME") { // Do not perform timezone conversion diff --git a/server/setup-database.js b/server/setup-database.js index 483f2c9a49..a73e285968 100644 --- a/server/setup-database.js +++ b/server/setup-database.js @@ -1,3 +1,4 @@ +/* eslint-disable linebreak-style */ const express = require("express"); const { log } = require("../src/util"); const expressStaticGzip = require("express-static-gzip"); @@ -6,6 +7,7 @@ const path = require("path"); const Database = require("./database"); const { allowDevAllOrigin } = require("./util-server"); const mysql = require("mysql2/promise"); +const { mariaDbUseSSL, mariaDbSslCert } = require("./config"); /** * A standalone express app that is used to setup a database @@ -208,11 +210,22 @@ class SetupDatabase { // Test connection try { + let sslConfig = null; + let serverCa = undefined; + if (mariaDbUseSSL) { + serverCa = [ fs.readFileSync(mariaDbSslCert, "utf8") ]; + sslConfig = { + rejectUnauthorized: true, + ca: serverCa + }; + } + const connection = await mysql.createConnection({ host: dbConfig.hostname, port: dbConfig.port, user: dbConfig.username, password: dbConfig.password, + ssl: sslConfig }); await connection.execute("SELECT 1"); connection.end(); From 98ba019cf08b3e46316763c5cefba35112131d6e Mon Sep 17 00:00:00 2001 From: Gabriel Ngandu-Biseba Date: Fri, 29 Nov 2024 11:42:06 +0100 Subject: [PATCH 02/14] Fix always true if condition --- server/database.js | 2 +- server/setup-database.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/server/database.js b/server/database.js index 55141faee3..a452bfed12 100644 --- a/server/database.js +++ b/server/database.js @@ -263,7 +263,7 @@ class Database { let sslConfig = null; let serverCa = undefined; - if (mariaDbUseSSL) { + if (mariaDbUseSSL === true) { serverCa = [ fs.readFileSync(mariaDbSslCert, "utf8") ]; sslConfig = { rejectUnauthorized: true, diff --git a/server/setup-database.js b/server/setup-database.js index a73e285968..dc8486b8bb 100644 --- a/server/setup-database.js +++ b/server/setup-database.js @@ -207,19 +207,17 @@ class SetupDatabase { this.runningSetup = false; return; } - // Test connection try { let sslConfig = null; let serverCa = undefined; - if (mariaDbUseSSL) { + if (mariaDbUseSSL === true) { serverCa = [ fs.readFileSync(mariaDbSslCert, "utf8") ]; sslConfig = { rejectUnauthorized: true, ca: serverCa }; } - const connection = await mysql.createConnection({ host: dbConfig.hostname, port: dbConfig.port, From 69896a7299a72f734ef0b719b335358f25d392f9 Mon Sep 17 00:00:00 2001 From: Gabriel Ngandu-Biseba Date: Fri, 6 Dec 2024 11:43:25 +0100 Subject: [PATCH 03/14] Rename the UPTIME_KUMA_DB_SSL_CERT environment variable to a more expressive name --- server/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/config.js b/server/config.js index ce1e35293b..e94804b63b 100644 --- a/server/config.js +++ b/server/config.js @@ -20,7 +20,7 @@ const sslKeyPassphrase = args["ssl-key-passphrase"] || process.env.UPTIME_KUMA_S const isSSL = sslKey && sslCert; -const mariaDbSslCert = args["UPTIME_KUMA_DB_SSL_CERT"] || process.env.UPTIME_KUMA_DB_SSL_CERT || process.env.MARIADB_SSL_CERT || undefined; +const mariaDbSslCert = args["UPTIME_KUMA_DB_SSL_CERT"] || process.env.UPTIME_KUMA_DB_CA_CERT || process.env.MARIADB_SSL_CERT || undefined; const mariaDbUseSSL = mariaDbSslCert ? "true" : "false"; /** From 0943e5d35407433d08cbfbd5af6838bd479864aa Mon Sep 17 00:00:00 2001 From: Gabriel Ngandu-Biseba Date: Fri, 6 Dec 2024 11:43:43 +0100 Subject: [PATCH 04/14] Remove unused config --- server/database.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/server/database.js b/server/database.js index a452bfed12..26a46cf2b2 100644 --- a/server/database.js +++ b/server/database.js @@ -12,7 +12,6 @@ const { UptimeCalculator } = require("./uptime-calculator"); const dayjs = require("dayjs"); const { SimpleMigrationServer } = require("./utils/simple-migration-server"); const KumaColumnCompiler = require("./utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler"); -const { mariaDbSslCert, mariaDbUseSSL } = require("./config"); /** * Database & App Data Folder @@ -186,10 +185,18 @@ class Database { /** * @typedef {string|undefined} envString - * @param {{type: "sqlite"} | {type:envString, hostname:envString, port:envString, database:envString, username:envString, password:envString}} dbConfig the database configuration that should be written + * @param {{type: "sqlite"} | {type:envString, hostname:envString, port:envString, database:envString, username:envString, password:envString, caFilePath:envString}} dbConfig the database configuration that should be written * @returns {void} */ static writeDBConfig(dbConfig) { + // Move CA file to the data directory + if (dbConfig.caFilePath) { + const dataCaFilePath = path.join(Database.dataDir, "mariadb-ca.pem"); + fs.renameSync(dbConfig.caFilePath, dataCaFilePath); + dbConfig.caFilePath = dataCaFilePath; + dbConfig.ssl = undefined; + dbConfig.caFile = undefined; + } fs.writeFileSync(path.join(Database.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4)); } @@ -263,8 +270,8 @@ class Database { let sslConfig = null; let serverCa = undefined; - if (mariaDbUseSSL === true) { - serverCa = [ fs.readFileSync(mariaDbSslCert, "utf8") ]; + if (dbConfig.caFilePath) { + serverCa = [ fs.readFileSync(dbConfig.caFilePath, "utf8") ]; sslConfig = { rejectUnauthorized: true, ca: serverCa @@ -290,8 +297,9 @@ class Database { user: dbConfig.username, password: dbConfig.password, database: dbConfig.dbName, - timezone: "Z", ssl: sslConfig, + timezone: "Z", + //ssl: sslConfig, typeCast: function (field, next) { if (field.type === "DATETIME") { // Do not perform timezone conversion From bef4479976b730eeb295e77b3a4b004230745f78 Mon Sep 17 00:00:00 2001 From: Gabriel Ngandu-Biseba Date: Fri, 6 Dec 2024 11:44:24 +0100 Subject: [PATCH 05/14] Add CA file upload to the maria db ui --- src/lang/en.json | 1 + src/pages/SetupDatabase.vue | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/lang/en.json b/src/lang/en.json index e215f1031f..cdea2ad9da 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -6,6 +6,7 @@ "setupDatabaseSQLite": "A simple database file, recommended for small-scale deployments. Prior to v2.0.0, Uptime Kuma used SQLite as the default database.", "settingUpDatabaseMSG": "Setting up the database. It may take a while, please be patient.", "dbName": "Database Name", + "caFile": "Database CA certificate", "Settings": "Settings", "Dashboard": "Dashboard", "Help": "Help", diff --git a/src/pages/SetupDatabase.vue b/src/pages/SetupDatabase.vue index 81738a98b5..c7fef2c8c6 100644 --- a/src/pages/SetupDatabase.vue +++ b/src/pages/SetupDatabase.vue @@ -90,8 +90,12 @@ - +
+ + +
+ @@ -117,6 +121,7 @@ export default { username: "", password: "", dbName: "kuma", + caFile: "" }, info: { needSetup: false, @@ -178,6 +183,15 @@ export default { } }, + onCaFileChange(e) { + const fileReader = new FileReader(); + fileReader.onload = () => { + this.dbConfig.caFile = fileReader.result; + console.log(this.dbConfig.caFile); + }; + fileReader.readAsDataURL(e.target.files[0]); + }, + test() { this.$root.toastError("not implemented"); } @@ -186,6 +200,22 @@ export default {