263 lines
10 KiB
JavaScript
263 lines
10 KiB
JavaScript
// Phase 3 & 4: Complete DNS, TCP, Security, and Isolation Tests on Ghaymah
|
|
// This runs AFTER apps are deployed and accessible
|
|
|
|
const APP_A_ID = 'af1b9e91-9bec-4d6a-b76c-92d7273b82ce';
|
|
const APP_B_ID = 'fcbd895c-2370-46db-bd6a-057f6d887b0f';
|
|
const PROJECT_ID = '3f55ceef-78ae-40c8-a41b-0505db8c59ee';
|
|
|
|
const fs = require('fs');
|
|
let report = [];
|
|
|
|
function log(phase, msg, data) {
|
|
const ts = new Date().toISOString();
|
|
const line = `[${ts}] [${phase}] ${msg}`;
|
|
console.log(line);
|
|
if (data) console.log(JSON.stringify(data, null, 2));
|
|
report.push({ ts, phase, msg, data });
|
|
}
|
|
|
|
async function auth() {
|
|
const res = await fetch('https://auth.ghaymah.systems/signin/email-password', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ email: 'ziadalex2003@gmail.com', password: '2062003Mz*' })
|
|
});
|
|
const data = await res.json();
|
|
return { token: data.session.accessToken, userId: data.session.user.id };
|
|
}
|
|
|
|
async function gql(token, query, variables = {}) {
|
|
const res = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
|
|
method: 'POST',
|
|
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ query, variables })
|
|
});
|
|
return res.json();
|
|
}
|
|
|
|
async function appRequest(domain, path, timeout = 8000) {
|
|
try {
|
|
const res = await fetch(`https://${domain}${path}`, { signal: AbortSignal.timeout(timeout) });
|
|
return await res.json();
|
|
} catch (e) {
|
|
return { _error: e.message };
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const { token, userId } = await auth();
|
|
log('AUTH', 'Authenticated', { userId });
|
|
|
|
// Step 1: Get app domains and status
|
|
log('RECON', 'Fetching app resources...');
|
|
const resData = await gql(token, `query GetResources($userId: uuid) {
|
|
ghaymah_cloud_resources(where: { user_id: { _eq: $userId }, deleted: { _eq: false }, chart_name: { _eq: "ghaymah-app" } }) {
|
|
id chart_name status values message
|
|
}
|
|
}`, { userId });
|
|
|
|
const apps = resData.data?.ghaymah_cloud_resources || [];
|
|
const appA = apps.find(a => a.values?.name === 'tcp-echo-server');
|
|
const appB = apps.find(a => a.values?.name === 'tcp-test-client');
|
|
const authDeploy = apps.find(a => a.values?.name === 'auth-deploy');
|
|
|
|
const domainA = appA?.values?.publicAccess?.domain;
|
|
const domainB = appB?.values?.publicAccess?.domain;
|
|
const domainAuth = authDeploy?.values?.publicAccess?.domain;
|
|
|
|
log('RECON', 'App A (tcp-echo-server)', { status: appA?.status, domain: domainA, project: appA?.values?.projectId });
|
|
log('RECON', 'App B (tcp-test-client)', { status: appB?.status, domain: domainB, project: appB?.values?.projectId });
|
|
log('RECON', 'App auth-deploy (different project)', { status: authDeploy?.status, domain: domainAuth, project: authDeploy?.values?.projectId });
|
|
|
|
if (!domainA || !domainB) {
|
|
log('ERROR', 'Missing domains! Cannot proceed with tests.');
|
|
|
|
// Try ListApps mutation to get hostnames
|
|
const listApps = await gql(token, `mutation ListApps {
|
|
listApps {
|
|
id name status alias
|
|
ports { name number hostname }
|
|
}
|
|
}`);
|
|
log('RECON', 'ListApps result', listApps.data?.listApps);
|
|
|
|
// Save partial report and exit
|
|
fs.writeFileSync('D:/cumin/ghaymah_dns_net_sec_tests/logs/06_test_results.json', JSON.stringify(report, null, 2));
|
|
return;
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// PHASE 3: FUNCTIONALITY & QA TESTING
|
|
// ═══════════════════════════════════════════════
|
|
log('PHASE3', '═══ PHASE 3: FUNCTIONALITY & QA TESTING ═══');
|
|
|
|
// 3.1: Health checks
|
|
log('PHASE3', 'Testing App A health...');
|
|
const healthA = await appRequest(domainA, '/health');
|
|
log('PHASE3', 'App A health response', healthA);
|
|
|
|
log('PHASE3', 'Testing App B health...');
|
|
const healthB = await appRequest(domainB, '/health');
|
|
log('PHASE3', 'App B health response', healthB);
|
|
|
|
// 3.2: DNS Infrastructure
|
|
log('PHASE3-DNS', '--- Internal DNS Verification ---');
|
|
|
|
// Get resolv.conf from both apps
|
|
const resolvA = await appRequest(domainA, '/resolv-conf');
|
|
log('PHASE3-DNS', 'App A resolv.conf', resolvA);
|
|
|
|
const resolvB = await appRequest(domainB, '/resolv-conf');
|
|
log('PHASE3-DNS', 'App B resolv.conf', resolvB);
|
|
|
|
// Get IP addresses from both apps
|
|
const ipA = await appRequest(domainA, '/ip-addr');
|
|
log('PHASE3-DNS', 'App A network interfaces', ipA);
|
|
|
|
const ipB = await appRequest(domainB, '/ip-addr');
|
|
log('PHASE3-DNS', 'App B network interfaces', ipB);
|
|
|
|
// DNS Lookup tests from App B -> App A
|
|
const dnsPatterns = [
|
|
'tcp-echo-server',
|
|
'tcp-echo-server.mesh',
|
|
'tcp-echo-server.dns-tcp-test.mesh',
|
|
'tcp-echo-server.local',
|
|
'tcp-echo-server.dns-tcp-test',
|
|
'tcp-echo-server.internal',
|
|
'tcp-echo-server.svc.cluster.local',
|
|
];
|
|
|
|
log('PHASE3-DNS', 'Testing DNS resolution patterns from App B -> App A...');
|
|
for (const pattern of dnsPatterns) {
|
|
const dnsResult = await appRequest(domainB, `/dns-lookup?name=${encodeURIComponent(pattern)}`);
|
|
log('PHASE3-DNS', `DNS lookup: ${pattern}`, dnsResult);
|
|
}
|
|
|
|
// DNS Lookup tests from App A -> App B
|
|
const dnsPatternsB = [
|
|
'tcp-test-client',
|
|
'tcp-test-client.mesh',
|
|
'tcp-test-client.dns-tcp-test.mesh',
|
|
'tcp-test-client.local',
|
|
'tcp-test-client.dns-tcp-test',
|
|
];
|
|
|
|
log('PHASE3-DNS', 'Testing DNS resolution patterns from App A -> App B...');
|
|
for (const pattern of dnsPatternsB) {
|
|
const dnsResult = await appRequest(domainA, `/dns-lookup?name=${encodeURIComponent(pattern)}`);
|
|
log('PHASE3-DNS', `DNS lookup: ${pattern}`, dnsResult);
|
|
}
|
|
|
|
// 3.3: TCP Connection Tests
|
|
log('PHASE3-TCP', '--- TCP Connection Tests ---');
|
|
|
|
// Extract WireGuard/mesh IPs from App A
|
|
let appAPrivateIp = null;
|
|
if (ipA?.interfaces) {
|
|
for (const [name, addrs] of Object.entries(ipA.interfaces)) {
|
|
if (name === 'wg0' || name.startsWith('wg')) {
|
|
const v4 = addrs.find(a => a.family === 'IPv4');
|
|
if (v4) appAPrivateIp = v4.address;
|
|
}
|
|
}
|
|
if (!appAPrivateIp) {
|
|
// Try eth0
|
|
for (const [name, addrs] of Object.entries(ipA.interfaces)) {
|
|
if (name === 'eth0') {
|
|
const v4 = addrs.find(a => a.family === 'IPv4');
|
|
if (v4) appAPrivateIp = v4.address;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
log('PHASE3-TCP', `App A private IP: ${appAPrivateIp}`);
|
|
|
|
// TCP connect from App B to App A on port 9000 using IP
|
|
if (appAPrivateIp) {
|
|
const tcpByIp = await appRequest(domainB, `/tcp-connect?host=${appAPrivateIp}&port=9000`);
|
|
log('PHASE3-TCP', `TCP via IP (${appAPrivateIp}:9000)`, tcpByIp);
|
|
}
|
|
|
|
// TCP connect from App B to App A via DNS name
|
|
for (const dnsName of ['tcp-echo-server', 'tcp-echo-server.mesh']) {
|
|
const tcpByDns = await appRequest(domainB, `/tcp-connect?host=${dnsName}&port=9000`);
|
|
log('PHASE3-TCP', `TCP via DNS (${dnsName}:9000)`, tcpByDns);
|
|
}
|
|
|
|
// HTTP connectivity test (App B -> App A port 8080)
|
|
if (appAPrivateIp) {
|
|
const httpByIp = await appRequest(domainB, `/tcp-connect?host=${appAPrivateIp}&port=8080`);
|
|
log('PHASE3-TCP', `HTTP port via IP (${appAPrivateIp}:8080)`, httpByIp);
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// PHASE 4: SECURITY & SCOPING
|
|
// ═══════════════════════════════════════════════
|
|
log('PHASE4', '═══ PHASE 4: SECURITY & SCOPING ═══');
|
|
|
|
// 4.1: Isolation & Lateral Movement
|
|
log('PHASE4-ISO', '--- Isolation & Lateral Movement Tests ---');
|
|
|
|
// Try to reach auth-deploy (DIFFERENT project)
|
|
if (domainAuth) {
|
|
// DNS lookup for cross-project app
|
|
const crossProjectPatterns = [
|
|
'auth-deploy',
|
|
'auth-deploy.mesh',
|
|
'auth-deploy.auth-deploy.mesh',
|
|
];
|
|
for (const pattern of crossProjectPatterns) {
|
|
const crossDns = await appRequest(domainB, `/dns-lookup?name=${encodeURIComponent(pattern)}`);
|
|
log('PHASE4-ISO', `Cross-project DNS: ${pattern}`, crossDns);
|
|
}
|
|
}
|
|
|
|
// Try to reach scanner-clean (DIFFERENT project, in backoff)
|
|
const crossPatterns2 = [
|
|
'scanner-clean',
|
|
'scanner-clean.mesh',
|
|
];
|
|
for (const pattern of crossPatterns2) {
|
|
const crossDns = await appRequest(domainB, `/dns-lookup?name=${encodeURIComponent(pattern)}`);
|
|
log('PHASE4-ISO', `Cross-project DNS (scanner-clean): ${pattern}`, crossDns);
|
|
}
|
|
|
|
// 4.2: Network Enumeration (Port Scan)
|
|
log('PHASE4-SCAN', '--- Network Enumeration ---');
|
|
|
|
if (appAPrivateIp) {
|
|
const scanA = await appRequest(domainB, `/port-scan?host=${appAPrivateIp}&ports=22,53,80,443,3000,4000,5432,6379,8080,9000`, 15000);
|
|
log('PHASE4-SCAN', `Port scan App A (${appAPrivateIp})`, scanA);
|
|
}
|
|
|
|
// Scan common internal gateway IPs
|
|
const internalIps = ['10.100.0.1', '10.100.0.2', '172.26.64.1', '127.0.0.1'];
|
|
for (const ip of internalIps) {
|
|
const scanGw = await appRequest(domainB, `/port-scan?host=${ip}&ports=22,53,80,443,8080,9090`, 12000);
|
|
log('PHASE4-SCAN', `Port scan ${ip}`, scanGw);
|
|
}
|
|
|
|
// 4.3: External Internet Access & DNS Leak
|
|
log('PHASE4-LEAK', '--- Dual Routing & DNS Leaks ---');
|
|
|
|
const extA = await appRequest(domainA, '/external-test');
|
|
log('PHASE4-LEAK', 'App A external internet access', extA);
|
|
|
|
const extB = await appRequest(domainB, '/external-test');
|
|
log('PHASE4-LEAK', 'App B external internet access', extB);
|
|
|
|
// DNS leak test: resolve external domains
|
|
const externalDns = ['google.com', 'github.com', 'cloudflare.com'];
|
|
for (const name of externalDns) {
|
|
const dnsExt = await appRequest(domainB, `/dns-lookup?name=${encodeURIComponent(name)}`);
|
|
log('PHASE4-LEAK', `External DNS resolution: ${name}`, dnsExt);
|
|
}
|
|
|
|
// Save full results
|
|
fs.writeFileSync('D:/cumin/ghaymah_dns_net_sec_tests/logs/06_test_results.json', JSON.stringify(report, null, 2));
|
|
log('DONE', 'All tests completed. Results saved.');
|
|
}
|
|
|
|
main().catch(e => { console.error('FATAL:', e); process.exit(1); });
|