Files
ldap-auth-service/scripts/generate-krb5-conf.js
2026-08-21 12:16:37 +05:00

126 lines
4.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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();
}