65 lines
6.8 KiB
JavaScript
65 lines
6.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// Probele gardei de adrese private (adrese-private.mjs) si ale scanerului care o foloseste. Offline: rezolvarea DNS se
|
|
// injecteaza, iar "conexiunea" se masoara pe un server TLS local care NUMARA cine il atinge.
|
|
// node aerenew/cloud-gateway/proba-adrese-private.mjs
|
|
import assert from 'node:assert';
|
|
import net from 'node:net';
|
|
import os from 'node:os';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
process.env.AERE_READINESS_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-readiness-'));
|
|
const { ipPrivat, lookupFixat } = await import('./adrese-private.mjs');
|
|
const { scaneaza } = await import('./readiness-service.mjs');
|
|
|
|
let treceri = 0; const esecuri = [];
|
|
async function test(nume, fn) { try { await fn(); treceri++; console.log(' OK ' + nume); } catch (e) { esecuri.push(nume); console.log(' ESEC ' + nume + ' -> ' + String(e.message || e).slice(0, 260)); } }
|
|
|
|
const PRIVATE = ['127.0.0.1', '127.255.255.254', '10.10.0.23', '10.0.0.1', '172.16.0.1', '172.31.255.255', '192.168.1.1', '169.254.169.254', '100.64.0.1', '100.127.255.255', // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
'0.0.0.0', '192.0.0.1', '192.0.2.5', '198.18.0.1', '198.19.255.255', '198.51.100.7', '203.0.113.9', '224.0.0.1', '255.255.255.255', // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
'::1', '::', '::ffff:127.0.0.1', '::ffff:10.10.0.23', '::ffff:a0a:17', '::10.0.0.1', 'fe80::1', 'fe80::1%eth0', 'fc00::1', 'fd12:3456::1', 'ff02::1', '2001:db8::1', '2001::1', // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
'64:ff9b::10.0.0.1', '64:ff9b::7f00:1', '2002:0a0a:0017::1', '3fff::1', '4000::1', 'nu-e-adresa', '', '1.2.3', '1.2.3.4.5', '256.1.1.1', '12345::1', ':::1']; // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
const PUBLICE = ['1.1.1.1', '8.8.8.8', '172.15.255.255', '172.32.0.1', '100.63.255.255', '100.128.0.1', '192.167.255.255', '169.253.0.1', '198.17.255.255', '198.20.0.1', '223.255.255.255', // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
'2606:4700:4700::1111', '2a01:4f8::1', '::ffff:8.8.8.8', '64:ff9b::8.8.8.8', '2002:0808:0808::1']; // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
|
|
await test(`predicatul: ${PRIVATE.length} adrese care NU primesc conexiune (loopback, private, CGNAT, link-local, documentatie, multicast, formele lor IPv6, si orice text care nu e adresa)`, () => {
|
|
const scapate = PRIVATE.filter((a) => !ipPrivat(a)); assert.deepStrictEqual(scapate, []);
|
|
});
|
|
await test(`predicatul: ${PUBLICE.length} adrese publice, inclusiv vecinele de granita ale fiecarui bloc privat, NU sunt refuzate`, () => {
|
|
const refuzate = PUBLICE.filter((a) => ipPrivat(a)); assert.deepStrictEqual(refuzate, []);
|
|
});
|
|
await test('regula veche a gateway-ului lasa sa treaca exact ce s-a adaugat (CGNAT, IPv4 privat mapat, ::): proba deosebeste cele doua reguli', () => {
|
|
const veche = (ip) => { if (ip.includes(':')) return ip === '::1' || ip.startsWith('fe80') || ip.startsWith('fc') || ip.startsWith('fd') || ip.startsWith('::ffff:127.'); const [a, b] = ip.split('.').map(Number); return a === 127 || a === 10 || a === 0 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || (a === 169 && b === 254); };
|
|
for (const a of ['100.64.0.1', '::ffff:10.10.0.23', '::', '198.18.0.1', '64:ff9b::10.0.0.1']) { assert.strictEqual(veche(a), false, a + ': regula veche o lasa'); assert.strictEqual(ipPrivat(a), true, a); } // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
});
|
|
|
|
// un ascultator local care numara conexiunile: daca scanerul il atinge, garda nu a tinut
|
|
let atingeri = 0; const srv = net.createServer((s) => { atingeri++; s.destroy(); });
|
|
await new Promise((r) => srv.listen(0, '127.0.0.1', r)); // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
|
|
await test('scanerul: o gazda rezolvata la o adresa privata -> private_target, FARA adrese in raspuns si fara nicio conexiune', async () => {
|
|
for (const adrese of [[{ address: '10.10.0.23', family: 4 }], [{ address: '::1', family: 6 }, { address: '127.0.0.1', family: 4 }], [{ address: '1.1.1.1', family: 4 }, { address: '169.254.169.254', family: 4 }]]) { // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
const t0 = Date.now(); const r = await scaneaza('intern.example', { rezolva: async () => adrese });
|
|
assert.strictEqual(r.error, 'private_target', JSON.stringify(r)); assert.strictEqual(r.addresses, undefined, 'raspunsul nu are voie sa spuna adresele'); assert.doesNotMatch(JSON.stringify(r), /10\.10\.0\.23|169\.254|127\.0\.0\.1/);
|
|
assert.ok(Date.now() - t0 < 1000, 'a durat ' + (Date.now() - t0) + ' ms: a incercat o conexiune');
|
|
}
|
|
assert.strictEqual(atingeri, 0);
|
|
});
|
|
await test('scanerul: o rezolvare goala sau cazuta e eroare de DNS, nu o scanare', async () => {
|
|
assert.strictEqual((await scaneaza('gol.example', { rezolva: async () => [] })).error, 'dns');
|
|
assert.strictEqual((await scaneaza('cade.example', { rezolva: async () => { const e = new Error('x'); e.code = 'ENOTFOUND'; throw e; } })).detail, 'ENOTFOUND');
|
|
});
|
|
await test('lookupFixat: conexiunea merge pe adresa VERIFICATA, in amandoua formele de apel (cu si fara all), oricare ar fi numele', async () => {
|
|
const l = lookupFixat({ address: '1.1.1.1', family: 4 }); // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
await new Promise((ok, rau) => l('se-schimba.example', { all: true }, (e, v) => { try { assert.deepStrictEqual(v, [{ address: '1.1.1.1', family: 4 }]); ok(); } catch (x) { rau(x); } })); // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
await new Promise((ok, rau) => l('se-schimba.example', {}, (e, a, f) => { try { assert.deepStrictEqual([a, f], ['1.1.1.1', 4]); ok(); } catch (x) { rau(x); } })); // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
await new Promise((ok, rau) => l('se-schimba.example', (e, a, f) => { try { assert.deepStrictEqual([a, f], ['1.1.1.1', 4]); ok(); } catch (x) { rau(x); } })); // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
// si chiar foloseste adresa: o conexiune net cu lookup fixat pe ascultatorul local ajunge la el, desi numele nu exista
|
|
const inainte = atingeri;
|
|
await new Promise((ok) => { const s = net.connect({ host: 'nume-care-nu-exista.invalid', port: srv.address().port, lookup: lookupFixat({ address: '127.0.0.1', family: 4 }) }); s.on('close', ok); s.on('error', ok); }); // AERE-SINTETIC: adrese de granita ale regulii (vectori de proba), nu gazde
|
|
assert.strictEqual(atingeri, inainte + 1, 'lookup-ul fixat nu a fost folosit');
|
|
});
|
|
srv.close();
|
|
fs.rmSync(process.env.AERE_READINESS_DIR, { recursive: true, force: true });
|
|
console.log(`\n${treceri} treceri, ${esecuri.length} esecuri`);
|
|
process.exitCode = esecuri.length ? 1 : 0;
|