streamflow/backend/utils/vpnDiagnostics.js
2025-12-17 00:42:43 +00:00

245 lines
6.7 KiB
JavaScript

const axios = require('axios');
const { exec } = require('child_process');
const { promisify } = require('util');
const fs = require('fs').promises;
const execAsync = promisify(exec);
/**
* VPN Diagnostics - Check for IP and DNS leaks
*/
class VPNDiagnostics {
/**
* Get current public IP address
*/
static async getPublicIP() {
try {
const response = await axios.get('https://api.ipify.org?format=json', {
timeout: 10000,
validateStatus: () => true
});
return response.data.ip;
} catch (error) {
throw new Error(`Failed to get public IP: ${error.message}`);
}
}
/**
* Get detailed IP information (location, ISP, etc.)
*/
static async getIPInfo(ip) {
try {
const response = await axios.get(`https://ipinfo.io/${ip || ''}/json`, {
timeout: 10000,
validateStatus: () => true
});
return response.data;
} catch (error) {
console.warn('Could not get IP info:', error.message);
return null;
}
}
/**
* Get current DNS servers from resolv.conf
*/
static async getDNSServers() {
try {
const resolvConf = await fs.readFile('/etc/resolv.conf', 'utf8');
const nameservers = resolvConf
.split('\n')
.filter(line => line.trim().startsWith('nameserver'))
.map(line => line.split(/\s+/)[1]);
return nameservers;
} catch (error) {
throw new Error(`Failed to read DNS servers: ${error.message}`);
}
}
/**
* Check if VPN interface (tun) exists
*/
static async checkVPNInterface() {
try {
const { stdout } = await execAsync('ip addr show tun0 2>/dev/null');
return {
exists: true,
details: stdout.trim()
};
} catch (error) {
return {
exists: false,
details: null
};
}
}
/**
* Get routing table
*/
static async getRoutingTable() {
try {
const { stdout } = await execAsync('ip route');
return stdout.trim().split('\n');
} catch (error) {
throw new Error(`Failed to get routing table: ${error.message}`);
}
}
/**
* Test DNS resolution through current DNS servers
*/
static async testDNSResolution(domain = 'google.com') {
try {
const { stdout } = await execAsync(`nslookup ${domain} | grep -A1 "Name:" | tail -1`);
return {
success: true,
result: stdout.trim()
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* Check for DNS leaks by testing against multiple DNS leak test services
*/
static async checkDNSLeaks() {
const dnsServers = await this.getDNSServers();
// Check if DNS servers are common VPN DNS servers
const commonVPNDNS = ['10.2.0.1', '10.2.0.2', '10.8.0.1', '10.0.0.1'];
const isUsingVPNDNS = dnsServers.some(dns => commonVPNDNS.includes(dns) || dns.startsWith('10.'));
// Check if DNS servers match common public DNS (potential leak)
const commonPublicDNS = [
'8.8.8.8', '8.8.4.4', // Google
'1.1.1.1', '1.0.0.1', // Cloudflare
'9.9.9.9', // Quad9
];
const isUsingPublicDNS = dnsServers.some(dns => commonPublicDNS.includes(dns));
return {
dnsServers,
isUsingVPNDNS,
isUsingPublicDNS,
potentialLeak: !isUsingVPNDNS && isUsingPublicDNS
};
}
/**
* Run comprehensive VPN diagnostics
*/
static async runFullDiagnostics() {
console.log('🔍 Running VPN diagnostics...');
const results = {
timestamp: new Date().toISOString(),
publicIP: null,
ipInfo: null,
dnsServers: [],
vpnInterface: null,
routingTable: [],
dnsLeakCheck: null,
summary: {
vpnActive: false,
ipLeak: false,
dnsLeak: false,
issues: []
}
};
try {
// Get public IP
results.publicIP = await this.getPublicIP();
console.log(`✓ Public IP: ${results.publicIP}`);
// Get IP info
results.ipInfo = await this.getIPInfo(results.publicIP);
if (results.ipInfo) {
console.log(`✓ Location: ${results.ipInfo.city}, ${results.ipInfo.country}`);
console.log(`✓ ISP: ${results.ipInfo.org}`);
}
// Get DNS servers
results.dnsServers = await this.getDNSServers();
console.log(`✓ DNS Servers: ${results.dnsServers.join(', ')}`);
// Check VPN interface
results.vpnInterface = await this.checkVPNInterface();
if (results.vpnInterface.exists) {
console.log('✓ VPN interface (tun0) is UP');
results.summary.vpnActive = true;
} else {
console.log('✗ VPN interface (tun0) NOT found');
results.summary.issues.push('VPN interface not found');
}
// Get routing table
results.routingTable = await this.getRoutingTable();
const defaultRoute = results.routingTable.find(route => route.startsWith('default'));
if (defaultRoute) {
console.log(`✓ Default route: ${defaultRoute}`);
if (defaultRoute.includes('tun')) {
console.log('✓ Traffic is routed through VPN');
} else {
console.log('✗ Traffic NOT routed through VPN');
results.summary.ipLeak = true;
results.summary.issues.push('Traffic not routed through VPN interface');
}
}
// DNS leak check
results.dnsLeakCheck = await this.checkDNSLeaks();
if (results.dnsLeakCheck.isUsingVPNDNS) {
console.log('✓ Using VPN DNS servers');
} else if (results.dnsLeakCheck.potentialLeak) {
console.log('✗ Potential DNS leak detected');
results.summary.dnsLeak = true;
results.summary.issues.push('Using non-VPN DNS servers');
}
// Overall status
if (results.summary.vpnActive && !results.summary.ipLeak && !results.summary.dnsLeak) {
console.log('\n✓ VPN is working correctly - No leaks detected');
} else {
console.log('\n✗ VPN issues detected:');
results.summary.issues.forEach(issue => console.log(` - ${issue}`));
}
} catch (error) {
console.error('Error during diagnostics:', error);
results.summary.issues.push(error.message);
}
return results;
}
/**
* Quick check if VPN is working (used by status endpoint)
*/
static async quickCheck() {
try {
const [publicIP, dnsServers, vpnInterface] = await Promise.all([
this.getPublicIP(),
this.getDNSServers(),
this.checkVPNInterface()
]);
return {
publicIP,
dnsServers,
vpnInterfaceActive: vpnInterface.exists,
timestamp: new Date().toISOString()
};
} catch (error) {
throw new Error(`Quick check failed: ${error.message}`);
}
}
}
module.exports = VPNDiagnostics;