diff --git a/src/app.ts b/src/app.ts index 623c70e..99d9da0 100644 --- a/src/app.ts +++ b/src/app.ts @@ -7,6 +7,7 @@ import { Authentication } from './auth.js'; import { IAuthentication } from './interfaces/Authentication.js'; import { RadiusServer } from './radius/RadiusServer.js'; import { ConsoleLogger, LogLevel } from './logger/ConsoleLogger.js'; +import { Logger } from './logger/Logger.js'; function isValidLogLevel(debug?: string): debug is LogLevel | undefined { switch (debug) { @@ -23,14 +24,15 @@ function isValidLogLevel(debug?: string): debug is LogLevel | undefined { } (async () => { - const logger = new ConsoleLogger( + const consoleLogger = new ConsoleLogger( (isValidLogLevel(process.env.LOGLEVEL) && process.env.LOGLEVEL) || (isValidLogLevel(config.loglevel) && config.loglevel) || (process.env.NODE_ENV === 'development' && LogLevel.Debug) || LogLevel.Log ); + Logger.registerLogger(consoleLogger); - const ctxLogger = logger.context('app'); + const logger = new Logger('app'); const { argv } = yargs(hideBin(process.argv)) .usage('NODE RADIUS Server\nUsage: radius-server') @@ -49,10 +51,10 @@ function isValidLogLevel(debug?: string): debug is LogLevel | undefined { argv: { port?: number; secret?: string; authentication?: string; authenticationOptions?: any }; }; - ctxLogger.log(`Listener Port: ${argv.port || 1812}`); - ctxLogger.log(`RADIUS Secret: ${argv.secret}`); - ctxLogger.log(`Auth ${argv.authentication}`); - ctxLogger.debug(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined, 3)}`); + logger.log(`Listener Port: ${argv.port || 1812}`); + logger.log(`RADIUS Secret: ${argv.secret}`); + logger.log(`Auth ${argv.authentication}`); + logger.debug(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined, 3)}`); // configure auth mechanism let auth: IAuthentication; @@ -60,13 +62,13 @@ function isValidLogLevel(debug?: string): debug is LogLevel | undefined { const AuthMechanism = (await import(`./auth/${config.authentication}.js`))[ config.authentication ]; - auth = new AuthMechanism(config.authenticationOptions, logger); + auth = new AuthMechanism(config.authenticationOptions); } catch (err) { - ctxLogger.error('cannot load auth mechanisms', config.authentication); + logger.error('cannot load auth mechanisms', config.authentication); throw err; } // start radius server - const authentication = new Authentication(auth, logger); + const authentication = new Authentication(auth); const server = new RadiusServer({ secret: config.secret, @@ -74,7 +76,7 @@ function isValidLogLevel(debug?: string): debug is LogLevel | undefined { address: '0.0.0.0', tlsOptions: config.certificate, authentication, - logger, + logger: consoleLogger, }); // start server diff --git a/src/auth.ts b/src/auth.ts index 94d45d4..9f0e52d 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -1,7 +1,7 @@ import NodeCache from 'node-cache'; import { Cache, ExpirationStrategy, MemoryStorage } from '@hokify/node-ts-cache'; import { IAuthentication } from './interfaces/Authentication.js'; -import { IContextLogger, ILogger } from './interfaces/Logger.js'; +import { Logger } from './logger/Logger.js'; const cacheStrategy = new ExpirationStrategy(new MemoryStorage()); /** @@ -9,13 +9,11 @@ const cacheStrategy = new ExpirationStrategy(new MemoryStorage()); * an application layer for caching credentials */ export class Authentication implements IAuthentication { - private cache = new NodeCache(); + private logger = new Logger('Authentication'); - private logger: IContextLogger; + private cache = new NodeCache(); - constructor(private authenticator: IAuthentication, logger: ILogger) { - this.logger = logger.context('Authentication'); - } + constructor(private authenticator: IAuthentication) {} @Cache(cacheStrategy, { ttl: 60000 }) async authenticate(username: string, password: string): Promise { diff --git a/src/auth/GoogleLDAPAuth.ts b/src/auth/GoogleLDAPAuth.ts index 8555887..7a79760 100644 --- a/src/auth/GoogleLDAPAuth.ts +++ b/src/auth/GoogleLDAPAuth.ts @@ -2,7 +2,7 @@ import ldapjs, { ClientOptions } from 'ldapjs'; import * as tls from 'tls'; import * as fs from 'fs'; import { IAuthentication } from '../interfaces/Authentication.js'; -import { IContextLogger, ILogger } from '../interfaces/Logger.js'; +import { Logger } from '../logger/Logger.js'; const usernameFields = ['posixUid', 'mail']; @@ -27,6 +27,8 @@ interface IGoogleLDAPAuthOptions { } export class GoogleLDAPAuth implements IAuthentication { + private logger = new Logger('GoogleLDAPAuth'); + private base: string; private config: ClientOptions; @@ -35,12 +37,9 @@ export class GoogleLDAPAuth implements IAuthentication { private dnsFetch: Promise<{ [key: string]: string }> | undefined; - private logger: IContextLogger; - - constructor(config: IGoogleLDAPAuthOptions, logger: ILogger) { + constructor(config: IGoogleLDAPAuthOptions) { this.base = config.base; this.searchBase = config.searchBase || `ou=users,${this.base}`; - this.logger = logger.context('GoogleLDAPAuth'); const tlsOptions = { key: fs.readFileSync(config.tls.keyFile), @@ -114,7 +113,7 @@ export class GoogleLDAPAuth implements IAuthentication { }, 60 * 60 * 12 * 1000); // reset cache after 12h return dnResult; } catch (err) { - console.error('dns fetch err', err); + this.logger.error('dns fetch err', err); // retry dns fetch next time this.dnsFetch = undefined; throw err; diff --git a/src/auth/HTTPAuth.ts b/src/auth/HTTPAuth.ts index 65aed30..aea8d88 100644 --- a/src/auth/HTTPAuth.ts +++ b/src/auth/HTTPAuth.ts @@ -1,19 +1,18 @@ import fetch from 'node-fetch'; import { IAuthentication } from '../interfaces/Authentication.js'; -import { IContextLogger, ILogger } from '../interfaces/Logger.js'; +import { Logger } from '../logger/Logger.js'; interface IHTTPAuthOptions { url: string; } export class HTTPAuth implements IAuthentication { - private url: string; + private logger = new Logger('HTTPAuth'); - private logger: IContextLogger; + private url: string; - constructor(config: IHTTPAuthOptions, logger: ILogger) { + constructor(config: IHTTPAuthOptions) { this.url = config.url; - this.logger = logger.context('HTTPAuth'); } async authenticate(username: string, password: string) { diff --git a/src/auth/IMAPAuth.ts b/src/auth/IMAPAuth.ts index bca0e3a..2d9e277 100644 --- a/src/auth/IMAPAuth.ts +++ b/src/auth/IMAPAuth.ts @@ -1,6 +1,6 @@ import * as imaps from 'imap-simple'; import { IAuthentication } from '../interfaces/Authentication.js'; -import { IContextLogger, ILogger } from '../interfaces/Logger.js'; +import { Logger } from '../logger/Logger.js'; interface IIMAPAuthOptions { host: string; @@ -10,6 +10,8 @@ interface IIMAPAuthOptions { } export class IMAPAuth implements IAuthentication { + private logger = new Logger('IMAPAuth'); + private host: string; private port = 143; @@ -18,9 +20,7 @@ export class IMAPAuth implements IAuthentication { private validHosts?: string[]; - private logger: IContextLogger; - - constructor(config: IIMAPAuthOptions, logger: ILogger) { + constructor(config: IIMAPAuthOptions) { this.host = config.host; if (config.port !== undefined) { this.port = config.port; @@ -31,14 +31,13 @@ export class IMAPAuth implements IAuthentication { if (config.validHosts !== undefined) { this.validHosts = config.validHosts; } - this.logger = logger.context('IMAPAuth'); } async authenticate(username: string, password: string) { if (this.validHosts) { const domain = username.split('@').pop(); if (!domain || !this.validHosts.includes(domain)) { - this.logger.log('invalid or no domain in username', username, domain); + this.logger.log(`invalid or no domain in username ${username} ${domain}`); return false; } } diff --git a/src/auth/LDAPAuth.ts b/src/auth/LDAPAuth.ts index 15c3b98..d2aaf18 100644 --- a/src/auth/LDAPAuth.ts +++ b/src/auth/LDAPAuth.ts @@ -1,7 +1,7 @@ import * as LdapAuth from 'ldapauth-fork'; import * as fs from 'fs'; import { IAuthentication } from '../interfaces/Authentication.js'; -import { IContextLogger, ILogger } from '../interfaces/Logger.js'; +import { Logger } from '../logger/Logger.js'; interface ILDAPAuthOptions { /** ldap url @@ -28,17 +28,16 @@ interface ILDAPAuthOptions { } export class LDAPAuth implements IAuthentication { - private ldap: LdapAuth; + private logger = new Logger('LDAPAuth'); - private logger: IContextLogger; + private ldap: LdapAuth; - constructor(config: ILDAPAuthOptions, logger: ILogger) { + constructor(config: ILDAPAuthOptions) { const tlsOptions = { key: fs.readFileSync(config.tls.keyFile), cert: fs.readFileSync(config.tls.certFile), ...config.tlsOptions, }; - this.logger = logger.context('LDAPAuth'); this.ldap = new LdapAuth({ url: config.url, diff --git a/src/auth/SMTPAuth.ts b/src/auth/SMTPAuth.ts index 5a1a4ae..8e43a2d 100644 --- a/src/auth/SMTPAuth.ts +++ b/src/auth/SMTPAuth.ts @@ -1,6 +1,6 @@ import { SMTPClient } from 'smtp-client'; import { IAuthentication } from '../interfaces/Authentication.js'; -import { IContextLogger, ILogger } from '../interfaces/Logger.js'; +import { Logger } from '../logger/Logger.js'; interface ISMTPAuthOptions { host: string; @@ -10,6 +10,8 @@ interface ISMTPAuthOptions { } export class SMTPAuth implements IAuthentication { + private logger = new Logger('SMTPAuth'); + private host: string; private port = 25; @@ -18,10 +20,7 @@ export class SMTPAuth implements IAuthentication { private validHosts?: string[]; - private logger: IContextLogger; - - constructor(options: ISMTPAuthOptions, logger: ILogger) { - this.logger = logger.context('SMTPAuth'); + constructor(options: ISMTPAuthOptions) { this.host = options.host; if (options.port !== undefined) { @@ -41,7 +40,7 @@ export class SMTPAuth implements IAuthentication { if (this.validHosts) { const domain = username.split('@').pop(); if (!domain || !this.validHosts.includes(domain)) { - this.logger.log('invalid or no domain in username', username, domain); + this.logger.log(`invalid or no domain in username ${username} ${domain}`); return false; } } diff --git a/src/auth/StaticAuth.ts b/src/auth/StaticAuth.ts index 1f0202c..58beeaf 100644 --- a/src/auth/StaticAuth.ts +++ b/src/auth/StaticAuth.ts @@ -1,5 +1,5 @@ import { IAuthentication } from '../interfaces/Authentication.js'; -import { IContextLogger, ILogger } from '../interfaces/Logger.js'; +import { Logger } from '../logger/Logger.js'; interface IStaticAuthOtions { validCredentials: { @@ -9,11 +9,11 @@ interface IStaticAuthOtions { } export class StaticAuth implements IAuthentication { + private logger = new Logger('StaticAuth'); + private validCredentials: { username: string; password: string }[]; - private logger: IContextLogger; - constructor(options: IStaticAuthOtions, logger: ILogger) { - this.logger = logger.context('StaticAuth'); + constructor(options: IStaticAuthOtions) { this.validCredentials = options.validCredentials; } diff --git a/src/index.ts b/src/index.ts index dfdd1e5..c3b6b3e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,14 @@ import { IAuthentication } from './interfaces/Authentication.js'; -import { RadiusServerOptions } from './interfaces/RadiusServerOptions.js'; +import { IRadiusServerOptions } from './interfaces/RadiusServerOptions.js'; import { RadiusServer } from './radius/RadiusServer.js'; +import { ILogger } from './interfaces/Logger.js'; -export { IAuthentication as RadiusAuthentication, RadiusServerOptions, RadiusServer }; +export { + IAuthentication as RadiusAuthentication, + IRadiusServerOptions as RadiusServerOptions, + RadiusServer, + ILogger as RadiusLogger, +}; /* Export RadiusServer and relevant interfaces, so it can be used in other projects (e.g. a NestJS backend) diff --git a/src/interfaces/Logger.ts b/src/interfaces/Logger.ts index 090c464..85508b8 100644 --- a/src/interfaces/Logger.ts +++ b/src/interfaces/Logger.ts @@ -1,16 +1,7 @@ export interface ILogger { - log(context: string, message: unknown, ...optionalParams: any[]): void; - error(context: string, message: unknown, ...optionalParams: any[]): void; - warn(context: string, message: unknown, ...optionalParams: any[]): void; - debug(context: string, message: unknown, ...optionalParams: any[]): void; - verbose?(context: string, message: unknown, ...optionalParams: any[]): void; - context(context: string): IContextLogger; -} - -export interface IContextLogger extends ILogger { - log(message: unknown, ...optionalParams: any[]): void; - error(message: unknown, ...optionalParams: any[]): void; - warn(message: unknown, ...optionalParams: any[]): void; - debug(message: unknown, ...optionalParams: any[]): void; - verbose?(message: unknown, ...optionalParams: any[]): void; + log(context: string, message: string, ...optionalParams: unknown[]): void; + error(context: string, message: string, ...optionalParams: unknown[]): void; + warn(context: string, message: string, ...optionalParams: unknown[]): void; + debug(context: string, message: string, ...optionalParams: unknown[]): void; + verbose(context: string, message: string, ...optionalParams: unknown[]): void; } diff --git a/src/interfaces/RadiusServerOptions.ts b/src/interfaces/RadiusServerOptions.ts index 51db0b2..315ed07 100644 --- a/src/interfaces/RadiusServerOptions.ts +++ b/src/interfaces/RadiusServerOptions.ts @@ -3,21 +3,13 @@ import { IAuthentication } from './Authentication.js'; import { ILogger } from './Logger.js'; import { LogLevel } from '../logger/ConsoleLogger.js'; -export type RadiusServerOptions = IRadiusServerOptions & - ( - | { - logger?: ILogger; - } - | { - logLevel: LogLevel; - } - ); - -interface IRadiusServerOptions { +export interface IRadiusServerOptions { secret: string; tlsOptions: SecureContextOptions; authentication: IAuthentication; vlan?: number; port?: number; address?: string; + logger?: ILogger; + logLevel?: LogLevel; } diff --git a/src/logger/ConsoleLogger.ts b/src/logger/ConsoleLogger.ts index 85849a6..7edef26 100644 --- a/src/logger/ConsoleLogger.ts +++ b/src/logger/ConsoleLogger.ts @@ -10,72 +10,66 @@ export enum LogLevel { export class ConsoleLogger implements ILogger { constructor(private readonly logLevel: LogLevel) { - console.log(`ConsoleLogger initialized with LogLevel: ${logLevel}`); + this.debug('ConsoleLogger', `initialized with LogLevel: ${logLevel}`); } - error(context: string, message: unknown, ...optionalParams: unknown[]): void { + public error(context: string, message: string, ...optionalParams: unknown[]): void { switch (this.logLevel) { case LogLevel.Verbose: case LogLevel.Debug: case LogLevel.Log: case LogLevel.Warn: case LogLevel.Error: - console.error(`[${context}]`, message, ...optionalParams); + // eslint-disable-next-line no-console + console.error(`[${context ?? '?'}]`, message, ...optionalParams); break; default: } } - warn(context: string, message: unknown, ...optionalParams: unknown[]): void { + public warn(context: string, message: string, ...optionalParams: unknown[]): void { switch (this.logLevel) { case LogLevel.Verbose: case LogLevel.Debug: case LogLevel.Log: case LogLevel.Warn: - console.warn(`[${context}]`, message, ...optionalParams); + // eslint-disable-next-line no-console + console.warn(`[${context ?? '?'}]`, message, ...optionalParams); break; default: } } - log(context: string, message: unknown, ...optionalParams: unknown[]): void { + public log(context: string, message: string, ...optionalParams: unknown[]): void { switch (this.logLevel) { case LogLevel.Verbose: case LogLevel.Debug: case LogLevel.Log: - console.log(`[${context}]`, message, ...optionalParams); + // eslint-disable-next-line no-console + console.log(`[${context ?? '?'}]`, message, ...optionalParams); break; default: } } - debug(context: string, message: unknown, ...optionalParams: unknown[]): void { + public debug(context: string, message: string, ...optionalParams: unknown[]): void { switch (this.logLevel) { case LogLevel.Verbose: case LogLevel.Debug: - console.debug(`[${context}]`, message, ...optionalParams); + // eslint-disable-next-line no-console + console.debug(`[${context ?? '?'}]`, message, ...optionalParams); break; default: } } - verbose(context: string, message: unknown, ...optionalParams: unknown[]): void { + public verbose(context: string, message: string, ...optionalParams: unknown[]): void { switch (this.logLevel) { case LogLevel.Verbose: - console.debug(`[${context}]`, message, ...optionalParams); + // eslint-disable-next-line no-console + console.debug(`[${context ?? '?'}]`, message, ...optionalParams); break; default: } } - - context(context: string) { - return { - context: this.context.bind(this), - error: (message: unknown, ...args) => this.error(context, message, ...args), - warn: (message: unknown, ...args) => this.warn(context, message, ...args), - log: (message: unknown, ...args) => this.log(context, message, ...args), - debug: (message: unknown, ...args) => this.debug(context, message, ...args), - verbose: (message: unknown, ...args) => this.verbose(context, message, ...args), - }; - } } diff --git a/src/logger/Logger.ts b/src/logger/Logger.ts new file mode 100644 index 0000000..182590f --- /dev/null +++ b/src/logger/Logger.ts @@ -0,0 +1,31 @@ +import { ILogger } from '../interfaces/Logger.js'; + +export class Logger implements ILogger { + private static instanceLogger?: ILogger; + + public constructor(public readonly context?: string) {} + + public static registerLogger(logger: ILogger): void { + this.instanceLogger = logger; + } + + public log(message: string, ...optionalParams: unknown[]): void { + Logger.instanceLogger?.log(this.context ?? '?', message, ...optionalParams); + } + + public error(message: string, ...optionalParams: unknown[]): void { + Logger.instanceLogger?.error(this.context ?? '?', message, ...optionalParams); + } + + public warn(message: string, ...optionalParams: unknown[]): void { + Logger.instanceLogger?.warn(this.context ?? '?', message, ...optionalParams); + } + + public debug(message: string, ...optionalParams: unknown[]): void { + Logger.instanceLogger?.debug(this.context ?? '?', message, ...optionalParams); + } + + public verbose(message: string, ...optionalParams: unknown[]): void { + Logger.instanceLogger?.verbose(this.context ?? '?', message, ...optionalParams); + } +} diff --git a/src/radius/PacketHandler.ts b/src/radius/PacketHandler.ts index b19a935..1a5800b 100644 --- a/src/radius/PacketHandler.ts +++ b/src/radius/PacketHandler.ts @@ -7,32 +7,27 @@ import { EAPTTLS } from './handler/eap/eapMethods/EAP-TTLS.js'; import { EAPGTC } from './handler/eap/eapMethods/EAP-GTC.js'; import { EAPMD5 } from './handler/eap/eapMethods/EAP-MD5.js'; import { UserPasswordPacketHandler } from './handler/UserPasswordPacketHandler.js'; -import { IContextLogger, ILogger } from '../interfaces/Logger.js'; +import { Logger } from '../logger/Logger.js'; export class PacketHandler implements IPacketHandler { - packetHandlers: IPacketHandler[] = []; + private logger = new Logger('PacketHandler'); - private logger: IContextLogger; + packetHandlers: IPacketHandler[] = []; constructor( authentication: IAuthentication, tlsOptions: tls.SecureContextOptions, - logger: ILogger, private secret: string, private vlan?: number ) { - this.logger = logger.context('PacketHandler'); this.packetHandlers.push( - new EAPPacketHandler( - [ - new EAPTTLS(authentication, tlsOptions, this, logger, secret, vlan), - new EAPGTC(authentication, logger), - new EAPMD5(authentication, logger), - ], - logger - ) + new EAPPacketHandler([ + new EAPTTLS(authentication, tlsOptions, this, secret, vlan), + new EAPGTC(authentication), + new EAPMD5(authentication), + ]) ); - this.packetHandlers.push(new UserPasswordPacketHandler(authentication, logger)); + this.packetHandlers.push(new UserPasswordPacketHandler(authentication)); } async handlePacket(packet: IPacket, handlingType?: number) { diff --git a/src/radius/RadiusServer.ts b/src/radius/RadiusServer.ts index cea3066..371d6b4 100644 --- a/src/radius/RadiusServer.ts +++ b/src/radius/RadiusServer.ts @@ -5,31 +5,25 @@ import { IPacketHandlerResult, PacketResponseCode } from '../interfaces/PacketHa import { PacketHandler } from './PacketHandler.js'; import { UDPServer } from '../server/UDPServer.js'; import { startTLSServer } from '../tls/crypt.js'; -import { RadiusServerOptions } from '../interfaces/RadiusServerOptions.js'; -import { ConsoleLogger, LogLevel } from '../logger/ConsoleLogger.js'; - -function hasLogger(options): options is { logger: ConsoleLogger } { - return !!options.logger; -} - -function hasLogLevel(options): options is { logLevel?: LogLevel } { - return options.logLevel !== undefined; -} +import { IRadiusServerOptions } from '../interfaces/RadiusServerOptions.js'; +import { ConsoleLogger } from '../logger/ConsoleLogger.js'; +import { Logger } from '../logger/Logger.js'; export class RadiusServer extends UDPServer { + protected override logger = new Logger('RadiusServer'); + private packetHandler: PacketHandler; - constructor(private options: RadiusServerOptions) { - super( - options.port || 1812, - options.address || '0.0.0.0', - (hasLogger(options) && options.logger) || - new ConsoleLogger((hasLogLevel(options) && options.logLevel) || LogLevel.Log) - ); + constructor(private options: IRadiusServerOptions) { + super(options.port || 1812, options.address || '0.0.0.0'); + if (options.logger) { + Logger.registerLogger(options.logger); + } else if (options.logLevel) { + Logger.registerLogger(new ConsoleLogger(options.logLevel)); + } this.packetHandler = new PacketHandler( options.authentication, options.tlsOptions, - this.logger, options.secret, options.vlan ); @@ -38,10 +32,10 @@ export class RadiusServer extends UDPServer { public override async start(): Promise { // test node version - const testSocket = startTLSServer(this.options.tlsOptions, this.logger); + const testSocket = startTLSServer(this.options.tlsOptions); if (typeof testSocket.tls.exportKeyingMaterial !== 'function') { - this.logger.error('RadiusServer', `UNSUPPORTED NODE VERSION (${process.version}) FOUND!!`); - this.logger.log('RadiusServer', 'min version supported is node js 14. run "sudo npx n 14"'); + this.logger.error(`UNSUPPORTED NODE VERSION (${process.version}) FOUND!!`); + this.logger.log('min version supported is node js 14. run "sudo npx n 14"'); process.exit(-1); } return super.start(); @@ -66,7 +60,7 @@ export class RadiusServer extends UDPServer { ); } } catch (err) { - this.logger.error('RadiusServer', 'err', err); + this.logger.error('err', err); } }); } diff --git a/src/radius/handler/EAPPacketHandler.ts b/src/radius/handler/EAPPacketHandler.ts index 8b21e7e..df33c10 100644 --- a/src/radius/handler/EAPPacketHandler.ts +++ b/src/radius/handler/EAPPacketHandler.ts @@ -5,19 +5,17 @@ import { makeid } from '../../helpers.js'; import { IPacket, IPacketHandler, IPacketHandlerResult } from '../../interfaces/PacketHandler.js'; import { IEAPMethod } from '../../interfaces/EAPMethod.js'; import { buildEAPResponse, decodeEAPHeader } from './eap/EAPHelper.js'; -import { IContextLogger, ILogger } from '../../interfaces/Logger.js'; +import { Logger } from '../../logger/Logger.js'; export class EAPPacketHandler implements IPacketHandler { + private logger = new Logger('EAPPacketHandler'); + private identities = new NodeCache({ useClones: false, stdTTL: 60 }); // queue data maximum for 60 seconds // private eapConnectionStates: { [key: string]: { validMethods: IEAPMethod[] } } = {}; private eapConnectionStates = new NodeCache({ useClones: false, stdTTL: 3600 }); // max for one hour - private logger: IContextLogger; - - constructor(private eapMethods: IEAPMethod[], logger: ILogger) { - this.logger = logger.context('EAPPacketHandler'); - } + constructor(private eapMethods: IEAPMethod[]) {} async handlePacket(packet: IPacket, handlingType?: number): Promise { if (!packet.attributes['EAP-Message']) { diff --git a/src/radius/handler/UserPasswordPacketHandler.ts b/src/radius/handler/UserPasswordPacketHandler.ts index 329356d..a93a44c 100644 --- a/src/radius/handler/UserPasswordPacketHandler.ts +++ b/src/radius/handler/UserPasswordPacketHandler.ts @@ -5,14 +5,12 @@ import { IPacketHandlerResult, PacketResponseCode, } from '../../interfaces/PacketHandler.js'; -import { IContextLogger, ILogger } from '../../interfaces/Logger.js'; +import { Logger } from '../../logger/Logger.js'; export class UserPasswordPacketHandler implements IPacketHandler { - private logger: IContextLogger; + private logger = new Logger('UserPasswordPacketHandler'); - constructor(private authentication: IAuthentication, logger: ILogger) { - this.logger = logger.context('UserPasswordPacketHandler'); - } + constructor(private authentication: IAuthentication) {} async handlePacket(packet: IPacket): Promise { const username = packet.attributes['User-Name']; diff --git a/src/radius/handler/eap/eapMethods/EAP-GTC.ts b/src/radius/handler/eap/eapMethods/EAP-GTC.ts index 0bf11d7..76297c2 100644 --- a/src/radius/handler/eap/eapMethods/EAP-GTC.ts +++ b/src/radius/handler/eap/eapMethods/EAP-GTC.ts @@ -5,10 +5,10 @@ import { IPacketHandlerResult, PacketResponseCode } from '../../../../interfaces import { IEAPMethod } from '../../../../interfaces/EAPMethod.js'; import { IAuthentication } from '../../../../interfaces/Authentication.js'; import { buildEAPResponse, decodeEAPHeader } from '../EAPHelper.js'; -import { IContextLogger, ILogger } from '../../../../interfaces/Logger.js'; +import { Logger } from '../../../../logger/Logger.js'; export class EAPGTC implements IEAPMethod { - private logger: IContextLogger; + private logger = new Logger('EAPGTC'); getEAPType(): number { return 6; @@ -26,9 +26,7 @@ export class EAPGTC implements IEAPMethod { return buildEAPResponse(identifier, 6, Buffer.from('Password: ')); } - constructor(private authentication: IAuthentication, logger: ILogger) { - this.logger = logger.context('EAPGTC'); - } + constructor(private authentication: IAuthentication) {} async handleMessage( _identifier: number, diff --git a/src/radius/handler/eap/eapMethods/EAP-MD5.ts b/src/radius/handler/eap/eapMethods/EAP-MD5.ts index 7661c46..8314a6c 100644 --- a/src/radius/handler/eap/eapMethods/EAP-MD5.ts +++ b/src/radius/handler/eap/eapMethods/EAP-MD5.ts @@ -5,10 +5,10 @@ import { RadiusPacket } from 'radius'; import { IPacketHandlerResult } from '../../../../interfaces/PacketHandler.js'; import { IEAPMethod } from '../../../../interfaces/EAPMethod.js'; import { IAuthentication } from '../../../../interfaces/Authentication.js'; -import { IContextLogger, ILogger } from '../../../../interfaces/Logger.js'; +import { Logger } from '../../../../logger/Logger.js'; export class EAPMD5 implements IEAPMethod { - private logger: IContextLogger; + private logger = new Logger('EAPMD5'); getEAPType(): number { return 4; @@ -19,9 +19,7 @@ export class EAPMD5 implements IEAPMethod { return {}; } - constructor(private authentication: IAuthentication, logger: ILogger) { - this.logger = logger.context('EAPMD5'); - } + constructor(private authentication: IAuthentication) {} async handleMessage( _identifier: number, diff --git a/src/radius/handler/eap/eapMethods/EAP-TTLS.ts b/src/radius/handler/eap/eapMethods/EAP-TTLS.ts index 5651d68..a2d494c 100644 --- a/src/radius/handler/eap/eapMethods/EAP-TTLS.ts +++ b/src/radius/handler/eap/eapMethods/EAP-TTLS.ts @@ -16,7 +16,7 @@ import { import { MAX_RADIUS_ATTRIBUTE_SIZE, newDeferredPromise } from '../../../../helpers.js'; import { EAPMessageType, IEAPMethod } from '../../../../interfaces/EAPMethod.js'; import { IAuthentication } from '../../../../interfaces/Authentication.js'; -import { IContextLogger, ILogger } from '../../../../interfaces/Logger.js'; +import { Logger } from '../../../../logger/Logger.js'; function tlsHasExportKeyingMaterial(tlsSocket): tlsSocket is { exportKeyingMaterial: (length: number, label: string, context?: Buffer) => Buffer; @@ -37,6 +37,8 @@ interface IAVPEntry { } export class EAPTTLS implements IEAPMethod { + private logger = new Logger('EAPTTLS'); + private lastProcessedIdentifier = new NodeCache({ useClones: false, stdTTL: 60 }); // { [key: string]: Buffer } = {}; @@ -44,8 +46,6 @@ export class EAPTTLS implements IEAPMethod { private openTLSSockets = new NodeCache({ useClones: false, stdTTL: 3600 }); // keep sockets for about one hour - private logger: IContextLogger; - getEAPType(): number { return EAPMessageType.TTLS; } @@ -73,12 +73,9 @@ export class EAPTTLS implements IEAPMethod { private authentication: IAuthentication, private tlsOptions: tls.SecureContextOptions, private innerTunnel: IPacketHandler, - logger: ILogger, private secret: string, private vlan?: number - ) { - this.logger = logger.context('EAPTTLS'); - } + ) {} private buildEAPTTLS( identifier: number, @@ -89,7 +86,7 @@ export class EAPTTLS implements IEAPMethod { newResponse = true, maxSize = (MAX_RADIUS_ATTRIBUTE_SIZE - 5) * 4 ): Buffer { - this.logger.debug('maxSize', data?.length, ' > ', maxSize); + this.logger.debug('maxSize', `${data?.length} > ${maxSize}`); /* it's the first one and we have more, therefore include length */ const includeLength = maxSize > 0 && data && newResponse && data.length > maxSize; @@ -418,7 +415,7 @@ export class EAPTTLS implements IEAPMethod { let connection = this.openTLSSockets.get(stateID) as ITLSServer; if (!connection) { - connection = startTLSServer(this.tlsOptions, this.logger); + connection = startTLSServer(this.tlsOptions); this.openTLSSockets.set(stateID, connection); connection.events.on('end', () => { @@ -572,7 +569,7 @@ export class EAPTTLS implements IEAPMethod { // send response return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData); } catch (err) { - this.logger.error('decoding of EAP-TTLS package failed', msg, err); + this.logger.error(`decoding of EAP-TTLS package failed: ${msg}`, err); return { code: PacketResponseCode.AccessReject, }; diff --git a/src/server/UDPServer.ts b/src/server/UDPServer.ts index 557bf3a..6776b36 100644 --- a/src/server/UDPServer.ts +++ b/src/server/UDPServer.ts @@ -4,23 +4,19 @@ import * as events from 'events'; import { EventEmitter } from 'events'; import { newDeferredPromise } from '../helpers.js'; import type { IServer } from '../interfaces/Server.js'; -import type { ILogger } from '../interfaces/Logger.js'; +import { Logger } from '../logger/Logger.js'; export class UDPServer extends events.EventEmitter implements IServer { + protected logger = new Logger('UDPServer'); + static MAX_RETRIES = 3; private timeout: { [key: string]: NodeJS.Timeout } = {}; private server: dgram.Socket; - constructor( - private port: number, - private address: string, - protected logger: ILogger, - type: SocketType = 'udp4' - ) { + constructor(private port: number, private address: string, type: SocketType = 'udp4') { super(); - this.server = dgram.createSocket(type); } @@ -36,7 +32,6 @@ export class UDPServer extends events.EventEmitter implements IServer { const sendResponse = (): void => { if (retried > 0) { this.logger.warn( - 'UDPServer', `no confirmation of last message from ${address}:${port}, re-sending response... (bytes: ${msg.length}, try: ${retried}/${UDPServer.MAX_RETRIES})` ); } @@ -61,7 +56,7 @@ export class UDPServer extends events.EventEmitter implements IServer { const startServer = newDeferredPromise(); this.server.on('listening', () => { const address = this.server.address(); - this.logger.log('UDPServer', `radius server listening ${address.address}:${address.port}`); + this.logger.log(`radius server listening ${address.address}:${address.port}`); this.setupListeners(); startServer.resolve(); diff --git a/src/tls/crypt.ts b/src/tls/crypt.ts index 81bb8f8..9343ca7 100644 --- a/src/tls/crypt.ts +++ b/src/tls/crypt.ts @@ -4,7 +4,7 @@ import { createSecureContext } from 'tls'; import * as crypto from 'crypto'; import DuplexPair from 'native-duplexpair'; import NodeCache from 'node-cache'; -import { ILogger } from '../interfaces/Logger.js'; +import { Logger } from '../logger/Logger.js'; export interface ITLSServer { events: events.EventEmitter; @@ -13,11 +13,11 @@ export interface ITLSServer { const resumeSessions = new NodeCache({ stdTTL: 86400 }); // session reidentification maximum 1 day -// https://nodejs.org/api/tls.html -export function startTLSServer(tlsOptions: tls.SecureContextOptions, logger: ILogger): ITLSServer { - const ctxLogger = logger.context('crypt'); +const logger = new Logger('crypt'); - ctxLogger.debug('tlsOptions', tlsOptions); +// https://nodejs.org/api/tls.html +export function startTLSServer(tlsOptions: tls.SecureContextOptions): ITLSServer { + logger.debug('tlsOptions', tlsOptions); const secureContext = createSecureContext(tlsOptions); const duplexpair = new DuplexPair(); @@ -35,7 +35,7 @@ export function startTLSServer(tlsOptions: tls.SecureContextOptions, logger: ILo // for older tls versions without ticketing support cleartext.on('newSession', (sessionId: Buffer, sessionData: Buffer, callback: () => void) => { - ctxLogger.debug(`TLS new session (${sessionId.toString('hex')})`); + logger.debug(`TLS new session (${sessionId.toString('hex')})`); resumeSessions.set(sessionId.toString('hex'), sessionData); callback(); @@ -47,7 +47,7 @@ export function startTLSServer(tlsOptions: tls.SecureContextOptions, logger: ILo const resumedSession = (resumeSessions.get(sessionId.toString('hex')) as Buffer) || null; if (resumedSession) { - ctxLogger.debug(`TLS resumed session (${sessionId.toString('hex')})`); + logger.debug(`TLS resumed session (${sessionId.toString('hex')})`); } callback(null, resumedSession); @@ -73,7 +73,7 @@ export function startTLSServer(tlsOptions: tls.SecureContextOptions, logger: ILo const cipher = cleartext.getCipher(); if (cipher) { - ctxLogger.debug(`TLS negotiated (${cipher.name}, ${cipher.version})`); + logger.debug(`TLS negotiated (${cipher.name}, ${cipher.version})`); } cleartext.on('data', (data: Buffer) => { @@ -82,21 +82,21 @@ export function startTLSServer(tlsOptions: tls.SecureContextOptions, logger: ILo }); cleartext.once('close', (_data: Buffer) => { - ctxLogger.debug('cleartext close'); + logger.debug('cleartext close'); emitter.emit('end'); }); cleartext.on('keylog', (line) => { - ctxLogger.debug('############ KEYLOG #############', line); + logger.debug('############ KEYLOG #############', line); // cleartext.getTicketKeys() }); - ctxLogger.debug('*********** new TLS connection established / secured ********'); + logger.debug('*********** new TLS connection established / secured ********'); emitter.emit('secured', cleartext.isSessionReused()); }); cleartext.on('error', (err?: Error) => { - ctxLogger.debug('cleartext error', err); + logger.debug('cleartext error', err); encrypted.destroy(); cleartext.destroy(err); @@ -177,7 +177,7 @@ export function encodeTunnelPW(key: Buffer, authenticator: Buffer, secret: strin Intermediate values b(1), b(2)...c(i) are required. Encryption is performed in the following manner ('+' indicates concatenation): - + Zorn Informational [Page 21] RFC 2548 Microsoft Vendor-specific RADIUS Attributes March 1999