feat: initial commit of Ghaymah internal DNS, TCP and security testing suite and report
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
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';
|
||||
|
||||
async function main() {
|
||||
const authRes = 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 { session } = await authRes.json();
|
||||
const token = session.accessToken;
|
||||
const userId = session.user.id;
|
||||
|
||||
// Helper to generate domain hash like ghaymah does
|
||||
async function genDomainHash(name, id) {
|
||||
const input = `${name}-${id}`;
|
||||
const data = new TextEncoder().encode(input);
|
||||
const hash = await crypto.subtle.digest('SHA-256', data);
|
||||
const hex = Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
return hex.substring(0, 12);
|
||||
}
|
||||
|
||||
const hashA = await genDomainHash('tcp-echo-server', APP_A_ID);
|
||||
const hashB = await genDomainHash('tcp-test-client', APP_B_ID);
|
||||
|
||||
const domainA = `tcp-echo-server-${hashA}.hosted.ghaymah.systems`;
|
||||
const domainB = `tcp-test-client-${hashB}.hosted.ghaymah.systems`;
|
||||
|
||||
console.log('App A domain would be:', domainA);
|
||||
console.log('App B domain would be:', domainB);
|
||||
|
||||
// Update App A with proper values including publicAccess domain
|
||||
const updateA = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: `mutation UpdateResourceValues($id: uuid!, $values: json!) {
|
||||
update_ghaymah_cloud_resources_by_pk(
|
||||
pk_columns: { id: $id },
|
||||
_set: { values: $values }
|
||||
) {
|
||||
id status
|
||||
}
|
||||
}`,
|
||||
variables: {
|
||||
id: APP_A_ID,
|
||||
values: {
|
||||
name: 'tcp-echo-server',
|
||||
projectId: PROJECT_ID,
|
||||
container: { image: 'node:22-alpine' },
|
||||
resourceTier: 't1',
|
||||
ports: [{ number: 8080, expose: true }],
|
||||
publicAccess: {
|
||||
enabled: true,
|
||||
domain: domainA
|
||||
},
|
||||
source: { enabled: false },
|
||||
command: ['node', '-e', `
|
||||
const net = require('net');
|
||||
const http = require('http');
|
||||
const dns = require('dns');
|
||||
const os = require('os');
|
||||
const tcpServer = net.createServer(socket => {
|
||||
console.log('TCP connection from', socket.remoteAddress);
|
||||
socket.on('data', data => { socket.write('ECHO:' + data.toString()); });
|
||||
socket.on('error', () => {});
|
||||
});
|
||||
tcpServer.listen(9000, () => console.log('TCP echo server on 9000'));
|
||||
http.createServer(async (req, res) => {
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
if (url.pathname === '/health' || url.pathname === '/') {
|
||||
res.end(JSON.stringify({ status: 'ok', service: 'tcp-echo-server', hostname: os.hostname() }));
|
||||
} else if (url.pathname === '/dns-lookup') {
|
||||
const name = url.searchParams.get('name');
|
||||
try { const addrs = await dns.promises.resolve4(name); res.end(JSON.stringify({ name, addresses: addrs })); }
|
||||
catch(e) { try { const r = await dns.promises.lookup(name); res.end(JSON.stringify({ name, address: r.address, source: 'lookup' })); } catch(e2) { res.end(JSON.stringify({ name, error: e.code, error2: e2.code })); } }
|
||||
} else if (url.pathname === '/resolv-conf') {
|
||||
try { res.end(JSON.stringify({ resolv_conf: require('fs').readFileSync('/etc/resolv.conf','utf8') })); } catch(e) { res.end(JSON.stringify({ error: e.message })); }
|
||||
} else if (url.pathname === '/ip-addr') {
|
||||
res.end(JSON.stringify({ interfaces: os.networkInterfaces() }));
|
||||
} else if (url.pathname === '/tcp-connect') {
|
||||
const host = url.searchParams.get('host'), port = parseInt(url.searchParams.get('port'));
|
||||
const sock = new net.Socket(); sock.setTimeout(5000);
|
||||
sock.connect(port, host, () => { sock.write('HELLO'); sock.on('data', d => { res.end(JSON.stringify({ success: true, host, port, response: d.toString() })); sock.destroy(); }); });
|
||||
sock.on('error', e => { res.end(JSON.stringify({ success: false, host, port, error: e.message })); });
|
||||
sock.on('timeout', () => { res.end(JSON.stringify({ success: false, host, port, error: 'timeout' })); sock.destroy(); });
|
||||
} else if (url.pathname === '/external-test') {
|
||||
try { const r = await fetch('https://httpbin.org/ip',{signal:AbortSignal.timeout(5000)}); res.end(JSON.stringify({ external: true, data: await r.json() })); }
|
||||
catch(e) { res.end(JSON.stringify({ external: false, error: e.message })); }
|
||||
} else { res.end(JSON.stringify({ error: 'not found' })); }
|
||||
}).listen(8080, () => console.log('HTTP diagnostics on 8080'));
|
||||
`]
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
console.log('Update A:', JSON.stringify(await updateA.json()));
|
||||
|
||||
// Update App B
|
||||
const updateB = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: `mutation UpdateResourceValues($id: uuid!, $values: json!) {
|
||||
update_ghaymah_cloud_resources_by_pk(
|
||||
pk_columns: { id: $id },
|
||||
_set: { values: $values }
|
||||
) {
|
||||
id status
|
||||
}
|
||||
}`,
|
||||
variables: {
|
||||
id: APP_B_ID,
|
||||
values: {
|
||||
name: 'tcp-test-client',
|
||||
projectId: PROJECT_ID,
|
||||
container: { image: 'node:22-alpine' },
|
||||
resourceTier: 't1',
|
||||
ports: [{ number: 8080, expose: true }],
|
||||
publicAccess: {
|
||||
enabled: true,
|
||||
domain: domainB
|
||||
},
|
||||
source: { enabled: false },
|
||||
command: ['node', '-e', `
|
||||
const net = require('net');
|
||||
const http = require('http');
|
||||
const dns = require('dns');
|
||||
const os = require('os');
|
||||
http.createServer(async (req, res) => {
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
if (url.pathname === '/health' || url.pathname === '/') {
|
||||
res.end(JSON.stringify({ status: 'ok', service: 'tcp-test-client', hostname: os.hostname() }));
|
||||
} else if (url.pathname === '/dns-lookup') {
|
||||
const name = url.searchParams.get('name');
|
||||
try { const addrs = await dns.promises.resolve4(name); res.end(JSON.stringify({ name, addresses: addrs, source: 'resolve4' })); }
|
||||
catch(e) { try { const r = await dns.promises.lookup(name); res.end(JSON.stringify({ name, address: r.address, source: 'lookup' })); } catch(e2) { res.end(JSON.stringify({ name, error: e.code, error2: e2.code })); } }
|
||||
} else if (url.pathname === '/resolv-conf') {
|
||||
try { res.end(JSON.stringify({ resolv_conf: require('fs').readFileSync('/etc/resolv.conf','utf8') })); } catch(e) { res.end(JSON.stringify({ error: e.message })); }
|
||||
} else if (url.pathname === '/ip-addr') {
|
||||
res.end(JSON.stringify({ interfaces: os.networkInterfaces() }));
|
||||
} else if (url.pathname === '/tcp-connect') {
|
||||
const host = url.searchParams.get('host'), port = parseInt(url.searchParams.get('port'));
|
||||
const sock = new net.Socket(); sock.setTimeout(5000);
|
||||
sock.connect(port, host, () => { sock.write('HELLO_B'); sock.on('data', d => { res.end(JSON.stringify({ success: true, host, port, response: d.toString() })); sock.destroy(); }); });
|
||||
sock.on('error', e => { res.end(JSON.stringify({ success: false, host, port, error: e.message })); });
|
||||
sock.on('timeout', () => { res.end(JSON.stringify({ success: false, host, port, error: 'timeout' })); sock.destroy(); });
|
||||
} else if (url.pathname === '/external-test') {
|
||||
try { const r = await fetch('https://httpbin.org/ip',{signal:AbortSignal.timeout(5000)}); res.end(JSON.stringify({ external: true, data: await r.json() })); }
|
||||
catch(e) { res.end(JSON.stringify({ external: false, error: e.message })); }
|
||||
} else if (url.pathname === '/port-scan') {
|
||||
const host = url.searchParams.get('host');
|
||||
const portsStr = url.searchParams.get('ports') || '22,53,80,443,3000,4000,5432,6379,8080,9000';
|
||||
const ports = portsStr.split(',').map(Number);
|
||||
const results = [];
|
||||
for (const p of ports) {
|
||||
await new Promise((resolve) => {
|
||||
const s = new net.Socket(); s.setTimeout(2000);
|
||||
s.connect(p, host, () => { results.push({ port: p, status: 'open' }); s.destroy(); resolve(); });
|
||||
s.on('error', e => { results.push({ port: p, status: 'closed', error: e.code }); resolve(); });
|
||||
s.on('timeout', () => { results.push({ port: p, status: 'filtered' }); s.destroy(); resolve(); });
|
||||
});
|
||||
}
|
||||
res.end(JSON.stringify({ host, scan: results }));
|
||||
} else { res.end(JSON.stringify({ error: 'not found' })); }
|
||||
}).listen(8080, () => console.log('TCP test client on 8080'));
|
||||
`]
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
console.log('Update B:', JSON.stringify(await updateB.json()));
|
||||
|
||||
// Wait for re-deployment
|
||||
console.log('Waiting 30s for re-deployment...');
|
||||
await new Promise(r => setTimeout(r, 30000));
|
||||
|
||||
// Check status again
|
||||
const finalRes = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: `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
|
||||
}
|
||||
}`,
|
||||
variables: { userId }
|
||||
})
|
||||
});
|
||||
const finalData = await finalRes.json();
|
||||
finalData.data.ghaymah_cloud_resources.forEach(r => {
|
||||
if (r.values.name === 'tcp-echo-server' || r.values.name === 'tcp-test-client') {
|
||||
console.log('FINAL:', r.values.name, '| status:', r.status, '| domain:', r.values.publicAccess?.domain, '| msg:', r.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Test the domains
|
||||
for (const domain of [domainA, domainB]) {
|
||||
try {
|
||||
const testRes = await fetch(`https://${domain}/health`, { signal: AbortSignal.timeout(5000) });
|
||||
console.log(`${domain} /health:`, testRes.status, await testRes.text());
|
||||
} catch(e) {
|
||||
console.log(`${domain} /health: ERROR`, e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
main().catch(e => console.error('FATAL:', e));
|
||||
Reference in New Issue
Block a user