mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-31 07:35:24 +00:00
fix: let non docker installs switch nginx to https (#1711)
This commit is contained in:
1 parent
d43f543c59
commit
0e9b3db5fc
4 files changed
+112
-15
No files matched your search
@@ -9,6 +9,7 @@
|
|||||||
- The plugin list tells the mobile app which plugin API each plugin needs, so the app can say when it or a plugin needs an update
|
- The plugin list tells the mobile app which plugin API each plugin needs, so the app can say when it or a plugin needs an update
|
||||||
- Hosts can have a color and an icon, which sync to the desktop and mobile apps
|
- Hosts can have a color and an icon, which sync to the desktop and mobile apps
|
||||||
- A sidebar setting to turn off alternating row colors
|
- A sidebar setting to turn off alternating row colors
|
||||||
|
- TERMIX_NGINX_TEMPLATE, TERMIX_NGINX_CONF, TERMIX_NGINX_PID and TERMIX_APP_DIR let an install outside Docker, like the Proxmox LXC script, switch nginx to HTTPS when a certificate is added
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
- A plugin the app no longer ships and could not download (for example on a locked down network) shows as failed with the reason on the Plugins tab, instead of as installed
|
- A plugin the app no longer ships and could not download (for example on a locked down network) shows as failed with the reason on the Plugins tab, instead of as installed
|
||||||
- The desktop app no longer shows This layout could not be shown when the layout is asked for before its plugin has finished starting; it tries again
|
- The desktop app no longer shows This layout could not be shown when the layout is asked for before its plugin has finished starting; it tries again
|
||||||
- On the desktop, a plugin installed from the registry that uses core's packages through require (like ACME SSL and axios) starts instead of failing with Cannot find module
|
- On the desktop, a plugin installed from the registry that uses core's packages through require (like ACME SSL and axios) starts instead of failing with Cannot find module
|
||||||
|
- Adding a certificate on an install where Termix cannot manage nginx says what to set instead of only asking for a restart
|
||||||
|
|
||||||
## 26.10.5
|
## 26.10.5
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,30 @@
|
|||||||
"default": "8443",
|
"default": "8443",
|
||||||
"description": "Port for HTTPS."
|
"description": "Port for HTTPS."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "TERMIX_NGINX_TEMPLATE",
|
||||||
|
"group": "https",
|
||||||
|
"default": "/app/nginx/nginx-https.conf.template",
|
||||||
|
"description": "The HTTPS nginx template Termix renders when a certificate is added. Set it on an install outside Docker."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TERMIX_NGINX_CONF",
|
||||||
|
"group": "https",
|
||||||
|
"default": "/tmp/nginx/nginx.conf",
|
||||||
|
"description": "Where Termix writes the rendered nginx config before it reloads nginx."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TERMIX_NGINX_PID",
|
||||||
|
"group": "https",
|
||||||
|
"default": "/tmp/nginx/nginx.pid",
|
||||||
|
"description": "The nginx pid file. Termix only reloads nginx when it exists."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TERMIX_APP_DIR",
|
||||||
|
"group": "https",
|
||||||
|
"default": "/app",
|
||||||
|
"description": "Where Termix is installed, used in place of /app in the nginx template."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "SSL_CERT_PATH",
|
"name": "SSL_CERT_PATH",
|
||||||
"group": "https",
|
"group": "https",
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
vi.mock("../../utils/logger.js", () => ({
|
||||||
|
authLogger: { info: vi.fn(), error: vi.fn() },
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { renderNginxTemplate } = await import("../../tls/nginx-reload.js");
|
||||||
|
|
||||||
|
const TEMPLATE = [
|
||||||
|
"listen ${PORT};",
|
||||||
|
"listen ${SSL_PORT} ssl;",
|
||||||
|
"ssl_certificate ${SSL_CERT_PATH};",
|
||||||
|
" root /app/html;",
|
||||||
|
"pid /tmp/nginx/nginx.pid;",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const values = {
|
||||||
|
port: "8080",
|
||||||
|
sslPort: "443",
|
||||||
|
certPath: "/data/ssl/termix.crt",
|
||||||
|
keyPath: "/data/ssl/termix.key",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("renderNginxTemplate", () => {
|
||||||
|
it("fills the ports and certificate", () => {
|
||||||
|
const out = renderNginxTemplate(TEMPLATE, values);
|
||||||
|
expect(out).toContain("listen 8080;");
|
||||||
|
expect(out).toContain("listen 443 ssl;");
|
||||||
|
expect(out).toContain("ssl_certificate /data/ssl/termix.crt;");
|
||||||
|
expect(out).toContain("root /app/html;");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses the install folder in place of /app", () => {
|
||||||
|
const out = renderNginxTemplate(TEMPLATE, {
|
||||||
|
...values,
|
||||||
|
appDir: "/opt/termix/",
|
||||||
|
});
|
||||||
|
expect(out).toContain("root /opt/termix/html;");
|
||||||
|
expect(out).toContain("pid /tmp/nginx/nginx.pid;");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,9 +3,40 @@ import { existsSync, readFileSync, writeFileSync } from "fs";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { authLogger } from "../utils/logger.js";
|
import { authLogger } from "../utils/logger.js";
|
||||||
|
|
||||||
const NGINX_TEMPLATE = "/app/nginx/nginx-https.conf.template";
|
// The Docker image's paths. An install outside Docker (a Proxmox LXC script,
|
||||||
const NGINX_CONF = "/tmp/nginx/nginx.conf";
|
// a source checkout) points these at its own nginx instead.
|
||||||
const NGINX_PID = "/tmp/nginx/nginx.pid";
|
const nginxTemplate = () =>
|
||||||
|
process.env.TERMIX_NGINX_TEMPLATE || "/app/nginx/nginx-https.conf.template";
|
||||||
|
const nginxConf = () =>
|
||||||
|
process.env.TERMIX_NGINX_CONF || "/tmp/nginx/nginx.conf";
|
||||||
|
const nginxPid = () => process.env.TERMIX_NGINX_PID || "/tmp/nginx/nginx.pid";
|
||||||
|
|
||||||
|
/** The template with Termix's own folder in place of the image's /app. */
|
||||||
|
export function renderNginxTemplate(
|
||||||
|
template: string,
|
||||||
|
values: {
|
||||||
|
port: string;
|
||||||
|
sslPort: string;
|
||||||
|
certPath: string;
|
||||||
|
keyPath: string;
|
||||||
|
appDir?: string;
|
||||||
|
},
|
||||||
|
): string {
|
||||||
|
let rendered = template
|
||||||
|
.split("${PORT}")
|
||||||
|
.join(values.port)
|
||||||
|
.split("${SSL_PORT}")
|
||||||
|
.join(values.sslPort)
|
||||||
|
.split("${SSL_CERT_PATH}")
|
||||||
|
.join(values.certPath)
|
||||||
|
.split("${SSL_KEY_PATH}")
|
||||||
|
.join(values.keyPath);
|
||||||
|
const appDir = values.appDir?.replace(/\/+$/, "");
|
||||||
|
if (appDir && appDir !== "/app") {
|
||||||
|
rendered = rendered.replace(/(^|[\s;])\/app\//gm, `$1${appDir}/`);
|
||||||
|
}
|
||||||
|
return rendered;
|
||||||
|
}
|
||||||
|
|
||||||
const DATA_DIR = process.env.DATA_DIR || "./db/data";
|
const DATA_DIR = process.env.DATA_DIR || "./db/data";
|
||||||
const SSL_DIR = path.join(DATA_DIR, "ssl");
|
const SSL_DIR = path.join(DATA_DIR, "ssl");
|
||||||
@@ -51,11 +82,13 @@ export function persistSSLEnv(
|
|||||||
* restart. No-ops outside the Docker image (template/binary won't exist).
|
* restart. No-ops outside the Docker image (template/binary won't exist).
|
||||||
*/
|
*/
|
||||||
export function reloadNginxWithSSL(): { applied: boolean; message: string } {
|
export function reloadNginxWithSSL(): { applied: boolean; message: string } {
|
||||||
if (!existsSync(NGINX_TEMPLATE) || !existsSync(NGINX_PID)) {
|
const NGINX_TEMPLATE = nginxTemplate();
|
||||||
|
const NGINX_CONF = nginxConf();
|
||||||
|
if (!existsSync(NGINX_TEMPLATE) || !existsSync(nginxPid())) {
|
||||||
return {
|
return {
|
||||||
applied: false,
|
applied: false,
|
||||||
message:
|
message:
|
||||||
"nginx is not managed by this environment; restart Termix with ENABLE_SSL=true to apply the certificate.",
|
"Termix could not find the nginx it runs with, so HTTPS is not on yet. In Docker, restart Termix with ENABLE_SSL=true. On another install, set TERMIX_NGINX_TEMPLATE, TERMIX_NGINX_CONF and TERMIX_NGINX_PID (and TERMIX_APP_DIR if it is not /app), or add a listen 443 ssl block with this certificate to your nginx config.",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,16 +99,13 @@ export function reloadNginxWithSSL(): { applied: boolean; message: string } {
|
|||||||
const keyPath = process.env.SSL_KEY_PATH || path.join(SSL_DIR, "termix.key");
|
const keyPath = process.env.SSL_KEY_PATH || path.join(SSL_DIR, "termix.key");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const template = readFileSync(NGINX_TEMPLATE, "utf8");
|
const rendered = renderNginxTemplate(readFileSync(NGINX_TEMPLATE, "utf8"), {
|
||||||
const rendered = template
|
port,
|
||||||
.split("${PORT}")
|
sslPort,
|
||||||
.join(port)
|
certPath,
|
||||||
.split("${SSL_PORT}")
|
keyPath,
|
||||||
.join(sslPort)
|
appDir: process.env.TERMIX_APP_DIR,
|
||||||
.split("${SSL_CERT_PATH}")
|
});
|
||||||
.join(certPath)
|
|
||||||
.split("${SSL_KEY_PATH}")
|
|
||||||
.join(keyPath);
|
|
||||||
|
|
||||||
writeFileSync(NGINX_CONF, rendered);
|
writeFileSync(NGINX_CONF, rendered);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user