'use strict'; // Generates /etc/krb5.conf and /etc/openldap/ldap.conf from .env values // (realm, DC list, domain). // Run at every container start before server.js: // node scripts/generate-krb5-conf.js // Output paths default to /etc/krb5.conf and /etc/openldap/ldap.conf; // override with KRB5_CONF_PATH / KRB5_LDAP_CONF_PATH (useful for tests or // when the process has no write access to /etc). const fs = require('fs'); const path = require('path'); const config = require('../lib/config'); const DEFAULT_OUTPUT = '/etc/krb5.conf'; const DEFAULT_LDAP_CONF = '/etc/openldap/ldap.conf'; function hostFromUrl(url) { const match = /^[a-z0-9+.-]+:\/\/([^/:]+)(?::\d+)?/i.exec(String(url || '').trim()); return match ? match[1].toLowerCase() : ''; } function collectKdcServers() { const hosts = []; const push = (host) => { const value = String(host || '').trim().split(':')[0].toLowerCase(); if (value && !hosts.some((h) => h.toLowerCase() === value)) hosts.push(value); }; push(hostFromUrl(config.ldapUrl)); for (const item of String(config.kdcServers || '').split(/[\s,]+/)) { if (item) push(item); } return hosts; } function buildKrb5Conf({ realm, domain, kdcServers }) { const lines = [ '# Generated by scripts/generate-krb5-conf.js from .env — do not edit manually.', '# Overrides: LDAP_REALM (realm), LDAP_KDC_SERVERS (comma-separated DC list),', '# LDAP_URL (primary DC host is always included).', '', '[libdefaults]', ` default_realm = ${realm}`, ' dns_lookup_kdc = true', ' dns_lookup_realm = false', ' rdns = false', ' udp_preference_limit = 1', ' kdc_timeout = 5', ' max_retries = 2', ' forwardable = true', ' ticket_lifetime = 10h', '' ]; if (kdcServers.length > 0) { lines.push('[realms]', ` ${realm} = {`); for (const kdc of kdcServers) lines.push(` kdc = ${kdc}`); lines.push(' }', ''); } if (domain) { lines.push('[domain_realm]', ` .${domain} = ${realm}`, ` ${domain} = ${realm}`); } return lines.join('\n') + '\n'; } // Pins the SASL GSSAPI service principal to the DC host from LDAP_URL. // Without SASL_HOST, ldapmodify may canonicalize the hostname and request // e.g. ldap/school25.ru@REALM, which is not registered in AD (KDC error // "Server (ldap/school25.ru@REALM) unknown"). SASL_HOST/SASL_REALM override // the host used to build the SPN. function buildLdapConf({ host, realm }) { const lines = [ '# Generated by scripts/generate-krb5-conf.js from .env — do not edit manually.', `SASL_HOST ${host || ''}`, `SASL_REALM ${realm}`, '' ]; return lines.join('\n'); } function writeFile(content, outputPath) { const target = outputPath; const dir = path.dirname(target); fs.mkdirSync(dir, { recursive: true }); const tmp = `${target}.tmp-${process.pid}`; fs.writeFileSync(tmp, content, { encoding: 'utf8', mode: 0o644 }); fs.renameSync(tmp, target); return target; } function main() { try { const realm = config.ldapRealm; const domain = config.domain; const kdcServers = collectKdcServers(); const ldapHost = hostFromUrl(config.ldapUrl); const krb5Path = process.env.KRB5_CONF_PATH || DEFAULT_OUTPUT; writeFile(buildKrb5Conf({ realm, domain, kdcServers }), krb5Path); console.log(`krb5.conf generated -> ${krb5Path} (realm ${realm}, kdc: ${kdcServers.join(', ') || '(DNS SRV)'})`); const ldapConfPath = process.env.KRB5_LDAP_CONF_PATH || DEFAULT_LDAP_CONF; writeFile(buildLdapConf({ host: ldapHost, realm }), ldapConfPath); console.log(`ldap.conf generated -> ${ldapConfPath} (SASL_HOST ${ldapHost || '(empty)'}, SASL_REALM ${realm})`); } catch (err) { console.error(`Не удалось сгенерировать конфиги: ${err.message}`); process.exit(1); } } module.exports = { buildKrb5Conf, buildLdapConf, collectKdcServers, hostFromUrl, writeFile, main, DEFAULT_OUTPUT, DEFAULT_LDAP_CONF }; if (require.main === module) { main(); }