kerberos
This commit is contained in:
@@ -50,6 +50,12 @@ ENCRYPT_TRAFFIC=false
|
||||
# LDAP_USE_POWERSHELL_PASSWORD=true
|
||||
# LDAP_POWERSHELL_EXE=powershell.exe
|
||||
|
||||
# ============ Kerberos (password setting) ============
|
||||
# Additional domain controllers for the generated /etc/krb5.conf
|
||||
# (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
|
||||
|
||||
# ============ Email (registration) ============
|
||||
# SMTP server used to send verification codes and registration result letters.
|
||||
# If EMAIL_HOST is empty the registration endpoints return 503.
|
||||
|
||||
30
Dockerfile.example
Normal file
30
Dockerfile.example
Normal file
@@ -0,0 +1,30 @@
|
||||
# Example Dockerfile for running the service in a Linux container.
|
||||
# Password setting works via Kerberos (kinit + ldapmodify -Y GSSAPI):
|
||||
# - openldap-clients provides ldapmodify
|
||||
# - cyrus-sasl-gssapiv2 provides the SASL GSSAPI plugin used by ldapmodify
|
||||
# - krb5 provides kinit / kdestroy (there is no krb5-client
|
||||
# package in Alpine v3.24 — the tools are in krb5)
|
||||
#
|
||||
# /etc/krb5.conf is regenerated from .env at every start by
|
||||
# scripts/generate-krb5-conf.js (the static file below is only a fallback).
|
||||
# Make sure the container environment does NOT contain
|
||||
# LDAP_USE_POWERSHELL_PASSWORD=true (remove it from the .env you mount).
|
||||
|
||||
FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache git build-base python3 \
|
||||
openldap-clients cyrus-sasl-gssapiv2 krb5
|
||||
|
||||
COPY config/krb5.conf /etc/krb5.conf
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
# Regenerate /etc/krb5.conf from .env at every start, then launch the server
|
||||
CMD ["sh", "-c", "node scripts/generate-krb5-conf.js && node server.js"]
|
||||
@@ -26,6 +26,7 @@ const config = {
|
||||
caFile: getEnv('LDAP_CA_FILE', ''),
|
||||
passwordMethod: getEnv('LDAP_PASSWORD_METHOD', passwordMethodDefault),
|
||||
ldapRealm: getEnv('LDAP_REALM', String(getEnv('LDAP_DOMAIN', 'school25.ru')).toUpperCase()),
|
||||
kdcServers: getEnv('LDAP_KDC_SERVERS', ''),
|
||||
powerShellExe: getEnv('LDAP_POWERSHELL_EXE', 'powershell.exe'),
|
||||
emailHost: getEnv('EMAIL_HOST', ''),
|
||||
emailPort: parseInt(getEnv('EMAIL_PORT', '587'), 10),
|
||||
|
||||
94
scripts/generate-krb5-conf.js
Normal file
94
scripts/generate-krb5-conf.js
Normal file
@@ -0,0 +1,94 @@
|
||||
'use strict';
|
||||
|
||||
// Generates /etc/krb5.conf from .env values (realm, DC list, domain).
|
||||
// Run at every container start before server.js:
|
||||
// node scripts/generate-krb5-conf.js
|
||||
// Output path defaults to /etc/krb5.conf; override with KRB5_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';
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
function writeKrb5Conf(content, outputPath) {
|
||||
const target = outputPath || DEFAULT_OUTPUT;
|
||||
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 outputPath = process.env.KRB5_CONF_PATH || DEFAULT_OUTPUT;
|
||||
const content = buildKrb5Conf({ realm, domain, kdcServers });
|
||||
const target = writeKrb5Conf(content, outputPath);
|
||||
console.log(`krb5.conf generated -> ${target} (realm ${realm}, kdc: ${kdcServers.join(', ') || '(DNS SRV)'})`);
|
||||
} catch (err) {
|
||||
console.error(`Не удалось сгенерировать krb5.conf: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { buildKrb5Conf, collectKdcServers, hostFromUrl, writeKrb5Conf, main, DEFAULT_OUTPUT };
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
Reference in New Issue
Block a user