fix: improve coping with long running auth requests

This commit is contained in:
simon 2020-02-27 11:33:05 +01:00
parent 87e8313108
commit 7ca60a20cc
3 changed files with 33 additions and 81 deletions

View File

@ -1,4 +1,4 @@
import { Client, ClientOptions, createClient } from 'ldapjs'; import { ClientOptions, createClient } from 'ldapjs';
import debug from 'debug'; import debug from 'debug';
import * as tls from 'tls'; import * as tls from 'tls';
import { IAuthentication } from '../types/Authentication'; import { IAuthentication } from '../types/Authentication';
@ -22,8 +22,6 @@ interface IGoogleLDAPAuthOptions {
} }
export class GoogleLDAPAuth implements IAuthentication { export class GoogleLDAPAuth implements IAuthentication {
private ldapDNClient: Client;
private lastDNsFetch: Date; private lastDNsFetch: Date;
private allValidDNsCache: { [key: string]: string }; private allValidDNsCache: { [key: string]: string };
@ -43,10 +41,6 @@ export class GoogleLDAPAuth implements IAuthentication {
} }
}; };
this.ldapDNClient = createClient(this.config).on('error', error => {
console.error('Error in ldap', error);
});
this.fetchDNs(); this.fetchDNs();
} }
@ -54,7 +48,12 @@ export class GoogleLDAPAuth implements IAuthentication {
const dns: { [key: string]: string } = {}; const dns: { [key: string]: string } = {};
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
this.ldapDNClient.search( const ldapDNClient = createClient(this.config).on('error', error => {
console.error('Error in ldap', error);
reject(error);
});
ldapDNClient.search(
this.base, this.base,
{ {
scope: 'sub' scope: 'sub'
@ -100,6 +99,13 @@ export class GoogleLDAPAuth implements IAuthentication {
const cacheValidTime = new Date(); const cacheValidTime = new Date();
cacheValidTime.setHours(cacheValidTime.getHours() - 12); cacheValidTime.setHours(cacheValidTime.getHours() - 12);
/*
just a test for super slow google responses
await new Promise((resolve, reject) => {
setTimeout(resolve, 10000); // wait 10 seconds
})
*/
let dnsFetched = false; let dnsFetched = false;
if (!this.lastDNsFetch || this.lastDNsFetch < cacheValidTime || forceFetching) { if (!this.lastDNsFetch || this.lastDNsFetch < cacheValidTime || forceFetching) {

View File

@ -3,6 +3,7 @@
/* eslint-disable no-bitwise */ /* eslint-disable no-bitwise */
import * as tls from 'tls'; import * as tls from 'tls';
import * as NodeCache from 'node-cache'; import * as NodeCache from 'node-cache';
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore // @ts-ignore
import { attr_id_to_name, attr_name_to_id } from 'radius'; import { attr_id_to_name, attr_name_to_id } from 'radius';
import debug from 'debug'; import debug from 'debug';
@ -43,6 +44,8 @@ interface IAVPEntry {
} }
export class EAPTTLS implements IEAPMethod { export class EAPTTLS implements IEAPMethod {
private lastProcessedIdentifier = new NodeCache({ useClones: false, stdTTL: 60 });
// { [key: string]: Buffer } = {}; // { [key: string]: Buffer } = {};
private queueData = new NodeCache({ useClones: false, stdTTL: 60 }); // queue data maximum for 60 seconds private queueData = new NodeCache({ useClones: false, stdTTL: 60 }); // queue data maximum for 60 seconds
@ -185,7 +188,7 @@ export class EAPTTLS implements IEAPMethod {
Message Length | Data... Message Length | Data...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/ */
// const identifier = msg.slice(1, 2).readUInt8(0); const identifier = msg.slice(1, 2).readUInt8(0);
const flags = msg.slice(5, 6).readUInt8(0); // .toString('hex'); const flags = msg.slice(5, 6).readUInt8(0); // .toString('hex');
/* /*
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
@ -201,15 +204,15 @@ export class EAPTTLS implements IEAPMethod {
*/ */
const decodedFlags = { const decodedFlags = {
// L // L
lengthIncluded: flags & 0b010000000, lengthIncluded: !!(flags & 0b10000000),
// M // M
moreFragments: flags & 0b001000000, moreFragments: !!(flags & 0b01000000),
// S // S
start: flags & 0b000100000, start: !!(flags & 0b00100000),
// R // R
// reserved: flags & 0b000011000, // reserved: flags & 0b00011000,
// V // V
version: flags & 0b010000111 version: flags & 0b00000111
}; };
let msglength; let msglength;
@ -221,7 +224,7 @@ export class EAPTTLS implements IEAPMethod {
log('>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS', { log('>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS', {
flags: `00000000${flags.toString(2)}`.substr(-8), flags: `00000000${flags.toString(2)}`.substr(-8),
decodedFlags, decodedFlags,
// identifier, identifier,
msglength, msglength,
data data
// dataStr: data.toString() // dataStr: data.toString()
@ -291,15 +294,23 @@ export class EAPTTLS implements IEAPMethod {
msg: Buffer, msg: Buffer,
packet: IPacket packet: IPacket
): Promise<IPacketHandlerResult> { ): Promise<IPacketHandlerResult> {
if (identifier === this.lastProcessedIdentifier.get(stateID)) {
log(`ignoring message ${identifier}, because it's processing already... ${stateID}`);
return {};
}
this.lastProcessedIdentifier.set(stateID, identifier);
const { data } = this.decodeTTLSMessage(msg); const { data } = this.decodeTTLSMessage(msg);
// check if no data package is there and we have something in the queue, if so.. empty the queue first // check if no data package is there and we have something in the queue, if so.. empty the queue first
if (!data || data.length === 0) { if (!data || data.length === 0) {
const queuedData = this.queueData.get(stateID); const queuedData = this.queueData.get(stateID);
if (queuedData instanceof Buffer && queuedData.length > 0) { if (queuedData instanceof Buffer && queuedData.length > 0) {
log(`returning queued data for ${stateID}`);
return this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, queuedData, false); return this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, queuedData, false);
} }
log(`empty data queue for ${stateID}`);
return {}; return {};
} }
@ -373,71 +384,6 @@ export class EAPTTLS implements IEAPMethod {
'encrypt', 'encrypt',
this.buildAVP(attr_name_to_id('EAP-Message'), eapMessage[1] as Buffer) this.buildAVP(attr_name_to_id('EAP-Message'), eapMessage[1] as Buffer)
); );
/*
switch (type) {
case 1: // PAP
try {
const { username, password } = this.papChallenge.decode(incomingData);
const authResult = await this.authentication.authenticate(username, password);
sendResponsePromise.resolve(
this.authResponse(identifier, authResult, connection.tls, orgRadiusPacket)
);
} catch (err) {
// pwd not found..
console.error('pwd not found', err);
connection.events.emit('end');
// NAK
sendResponsePromise.resolve(this.buildEAPTTLSResponse(identifier, 3, 0, stateID));
}
break;
case 79: {
const result = await this.tunnelEAP.handlePacket(
{
State: `${stateID}-inner`,
'EAP-Message': AVPdata
},
orgRadiusPacket
);
log('inner tunnel result', result);
if (
result.code === PacketResponseCode.AccessReject ||
result.code === PacketResponseCode.AccessAccept
) {
sendResponsePromise.resolve(
this.authResponse(
identifier,
result.code === PacketResponseCode.AccessAccept,
connection.tls,
orgRadiusPacket
)
);
return;
}
const eapMessage = result.attributes?.find(attr => attr[0] === 'EAP-Message');
if (!eapMessage) {
throw new Error('no eap message found');
}
connection.events.emit('encrypt', this.buildAVP(79, eapMessage[1]));
break;
}
default: {
log('data', incomingData);
log('data str', incomingData.toString());
log('UNSUPPORTED AUTH TYPE, requesting identify again (we need PAP!)', type);
connection.events.emit(
'encrypt',
this.buildAVP(79, this.buildEAPTTLS(identifier, 3, 0, stateID, Buffer.from([1])))
);
}
} */
}; };
const responseHandler = (encryptedResponseData: Buffer) => { const responseHandler = (encryptedResponseData: Buffer) => {

View File

@ -41,7 +41,7 @@ export class UDPServer extends events.EventEmitter implements IServer {
// if expectAcknowledgment is false (e.g. Access-Accept or Access-Reject), we do not retry // if expectAcknowledgment is false (e.g. Access-Accept or Access-Reject), we do not retry
const identifierForRetry = `${address}:${port}`; const identifierForRetry = `${address}:${port}`;
if (expectAcknowledgment && retried < UDPServer.MAX_RETRIES) { if (expectAcknowledgment && retried < UDPServer.MAX_RETRIES) {
this.timeout[identifierForRetry] = setTimeout(sendResponse, 600 * (retried + 1)); this.timeout[identifierForRetry] = setTimeout(() => sendResponse(), 1600 * (retried + 1));
} }
retried += 1; retried += 1;
}; };