v0.34.16
This commit is contained in:
1 parent
e364392659
commit
f27fbf6a30
28 files changed
+2000
-60
No files matched your search
@@ -10,7 +10,7 @@ function basicAuthEditor(parent, settings = {}) {
|
||||
row.prepare = async () => { if (!password.value) { if (!row.userHash) throw new Error(t("basicHashRequired")); return; } const name = username.value.trim(), secret = password.value, epoch = draftEpoch; const result = await api("/api/basic-auth/hash", { method: "POST", body: JSON.stringify({ username: name, password: secret }) }); if (epoch !== draftEpoch || !row.isConnected || password.value !== secret || username.value.trim() !== name) throw new Error(t("vhUnsaved")); row.userHash = result.password_hash; password.value = ""; password.placeholder = t("basicKeepPassword"); state.textContent = t("basicHashSaved"); };
|
||||
}
|
||||
for (const user of settings.users || []) add(user); parent.append(button("basicAddUser", () => { add(); changed(); })); note(parent, "basicHelp");
|
||||
return { async prepare() { for (const row of rows.children) await row.prepare(); }, read: () => ({ enabled: str("auth-mode") === "basic", realm: realm.value.trim(), require_tls: secure.checked, forward_credentials: forward.checked, users: [...rows.children].map(row => ({ username: str("basic-username", row), password_hash: row.userHash })) }) };
|
||||
return { async prepare() { for (const row of rows.children) await row.prepare(); }, read: () => ({ enabled: selectedAuthentication().provider === "basic", realm: realm.value.trim(), require_tls: secure.checked, forward_credentials: forward.checked, users: [...rows.children].map(row => ({ username: str("basic-username", row), password_hash: row.userHash })) }) };
|
||||
}
|
||||
|
||||
function hostTagEditor(parent, hosts = [], onUpdate = () => { }) {
|
||||
@@ -33,10 +33,29 @@ function hostTagEditor(parent, hosts = [], onUpdate = () => { }) {
|
||||
return () => { if (input.value.trim()) consume(input.value); if (!values.length) throw new Error(t("vhHostRequired")); return [...values]; };
|
||||
}
|
||||
|
||||
const authMethodsByProvider = {
|
||||
none: [], basic: ["basic"], tinyauth: ["forward_auth", "oidc"],
|
||||
authelia: ["forward_auth", "oidc"], authentik: ["forward_auth", "oidc"],
|
||||
pocket_id: ["oidc"], custom: ["oidc"]
|
||||
};
|
||||
function inferredAuthentication(value) {
|
||||
if (value.authentication?.provider && value.authentication?.method) return value.authentication;
|
||||
if (value.basic_auth?.enabled) return { provider: "basic", method: "basic" };
|
||||
for (const provider of ["tinyauth", "authelia", "authentik"]) {
|
||||
if (value[provider]?.enabled) return { provider, method: "forward_auth" };
|
||||
}
|
||||
if (value.oidc?.enabled) return { provider: "custom", method: "oidc" };
|
||||
return { provider: "none", method: "none" };
|
||||
}
|
||||
function selectedAuthentication() {
|
||||
const provider = str("auth-provider"), method = str("auth-method");
|
||||
return provider === "none" ? { provider, method: "none" } : { provider, method };
|
||||
}
|
||||
|
||||
function readForwardProvider(id) {
|
||||
const value = key => str(`${id}-${key}`);
|
||||
return {
|
||||
enabled: str("auth-mode") === id,
|
||||
enabled: selectedAuthentication().provider === id && selectedAuthentication().method === "forward_auth",
|
||||
url: value("url"), login_url: id === "authelia" ? value("login") : "",
|
||||
allow_private: yes(`${id}-private`), redirect_forbidden: yes(`${id}-redirect-forbidden`),
|
||||
connect_timeout_seconds: num(`${id}-connect`), timeout_seconds: num(`${id}-timeout`),
|
||||
@@ -344,7 +363,9 @@ function load(value = defaults(), migrationIds = [], show = true) {
|
||||
const syncTlsPolicy = () => { for (const input of [tls12, tls13, tlsCiphers, tlsPqc]) input.disabled = !customTlsPolicy.checked; };
|
||||
customTlsPolicy.addEventListener("change", syncTlsPolicy); syncTlsPolicy(); note(g, "vhTlsOverrideHelp"); note(g, "tlsPqcVhostHelp");
|
||||
g = group("vhTinyauth"); const auth = value.tinyauth || {};
|
||||
const authMode = field(g, "auth-mode", "authMode", value.basic_auth?.enabled ? "basic" : auth.enabled ? "tinyauth" : value.authelia?.enabled ? "authelia" : value.authentik?.enabled ? "authentik" : "none", "select", { values: [["none", "authNone"], ["tinyauth", "authTinyauth"], ["authelia", "authAuthelia"], ["authentik", "authAuthentik"], ["basic", "authBasic"]] });
|
||||
const initialAuth = inferredAuthentication(value);
|
||||
const authProvider = field(g, "auth-provider", "authProvider", initialAuth.provider, "select", { values: [["none", "authNone"], ["tinyauth", "authTinyauth"], ["authelia", "authAuthelia"], ["authentik", "authAuthentik"], ["pocket_id", "authPocketId"], ["custom", "authCustomOidc"], ["basic", "authBasic"]] });
|
||||
const authMethod = field(g, "auth-method", "authMethod", initialAuth.method, "select", { values: [["forward_auth", "authForwardAuth"], ["oidc", "authOidc"], ["basic", "authBasic"]] });
|
||||
note(g, "authModeHelp");
|
||||
|
||||
const taBody = el("section", undefined, "vh-wide auth-subcard"), basicBody = el("section", undefined, "vh-wide auth-subcard"); g.append(taBody, basicBody);
|
||||
@@ -397,21 +418,63 @@ function load(value = defaults(), migrationIds = [], show = true) {
|
||||
forwardEditors[id] = { body, url, login };
|
||||
}
|
||||
|
||||
const oidc = value.oidc || {};
|
||||
const oidcBody = el("section", undefined, "vh-wide auth-subcard"); g.append(oidcBody);
|
||||
oidcBody.append(text("h3", "authOidc"));
|
||||
const oidcPrimary = el("div", undefined, "vh-grid"); oidcBody.append(oidcPrimary);
|
||||
const oidcIssuer = field(oidcPrimary, "oidc-issuer", "oidcIssuer", oidc.issuer_url || "", "url", { maxLength: 2048 });
|
||||
oidcIssuer.placeholder = "https://auth.example.com";
|
||||
const oidcBackend = field(oidcPrimary, "oidc-backend", "oidcBackend", oidc.backend_url || "", "url", { maxLength: 2048 });
|
||||
oidcBackend.placeholder = "http://172.17.0.20:9091";
|
||||
const oidcRedirect = field(oidcPrimary, "oidc-redirect", "oidcRedirect", oidc.redirect_url || (value.frontend?.hosts || []).filter(host => !host.startsWith("*.")).map(host => `https://${host}/_rustproxy/oidc/callback`)[0] || "", "url", { maxLength: 2048 });
|
||||
oidcRedirect.placeholder = "https://app.example.com/_rustproxy/oidc/callback";
|
||||
const oidcClient = field(oidcPrimary, "oidc-client", "oidcClient", oidc.client_id || "", "text", { maxLength: 256 });
|
||||
const oidcSecret = field(oidcPrimary, "oidc-secret", "oidcSecret", oidc.client_secret || "", "password", { maxLength: 2048 });
|
||||
oidcSecret.autocomplete = "new-password";
|
||||
const oidcSession = field(oidcPrimary, "oidc-session", "oidcSession", oidc.session_secret || "", "password", { maxLength: 4096 });
|
||||
oidcSession.autocomplete = "new-password";
|
||||
const oidcScopes = field(oidcPrimary, "oidc-scopes", "oidcScopes", oidc.scopes || "openid profile email", "text", { maxLength: 1024 });
|
||||
const oidcOptions = el("div", undefined, "vh-wide auth-provider-options"); oidcPrimary.append(oidcOptions);
|
||||
field(oidcOptions, "oidc-private", "vhPrivate", oidc.allow_private === true, "checkbox");
|
||||
note(oidcBody, "oidcHelp");
|
||||
const oidcAdvanced = el("details", undefined, "vh-wide auth-advanced"); oidcAdvanced.append(text("summary", "forwardAuthAdvanced"));
|
||||
const oidcAdvancedGrid = el("div", undefined, "vh-grid"); oidcAdvanced.append(oidcAdvancedGrid); oidcBody.append(oidcAdvanced);
|
||||
field(oidcAdvancedGrid, "oidc-connect", "taConnect", oidc.connect_timeout_seconds ?? 5, "number", { min: 1, max: 30, required: true });
|
||||
field(oidcAdvancedGrid, "oidc-timeout", "taTimeout", oidc.timeout_seconds ?? 10, "number", { min: 1, max: 60, required: true });
|
||||
field(oidcAdvancedGrid, "oidc-ttl", "oidcTtl", oidc.session_ttl_seconds ?? 3600, "number", { min: 60, max: 86400, required: true });
|
||||
field(oidcAdvancedGrid, "oidc-allowlist", "vhAddressAllowlist", (oidc.address_allowlist || []).join("\n"), "textarea", { rows: 2, maxLength: 8192 });
|
||||
field(oidcAdvancedGrid, "oidc-ca", "vhCa", oidc.tls_ca_pem || "", "textarea", { rows: 2, maxLength: 65536 });
|
||||
|
||||
basicBody.append(text("h3", "authBasic"));
|
||||
const basicGrid = el("div", undefined, "vh-grid"); basicBody.append(basicGrid);
|
||||
basicEditor = basicAuthEditor(basicGrid, value.basic_auth || {});
|
||||
const syncAuth = () => {
|
||||
const tinyauthSelected = authMode.value === "tinyauth";
|
||||
const methods = authMethodsByProvider[authProvider.value] || [];
|
||||
const previous = authMethod.value;
|
||||
authMethod.replaceChildren(...methods.map(method => {
|
||||
const label = method === "forward_auth" ? "authForwardAuth" : method === "basic" ? "authBasic" : "authOidc";
|
||||
const option = text("option", label); option.value = method; return option;
|
||||
}));
|
||||
if (methods.includes(previous)) authMethod.value = previous;
|
||||
authMethod.closest("label").hidden = authProvider.value === "none";
|
||||
const { provider, method } = selectedAuthentication();
|
||||
const forward = method === "forward_auth";
|
||||
const tinyauthSelected = provider === "tinyauth" && forward;
|
||||
taBody.hidden = !tinyauthSelected;
|
||||
basicBody.hidden = authMode.value !== "basic";
|
||||
basicBody.hidden = provider !== "basic";
|
||||
authUrl.required = loginUrl.required = tinyauthSelected;
|
||||
oidcBody.hidden = method !== "oidc";
|
||||
for (const input of [oidcIssuer, oidcRedirect, oidcClient, oidcSecret, oidcSession, oidcScopes]) input.required = method === "oidc";
|
||||
for (const [id, editor] of Object.entries(forwardEditors)) {
|
||||
editor.body.hidden = id !== authMode.value;
|
||||
editor.url.required = id === authMode.value;
|
||||
if (editor.login) editor.login.required = id === authMode.value;
|
||||
const selected = provider === id && forward;
|
||||
editor.body.hidden = !selected;
|
||||
editor.url.required = selected;
|
||||
if (editor.login) editor.login.required = selected;
|
||||
}
|
||||
};
|
||||
authMode.addEventListener("change", syncAuth); syncAuth();
|
||||
authProvider.addEventListener("change", syncAuth);
|
||||
authMethod.addEventListener("change", syncAuth);
|
||||
syncAuth();
|
||||
g = group("vhResponsePolicy"); const server = value.server_header, diagnostics = value.diagnostic_headers;
|
||||
const serverMode = field(g, "server-mode", "serverHeader", server?.mode || "inherit", "select", { values: [["inherit", "vhInherit"], ["preserve", "serverPreserve"], ["hide", "serverHide"], ["custom", "serverCustom"]] });
|
||||
const serverValue = field(g, "server-value", "serverValue", server?.value || "RustProxy", "text", { maxLength: 256 });
|
||||
@@ -554,8 +617,15 @@ function collect() {
|
||||
content_type_policy: { enabled: yes("content-type-block-enabled"), blocked: lines(str("content-type-block-list")).map(value => value.toLowerCase()) },
|
||||
server_header: str("server-mode") === "inherit" ? null : { mode: str("server-mode"), value: str("server-value") },
|
||||
diagnostic_headers: yes("diag-inherit") ? null : { request_id: yes("diag-request_id") },
|
||||
tinyauth: { enabled: str("auth-mode") === "tinyauth", url: str("auth-url"), login_url: str("auth-login"), allow_private: yes("auth-private"), redirect_forbidden: yes("auth-redirect-forbidden"), connect_timeout_seconds: num("auth-connect"), timeout_seconds: num("auth-timeout"), request_headers: lines(str("auth-request-headers")), response_headers: lines(str("auth-response-headers")), address_allowlist: lines(str("auth-allowlist")), tls_ca_pem: nullable("auth-ca") },
|
||||
authentication: selectedAuthentication().provider === "none" ? null : selectedAuthentication(),
|
||||
tinyauth: { enabled: selectedAuthentication().provider === "tinyauth" && selectedAuthentication().method === "forward_auth", url: str("auth-url"), login_url: str("auth-login"), allow_private: yes("auth-private"), redirect_forbidden: yes("auth-redirect-forbidden"), connect_timeout_seconds: num("auth-connect"), timeout_seconds: num("auth-timeout"), request_headers: lines(str("auth-request-headers")), response_headers: lines(str("auth-response-headers")), address_allowlist: lines(str("auth-allowlist")), tls_ca_pem: nullable("auth-ca") },
|
||||
authelia: readForwardProvider("authelia"), authentik: readForwardProvider("authentik"),
|
||||
oidc: { enabled: selectedAuthentication().method === "oidc", issuer_url: str("oidc-issuer"), backend_url: str("oidc-backend"),
|
||||
redirect_url: str("oidc-redirect"), client_id: str("oidc-client"),
|
||||
client_secret: str("oidc-secret"), session_secret: str("oidc-session"), scopes: str("oidc-scopes"),
|
||||
allow_private: yes("oidc-private"), address_allowlist: lines(str("oidc-allowlist")),
|
||||
tls_ca_pem: nullable("oidc-ca"), connect_timeout_seconds: num("oidc-connect"),
|
||||
timeout_seconds: num("oidc-timeout"), session_ttl_seconds: num("oidc-ttl") },
|
||||
backends: members, routes: paths, acl, headers,
|
||||
load_balancing: {
|
||||
algorithm: str("algorithm"), slow_start_seconds: num("slow-start"), keepalive: { enabled: yes("keepalive"), idle_timeout_seconds: num("idle-timeout"), max_idle_per_host: num("max-idle"), tcp_keepalive_seconds: num("tcp-keepalive"), pool_recycle_requests: num("pool-recycle-requests"), pool_recycle_seconds: num("pool-recycle-seconds"), http1_max_buffer_bytes: num("http1-max-buffer") },
|
||||
|
||||
@@ -386,10 +386,18 @@ function render() {
|
||||
const health = el("p", t("vhHealthEmpty"), "vh-health-summary"); health.dataset.healthFor = v.id; box.append(health);
|
||||
}
|
||||
const policy = el("div", undefined, "vhost-policy-pills");
|
||||
if (v.basic_auth?.enabled) policy.append(el("small", t("authBasic"), "pill"));
|
||||
if (v.tinyauth?.enabled) policy.append(el("small", "Tinyauth", "pill"));
|
||||
if (v.authelia?.enabled) policy.append(el("small", "Authelia", "pill"));
|
||||
if (v.authentik?.enabled) policy.append(el("small", "authentik", "pill"));
|
||||
if (v.authentication?.provider) {
|
||||
const providers = { basic: "authBasic", tinyauth: "authTinyauth", authelia: "authAuthelia", authentik: "authAuthentik", pocket_id: "authPocketId", custom: "authCustomOidc" };
|
||||
const provider = providers[v.authentication.provider];
|
||||
const method = v.authentication.method;
|
||||
if (provider) policy.append(el("small", method === "basic" ? t(provider) : `${t(provider)} · ${method === "oidc" ? "OIDC" : t("authForwardAuth")}`, "pill"));
|
||||
} else {
|
||||
if (v.basic_auth?.enabled) policy.append(el("small", t("authBasic"), "pill"));
|
||||
if (v.tinyauth?.enabled) policy.append(el("small", "Tinyauth", "pill"));
|
||||
if (v.authelia?.enabled) policy.append(el("small", "Authelia", "pill"));
|
||||
if (v.authentik?.enabled) policy.append(el("small", "authentik", "pill"));
|
||||
if (v.oidc?.enabled) policy.append(el("small", "OIDC", "pill"));
|
||||
}
|
||||
if (v.rate_limit?.enabled) policy.append(el("small", t("vhRateEnabled"), "pill"));
|
||||
if (v.acl_profile_id) policy.append(el("small", `${t("vhAclProfile")}: ${v.acl_profile_id}`, "pill"));
|
||||
if (v.force_ssl) policy.append(el("small", t("vhForceSsl"), "pill"));
|
||||
|
||||
Reference in new issue
Block a user