ssd error
This commit is contained in:
@@ -55,6 +55,11 @@ ENCRYPT_TRAFFIC=false
|
||||
# (comma-separated). Default: only the host from LDAP_URL is used,
|
||||
# the rest is discovered via DNS SRV (_kerberos._tcp.<domain>).
|
||||
LDAP_KDC_SERVERS=dc.school25.ru,dc3.school25.ru,srv.school25.ru
|
||||
# Transient GSSAPI/KDC errors (e.g. "Server (ldap/...@REALM) unknown") are
|
||||
# retried automatically. Number of attempts (default 3) and pause between them
|
||||
# in milliseconds (default 1000). Applies to the kerberos password method.
|
||||
# LDAP_PASSWORD_RETRIES=3
|
||||
# LDAP_PASSWORD_RETRY_DELAY_MS=1000
|
||||
|
||||
# ============ Email (registration) ============
|
||||
# SMTP server used to send verification codes and registration result letters.
|
||||
|
||||
@@ -27,6 +27,8 @@ const config = {
|
||||
passwordMethod: getEnv('LDAP_PASSWORD_METHOD', passwordMethodDefault),
|
||||
ldapRealm: getEnv('LDAP_REALM', String(getEnv('LDAP_DOMAIN', 'school25.ru')).toUpperCase()),
|
||||
kdcServers: getEnv('LDAP_KDC_SERVERS', ''),
|
||||
passwordRetries: Math.max(1, parseInt(getEnv('LDAP_PASSWORD_RETRIES', '3'), 10) || 3),
|
||||
passwordRetryDelayMs: Math.max(0, parseInt(getEnv('LDAP_PASSWORD_RETRY_DELAY_MS', '1000'), 10) || 1000),
|
||||
powerShellExe: getEnv('LDAP_POWERSHELL_EXE', 'powershell.exe'),
|
||||
emailHost: getEnv('EMAIL_HOST', ''),
|
||||
emailPort: parseInt(getEnv('EMAIL_PORT', '587'), 10),
|
||||
|
||||
@@ -248,7 +248,9 @@ async function setPassword(client, userDN, password) {
|
||||
await passwordSetter.setPasswordViaKerberos(userDN, password, {
|
||||
ldapUrl: config.ldapUrl,
|
||||
principal,
|
||||
adminPassword: config.adminPassword
|
||||
adminPassword: config.adminPassword,
|
||||
retries: config.passwordRetries,
|
||||
retryDelayMs: config.passwordRetryDelayMs
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -173,48 +173,76 @@ async function setPasswordViaKerberos(userDN, password, {
|
||||
ldapUrl = '',
|
||||
principal = '',
|
||||
adminPassword = '',
|
||||
ccache = '/tmp/krb5cc_ldapauth'
|
||||
ccache = '/tmp/krb5cc_ldapauth',
|
||||
retries = 3,
|
||||
retryDelayMs = 1000
|
||||
} = {}) {
|
||||
if (!principal) {
|
||||
throw new Error('Не задан principal администратора (ADMIN_LOGIN/LDAP_REALM) для Kerberos');
|
||||
}
|
||||
const maxAttempts = retries >= 1 ? retries : 3;
|
||||
const ccacheEnv = { KRB5CCNAME: ccache };
|
||||
|
||||
const kinit = await runChild(exe, ['-c', ccache, principal], {
|
||||
env: ccacheEnv,
|
||||
input: String(adminPassword || '') + '\n',
|
||||
timeoutMs,
|
||||
describe: 'kinit'
|
||||
});
|
||||
if (kinit.code !== 0) {
|
||||
const detail = String(kinit.stderr || kinit.stdout || '').trim();
|
||||
throw new Error(`Не удалось получить билет Kerberos (kinit, код ${kinit.code})${detail ? ': ' + detail : ''}`);
|
||||
async function destroyCache() {
|
||||
try {
|
||||
await runChild('kdestroy', ['-c', ccache], {
|
||||
env: ccacheEnv,
|
||||
input: '',
|
||||
timeoutMs: 5000,
|
||||
describe: 'kdestroy'
|
||||
});
|
||||
} catch (_) { /* best-effort cleanup */ }
|
||||
}
|
||||
|
||||
const ldapArgs = ['-Y', 'GSSAPI'];
|
||||
if (ldapUrl) ldapArgs.push('-H', ldapUrl);
|
||||
ldapArgs.push('-O', 'maxssf=256');
|
||||
// Transient GSSAPI/KDC errors (e.g. "Server (ldap/...@REALM) unknown" from
|
||||
// ldapmodify) often pass on retry — DNS canonicalization or DC selection may
|
||||
// differ. Each attempt gets a fresh kinit ticket so a partial/poisoned
|
||||
// ccache can't cause repeated failures. Rewriting the same unicodePwd is
|
||||
// idempotent, so retrying is safe.
|
||||
let lastError = null;
|
||||
|
||||
const ldapmodify = await runChild(ldapmodifyExe, ldapArgs, {
|
||||
env: ccacheEnv,
|
||||
input: buildUnicodePwdLdif(userDN, password),
|
||||
timeoutMs,
|
||||
describe: 'ldapmodify'
|
||||
});
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
const kinit = await runChild(exe, ['-c', ccache, principal], {
|
||||
env: ccacheEnv,
|
||||
input: String(adminPassword || '') + '\n',
|
||||
timeoutMs,
|
||||
describe: 'kinit'
|
||||
});
|
||||
if (kinit.code !== 0) {
|
||||
const detail = String(kinit.stderr || kinit.stdout || '').trim();
|
||||
throw new Error(`Не удалось получить билет Kerberos (kinit, код ${kinit.code})${detail ? ': ' + detail : ''}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await runChild('kdestroy', ['-c', ccache], {
|
||||
env: ccacheEnv,
|
||||
input: '',
|
||||
timeoutMs: 5000,
|
||||
describe: 'kdestroy'
|
||||
});
|
||||
} catch (_) { /* best-effort cleanup */ }
|
||||
const ldapArgs = ['-Y', 'GSSAPI'];
|
||||
if (ldapUrl) ldapArgs.push('-H', ldapUrl);
|
||||
ldapArgs.push('-O', 'maxssf=256');
|
||||
|
||||
if (ldapmodify.code !== 0) {
|
||||
const detail = String(ldapmodify.stderr || ldapmodify.stdout || '').trim();
|
||||
throw new Error(`Ошибка установки пароля через Kerberos/GSSAPI (код ${ldapmodify.code})${detail ? ': ' + detail : ''}`);
|
||||
const ldapmodify = await runChild(ldapmodifyExe, ldapArgs, {
|
||||
env: ccacheEnv,
|
||||
input: buildUnicodePwdLdif(userDN, password),
|
||||
timeoutMs,
|
||||
describe: 'ldapmodify'
|
||||
});
|
||||
|
||||
if (ldapmodify.code !== 0) {
|
||||
const detail = String(ldapmodify.stderr || ldapmodify.stdout || '').trim();
|
||||
throw new Error(`Ошибка установки пароля через Kerberos/GSSAPI (код ${ldapmodify.code})${detail ? ': ' + detail : ''}`);
|
||||
}
|
||||
|
||||
await destroyCache();
|
||||
return;
|
||||
} catch (err) {
|
||||
lastError = err;
|
||||
await destroyCache();
|
||||
if (attempt < maxAttempts) {
|
||||
console.warn(`⚠ Установка пароля через Kerberos: попытка ${attempt}/${maxAttempts} не удалась, повтор через ${retryDelayMs} мс: ${err.message}`);
|
||||
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user