diff --git a/.gitignore b/.gitignore
index 819fd6c..ddb1729 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,5 @@ tsconfig.tsbuildinfo
# custom certificates
/ssl-*/
+
+.idea
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..82a885f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,39 @@
+@coreid/node-radius-server - CoreID maintained fork
+Copyright (C) 2021 Simon Tretter, Garrett Mills
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+--
+
+Note: this is a fork of Simon Tretter's node-radius-server package
+modified for use in Starship CoreID. Per the terms of GPLv3, the
+modifications made are documented below:
+
+- Introduced the PackageInterface helper class
+ - enables integrating the library into code rather than as a
+ standalone CLI app
+
+- Modified existing code to use configuration on the PackageInterface
+ class instead of global imports
+
+- Modified existing code to log through PackageInterface.logger
+
+- Modified existing code to introduce a credentialMiddleware method
+ to IPacket, which allows external code to customize the credentials
+ before they are sent
+
+- Modified RadiusService to allow specifying a custom packet decoder
+ method on PackageInterface.
+
+- Modified UDPServer class to expose a stop() method
diff --git a/package.json b/package.json
index 5e6eb74..a2f3299 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
- "name": "radius-server",
- "description": "radius server for google LDAP and TTLS",
+ "name": "@coreid/radius-server",
+ "description": "CoreID fork of radius server for google LDAP and TTLS",
"version": "1.2.1",
"engines": {
"node": ">13.10.1"
@@ -14,7 +14,7 @@
"dist",
"ssl"
],
- "homepage": "https://github.com/simllll/node-radius-server",
+ "homepage": "https://code.garrettmills.dev/starship/node-radius-server",
"scripts": {
"release": "npm run build && standard-version",
"debug": "DEBUG=radius:* node --tls-min-v1.0 dist/app.js",
diff --git a/src/app.ts b/src/app.ts
index 2ee2672..1c59e0d 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -6,39 +6,44 @@ import * as config from '../config';
import { Authentication } from './auth';
import { IAuthentication } from './types/Authentication';
import { startTLSServer } from './tls/crypt';
+import PackageInterface from './interface';
-/* test node version */
-const testSocket = startTLSServer();
-if (typeof testSocket.tls.exportKeyingMaterial !== 'function') {
- console.error(`UNSUPPORTED NODE VERSION (${process.version}) FOUND!!`);
+const packageInterface = PackageInterface.get();
- console.log('min version supported is node js 14. run "sudo npx n 14"');
- process.exit(-1);
-}
+const prestartServer = () => {
+ /* test node version */
+ const testSocket = startTLSServer();
+ if (typeof testSocket.tls.exportKeyingMaterial !== 'function') {
+ packageInterface.log(`UNSUPPORTED NODE VERSION (${process.version}) FOUND!!`);
-const { argv } = yargs
- .usage('NODE RADIUS Server\nUsage: radius-server')
- .example('radius-server --port 1812 -s radiussecret', 'start on port 1812 with a secret')
- .default({
- port: config.port || 1812,
- s: config.secret || 'testing123',
- authentication: config.authentication,
- authenticationOptions: config.authenticationOptions,
- })
- .describe('port', 'RADIUS server listener port')
- .alias('s', 'secret')
- .describe('secret', 'RADIUS secret')
- .number('port')
- .string(['secret', 'authentication']) as {
- argv: { port?: number; secret?: string; authentication?: string; authenticationOptions?: any };
-};
+ packageInterface.log('min version supported is node js 14. run "sudo npx n 14"');
+ process.exit(-1);
+ }
+
+ const { argv } = yargs
+ .usage('NODE RADIUS Server\nUsage: radius-server')
+ .example('radius-server --port 1812 -s radiussecret', 'start on port 1812 with a secret')
+ .default({
+ port: config.port || 1812,
+ s: config.secret || 'testing123',
+ authentication: config.authentication,
+ authenticationOptions: config.authenticationOptions,
+ })
+ .describe('port', 'RADIUS server listener port')
+ .alias('s', 'secret')
+ .describe('secret', 'RADIUS secret')
+ .number('port')
+ .string(['secret', 'authentication']) as {
+ argv: { port?: number; secret?: string; authentication?: string; authenticationOptions?: any };
+ };
-console.log(`Listener Port: ${argv.port || 1812}`);
-console.log(`RADIUS Secret: ${argv.secret}`);
-console.log(`Auth ${argv.authentication}`);
-console.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined, 3)}`);
+ packageInterface.log(`Listener Port: ${argv.port || 1812}`);
+ packageInterface.log(`RADIUS Secret: ${argv.secret}`);
+ packageInterface.log(`Auth ${argv.authentication}`);
+ packageInterface.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined, 3)}`);
+};
-(async () => {
+const startServer = async () => {
/* configure auth mechansim */
let auth: IAuthentication;
try {
@@ -47,7 +52,7 @@ console.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined
];
auth = new AuthMechanismus(config.authenticationOptions);
} catch (err) {
- console.error('cannot load auth mechanismus', config.authentication);
+ packageInterface.log('cannot load auth mechanismus', config.authentication);
throw err;
}
// start radius server
@@ -66,7 +71,7 @@ console.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined
rinfo.address,
(err, _bytes) => {
if (err) {
- console.log('Error sending response to ', rinfo);
+ packageInterface.log('Error sending response to ', rinfo);
}
},
response.expectAcknowledgment
@@ -76,4 +81,9 @@ console.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined
// start server
await server.start();
-})();
+};
+
+if (packageInterface.start) {
+ prestartServer();
+ startServer();
+}
diff --git a/src/auth.ts b/src/auth.ts
index 3baf804..5fb2ba0 100644
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -1,8 +1,11 @@
import * as NodeCache from 'node-cache';
import { Cache, ExpirationStrategy, MemoryStorage } from '@hokify/node-ts-cache';
import { IAuthentication } from './types/Authentication';
+import PackageInterface from './interface';
const cacheStrategy = new ExpirationStrategy(new MemoryStorage());
+const packageInterface = PackageInterface.get();
+
/**
* this is just a simple abstraction to provide
* an application layer for caching credentials
@@ -12,18 +15,25 @@ export class Authentication implements IAuthentication {
constructor(private authenticator: IAuthentication) {}
- @Cache(cacheStrategy, { ttl: 60000 })
+ @Cache(cacheStrategy, { ttl: packageInterface.cacheTTL })
async authenticate(username: string, password: string): Promise {
const cacheKey = `usr:${username}|pwd:${password}`;
const fromCache = this.cache.get(cacheKey) as undefined | boolean;
if (fromCache !== undefined) {
- console.log(`Cached Auth Result for user ${username}`, fromCache ? 'SUCCESS' : 'Failure');
+ packageInterface.log(
+ `Cached Auth Result for user ${username}`,
+ fromCache ? 'SUCCESS' : 'Failure'
+ );
return fromCache;
}
const authResult = await this.authenticator.authenticate(username, password);
- console.log(`Auth Result for user ${username}`, authResult ? 'SUCCESS' : 'Failure');
- this.cache.set(cacheKey, authResult, authResult ? 86400 : 60); // cache for one day on success, otherwise just for 60 seconds
+ packageInterface.log(`Auth Result for user ${username}`, authResult ? 'SUCCESS' : 'Failure');
+ this.cache.set(
+ cacheKey,
+ authResult,
+ authResult ? packageInterface.cacheSuccessTTL : packageInterface.cacheFailTTL
+ ); // cache for one day on success, otherwise just for 60 seconds
return authResult;
}
diff --git a/src/auth/GoogleLDAPAuth.ts b/src/auth/GoogleLDAPAuth.ts
index 97702aa..367b6be 100644
--- a/src/auth/GoogleLDAPAuth.ts
+++ b/src/auth/GoogleLDAPAuth.ts
@@ -1,12 +1,14 @@
import { ClientOptions, createClient } from 'ldapjs';
-import debug from 'debug';
import * as tls from 'tls';
import * as fs from 'fs';
import { IAuthentication } from '../types/Authentication';
+import PackageInterface from '../interface';
+
+const packageInterface = PackageInterface.get();
+const log = (...args) => packageInterface.log(...args);
const usernameFields = ['posixUid', 'mail'];
-const log = debug('radius:auth:google-ldap');
// TLS:
// https://github.com/ldapjs/node-ldapjs/issues/307
@@ -55,7 +57,7 @@ export class GoogleLDAPAuth implements IAuthentication {
};
this.fetchDNs().catch((err) => {
- console.error('fatal error google ldap auth, cannot fetch DNs', err);
+ log('fatal error google ldap auth, cannot fetch DNs', err);
});
}
@@ -64,7 +66,7 @@ export class GoogleLDAPAuth implements IAuthentication {
await new Promise((resolve, reject) => {
const ldapDNClient = createClient(this.config).on('error', (error) => {
- console.error('Error in ldap', error);
+ log('Error in ldap', error);
reject(error);
});
@@ -92,7 +94,7 @@ export class GoogleLDAPAuth implements IAuthentication {
});
res.on('error', (ldapErr) => {
- console.error(`error: ${JSON.stringify(ldapErr)}`);
+ log(`error: ${JSON.stringify(ldapErr)}`);
reject(ldapErr);
});
@@ -144,7 +146,7 @@ export class GoogleLDAPAuth implements IAuthentication {
return this.authenticate(username, password, count, true);
}
// console.log('this.allValidDNsCache', this.allValidDNsCache);
- console.error(`invalid username, not found in DN: ${username}`); // , this.allValidDNsCache);
+ log(`invalid username, not found in DN: ${username}`); // , this.allValidDNsCache);
return false;
}
diff --git a/src/auth/HTTPAuth.ts b/src/auth/HTTPAuth.ts
index 247acf3..18806c3 100644
--- a/src/auth/HTTPAuth.ts
+++ b/src/auth/HTTPAuth.ts
@@ -1,5 +1,9 @@
import axios from 'axios';
import { IAuthentication } from '../types/Authentication';
+import PackageInterface from '../interface';
+
+const packageInterface = PackageInterface.get();
+const log = (...args) => packageInterface.log(...args);
interface IHTTPAuthOptions {
url: string;
@@ -30,7 +34,7 @@ export class HTTPAuth implements IAuthentication {
return true;
}
- console.log(`HTTP authentication failed, response code: ${result.status}`);
+ log(`HTTP authentication failed, response code: ${result.status}`);
return false;
}
diff --git a/src/auth/IMAPAuth.ts b/src/auth/IMAPAuth.ts
index f315b19..a2f2ae8 100644
--- a/src/auth/IMAPAuth.ts
+++ b/src/auth/IMAPAuth.ts
@@ -1,5 +1,9 @@
import * as imaps from 'imap-simple';
import { IAuthentication } from '../types/Authentication';
+import PackageInterface from '../interface';
+
+const packageInterface = PackageInterface.get();
+const log = (...args) => packageInterface.log(...args);
interface IIMAPAuthOptions {
host: string;
@@ -34,7 +38,7 @@ export class IMAPAuth implements IAuthentication {
if (this.validHosts) {
const domain = username.split('@').pop();
if (!domain || !this.validHosts.includes(domain)) {
- console.info('invalid or no domain in username', username, domain);
+ log('invalid or no domain in username', username, domain);
return false;
}
}
@@ -57,7 +61,7 @@ export class IMAPAuth implements IAuthentication {
connection.end();
} catch (err) {
- console.error('imap auth failed', err);
+ log('imap auth failed', err);
}
return success;
}
diff --git a/src/auth/LDAPAuth.ts b/src/auth/LDAPAuth.ts
index d032496..1daac1c 100644
--- a/src/auth/LDAPAuth.ts
+++ b/src/auth/LDAPAuth.ts
@@ -1,6 +1,10 @@
import * as LdapAuth from 'ldapauth-fork';
import * as fs from 'fs';
import { IAuthentication } from '../types/Authentication';
+import PackageInterface from '../interface';
+
+const packageInterface = PackageInterface.get();
+const log = (...args) => packageInterface.log(...args);
interface ILDAPAuthOptions {
/** ldap url
@@ -44,7 +48,7 @@ export class LDAPAuth implements IAuthentication {
reconnect: true,
});
this.ldap.on('error', (err) => {
- console.error('LdapAuth: ', err);
+ log('LdapAuth: ', err);
});
}
@@ -53,7 +57,7 @@ export class LDAPAuth implements IAuthentication {
this.ldap.authenticate(username, password, (err, user) => {
if (err) {
resolve(false);
- console.error('ldap error', err);
+ log('ldap error', err);
// reject(err);
}
if (user) resolve(user);
diff --git a/src/auth/SMTPAuth.ts b/src/auth/SMTPAuth.ts
index 93a3330..d68db40 100644
--- a/src/auth/SMTPAuth.ts
+++ b/src/auth/SMTPAuth.ts
@@ -1,5 +1,8 @@
import { SMTPClient } from 'smtp-client';
import { IAuthentication } from '../types/Authentication';
+import PackageInterface from '../interface';
+
+const log = (...args) => PackageInterface.get().log(...args);
interface ISMTPAuthOptions {
host: string;
@@ -37,7 +40,7 @@ export class SMTPAuth implements IAuthentication {
if (this.validHosts) {
const domain = username.split('@').pop();
if (!domain || !this.validHosts.includes(domain)) {
- console.info('invalid or no domain in username', username, domain);
+ log('invalid or no domain in username', username, domain);
return false;
}
}
@@ -61,7 +64,7 @@ export class SMTPAuth implements IAuthentication {
s.close(); // runs QUIT command
} catch (err) {
- console.error('imap auth failed', err);
+ log('imap auth failed', err);
}
return success;
}
diff --git a/src/interface.ts b/src/interface.ts
new file mode 100644
index 0000000..e632e02
--- /dev/null
+++ b/src/interface.ts
@@ -0,0 +1,47 @@
+import * as radius from 'radius';
+import * as config from '../config';
+import { IPacket } from './types/PacketHandler';
+
+export type PacketDecoder = (msg: Buffer) => {
+ packet?: radius.RadiusPacket & IPacket;
+ secret: string;
+};
+
+export default class PackageInterface {
+ private static _instance?: PackageInterface;
+
+ public static get(): PackageInterface {
+ if (!this._instance) {
+ this._instance = new PackageInterface();
+ }
+
+ return this._instance;
+ }
+
+ public packetDecoder?: PacketDecoder;
+
+ public start = true;
+
+ public cacheTTL = 60000;
+
+ public cacheSuccessTTL = 86400;
+
+ public cacheFailTTL = 60;
+
+ public logger: (...any: unknown[]) => unknown = console.log; // eslint-disable-line no-console
+
+ private config: any = config;
+
+ public log(...any: unknown[]): void {
+ this.logger(...any);
+ }
+
+ public getConfig(): any {
+ return this.config;
+ }
+
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+ public setConfig(inConfig: any) {
+ this.config = inConfig;
+ }
+}
diff --git a/src/radius/RadiusService.ts b/src/radius/RadiusService.ts
index 1b0f28e..9ddfaf3 100644
--- a/src/radius/RadiusService.ts
+++ b/src/radius/RadiusService.ts
@@ -1,8 +1,11 @@
import * as radius from 'radius';
import { IAuthentication } from '../types/Authentication';
-import { IPacketHandlerResult, PacketResponseCode } from '../types/PacketHandler';
+import {IPacket, IPacketHandlerResult, PacketResponseCode} from '../types/PacketHandler';
import { PacketHandler } from './PacketHandler';
+import PackageInterface from '../interface';
+
+const packageInterface = PackageInterface.get();
export class RadiusService {
private packetHandler: PacketHandler;
@@ -11,13 +14,29 @@ export class RadiusService {
this.packetHandler = new PacketHandler(authentication);
}
+ defaultDecoder(msg: Buffer): { packet?: radius.RadiusPacket & IPacket; secret: string } {
+ const packet = radius.decode({ packet: msg, secret: this.secret });
+
+ return {
+ packet,
+ secret: this.secret,
+ };
+ }
+
async handleMessage(
msg: Buffer
): Promise<{ data: Buffer; expectAcknowledgment?: boolean } | undefined> {
- const packet = radius.decode({ packet: msg, secret: this.secret });
+ const { packet, secret } = packageInterface.packetDecoder
+ ? packageInterface.packetDecoder(msg)
+ : this.defaultDecoder(msg);
+
+ if (!packet) {
+ packageInterface.log('Unable to parse packet from message.');
+ return undefined;
+ }
if (packet.code !== 'Access-Request') {
- console.error('unknown packet type: ', packet.code);
+ packageInterface.log('unknown packet type: ', packet.code);
return undefined;
}
@@ -33,7 +52,7 @@ export class RadiusService {
data: radius.encode_response({
packet,
code: response.code,
- secret: this.secret,
+ secret,
attributes: response.attributes,
}),
// if message is accept or reject, we conside this as final message
diff --git a/src/radius/handler/EAPPacketHandler.ts b/src/radius/handler/EAPPacketHandler.ts
index 164515f..64c4230 100644
--- a/src/radius/handler/EAPPacketHandler.ts
+++ b/src/radius/handler/EAPPacketHandler.ts
@@ -1,13 +1,14 @@
// https://tools.ietf.org/html/rfc3748#section-4.1
import * as NodeCache from 'node-cache';
-import debug from 'debug';
import { makeid } from '../../helpers';
import { IPacket, IPacketHandler, IPacketHandlerResult } from '../../types/PacketHandler';
import { IEAPMethod } from '../../types/EAPMethod';
import { buildEAPResponse, decodeEAPHeader } from './eap/EAPHelper';
+import PackageInterface from '../../interface';
-const log = debug('radius:eap');
+const packageInterface = PackageInterface.get();
+const log = (...args) => packageInterface.log(...args);
export class EAPPacketHandler implements IPacketHandler {
private identities = new NodeCache({ useClones: false, stdTTL: 60 }); // queue data maximum for 60 seconds
@@ -66,15 +67,15 @@ export class EAPPacketHandler implements IPacketHandler {
return buildEAPResponse(identifier, 3); // NAK
case 2: // notification
log('>>>>>>>>>>>> REQUEST FROM CLIENT: notification', {});
- console.info('notification');
+ log('notification');
break;
case 4: // md5-challenge
log('>>>>>>>>>>>> REQUEST FROM CLIENT: md5-challenge', {});
- console.info('md5-challenge');
+ log('md5-challenge');
break;
case 254: // expanded type
- console.error('not implemented type', type);
+ log('not implemented type', type);
break;
case 3: // nak
// console.log('got NAK', data);
@@ -118,7 +119,7 @@ export class EAPPacketHandler implements IPacketHandler {
method.getEAPType()
);
- console.error('unsupported type', type, `requesting: ${serverSupportedMethods}`);
+ log('unsupported type', type, `requesting: ${serverSupportedMethods}`);
return buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods));
}
@@ -135,7 +136,7 @@ export class EAPPacketHandler implements IPacketHandler {
// silently ignore;
return {};
} catch (err) {
- console.error(
+ log(
'decoding of (generic) EAP package failed',
msg,
err,
diff --git a/src/radius/handler/UserPasswordPacketHandler.ts b/src/radius/handler/UserPasswordPacketHandler.ts
index 9abb4d2..746fb5c 100644
--- a/src/radius/handler/UserPasswordPacketHandler.ts
+++ b/src/radius/handler/UserPasswordPacketHandler.ts
@@ -1,4 +1,3 @@
-import debug from 'debug';
import { IAuthentication } from '../../types/Authentication';
import {
IPacket,
@@ -6,8 +5,10 @@ import {
IPacketHandlerResult,
PacketResponseCode,
} from '../../types/PacketHandler';
+import PackageInterface from '../../interface';
-const log = debug('radius:user-pwd');
+const packageInterface = PackageInterface.get();
+const log = (...args) => packageInterface.log(...args);
export class UserPasswordPacketHandler implements IPacketHandler {
constructor(private authentication: IAuthentication) {}
@@ -29,10 +30,11 @@ export class UserPasswordPacketHandler implements IPacketHandler {
log('username', username, username.toString());
log('token', password, password.toString());
- const authenticated = await this.authentication.authenticate(
- username.toString(),
- password.toString()
- );
+ const [strUsername, strPassword] = packet.credentialMiddleware
+ ? packet.credentialMiddleware(username.toString(), password.toString())
+ : [username.toString(), password.toString()];
+
+ const authenticated = await this.authentication.authenticate(strUsername, strPassword);
if (authenticated) {
// success
return {
diff --git a/src/radius/handler/eap/eapMethods/EAP-GTC.ts b/src/radius/handler/eap/eapMethods/EAP-GTC.ts
index 8f64084..4aacd51 100644
--- a/src/radius/handler/eap/eapMethods/EAP-GTC.ts
+++ b/src/radius/handler/eap/eapMethods/EAP-GTC.ts
@@ -1,13 +1,14 @@
// https://tools.ietf.org/html/rfc5281 TTLS v0
// https://tools.ietf.org/html/draft-funk-eap-ttls-v1-00 TTLS v1 (not implemented)
/* eslint-disable no-bitwise */
-import debug from 'debug';
import { IPacketHandlerResult, PacketResponseCode } from '../../../../types/PacketHandler';
import { IEAPMethod } from '../../../../types/EAPMethod';
import { IAuthentication } from '../../../../types/Authentication';
import { buildEAPResponse, decodeEAPHeader } from '../EAPHelper';
+import PackageInterface from '../../../../interface';
-const log = debug('radius:eap:gtc');
+const packageInterface = PackageInterface.get();
+const log = (...args) => packageInterface.log(...args);
export class EAPGTC implements IEAPMethod {
getEAPType(): number {
@@ -56,7 +57,7 @@ export class EAPGTC implements IEAPMethod {
attributes: (success && [['User-Name', username]]) || undefined,
};
} catch (err) {
- console.error('decoding of EAP-GTC package failed', msg, err);
+ log('decoding of EAP-GTC package failed', msg, err);
return {
code: PacketResponseCode.AccessReject,
};
diff --git a/src/radius/handler/eap/eapMethods/EAP-MD5.ts b/src/radius/handler/eap/eapMethods/EAP-MD5.ts
index db4c1ef..8e653c8 100644
--- a/src/radius/handler/eap/eapMethods/EAP-MD5.ts
+++ b/src/radius/handler/eap/eapMethods/EAP-MD5.ts
@@ -2,10 +2,12 @@
// https://tools.ietf.org/html/draft-funk-eap-ttls-v1-00 TTLS v1 (not implemented)
/* eslint-disable no-bitwise */
import { RadiusPacket } from 'radius';
-import debug from 'debug';
import { IPacketHandlerResult } from '../../../../types/PacketHandler';
import { IEAPMethod } from '../../../../types/EAPMethod';
import { IAuthentication } from '../../../../types/Authentication';
+import PackageInterface from '../../../../interface';
+
+const packageInterface = PackageInterface.get();
export class EAPMD5 implements IEAPMethod {
getEAPType(): number {
@@ -27,7 +29,7 @@ export class EAPMD5 implements IEAPMethod {
): Promise {
// not implemented
- debug('eap md5 not implemented...');
+ packageInterface.log('eap md5 not implemented...');
return {};
}
diff --git a/src/radius/handler/eap/eapMethods/EAP-TTLS.ts b/src/radius/handler/eap/eapMethods/EAP-TTLS.ts
index 31aed43..ab90ab3 100644
--- a/src/radius/handler/eap/eapMethods/EAP-TTLS.ts
+++ b/src/radius/handler/eap/eapMethods/EAP-TTLS.ts
@@ -6,8 +6,6 @@ import * as NodeCache from 'node-cache';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { attr_id_to_name, attr_name_to_id } from 'radius';
-import debug from 'debug';
-
import { encodeTunnelPW, ITLSServer, startTLSServer } from '../../../../tls/crypt';
import {
IPacket,
@@ -20,8 +18,10 @@ import { MAX_RADIUS_ATTRIBUTE_SIZE, newDeferredPromise } from '../../../../helpe
import { IEAPMethod } from '../../../../types/EAPMethod';
import { IAuthentication } from '../../../../types/Authentication';
import { secret } from '../../../../../config';
+import PackageInterface from '../../../../interface';
-const log = debug('radius:eap:ttls');
+const packageInterface = PackageInterface.get();
+const log = (...args) => packageInterface.log(...args);
function tlsHasExportKeyingMaterial(tlsSocket): tlsSocket is {
exportKeyingMaterial: (length: number, label: string, context?: Buffer) => Buffer;
@@ -276,7 +276,7 @@ export class EAPTTLS implements IEAPMethod {
[[17, encodeTunnelPW(keyingMaterial.slice(0, 64), packet.authenticator, secret)]],
]); // MS-MPPE-Recv-Key
} else {
- console.error(
+ log(
'FATAL: no exportKeyingMaterial method available!!!, you need latest NODE JS, see https://github.com/nodejs/node/pull/31814'
);
}
@@ -420,7 +420,7 @@ export class EAPTTLS implements IEAPMethod {
// send response
return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData);
} catch (err) {
- console.error('decoding of EAP-TTLS package failed', msg, err);
+ log('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 7767a64..d0a131e 100644
--- a/src/server/UDPServer.ts
+++ b/src/server/UDPServer.ts
@@ -4,6 +4,9 @@ import * as events from 'events';
import { EventEmitter } from 'events';
import { newDeferredPromise } from '../helpers';
import { IServer } from '../types/Server';
+import PackageInterface from '../interface';
+
+const packageInterface = PackageInterface.get();
export class UDPServer extends events.EventEmitter implements IServer {
static MAX_RETRIES = 3;
@@ -53,7 +56,7 @@ export class UDPServer extends events.EventEmitter implements IServer {
const startServer = newDeferredPromise();
this.server.on('listening', () => {
const address = this.server.address();
- console.log(`radius server listening ${address.address}:${address.port}`);
+ packageInterface.log(`radius server listening ${address.address}:${address.port}`);
this.setupListeners();
startServer.resolve();
@@ -72,6 +75,12 @@ export class UDPServer extends events.EventEmitter implements IServer {
return startServer.promise;
}
+ stop(): Promise {
+ return new Promise((res) => {
+ this.server.close(() => res());
+ });
+ }
+
private setupListeners() {
this.server.on('message', (message, rinfo) => this.emit('message', message, rinfo));
}
diff --git a/src/tls/crypt.ts b/src/tls/crypt.ts
index 6c69942..653f09f 100644
--- a/src/tls/crypt.ts
+++ b/src/tls/crypt.ts
@@ -3,33 +3,36 @@ import * as tls from 'tls';
import { createSecureContext } from 'tls';
import * as crypto from 'crypto';
import * as DuplexPair from 'native-duplexpair';
-import debug from 'debug';
import * as NodeCache from 'node-cache';
// import * as constants from 'constants';
-import * as config from '../../config';
+import PackageInterface from '../interface';
-const log = debug('radius:tls');
+const packageInterface = PackageInterface.get();
+
+const log = (...args) => packageInterface.log(...args);
// https://nodejs.org/api/tls.html
-const tlsOptions: tls.SecureContextOptions = {
- ...config.certificate,
-};
-log('tlsOptions', tlsOptions);
-const secureContext = createSecureContext(tlsOptions);
+export function getTLSSecureContext(): tls.SecureContext {
+ const tlsOptions: tls.SecureContextOptions = {
+ ...packageInterface.getConfig().certificate,
+ };
+
+ return createSecureContext(tlsOptions);
+}
export interface ITLSServer {
events: events.EventEmitter;
tls: tls.TLSSocket;
}
-const resumeSessions = new NodeCache({ stdTTL: 86400 }); // session reidentification maximum 1 day
+const resumeSessions = new NodeCache({ stdTTL: packageInterface.cacheTTL }); // session reidentification maximum 1 day
export function startTLSServer(): ITLSServer {
const duplexpair = new DuplexPair();
const emitter = new events.EventEmitter();
const cleartext = new tls.TLSSocket(duplexpair.socket1, {
- secureContext,
+ secureContext: getTLSSecureContext(),
isServer: true,
// enableTrace: true,
rejectUnauthorized: false,
@@ -133,14 +136,14 @@ export function encodeTunnelPW(key: Buffer, authenticator: Buffer, secret: strin
// https://tools.ietf.org/html/rfc2548
/**
- * Salt
- The Salt field is two octets in length and is used to ensure the
- uniqueness of the keys used to encrypt each of the encrypted
- attributes occurring in a given Access-Accept packet. The most
- significant bit (leftmost) of the Salt field MUST be set (1). The
- contents of each Salt field in a given Access-Accept packet MUST
- be unique.
- */
+ * Salt
+ The Salt field is two octets in length and is used to ensure the
+ uniqueness of the keys used to encrypt each of the encrypted
+ attributes occurring in a given Access-Accept packet. The most
+ significant bit (leftmost) of the Salt field MUST be set (1). The
+ contents of each Salt field in a given Access-Accept packet MUST
+ be unique.
+ */
const salt = crypto.randomBytes(2);
// eslint-disable-next-line no-bitwise
@@ -183,7 +186,7 @@ export function encodeTunnelPW(key: Buffer, authenticator: Buffer, secret: strin
is performed in the following manner ('+' indicates
concatenation):
- Zorn Informational [Page 21]
+ Zorn Informational [Page 21]
RFC 2548 Microsoft Vendor-specific RADIUS Attributes March 1999
diff --git a/src/types/PacketHandler.ts b/src/types/PacketHandler.ts
index 2945391..0eeb28f 100644
--- a/src/types/PacketHandler.ts
+++ b/src/types/PacketHandler.ts
@@ -16,6 +16,7 @@ export interface IPacketAttributes {
export interface IPacket {
attributes: { [key: string]: string | Buffer };
authenticator?: Buffer;
+ credentialMiddleware?: (username: string, password: string) => [string, string];
}
export interface IPacketHandler {