feat: initial commit of Ghaymah internal DNS, TCP and security testing suite and report
This commit is contained in:
@@ -0,0 +1,404 @@
|
||||
const fs = require('fs');
|
||||
const crypto = require('crypto');
|
||||
const zlib = require('zlib');
|
||||
|
||||
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 DOMAIN_A = 'tcp-echo-server-42b5404df3a2.hosted.ghaymah.systems';
|
||||
const DOMAIN_B = 'tcp-test-client-002b2b09325b.hosted.ghaymah.systems';
|
||||
|
||||
function createTar(files) {
|
||||
const chunks = [];
|
||||
for (const file of files) {
|
||||
const name = file.name;
|
||||
const content = Buffer.isBuffer(file.content) ? file.content : Buffer.from(file.content, 'utf8');
|
||||
const size = content.length;
|
||||
|
||||
const header = Buffer.alloc(512);
|
||||
header.write(name, 0, 100, 'utf8');
|
||||
header.write('0000644\0', 100, 8, 'utf8');
|
||||
header.write('0000000\0', 108, 8, 'utf8');
|
||||
header.write('0000000\0', 116, 8, 'utf8');
|
||||
header.write(size.toString(8).padStart(11, '0') + '\0', 124, 12, 'utf8');
|
||||
const mtime = Math.floor(Date.now() / 1000);
|
||||
header.write(mtime.toString(8).padStart(11, '0') + '\0', 136, 12, 'utf8');
|
||||
header.write(' ', 148, 8, 'utf8');
|
||||
header.write('0', 156, 1, 'utf8');
|
||||
header.write('ustar\0', 257, 6, 'utf8');
|
||||
header.write('00', 263, 2, 'utf8');
|
||||
|
||||
let sum = 0;
|
||||
for (let i = 0; i < 512; i++) sum += header[i];
|
||||
header.write(sum.toString(8).padStart(6, '0') + '\0 ', 148, 8, 'utf8');
|
||||
|
||||
chunks.push(header);
|
||||
chunks.push(content);
|
||||
|
||||
const remainder = size % 512;
|
||||
if (remainder > 0) chunks.push(Buffer.alloc(512 - remainder));
|
||||
}
|
||||
chunks.push(Buffer.alloc(1024));
|
||||
return zlib.gzipSync(Buffer.concat(chunks));
|
||||
}
|
||||
|
||||
// Code for App A (TCP Echo Server + Diagnostic HTTP)
|
||||
const serverACode = `
|
||||
const http = require('http');
|
||||
const net = require('net');
|
||||
const dns = require('dns');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const cp = require('child_process');
|
||||
|
||||
// 1. Raw TCP Echo Server on port 9000
|
||||
const tcpServer = net.createServer(socket => {
|
||||
const remote = socket.remoteAddress + ':' + socket.remotePort;
|
||||
console.log('TCP client connected from', remote);
|
||||
socket.on('data', data => {
|
||||
console.log('TCP data received:', data.toString());
|
||||
socket.write('ECHO:' + data.toString());
|
||||
});
|
||||
socket.on('error', err => console.log('TCP socket error:', err.message));
|
||||
});
|
||||
tcpServer.listen(9000, '0.0.0.0', () => {
|
||||
console.log('Raw TCP Echo Server listening on 0.0.0.0:9000');
|
||||
});
|
||||
|
||||
// 2. HTTP Diagnostic Server on port 8080
|
||||
const httpServer = http.createServer(async (req, res) => {
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
|
||||
if (url.pathname === '/' || url.pathname === '/health') {
|
||||
res.end(JSON.stringify({
|
||||
status: 'ok',
|
||||
service: 'tcp-echo-server',
|
||||
hostname: os.hostname(),
|
||||
uptime: process.uptime(),
|
||||
interfaces: os.networkInterfaces()
|
||||
}));
|
||||
} else if (url.pathname === '/ip-addr') {
|
||||
res.end(JSON.stringify({ interfaces: os.networkInterfaces() }));
|
||||
} else if (url.pathname === '/resolv-conf') {
|
||||
try {
|
||||
const content = fs.readFileSync('/etc/resolv.conf', 'utf8');
|
||||
res.end(JSON.stringify({ resolv_conf: content }));
|
||||
} catch (e) {
|
||||
res.end(JSON.stringify({ error: e.message }));
|
||||
}
|
||||
} else if (url.pathname === '/dns-lookup') {
|
||||
const name = url.searchParams.get('name');
|
||||
if (!name) { res.end(JSON.stringify({ error: 'Missing ?name=' })); return; }
|
||||
const result = { name };
|
||||
try {
|
||||
result.resolve4 = await dns.promises.resolve4(name);
|
||||
} catch (e) {
|
||||
result.resolve4_error = e.code || e.message;
|
||||
}
|
||||
try {
|
||||
const lk = await dns.promises.lookup(name);
|
||||
result.lookup = lk;
|
||||
} catch (e) {
|
||||
result.lookup_error = e.code || e.message;
|
||||
}
|
||||
res.end(JSON.stringify(result));
|
||||
} else if (url.pathname === '/tcp-connect') {
|
||||
const host = url.searchParams.get('host');
|
||||
const port = parseInt(url.searchParams.get('port') || '9000');
|
||||
const client = new net.Socket();
|
||||
client.setTimeout(4000);
|
||||
client.connect(port, host, () => {
|
||||
client.write('PING_FROM_A\\n');
|
||||
});
|
||||
client.on('data', data => {
|
||||
res.end(JSON.stringify({ success: true, host, port, received: data.toString() }));
|
||||
client.destroy();
|
||||
});
|
||||
client.on('error', e => {
|
||||
res.end(JSON.stringify({ success: false, host, port, error: e.message }));
|
||||
});
|
||||
client.on('timeout', () => {
|
||||
res.end(JSON.stringify({ success: false, host, port, error: 'Connection timed out' }));
|
||||
client.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 === '/exec') {
|
||||
const cmd = url.searchParams.get('cmd');
|
||||
try {
|
||||
const out = cp.execSync(cmd, { timeout: 5000, encoding: 'utf8' });
|
||||
res.end(JSON.stringify({ cmd, stdout: out }));
|
||||
} catch (e) {
|
||||
res.end(JSON.stringify({ cmd, error: e.message, stderr: e.stderr?.toString() }));
|
||||
}
|
||||
} else {
|
||||
res.statusCode = 404;
|
||||
res.end(JSON.stringify({ error: 'Not found' }));
|
||||
}
|
||||
});
|
||||
httpServer.listen(8080, '0.0.0.0', () => {
|
||||
console.log('HTTP Diagnostic Server listening on 0.0.0.0:8080');
|
||||
});
|
||||
`;
|
||||
|
||||
// Code for App B (Diagnostic HTTP client + port scanner)
|
||||
const serverBCode = `
|
||||
const http = require('http');
|
||||
const net = require('net');
|
||||
const dns = require('dns');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const cp = require('child_process');
|
||||
|
||||
const httpServer = http.createServer(async (req, res) => {
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
|
||||
if (url.pathname === '/' || url.pathname === '/health') {
|
||||
res.end(JSON.stringify({
|
||||
status: 'ok',
|
||||
service: 'tcp-test-client',
|
||||
hostname: os.hostname(),
|
||||
uptime: process.uptime(),
|
||||
interfaces: os.networkInterfaces()
|
||||
}));
|
||||
} else if (url.pathname === '/ip-addr') {
|
||||
res.end(JSON.stringify({ interfaces: os.networkInterfaces() }));
|
||||
} else if (url.pathname === '/resolv-conf') {
|
||||
try {
|
||||
const content = fs.readFileSync('/etc/resolv.conf', 'utf8');
|
||||
res.end(JSON.stringify({ resolv_conf: content }));
|
||||
} catch (e) {
|
||||
res.end(JSON.stringify({ error: e.message }));
|
||||
}
|
||||
} else if (url.pathname === '/dns-lookup') {
|
||||
const name = url.searchParams.get('name');
|
||||
if (!name) { res.end(JSON.stringify({ error: 'Missing ?name=' })); return; }
|
||||
const result = { name };
|
||||
try {
|
||||
result.resolve4 = await dns.promises.resolve4(name);
|
||||
} catch (e) {
|
||||
result.resolve4_error = e.code || e.message;
|
||||
}
|
||||
try {
|
||||
const lk = await dns.promises.lookup(name);
|
||||
result.lookup = lk;
|
||||
} catch (e) {
|
||||
result.lookup_error = e.code || e.message;
|
||||
}
|
||||
res.end(JSON.stringify(result));
|
||||
} else if (url.pathname === '/tcp-connect') {
|
||||
const host = url.searchParams.get('host');
|
||||
const port = parseInt(url.searchParams.get('port') || '9000');
|
||||
const client = new net.Socket();
|
||||
client.setTimeout(4000);
|
||||
client.connect(port, host, () => {
|
||||
client.write('PING_FROM_B_CLIENT\\n');
|
||||
});
|
||||
client.on('data', data => {
|
||||
res.end(JSON.stringify({ success: true, host, port, received: data.toString() }));
|
||||
client.destroy();
|
||||
});
|
||||
client.on('error', e => {
|
||||
res.end(JSON.stringify({ success: false, host, port, error: e.message }));
|
||||
});
|
||||
client.on('timeout', () => {
|
||||
res.end(JSON.stringify({ success: false, host, port, error: 'Connection timed out' }));
|
||||
client.destroy();
|
||||
});
|
||||
} 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(1500);
|
||||
s.connect(p, host, () => {
|
||||
results.push({ port: p, status: 'open' });
|
||||
s.destroy();
|
||||
resolve();
|
||||
});
|
||||
s.on('error', err => {
|
||||
results.push({ port: p, status: 'closed', error: err.code });
|
||||
resolve();
|
||||
});
|
||||
s.on('timeout', () => {
|
||||
results.push({ port: p, status: 'filtered' });
|
||||
s.destroy();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
res.end(JSON.stringify({ host, scan: results }));
|
||||
} 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 === '/exec') {
|
||||
const cmd = url.searchParams.get('cmd');
|
||||
try {
|
||||
const out = cp.execSync(cmd, { timeout: 5000, encoding: 'utf8' });
|
||||
res.end(JSON.stringify({ cmd, stdout: out }));
|
||||
} catch (e) {
|
||||
res.end(JSON.stringify({ cmd, error: e.message, stderr: e.stderr?.toString() }));
|
||||
}
|
||||
} else {
|
||||
res.statusCode = 404;
|
||||
res.end(JSON.stringify({ error: 'Not found' }));
|
||||
}
|
||||
});
|
||||
httpServer.listen(8080, '0.0.0.0', () => {
|
||||
console.log('HTTP Diagnostic Client listening on 0.0.0.0:8080');
|
||||
});
|
||||
`;
|
||||
|
||||
const dockerfileA = `FROM node:22-alpine
|
||||
WORKDIR /app
|
||||
COPY server.js /app/server.js
|
||||
EXPOSE 8080 9000
|
||||
CMD ["node", "/app/server.js"]
|
||||
`;
|
||||
|
||||
const dockerfileB = `FROM node:22-alpine
|
||||
WORKDIR /app
|
||||
COPY server.js /app/server.js
|
||||
EXPOSE 8080
|
||||
CMD ["node", "/app/server.js"]
|
||||
`;
|
||||
|
||||
async function main() {
|
||||
const tokenData = JSON.parse(fs.readFileSync('D:/cumin/ghaymah_dns_net_sec_tests/token.json', 'utf8'));
|
||||
const token = tokenData.token;
|
||||
const userId = tokenData.userId;
|
||||
|
||||
console.log('Building tarballs...');
|
||||
const tarA = createTar([
|
||||
{ name: 'Dockerfile', content: dockerfileA },
|
||||
{ name: 'server.js', content: serverACode }
|
||||
]);
|
||||
const shaA = crypto.createHash('sha256').update(tarA).digest('hex');
|
||||
console.log('Tar A ready, size:', tarA.length, 'sha256:', shaA);
|
||||
|
||||
const tarB = createTar([
|
||||
{ name: 'Dockerfile', content: dockerfileB },
|
||||
{ name: 'server.js', content: serverBCode }
|
||||
]);
|
||||
const shaB = crypto.createHash('sha256').update(tarB).digest('hex');
|
||||
console.log('Tar B ready, size:', tarB.length, 'sha256:', shaB);
|
||||
|
||||
// Upload Tar A
|
||||
console.log('Uploading Tar A to Ghaymah storage...');
|
||||
const urlA = `https://s3-nhost-proxy-83e02743fd61.hosted.ghaymah.systems/files/${userId}/${APP_A_ID}.tar.gz`;
|
||||
const upA = await fetch(urlA, {
|
||||
method: 'PUT',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/gzip' },
|
||||
body: tarA
|
||||
});
|
||||
console.log('Upload A status:', upA.status, await upA.text());
|
||||
|
||||
// Upload Tar B
|
||||
console.log('Uploading Tar B to Ghaymah storage...');
|
||||
const urlB = `https://s3-nhost-proxy-83e02743fd61.hosted.ghaymah.systems/files/${userId}/${APP_B_ID}.tar.gz`;
|
||||
const upB = await fetch(urlB, {
|
||||
method: 'PUT',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/gzip' },
|
||||
body: tarB
|
||||
});
|
||||
console.log('Upload B status:', upB.status, await upB.text());
|
||||
|
||||
// Update App A Resource
|
||||
console.log('Updating App A resource values in Ghaymah GraphQL...');
|
||||
const mutA = 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,
|
||||
resourceTier: 't1',
|
||||
container: {
|
||||
image: `registry.ghaymah.systems/${userId}/${APP_A_ID}:latest`
|
||||
},
|
||||
ports: [{ number: 8080, expose: true }],
|
||||
publicAccess: {
|
||||
enabled: true,
|
||||
domain: DOMAIN_A
|
||||
},
|
||||
source: {
|
||||
enabled: true,
|
||||
ref: shaA,
|
||||
type: 'artifact',
|
||||
url: urlA
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
console.log('GraphQL Update App A:', JSON.stringify(await mutA.json()));
|
||||
|
||||
// Update App B Resource
|
||||
console.log('Updating App B resource values in Ghaymah GraphQL...');
|
||||
const mutB = 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,
|
||||
resourceTier: 't1',
|
||||
container: {
|
||||
image: `registry.ghaymah.systems/${userId}/${APP_B_ID}:latest`
|
||||
},
|
||||
ports: [{ number: 8080, expose: true }],
|
||||
publicAccess: {
|
||||
enabled: true,
|
||||
domain: DOMAIN_B
|
||||
},
|
||||
source: {
|
||||
enabled: true,
|
||||
ref: shaB,
|
||||
type: 'artifact',
|
||||
url: urlB
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
console.log('GraphQL Update App B:', JSON.stringify(await mutB.json()));
|
||||
|
||||
console.log('\nDeployment triggered! Now monitor build & deploy status...');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user