feat: move step-ca into a plugin (C4)

This commit is contained in:
LukeGus committed 2026-09-24 19:55:28 -05:00
1 parent 98d367d7e2
commit b027717c46
128 files changed
+3805 -1781

No files matched your search

-70
View File
@@ -15,80 +15,11 @@ import type {
SshConnectHost,
} from "../hosts/connect/types.js";
const STEPCA_REQUIRED_MESSAGE =
"Step CA authentication required. Please open a Terminal connection to this host first to complete browser-based authentication.";
const VAULT_REQUIRED_MESSAGE =
"Vault SSH signer authentication required. Please open a Terminal connection first.";
const AUTH_FAILED_PATTERN = /All configured authentication methods failed/i;
const stepCaProvider: SshAuthProvider = {
type: "stepca",
pluginId: "core",
labelKey: "hosts.filterAuthStepca",
needsUserInteraction: true,
supportsBackground: false,
interaction: "stepca",
prepare: async (config, host, env) => {
const { getStepCaCert } = await import("../hosts/step-ca-auth.js");
const cert = getStepCaCert(env.userId, env.hostId);
if (!cert) {
env.log("info", "No valid certificate found, requesting sign-in");
return {
status: "interaction-required",
interaction: "stepca",
message: STEPCA_REQUIRED_MESSAGE,
};
}
try {
await applyCertificateAuth(
config,
env.client,
{ privateKey: cert.privateKey, certificate: cert.sshCert },
host.username,
);
} catch (error) {
return {
status: "error",
code: "failed",
message: "Step CA authentication failed: " + getErrorMessage(error),
};
}
env.log("info", "Using cached SSH certificate");
return { status: "ready" };
},
onAuthFailed: (_host, env, context) => {
if (!AUTH_FAILED_PATTERN.test(context.error.message)) return;
void import("../hosts/step-ca-auth.js")
.then(({ invalidateStepCaCert }) =>
invalidateStepCaCert(env.userId, env.hostId),
)
.catch(() => {});
return {
status: "interaction-required",
interaction: "stepca",
message:
"Step CA authentication failed or expired. Please authenticate again.",
};
},
startInteraction: async (request) => {
const { startStepCaAuth } = await import("../hosts/step-ca-auth.js");
await startStepCaAuth(
request.userId,
request.hostId,
request.host.username,
request.socket,
request.requestOrigin,
);
},
cancelInteraction: async (request) => {
if (!request.requestId) return;
const { cancelStepCaAuth } = await import("../hosts/step-ca-auth.js");
cancelStepCaAuth(request.requestId);
},
};
function vaultProfileId(host: SshConnectHost): number | undefined {
return host.vaultProfile?.id ?? undefined;
}
@@ -192,7 +123,6 @@ export function registerLegacySshAuthProviders(): void {
if (sshRegistered) return;
sshRegistered = true;
registerSshAuthProvider(stepCaProvider);
registerSshAuthProvider(vaultProvider);
}
@@ -1,8 +1,8 @@
/**
* The 2.8 URLs under /host that something outside Termix still depends on:
* the OPKSSH redirect URI registered with identity providers. It forwards to
* the plugin route that replaced it, so with the plugin off it signs nobody
* in.
* the OPKSSH and Step CA redirect URIs registered with identity providers.
* Each forwards to the plugin route that replaced it, so with the plugin off
* it signs nobody in.
*/
import type { Request, Router } from "express";
@@ -33,4 +33,20 @@ export function registerHostAuthCompatRoutes(router: Router): void {
const rest = req.path.slice("/opkssh-callback".length);
res.redirect(307, localUrl(req, `/plugin-api/opkssh/callback${rest}`));
});
/**
* @openapi
* /host/step-ca-callback:
* get:
* summary: Step CA callback (2.8 URL)
* description: The redirect URI Step CA identity providers were set up with before 2.9. Redirects to /plugin-api/step-ca/callback with the same query, so existing provider configurations keep working.
* tags:
* - Auth
* responses:
* 307:
* description: Redirect to the step-ca plugin's callback.
*/
router.get("/step-ca-callback", (req, res) => {
res.redirect(307, localUrl(req, "/plugin-api/step-ca/callback"));
});
}
@@ -1,42 +0,0 @@
import type { Request, Response, Router } from "express";
function escapeHtml(value: string): string {
return value
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function resultPage(ok: boolean, message: string): string {
return `<!doctype html><html><head><meta charset="utf-8"><title>Termix</title>
<style>body{font-family:system-ui,sans-serif;background:#111;color:#eee;display:flex;align-items:center;justify-content:center;height:100vh;margin:0}
main{max-width:28rem;padding:2rem;border:1px solid #333;background:#181818}h1{font-size:1.1rem;margin:0 0 .5rem}p{margin:0;color:#aaa}</style></head>
<body><main><h1>${ok ? "Signed in" : "Sign-in failed"}</h1><p>${escapeHtml(message)}</p></main></body></html>`;
}
/**
* The OIDC redirect target for Step CA sign-ins. Unauthenticated on purpose:
* the browser that finishes the sign-in may not be the one running Termix,
* so the request is matched to its session by the OAuth state.
*/
export function registerHostStepCaRoutes(router: Router): void {
router.get("/step-ca-callback", async (req: Request, res: Response) => {
const { completeStepCaAuth } = await import("../../hosts/step-ca-auth.js");
const stringQuery = (name: string): string | undefined => {
const value = req.query[name];
return typeof value === "string" ? value : undefined;
};
const result = await completeStepCaAuth({
state: stringQuery("state"),
code: stringQuery("code"),
error: stringQuery("error"),
error_description: stringQuery("error_description"),
});
res
.status(result.ok ? 200 : 400)
.type("html")
.send(resultPage(result.ok, result.message));
});
}
-2
View File
@@ -46,7 +46,6 @@ import {
} from "./host-plugin-settings.js";
import { validateParentHostId } from "./host-parent-validation.js";
import { registerHostAuthCompatRoutes } from "./host-compat-routes.js";
import { registerHostStepCaRoutes } from "./host-step-ca-routes.js";
import { registerHostFolderRoutes } from "./host-folder-routes.js";
import { registerHostAutostartRoutes } from "./host-autostart-routes.js";
import { registerHostInternalRoutes } from "./host-internal-routes.js";
@@ -2661,7 +2660,6 @@ registerHostAutostartRoutes(router, {
});
registerHostAuthCompatRoutes(router);
registerHostStepCaRoutes(router);
registerHostNetworkRoutes(router, {
authenticateJWT,
@@ -1,5 +1,4 @@
import type { AuthenticatedRequest } from "../../../types/index.js";
import { getErrorMessage } from "../../utils/error-message.js";
import type { RequestHandler, Router } from "express";
import {
authLogger,
@@ -13,7 +12,6 @@ import {
AUDIT_FORWARD_URL_SETTING,
} from "../../utils/audit-forwarder.js";
import { getTelemetryEnvOverride } from "../../utils/analytics.js";
import { STEP_CA_PRIVATE_ALLOWLIST_KEY } from "../../utils/step-ca-egress.js";
import {
NOTIFICATION_PRIVATE_ALLOWLIST_KEY,
parseNotificationAllowlist,
@@ -464,7 +462,7 @@ export function registerUserSettingsRoutes(
/**
* GET/PATCH a comma-list of private hosts an outbound feature may reach.
* Shared by notifications and Step CA; each keeps its own setting key.
* One route pair per private endpoint allowlist, each with its own setting key.
*/
const registerPrivateEndpointAllowlist = (
path: string,
@@ -562,131 +560,6 @@ export function registerUserSettingsRoutes(
"notification endpoint",
);
/**
* @openapi
* /users/step-ca-private-endpoints:
* get:
* summary: Get the private hosts the Step CA certificate flow may contact (admin only)
* tags:
* - Users
* patch:
* summary: Replace that allowlist (admin only)
* tags:
* - Users
*/
registerPrivateEndpointAllowlist(
"/step-ca-private-endpoints",
STEP_CA_PRIVATE_ALLOWLIST_KEY,
"update_step_ca_private_endpoints",
"Step CA endpoint",
);
/**
* @openapi
* /users/step-ca-settings:
* get:
* summary: Step CA settings. Admins get the values; everyone else only whether it is configured.
* tags:
* - Users
* patch:
* summary: Set the Step CA URL, root fingerprint and OIDC provisioner (admin only). Empty values clear the configuration.
* tags:
* - Users
*/
router.get("/step-ca-settings", authenticateJWT, async (req, res) => {
const userId = (req as AuthenticatedRequest).userId;
try {
const { readStepCaSettings } =
await import("../../hosts/step-ca-auth.js");
const settings = await readStepCaSettings();
if (!(await getAdminActor(userId))) {
return res.json({ configured: settings !== null });
}
res.json({
configured: settings !== null,
caUrl: settings?.caUrl ?? "",
fingerprint: settings?.fingerprint ?? "",
provisioner: settings?.provisioner ?? "",
});
} catch (err) {
authLogger.error("Failed to get Step CA settings", err);
res.status(500).json({ error: "Failed to get Step CA settings" });
}
});
router.patch("/step-ca-settings", authenticateJWT, async (req, res) => {
const userId = (req as AuthenticatedRequest).userId;
try {
const actor = await getAdminActor(userId);
if (!actor) {
return res.status(403).json({ error: "Not authorized" });
}
const { caUrl, fingerprint, provisioner } = req.body ?? {};
if (
[caUrl, fingerprint, provisioner].some((v) => typeof v !== "string")
) {
return res.status(400).json({
error: "caUrl, fingerprint and provisioner must be strings",
});
}
const values = {
caUrl: caUrl.trim(),
fingerprint: fingerprint.trim(),
provisioner: provisioner.trim(),
};
const clearing =
!values.caUrl && !values.fingerprint && !values.provisioner;
if (!clearing) {
const { normalizeCaUrl, normalizeFingerprint } =
await import("../../utils/step-ca-client.js");
try {
values.caUrl = normalizeCaUrl(values.caUrl);
values.fingerprint = normalizeFingerprint(values.fingerprint);
} catch (err) {
return res.status(400).json({ error: getErrorMessage(err) });
}
if (!values.provisioner) {
return res.status(400).json({ error: "provisioner is required" });
}
}
const { STEP_CA_SETTING_KEYS } =
await import("../../hosts/step-ca-auth.js");
const settings = createCurrentSettingsRepository();
if (clearing) {
await settings.delete(STEP_CA_SETTING_KEYS.url);
await settings.delete(STEP_CA_SETTING_KEYS.fingerprint);
await settings.delete(STEP_CA_SETTING_KEYS.provisioner);
} else {
await settings.set(STEP_CA_SETTING_KEYS.url, values.caUrl);
await settings.set(
STEP_CA_SETTING_KEYS.fingerprint,
values.fingerprint,
);
await settings.set(
STEP_CA_SETTING_KEYS.provisioner,
values.provisioner,
);
}
const { ipAddress, userAgent } = getRequestMeta(req);
await logAudit({
userId,
username: actor.username ?? userId,
action: "update_step_ca_settings",
resourceType: "setting",
details: JSON.stringify({ configured: !clearing, caUrl: values.caUrl }),
ipAddress,
userAgent,
success: true,
});
res.json({ configured: !clearing, ...values });
} catch (err) {
authLogger.error("Failed to update Step CA settings", err);
res.status(500).json({ error: "Failed to update Step CA settings" });
}
});
/**
* @openapi
* /users/host-defaults:
-376
View File
@@ -1,376 +0,0 @@
import { randomBytes } from "crypto";
import type { WebSocket } from "ws";
import { sshLogger } from "../utils/logger.js";
import { getErrorMessage } from "../utils/error-message.js";
import { createCurrentSettingsRepository } from "../database/repositories/factory.js";
import { readStepCaPrivateAllowlist } from "../utils/step-ca-egress.js";
import {
stepCaRuntime,
type StepCaCallbackQuery,
type StepCaCallbackResult,
} from "./step-ca-runtime.js";
import {
buildAuthorizationUrl,
createPkce,
decodeJwtClaims,
discoverOidcEndpoints,
exchangeCodeForIdToken,
fetchRootCertificate,
findOidcProvisioner,
generateSshKeyPair,
parseSshCertificate,
signSshCertificate,
type StepCaTarget,
} from "../utils/step-ca-client.js";
/**
* Step CA (smallstep) SSH user certificates through its OIDC provisioner.
*
* The CA's HTTP API plus one OIDC redirect back to Termix. Issued
* certificates are kept in memory until they expire.
*/
export const STEP_CA_CALLBACK_PATH = "/host/step-ca-callback";
export const STEP_CA_SETTING_KEYS = {
url: "step_ca_url",
fingerprint: "step_ca_fingerprint",
provisioner: "step_ca_provisioner",
} as const;
const AUTH_TIMEOUT_MS = 5 * 60 * 1000;
const REMOTE_CALLBACK_WAIT_MS = 30_000;
const REMOTE_POLL_MS = 250;
export interface StepCaSettings {
caUrl: string;
fingerprint: string;
provisioner: string;
}
export async function readStepCaSettings(): Promise<StepCaSettings | null> {
const settings = createCurrentSettingsRepository();
const [caUrl, fingerprint, provisioner] = await Promise.all([
settings.get(STEP_CA_SETTING_KEYS.url),
settings.get(STEP_CA_SETTING_KEYS.fingerprint),
settings.get(STEP_CA_SETTING_KEYS.provisioner),
]);
if (!caUrl || !fingerprint || !provisioner) return null;
return { caUrl, fingerprint, provisioner };
}
interface StepCaAuthSession {
state: string;
userId: string;
hostId: number;
username: string;
ws: WebSocket;
target: StepCaTarget;
rootPem: string;
clientId: string;
clientSecret?: string;
tokenEndpoint: string;
redirectUri: string;
codeVerifier: string;
nonce: string;
keyPair: { publicKeyLine: string; privateKeyPem: string };
timeout: NodeJS.Timeout;
commandPoll: NodeJS.Timeout | null;
processing: boolean;
completed: boolean;
}
const sessions = new Map<string, StepCaAuthSession>();
interface IssuedCertificate {
sshCert: string;
privateKey: string;
expiresAt: number;
}
const issued = new Map<string, IssuedCertificate>();
function certKey(userId: string, hostId: number): string {
return `${userId}:${hostId}`;
}
/** The cached certificate for this user and host, while it is valid. */
export function getStepCaCert(
userId: string,
hostId: number,
): { sshCert: string; privateKey: string } | null {
const key = certKey(userId, hostId);
const cert = issued.get(key);
if (!cert) return null;
if (cert.expiresAt <= Date.now()) {
issued.delete(key);
return null;
}
return { sshCert: cert.sshCert, privateKey: cert.privateKey };
}
export function invalidateStepCaCert(userId: string, hostId: number): void {
issued.delete(certKey(userId, hostId));
}
function send(ws: WebSocket, message: object): void {
try {
ws.send(JSON.stringify(message));
} catch {
/* socket already gone */
}
}
function endSession(session: StepCaAuthSession, removeRuntime = true): void {
clearTimeout(session.timeout);
if (session.commandPoll) clearInterval(session.commandPoll);
sessions.delete(session.state);
if (removeRuntime) void stepCaRuntime.remove(session.state);
}
export async function startStepCaAuth(
userId: string,
hostId: number,
username: string,
ws: WebSocket,
requestOrigin: string,
): Promise<void> {
const settings = await readStepCaSettings();
if (!settings) {
send(ws, {
type: "stepca_config_error",
requestId: "",
error:
"Step CA is not configured. An administrator must set the CA URL, root fingerprint and OIDC provisioner under Admin Settings.",
});
return;
}
const state = randomBytes(24).toString("base64url");
try {
const target: StepCaTarget = {
caUrl: settings.caUrl,
fingerprint: settings.fingerprint,
allowedPrivateHosts: await readStepCaPrivateAllowlist(),
};
const rootPem = await fetchRootCertificate(target);
const provisioner = await findOidcProvisioner(
target,
rootPem,
settings.provisioner,
);
const endpoints = await discoverOidcEndpoints(
provisioner.configurationEndpoint,
target.allowedPrivateHosts,
);
const pkce = createPkce();
const nonce = randomBytes(16).toString("base64url");
const redirectUri = `${requestOrigin}${STEP_CA_CALLBACK_PATH}`;
const session: StepCaAuthSession = {
state,
userId,
hostId,
username,
ws,
target,
rootPem,
clientId: provisioner.clientID,
clientSecret: provisioner.clientSecret,
tokenEndpoint: endpoints.tokenEndpoint,
redirectUri,
codeVerifier: pkce.verifier,
nonce,
keyPair: generateSshKeyPair(),
processing: false,
completed: false,
commandPoll: null,
timeout: setTimeout(() => {
const current = sessions.get(state);
if (!current || current.completed || current.processing) return;
send(ws, { type: "stepca_timeout", requestId: state });
endSession(current);
}, AUTH_TIMEOUT_MS),
};
sessions.set(state, session);
await stepCaRuntime.register(state);
session.commandPoll = setInterval(() => {
if (session.processing || session.completed) return;
void stepCaRuntime.takeCommand(state).then(async (query) => {
if (!query || session.processing || session.completed) return;
session.processing = true;
const result = await finishStepCaAuth(session, query, false);
await stepCaRuntime.complete(state, result);
});
}, REMOTE_POLL_MS);
session.commandPoll.unref();
ws.once("close", () => {
const current = sessions.get(state);
if (current && !current.completed) endSession(current);
});
send(ws, {
type: "stepca_status",
requestId: state,
stage: "chooser",
label: "Step CA",
url: buildAuthorizationUrl({
authorizationEndpoint: endpoints.authorizationEndpoint,
clientId: provisioner.clientID,
redirectUri,
state,
nonce,
codeChallenge: pkce.challenge,
}),
providers: [],
});
} catch (error) {
sshLogger.error("Failed to start Step CA authentication", error, {
operation: "step_ca_start_error",
userId,
hostId,
});
send(ws, {
type: "stepca_error",
requestId: state,
error: `Step CA: ${getErrorMessage(error)}`,
});
}
}
export function cancelStepCaAuth(requestId: string): boolean {
const session = sessions.get(requestId);
if (!session) return false;
endSession(session);
return true;
}
/**
* Finishes the flow once the identity provider redirects back: exchanges
* the code, has the CA sign the key, caches the certificate and tells the
* terminal to reconnect.
*/
export async function completeStepCaAuth(
query: StepCaCallbackQuery,
): Promise<StepCaCallbackResult> {
const session = query.state ? sessions.get(query.state) : undefined;
if (!session) {
if (!query.state || !(await stepCaRuntime.submit(query.state, query))) {
return {
ok: false,
message: "This sign-in request is no longer active.",
};
}
const deadline = Date.now() + REMOTE_CALLBACK_WAIT_MS;
while (Date.now() < deadline) {
const result = await stepCaRuntime.takeResult(query.state);
if (result) return result;
await new Promise((resolve) => setTimeout(resolve, REMOTE_POLL_MS));
}
return {
ok: false,
message: "The Termix instance handling this sign-in did not respond.",
};
}
if (session.processing || session.completed) {
return { ok: false, message: "This sign-in request was already used." };
}
session.processing = true;
return finishStepCaAuth(session, query);
}
async function finishStepCaAuth(
session: StepCaAuthSession,
query: StepCaCallbackQuery,
removeRuntime = true,
): Promise<StepCaCallbackResult> {
if (query.error || !query.code) {
const message = query.error_description || query.error || "Sign-in failed";
send(session.ws, {
type: "stepca_error",
requestId: session.state,
error: `Step CA: ${message}`,
});
endSession(session, removeRuntime);
return { ok: false, message };
}
try {
send(session.ws, {
type: "stepca_status",
requestId: session.state,
stage: "authenticating",
});
const idToken = await exchangeCodeForIdToken({
tokenEndpoint: session.tokenEndpoint,
clientId: session.clientId,
clientSecret: session.clientSecret,
code: query.code,
redirectUri: session.redirectUri,
codeVerifier: session.codeVerifier,
allowedPrivateHosts: session.target.allowedPrivateHosts,
});
const claims = decodeJwtClaims(idToken);
if (claims.nonce !== session.nonce) {
throw new Error("The identity token does not match this sign-in");
}
const email = typeof claims.email === "string" ? claims.email : undefined;
const certificate = await signSshCertificate(
session.target,
session.rootPem,
{
publicKeyLine: session.keyPair.publicKeyLine,
ott: idToken,
principals: [session.username],
keyId: email ?? session.username,
},
);
const certificateInfo = parseSshCertificate(certificate);
if (certificateInfo.publicKeyLine !== session.keyPair.publicKeyLine) {
throw new Error("The CA returned a certificate for a different key");
}
if (!certificateInfo.principals.includes(session.username)) {
throw new Error("The CA certificate does not include the host username");
}
const now = Date.now();
if (
certificateInfo.validBefore.getTime() <= now ||
certificateInfo.validAfter.getTime() > now + 60_000
) {
throw new Error(
"The CA returned a certificate outside its validity window",
);
}
const expiresAt = certificateInfo.validBefore;
issued.set(certKey(session.userId, session.hostId), {
sshCert: certificate,
privateKey: session.keyPair.privateKeyPem,
expiresAt: expiresAt.getTime(),
});
session.completed = true;
send(session.ws, {
type: "stepca_completed",
requestId: session.state,
expiresAt: expiresAt.toISOString(),
});
endSession(session, removeRuntime);
return { ok: true, message: "Signed in. You can close this window." };
} catch (error) {
sshLogger.error("Step CA certificate issuance failed", error, {
operation: "step_ca_complete_error",
userId: session.userId,
hostId: session.hostId,
});
const message = getErrorMessage(error);
send(session.ws, {
type: "stepca_error",
requestId: session.state,
error: `Step CA: ${message}`,
});
endSession(session, removeRuntime);
return { ok: false, message };
}
}
-172
View File
@@ -1,172 +0,0 @@
import { createClient } from "redis";
import { sshLogger } from "../utils/logger.js";
import {
decryptSystemSecret,
encryptSystemSecret,
} from "../utils/system-secret-crypto.js";
export interface StepCaCallbackQuery {
state?: string;
code?: string;
error?: string;
error_description?: string;
}
export interface StepCaCallbackResult {
ok: boolean;
message: string;
}
const PREFIX =
process.env.TERMIX_STEP_CA_REDIS_PREFIX?.trim() || "termix:step-ca";
const SESSION_TTL_SECONDS = 5 * 60;
const RESULT_TTL_SECONDS = 60;
const CONNECT_RETRY_MS = 15_000;
export class StepCaRuntime {
private client: ReturnType<typeof createClient> | null = null;
private connecting: Promise<boolean> | null = null;
private nextConnectAttempt = 0;
async register(state: string): Promise<void> {
if (!(await this.ensureConnected()) || !this.client) return;
await this.client
.set(this.routeKey(state), "active", { EX: SESSION_TTL_SECONDS })
.catch((error) => this.logFailure("register", error));
}
async submit(state: string, query: StepCaCallbackQuery): Promise<boolean> {
if (!(await this.ensureConnected()) || !this.client) return false;
try {
if (!(await this.client.exists(this.routeKey(state)))) return false;
const encrypted = await encryptSystemSecret(JSON.stringify(query));
const stored = await this.client.set(this.commandKey(state), encrypted, {
EX: SESSION_TTL_SECONDS,
NX: true,
});
return stored === "OK";
} catch (error) {
this.logFailure("submit", error);
return false;
}
}
async takeCommand(state: string): Promise<StepCaCallbackQuery | null> {
if (!(await this.ensureConnected()) || !this.client) return null;
try {
const encrypted = await this.client.getDel(this.commandKey(state));
if (!encrypted) return null;
return this.decode<StepCaCallbackQuery>(
await decryptSystemSecret(encrypted.toString()),
);
} catch (error) {
this.logFailure("take_command", error);
return null;
}
}
async complete(state: string, result: StepCaCallbackResult): Promise<void> {
if (!(await this.ensureConnected()) || !this.client) return;
try {
const encrypted = await encryptSystemSecret(JSON.stringify(result));
await this.client
.multi()
.set(this.resultKey(state), encrypted, { EX: RESULT_TTL_SECONDS })
.del(this.routeKey(state))
.del(this.commandKey(state))
.exec();
} catch (error) {
this.logFailure("complete", error);
}
}
async takeResult(state: string): Promise<StepCaCallbackResult | null> {
if (!(await this.ensureConnected()) || !this.client) return null;
try {
const encrypted = await this.client.getDel(this.resultKey(state));
if (!encrypted) return null;
return this.decode<StepCaCallbackResult>(
await decryptSystemSecret(encrypted.toString()),
);
} catch (error) {
this.logFailure("take_result", error);
return null;
}
}
async remove(state: string): Promise<void> {
if (!(await this.ensureConnected()) || !this.client) return;
await this.client
.del([
this.routeKey(state),
this.commandKey(state),
this.resultKey(state),
])
.catch((error) => this.logFailure("remove", error));
}
async close(): Promise<void> {
if (this.client?.isOpen) await this.client.quit();
this.client = null;
}
private async ensureConnected(): Promise<boolean> {
const url = process.env.REDIS_URL?.trim();
if (!url) return false;
if (this.client?.isReady) return true;
if (Date.now() < this.nextConnectAttempt) return false;
if (this.connecting) return this.connecting;
this.connecting = this.connect(url).finally(() => {
this.connecting = null;
});
return this.connecting;
}
private async connect(url: string): Promise<boolean> {
try {
this.client = createClient({
url,
socket: { connectTimeout: 1500, reconnectStrategy: false },
});
this.client.on("error", (error) => this.logFailure("client", error));
await this.client.connect();
this.nextConnectAttempt = 0;
return true;
} catch (error) {
this.nextConnectAttempt = Date.now() + CONNECT_RETRY_MS;
this.logFailure("connect", error);
await this.client?.disconnect().catch(() => {});
this.client = null;
return false;
}
}
private routeKey(state: string): string {
return `${PREFIX}:route:${state}`;
}
private commandKey(state: string): string {
return `${PREFIX}:command:${state}`;
}
private resultKey(state: string): string {
return `${PREFIX}:result:${state}`;
}
private decode<T>(raw: string): T | null {
try {
return JSON.parse(raw) as T;
} catch {
return null;
}
}
private logFailure(operation: string, error: unknown): void {
sshLogger.warn("Step CA Redis runtime unavailable", {
operation: `step_ca_redis_${operation}`,
error: error instanceof Error ? error.message : String(error),
});
}
}
export const stepCaRuntime = new StepCaRuntime();
+6
View File
@@ -147,6 +147,12 @@ export function createPluginFetch(deps: Deps): PluginFetch {
signal: controller.signal,
},
(init.allowPrivateHosts ?? []).map((host) => host.toLowerCase()),
{
...(init.tls?.ca ? { ca: init.tls.ca } : {}),
...(init.tls?.rejectUnauthorized === false
? { rejectUnauthorized: false }
: {}),
},
);
} finally {
clearTimeout(timer);
+4
View File
@@ -377,6 +377,10 @@ async function provisionLocalDesktopUserIfNeeded(): Promise<void> {
await import("./utils/crypto-migration/warpgate-settings-migration.js");
await runWarpgateSettingsMigration();
const { runStepCaSettingsMigration } =
await import("./utils/crypto-migration/step-ca-settings-migration.js");
await runStepCaSettingsMigration();
const { runSecretSourcesTokenMigration } =
await import("./utils/crypto-migration/secret-sources-token-migration.js");
await runSecretSourcesTokenMigration();
@@ -1,5 +1,5 @@
/**
* The OPKSSH redirect URI identity providers were registered with before
* The OPKSSH and Step CA redirect URIs identity providers were registered with before
* 2.9 must keep reaching the plugin's callback, query intact.
*/
@@ -49,3 +49,16 @@ describe("the old OPKSSH callback", () => {
);
});
});
describe("the old Step CA callback", () => {
it("forwards to the plugin's callback with the query", async () => {
const response = await fetch(
`${base}/host/step-ca-callback?code=abc&state=xyz`,
{ redirect: "manual" },
);
expect(response.status).toBe(307);
expect(response.headers.get("location")).toBe(
"/plugin-api/step-ca/callback?code=abc&state=xyz",
);
});
});
@@ -18,8 +18,6 @@ vi.mock("ssh2", async (importOriginal) => {
const mocks = vi.hoisted(() => ({
verifier: () => {},
createHostVerifier: vi.fn(),
getStepCaCert: vi.fn(),
invalidateStepCaCert: vi.fn(),
applyCertificateAuth: vi.fn(),
getVaultCert: vi.fn(),
deleteVaultCert: vi.fn(),
@@ -40,10 +38,6 @@ vi.mock("../../../hosts/host-key-verifier.js", () => ({
createHostVerifier: mocks.createHostVerifier,
},
}));
vi.mock("../../../hosts/step-ca-auth.js", () => ({
getStepCaCert: mocks.getStepCaCert,
invalidateStepCaCert: mocks.invalidateStepCaCert,
}));
vi.mock("@termix/plugin-sdk/ssh-certs", () => ({
applyCertificateAuth: mocks.applyCertificateAuth,
}));
@@ -276,25 +270,20 @@ describe("buildConnectConfig per auth type", () => {
// OPKSSH is a plugin-registered SSH auth provider now: see
// plugins/opkssh/tests/backend/provider.test.ts.
it("stepca without a cached certificate asks for a sign-in", async () => {
mocks.getStepCaCert.mockReturnValueOnce(null);
const { outcome } = await build(host({ authType: "stepca" }));
expect(outcome).toMatchObject({
status: "interaction-required",
interaction: "stepca",
});
expect(mocks.getStepCaCert).toHaveBeenCalledWith("user-1", 7);
});
// Step CA is the step-ca plugin's provider: see
// plugins/step-ca/tests/backend/step-ca.test.ts.
it("stepca uses the cached certificate", async () => {
mocks.getStepCaCert.mockReturnValueOnce({
privateKey: "k",
sshCert: "c",
it("a stepca host without the step-ca plugin names it", async () => {
setSshAuthTypeOwnerSource(() => [
{ type: "stepca", pluginId: "step-ca", pluginName: "Step CA" },
]);
const { outcome } = await build(host({ authType: "stepca" }));
expect(outcome).toEqual({
status: "error",
code: "provider-missing",
message: "This host uses stepca, which needs the Step CA plugin",
});
const { config, outcome } = await build(host({ authType: "stepca" }));
expect(outcome.status).toBe("ready");
expect(config.authHandler).toBe("cert-handler");
expect(mocks.applyCertificateAuth).toHaveBeenCalledOnce();
setSshAuthTypeOwnerSource(() => []);
});
it("vault needs a profile, then a cached certificate", async () => {
@@ -465,18 +454,20 @@ describe("connectHost", () => {
});
it("throws the auth outcome instead of connecting", async () => {
mocks.getStepCaCert.mockReturnValue(null);
const attempt = connectHost(host({ authType: "stepca" }), {
mocks.getVaultCert.mockResolvedValue(null);
const vaultHost = () =>
host({ authType: "vault", vaultProfile: { id: 3 } });
const attempt = connectHost(vaultHost(), {
userId: "user-1",
purpose: "tmux",
});
await expect(attempt).rejects.toBeInstanceOf(SshConnectError);
await expect(
connectHost(host({ authType: "stepca" }), {
connectHost(vaultHost(), {
userId: "user-1",
purpose: "tmux",
}).catch((error) => error.code),
).resolves.toBe("stepca-required");
).resolves.toBe("vault-required");
});
it("goes through the jump chain and forwards to the target", async () => {
@@ -628,18 +619,18 @@ describe("connectHost", () => {
it("lets the provider react to an auth failure", async () => {
FakeSshClient.nextBehaviour = ["auth-fail"];
mocks.getStepCaCert.mockReturnValueOnce({
mocks.getVaultCert.mockResolvedValueOnce({
privateKey: "k",
sshCert: "c",
});
await expect(
connectHost(host({ authType: "stepca" }), {
connectHost(host({ authType: "vault", vaultProfile: { id: 3 } }), {
userId: "user-1",
purpose: "fleet",
}),
).rejects.toThrow(/All configured authentication methods failed/);
await vi.waitFor(() =>
expect(mocks.invalidateStepCaCert).toHaveBeenCalledWith("user-1", 7),
expect(mocks.deleteVaultCert).toHaveBeenCalledWith("user-1", 3),
);
});
@@ -1,184 +0,0 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { WebSocket } from "ws";
const state = vi.hoisted(() => ({
sent: [] as Array<Record<string, unknown>>,
parseInfo: {
publicKeyLine: "ssh-ed25519 TESTKEY",
principals: ["alice"],
validAfter: new Date(Date.now() - 60_000),
validBefore: new Date(Date.now() + 60 * 60_000),
},
idToken: "",
command: null as Record<string, string> | null,
remoteResult: null as { ok: boolean; message: string } | null,
runtimeComplete: vi.fn(async () => undefined),
}));
vi.mock("../../utils/logger.js", () => ({
sshLogger: { error: vi.fn(), warn: vi.fn(), info: vi.fn() },
}));
vi.mock("../../database/repositories/factory.js", () => ({
createCurrentSettingsRepository: () => ({
get: async (key: string) =>
({
step_ca_url: "https://ca.example",
step_ca_fingerprint: "a".repeat(64),
step_ca_provisioner: "oidc",
})[key],
}),
}));
vi.mock("../../utils/step-ca-egress.js", () => ({
readStepCaPrivateAllowlist: async () => [],
}));
vi.mock("../../utils/step-ca-client.js", () => ({
fetchRootCertificate: async () => "ROOT",
findOidcProvisioner: async () => ({
clientID: "client",
configurationEndpoint: "https://idp.example/.well-known/openid",
}),
discoverOidcEndpoints: async () => ({
authorizationEndpoint: "https://idp.example/auth",
tokenEndpoint: "https://idp.example/token",
}),
createPkce: () => ({ verifier: "verifier", challenge: "challenge" }),
generateSshKeyPair: () => ({
publicKeyLine: "ssh-ed25519 TESTKEY",
privateKeyPem: "PRIVATE",
}),
buildAuthorizationUrl: (input: Record<string, string>) => {
const url = new URL("https://idp.example/auth");
url.searchParams.set("state", input.state);
url.searchParams.set("nonce", input.nonce);
return url.toString();
},
exchangeCodeForIdToken: async () => state.idToken,
decodeJwtClaims: (token: string) => {
const payload = token.split(".")[1];
return payload
? JSON.parse(Buffer.from(payload, "base64url").toString("utf8"))
: {};
},
signSshCertificate: async () => "CERT",
parseSshCertificate: () => state.parseInfo,
}));
vi.mock("../../hosts/step-ca-runtime.js", () => ({
stepCaRuntime: {
register: vi.fn(async () => undefined),
remove: vi.fn(async () => undefined),
submit: vi.fn(async () => true),
takeCommand: vi.fn(async () => {
const command = state.command;
state.command = null;
return command;
}),
complete: state.runtimeComplete,
takeResult: vi.fn(async () => {
const result = state.remoteResult;
state.remoteResult = null;
return result;
}),
},
}));
const {
cancelStepCaAuth,
completeStepCaAuth,
getStepCaCert,
invalidateStepCaCert,
startStepCaAuth,
} = await import("../../hosts/step-ca-auth.js");
function jwt(payload: object): string {
return `x.${Buffer.from(JSON.stringify(payload)).toString("base64url")}.y`;
}
function fakeWs(): WebSocket {
return {
send: (raw: string) => state.sent.push(JSON.parse(raw)),
once: vi.fn(),
} as unknown as WebSocket;
}
async function start(): Promise<{ requestId: string; nonce: string }> {
await startStepCaAuth(
"user-1",
7,
"alice",
fakeWs(),
"https://termix.example",
);
const chooser = state.sent.at(-1)!;
const url = new URL(String(chooser.url));
return {
requestId: String(chooser.requestId),
nonce: url.searchParams.get("nonce")!,
};
}
beforeEach(() => {
state.sent.length = 0;
invalidateStepCaCert("user-1", 7);
state.runtimeComplete.mockClear();
state.command = null;
state.remoteResult = null;
state.parseInfo = {
publicKeyLine: "ssh-ed25519 TESTKEY",
principals: ["alice"],
validAfter: new Date(Date.now() - 60_000),
validBefore: new Date(Date.now() + 60 * 60_000),
};
});
describe("Step CA authentication", () => {
it("stores only a certificate bound to the nonce, key, principal and validity window", async () => {
const { requestId, nonce } = await start();
state.idToken = jwt({ nonce, email: "alice@example.com" });
await expect(
completeStepCaAuth({ state: requestId, code: "code" }),
).resolves.toEqual({
ok: true,
message: "Signed in. You can close this window.",
});
expect(getStepCaCert("user-1", 7)).toEqual({
sshCert: "CERT",
privateKey: "PRIVATE",
});
expect(state.sent.at(-1)).toMatchObject({ type: "stepca_completed" });
});
it("rejects a token without the requested nonce", async () => {
const { requestId } = await start();
state.idToken = jwt({ email: "alice@example.com" });
const result = await completeStepCaAuth({ state: requestId, code: "code" });
expect(result.ok).toBe(false);
expect(result.message).toMatch(/does not match/);
expect(getStepCaCert("user-1", 7)).toBeNull();
});
it("routes a callback through Redis to the instance holding the WebSocket", async () => {
const { requestId, nonce } = await start();
state.idToken = jwt({ nonce });
state.command = { state: requestId, code: "remote-code" };
await vi.waitFor(() => expect(state.runtimeComplete).toHaveBeenCalled(), {
timeout: 2000,
});
expect(getStepCaCert("user-1", 7)).not.toBeNull();
});
it("returns a result produced by another instance", async () => {
state.remoteResult = { ok: true, message: "remote success" };
await expect(
completeStepCaAuth({ state: "remote-state", code: "code" }),
).resolves.toEqual({ ok: true, message: "remote success" });
});
it("cancels local sessions", async () => {
const { requestId } = await start();
expect(cancelStepCaAuth(requestId)).toBe(true);
expect(cancelStepCaAuth(requestId)).toBe(false);
});
});
@@ -23,6 +23,7 @@ const state = vi.hoisted(() => ({
delivered: [] as Array<{ channelId: number; title: string }>,
failChannel: null as number | null,
fetchCalls: [] as Array<{ url: string; allowlist: readonly string[] }>,
lastTls: null as Record<string, unknown> | null,
lastSignal: null as AbortSignal | null,
}));
@@ -61,7 +62,9 @@ vi.mock("../../utils/safe-outbound-fetch.js", () => ({
url: string,
init: RequestInit,
allowlist: readonly string[],
tls: Record<string, unknown> = {},
) => {
state.lastTls = tls;
state.lastSignal = init.signal ?? null;
state.fetchCalls.push({ url, allowlist });
return new Response("ok", { status: 200 });
@@ -184,6 +187,18 @@ describe("ctx.fetch", () => {
]);
});
it("passes a pinned CA and the fingerprint bootstrap flag to the guard", async () => {
const ctx = contextFor(["network:outbound"]);
await ctx.fetch("https://ca.lan/root/abc", {
tls: { rejectUnauthorized: false },
});
expect(state.lastTls).toEqual({ rejectUnauthorized: false });
await ctx.fetch("https://ca.lan/provisioners", { tls: { ca: "PEM" } });
expect(state.lastTls).toEqual({ ca: "PEM" });
await ctx.fetch("https://example.com");
expect(state.lastTls).toEqual({});
});
it("aborts the request, and a streamed body, when the caller's signal fires", async () => {
const ctx = contextFor(["network:outbound"]);
const controller = new AbortController();
@@ -1,103 +0,0 @@
import { describe, expect, it } from "vitest";
import crypto from "crypto";
import {
buildAuthorizationUrl,
certificateFingerprint,
createPkce,
decodeJwtClaims,
generateSshKeyPair,
normalizeCaUrl,
normalizeFingerprint,
parseSshCertificate,
} from "../../utils/step-ca-client.js";
import {
generateCa,
signUserCertificate,
} from "../../database/routes/ssh-certificate.js";
describe("step-ca client helpers", () => {
it("normalizes the CA url and fingerprint the way step does", () => {
expect(normalizeCaUrl(" https://ca.internal:9000/ ")).toBe(
"https://ca.internal:9000",
);
expect(() => normalizeCaUrl("http://ca.internal")).toThrow(/https/);
const fp = "AB:cd".repeat(16).replace(/:/g, "") + "";
expect(normalizeFingerprint("AB:cd".repeat(16))).toBe(fp.toLowerCase());
expect(() => normalizeFingerprint("abcd")).toThrow(/SHA-256/);
});
it("fingerprints a PEM certificate by the sha256 of its DER", () => {
const der = crypto.randomBytes(64);
const pem = `-----BEGIN CERTIFICATE-----\n${der.toString("base64")}\n-----END CERTIFICATE-----\n`;
expect(certificateFingerprint(pem)).toBe(
crypto.createHash("sha256").update(der).digest("hex"),
);
});
it("builds a PKCE authorization request", () => {
const { verifier, challenge } = createPkce();
expect(challenge).toBe(
crypto.createHash("sha256").update(verifier).digest("base64url"),
);
const url = new URL(
buildAuthorizationUrl({
authorizationEndpoint: "https://idp.example/auth?tenant=x",
clientId: "cid",
redirectUri: "https://termix.example/callback",
state: "s",
nonce: "n",
codeChallenge: challenge,
}),
);
expect(url.searchParams.get("tenant")).toBe("x");
expect(url.searchParams.get("response_type")).toBe("code");
expect(url.searchParams.get("code_challenge_method")).toBe("S256");
expect(url.searchParams.get("scope")).toContain("openid");
});
it("generates an ed25519 key whose public line a CA can certify, and reads the cert back", () => {
const { publicKeyLine, privateKeyPem } = generateSshKeyPair();
expect(publicKeyLine).toMatch(/^ssh-ed25519 [A-Za-z0-9+/=]+$/);
expect(privateKeyPem).toContain("BEGIN PRIVATE KEY");
const ca = generateCa();
const cert = signUserCertificate({
userPublicKeyLine: publicKeyLine,
caPrivateKeyPem: ca.privateKeyPem,
caPublicKeyLine: ca.publicKeyLine,
keyId: "alice@example",
principals: ["alice", "ops"],
validAfter: 1_700_000_000,
validBefore: 1_700_057_600,
});
expect(cert).not.toBeNull();
const info = parseSshCertificate(cert!);
expect(info).toMatchObject({
keyType: "ssh-ed25519-cert-v01@openssh.com",
publicKeyLine,
keyId: "alice@example",
principals: ["alice", "ops"],
});
expect(info.validAfter.toISOString()).toBe("2023-11-14T22:13:20.000Z");
expect(info.validBefore.getTime() - info.validAfter.getTime()).toBe(
16 * 3600 * 1000,
);
expect(() => parseSshCertificate(publicKeyLine)).toThrow(/certificate/);
expect(() =>
parseSshCertificate("ssh-ed25519-cert-v01@openssh.com AAAA"),
).toThrow(/certificate/);
});
});
describe("decodeJwtClaims", () => {
it("reads the payload without verifying and tolerates junk", () => {
const payload = Buffer.from(
JSON.stringify({ email: "a@b.c", nonce: "n1" }),
).toString("base64url");
expect(decodeJwtClaims(`x.${payload}.y`)).toEqual({
email: "a@b.c",
nonce: "n1",
});
expect(decodeJwtClaims("not-a-jwt")).toEqual({});
});
});
@@ -0,0 +1,116 @@
/**
* The Step CA settings migration: a 2.8 CA configuration and its private
* endpoint allowlist come through into the step-ca plugin's admin settings,
* the old redirect URI stays in use, and a second run changes nothing.
*/
import { beforeEach, describe, expect, it, vi } from "vitest";
const state = vi.hoisted(() => ({
legacy: {} as Record<string, string>,
pluginRows: [] as Array<{ key: string; value: string | null }>,
pluginInstalled: true,
}));
vi.mock("../../database/repositories/factory.js", () => ({
createCurrentPluginRepository: () => ({
findById: async (id: string) =>
state.pluginInstalled && id === "step-ca" ? { id } : null,
}),
createCurrentSettingsRepository: () => ({
get: async (key: string) => state.legacy[key] ?? null,
}),
createCurrentPluginSettingsRepository: () => ({
get: async (
_pluginId: string,
_scope: string,
_scopeId: string | null,
key: string,
) => state.pluginRows.find((row) => row.key === key) ?? null,
set: async (
_pluginId: string,
_scope: string,
_scopeId: string | null,
key: string,
value: string | null,
) => {
state.pluginRows = state.pluginRows.filter((row) => row.key !== key);
state.pluginRows.push({ key, value });
},
}),
}));
vi.mock("../../utils/logger.js", () => ({
databaseLogger: { info: vi.fn(), warn: vi.fn() },
}));
import { runStepCaSettingsMigration } from "../../utils/crypto-migration/step-ca-settings-migration.js";
const value = (key: string) => {
const row = state.pluginRows.find((candidate) => candidate.key === key);
return row?.value == null ? undefined : JSON.parse(row.value);
};
beforeEach(() => {
state.legacy = {
step_ca_url: "https://ca.internal:9000",
step_ca_fingerprint: "a".repeat(64),
step_ca_provisioner: "oidc",
step_ca_private_endpoint_allowlist: JSON.stringify([
"ca.internal",
"SSO.internal",
]),
};
state.pluginRows = [];
state.pluginInstalled = true;
});
describe("runStepCaSettingsMigration", () => {
it("moves the 2.8 settings and keeps the old redirect URI", async () => {
const result = await runStepCaSettingsMigration();
expect(result.moved).toEqual([
"step_ca_url",
"step_ca_fingerprint",
"step_ca_provisioner",
"step_ca_private_endpoint_allowlist",
]);
expect(result.legacyCallback).toBe(true);
expect(value("caUrl")).toBe("https://ca.internal:9000");
expect(value("fingerprint")).toBe("a".repeat(64));
expect(value("provisioner")).toBe("oidc");
expect(value("privateEndpoints")).toBe("ca.internal, sso.internal");
expect(value("legacyCallback")).toBe(true);
});
it("changes nothing the second time", async () => {
await runStepCaSettingsMigration();
const before = JSON.stringify(state.pluginRows);
const again = await runStepCaSettingsMigration();
expect(again).toEqual({ moved: [], legacyCallback: false });
expect(JSON.stringify(state.pluginRows)).toBe(before);
});
it("never overwrites a value set in the plugin", async () => {
state.pluginRows = [
{ key: "caUrl", value: JSON.stringify("https://new.ca") },
{ key: "legacyCallback", value: JSON.stringify(false) },
];
await runStepCaSettingsMigration();
expect(value("caUrl")).toBe("https://new.ca");
expect(value("legacyCallback")).toBe(false);
expect(value("provisioner")).toBe("oidc");
});
it("leaves an install that never used Step CA alone", async () => {
state.legacy = {};
const result = await runStepCaSettingsMigration();
expect(result).toEqual({ moved: [], legacyCallback: false });
expect(state.pluginRows).toEqual([]);
});
it("does nothing until the plugin row exists", async () => {
state.pluginInstalled = false;
await runStepCaSettingsMigration();
expect(state.pluginRows).toEqual([]);
});
});
@@ -0,0 +1,110 @@
/**
* Moves the 2.8 Step CA configuration into the step-ca plugin.
*
* The CA URL, root fingerprint, OIDC provisioner and private endpoint
* allowlist lived in core `settings` rows. They become the plugin's admin
* settings. An install that had a CA configured keeps sending the pre-2.9
* redirect URI (/host/step-ca-callback), because that is the one registered
* with its identity provider: the plugin's legacyCallback setting is turned
* on.
*
* Idempotent: a value already present in plugin_settings is never
* overwritten. The old rows stay for one release so a downgrade still works.
*/
import { databaseLogger } from "../logger.js";
import {
createCurrentPluginRepository,
createCurrentPluginSettingsRepository,
createCurrentSettingsRepository,
} from "../../database/repositories/factory.js";
import { parseNotificationAllowlist } from "../notification-egress.js";
const PLUGIN_ID = "step-ca";
const MOVES: { legacyKey: string; field: string }[] = [
{ legacyKey: "step_ca_url", field: "caUrl" },
{ legacyKey: "step_ca_fingerprint", field: "fingerprint" },
{ legacyKey: "step_ca_provisioner", field: "provisioner" },
];
const LEGACY_ALLOWLIST_KEY = "step_ca_private_endpoint_allowlist";
export interface StepCaSettingsMigrationResult {
moved: string[];
legacyCallback: boolean;
}
export async function runStepCaSettingsMigration(): Promise<StepCaSettingsMigrationResult> {
const result: StepCaSettingsMigrationResult = {
moved: [],
legacyCallback: false,
};
try {
// plugin_settings has a foreign key to plugins: nothing to move into yet.
const plugin = await createCurrentPluginRepository().findById(PLUGIN_ID);
if (!plugin) return result;
const settings = createCurrentSettingsRepository();
const pluginSettings = createCurrentPluginSettingsRepository();
const writeIfUnset = async (field: string, value: unknown) => {
const existing = await pluginSettings.get(
PLUGIN_ID,
"admin",
null,
field,
);
if (existing && existing.value !== null) return false;
await pluginSettings.set(
PLUGIN_ID,
"admin",
null,
field,
JSON.stringify(value),
);
return true;
};
for (const move of MOVES) {
const value = (await settings.get(move.legacyKey))?.trim();
if (!value) continue;
if (await writeIfUnset(move.field, value)) {
result.moved.push(move.legacyKey);
}
}
const hosts = parseNotificationAllowlist(
await settings.get(LEGACY_ALLOWLIST_KEY),
);
if (
hosts.length > 0 &&
(await writeIfUnset("privateEndpoints", hosts.join(", ")))
) {
result.moved.push(LEGACY_ALLOWLIST_KEY);
}
if ((await settings.get("step_ca_url"))?.trim()) {
result.legacyCallback = await writeIfUnset("legacyCallback", true);
}
if (result.moved.length > 0) {
databaseLogger.info(
`Moved ${result.moved.length} Step CA setting(s) into plugin settings`,
{
operation: "step_ca_settings_migration",
moved: result.moved.join(", "),
},
);
}
} catch (error) {
// Must not stop the backend: the plugin then reports "not configured".
databaseLogger.warn("Step CA settings migration failed", {
operation: "step_ca_settings_migration",
error: error instanceof Error ? error.message : String(error),
});
}
return result;
}
-394
View File
@@ -1,394 +0,0 @@
import crypto from "crypto";
import { safeOutboundFetch } from "./safe-outbound-fetch.js";
/**
* A minimal client for smallstep's step-ca SSH user-certificate flow, done
* over its HTTP API rather than the `step` binary:
*
* 1. bootstrap the CA's root certificate by fingerprint (GET /root/{fp})
* 2. read the OIDC provisioner's client settings (GET /provisioners)
* 3. run the OIDC authorization-code flow against the provider
* 4. POST the id_token as the one-time token to /1.0/ssh/sign
*/
export interface StepCaTarget {
caUrl: string;
fingerprint: string;
/** Hosts the SSRF guard may reach even when they resolve to private ranges. */
allowedPrivateHosts: readonly string[];
}
export interface StepCaOidcProvisioner {
name: string;
clientID: string;
clientSecret?: string;
configurationEndpoint: string;
}
export interface OidcEndpoints {
authorizationEndpoint: string;
tokenEndpoint: string;
}
const FETCH_TIMEOUT_MS = 15_000;
/** Display-only claims; the CA is the component that verifies the token. */
export function decodeJwtClaims(token: string): Record<string, unknown> {
const payload = token.split(".")[1];
if (!payload) return {};
try {
return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
} catch {
return {};
}
}
export function normalizeCaUrl(raw: string): string {
const url = new URL(raw.trim());
if (url.protocol !== "https:") {
throw new Error("Step CA URL must use https");
}
return url.toString().replace(/\/+$/, "");
}
export function normalizeFingerprint(raw: string): string {
const hex = raw.replace(/[^0-9a-fA-F]/g, "").toLowerCase();
if (hex.length !== 64) {
throw new Error("CA fingerprint must be a SHA-256 hex digest");
}
return hex;
}
export function pemToDer(pem: string): Buffer {
const body = pem
.replace(/-----BEGIN [^-]+-----/g, "")
.replace(/-----END [^-]+-----/g, "")
.replace(/\s+/g, "");
return Buffer.from(body, "base64");
}
export function certificateFingerprint(pem: string): string {
return crypto.createHash("sha256").update(pemToDer(pem)).digest("hex");
}
async function readJson<T>(response: Response, what: string): Promise<T> {
if (!response.ok) {
throw new Error(`${what} failed: HTTP ${response.status}`);
}
return (await response.json()) as T;
}
/**
* The root endpoint is served under the CA's own TLS certificate, which
* nothing trusts yet - so this one request skips verification and trusts
* the fingerprint instead, exactly like `step ca bootstrap`.
*/
export async function fetchRootCertificate(
target: StepCaTarget,
): Promise<string> {
const fingerprint = normalizeFingerprint(target.fingerprint);
const response = await safeOutboundFetch(
`${normalizeCaUrl(target.caUrl)}/root/${fingerprint}`,
{ method: "GET", signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) },
target.allowedPrivateHosts,
{ rejectUnauthorized: false },
);
const { ca } = await readJson<{ ca?: string }>(
response,
"Fetching the CA root",
);
if (!ca || certificateFingerprint(ca) !== fingerprint) {
throw new Error("CA root certificate does not match the fingerprint");
}
return ca;
}
export async function findOidcProvisioner(
target: StepCaTarget,
rootPem: string,
name: string,
): Promise<StepCaOidcProvisioner> {
const base = normalizeCaUrl(target.caUrl);
let cursor = "";
for (let page = 0; page < 20; page++) {
const response = await safeOutboundFetch(
`${base}/provisioners?limit=100${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`,
{ method: "GET", signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) },
target.allowedPrivateHosts,
{ ca: rootPem },
);
const body = await readJson<{
provisioners?: Array<Record<string, unknown>>;
nextCursor?: string;
}>(response, "Listing CA provisioners");
const match = (body.provisioners ?? []).find(
(p) => p.name === name && p.type === "OIDC",
);
if (match) {
if (
typeof match.clientID !== "string" ||
typeof match.configurationEndpoint !== "string"
) {
throw new Error("The OIDC provisioner is missing its client settings");
}
return {
name,
clientID: match.clientID,
clientSecret:
typeof match.clientSecret === "string"
? match.clientSecret
: undefined,
configurationEndpoint: match.configurationEndpoint,
};
}
if (!body.nextCursor) break;
cursor = body.nextCursor;
}
throw new Error(`OIDC provisioner "${name}" not found on the CA`);
}
export async function discoverOidcEndpoints(
configurationEndpoint: string,
allowedPrivateHosts: readonly string[],
): Promise<OidcEndpoints> {
const response = await safeOutboundFetch(
configurationEndpoint,
{ method: "GET", signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) },
allowedPrivateHosts,
);
const doc = await readJson<{
authorization_endpoint?: string;
token_endpoint?: string;
}>(response, "OIDC discovery");
if (!doc.authorization_endpoint || !doc.token_endpoint) {
throw new Error("OIDC discovery document is incomplete");
}
return {
authorizationEndpoint: doc.authorization_endpoint,
tokenEndpoint: doc.token_endpoint,
};
}
export function createPkce(): { verifier: string; challenge: string } {
const verifier = crypto.randomBytes(48).toString("base64url");
const challenge = crypto
.createHash("sha256")
.update(verifier)
.digest("base64url");
return { verifier, challenge };
}
export function buildAuthorizationUrl(input: {
authorizationEndpoint: string;
clientId: string;
redirectUri: string;
state: string;
nonce: string;
codeChallenge: string;
}): string {
const url = new URL(input.authorizationEndpoint);
url.searchParams.set("response_type", "code");
url.searchParams.set("client_id", input.clientId);
url.searchParams.set("redirect_uri", input.redirectUri);
url.searchParams.set("scope", "openid email profile");
url.searchParams.set("state", input.state);
url.searchParams.set("nonce", input.nonce);
url.searchParams.set("code_challenge", input.codeChallenge);
url.searchParams.set("code_challenge_method", "S256");
return url.toString();
}
export async function exchangeCodeForIdToken(input: {
tokenEndpoint: string;
clientId: string;
clientSecret?: string;
code: string;
redirectUri: string;
codeVerifier: string;
allowedPrivateHosts: readonly string[];
}): Promise<string> {
const form = new URLSearchParams({
grant_type: "authorization_code",
code: input.code,
redirect_uri: input.redirectUri,
client_id: input.clientId,
code_verifier: input.codeVerifier,
});
if (input.clientSecret) form.set("client_secret", input.clientSecret);
const response = await safeOutboundFetch(
input.tokenEndpoint,
{
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: form.toString(),
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
},
input.allowedPrivateHosts,
);
const body = await readJson<{ id_token?: string }>(
response,
"OIDC token exchange",
);
if (!body.id_token) {
throw new Error("The identity provider returned no id_token");
}
return body.id_token;
}
// --- SSH keys and certificates ---------------------------------------------
function sshString(value: Buffer | string): Buffer {
const data = Buffer.isBuffer(value) ? value : Buffer.from(value, "utf8");
const len = Buffer.alloc(4);
len.writeUInt32BE(data.length, 0);
return Buffer.concat([len, data]);
}
export function generateSshKeyPair(): {
publicKeyLine: string;
privateKeyPem: string;
} {
const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
const jwk = publicKey.export({ format: "jwk" }) as { x: string };
const raw = Buffer.from(jwk.x, "base64url");
const blob = Buffer.concat([sshString("ssh-ed25519"), sshString(raw)]);
return {
publicKeyLine: `ssh-ed25519 ${blob.toString("base64")}`,
privateKeyPem: privateKey
.export({ format: "pem", type: "pkcs8" })
.toString(),
};
}
export async function signSshCertificate(
target: StepCaTarget,
rootPem: string,
input: {
publicKeyLine: string;
ott: string;
principals: string[];
keyId: string;
},
): Promise<string> {
const blob = input.publicKeyLine.trim().split(/\s+/)[1];
if (!blob) throw new Error("Invalid public key line");
const response = await safeOutboundFetch(
`${normalizeCaUrl(target.caUrl)}/1.0/ssh/sign`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
publicKey: blob,
ott: input.ott,
certType: "user",
principals: input.principals,
keyID: input.keyId,
}),
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
},
target.allowedPrivateHosts,
{ ca: rootPem },
);
if (!response.ok) {
let detail = "";
try {
const err = (await response.json()) as { message?: string };
detail = err.message ? `: ${err.message}` : "";
} catch {
/* no body */
}
throw new Error(
`The CA refused to sign the key (HTTP ${response.status})${detail}`,
);
}
const body = (await response.json()) as { crt?: string };
if (!body.crt) throw new Error("The CA returned no certificate");
return body.crt.trim();
}
export interface SshCertificateInfo {
keyType: string;
publicKeyLine: string;
keyId: string;
principals: string[];
validAfter: Date;
validBefore: Date;
}
/** Reads the identity and validity window out of an OpenSSH certificate line. */
export function parseSshCertificate(line: string): SshCertificateInfo {
const blob = Buffer.from(line.trim().split(/\s+/)[1] ?? "", "base64");
if (blob.length === 0) throw new Error("Invalid SSH certificate");
let offset = 0;
const readString = (): Buffer => {
if (offset + 4 > blob.length) throw new Error("Truncated SSH certificate");
const len = blob.readUInt32BE(offset);
offset += 4;
if (offset + len > blob.length) {
throw new Error("Truncated SSH certificate");
}
const value = blob.subarray(offset, offset + len);
offset += len;
return value;
};
const readUint64 = (): bigint => {
if (offset + 8 > blob.length) throw new Error("Truncated SSH certificate");
const value = blob.readBigUInt64BE(offset);
offset += 8;
return value;
};
const keyType = readString().toString();
if (!keyType.endsWith("-cert-v01@openssh.com")) {
throw new Error("The CA returned a public key instead of a certificate");
}
readString(); // nonce
// Public key fields differ by algorithm; consume them by shape.
const publicKeyParts: Buffer[] = [];
let plainKeyType: string;
if (keyType.startsWith("ssh-rsa")) {
plainKeyType = "ssh-rsa";
publicKeyParts.push(readString(), readString()); // e, n
} else if (keyType.startsWith("ecdsa-")) {
plainKeyType = keyType.replace(/-cert-v01@openssh\.com$/, "");
publicKeyParts.push(readString(), readString()); // curve, Q
} else {
plainKeyType = keyType.replace(/-cert-v01@openssh\.com$/, "");
publicKeyParts.push(readString()); // ed25519 pk
}
const publicKeyBlob = Buffer.concat([
sshString(plainKeyType),
...publicKeyParts.map(sshString),
]);
readUint64(); // serial
offset += 4; // type
const keyId = readString().toString();
const principalsBlob = readString();
const principals: string[] = [];
for (let p = 0; p < principalsBlob.length;) {
if (p + 4 > principalsBlob.length) {
throw new Error("Invalid SSH certificate principals");
}
const len = principalsBlob.readUInt32BE(p);
if (p + 4 + len > principalsBlob.length) {
throw new Error("Invalid SSH certificate principals");
}
principals.push(principalsBlob.subarray(p + 4, p + 4 + len).toString());
p += 4 + len;
}
const validAfter = readUint64();
const validBefore = readUint64();
const toDate = (seconds: bigint) =>
new Date(
Number(seconds > 8_640_000_000_000n ? 8_640_000_000_000n : seconds) *
1000,
);
return {
keyType,
publicKeyLine: `${plainKeyType} ${publicKeyBlob.toString("base64")}`,
keyId,
principals,
validAfter: toDate(validAfter),
validBefore: toDate(validBefore),
};
}
-13
View File
@@ -1,13 +0,0 @@
import { createCurrentSettingsRepository } from "../database/repositories/factory.js";
import { parseNotificationAllowlist } from "./notification-egress.js";
/** Private hosts (a CA on the LAN, an internal IdP) the Step CA flow may reach. */
export const STEP_CA_PRIVATE_ALLOWLIST_KEY =
"step_ca_private_endpoint_allowlist";
export async function readStepCaPrivateAllowlist(): Promise<string[]> {
const raw = await createCurrentSettingsRepository().get(
STEP_CA_PRIVATE_ALLOWLIST_KEY,
);
return parseNotificationAllowlist(raw);
}
-19
View File
@@ -14,25 +14,6 @@ export async function getNotificationPrivateEndpoints(): Promise<string[]> {
}
}
export async function getStepCaPrivateEndpoints(): Promise<string[]> {
try {
return (await authApi.get("/users/step-ca-private-endpoints")).data.hosts;
} catch (error) {
throw handleApiError(error, "get Step CA endpoint allowlist");
}
}
export async function setStepCaPrivateEndpoints(
hosts: string[],
): Promise<string[]> {
try {
return (await authApi.patch("/users/step-ca-private-endpoints", { hosts }))
.data.hosts;
} catch (error) {
throw handleApiError(error, "update Step CA endpoint allowlist");
}
}
export async function setNotificationPrivateEndpoints(
hosts: string[],
): Promise<string[]> {
-33
View File
@@ -33,39 +33,6 @@ export async function getSessionTimeout(): Promise<{ timeoutHours: number }> {
}
}
export interface StepCaSettings {
configured: boolean;
caUrl: string;
fingerprint: string;
provisioner: string;
}
export async function getStepCaSettings(): Promise<StepCaSettings> {
try {
const response = await authApi.get("/users/step-ca-settings");
return {
configured: !!response.data.configured,
caUrl: response.data.caUrl ?? "",
fingerprint: response.data.fingerprint ?? "",
provisioner: response.data.provisioner ?? "",
};
} catch (error) {
handleApiError(error, "fetch Step CA settings");
}
}
export async function updateStepCaSettings(input: {
caUrl: string;
fingerprint: string;
provisioner: string;
}): Promise<void> {
try {
await authApi.patch("/users/step-ca-settings", input);
} catch (error) {
handleApiError(error, "update Step CA settings");
}
}
export async function updateSessionTimeout(
timeoutHours: number,
): Promise<void> {
-3
View File
@@ -46,7 +46,6 @@ import {
type SecondFactorRef,
} from "@/api/auth-methods-api";
import { useLoginMethods, useSecondFactors } from "@/plugin-host/auth-registry";
import { ensureLegacyAuthUI } from "./legacy-auth-ui";
import { startPreLoginPlugins } from "@/plugin-host/loader";
import { Checkbox } from "@/components/checkbox";
import { useBranding } from "@/contexts/BrandingContext";
@@ -99,8 +98,6 @@ const LANGUAGES = [
{ code: "vi", label: "Tiếng Việt" },
];
ensureLegacyAuthUI();
const STORAGE_KEY = "termix_auth";
/** An empty list means "whichever factors this screen has UI for". */
-66
View File
@@ -1,66 +0,0 @@
/**
* The certificate SSH auth editors that still live in core for 2.9.0,
* registered through the same registry a plugin uses.
* Phase C moves each into its plugin by moving its block out of this file;
* D1 deletes the file once it is empty.
*/
import { useTranslation } from "react-i18next";
import { registerSshAuthEditor } from "@/plugin-host/auth-registry";
function CertificateAuthInfo({
labelKey,
descriptionKey,
docsUrl,
}: {
labelKey: string;
descriptionKey: string;
docsUrl: string;
}) {
const { t } = useTranslation();
return (
<div className="flex flex-col gap-2 border-t border-border pt-3">
<div className="flex items-center justify-between">
<span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground">
{t(labelKey)}
</span>
<a
href={docsUrl}
target="_blank"
rel="noreferrer"
className="text-[10px] text-accent-brand hover:underline"
>
{t("hosts.docsLink")}
</a>
</div>
<p className="text-[10px] text-muted-foreground">{t(descriptionKey)}</p>
</div>
);
}
function StepCaAuthEditor() {
return (
<CertificateAuthInfo
labelKey="hosts.stepcaLabel"
descriptionKey="hosts.stepcaDesc"
docsUrl="https://smallstep.com/docs/step-ca/provisioners/#oauthoidc-single-sign-on"
/>
);
}
let registered = false;
export function ensureLegacyAuthUI(): void {
if (registered) return;
registered = true;
registerSshAuthEditor({
id: "stepca",
pluginId: "core",
titleKey: "hosts.filterAuthStepca",
component: StepCaAuthEditor,
});
}
/** Test helper. */
export function resetLegacyAuthUIForTests(): void {
registered = false;
}
-11
View File
@@ -643,8 +643,6 @@
"keyPassphraseSaved": "Passphrase saved, type to change",
"replaceKey": "Replace key",
"docsLink": "View docs",
"stepcaLabel": "Step CA",
"stepcaDesc": "Sign in through your identity provider and let your Step CA issue a short-lived SSH certificate for this host. The CA URL, root fingerprint and OIDC provisioner are set by an administrator under Admin Settings.",
"agentLabel": "SSH Agent",
"agentDesc": "Authenticate using an SSH agent running on the Termix server (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). The agent must be running on the machine where Termix is hosted.",
"agentAuthenticationDesc": "Use keys from an SSH agent to authenticate this connection. Leave the socket path empty to use SSH_AUTH_SOCK.",
@@ -1995,15 +1993,6 @@
"admin": "Administration"
}
},
"stepCa": "Step CA",
"stepCaDesc": "Issue short-lived SSH certificates from a smallstep CA. Enter the CA URL, its root fingerprint (as shown by step ca bootstrap) and the name of the OIDC provisioner; then choose \"Step CA\" as a host's authentication type. Leave all three empty to disable.",
"stepCaFingerprint": "Root fingerprint (SHA-256)",
"stepCaProvisioner": "OIDC provisioner name",
"stepCaSaved": "Step CA settings saved",
"stepCaSaveFailed": "Failed to save Step CA settings",
"stepCaPrivateEndpoints": "Allowed private Step CA hosts",
"stepCaPrivateEndpointsDesc": "Private hosts the Step CA certificate flow may contact: the CA itself and, if internal, your identity provider. Separate them with commas.",
"updateStepCaEndpointsFailed": "Failed to update the Step CA endpoint allowlist",
"notificationPrivateEndpoints": "Allowed private notification hosts",
"notificationPrivateEndpointsDesc": "Exact private hosts that notification channels may contact. Separate them with commas.",
"updateNotificationEndpointsFailed": "Failed to update notification endpoint allowlist",
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Wagwoordfrase gestoor, tik om te verander",
"replaceKey": "Vervang sleutel",
"docsLink": "Bekyk dokumente",
"stepcaLabel": "Stap CA",
"stepcaDesc": "Meld aan deur jou identiteitsverskaffer en laat jou Step CA 'n kortstondige SSH-sertifikaat vir hierdie gasheer uitreik. Die CA-URL, wortelvingerafdruk en OIDC-voorsiening word deur 'n administrateur onder Admin-instellings ingestel.",
"agentLabel": "SSH-agent",
"agentDesc": "Verifieer deur 'n SSH-agent te gebruik wat op die Termix-bediener loop (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Die agent moet loop op die masjien waar Termix gehuisves word.",
"agentAuthenticationDesc": "Gebruik sleutels van 'n SSH-agent om hierdie verbinding te verifieer. Los die sokpad leeg om SSH_AUTH_SOCK te gebruik.",
@@ -2121,18 +2119,9 @@
"save": "Stoor",
"saving": "Stoor..."
},
"stepCa": "Stap CA",
"stepCaDesc": "Reik kortstondige SSH-sertifikate uit vanaf 'n smallstep CA. Voer die CA-URL, die wortelvingerafdruk daarvan (soos getoon deur stap ca se selflaaistelsel) en die naam van die OIDC-voorsiener in; kies dan \"Stap CA\" as 'n gasheer se verifikasietipe. Los al drie leeg om te deaktiveer.",
"stepCaFingerprint": "Wortelvingerafdruk (SHA-256)",
"stepCaProvisioner": "OIDC-voorsieningsnaam",
"stepCaSaved": "Stap CA-instellings gestoor",
"stepCaSaveFailed": "Kon nie Stap CA-instellings stoor nie",
"secretSourcePrivateEndpoints": "Toegelate privaat geheime brongashere",
"secretSourcePrivateEndpointsDesc": "Privaat gashere wat geheime bronne (1Password Connect) mag kontak. Skei hulle met kommas.",
"updateSecretSourceEndpointsFailed": "Kon nie die geheime bron-eindpunt-toelaatlys opdateer nie",
"stepCaPrivateEndpoints": "Toegelate private Step CA-gashere",
"stepCaPrivateEndpointsDesc": "Privaat gashere, die Step CA-sertifikaatvloei, mag kontak maak met: die CA self en, indien intern, jou identiteitsverskaffer. Skei hulle met kommas.",
"updateStepCaEndpointsFailed": "Kon nie die Step CA-eindpunt-toelaatlys opdateer nie",
"notificationPrivateEndpoints": "Toegelate privaat kennisgewinggashere",
"notificationPrivateEndpointsDesc": "Presiese private gashere wat kennisgewingkanale mag kontak. Skei hulle met kommas.",
"updateNotificationEndpointsFailed": "Kon nie kennisgewing-eindpunt-toelaatlys opdateer nie"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "تم حفظ عبارة المرور، اكتب للتغيير",
"replaceKey": "استبدال المفتاح",
"docsLink": "عرض المستندات",
"stepcaLabel": "الخطوة ج",
"stepcaDesc": "سجّل الدخول عبر موفر الهوية الخاص بك، ودع جهة إصدار الشهادات المعتمدة (SA) التابعة لـ Step CA تُصدر شهادة SSH مؤقتة لهذا المضيف. يتم تحديد عنوان URL الخاص بجهة إصدار الشهادات المعتمدة، وبصمة الجذر، وموفر OIDC بواسطة مسؤول النظام ضمن إعدادات المسؤول.",
"agentLabel": "وكيل SSH",
"agentDesc": "المصادقة باستخدام وكيل SSH يعمل على خادم Termix (مثل Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). يجب أن يكون الوكيل قيد التشغيل على الجهاز الذي يستضيف Termix.",
"agentAuthenticationDesc": "استخدم مفاتيح من وكيل SSH لمصادقة هذا الاتصال. اترك مسار المقبس فارغًا لاستخدام SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "حفظ",
"saving": "جارٍ الحفظ..."
},
"stepCa": "الخطوة ج",
"stepCaDesc": "إصدار شهادات SSH قصيرة الأجل من جهة مصدقة صغيرة (Smallstep CA). أدخل عنوان URL الخاص بالجهة المصدقة، وبصمة الجذر (كما هو موضح في أمر step ca bootstrap)، واسم موفر OIDC؛ ثم اختر \"Step CA\" كنوع مصادقة المضيف. اترك الحقول الثلاثة فارغة لتعطيل هذه الميزة.",
"stepCaFingerprint": "بصمة الجذر (SHA-256)",
"stepCaProvisioner": "اسم موفر OIDC",
"stepCaSaved": "تم حفظ إعدادات CA للخطوة",
"stepCaSaveFailed": "فشل حفظ إعدادات الخطوة CA",
"secretSourcePrivateEndpoints": "مضيفو مصادر الأسرار الخاصة المسموح بهم",
"secretSourcePrivateEndpointsDesc": "المضيفون الخاصون الذين قد تتصل بهم مصادر سرية (مثل 1Password Connect). افصل بينهم بفواصل.",
"updateSecretSourceEndpointsFailed": "فشل تحديث قائمة السماح لنقاط نهاية المصدر السرية",
"stepCaPrivateEndpoints": "مضيفو شهادات الخطوة الخاصة المسموح بها",
"stepCaPrivateEndpointsDesc": "قد تتصل جهات الاستضافة الخاصة بعملية شهادة Step CA بما يلي: جهة إصدار الشهادات نفسها، وإذا كانت داخلية، مزود الهوية الخاص بك. افصل بينهما بفواصل.",
"updateStepCaEndpointsFailed": "فشل تحديث قائمة السماح لنقاط نهاية شهادة التحقق من صحة الخطوة",
"notificationPrivateEndpoints": "المضيفون المسموح لهم بالإشعارات الخاصة",
"notificationPrivateEndpointsDesc": "حدد المضيفين الخاصين الذين يمكن لقنوات الإشعارات الاتصال بهم. افصل بينهم بفواصل.",
"updateNotificationEndpointsFailed": "فشل تحديث قائمة السماح لنقاط نهاية الإشعارات"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Паролата е запазена, въведете, за да я промените",
"replaceKey": "Сменете ключа",
"docsLink": "Преглед на документи",
"stepcaLabel": "Стъпка CA",
"stepcaDesc": "Влезте чрез вашия доставчик на самоличност и позволете на вашия Step CA да издаде краткотраен SSH сертификат за този хост. URL адресът на CA, root пръстовият отпечатък и OIDC provisioner се задават от администратор в „Настройки на администратора“.",
"agentLabel": "SSH агент",
"agentDesc": "Удостоверете се, използвайки SSH агент, работещ на Termix сървъра (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Агентът трябва да работи на машината, където се хоства Termix.",
"agentAuthenticationDesc": "Използвайте ключове от SSH агент за удостоверяване на тази връзка. Оставете пътя на сокета празен, за да използвате SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Запазване",
"saving": "Запазване..."
},
"stepCa": "Стъпка CA",
"stepCaDesc": "Издавайте краткотрайни SSH сертификати от smallstep CA. Въведете URL адреса на CA, неговия root fingerprint (както е показано от step ca bootstrap) и името на OIDC provisioner; след това изберете „Step CA“ като тип удостоверяване на хоста. Оставете и трите празни, за да деактивирате.",
"stepCaFingerprint": "Коренен пръстов отпечатък (SHA-256)",
"stepCaProvisioner": "Име на доставчика на OIDC",
"stepCaSaved": "Настройките на стъпка CA са запазени",
"stepCaSaveFailed": "Запазването на настройките на стъпка CA не бе успешно",
"secretSourcePrivateEndpoints": "Разрешени частни секретни хостове от източници",
"secretSourcePrivateEndpointsDesc": "Частни хостове, с които тайни източници (1Password Connect) могат да се свържат. Разделете ги със запетаи.",
"updateSecretSourceEndpointsFailed": "Актуализирането на списъка с разрешени крайни точки на секретния източник не бе успешно",
"stepCaPrivateEndpoints": "Разрешени частни хостове на Step CA",
"stepCaPrivateEndpointsDesc": "Частни хостове, с които потокът от сертификати на Step CA може да се свърже: самия CA и, ако е вътрешен, вашият доставчик на идентичност. Разделете ги със запетаи.",
"updateStepCaEndpointsFailed": "Актуализирането на списъка с разрешени крайни точки на Step CA не бе успешно",
"notificationPrivateEndpoints": "Разрешени хостове за частни известия",
"notificationPrivateEndpointsDesc": "Точните частни хостове, с които каналите за известия могат да се свържат. Разделете ги със запетаи.",
"updateNotificationEndpointsFailed": "Актуализирането на списъка с разрешени крайни точки за известия не бе успешно"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "পাসফ্রেজ সংরক্ষিত হয়েছে, পরিবর্তন করতে টাইপ করুন",
"replaceKey": "চাবি প্রতিস্থাপন করুন",
"docsLink": "নথি দেখুন",
"stepcaLabel": "স্টেপ সিএ",
"stepcaDesc": "আপনার আইডেন্টিটি প্রোভাইডারের মাধ্যমে সাইন ইন করুন এবং আপনার স্টেপ সিএ-কে এই হোস্টের জন্য একটি স্বল্পস্থায়ী এসএসএইচ সার্টিফিকেট ইস্যু করতে দিন। সিএ ইউআরএল, রুট ফিঙ্গারপ্রিন্ট এবং ওআইডিসি প্রোভিশনার একজন অ্যাডমিনিস্ট্রেটর দ্বারা অ্যাডমিন সেটিংস-এর অধীনে সেট করা হয়।",
"agentLabel": "এসএসএইচ এজেন্ট",
"agentDesc": "Termix সার্ভারে চলমান একটি SSH এজেন্ট (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent) ব্যবহার করে প্রমাণীকরণ করুন। এজেন্টটি অবশ্যই সেই মেশিনে চালু থাকতে হবে যেখানে Termix হোস্ট করা আছে।",
"agentAuthenticationDesc": "এই সংযোগটি প্রমাণীকরণের জন্য একটি SSH এজেন্ট থেকে কী ব্যবহার করুন। SSH_AUTH_SOCK ব্যবহার করতে সকেট পাথটি খালি রাখুন।",
@@ -2121,18 +2119,9 @@
"save": "সংরক্ষণ করুন",
"saving": "সঞ্চয়..."
},
"stepCa": "স্টেপ সিএ",
"stepCaDesc": "একটি smallstep CA থেকে স্বল্পস্থায়ী SSH সার্টিফিকেট ইস্যু করুন। CA URL, এর রুট ফিঙ্গারপ্রিন্ট (যেমনটি step ca bootstrap দ্বারা দেখানো হয়েছে) এবং OIDC প্রোভিশনারের নাম লিখুন; তারপর হোস্টের অথেনটিকেশন টাইপ হিসেবে \"Step CA\" বেছে নিন। নিষ্ক্রিয় করতে তিনটিই খালি রাখুন।",
"stepCaFingerprint": "রুট ফিঙ্গারপ্রিন্ট (SHA-256)",
"stepCaProvisioner": "OIDC সরবরাহকারীর নাম",
"stepCaSaved": "ধাপ CA সেটিংস সংরক্ষিত হয়েছে",
"stepCaSaveFailed": "স্টেপ সিএ সেটিংস সংরক্ষণ করতে ব্যর্থ হয়েছে",
"secretSourcePrivateEndpoints": "অনুমোদিত ব্যক্তিগত গোপন উৎস হোস্ট",
"secretSourcePrivateEndpointsDesc": "ব্যক্তিগত হোস্ট, যাদের সাথে গোপন সূত্র (1Password Connect) যোগাযোগ করতে পারে। এগুলিকে কমা দিয়ে আলাদা করুন।",
"updateSecretSourceEndpointsFailed": "গোপন উৎস এন্ডপয়েন্ট অনুমতি তালিকা আপডেট করতে ব্যর্থ হয়েছে",
"stepCaPrivateEndpoints": "ব্যক্তিগত স্টেপ সিএ হোস্টদের অনুমতি দেওয়া হয়েছে",
"stepCaPrivateEndpointsDesc": "স্টেপ সিএ সার্টিফিকেট ফ্লো যেসব প্রাইভেট হোস্টের সাথে যোগাযোগ করতে পারে, সেগুলো হলো: স্বয়ং সিএ এবং, যদি অভ্যন্তরীণ হয়, আপনার আইডেন্টিটি প্রোভাইডার। এদেরকে কমা দিয়ে আলাদা করুন।",
"updateStepCaEndpointsFailed": "Step CA এন্ডপয়েন্ট অ্যালাওলিস্ট আপডেট করতে ব্যর্থ হয়েছে",
"notificationPrivateEndpoints": "ব্যক্তিগত বিজ্ঞপ্তি হোস্ট অনুমোদিত",
"notificationPrivateEndpointsDesc": "সুনির্দিষ্ট ব্যক্তিগত হোস্টগুলো, যাদের সাথে নোটিফিকেশন চ্যানেলগুলো যোগাযোগ করতে পারে। এগুলোকে কমা দিয়ে আলাদা করুন।",
"updateNotificationEndpointsFailed": "নোটিফিকেশন এন্ডপয়েন্ট অ্যালাওলিস্ট আপডেট করতে ব্যর্থ হয়েছে"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Contrasenya desada, escriu per canviar-la",
"replaceKey": "Substitueix la clau",
"docsLink": "Veure documents",
"stepcaLabel": "Pas CA",
"stepcaDesc": "Inicieu la sessió a través del vostre proveïdor d'identitat i deixeu que la vostra Step CA emeti un certificat SSH de curta durada per a aquest amfitrió. L'URL de la CA, l'empremta digital arrel i el proveïdor OIDC els estableix un administrador a Configuració de l'administrador.",
"agentLabel": "Agent SSH",
"agentDesc": "Autentiqueu-vos mitjançant un agent SSH que s'executi al servidor Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). L'agent ha d'estar executant-se a la màquina on s'allotja Termix.",
"agentAuthenticationDesc": "Feu servir claus d'un agent SSH per autenticar aquesta connexió. Deixeu la ruta del sòcol buida per utilitzar SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Desa",
"saving": "Desant..."
},
"stepCa": "Pas CA",
"stepCaDesc": "Emet certificats SSH de curta durada des d'una CA Smallstep. Introdueix l'URL de la CA, la seva empremta digital arrel (tal com es mostra a l'arrencada de la CA Step) i el nom del proveïdor OIDC; a continuació, tria \"Step CA\" com a tipus d'autenticació de l'amfitrió. Deixa els tres buits per desactivar-los.",
"stepCaFingerprint": "Empremta digital arrel (SHA-256)",
"stepCaProvisioner": "Nom del proveïdor OIDC",
"stepCaSaved": "S'ha desat la configuració de l'Administració de Control del Pas",
"stepCaSaveFailed": "No s'ha pogut desar la configuració de Step CA.",
"secretSourcePrivateEndpoints": "Amfitrions d'origen secret privat permesos",
"secretSourcePrivateEndpointsDesc": "Amfitrions privats amb els quals es poden posar en contacte fonts secretes (1Password Connect). Separeu-los amb comes.",
"updateSecretSourceEndpointsFailed": "No s'ha pogut actualitzar la llista de permesos del punt final de la font secreta",
"stepCaPrivateEndpoints": "Amfitrions privats permesos de Step CA",
"stepCaPrivateEndpointsDesc": "Amfitrions privats amb els quals el flux de certificats de la CA de Step pot contactar: la mateixa CA i, si és intern, el vostre proveïdor d'identitat. Separeu-los amb comes.",
"updateStepCaEndpointsFailed": "No s'ha pogut actualitzar la llista permesa del punt final de Step CA.",
"notificationPrivateEndpoints": "Amfitrions de notificacions privades permesos",
"notificationPrivateEndpointsDesc": "Amfitrions privats exactes amb els quals els canals de notificació poden contactar. Separeu-los amb comes.",
"updateNotificationEndpointsFailed": "No s'ha pogut actualitzar la llista de permesos del punt final de notificació"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Heslo uloženo, zadejte heslo pro změnu.",
"replaceKey": "Vyměňte klíč",
"docsLink": "Zobrazit dokumenty",
"stepcaLabel": "Krok CA",
"stepcaDesc": "Přihlaste se prostřednictvím svého poskytovatele identity a nechte svou certifikační autoritu Step CA vydat krátkodobý SSH certifikát pro tohoto hostitele. URL certifikační autority, otisk kořenového serveru a zřizovatel OIDC nastavuje administrátor v Nastavení správce.",
"agentLabel": "SSH agent",
"agentDesc": "Ověřte se pomocí SSH agenta spuštěného na serveru Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agent musí běžet na počítači, kde je hostován Termix.",
"agentAuthenticationDesc": "K ověření tohoto připojení použijte klíče od SSH agenta. Pro použití SSH_AUTH_SOCK nechte cestu k socketu prázdnou.",
@@ -2121,18 +2119,9 @@
"save": "Uložit",
"saving": "Ukládání..."
},
"stepCa": "Krok CA",
"stepCaDesc": "Vydávejte krátkodobé SSH certifikáty z certifikační autority smallstep. Zadejte URL adresu certifikační autority, její kořenový otisk prstu (jak je znázorněno v kroku ca bootstrap) a název poskytovatele OIDC; poté jako typ ověřování hostitele vyberte „Step CA“. Pro deaktivaci ponechte všechny tři pole prázdné.",
"stepCaFingerprint": "Kořenový otisk (SHA-256)",
"stepCaProvisioner": "Název zřizovatele OIDC",
"stepCaSaved": "Nastavení kroku CA uloženo",
"stepCaSaveFailed": "Nepodařilo se uložit nastavení kroku CA",
"secretSourcePrivateEndpoints": "Povolené hostitele soukromých tajných zdrojů",
"secretSourcePrivateEndpointsDesc": "Soukromé hostitele, které mohou kontaktovat tajné zdroje (1Password Connect). Oddělte je čárkami.",
"updateSecretSourceEndpointsFailed": "Nepodařilo se aktualizovat seznam povolených koncových bodů tajného zdroje.",
"stepCaPrivateEndpoints": "Povolení soukromí hostitelé Step CA",
"stepCaPrivateEndpointsDesc": "Soukromí hostitelé, které může tok certifikátů Step CA kontaktovat: samotná CA a v případě interní certifikace také váš poskytovatel identity. Oddělte je čárkami.",
"updateStepCaEndpointsFailed": "Nepodařilo se aktualizovat seznam povolených koncových bodů Step CA.",
"notificationPrivateEndpoints": "Povolení hostitelé soukromých oznámení",
"notificationPrivateEndpointsDesc": "Přesné soukromé hostitele, které mohou notifikační kanály kontaktovat. Oddělte je čárkami.",
"updateNotificationEndpointsFailed": "Nepodařilo se aktualizovat seznam povolených koncových bodů oznámení"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Adgangskode gemt, skriv for at ændre",
"replaceKey": "Udskift nøgle",
"docsLink": "Se dokumenter",
"stepcaLabel": "Trin CA",
"stepcaDesc": "Log ind via din identitetsudbyder, og lad din Step CA udstede et kortvarigt SSH-certifikat til denne vært. CA-URL'en, root-fingeraftrykket og OIDC-provisioneren indstilles af en administrator under administratorindstillinger.",
"agentLabel": "SSH-agent",
"agentDesc": "Godkend ved hjælp af en SSH-agent, der kører på Termix-serveren (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agenten skal køre på den maskine, hvor Termix hostes.",
"agentAuthenticationDesc": "Brug nøgler fra en SSH-agent til at godkende denne forbindelse. Lad socket-stien være tom for at bruge SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Spare",
"saving": "Gemmer..."
},
"stepCa": "Trin CA",
"stepCaDesc": "Udsted kortlivede SSH-certifikater fra en smallstep CA. Indtast CA-URL'en, dens rodfingeraftryk (som vist i step ca bootstrap) og navnet på OIDC-provisioneren; vælg derefter \"Step CA\" som en værts godkendelsestype. Lad alle tre være tomme for at deaktivere.",
"stepCaFingerprint": "Rodfingeraftryk (SHA-256)",
"stepCaProvisioner": "OIDC-provisioneringsnavn",
"stepCaSaved": "Trin CA-indstillinger gemt",
"stepCaSaveFailed": "Kunne ikke gemme trin CA-indstillinger",
"secretSourcePrivateEndpoints": "Tilladte private hemmelige kildeværter",
"secretSourcePrivateEndpointsDesc": "Private værter som hemmelige kilder (1Password Connect) kan kontakte. Adskil dem med kommaer.",
"updateSecretSourceEndpointsFailed": "Kunne ikke opdatere tilladelseslisten for det hemmelige kildeslutpunkt",
"stepCaPrivateEndpoints": "Tilladte private Step CA-værter",
"stepCaPrivateEndpointsDesc": "Private værter, som Step CA-certifikatflowet kan kontakte: selve CA'en og, hvis den er intern, din identitetsudbyder. Adskil dem med kommaer.",
"updateStepCaEndpointsFailed": "Kunne ikke opdatere Step CA-slutpunktstilladelseslisten",
"notificationPrivateEndpoints": "Tilladte private notifikationsværter",
"notificationPrivateEndpointsDesc": "Præcise private værter, som notifikationskanaler kan kontakte. Adskil dem med kommaer.",
"updateNotificationEndpointsFailed": "Kunne ikke opdatere tilladelseslisten for notifikationsslutpunktet"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Passphrase gespeichert, zum Ändern eingeben",
"replaceKey": "Schlüssel ersetzen",
"docsLink": "Dokumentation anzeigen",
"stepcaLabel": "Schritt CA",
"stepcaDesc": "Melden Sie sich über Ihren Identitätsanbieter an und lassen Sie Ihre Step-Zertifizierungsstelle ein kurzlebiges SSH-Zertifikat für diesen Host ausstellen. Die URL der Zertifizierungsstelle, der Root-Fingerabdruck und der OIDC-Provisioner werden von einem Administrator unter „Admin-Einstellungen“ festgelegt.",
"agentLabel": "SSH-Agent",
"agentDesc": "Authentifizieren Sie sich mit einem SSH-Agenten, der auf dem Termix-Server läuft (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Der Agent muss auf dem Rechner laufen, auf dem Termix gehostet wird.",
"agentAuthenticationDesc": "Verwenden Sie Schlüssel eines SSH-Agenten, um diese Verbindung zu authentifizieren. Lassen Sie den Socket-Pfad leer, um SSH_AUTH_SOCK zu verwenden.",
@@ -2121,18 +2119,9 @@
"save": "Speichern",
"saving": "Wird gespeichert …"
},
"stepCa": "Schritt CA",
"stepCaDesc": "Stellen Sie kurzlebige SSH-Zertifikate von einer Smallstep-CA aus. Geben Sie die CA-URL, ihren Root-Fingerabdruck (wie von step ca bootstrap angezeigt) und den Namen des OIDC-Provisioners ein. Wählen Sie anschließend „Step CA“ als Authentifizierungstyp für den Host. Lassen Sie alle drei Felder leer, um die Authentifizierung zu deaktivieren.",
"stepCaFingerprint": "Root-Fingerabdruck (SHA-256)",
"stepCaProvisioner": "OIDC-Provisionername",
"stepCaSaved": "Schritt CA-Einstellungen gespeichert",
"stepCaSaveFailed": "Speichern der Step CA-Einstellungen fehlgeschlagen",
"secretSourcePrivateEndpoints": "Zulässige private geheime Quellhosts",
"secretSourcePrivateEndpointsDesc": "Private Hosts, die von geheimen Quellen (1Password Connect) kontaktiert werden können. Trennen Sie diese durch Kommas.",
"updateSecretSourceEndpointsFailed": "Die Aktualisierung der Zulassungsliste für geheime Quellendpunkte ist fehlgeschlagen.",
"stepCaPrivateEndpoints": "Zulässige private Step CA-Hosts",
"stepCaPrivateEndpointsDesc": "Private Hosts, mit denen der Step CA-Zertifikatsfluss in Kontakt treten kann, kontaktieren möglicherweise die Zertifizierungsstelle selbst und, falls intern, Ihren Identitätsanbieter. Trennen Sie diese durch Kommas.",
"updateStepCaEndpointsFailed": "Die Aktualisierung der Step CA-Endpunkt-Zulassungsliste ist fehlgeschlagen.",
"notificationPrivateEndpoints": "Zulässige Hosts für private Benachrichtigungen",
"notificationPrivateEndpointsDesc": "Genaue private Hosts, die Benachrichtigungskanäle kontaktieren dürfen. Durch Kommas trennen.",
"updateNotificationEndpointsFailed": "Aktualisierung der Zulassungsliste für Benachrichtigungsendpunkte fehlgeschlagen"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Η φράση πρόσβασης αποθηκεύτηκε, πληκτρολογήστε για αλλαγή",
"replaceKey": "Αντικατάσταση κλειδιού",
"docsLink": "Προβολή εγγράφων",
"stepcaLabel": "Βήμα CA",
"stepcaDesc": "Συνδεθείτε μέσω του παρόχου ταυτότητας και αφήστε την Step CA σας να εκδώσει ένα βραχύβιο πιστοποιητικό SSH για αυτόν τον κεντρικό υπολογιστή. Η διεύθυνση URL CA, το δακτυλικό αποτύπωμα root και ο πάροχος OIDC ορίζονται από έναν διαχειριστή στις Ρυθμίσεις διαχειριστή.",
"agentLabel": "Πράκτορας SSH",
"agentDesc": "Πραγματοποιήστε έλεγχο ταυτότητας χρησιμοποιώντας έναν παράγοντα SSH που εκτελείται στον διακομιστή Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Ο παράγοντας πρέπει να εκτελείται στον υπολογιστή όπου φιλοξενείται το Termix.",
"agentAuthenticationDesc": "Χρησιμοποιήστε κλειδιά από έναν παράγοντα SSH για να επαληθεύσετε αυτήν τη σύνδεση. Αφήστε τη διαδρομή υποδοχής κενή για να χρησιμοποιήσετε το SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Εκτός",
"saving": "Οικονομία..."
},
"stepCa": "Βήμα CA",
"stepCaDesc": "Έκδοση βραχύβιων πιστοποιητικών SSH από μια αρχή έκδοσης πιστοποιητικών smallstep. Εισαγάγετε τη διεύθυνση URL της αρχής έκδοσης πιστοποιητικών, το ριζικό της αποτύπωμα (όπως φαίνεται από το bootstrap του step ca) και το όνομα του παροχέα OIDC. Στη συνέχεια, επιλέξτε \"Step CA\" ως τύπο ελέγχου ταυτότητας κεντρικού υπολογιστή. Αφήστε και τα τρία κενά για απενεργοποίηση.",
"stepCaFingerprint": "Δακτυλικό αποτύπωμα ρίζας (SHA-256)",
"stepCaProvisioner": "Όνομα παρόχου OIDC",
"stepCaSaved": "Οι ρυθμίσεις CA του βήματος αποθηκεύτηκαν",
"stepCaSaveFailed": "Αποτυχία αποθήκευσης ρυθμίσεων Step CA",
"secretSourcePrivateEndpoints": "Επιτρεπόμενοι ιδιωτικοί μυστικοί κεντρικοί υπολογιστές πηγών",
"secretSourcePrivateEndpointsDesc": "Ιδιωτικοί κεντρικοί υπολογιστές με τους οποίους ενδέχεται να επικοινωνήσουν μυστικές πηγές (1Password Connect). Διαχωρίστε τους με κόμματα.",
"updateSecretSourceEndpointsFailed": "Αποτυχία ενημέρωσης της λίστας επιτρεπόμενων τελικών σημείων μυστικής πηγής",
"stepCaPrivateEndpoints": "Επιτρεπόμενοι ιδιωτικοί κεντρικοί υπολογιστές Step CA",
"stepCaPrivateEndpointsDesc": "Οι ιδιωτικοί κεντρικοί υπολογιστές της ροής πιστοποιητικών Step CA ενδέχεται να επικοινωνήσουν με: την ίδια την CA και, εάν είναι εσωτερική, τον πάροχο ταυτότητάς σας. Διαχωρίστε τους με κόμματα.",
"updateStepCaEndpointsFailed": "Αποτυχία ενημέρωσης της λίστας επιτρεπόμενων τελικών σημείων Step CA",
"notificationPrivateEndpoints": "Επιτρεπόμενοι ιδιωτικοί κεντρικοί υπολογιστές ειδοποιήσεων",
"notificationPrivateEndpointsDesc": "Ακριβώς ορίστε τους ιδιωτικούς κεντρικούς υπολογιστές με τους οποίους ενδέχεται να επικοινωνήσουν τα κανάλια ειδοποιήσεων. Διαχωρίστε τους με κόμματα.",
"updateNotificationEndpointsFailed": "Αποτυχία ενημέρωσης της λίστας επιτρεπόμενων τελικών σημείων ειδοποιήσεων"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Frase de contraseña guardada, escriba para cambiarla",
"replaceKey": "Reemplazar clave",
"docsLink": "Ver documentación",
"stepcaLabel": "Paso CA",
"stepcaDesc": "Inicie sesión a través de su proveedor de identidad y permita que su CA de Step emita un certificado SSH de corta duración para este host. La URL de la CA, la huella digital raíz y el proveedor OIDC se configuran mediante un administrador en la sección de Configuración de administración.",
"agentLabel": "Agente SSH",
"agentDesc": "Autentíquese usando un agente SSH que se ejecuta en el servidor de Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). El agente debe estar ejecutándose en la máquina donde está alojado Termix.",
"agentAuthenticationDesc": "Utilice las claves de un agente SSH para autenticar esta conexión. Deje la ruta del socket vacía para usar SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Guardar",
"saving": "Guardando..."
},
"stepCa": "Paso CA",
"stepCaDesc": "Emita certificados SSH de corta duración desde una CA de paso pequeño. Introduzca la URL de la CA, su huella digital raíz (tal como la muestra step ca bootstrap) y el nombre del proveedor OIDC; a continuación, seleccione \"Step CA\" como tipo de autenticación del host. Deje los tres campos vacíos para desactivarlo.",
"stepCaFingerprint": "Huella digital de la raíz (SHA-256)",
"stepCaProvisioner": "nombre del proveedor OIDC",
"stepCaSaved": "Configuración de CA guardada",
"stepCaSaveFailed": "No se pudieron guardar los ajustes de Step CA.",
"secretSourcePrivateEndpoints": "Hosts de fuentes secretas privadas permitidas",
"secretSourcePrivateEndpointsDesc": "Hosts privados con los que fuentes secretas (1Password Connect) pueden contactar. Sepárelos con comas.",
"updateSecretSourceEndpointsFailed": "No se pudo actualizar la lista de permitidos del punto final de origen secreto.",
"stepCaPrivateEndpoints": "Hosts de CA de paso privado permitidos",
"stepCaPrivateEndpointsDesc": "Los hosts privados con los que puede contactar el flujo de certificados de Step CA son: la propia CA y, si es interna, su proveedor de identidad. Sepárelos con comas.",
"updateStepCaEndpointsFailed": "No se pudo actualizar la lista de puntos finales permitidos de Step CA.",
"notificationPrivateEndpoints": "Hosts de notificación privada permitidos",
"notificationPrivateEndpointsDesc": "Hosts privados exactos con los que los canales de notificación pueden contactar. Sepárelos con comas.",
"updateNotificationEndpointsFailed": "No se pudo actualizar la lista de permitidos del punto final de notificación."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Salasana tallennettu, kirjoita muuttaaksesi",
"replaceKey": "Vaihda avain",
"docsLink": "Näytä dokumentit",
"stepcaLabel": "Vaihe CA",
"stepcaDesc": "Kirjaudu sisään identiteetintarjoajasi kautta ja anna vaiheen varmentajan myöntää lyhytaikainen SSH-varmenne tälle isännälle. Varmentajan URL-osoitteen, pääkäyttäjän sormenjäljen ja OIDC-varmenteen määrittäjän määrittää järjestelmänvalvojan asetuksissa.",
"agentLabel": "SSH-agentti",
"agentDesc": "Todenna itsesi käyttämällä Termix-palvelimella käynnissä olevaa SSH-agenttia (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agentin on oltava käynnissä koneella, jolla Termix sijaitsee.",
"agentAuthenticationDesc": "Käytä SSH-agentin avaimia tämän yhteyden todentamiseen. Jätä soketin polku tyhjäksi käyttääksesi SSH_AUTH_SOCK-ominaisuutta.",
@@ -2121,18 +2119,9 @@
"save": "Tallentaa",
"saving": "Tallennetaan..."
},
"stepCa": "Vaihe CA",
"stepCaDesc": "Myönnä lyhytaikaisia SSH-varmenteita smallstep-sertifikaatin myöntäjältä. Syötä sertifikaatin myöntäjän URL-osoite, sen juuritunniste (kuten step ca bootstrapissa näytetään) ja OIDC-palveluntarjoajan nimi. Valitse sitten isännän todennustyypiksi \"Step CA\". Jätä kaikki kolme tyhjiksi poistaaksesi sen käytöstä.",
"stepCaFingerprint": "Juurisormenjälki (SHA-256)",
"stepCaProvisioner": "OIDC-palveluntarjoajan nimi",
"stepCaSaved": "Vaiheen CA-asetukset tallennettu",
"stepCaSaveFailed": "Vaiheen CA-asetusten tallentaminen epäonnistui",
"secretSourcePrivateEndpoints": "Sallitut yksityiset salaiset lähdepalvelimet",
"secretSourcePrivateEndpointsDesc": "Yksityiset palvelimet, joihin salaiset lähteet (1Password Connect) voivat ottaa yhteyttä. Erota ne pilkuilla.",
"updateSecretSourceEndpointsFailed": "Salaisen lähteen päätepisteen sallittujen luettelon päivittäminen epäonnistui.",
"stepCaPrivateEndpoints": "Sallitut yksityiset Step CA -isännät",
"stepCaPrivateEndpointsDesc": "Yksityiset isännöitsijät, joilla Step CA -varmennetyönkulku voi ottaa yhteyttä: itse CA:han ja, jos kyseessä on sisäinen varmenne, identiteetintarjoajaasi. Erota ne pilkuilla.",
"updateStepCaEndpointsFailed": "Vaiheen CA-päätepisteiden sallittujen luettelon päivittäminen epäonnistui.",
"notificationPrivateEndpoints": "Sallitut yksityisten ilmoitusten isännät",
"notificationPrivateEndpointsDesc": "Tarkat yksityiset isännät, joihin ilmoituskanavat voivat ottaa yhteyttä. Erota ne pilkuilla.",
"updateNotificationEndpointsFailed": "Ilmoitusten päätepisteiden sallittujen luettelon päivittäminen epäonnistui."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Phrase secrète enregistrée, saisissez pour modifier",
"replaceKey": "Remplacer la clé",
"docsLink": "Voir la documentation",
"stepcaLabel": "Étape CA",
"stepcaDesc": "Connectez-vous via votre fournisseur d'identité et laissez votre autorité de certification Step CA émettre un certificat SSH temporaire pour cet hôte. L'URL de l'autorité de certification, l'empreinte racine et le provisionneur OIDC sont configurés par un administrateur dans les paramètres d'administration.",
"agentLabel": "Agent SSH",
"agentDesc": "Authentifiez-vous en utilisant un agent SSH exécuté sur le serveur Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). L'agent doit être en cours d'exécution sur la machine où Termix est hébergé.",
"agentAuthenticationDesc": "Utilisez les clés d'un agent SSH pour authentifier cette connexion. Laissez le chemin du socket vide pour utiliser SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Enregistrer",
"saving": "Enregistrement..."
},
"stepCa": "Étape CA",
"stepCaDesc": "Émettez des certificats SSH à durée de vie limitée auprès d'une autorité de certification Smallstep. Saisissez l'URL de l'autorité de certification, son empreinte racine (telle qu'affichée par l'outil d'amorçage de l'autorité de certification Smallstep) et le nom du provisionneur OIDC ; puis sélectionnez « Autorité de certification Smallstep » comme type d'authentification de l'hôte. Laissez ces trois champs vides pour désactiver l'authentification.",
"stepCaFingerprint": "Empreinte racine (SHA-256)",
"stepCaProvisioner": "Nom du fournisseur OIDC",
"stepCaSaved": "Paramètres de l'étape CA enregistrés",
"stepCaSaveFailed": "Échec de l'enregistrement des paramètres de l'étape CA",
"secretSourcePrivateEndpoints": "Hôtes de sources de secrets privés autorisés",
"secretSourcePrivateEndpointsDesc": "Hôtes privés que les sources secrètes (1Password Connect) peuvent contacter. Séparez-les par des virgules.",
"updateSecretSourceEndpointsFailed": "Échec de la mise à jour de la liste blanche des points de terminaison sources secrets",
"stepCaPrivateEndpoints": "Hôtes d'autorité de certification privés autorisés",
"stepCaPrivateEndpointsDesc": "Les hôtes privés pour lesquels le flux de certificats Step CA peut être sollicité peuvent contacter : l’autorité de certification elle-même et, si elle est interne, votre fournisseur d’identité. Séparez-les par des virgules.",
"updateStepCaEndpointsFailed": "Échec de la mise à jour de la liste blanche du point de terminaison Step CA",
"notificationPrivateEndpoints": "Hôtes de notifications privées autorisés",
"notificationPrivateEndpointsDesc": "Indiquez précisément les hôtes privés que les canaux de notification peuvent contacter. Séparez-les par des virgules.",
"updateNotificationEndpointsFailed": "Échec de la mise à jour de la liste blanche du point de terminaison de notification"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "סיסמת הסיסמה נשמרה, הקלד כדי לשנות",
"replaceKey": "החלפת מפתח",
"docsLink": "הצג מסמכים",
"stepcaLabel": "שלב CA",
"stepcaDesc": "היכנס דרך ספק הזהויות שלך ותן ל-Step CA שלך להנפיק אישור SSH קצר מועד עבור מארח זה. כתובת ה-URL של CA, טביעת האצבע של הבסיס וספק ה-OIDC מוגדרים על ידי מנהל מערכת תחת הגדרות מנהל מערכת.",
"agentLabel": "סוכן SSH",
"agentDesc": "בצע אימות באמצעות סוכן SSH הפועל על שרת Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). הסוכן חייב לפעול על המכונה שבה מתארח Termix.",
"agentAuthenticationDesc": "השתמש במפתחות מסוכן SSH כדי לאמת חיבור זה. השאר את נתיב ה-socket ריק כדי להשתמש ב-SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "לְהַצִיל",
"saving": "חִסָכוֹן..."
},
"stepCa": "שלב CA",
"stepCaDesc": "הנפק אישורי SSH קצרי מועד מ-smallstep CA. הזן את כתובת ה-URL של CA, טביעת האצבע הבסיסית שלה (כפי שמוצג על ידי step ca bootstrap) ואת שם ספק ה-OIDC; לאחר מכן בחר \"Step CA\" כסוג האימות של המארח. השאר את שלושתם ריקים כדי להשבית.",
"stepCaFingerprint": "טביעת אצבע של שורש (SHA-256)",
"stepCaProvisioner": "שם ספק OIDC",
"stepCaSaved": "הגדרות שלב CA נשמרו",
"stepCaSaveFailed": "נכשלה שמירת הגדרות שלב CA",
"secretSourcePrivateEndpoints": "מארחי מקור סודי פרטיים מורשים",
"secretSourcePrivateEndpointsDesc": "מארחים פרטיים שמקורות סודיים (1Password Connect) עשויים ליצור קשר איתם. יש להפריד ביניהם באמצעות פסיקים.",
"updateSecretSourceEndpointsFailed": "נכשל עדכון רשימת ההיתרים של נקודת הקצה של המקור הסודי",
"stepCaPrivateEndpoints": "מארחים פרטיים של Step CA מורשים",
"stepCaPrivateEndpointsDesc": "מארחים פרטיים, זרימת האישורים של Step CA עשויה ליצור קשר עם: רשות האישורים עצמה, ואם היא פנימית, עם ספק הזהויות שלך. הפרידו ביניהם באמצעות פסיקים.",
"updateStepCaEndpointsFailed": "נכשל עדכון רשימת ההיתרים של נקודת הקצה של Step CA",
"notificationPrivateEndpoints": "מארחי התראות פרטיים מורשים",
"notificationPrivateEndpointsDesc": "מארחים פרטיים מדויקים שערוצים של התראות עשויים ליצור איתם קשר. הפרד ביניהם באמצעות פסיקים.",
"updateNotificationEndpointsFailed": "נכשל עדכון רשימת ההיתרים של נקודת הקצה של ההתראות"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "पासफ़्रेज़ सहेजा गया, बदलने के लिए टाइप करें",
"replaceKey": "कुंजी बदलें",
"docsLink": "दस्तावेज़ देखें",
"stepcaLabel": "स्टेप सीए",
"stepcaDesc": "अपने आइडेंटिटी प्रोवाइडर के माध्यम से साइन इन करें और अपने स्टेप सीए को इस होस्ट के लिए एक अल्पकालिक एसएसएच प्रमाणपत्र जारी करने दें। सीए यूआरएल, रूट फिंगरप्रिंट और ओआईडीसी प्रोविजनर को एडमिनिस्ट्रेटर द्वारा एडमिन सेटिंग्स के अंतर्गत सेट किया जाता है।",
"agentLabel": "SSH एजेंट",
"agentDesc": "Termix सर्वर पर चल रहे SSH एजेंट (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent) का उपयोग करके प्रमाणित करें। एजेंट उस मशीन पर चल रहा होना चाहिए जहां Termix होस्ट किया गया है।",
"agentAuthenticationDesc": "इस कनेक्शन को प्रमाणित करने के लिए SSH एजेंट से कुंजियों का उपयोग करें। SSH_AUTH_SOCK का उपयोग करने के लिए सॉकेट पथ को खाली छोड़ दें।",
@@ -2121,18 +2119,9 @@
"save": "सहेजें",
"saving": "सहेज रहा है..."
},
"stepCa": "स्टेप सीए",
"stepCaDesc": "एक छोटे स्टेप सीए से अल्पकालिक एसएसएच प्रमाणपत्र जारी करें। सीए यूआरएल, उसका रूट फिंगरप्रिंट (जैसा कि स्टेप सीए बूटस्ट्रैप द्वारा दिखाया गया है) और ओआईडीसी प्रोविजनर का नाम दर्ज करें; फिर होस्ट के प्रमाणीकरण प्रकार के रूप में \"स्टेप सीए\" चुनें। इसे अक्षम करने के लिए तीनों को खाली छोड़ दें।",
"stepCaFingerprint": "रूट फिंगरप्रिंट (SHA-256)",
"stepCaProvisioner": "OIDC प्रोविज़नर नाम",
"stepCaSaved": "चरण CA सेटिंग्स सहेजी गईं",
"stepCaSaveFailed": "स्टेप CA सेटिंग्स को सहेजने में विफल",
"secretSourcePrivateEndpoints": "निजी गुप्त स्रोत होस्टों को अनुमति दी गई",
"secretSourcePrivateEndpointsDesc": "वे निजी होस्ट जिनसे गुप्त स्रोत (1Password Connect) संपर्क कर सकते हैं। इन्हें अल्पविराम से अलग करें।",
"updateSecretSourceEndpointsFailed": "गुप्त स्रोत एंडपॉइंट अनुमति सूची को अपडेट करने में विफल।",
"stepCaPrivateEndpoints": "निजी स्टेप सीए होस्ट की अनुमति है",
"stepCaPrivateEndpointsDesc": "निजी होस्ट, स्टेप सीए प्रमाणपत्र प्रवाह के दौरान निम्नलिखित से संपर्क कर सकते हैं: सीए स्वयं और, यदि आंतरिक है, तो आपका पहचान प्रदाता। इन्हें अल्पविराम से अलग करें।",
"updateStepCaEndpointsFailed": "स्टेप सीए एंडपॉइंट अनुमति सूची को अपडेट करने में विफलता",
"notificationPrivateEndpoints": "निजी अधिसूचना होस्टों को अनुमति दी गई",
"notificationPrivateEndpointsDesc": "वे सटीक निजी होस्ट जिनके साथ नोटिफिकेशन चैनल संपर्क कर सकते हैं। इन्हें अल्पविराम से अलग करें।",
"updateNotificationEndpointsFailed": "नोटिफिकेशन एंडपॉइंट की अनुमति सूची को अपडेट करने में विफलता"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Jelszó mentve, gépelje be a módosításhoz",
"replaceKey": "Kulcs cseréje",
"docsLink": "Dokumentumok megtekintése",
"stepcaLabel": "CA lépés",
"stepcaDesc": "Jelentkezzen be az identitásszolgáltatóján keresztül, és hagyja, hogy a lépéses hitelesítésszolgáltatója rövid életű SSH-tanúsítványt állítson ki ehhez a gazdagéphez. A hitelesítésszolgáltató URL-címét, a root ujjlenyomatát és az OIDC-kiszolgálót a rendszergazda állítja be az Adminisztrátori beállítások alatt.",
"agentLabel": "SSH-ügynök",
"agentDesc": "Hitelesítést végezzen egy, a Termix szerveren futó SSH ügynökkel (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Az ügynöknek azon a gépen kell futnia, amelyen a Termix fut.",
"agentAuthenticationDesc": "SSH ügynöktől származó kulcsok használata a kapcsolat hitelesítéséhez. Az SSH_AUTH_SOCK használatához hagyja üresen a socket elérési útját.",
@@ -2121,18 +2119,9 @@
"save": "Megtakarítás",
"saving": "Megtakarítás..."
},
"stepCa": "CA lépés",
"stepCaDesc": "Rövid életű SSH tanúsítványok kibocsátása smallstep CA-tól. Adja meg a CA URL-címét, a gyökér ujjlenyomatát (ahogy a step ca bootstrap mutatja) és az OIDC szolgáltató nevét; majd válassza a „Step CA” lehetőséget a gazdagép hitelesítési típusaként. A letiltáshoz hagyja mindhármat üresen.",
"stepCaFingerprint": "Gyökér ujjlenyomat (SHA-256)",
"stepCaProvisioner": "OIDC-szolgáltató neve",
"stepCaSaved": "Lépés CA beállításai mentve",
"stepCaSaveFailed": "Nem sikerült menteni a Step CA beállításait",
"secretSourcePrivateEndpoints": "Engedélyezett privát titkos forráshosztok",
"secretSourcePrivateEndpointsDesc": "Privát tárhelyszolgáltatók, amelyekkel titkos források (1Password Connect) kapcsolatba léphetnek. Vesszővel válassza el őket.",
"updateSecretSourceEndpointsFailed": "Nem sikerült frissíteni a titkos forrásvégpontok engedélyezőlistáját.",
"stepCaPrivateEndpoints": "Engedélyezett privát Step CA-hosztok",
"stepCaPrivateEndpointsDesc": "A Step CA tanúsítványfolyamat által érintett privát hosztok a következőkkel vehetik fel a kapcsolatot: maga a CA, és ha belső, akkor az identitásszolgáltató. Vesszővel válassza el őket.",
"updateStepCaEndpointsFailed": "Nem sikerült frissíteni a Step CA végpontok engedélyezőlistáját.",
"notificationPrivateEndpoints": "Engedélyezett privát értesítési hosztok",
"notificationPrivateEndpointsDesc": "Pontosan azok a privát hosztok, amelyekkel az értesítési csatornák kapcsolatba léphetnek. Vesszővel válassza el őket.",
"updateNotificationEndpointsFailed": "Nem sikerült frissíteni az értesítési végpontok engedélyezőlistáját"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Kata sandi tersimpan, ketik untuk mengubahnya",
"replaceKey": "Ganti kunci",
"docsLink": "Lihat dokumen",
"stepcaLabel": "Langkah CA",
"stepcaDesc": "Masuk melalui penyedia identitas Anda dan biarkan Step CA Anda menerbitkan sertifikat SSH berumur pendek untuk host ini. URL CA, sidik jari root, dan penyedia OIDC diatur oleh administrator di bawah Pengaturan Admin.",
"agentLabel": "Agen SSH",
"agentDesc": "Lakukan otentikasi menggunakan agen SSH yang berjalan di server Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agen tersebut harus berjalan di mesin tempat Termix dihosting.",
"agentAuthenticationDesc": "Gunakan kunci dari agen SSH untuk mengautentikasi koneksi ini. Biarkan jalur soket kosong untuk menggunakan SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Menyimpan",
"saving": "Penghematan..."
},
"stepCa": "Langkah CA",
"stepCaDesc": "Terbitkan sertifikat SSH berumur pendek dari CA smallstep. Masukkan URL CA, sidik jari root-nya (seperti yang ditunjukkan oleh step ca bootstrap) dan nama penyedia OIDC; kemudian pilih \"Step CA\" sebagai tipe otentikasi host. Biarkan ketiganya kosong untuk menonaktifkan.",
"stepCaFingerprint": "Sidik jari akar (SHA-256)",
"stepCaProvisioner": "Nama penyedia OIDC",
"stepCaSaved": "Pengaturan CA langkah tersimpan",
"stepCaSaveFailed": "Gagal menyimpan pengaturan Langkah CA.",
"secretSourcePrivateEndpoints": "Host sumber rahasia pribadi yang diizinkan",
"secretSourcePrivateEndpointsDesc": "Host pribadi yang dapat dihubungi oleh sumber rahasia (1Password Connect). Pisahkan dengan koma.",
"updateSecretSourceEndpointsFailed": "Gagal memperbarui daftar izin titik akhir sumber rahasia.",
"stepCaPrivateEndpoints": "Host CA Langkah pribadi yang diizinkan",
"stepCaPrivateEndpointsDesc": "Host privat yang dapat dihubungi oleh alur sertifikat Step CA adalah: CA itu sendiri dan, jika internal, penyedia identitas Anda. Pisahkan mereka dengan koma.",
"updateStepCaEndpointsFailed": "Gagal memperbarui daftar endpoint Step CA yang diizinkan.",
"notificationPrivateEndpoints": "Host pemberitahuan pribadi yang diizinkan",
"notificationPrivateEndpointsDesc": "Host privat yang tepat yang mungkin dihubungi oleh saluran pemberitahuan. Pisahkan dengan koma.",
"updateNotificationEndpointsFailed": "Gagal memperbarui daftar izin titik akhir notifikasi."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Passphrase salvata, digita per modificare",
"replaceKey": "Sostituisci chiave",
"docsLink": "Visualizza documentazione",
"stepcaLabel": "Passo CA",
"stepcaDesc": "Accedi tramite il tuo provider di identità e lascia che la tua CA Step emetta un certificato SSH temporaneo per questo host. L'URL della CA, l'impronta digitale della radice e il provisioner OIDC vengono impostati da un amministratore nelle Impostazioni di amministrazione.",
"agentLabel": "Agente SSH",
"agentDesc": "Autenticati usando un agente SSH in esecuzione sul server Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). L'agente deve essere in esecuzione sulla macchina dove è ospitato Termix.",
"agentAuthenticationDesc": "Utilizza le chiavi di un agente SSH per autenticare questa connessione. Lascia vuoto il percorso del socket per utilizzare SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Salva",
"saving": "Salvataggio in corso..."
},
"stepCa": "Passo CA",
"stepCaDesc": "Emetti certificati SSH di breve durata da una CA di smallstep. Inserisci l'URL della CA, la sua impronta digitale radice (come mostrato da step ca bootstrap) e il nome del provisioner OIDC; quindi scegli \"Step CA\" come tipo di autenticazione dell'host. Lascia tutti e tre i campi vuoti per disabilitare.",
"stepCaFingerprint": "Impronta digitale radice (SHA-256)",
"stepCaProvisioner": "Nome del fornitore OIDC",
"stepCaSaved": "Impostazioni di Step CA salvate",
"stepCaSaveFailed": "Impossibile salvare le impostazioni di Step CA",
"secretSourcePrivateEndpoints": "Host di fonti di segreti privati consentiti",
"secretSourcePrivateEndpointsDesc": "Host privati che le fonti segrete (1Password Connect) potrebbero contattare. Separarli con delle virgole.",
"updateSecretSourceEndpointsFailed": "Impossibile aggiornare l'elenco consentito dell'endpoint della sorgente segreta.",
"stepCaPrivateEndpoints": "Host Step CA privati consentiti",
"stepCaPrivateEndpointsDesc": "Gli host privati che utilizzano il flusso di certificati Step CA potrebbero contattare: la CA stessa e, se interna, il provider di identità. Separarli con delle virgole.",
"updateStepCaEndpointsFailed": "Impossibile aggiornare l'elenco di elementi consentiti dell'endpoint Step CA.",
"notificationPrivateEndpoints": "Host di notifica privata consentiti",
"notificationPrivateEndpointsDesc": "Indizi precisi degli host privati che i canali di notifica possono contattare. Separarli con delle virgole.",
"updateNotificationEndpointsFailed": "Impossibile aggiornare la lista di elementi consentiti dell'endpoint di notifica."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "パスフレーズが保存されました。変更するには入力してください",
"replaceKey": "鍵を置き換え",
"docsLink": "ドキュメントを表示",
"stepcaLabel": "ステップCA",
"stepcaDesc": "IDプロバイダー経由でサインインし、Step CAにこのホスト用の有効期限の短いSSH証明書を発行させてください。CAのURL、ルートフィンガープリント、およびOIDCプロビジョナーは、管理者が「管理設定」で設定します。",
"agentLabel": "SSHエージェント",
"agentDesc": "Termixサーバー上で動作しているSSHエージェント(Bitwarden、1Password、gpg-agent、KeeAgent、ssh-agent)を使用して認証します。エージェントはTermixがホストされているマシン上で実行されている必要があります。",
"agentAuthenticationDesc": "この接続を認証するには、SSHエージェントのキーを使用します。SSH_AUTH_SOCKを使用する場合は、ソケットパスを空欄にしてください。",
@@ -2121,18 +2119,9 @@
"save": "保存",
"saving": "保存中..."
},
"stepCa": "ステップCA",
"stepCaDesc": "smallstep CA から有効期限の短い SSH 証明書を発行します。CA の URL、ルートフィンガープリント (step ca bootstrap で表示されるもの)、および OIDC プロビジョナーの名前を入力し、ホストの認証タイプとして「Step CA」を選択します。3 つの項目すべてを空欄にすると無効になります。",
"stepCaFingerprint": "ルートフィンガープリント(SHA-256)",
"stepCaProvisioner": "OIDCプロビジョナー名",
"stepCaSaved": "ステップCAの設定が保存されました",
"stepCaSaveFailed": "ステップCAの設定を保存できませんでした",
"secretSourcePrivateEndpoints": "許可されたプライベートシークレットソースホスト",
"secretSourcePrivateEndpointsDesc": "秘密情報源(1Password Connectなど)が連絡を取る可能性のあるプライベートホスト。カンマで区切ってください。",
"updateSecretSourceEndpointsFailed": "シークレットソースエンドポイントの許可リストの更新に失敗しました",
"stepCaPrivateEndpoints": "許可されたプライベートStep CAホスト",
"stepCaPrivateEndpointsDesc": "プライベートホストの場合、Step CA 証明書フローは、認証局自体、および内部の場合は ID プロバイダーと連絡を取ることができます。これらはカンマで区切ってください。",
"updateStepCaEndpointsFailed": "Step CAエンドポイントの許可リストの更新に失敗しました",
"notificationPrivateEndpoints": "許可されたプライベート通知ホスト",
"notificationPrivateEndpointsDesc": "通知チャネルが接続する可能性のあるプライベートホストを正確に指定します。カンマで区切ってください。",
"updateNotificationEndpointsFailed": "通知エンドポイントの許可リストの更新に失敗しました"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "암호가 저장되었습니다. 변경하려면 입력하세요.",
"replaceKey": "키 교체",
"docsLink": "문서 보기",
"stepcaLabel": "스텝 CA",
"stepcaDesc": "ID 공급자를 통해 로그인하고 Step CA에서 이 호스트에 대한 단기 SSH 인증서를 발급받으세요. CA URL, 루트 지문 및 OIDC 프로비저너는 관리자가 관리자 설정에서 설정합니다.",
"agentLabel": "SSH 에이전트",
"agentDesc": "Termix 서버에서 실행 중인 SSH 에이전트(Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent)를 사용하여 인증합니다. 에이전트는 Termix가 호스팅된 시스템에서 실행 중이어야 합니다.",
"agentAuthenticationDesc": "SSH 에이전트의 키를 사용하여 이 연결을 인증하십시오. SSH_AUTH_SOCK을 사용하려면 소켓 경로를 비워 두십시오.",
@@ -2121,18 +2119,9 @@
"save": "저장",
"saving": "저장 중..."
},
"stepCa": "스텝 CA",
"stepCaDesc": "SmallStep CA에서 단기 SSH 인증서를 발급합니다. CA URL, 루트 지문(step ca bootstrap 명령으로 확인 가능) 및 OIDC 프로비저너 이름을 입력하고 호스트 인증 유형으로 \"Step CA\"를 선택합니다. 세 항목을 모두 비워두면 해당 기능을 비활성화할 수 있습니다.",
"stepCaFingerprint": "루트 지문(SHA-256)",
"stepCaProvisioner": "OIDC 프로비저너 이름",
"stepCaSaved": "단계 CA 설정이 저장되었습니다",
"stepCaSaveFailed": "Step CA 설정을 저장하는 데 실패했습니다.",
"secretSourcePrivateEndpoints": "허용된 개인 비밀 소스 호스트",
"secretSourcePrivateEndpointsDesc": "비밀 소스(1Password Connect)가 연결할 수 있는 비공개 호스트를 나열하세요. 쉼표로 구분하세요.",
"updateSecretSourceEndpointsFailed": "비밀 소스 엔드포인트 허용 목록 업데이트에 실패했습니다.",
"stepCaPrivateEndpoints": "허용된 개인 Step CA 호스트",
"stepCaPrivateEndpointsDesc": "Step CA 인증서 흐름이 접촉할 수 있는 개인 호스트는 CA 자체와 (내부 호스트인 경우) ID 공급자입니다. 이들을 쉼표로 구분하세요.",
"updateStepCaEndpointsFailed": "Step CA 엔드포인트 허용 목록 업데이트에 실패했습니다.",
"notificationPrivateEndpoints": "허용된 개인 알림 호스트",
"notificationPrivateEndpointsDesc": "알림 채널이 연락할 수 있는 정확한 비공개 호스트를 입력하세요. 쉼표로 구분하세요.",
"updateNotificationEndpointsFailed": "알림 엔드포인트 허용 목록 업데이트에 실패했습니다."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Wachtwoordzin opgeslagen, typ om te wijzigen",
"replaceKey": "Vervang de sleutel",
"docsLink": "Documenten bekijken",
"stepcaLabel": "Stap CA",
"stepcaDesc": "Meld u aan via uw identiteitsprovider en laat uw Step CA een kortstondig SSH-certificaat voor deze host uitgeven. De CA-URL, root-fingerprint en OIDC-provisioner worden door een beheerder ingesteld onder Beheerinstellingen.",
"agentLabel": "SSH-agent",
"agentDesc": "Authenticeer met behulp van een SSH-agent die op de Termix-server draait (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). De agent moet draaien op de machine waarop Termix is gehost.",
"agentAuthenticationDesc": "Gebruik sleutels van een SSH-agent om deze verbinding te authenticeren. Laat het socketpad leeg om SSH_AUTH_SOCK te gebruiken.",
@@ -2121,18 +2119,9 @@
"save": "Redden",
"saving": "Besparing..."
},
"stepCa": "Stap CA",
"stepCaDesc": "Geef kortstondige SSH-certificaten uit via een smallstep CA. Voer de URL van de CA, de root-fingerprint (zoals weergegeven door step ca bootstrap) en de naam van de OIDC-provisioner in; kies vervolgens \"Step CA\" als authenticatietype voor de host. Laat alle drie de velden leeg om deze functie uit te schakelen.",
"stepCaFingerprint": "Root-vingerafdruk (SHA-256)",
"stepCaProvisioner": "Naam van de OIDC-leverancier",
"stepCaSaved": "Stap CA-instellingen opgeslagen",
"stepCaSaveFailed": "Het opslaan van de Step CA-instellingen is mislukt.",
"secretSourcePrivateEndpoints": "Toegestane hosts voor privégeheime bronnen",
"secretSourcePrivateEndpointsDesc": "Privéhosts die mogelijk door geheime bronnen (1Password Connect) worden benaderd. Scheid ze met komma's.",
"updateSecretSourceEndpointsFailed": "Het bijwerken van de whitelist voor geheime bron-eindpunten is mislukt.",
"stepCaPrivateEndpoints": "Toegestane privé Step CA-hosts",
"stepCaPrivateEndpointsDesc": "De private hosts waarmee de Step CA-certificaatstroom contact kan opnemen, zijn: de certificeringsinstantie zelf en, indien intern, uw identiteitsprovider. Scheid deze met komma's.",
"updateStepCaEndpointsFailed": "Het bijwerken van de Step CA-eindpunt-whitelist is mislukt.",
"notificationPrivateEndpoints": "Toegestane hosts voor privénotificaties",
"notificationPrivateEndpointsDesc": "De exacte privéhosts die via notificatiekanalen gecontacteerd mogen worden. Scheid ze met komma's.",
"updateNotificationEndpointsFailed": "Het bijwerken van de lijst met toegestane notificatie-eindpunten is mislukt."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Passordfrase lagret, skriv inn for å endre",
"replaceKey": "Bytt ut nøkkelen",
"docsLink": "Vis dokumenter",
"stepcaLabel": "Trinn CA",
"stepcaDesc": "Logg på via identitetsleverandøren din, og la Step CA utstede et kortvarig SSH-sertifikat for denne verten. CA-URL-en, rotfingeravtrykket og OIDC-provisioneren angis av en administrator under Administratorinnstillinger.",
"agentLabel": "SSH-agent",
"agentDesc": "Autentiser ved hjelp av en SSH-agent som kjører på Termix-serveren (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agenten må kjøre på maskinen der Termix er driftet.",
"agentAuthenticationDesc": "Bruk nøkler fra en SSH-agent for å autentisere denne tilkoblingen. La socket-banen stå tom for å bruke SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Spare",
"saving": "Lagrer..."
},
"stepCa": "Trinn CA",
"stepCaDesc": "Utsted kortlivede SSH-sertifikater fra en smallstep CA. Skriv inn CA-URL-en, rotfingeravtrykket (som vist i step ca bootstrap) og navnet på OIDC-provisioneren; velg deretter \"Step CA\" som verts autentiseringstype. La alle tre stå tomme for å deaktivere.",
"stepCaFingerprint": "Rotfingeravtrykk (SHA-256)",
"stepCaProvisioner": "OIDC-provisionernavn",
"stepCaSaved": "Innstillinger for trinn CA er lagret",
"stepCaSaveFailed": "Kunne ikke lagre innstillingene for trinn CA",
"secretSourcePrivateEndpoints": "Tillatte private hemmelige kildeverter",
"secretSourcePrivateEndpointsDesc": "Private verter som hemmelige kilder (1Password Connect) kan kontakte. Skill dem med komma.",
"updateSecretSourceEndpointsFailed": "Kunne ikke oppdatere tillatelseslisten for hemmelig kildeendepunkt",
"stepCaPrivateEndpoints": "Tillatte private Step CA-verter",
"stepCaPrivateEndpointsDesc": "Private verter som Step CA-sertifikatflyten kan kontakte: selve CA-en og, hvis intern, identitetsleverandøren din. Skill dem med komma.",
"updateStepCaEndpointsFailed": "Kunne ikke oppdatere tillatelseslisten for Step CA-endepunktet",
"notificationPrivateEndpoints": "Tillatte private varslingsverter",
"notificationPrivateEndpointsDesc": "Nøyaktige private verter som varslingskanaler kan kontakte. Skill dem med komma.",
"updateNotificationEndpointsFailed": "Kunne ikke oppdatere tillatelseslisten for varslingsendepunktet"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Hasło klucza zapisane, wpisz, aby zmienić",
"replaceKey": "Zastąp klucz",
"docsLink": "Zobacz dokumentację",
"stepcaLabel": "Krok CA",
"stepcaDesc": "Zaloguj się za pomocą swojego dostawcy tożsamości i pozwól, aby urząd certyfikacji Step CA wydał tymczasowy certyfikat SSH dla tego hosta. Adres URL urzędu certyfikacji, odcisk palca głównego i dostawca OIDC są ustawiane przez administratora w Ustawieniach administratora.",
"agentLabel": "Agent SSH",
"agentDesc": "Uwierzytelnij się za pomocą agenta SSH uruchomionego na serwerze Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agent musi być uruchomiony na komputerze, na którym hostowany jest Termix.",
"agentAuthenticationDesc": "Użyj kluczy agenta SSH do uwierzytelnienia tego połączenia. Pozostaw ścieżkę do gniazda pustą, aby użyć SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Zapisz",
"saving": "Zapisywanie..."
},
"stepCa": "Krok CA",
"stepCaDesc": "Wydaj krótkotrwałe certyfikaty SSH z urzędu certyfikacji smallstep. Wprowadź adres URL urzędu certyfikacji, jego odcisk główny (jak pokazano w pliku bootstrap urzędu certyfikacji step) oraz nazwę dostawcy OIDC; następnie wybierz „Step CA” jako typ uwierzytelniania hosta. Pozostaw wszystkie trzy pola puste, aby wyłączyć uwierzytelnianie.",
"stepCaFingerprint": "Odcisk palca głównego (SHA-256)",
"stepCaProvisioner": "Nazwa dostawcy OIDC",
"stepCaSaved": "Ustawienia kroku CA zapisane",
"stepCaSaveFailed": "Nie udało się zapisać ustawień kroku CA",
"secretSourcePrivateEndpoints": "Dozwolone prywatne, tajne źródła hostów",
"secretSourcePrivateEndpointsDesc": "Prywatne serwery, z którymi mogą kontaktować się tajne źródła (1Password Connect). Rozdziel je przecinkami.",
"updateSecretSourceEndpointsFailed": "Nie udało się zaktualizować listy dozwolonych punktów końcowych źródła tajnego",
"stepCaPrivateEndpoints": "Dozwolone prywatne hosty Step CA",
"stepCaPrivateEndpointsDesc": "Prywatni hosty, z których przepływ certyfikatów Step CA może się kontaktować: z samym CA oraz, jeśli jest wewnętrzny, z Twoim dostawcą tożsamości. Rozdziel je przecinkami.",
"updateStepCaEndpointsFailed": "Nie udało się zaktualizować listy dozwolonych punktów końcowych urzędu certyfikacji kroku",
"notificationPrivateEndpoints": "Dozwolone hosty powiadomień prywatnych",
"notificationPrivateEndpointsDesc": "Dokładne nazwy prywatnych hostów, z którymi mogą kontaktować się kanały powiadomień. Oddziel je przecinkami.",
"updateNotificationEndpointsFailed": "Nie udało się zaktualizować listy dozwolonych punktów końcowych powiadomień"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Senha salva, digite para alterar",
"replaceKey": "Substituir chave",
"docsLink": "Ver documentos",
"stepcaLabel": "Etapa CA",
"stepcaDesc": "Faça login através do seu provedor de identidade e permita que a sua Autoridade Certificadora (CA) Step emita um certificado SSH de curta duração para este host. O URL da CA, a impressão digital da raiz e o provisionador OIDC são definidos por um administrador em Configurações de Administrador.",
"agentLabel": "Agente SSH",
"agentDesc": "Autentique-se usando um agente SSH em execução no servidor Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). O agente deve estar em execução na máquina onde o Termix está hospedado.",
"agentAuthenticationDesc": "Use as chaves de um agente SSH para autenticar esta conexão. Deixe o caminho do socket vazio para usar SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Salvar",
"saving": "Salvando..."
},
"stepCa": "Etapa CA",
"stepCaDesc": "Emita certificados SSH de curta duração a partir de uma CA Smallstep. Insira o URL da CA, sua impressão digital raiz (conforme mostrado pelo comando `step ca bootstrap`) e o nome do provisionador OIDC; em seguida, escolha \"Step CA\" como o tipo de autenticação do host. Deixe os três campos em branco para desativar.",
"stepCaFingerprint": "Impressão digital raiz (SHA-256)",
"stepCaProvisioner": "nome do provisionador OIDC",
"stepCaSaved": "Etapa Configurações CA salvas",
"stepCaSaveFailed": "Falha ao salvar as configurações da Etapa CA",
"secretSourcePrivateEndpoints": "Hosts de fontes secretas privadas permitidas",
"secretSourcePrivateEndpointsDesc": "Hosts privados que fontes secretas (1Password Connect) podem contatar. Separe-os com vírgulas.",
"updateSecretSourceEndpointsFailed": "Falha ao atualizar a lista de permissões do endpoint de origem secreta.",
"stepCaPrivateEndpoints": "Hosts CA privados permitidos",
"stepCaPrivateEndpointsDesc": "Para hosts privados, o fluxo de certificados da Step CA pode contatar: a própria CA e, se interna, seu provedor de identidade. Separe-os com vírgulas.",
"updateStepCaEndpointsFailed": "Falha ao atualizar a lista de permissões do endpoint da Autoridade Certificadora (CA) da Etapa.",
"notificationPrivateEndpoints": "Hosts de notificação privada permitidos",
"notificationPrivateEndpointsDesc": "Nomeie os hosts privados exatos que os canais de notificação podem contatar. Separe-os com vírgulas.",
"updateNotificationEndpointsFailed": "Falha ao atualizar a lista de permissões do endpoint de notificação."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Frase-passe guardada, escreva para alterar",
"replaceKey": "Substituir chave",
"docsLink": "Ver documentação",
"stepcaLabel": "Etapa CA",
"stepcaDesc": "Faça login através do seu fornecedor de identidade e permita que a sua Autoridade Certificadora (CA) Step emita um certificado SSH de curta duração para este host. O URL da AC, a impressão digital da raiz e o provisionador OIDC são definidos por um administrador em Definições de Administrador.",
"agentLabel": "Agente SSH",
"agentDesc": "Autentique-se usando um agente SSH em execução no servidor Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). O agente deve estar em execução na máquina onde o Termix está alojado.",
"agentAuthenticationDesc": "Utilize as chaves de um agente SSH para autenticar esta ligação. Deixe o caminho do socket vazio para utilizar SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Guardar",
"saving": "A guardar..."
},
"stepCa": "Etapa CA",
"stepCaDesc": "Emita certificados SSH de curta duração a partir de uma CA Smallstep. Introduza o URL da AC, a sua impressão digital raiz (conforme mostrado pelo comando `step ca bootstrap`) e o nome do provisionador OIDC; em seguida, escolha \"Step CA\" como tipo de autenticação do host. Deixe os três campos em branco para desativar.",
"stepCaFingerprint": "Impressão digital de raiz (SHA-256)",
"stepCaProvisioner": "nome do provisionador OIDC",
"stepCaSaved": "Passo Configurações CA guardadas",
"stepCaSaveFailed": "Falha ao guardar as definições da Etapa CA",
"secretSourcePrivateEndpoints": "Hosts de fontes secretas privadas permitidas",
"secretSourcePrivateEndpointsDesc": "Hosts privados que as fontes secretas (1Password Connect) podem contactar. Separe-os com vírgulas.",
"updateSecretSourceEndpointsFailed": "Falha ao atualizar a lista de permissões do endpoint de origem secreta.",
"stepCaPrivateEndpoints": "Hosts CA privados permitidos",
"stepCaPrivateEndpointsDesc": "Para hosts privados, o fluxo de certificados da Step CA pode contactar: a própria CA e, se for interna, o seu fornecedor de identidade. Separe-os com vírgulas.",
"updateStepCaEndpointsFailed": "Falha ao atualizar a lista de permissões do endpoint da Autoridade Certificadora (CA) da Etapa.",
"notificationPrivateEndpoints": "Hosts de notificação privada permitidos",
"notificationPrivateEndpointsDesc": "Nomeie os hosts privados exatos que os canais de notificação podem contactar. Separe-os com vírgulas.",
"updateNotificationEndpointsFailed": "Falha ao atualizar a lista de permissões do endpoint de notificação."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Parolă salvată, tastați pentru a o modifica",
"replaceKey": "Înlocuiți cheia",
"docsLink": "Vizualizați documentele",
"stepcaLabel": "Pasul CA",
"stepcaDesc": "Conectați-vă prin intermediul furnizorului de identitate și permiteți autorității de certificare Step CA să emită un certificat SSH de scurtă durată pentru această gazdă. Adresa URL a autorității de certificare, amprenta rădăcină și furnizorul OIDC sunt setate de un administrator în Setări administrator.",
"agentLabel": "Agent SSH",
"agentDesc": "Autentificați-vă folosind un agent SSH care rulează pe serverul Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agentul trebuie să ruleze pe mașina pe care este găzduit Termix.",
"agentAuthenticationDesc": "Folosește chei de la un agent SSH pentru a autentifica această conexiune. Lasă calea socketului goală pentru a utiliza SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Salva",
"saving": "Economisire..."
},
"stepCa": "Pasul CA",
"stepCaDesc": "Emiteți certificate SSH de scurtă durată de viață de la o CA Smallstep. Introduceți adresa URL a CA, amprenta sa rădăcină (așa cum se arată în bootstrap-ul CA Step) și numele furnizorului OIDC; apoi alegeți „Step CA” ca tip de autentificare al gazdei. Lăsați toate trei golite pentru a dezactiva.",
"stepCaFingerprint": "Amprentă digitală rădăcină (SHA-256)",
"stepCaProvisioner": "Numele furnizorului OIDC",
"stepCaSaved": "Setările Step CA au fost salvate",
"stepCaSaveFailed": "Setările Step CA nu au putut fi salvate.",
"secretSourcePrivateEndpoints": "Gazde sursă secrete private permise",
"secretSourcePrivateEndpointsDesc": "Gazde private pe care sursele secrete (1Password Connect) le pot contacta. Separați-le prin virgule.",
"updateSecretSourceEndpointsFailed": "Nu s-a putut actualiza lista de permise pentru endpoint-ul sursă secretă",
"stepCaPrivateEndpoints": "Gazde private permise de Step CA",
"stepCaPrivateEndpointsDesc": "Gazde private pe care fluxul de certificate Step CA le poate contacta: CA-ul în sine și, dacă este intern, furnizorul dvs. de identitate. Separați-le prin virgule.",
"updateStepCaEndpointsFailed": "Nu s-a putut actualiza lista de permise pentru endpoint-ul Step CA.",
"notificationPrivateEndpoints": "Gazde de notificări private permise",
"notificationPrivateEndpointsDesc": "Gazde private exacte pe care canalele de notificare le pot contacta. Separați-le prin virgule.",
"updateNotificationEndpointsFailed": "Nu s-a putut actualiza lista de permise pentru punctul final de notificare"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Парольная фраза сохранена, введите для изменения",
"replaceKey": "Заменить ключ",
"docsLink": "Документация",
"stepcaLabel": "Шаг CA",
"stepcaDesc": "Войдите в систему через своего поставщика идентификации и позвольте вашему центру сертификации Step CA выдать краткосрочный SSH-сертификат для этого хоста. URL-адрес центра сертификации, отпечаток корневого сертификата и средство подготовки OIDC задаются администратором в разделе «Настройки администратора».",
"agentLabel": "SSH-агент",
"agentDesc": "Аутентификация с помощью SSH-агента, запущенного на сервере Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Агент должен работать на машине, где размещён Termix.",
"agentAuthenticationDesc": "Для аутентификации этого соединения используйте ключи от SSH-агента. Оставьте поле пути к сокету пустым, чтобы использовать SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Сохранить",
"saving": "Сохранение…"
},
"stepCa": "Шаг CA",
"stepCaDesc": "Выдача краткосрочных SSH-сертификатов от центра сертификации smallstep. Введите URL-адрес центра сертификации, его корневой отпечаток (как показано командой step ca bootstrap) и имя поставщика OIDC; затем выберите «Step CA» в качестве типа аутентификации хоста. Оставьте все три поля пустыми, чтобы отключить эту функцию.",
"stepCaFingerprint": "Отпечаток корневого ключа (SHA-256)",
"stepCaProvisioner": "имя поставщика OIDC",
"stepCaSaved": "Настройки шага CA сохранены.",
"stepCaSaveFailed": "Не удалось сохранить настройки шага CA.",
"secretSourcePrivateEndpoints": "Разрешены хосты источников частных секретов",
"secretSourcePrivateEndpointsDesc": "Частные хосты, с которыми могут связываться секретные источники (1Password Connect). Разделяйте их запятыми.",
"updateSecretSourceEndpointsFailed": "Не удалось обновить список разрешенных конечных точек источника секретных данных.",
"stepCaPrivateEndpoints": "Разрешены частные хосты Step CA",
"stepCaPrivateEndpointsDesc": "Частные хосты, для которых выполняется процедура сертификации Step CA, могут связаться с: самим центром сертификации и, если он внутренний, с вашим поставщиком идентификации. Разделяйте их запятыми.",
"updateStepCaEndpointsFailed": "Не удалось обновить список разрешенных конечных точек Step CA.",
"notificationPrivateEndpoints": "Разрешены частные хосты для уведомлений",
"notificationPrivateEndpointsDesc": "Точные частные хосты, с которыми могут связываться каналы уведомлений. Разделяйте их запятыми.",
"updateNotificationEndpointsFailed": "Не удалось обновить список разрешенных конечных точек уведомлений."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Лозинка је сачувана, унесите текст да бисте је променили",
"replaceKey": "Замените кључ",
"docsLink": "Прикажи документе",
"stepcaLabel": "Корак CA",
"stepcaDesc": "Пријавите се преко свог добављача идентитета и дозволите свом Step CA да изда краткотрајни SSH сертификат за овај хост. CA URL, root отисак прста и OIDC provisioner подешава администратор у подешавањима администратора.",
"agentLabel": "SSH агент",
"agentDesc": "Аутентификујте се помоћу SSH агента који ради на Termix серверу (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Агент мора бити покренут на машини на којој се налази Termix.",
"agentAuthenticationDesc": "Користите кључеве од SSH агента за аутентификацију ове везе. Оставите путању сокета празну да бисте користили SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Сачувај",
"saving": "Чување..."
},
"stepCa": "Корак CA",
"stepCaDesc": "Издајте краткотрајне SSH сертификате из smallstep CA. Унесите CA URL, његов коренски отисак прста (као што је приказано у step ca bootstrap) и име OIDC провизора; затим изаберите „Step CA“ као тип аутентификације хоста. Оставите сва три празна да бисте онемогућили.",
"stepCaFingerprint": "Отисак коренског идентификатора (SHA-256)",
"stepCaProvisioner": "Име OIDC провизора",
"stepCaSaved": "Подешавања CA корака су сачувана",
"stepCaSaveFailed": "Чување подешавања корака CA није успело",
"secretSourcePrivateEndpoints": "Дозвољени приватни тајни изворни хостови",
"secretSourcePrivateEndpointsDesc": "Приватни хостови које тајни извори (1Password Connect) могу контактирати. Одвојите их зарезима.",
"updateSecretSourceEndpointsFailed": "Ажурирање листе дозвољених крајњих тачака тајног извора није успело",
"stepCaPrivateEndpoints": "Дозвољени приватни хостови Step CA",
"stepCaPrivateEndpointsDesc": "Приватни хостови које ток сертификата Step CA може контактирати: сам CA и, ако је интерно, ваш добављач идентитета. Раздвојите их зарезима.",
"updateStepCaEndpointsFailed": "Није успело ажурирање листе дозвољених крајњих тачака Step CA",
"notificationPrivateEndpoints": "Дозвољени хостови за приватна обавештења",
"notificationPrivateEndpointsDesc": "Тачни приватни хостови које канали за обавештења могу да контактирају. Одвојите их зарезима.",
"updateNotificationEndpointsFailed": "Ажурирање листе дозвољених крајњих тачака обавештења није успело"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Lösenfrasen sparad, skriv för att ändra",
"replaceKey": "Byt nyckel",
"docsLink": "Visa dokument",
"stepcaLabel": "Steg CA",
"stepcaDesc": "Logga in via din identitetsleverantör och låt din Step CA utfärda ett kortlivat SSH-certifikat för den här värden. CA-URL:en, rotfingeravtrycket och OIDC-provisionern ställs in av en administratör under administratörsinställningar.",
"agentLabel": "SSH-agent",
"agentDesc": "Autentisera med en SSH-agent som körs på Termix-servern (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agenten måste köras på den maskin där Termix finns.",
"agentAuthenticationDesc": "Använd nycklar från en SSH-agent för att autentisera den här anslutningen. Lämna socket-sökvägen tom om du vill använda SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Spara",
"saving": "Sparande..."
},
"stepCa": "Steg CA",
"stepCaDesc": "Utfärda kortlivade SSH-certifikat från en smallstep-CA. Ange CA-URL:en, dess rotfingeravtryck (som visas av steg-CA-bootstrap) och namnet på OIDC-provisioneraren; välj sedan \"Step CA\" som en värds autentiseringstyp. Lämna alla tre tomma för att inaktivera.",
"stepCaFingerprint": "Rotfingeravtryck (SHA-256)",
"stepCaProvisioner": "OIDC-provisioneringsnamn",
"stepCaSaved": "Steg CA-inställningar sparade",
"stepCaSaveFailed": "Kunde inte spara inställningarna för steg CA",
"secretSourcePrivateEndpoints": "Tillåtna privata hemliga källvärdar",
"secretSourcePrivateEndpointsDesc": "Privata värdar som hemliga källor (1Password Connect) kan kontakta. Separera dem med kommatecken.",
"updateSecretSourceEndpointsFailed": "Misslyckades med att uppdatera tillåtelselistan för slutpunkten för hemlig källa",
"stepCaPrivateEndpoints": "Tillåtna privata Step CA-värdar",
"stepCaPrivateEndpointsDesc": "Privata värdar som Step CA-certifikatflödet kan kontakta: själva CA:n och, om den är intern, din identitetsleverantör. Separera dem med kommatecken.",
"updateStepCaEndpointsFailed": "Misslyckades med att uppdatera tillåtelselistan för Step CA-slutpunkten",
"notificationPrivateEndpoints": "Tillåtna privata aviseringsvärdar",
"notificationPrivateEndpointsDesc": "Exakta privata värdar som aviseringskanaler kan kontakta. Separera dem med kommatecken.",
"updateNotificationEndpointsFailed": "Misslyckades med att uppdatera tillåtelselistan för aviseringsslutpunkten"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "บันทึกรหัสผ่านแล้ว พิมพ์เพื่อเปลี่ยน",
"replaceKey": "เปลี่ยนกุญแจ",
"docsLink": "ดูเอกสาร",
"stepcaLabel": "ขั้นตอน CA",
"stepcaDesc": "ลงชื่อเข้าใช้ผ่านผู้ให้บริการยืนยันตัวตนของคุณ และให้ Step CA ออกใบรับรอง SSH ที่มีอายุสั้นสำหรับโฮสต์นี้ URL ของ CA, ลายนิ้วมือรูท และตัวจัดเตรียม OIDC จะถูกตั้งค่าโดยผู้ดูแลระบบภายใต้การตั้งค่าผู้ดูแลระบบ",
"agentLabel": "เอเจนต์ SSH",
"agentDesc": "ยืนยันตัวตนโดยใช้เอเจนต์ SSH ที่ทำงานอยู่บนเซิร์ฟเวอร์ Termix (เช่น Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent) เอเจนต์จะต้องทำงานอยู่บนเครื่องที่ติดตั้ง Termix อยู่",
"agentAuthenticationDesc": "ใช้คีย์จากเอเจนต์ SSH เพื่อตรวจสอบสิทธิ์การเชื่อมต่อนี้ หากต้องการใช้ SSH_AUTH_SOCK ให้เว้นช่องเส้นทางซ็อกเก็ตว่างไว้",
@@ -2121,18 +2119,9 @@
"save": "บันทึก",
"saving": "ประหยัด..."
},
"stepCa": "ขั้นตอน CA",
"stepCaDesc": "ออกใบรับรอง SSH ที่มีอายุสั้นจาก CA ของ smallstep ป้อน URL ของ CA ลายนิ้วมือรูท (ตามที่แสดงโดย step ca bootstrap) และชื่อของตัวจัดเตรียม OIDC จากนั้นเลือก \"Step CA\" เป็นประเภทการตรวจสอบสิทธิ์ของโฮสต์ เว้นว่างทั้งสามช่องเพื่อปิดใช้งาน",
"stepCaFingerprint": "ลายนิ้วมือราก (SHA-256)",
"stepCaProvisioner": "ชื่อผู้จัดเตรียม OIDC",
"stepCaSaved": "บันทึกการตั้งค่าขั้นตอน CA แล้ว",
"stepCaSaveFailed": "ไม่สามารถบันทึกการตั้งค่าขั้นตอน CA ได้",
"secretSourcePrivateEndpoints": "อนุญาตให้โฮสต์แหล่งข้อมูลลับส่วนตัว",
"secretSourcePrivateEndpointsDesc": "โฮสต์ส่วนตัวที่แหล่งข้อมูลลับ (1Password Connect) อาจติดต่อได้ คั่นด้วยเครื่องหมายจุลภาค",
"updateSecretSourceEndpointsFailed": "ไม่สามารถอัปเดตรายการอนุญาตปลายทางแหล่งข้อมูลลับได้",
"stepCaPrivateEndpoints": "อนุญาตให้โฮสต์ Step CA ส่วนตัว",
"stepCaPrivateEndpointsDesc": "สำหรับโฮสต์ส่วนตัว กระบวนการขอใบรับรอง Step CA อาจติดต่อกับ: CA เอง และหากเป็นโฮสต์ภายใน ก็อาจติดต่อกับผู้ให้บริการยืนยันตัวตนของคุณด้วย คั่นด้วยเครื่องหมายจุลภาค",
"updateStepCaEndpointsFailed": "ไม่สามารถอัปเดตรายการอนุญาตปลายทาง Step CA ได้",
"notificationPrivateEndpoints": "อนุญาตให้โฮสต์แจ้งเตือนส่วนตัว",
"notificationPrivateEndpointsDesc": "ระบุโฮสต์ส่วนตัวที่ช่องทางการแจ้งเตือนสามารถติดต่อได้ คั่นด้วยเครื่องหมายจุลภาค",
"updateNotificationEndpointsFailed": "ไม่สามารถอัปเดตรายการอนุญาตปลายทางการแจ้งเตือนได้"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Parola kaydedildi, değiştirmek için yazın",
"replaceKey": "Anahtarı değiştir",
"docsLink": "Dokümanları görüntüle",
"stepcaLabel": "Adım CA",
"stepcaDesc": "Kimlik sağlayıcınız aracılığıyla oturum açın ve Step CA'nızın bu sunucu için kısa süreli bir SSH sertifikası düzenlemesine izin verin. CA URL'si, kök parmak izi ve OIDC sağlayıcısı, yönetici tarafından Yönetici Ayarları altında belirlenir.",
"agentLabel": "SSH Ajanı",
"agentDesc": "Termix sunucusunda çalışan bir SSH ajanı kullanarak kimlik doğrulayın (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Ajan, Termix'in barındırıldığı makinede çalışıyor olmalıdır.",
"agentAuthenticationDesc": "Bu bağlantıyı doğrulamak için bir SSH aracısından gelen anahtarları kullanın. SSH_AUTH_SOCK kullanmak için soket yolunu boş bırakın.",
@@ -2121,18 +2119,9 @@
"save": "Kaydet",
"saving": "Kaydediliyor..."
},
"stepCa": "Adım CA",
"stepCaDesc": "Smallstep CA'dan kısa süreli SSH sertifikaları verin. CA URL'sini, kök parmak izini (step ca bootstrap tarafından gösterildiği gibi) ve OIDC sağlayıcısının adını girin; ardından ana bilgisayarın kimlik doğrulama türü olarak \"Step CA\"yı seçin. Devre dışı bırakmak için üçünü de boş bırakın.",
"stepCaFingerprint": "Kök parmak izi (SHA-256)",
"stepCaProvisioner": "OIDC tedarikçi adı",
"stepCaSaved": "Adım CA ayarları kaydedildi",
"stepCaSaveFailed": "Step CA ayarlarını kaydetme başarısız oldu.",
"secretSourcePrivateEndpoints": "İzin verilen özel gizli kaynak sunucuları",
"secretSourcePrivateEndpointsDesc": "Gizli kaynakların (1Password Connect) iletişim kurabileceği özel sunucular. Bunları virgülle ayırın.",
"updateSecretSourceEndpointsFailed": "Gizli kaynak uç nokta izin listesini güncelleme başarısız oldu.",
"stepCaPrivateEndpoints": "Özel Step CA sunucularına izin verildi.",
"stepCaPrivateEndpointsDesc": "Özel sunucular, Step CA sertifika akışı kapsamında şu kuruluşlarla iletişime geçebilir: CA'nın kendisi ve (dahili ise) kimlik sağlayıcınız. Bunları virgülle ayırın.",
"updateStepCaEndpointsFailed": "Step CA uç nokta izin listesini güncelleme başarısız oldu.",
"notificationPrivateEndpoints": "İzin verilen özel bildirim sunucuları",
"notificationPrivateEndpointsDesc": "Bildirim kanallarının iletişim kurabileceği tam özel sunucuları belirtin. Bunları virgülle ayırın.",
"updateNotificationEndpointsFailed": "Bildirim uç noktası izin listesini güncelleme başarısız oldu."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Пароль збережено, введіть, щоб змінити",
"replaceKey": "Замінити ключ",
"docsLink": "Переглянути документи",
"stepcaLabel": "Крок CA",
"stepcaDesc": "Увійдіть через свого постачальника ідентифікаційних даних і дозвольте вашому центру сертифікації Step CA видати короткочасний SSH-сертифікат для цього хоста. URL-адресу центру сертифікації, кореневий відбиток і OIDC-провайдер встановлює адміністратор у розділі «Налаштування адміністратора».",
"agentLabel": "SSH-агент",
"agentDesc": "Автентифікуйтеся за допомогою SSH-агента, що працює на сервері Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Агент має бути запущений на машині, де розміщено Termix.",
"agentAuthenticationDesc": "Використовуйте ключі від SSH-агента для автентифікації цього з'єднання. Залиште шлях сокета порожнім, щоб використовувати SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Зберегти",
"saving": "Збереження..."
},
"stepCa": "Крок CA",
"stepCaDesc": "Видавати короткочасні SSH-сертифікати від центру сертифікації smallstep. Введіть URL-адресу центру сертифікації, його кореневий відбиток (як показано в step ca bootstrap) та ім'я постачальника OIDC; потім виберіть \"Step CA\" як тип автентифікації хоста. Залиште всі три поля порожніми, щоб вимкнути.",
"stepCaFingerprint": "Кореневий відбиток (SHA-256)",
"stepCaProvisioner": "Ім'я постачальника OIDC",
"stepCaSaved": "Налаштування кроку CA збережено",
"stepCaSaveFailed": "Не вдалося зберегти налаштування кроку CA",
"secretSourcePrivateEndpoints": "Дозволені приватні секретні хости джерел",
"secretSourcePrivateEndpointsDesc": "Приватні хости, з якими можуть зв’язуватися секретні джерела (1Password Connect). Розділіть їх комами.",
"updateSecretSourceEndpointsFailed": "Не вдалося оновити список дозволених кінцевих точок секретного джерела",
"stepCaPrivateEndpoints": "Дозволені приватні хости Step CA",
"stepCaPrivateEndpointsDesc": "Приватні хости, з якими може зв’язатися потік сертифікатів Step CA: сам CA та, якщо внутрішній, ваш постачальник ідентифікаційних даних. Розділяйте їх комами.",
"updateStepCaEndpointsFailed": "Не вдалося оновити список дозволених кінцевих точок Step CA",
"notificationPrivateEndpoints": "Дозволені хости приватних сповіщень",
"notificationPrivateEndpointsDesc": "Точні приватні хости, з якими можуть зв’язуватися канали сповіщень. Розділіть їх комами.",
"updateNotificationEndpointsFailed": "Не вдалося оновити список дозволених кінцевих точок сповіщень"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "Đã lưu cụm mật khẩu, nhập để thay đổi",
"replaceKey": "Thay khóa",
"docsLink": "Xem tài liệu",
"stepcaLabel": "Bước CA",
"stepcaDesc": "Đăng nhập thông qua nhà cung cấp danh tính của bạn và cho phép Step CA cấp chứng chỉ SSH có thời hạn ngắn cho máy chủ này. URL CA, dấu vân tay gốc và trình cung cấp OIDC được quản trị viên thiết lập trong Cài đặt quản trị.",
"agentLabel": "SSH Agent",
"agentDesc": "Xác thực bằng SSH agent chạy trên máy chủ Termix (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). Agent phải đang chạy trên máy chạy Termix.",
"agentAuthenticationDesc": "Sử dụng khóa từ SSH agent để xác thực kết nối này. Để trống đường dẫn socket để sử dụng SSH_AUTH_SOCK.",
@@ -2121,18 +2119,9 @@
"save": "Lưu",
"saving": "Đang lưu..."
},
"stepCa": "Bước CA",
"stepCaDesc": "Cấp chứng chỉ SSH có thời hạn ngắn từ CA smallstep. Nhập URL của CA, dấu vân tay gốc của nó (như hiển thị bởi step ca bootstrap) và tên của trình cung cấp OIDC; sau đó chọn \"Step CA\" làm loại xác thực của máy chủ. Để trống cả ba trường để vô hiệu hóa.",
"stepCaFingerprint": "Dấu vân tay gốc (SHA-256)",
"stepCaProvisioner": "Tên trình cung cấp OIDC",
"stepCaSaved": "Cài đặt bước CA đã được lưu",
"stepCaSaveFailed": "Không thể lưu cài đặt Bước CA.",
"secretSourcePrivateEndpoints": "Các máy chủ nguồn bí mật riêng tư được cho phép",
"secretSourcePrivateEndpointsDesc": "Các máy chủ riêng tư mà các nguồn bí mật (1Password Connect) có thể liên hệ. Phân tách chúng bằng dấu phẩy.",
"updateSecretSourceEndpointsFailed": "Không thể cập nhật danh sách cho phép điểm cuối nguồn bí mật.",
"stepCaPrivateEndpoints": "Cho phép máy chủ Step CA riêng tư",
"stepCaPrivateEndpointsDesc": "Các máy chủ riêng mà quy trình chứng chỉ CA từng bước có thể liên hệ: chính CA và, nếu là máy chủ nội bộ, nhà cung cấp danh tính của bạn. Phân tách chúng bằng dấu phẩy.",
"updateStepCaEndpointsFailed": "Không thể cập nhật danh sách cho phép điểm cuối Step CA.",
"notificationPrivateEndpoints": "Cho phép máy chủ thông báo riêng tư",
"notificationPrivateEndpointsDesc": "Liệt kê chính xác các máy chủ riêng mà kênh thông báo có thể liên hệ. Phân tách chúng bằng dấu phẩy.",
"updateNotificationEndpointsFailed": "Không thể cập nhật danh sách cho phép điểm cuối thông báo."
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "密码短语已保存,输入以更改",
"replaceKey": "替换密钥",
"docsLink": "查看文档",
"stepcaLabel": "步骤 CA",
"stepcaDesc": "通过您的身份提供商登录,并让您的 Step CA 为此主机颁发一个短期 SSH 证书。CA URL、根指纹和 OIDC 配置器由管理员在“管理设置”中设置。",
"agentLabel": "SSH 代理",
"agentDesc": "使用运行在 Termix 服务器上的 SSH 代理进行认证(Bitwarden、1Password、gpg-agent、KeeAgent、ssh-agent)。代理必须在托管 Termix 的机器上运行。",
"agentAuthenticationDesc": "使用 SSH 代理中的密钥来验证此连接。将套接字路径留空以使用 SSH_AUTH_SOCK。",
@@ -2121,18 +2119,9 @@
"save": "保存",
"saving": "保存中..."
},
"stepCa": "步骤 CA",
"stepCaDesc": "从小型 CA 颁发短期 SSH 证书。输入 CA URL、其根指纹(如 `step ca bootstrap` 命令所示)和 OIDC 配置器的名称;然后选择“Step CA”作为主机的身份验证类型。如果三项都留空,则禁用此功能。",
"stepCaFingerprint": "根指纹(SHA-256)",
"stepCaProvisioner": "OIDC配置器名称",
"stepCaSaved": "步骤 CA 设置已保存",
"stepCaSaveFailed": "保存步骤 CA 设置失败",
"secretSourcePrivateEndpoints": "允许的私有秘密来源主机",
"secretSourcePrivateEndpointsDesc": "秘密来源(例如 1Password Connect)可能联系的私有主机。请用逗号分隔。",
"updateSecretSourceEndpointsFailed": "更新秘密源端点允许列表失败",
"stepCaPrivateEndpoints": "允许的私有步骤 CA 主机",
"stepCaPrivateEndpointsDesc": "私有主机在执行步骤 CA 证书流程时可能会联系:CA 本身,以及(如果是内部)您的身份提供商。请用逗号分隔它们。",
"updateStepCaEndpointsFailed": "更新 Step CA 端点允许列表失败",
"notificationPrivateEndpoints": "允许的私人通知主机",
"notificationPrivateEndpointsDesc": "通知渠道可以联系的具体私有主机。请用逗号分隔。",
"updateNotificationEndpointsFailed": "更新通知端点允许列表失败"
-11
View File
@@ -771,8 +771,6 @@
"keyPassphraseSaved": "密碼片語已儲存,輸入以變更",
"replaceKey": "取代金鑰",
"docsLink": "檢視文件",
"stepcaLabel": "步驟 CA",
"stepcaDesc": "透過您的身分提供者登錄,並讓您的 Step CA 為此主機頒發短期 SSH 憑證。 CA URL、根指紋和 OIDC 配置器由管理員在「管理設定」中設定。",
"agentLabel": "SSH 代理",
"agentDesc": "使用在 Termix 伺服器上執行的 SSH 代理進行驗證(Bitwarden、1Password、gpg-agent、KeeAgent、ssh-agent)。代理必須在託管 Termix 的機器上執行。",
"agentAuthenticationDesc": "使用 SSH 代理程式中的金鑰來驗證此連線。將套接字路徑留空以使用 SSH_AUTH_SOCK。",
@@ -2121,18 +2119,9 @@
"save": "儲存",
"saving": "儲存中..."
},
"stepCa": "步驟 CA",
"stepCaDesc": "從小型 CA 頒發短期 SSH 憑證。輸入 CA URL、其根指紋(如 `step ca bootstrap` 指令所示)和 OIDC 設定器的名稱;然後選擇「Step CA」作為主機的驗證類型。如果三項都留空,則停用此功能。",
"stepCaFingerprint": "根指紋(SHA-256)",
"stepCaProvisioner": "OIDC配置器名稱",
"stepCaSaved": "步驟 CA 設定已儲存",
"stepCaSaveFailed": "儲存步驟 CA 設定失敗",
"secretSourcePrivateEndpoints": "允許的私有秘密來源主機",
"secretSourcePrivateEndpointsDesc": "秘密來源(例如 1Password Connect)可能聯絡的私有主機。請用逗號分隔。",
"updateSecretSourceEndpointsFailed": "更新秘密來源端點允許清單失敗",
"stepCaPrivateEndpoints": "允許的私有步驟 CA 主機",
"stepCaPrivateEndpointsDesc": "私有主機在執行步驟 CA 憑證流程時可能會聯絡:CA 本身,以及(如果是內部)您的身分提供者。請用逗號分隔它們。",
"updateStepCaEndpointsFailed": "更新 Step CA 端點允許清單失敗",
"notificationPrivateEndpoints": "允許的私人通知主機",
"notificationPrivateEndpointsDesc": "通知管道可以聯繫的具體私有主機。請用逗號分隔。",
"updateNotificationEndpointsFailed": "更新通知端點允許清單失敗"
-2
View File
@@ -12,8 +12,6 @@ import { createRegistry } from "@/lib/registry";
* type is picked. Login methods render on the login screen for methods the
* server reports as enabled; second factors render in the step after a first
* login. Either kind may bring an enrolment section for Settings > Security.
* Core's own (OIDC, LDAP) register from src/ui/auth/legacy-auth-ui.tsx until
* they move into plugins.
*/
export interface SshAuthEditorDef {
/** The authType value stored on the host. */
-54
View File
@@ -3,8 +3,6 @@ import { useTranslation } from "react-i18next";
import { useBranding } from "@/contexts/BrandingContext";
import {
getNotificationPrivateEndpoints,
getStepCaPrivateEndpoints,
setStepCaPrivateEndpoints as setStepCaPrivateEndpointsApi,
setNotificationPrivateEndpoints as setNotificationPrivateEndpointsApi,
} from "@/api/private-endpoints-api";
import {
@@ -73,7 +71,6 @@ import {
type AdminUser,
} from "./AdminManagementSections";
import { toast } from "sonner";
import { getStepCaSettings, updateStepCaSettings } from "@/api/settings-api";
import { getDatabaseTransferUrl } from "@/lib/database-transfer-url";
import {
AdminDatabaseSection,
@@ -129,25 +126,6 @@ export function AdminSettingsPanel({
const [analyticsLocked, setAnalyticsLocked] = useState(false);
const [oidcSilentLoginDefaultLocked, setOidcSilentLoginDefaultLocked] =
useState(false);
const [stepCaPrivateEndpoints, setStepCaPrivateEndpoints] = useState<
string[]
>([]);
const [stepCaSettings, setStepCaSettings] = useState({
caUrl: "",
fingerprint: "",
provisioner: "",
});
useEffect(() => {
getStepCaSettings()
.then((s) =>
setStepCaSettings({
caUrl: s.caUrl,
fingerprint: s.fingerprint,
provisioner: s.provisioner,
}),
)
.catch(() => {});
}, []);
const [notificationPrivateEndpoints, setNotificationPrivateEndpoints] =
useState<string[]>([]);
const [hostDefaults, setHostDefaults] = useState<HostDefaults>({});
@@ -333,7 +311,6 @@ export function AdminSettingsPanel({
oidcSilent,
analytics,
notificationEndpoints,
stepCaEndpoints,
branding,
] = await Promise.allSettled([
getRegistrationAllowed(),
@@ -347,7 +324,6 @@ export function AdminSettingsPanel({
getOidcSilentLoginDefault(),
getAnalyticsEnabled(),
getNotificationPrivateEndpoints(),
getStepCaPrivateEndpoints(),
getBranding(),
]);
@@ -379,9 +355,6 @@ export function AdminSettingsPanel({
setAnalyticsEnabled(analytics.value.enabled);
setAnalyticsLocked(analytics.value.locked ?? false);
}
if (stepCaEndpoints.status === "fulfilled") {
setStepCaPrivateEndpoints(stepCaEndpoints.value);
}
if (notificationEndpoints.status === "fulfilled") {
setNotificationPrivateEndpoints(notificationEndpoints.value);
}
@@ -499,28 +472,6 @@ export function AdminSettingsPanel({
}
}
async function handleSaveStepCaSettings() {
try {
await updateStepCaSettings(stepCaSettings);
toast.success(t("admin.stepCaSaved"));
} catch (error) {
toast.error(
error instanceof Error ? error.message : t("admin.stepCaSaveFailed"),
);
}
}
async function handleSaveStepCaPrivateEndpoints(hosts: string[]) {
const previous = stepCaPrivateEndpoints;
setStepCaPrivateEndpoints(hosts);
try {
setStepCaPrivateEndpoints(await setStepCaPrivateEndpointsApi(hosts));
} catch {
setStepCaPrivateEndpoints(previous);
toast.error(t("admin.updateStepCaEndpointsFailed"));
}
}
async function handleSaveNotificationPrivateEndpoints(hosts: string[]) {
const previous = notificationPrivateEndpoints;
setNotificationPrivateEndpoints(hosts);
@@ -938,11 +889,6 @@ export function AdminSettingsPanel({
analyticsLocked={analyticsLocked}
handleToggleAnalytics={handleToggleAnalytics}
notificationPrivateEndpoints={notificationPrivateEndpoints}
stepCaPrivateEndpoints={stepCaPrivateEndpoints}
onSaveStepCaPrivateEndpoints={handleSaveStepCaPrivateEndpoints}
stepCaSettings={stepCaSettings}
setStepCaSettings={setStepCaSettings}
handleSaveStepCaSettings={handleSaveStepCaSettings}
onSaveNotificationPrivateEndpoints={
handleSaveNotificationPrivateEndpoints
}
-79
View File
@@ -18,13 +18,6 @@ type GeneralSettingsSectionProps = {
handleToggleAnalytics: () => void;
notificationPrivateEndpoints: string[];
onSaveNotificationPrivateEndpoints: (hosts: string[]) => void;
stepCaPrivateEndpoints: string[];
onSaveStepCaPrivateEndpoints: (hosts: string[]) => void;
stepCaSettings: { caUrl: string; fingerprint: string; provisioner: string };
setStepCaSettings: Dispatch<
SetStateAction<{ caUrl: string; fingerprint: string; provisioner: string }>
>;
handleSaveStepCaSettings: () => void;
allowRegistration: boolean;
handleToggleRegistration: () => void;
allowPasswordLogin: boolean;
@@ -57,11 +50,6 @@ export function AdminGeneralSettingsSection({
handleToggleAnalytics,
notificationPrivateEndpoints,
onSaveNotificationPrivateEndpoints,
stepCaPrivateEndpoints,
onSaveStepCaPrivateEndpoints,
stepCaSettings,
setStepCaSettings,
handleSaveStepCaSettings,
allowRegistration,
handleToggleRegistration,
allowPasswordLogin,
@@ -130,73 +118,6 @@ export function AdminGeneralSettingsSection({
}
/>
</div>
<div className="flex flex-col gap-1.5 py-2">
<span className="text-xs font-medium">
{t("admin.stepCaPrivateEndpoints")}
</span>
<span className="text-[11px] leading-snug text-muted-foreground">
{t("admin.stepCaPrivateEndpointsDesc")}
</span>
<Input
className="rounded-none"
defaultValue={stepCaPrivateEndpoints.join(", ")}
placeholder="ca.internal, sso.internal"
onBlur={(event) =>
onSaveStepCaPrivateEndpoints(
event.target.value
.split(",")
.map((entry) => entry.trim())
.filter(Boolean),
)
}
/>
</div>
<div className="flex flex-col gap-2 border-t border-border pt-3 mt-2">
<span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground">
{t("admin.stepCa")}
</span>
<span className="text-[11px] leading-snug text-muted-foreground">
{t("admin.stepCaDesc")}
</span>
<Input
className="rounded-none"
placeholder="https://ca.internal:9000"
value={stepCaSettings.caUrl}
onChange={(e) =>
setStepCaSettings((p) => ({ ...p, caUrl: e.target.value }))
}
/>
<Input
className="rounded-none font-mono text-xs"
placeholder={t("admin.stepCaFingerprint")}
value={stepCaSettings.fingerprint}
onChange={(e) =>
setStepCaSettings((p) => ({ ...p, fingerprint: e.target.value }))
}
/>
<div className="flex items-center gap-2">
<Input
className="rounded-none"
placeholder={t("admin.stepCaProvisioner")}
value={stepCaSettings.provisioner}
onChange={(e) =>
setStepCaSettings((p) => ({
...p,
provisioner: e.target.value,
}))
}
/>
<Button
variant="outline"
size="sm"
className="text-xs border-accent-brand/40 text-accent-brand hover:bg-accent-brand/10 hover:text-accent-brand h-7"
onClick={handleSaveStepCaSettings}
>
{t("common.save")}
</Button>
</div>
</div>
<div className="border-t border-border pt-3 mt-2">
<SettingRow
label={t("admin.allowRegistration")}
-3
View File
@@ -88,7 +88,6 @@ import { useSshAuthEditors } from "@/plugin-host/auth-registry";
import { useSshAuthProviders } from "@/hooks/useSshAuthProviders";
import { SshAuthProviderFields } from "./SshAuthProviderFields";
import type { PluginSettingsField } from "@termix/plugin-sdk/manifest";
import { ensureLegacyAuthUI } from "@/auth/legacy-auth-ui";
import { PluginComponent } from "@/plugin-host/component-registry";
import {
toCredentialOption,
@@ -97,8 +96,6 @@ import {
const CUSTOM_FONT_OPTION = "__custom__";
ensureLegacyAuthUI();
export function HostEditor({
host,
activeTab,