UNPKG

n8n-nodes-netdevices

Version:

n8n node to interact with network devices (Cisco, MikroTik, Arista, Extreme Networks, Dell, Juniper, HP, Aruba, Ubiquiti, Palo Alto, Fortinet - ISP, data center, campus, and edge)

383 lines 13.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FortinetConnection = void 0; const base_connection_1 = require("../base-connection"); class FortinetConnection extends base_connection_1.BaseConnection { constructor(credentials) { super(credentials); this.vdoms = false; this.osVersion = ''; this.originalOutputMode = ''; this.outputMode = ''; this.inConfigGlobal = false; this.setupFortiGateAlgorithms(); } setupFortiGateAlgorithms() { const originalGetOptimizedAlgorithms = this.getOptimizedAlgorithms.bind(this); this.getOptimizedAlgorithms = () => { const fortiGateAlgorithms = { serverHostKey: ['ssh-rsa', 'ecdsa-sha2-nistp256'], cipher: ['aes128-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc'], hmac: ['hmac-sha1', 'hmac-sha2-256'], kex: [ 'diffie-hellman-group14-sha1', 'diffie-hellman-group-exchange-sha1', 'diffie-hellman-group-exchange-sha256', 'diffie-hellman-group1-sha1' ] }; return [fortiGateAlgorithms, ...originalGetOptimizedAlgorithms()]; }; } async sessionPreparation() { try { await this.createFortinetShellChannel(); await this.handleBanner(); await this.setBasePrompt(); await this.detectVDOMs(); await this.determineOSVersion(); await this.detectOutputMode(); await this.disablePaging(); } catch (error) { throw new Error(`FortiGate session preparation failed: ${error instanceof Error ? error.message : String(error)}`); } } async createFortinetShellChannel() { return new Promise((resolve, reject) => { this.client.shell((err, channel) => { if (err) { reject(err); return; } this.currentChannel = channel; this.currentChannel.setEncoding(this.encoding); setTimeout(() => resolve(), this.fastMode ? 500 : 1000); }); }); } async handleBanner() { try { const data = await this.readChannel(5000); if (data.includes('to accept')) { await this.writeChannel('a' + this.returnChar); await this.readChannel(3000); } if (data.includes('Press any key to continue') || data.includes('Press Enter to continue')) { await this.writeChannel(this.returnChar); await this.readChannel(3000); } if (data.includes('Welcome') || data.includes('FortiGate') || data.includes('FortiOS')) { await this.writeChannel(this.returnChar); await this.readChannel(3000); } } catch (error) { console.warn('Banner handling failed, continuing:', error); } } async setBasePrompt() { try { await this.writeChannel(this.returnChar); const output = await this.readChannel(3000); const lines = output.trim().split('\n'); let prompt = ''; for (let i = lines.length - 1; i >= 0; i--) { const line = lines[i].trim(); if (line.match(/[#$]\s*$/)) { prompt = line.replace(/[#$]\s*$/, '').trim(); break; } } if (!prompt) { const hostnameMatch = output.match(/FortiGate-(\S+)/i) || output.match(/(\S+)\s*[#$]/); if (hostnameMatch) { prompt = hostnameMatch[1]; } else { prompt = 'FortiGate'; } } this.basePrompt = prompt; this.enabledPrompt = this.basePrompt + '#'; this.configPrompt = this.basePrompt + '#'; } catch (error) { throw new Error(`Failed to set base prompt: ${error instanceof Error ? error.message : String(error)}`); } } async detectVDOMs() { try { const result = await this.sendCommand('get system status | grep Virtual'); const output = result.output.toLowerCase(); this.vdoms = output.includes('virtual domain configuration: multiple') || output.includes('virtual domain configuration: enable') || output.includes('virtual domain configuration: split-task'); } catch (error) { this.vdoms = false; } } async determineOSVersion() { try { const result = await this.sendCommand('get system status | grep Version'); const output = result.output; if (output.match(/Version: .* (v[78]\.)/)) { this.osVersion = 'v7_or_later'; } else if (output.match(/Version: .* (v[654]\.)/)) { this.osVersion = 'v6_or_earlier'; } else { this.osVersion = 'unknown'; } } catch (error) { this.osVersion = 'unknown'; } } async detectOutputMode() { try { if (this.osVersion === 'v6_or_earlier') { this.originalOutputMode = await this.getOutputModeV6(); } else { this.originalOutputMode = await this.getOutputModeV7(); } this.outputMode = this.originalOutputMode; } catch (error) { this.originalOutputMode = 'more'; this.outputMode = 'more'; } } async getOutputModeV6() { if (this.vdoms) { await this.enterConfigGlobal(); } const result = await this.sendCommand('show full-configuration system console'); const output = result.output; if (this.vdoms) { await this.exitConfigGlobal(); } const match = output.match(/^\s+set output (\S+)\s*$/m); if (match && ['more', 'standard'].includes(match[1])) { return match[1]; } return 'more'; } async getOutputModeV7() { if (this.vdoms) { await this.enterConfigGlobal(); } const result = await this.sendCommand('get system console'); const output = result.output; if (this.vdoms) { await this.exitConfigGlobal(); } const match = output.match(/output\s+:\s+(\S+)\s*$/m); if (match && ['more', 'standard'].includes(match[1])) { return match[1]; } return 'more'; } async enterConfigGlobal() { try { await this.writeChannel('config global' + this.newline); const output = await this.readChannel(3000); if (output.includes('#')) { this.inConfigGlobal = true; } else { throw new Error('Failed to enter config global mode'); } } catch (error) { throw new Error('Netmiko may require config global access to properly disable output paging. Alternatively you can try configuring configure system console -> set output standard.'); } } async exitConfigGlobal() { if (!this.inConfigGlobal) { return; } try { await this.writeChannel('end' + this.newline); const output = await this.readChannel(3000); if (!output.includes('config global')) { this.inConfigGlobal = false; } else { throw new Error('Failed to exit config global mode'); } } catch (error) { throw new Error('Unable to properly exit config global mode.'); } } async disablePaging() { if (this.outputMode === 'standard') { return; } try { if (this.vdoms) { await this.enterConfigGlobal(); } const commands = [ 'config system console', 'set output standard', 'end' ]; for (const command of commands) { await this.writeChannel(command + this.newline); await this.readChannel(2000); } this.outputMode = 'standard'; if (this.vdoms) { await this.exitConfigGlobal(); } } catch (error) { console.warn('Failed to disable paging, continuing:', error); } } async sendCommand(command) { try { if (!this.isConnected || !this.currentChannel) { throw new Error('Not connected to device'); } await this.writeChannel(command + this.newline); const timeout = this.fastMode ? 10000 : 20000; const output = await this.readUntilPrompt(undefined, timeout); const cleanOutput = this.sanitizeOutput(output, command); return { command, output: cleanOutput, success: true }; } catch (error) { return { command, output: '', success: false, error: error instanceof Error ? error.message : String(error) }; } } async sendConfig(configCommands) { try { if (!this.isConnected || !this.currentChannel) { throw new Error('Not connected to device'); } let fullOutput = ''; for (const command of configCommands) { await this.writeChannel(command + this.newline); const output = await this.readChannel(3000); fullOutput += output; } const cleanOutput = this.sanitizeOutput(fullOutput, configCommands.join('\n')); return { command: configCommands.join('\n'), output: cleanOutput, success: true }; } catch (error) { return { command: configCommands.join('\n'), output: '', success: false, error: error instanceof Error ? error.message : String(error) }; } } async getCurrentConfig() { return this.sendCommand('show full-configuration'); } async saveConfig() { return { command: 'save config', output: 'Fortinet configuration is typically saved automatically', success: true }; } async rebootDevice() { try { if (!this.isConnected || !this.currentChannel) { throw new Error('Not connected to device'); } await this.writeChannel('execute reboot' + this.newline); const output = await this.readChannel(5000); if (output.toLowerCase().includes('yes/no') || output.toLowerCase().includes('y/n')) { await this.writeChannel('yes' + this.newline); await this.readChannel(3000); } return { command: 'execute reboot', output: this.sanitizeOutput(output, 'execute reboot'), success: true }; } catch (error) { return { command: 'execute reboot', output: '', success: false, error: error instanceof Error ? error.message : String(error) }; } } sanitizeOutput(output, command) { const lines = output.split('\n'); if (lines.length > 0 && lines[0].includes(command)) { lines.shift(); } if (lines.length > 0) { const lastLine = lines[lines.length - 1]; const promptRegex = new RegExp(`^${this.escapeRegex(this.basePrompt)}[>#$]`); if (promptRegex.test(lastLine)) { lines.pop(); } } return lines.join('\n').trim(); } async enterConfigMode() { return; } async exitConfigMode() { return; } isInConfigMode() { return false; } async cleanup() { try { if (this.originalOutputMode === 'more') { if (this.vdoms) { await this.enterConfigGlobal(); } const commands = [ 'config system console', 'set output more', 'end' ]; for (const command of commands) { await this.writeChannel(command + this.newline); await this.readChannel(2000); } if (this.vdoms) { await this.exitConfigGlobal(); } } } catch (error) { } await this.disconnect(); } hasVDOMs() { return this.vdoms; } getOSVersion() { return this.osVersion; } getOutputMode() { return this.outputMode; } } exports.FortinetConnection = FortinetConnection; //# sourceMappingURL=fortinet-connection.js.map