126 lines
4.0 KiB
JavaScript
126 lines
4.0 KiB
JavaScript
'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();
|
||
} |