feat: complete multi-microservice project deployment, internal DNS, raw TCP and security penetration report
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
const fs = require('fs');
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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('--- Step 1: Creating Project: ghaymah-soc-microservices ---');
|
||||
const projectId = crypto.randomUUID();
|
||||
const projRes = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: `mutation CreateProject($id: uuid!, $userId: uuid!, $chartName: String!, $chartVersion: String!, $values: json!) {
|
||||
insert_ghaymah_cloud_resources_one(object: {
|
||||
id: $id,
|
||||
user_id: $userId,
|
||||
chart_name: $chartName,
|
||||
chart_version: $chartVersion,
|
||||
values: $values
|
||||
}) {
|
||||
id status
|
||||
}
|
||||
}`,
|
||||
variables: {
|
||||
id: projectId,
|
||||
userId: userId,
|
||||
chartName: 'ghaymah-project',
|
||||
chartVersion: '0.0',
|
||||
values: {
|
||||
name: 'ghaymah-soc-microservices',
|
||||
userId: userId
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
const projData = await projRes.json();
|
||||
console.log('Project creation response:', JSON.stringify(projData));
|
||||
|
||||
// IDs for services
|
||||
const redisId = crypto.randomUUID();
|
||||
const backendId = crypto.randomUUID();
|
||||
const gatewayId = crypto.randomUUID();
|
||||
const testerId = crypto.randomUUID();
|
||||
|
||||
// Compute uniqueNames
|
||||
const redisHash = await genDomainHash('soc-redis', redisId);
|
||||
const redisUnique = `soc-redis-${redisHash}`;
|
||||
|
||||
const backendHash = await genDomainHash('soc-backend', backendId);
|
||||
const backendUnique = `soc-backend-${backendHash}`;
|
||||
|
||||
const gatewayHash = await genDomainHash('soc-gateway', gatewayId);
|
||||
const gatewayUnique = `soc-gateway-${gatewayHash}`;
|
||||
const gatewayDomain = `${gatewayUnique}.hosted.ghaymah.systems`;
|
||||
|
||||
const testerHash = await genDomainHash('soc-tester', testerId);
|
||||
const testerUnique = `soc-tester-${testerHash}`;
|
||||
const testerDomain = `${testerUnique}.hosted.ghaymah.systems`;
|
||||
|
||||
console.log('\n--- Service Topology Plan ---');
|
||||
console.log('1. Database/Cache: soc-redis');
|
||||
console.log(' ID:', redisId);
|
||||
console.log(' Internal DNS UniqueName:', redisUnique);
|
||||
console.log(' Port: 6379 (TCP private)');
|
||||
|
||||
console.log('2. SOC Backend: soc-backend');
|
||||
console.log(' ID:', backendId);
|
||||
console.log(' Internal DNS UniqueName:', backendUnique);
|
||||
console.log(' Port: 4000 (HTTP/TCP private)');
|
||||
|
||||
console.log('3. Edge Gateway: soc-gateway');
|
||||
console.log(' ID:', gatewayId);
|
||||
console.log(' Internal DNS UniqueName:', gatewayUnique);
|
||||
console.log(' Public Domain:', gatewayDomain);
|
||||
console.log(' Port: 7681/3000 (HTTP public)');
|
||||
|
||||
console.log('4. Security Diagnostic Pod: soc-tester');
|
||||
console.log(' ID:', testerId);
|
||||
console.log(' Internal DNS UniqueName:', testerUnique);
|
||||
console.log(' Public Domain:', testerDomain);
|
||||
console.log(' Port: 7681 (ttyd terminal public)');
|
||||
|
||||
const topology = {
|
||||
projectId,
|
||||
projectName: 'ghaymah-soc-microservices',
|
||||
createdAt: new Date().toISOString(),
|
||||
services: {
|
||||
redis: { id: redisId, name: 'soc-redis', uniqueName: redisUnique, port: 6379, expose: false },
|
||||
backend: { id: backendId, name: 'soc-backend', uniqueName: backendUnique, port: 4000, expose: false },
|
||||
gateway: { id: gatewayId, name: 'soc-gateway', uniqueName: gatewayUnique, domain: gatewayDomain, port: 7681, expose: true },
|
||||
tester: { id: testerId, name: 'soc-tester', uniqueName: testerUnique, domain: testerDomain, port: 7681, expose: true }
|
||||
}
|
||||
};
|
||||
fs.writeFileSync('D:/cumin/ghaymah_dns_net_sec_tests/soc_project_topology.json', JSON.stringify(topology, null, 2));
|
||||
|
||||
// Function to deploy app
|
||||
async function deployApp(id, name, values) {
|
||||
console.log(`Deploying ${name}...`);
|
||||
const res = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: `mutation CreateApp($id: uuid!, $userId: uuid!, $chartName: String!, $chartVersion: String!, $values: json!) {
|
||||
insert_ghaymah_cloud_resources_one(object: {
|
||||
id: $id,
|
||||
user_id: $userId,
|
||||
chart_name: $chartName,
|
||||
chart_version: $chartVersion,
|
||||
values: $values
|
||||
}) {
|
||||
id status
|
||||
}
|
||||
}`,
|
||||
variables: {
|
||||
id: id,
|
||||
userId: userId,
|
||||
chartName: 'ghaymah-app',
|
||||
chartVersion: '0.1',
|
||||
values: values
|
||||
}
|
||||
})
|
||||
});
|
||||
const d = await res.json();
|
||||
console.log(` -> Response for ${name}:`, JSON.stringify(d));
|
||||
}
|
||||
|
||||
console.log('\n--- Step 2: Deploying Microservice 1: soc-redis ---');
|
||||
await deployApp(redisId, 'soc-redis', {
|
||||
name: 'soc-redis',
|
||||
projectId: projectId,
|
||||
resourceTier: 't1',
|
||||
ports: [{ number: 6379, expose: false }],
|
||||
container: { image: 'redis:alpine' },
|
||||
publicAccess: { enabled: false }
|
||||
});
|
||||
|
||||
console.log('\n--- Step 3: Deploying Microservice 2: soc-backend ---');
|
||||
// soc-backend runs an alpine container configured for SOC backend
|
||||
await deployApp(backendId, 'soc-backend', {
|
||||
name: 'soc-backend',
|
||||
projectId: projectId,
|
||||
resourceTier: 't1',
|
||||
ports: [{ number: 4000, expose: false }, { number: 7681, expose: false }],
|
||||
container: { image: 'tsl0922/ttyd:alpine' },
|
||||
publicAccess: { enabled: false },
|
||||
env: {
|
||||
PORT: '4000',
|
||||
REDIS_HOST: redisUnique,
|
||||
REDIS_PORT: '6379',
|
||||
SERVICE_NAME: 'soc-backend'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n--- Step 4: Deploying Microservice 3: soc-gateway ---');
|
||||
await deployApp(gatewayId, 'soc-gateway', {
|
||||
name: 'soc-gateway',
|
||||
projectId: projectId,
|
||||
resourceTier: 't1',
|
||||
ports: [{ number: 7681, expose: true }],
|
||||
container: { image: 'tsl0922/ttyd:alpine' },
|
||||
publicAccess: {
|
||||
enabled: true,
|
||||
domain: gatewayDomain,
|
||||
baseDomain: 'hosted.ghaymah.systems'
|
||||
},
|
||||
env: {
|
||||
PORT: '7681',
|
||||
BACKEND_URL: `http://${backendUnique}:4000`,
|
||||
SERVICE_NAME: 'soc-gateway'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n--- Step 5: Deploying Microservice 4: soc-tester ---');
|
||||
await deployApp(testerId, 'soc-tester', {
|
||||
name: 'soc-tester',
|
||||
projectId: projectId,
|
||||
resourceTier: 't1',
|
||||
ports: [{ number: 7681, expose: true }],
|
||||
container: { image: 'tsl0922/ttyd:alpine' },
|
||||
publicAccess: {
|
||||
enabled: true,
|
||||
domain: testerDomain,
|
||||
baseDomain: 'hosted.ghaymah.systems'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\nAll 4 microservices created successfully in project ghaymah-soc-microservices!');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,53 @@
|
||||
const fs = require('fs');
|
||||
|
||||
async function main() {
|
||||
const tokenData = JSON.parse(fs.readFileSync('D:/cumin/ghaymah_dns_net_sec_tests/token.json', 'utf8'));
|
||||
const topology = JSON.parse(fs.readFileSync('D:/cumin/ghaymah_dns_net_sec_tests/soc_project_topology.json', 'utf8'));
|
||||
|
||||
const backend = topology.services.backend;
|
||||
const domain = `${backend.uniqueName}.hosted.ghaymah.systems`;
|
||||
|
||||
console.log('Enabling management ingress on soc-backend:', domain);
|
||||
|
||||
const res = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + tokenData.token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: `mutation UpdateResource($id: uuid!, $values: json!) {
|
||||
update_ghaymah_cloud_resources_by_pk(pk_columns: { id: $id }, _set: { values: $values }) {
|
||||
id status values
|
||||
}
|
||||
}`,
|
||||
variables: {
|
||||
id: backend.id,
|
||||
values: {
|
||||
name: 'soc-backend',
|
||||
projectId: topology.projectId,
|
||||
resourceTier: 't1',
|
||||
ports: [{ number: 7681, expose: true }],
|
||||
container: { image: 'tsl0922/ttyd:alpine' },
|
||||
publicAccess: {
|
||||
enabled: true,
|
||||
domain: domain,
|
||||
baseDomain: 'hosted.ghaymah.systems'
|
||||
},
|
||||
env: {
|
||||
PORT: '4000',
|
||||
REDIS_HOST: topology.services.redis.uniqueName,
|
||||
REDIS_PORT: '6379'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
console.log('Update result:', JSON.stringify(data));
|
||||
|
||||
// Update topology file
|
||||
backend.domain = domain;
|
||||
backend.expose = true;
|
||||
fs.writeFileSync('D:/cumin/ghaymah_dns_net_sec_tests/soc_project_topology.json', JSON.stringify(topology, null, 2));
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,7 @@
|
||||
const fs = require('fs');
|
||||
const content = fs.readFileSync('D:/cumin/ghaymah_dns_net_sec_tests/ghaymah_bundle.js', 'utf8');
|
||||
|
||||
const idx = content.indexOf('publicAccess', 600000);
|
||||
if (idx !== -1) {
|
||||
console.log(content.substring(idx - 200, idx + 800));
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
const fs = require('fs');
|
||||
|
||||
async function main() {
|
||||
const tokenData = JSON.parse(fs.readFileSync('D:/cumin/ghaymah_dns_net_sec_tests/token.json', 'utf8'));
|
||||
const res = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + tokenData.token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: `query GetResources($userId: uuid) {
|
||||
ghaymah_cloud_resources(where: { user_id: { _eq: $userId }, deleted: { _eq: false } }) {
|
||||
id chart_name status values message
|
||||
}
|
||||
}`,
|
||||
variables: { userId: tokenData.userId }
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
console.log('Total resources:', data.data?.ghaymah_cloud_resources?.length);
|
||||
for (const r of data.data.ghaymah_cloud_resources) {
|
||||
console.log(`\n[${r.chart_name}] ID: ${r.id} | Status: ${r.status} | Name: ${r.values?.name}`);
|
||||
if (r.chart_name === 'ghaymah-app') {
|
||||
console.log(' Container:', JSON.stringify(r.values?.container));
|
||||
console.log(' Ports:', JSON.stringify(r.values?.ports));
|
||||
console.log(' PublicAccess:', JSON.stringify(r.values?.publicAccess));
|
||||
console.log(' Project ID:', r.values?.projectId);
|
||||
if (r.values?.command) console.log(' Command:', r.values?.command);
|
||||
if (r.values?.env) console.log(' Env:', JSON.stringify(r.values?.env));
|
||||
}
|
||||
}
|
||||
}
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,57 @@
|
||||
const fs = require('fs');
|
||||
|
||||
async function main() {
|
||||
const tokenData = JSON.parse(fs.readFileSync('D:/cumin/ghaymah_dns_net_sec_tests/token.json', 'utf8'));
|
||||
const topology = JSON.parse(fs.readFileSync('D:/cumin/ghaymah_dns_net_sec_tests/soc_project_topology.json', 'utf8'));
|
||||
|
||||
const resourceIds = [
|
||||
topology.projectId,
|
||||
topology.services.redis.id,
|
||||
topology.services.backend.id,
|
||||
topology.services.gateway.id,
|
||||
topology.services.tester.id
|
||||
];
|
||||
|
||||
console.log('Monitoring deployment status for project:', topology.projectName);
|
||||
|
||||
for (let attempt = 1; attempt <= 20; attempt++) {
|
||||
const res = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + tokenData.token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: `query CheckResources($ids: [uuid!]!) {
|
||||
ghaymah_cloud_resources(where: { id: { _in: $ids } }) {
|
||||
id chart_name status values message
|
||||
}
|
||||
}`,
|
||||
variables: { ids: resourceIds }
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
const list = data.data?.ghaymah_cloud_resources || [];
|
||||
|
||||
const statusMap = {};
|
||||
list.forEach(r => {
|
||||
const name = r.values?.name || r.chart_name;
|
||||
statusMap[name] = r.status;
|
||||
});
|
||||
|
||||
console.log(`[Attempt ${attempt}/20] Status:`, JSON.stringify(statusMap));
|
||||
|
||||
const allSynced = list.length === 5 && list.every(r => r.status === 'synced');
|
||||
if (allSynced) {
|
||||
console.log('>>> ALL 5 RESOURCES ARE SYNCED AND READY! <<<');
|
||||
return true;
|
||||
}
|
||||
|
||||
const anyFailed = list.some(r => r.status === 'failed' || r.status === 'error');
|
||||
if (anyFailed) {
|
||||
console.log('>>> Warning: Some resources failed:', list.filter(r => r.status === 'failed' || r.status === 'error'));
|
||||
}
|
||||
|
||||
await new Promise(r => setTimeout(r, 4000));
|
||||
}
|
||||
console.log('Timed out waiting for sync');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
+8
-12
@@ -1,14 +1,15 @@
|
||||
const WSS_URL = 'wss://client-terminal-0f1fe89209a8.hosted.ghaymah.systems/ws';
|
||||
const DEFAULT_URL = 'wss://soc-tester-9b6540e2d4ed.hosted.ghaymah.systems/ws';
|
||||
|
||||
class RemoteTerminal {
|
||||
constructor() {
|
||||
constructor(url = DEFAULT_URL) {
|
||||
this.url = url;
|
||||
this.ws = null;
|
||||
this.ready = false;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.ws = new WebSocket(WSS_URL, ['tty']);
|
||||
this.ws = new WebSocket(this.url, ['tty']);
|
||||
|
||||
this.ws.onopen = () => {
|
||||
this.ws.send(JSON.stringify({ AuthToken: '' }));
|
||||
@@ -19,11 +20,11 @@ class RemoteTerminal {
|
||||
}, 800);
|
||||
};
|
||||
|
||||
this.ws.onerror = (e) => reject(new Error('WS error: ' + e.message));
|
||||
this.ws.onerror = (e) => reject(new Error('WS error: ' + (e.message || JSON.stringify(e))));
|
||||
});
|
||||
}
|
||||
|
||||
async exec(command, timeoutMs = 10000) {
|
||||
async exec(command, timeoutMs = 12000) {
|
||||
if (!this.ready) await this.connect();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -43,7 +44,6 @@ class RemoteTerminal {
|
||||
if (text.startsWith('0')) text = text.substring(1);
|
||||
rawOutput += text;
|
||||
|
||||
// Match \n__RET__<code|marker>__END__ when printed by echo, NOT during command typing
|
||||
const regex = new RegExp(`\r?\n__RET__(\\d+)_${markerId}__END__`);
|
||||
const match = rawOutput.match(regex);
|
||||
|
||||
@@ -56,9 +56,7 @@ class RemoteTerminal {
|
||||
const exitCode = parseInt(match[1]);
|
||||
const lines = clean.split('\n');
|
||||
|
||||
// Find the line where the command was echoed
|
||||
const cmdIdx = lines.findIndex(l => l.includes(markerId) && l.includes('echo'));
|
||||
// Find the line where the result marker is
|
||||
const retIdx = lines.findIndex(l => l.startsWith(`__RET__${exitCode}_${markerId}__END__`));
|
||||
|
||||
let outLines = [];
|
||||
@@ -70,7 +68,6 @@ class RemoteTerminal {
|
||||
outLines = lines;
|
||||
}
|
||||
|
||||
// Filter out prompt lines
|
||||
const filtered = outLines.filter(l => !l.endsWith('~#') && !l.endsWith('$') && !l.includes('bash ('));
|
||||
|
||||
resolve({
|
||||
@@ -99,7 +96,6 @@ class RemoteTerminal {
|
||||
|
||||
this.ws.addEventListener('message', messageHandler);
|
||||
|
||||
// Execute command and output return marker
|
||||
const fullCmd = `${command}; echo __RET__$?_${markerId}__END__\n`;
|
||||
this.ws.send('0' + fullCmd);
|
||||
});
|
||||
@@ -124,8 +120,8 @@ if (require.main === module) {
|
||||
console.log(res1.output);
|
||||
console.log('Exit code:', res1.exitCode);
|
||||
|
||||
console.log('\nTesting cat /etc/resolv.conf...');
|
||||
const res2 = await term.exec('cat /etc/resolv.conf');
|
||||
console.log('\nTesting hostname & ip...');
|
||||
const res2 = await term.exec('hostname -i || ip addr');
|
||||
console.log('--- OUTPUT 2 ---');
|
||||
console.log(res2.output);
|
||||
console.log('Exit code:', res2.exitCode);
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
const fs = require('fs');
|
||||
const { RemoteTerminal } = require('./remote_exec');
|
||||
|
||||
const TOPOLOGY_FILE = 'D:/cumin/ghaymah_dns_net_sec_tests/soc_project_topology.json';
|
||||
const LOG_FILE = 'D:/cumin/ghaymah_dns_net_sec_tests/logs/soc_multiservice_execution.log';
|
||||
const RESULTS_FILE = 'D:/cumin/ghaymah_dns_net_sec_tests/logs/soc_multiservice_results.json';
|
||||
|
||||
const logs = [];
|
||||
const results = [];
|
||||
|
||||
function log(phase, title, details = '') {
|
||||
const ts = new Date().toISOString();
|
||||
const entry = `[${ts}] [${phase}] === ${title} ===\n${details}\n`;
|
||||
console.log(entry);
|
||||
logs.push(entry);
|
||||
fs.writeFileSync(LOG_FILE, logs.join('\n'));
|
||||
}
|
||||
|
||||
function recordResult(category, testName, target, status, output, notes = '') {
|
||||
results.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
category,
|
||||
testName,
|
||||
target,
|
||||
status,
|
||||
notes,
|
||||
output: (output || '').trim()
|
||||
});
|
||||
fs.writeFileSync(RESULTS_FILE, JSON.stringify(results, null, 2));
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const topology = JSON.parse(fs.readFileSync(TOPOLOGY_FILE, 'utf8'));
|
||||
const redisName = topology.services.redis.uniqueName;
|
||||
const backendName = topology.services.backend.uniqueName;
|
||||
const gatewayName = topology.services.gateway.uniqueName;
|
||||
const gatewayDomain = topology.services.gateway.domain;
|
||||
|
||||
log('INIT', 'Starting Comprehensive Multi-Microservice QA & Security Suite for Project:', topology.projectName);
|
||||
|
||||
// Connect to soc-tester pod
|
||||
const tester = new RemoteTerminal(topology.services.tester.domain ? `wss://${topology.services.tester.domain}/ws` : undefined);
|
||||
await tester.connect();
|
||||
log('INIT', 'Connected to soc-tester diagnostic container');
|
||||
|
||||
// Connect to soc-gateway pod
|
||||
const gateway = new RemoteTerminal(`wss://${gatewayDomain}/ws`);
|
||||
await gateway.connect();
|
||||
log('INIT', 'Connected to soc-gateway edge container');
|
||||
|
||||
// ==========================================
|
||||
// PHASE 1: RECONNAISSANCE & POD TOPOLOGY
|
||||
// ==========================================
|
||||
log('PHASE1-RECON', 'Container Network & OS Information (soc-tester)');
|
||||
const osInfo = await tester.exec('uname -a && cat /etc/os-release | grep PRETTY_NAME');
|
||||
log('PHASE1-RECON', 'OS Info', osInfo.output);
|
||||
recordResult('Reconnaissance', 'OS & Kernel', 'soc-tester', 'PASS', osInfo.output);
|
||||
|
||||
const ipInfo = await tester.exec('ip addr show eth0 || ifconfig eth0');
|
||||
log('PHASE1-RECON', 'IP & Network Interfaces', ipInfo.output);
|
||||
recordResult('Reconnaissance', 'IP Interface', 'soc-tester', 'PASS', ipInfo.output);
|
||||
|
||||
const resolvConf = await tester.exec('cat /etc/resolv.conf');
|
||||
log('PHASE1-RECON', 'CoreDNS Resolver Configuration', resolvConf.output);
|
||||
recordResult('Reconnaissance', 'CoreDNS Config (/etc/resolv.conf)', 'CoreDNS', 'PASS', resolvConf.output);
|
||||
|
||||
// ==========================================
|
||||
// PHASE 2: INTERNAL DNS VERIFICATION
|
||||
// ==========================================
|
||||
log('PHASE2-DNS', 'Internal DNS Resolution for Microservices');
|
||||
|
||||
// Test 2.1: soc-redis lookup
|
||||
const dnsRedis = await tester.exec(`nslookup ${redisName}`);
|
||||
log('PHASE2-DNS', `nslookup ${redisName}`, dnsRedis.output);
|
||||
const redisResolved = dnsRedis.output.includes('10.42.');
|
||||
recordResult('DNS Functionality', 'Resolve soc-redis', redisName, redisResolved ? 'PASS' : 'FAIL', dnsRedis.output, 'Resolves redis to internal overlay IP');
|
||||
|
||||
// Test 2.2: soc-backend lookup
|
||||
const dnsBackend = await tester.exec(`nslookup ${backendName}`);
|
||||
log('PHASE2-DNS', `nslookup ${backendName}`, dnsBackend.output);
|
||||
const backendResolved = dnsBackend.output.includes('10.42.');
|
||||
recordResult('DNS Functionality', 'Resolve soc-backend', backendName, backendResolved ? 'PASS' : 'FAIL', dnsBackend.output, 'Resolves backend to internal overlay IP');
|
||||
|
||||
// Test 2.3: soc-gateway lookup
|
||||
const dnsGateway = await tester.exec(`nslookup ${gatewayName}`);
|
||||
log('PHASE2-DNS', `nslookup ${gatewayName}`, dnsGateway.output);
|
||||
const gatewayResolved = dnsGateway.output.includes('10.42.');
|
||||
recordResult('DNS Functionality', 'Resolve soc-gateway', gatewayName, gatewayResolved ? 'PASS' : 'FAIL', dnsGateway.output, 'Resolves gateway to internal overlay IP');
|
||||
|
||||
// Test 2.4: dig full FQDN lookup
|
||||
const digRedis = await tester.exec(`dig +short ${redisName}`);
|
||||
log('PHASE2-DNS', `dig +short ${redisName}`, digRedis.output);
|
||||
recordResult('DNS Functionality', 'Dig A Record', redisName, digRedis.output.trim() ? 'PASS' : 'FAIL', digRedis.output);
|
||||
|
||||
// Test 2.5: Reverse DNS PTR check
|
||||
const redisIp = digRedis.output.trim();
|
||||
if (redisIp) {
|
||||
const ptrCheck = await tester.exec(`dig +short -x ${redisIp}`);
|
||||
log('PHASE2-DNS', `Reverse DNS PTR for ${redisIp}`, ptrCheck.output);
|
||||
recordResult('DNS Functionality', 'Reverse PTR Lookup', redisIp, 'INFO', ptrCheck.output);
|
||||
}
|
||||
|
||||
// Test 2.6: Negative DNS test (non-existent host)
|
||||
const negDns = await tester.exec('nslookup non-existent-service-mesh.local');
|
||||
log('PHASE2-DNS', 'Negative DNS Lookup', negDns.output);
|
||||
const negOk = negDns.output.includes('NXDOMAIN') || negDns.output.includes("can't find");
|
||||
recordResult('DNS Functionality', 'Negative DNS Handling', 'non-existent-service-mesh.local', negOk ? 'PASS' : 'FAIL', negDns.output, 'Expect NXDOMAIN');
|
||||
|
||||
// ==========================================
|
||||
// PHASE 3: RAW TCP PROTOCOL VERIFICATION (REDIS)
|
||||
// ==========================================
|
||||
log('PHASE3-TCP', 'Raw TCP Socket Communication with soc-redis (Port 6379)');
|
||||
|
||||
// Test 3.1: Raw TCP Port Check
|
||||
const tcpProbe = await tester.exec(`nc -zv -w 3 ${redisName} 6379 2>&1`);
|
||||
log('PHASE3-TCP', 'Raw TCP Port 6379 Probe', tcpProbe.output);
|
||||
const portOpen = tcpProbe.output.includes('open');
|
||||
recordResult('TCP Functionality', 'TCP Port 6379 Probe', `${redisName}:6379`, portOpen ? 'PASS' : 'FAIL', tcpProbe.output);
|
||||
|
||||
// Test 3.2: Redis PING Handshake
|
||||
const pingRes = await tester.exec(`printf "*1\\r\\n\\$4\\r\\nPING\\r\\n" | nc -w 3 ${redisName} 6379`);
|
||||
log('PHASE3-TCP', 'Raw TCP Redis PING', pingRes.output);
|
||||
const pingOk = pingRes.output.includes('+PONG');
|
||||
recordResult('TCP Functionality', 'Redis PING Handshake', `${redisName}:6379`, pingOk ? 'PASS' : 'FAIL', pingRes.output, 'Expect +PONG');
|
||||
|
||||
// Test 3.3: Write Security Threat Incident into Redis via raw TCP
|
||||
const alertPayload = 'CRITICAL_RANSOMWARE_BLOCKED_ON_MESH';
|
||||
const writeCmd = `printf "*3\\r\\n\\$3\\r\\nSET\\r\\n\\$16\\r\\nsoc:threat:id001\\r\\n\\$${alertPayload.length}\\r\\n${alertPayload}\\r\\n" | nc -w 3 ${redisName} 6379`;
|
||||
const writeRes = await tester.exec(writeCmd);
|
||||
log('PHASE3-TCP', 'Raw TCP Write Threat Event', writeRes.output);
|
||||
const writeOk = writeRes.output.includes('+OK');
|
||||
recordResult('TCP Functionality', 'Redis SET Security Event', `${redisName}:6379`, writeOk ? 'PASS' : 'FAIL', writeRes.output, 'Expect +OK');
|
||||
|
||||
// Test 3.4: Read Security Threat Incident from Redis via raw TCP
|
||||
const readCmd = `printf "*2\\r\\n\\$3\\r\\nGET\\r\\n\\$16\\r\\nsoc:threat:id001\\r\\n" | nc -w 3 ${redisName} 6379`;
|
||||
const readRes = await tester.exec(readCmd);
|
||||
log('PHASE3-TCP', 'Raw TCP Read Threat Event', readRes.output);
|
||||
const readOk = readRes.output.includes(alertPayload);
|
||||
recordResult('TCP Functionality', 'Redis GET Security Event', `${redisName}:6379`, readOk ? 'PASS' : 'FAIL', readRes.output, `Retrieved ${alertPayload}`);
|
||||
|
||||
// Test 3.5: Atomic Security Threat Counter Increment
|
||||
const incrCmd = `printf "*2\\r\\n\\$4\\r\\nINCR\\r\\n\\$24\\r\\nsoc:threats:total_count\\r\\n" | nc -w 3 ${redisName} 6379`;
|
||||
const incrRes = await tester.exec(incrCmd);
|
||||
log('PHASE3-TCP', 'Raw TCP Atomic Counter Increment', incrRes.output);
|
||||
const incrOk = incrRes.output.includes(':');
|
||||
recordResult('TCP Functionality', 'Redis INCR Attack Counter', `${redisName}:6379`, incrOk ? 'PASS' : 'FAIL', incrRes.output);
|
||||
|
||||
// Test 3.6: Multi-Microservice Mesh Hop: Edge Gateway to Redis via internal DNS
|
||||
log('PHASE3-TCP', 'Gateway-to-Redis Direct Internal TCP Communication');
|
||||
const gwPingRes = await gateway.exec(`printf "*1\\r\\n\\$4\\r\\nPING\\r\\n" | nc -w 3 ${redisName} 6379`);
|
||||
log('PHASE3-TCP', 'Gateway-to-Redis Ping', gwPingRes.output);
|
||||
const gwPingOk = gwPingRes.output.includes('+PONG');
|
||||
recordResult('Inter-Service Mesh', 'soc-gateway -> soc-redis (Port 6379)', `${gatewayName} -> ${redisName}`, gwPingOk ? 'PASS' : 'FAIL', gwPingRes.output, 'Edge Gateway queries Database over internal mesh');
|
||||
|
||||
// Test 3.7: Gateway-to-Backend Direct Internal TCP Communication
|
||||
log('PHASE3-TCP', 'Gateway-to-Backend Direct Internal TCP Communication');
|
||||
const gwBackendRes = await gateway.exec(`nc -zv -w 3 ${backendName} 7681 2>&1`);
|
||||
log('PHASE3-TCP', 'Gateway-to-Backend Port Probe', gwBackendRes.output);
|
||||
const gwBackendOk = gwBackendRes.output.includes('open');
|
||||
recordResult('Inter-Service Mesh', 'soc-gateway -> soc-backend (Port 7681)', `${gatewayName} -> ${backendName}`, gwBackendOk ? 'PASS' : 'FAIL', gwBackendRes.output, 'Gateway probes backend over internal mesh');
|
||||
|
||||
// ==========================================
|
||||
// PHASE 4: EDGE INGRESS & PUBLIC REACHABILITY
|
||||
// ==========================================
|
||||
log('PHASE4-EDGE', 'External Edge Ingress Verification');
|
||||
try {
|
||||
const extFetch = await fetch(`https://${gatewayDomain}/`, { signal: AbortSignal.timeout(8000) });
|
||||
const extBody = await extFetch.text();
|
||||
const extOk = extFetch.status === 200;
|
||||
log('PHASE4-EDGE', `Public Edge Ingress (https://${gatewayDomain}/)`, `HTTP Status: ${extFetch.status}, Length: ${extBody.length}`);
|
||||
recordResult('Edge Ingress', 'Public Gateway URL', `https://${gatewayDomain}/`, extOk ? 'PASS' : 'FAIL', `HTTP Status: ${extFetch.status}`);
|
||||
} catch (e) {
|
||||
log('PHASE4-EDGE', 'Edge Ingress Error', e.message);
|
||||
recordResult('Edge Ingress', 'Public Gateway URL', `https://${gatewayDomain}/`, 'FAIL', e.message);
|
||||
}
|
||||
|
||||
// Verify private database has NO public route
|
||||
try {
|
||||
const redisExt = await fetch(`https://${redisName}.hosted.ghaymah.systems/`, { signal: AbortSignal.timeout(4000) });
|
||||
const redisExtStatus = redisExt.status;
|
||||
log('PHASE4-EDGE', 'Private Database Public Leak Test', `HTTP ${redisExtStatus}`);
|
||||
recordResult('Edge Ingress Isolation', 'Database External Unreachability', `https://${redisName}.hosted.ghaymah.systems/`, redisExtStatus === 404 || redisExtStatus === 502 || redisExtStatus === 503 ? 'PASS' : 'WARNING', `Status: ${redisExtStatus}`, 'Private service should not be exposed externally');
|
||||
} catch (e) {
|
||||
log('PHASE4-EDGE', 'Private Database Public Leak Test', 'Connection blocked or NXDOMAIN (Expected)');
|
||||
recordResult('Edge Ingress Isolation', 'Database External Unreachability', `${redisName}.hosted.ghaymah.systems`, 'PASS', e.message, 'Properly blocked at ingress');
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// PHASE 5: SECURITY & PENETRATION TESTING
|
||||
// ==========================================
|
||||
log('PHASE5-SEC', 'Security & Penetration Testing (Isolation, Lateral Movement, Egress)');
|
||||
|
||||
// Test 5.1: Intra-Tenant Cross-Project DNS Resolution
|
||||
// Target: test-redis from project dns-tcp-test
|
||||
const crossDns1 = await tester.exec('nslookup test-redis-feccbaec5bd7');
|
||||
log('PHASE5-SEC', 'Cross-Project DNS: test-redis-feccbaec5bd7', crossDns1.output);
|
||||
const crossDns1Resolved = crossDns1.output.includes('10.42.');
|
||||
recordResult('Security / Isolation', 'Cross-Project DNS (test-redis-feccbaec5bd7)', 'test-redis-feccbaec5bd7', crossDns1Resolved ? 'FINDING' : 'SECURE', crossDns1.output, crossDns1Resolved ? 'Intra-tenant cross-project DNS allowed' : 'Project isolated');
|
||||
|
||||
// Target: auth-deploy from project auth-deploy
|
||||
const crossDns2 = await tester.exec('nslookup auth-deploy-4c0bca85312b');
|
||||
log('PHASE5-SEC', 'Cross-Project DNS: auth-deploy-4c0bca85312b', crossDns2.output);
|
||||
const crossDns2Resolved = crossDns2.output.includes('10.42.');
|
||||
recordResult('Security / Isolation', 'Cross-Project DNS (auth-deploy-4c0bca85312b)', 'auth-deploy-4c0bca85312b', crossDns2Resolved ? 'FINDING' : 'SECURE', crossDns2.output, crossDns2Resolved ? 'Intra-tenant cross-project DNS allowed' : 'Project isolated');
|
||||
|
||||
// Test 5.2: Intra-Tenant Cross-Project TCP Lateral Movement
|
||||
const crossTcp1 = await tester.exec('nc -zv -w 3 test-redis-feccbaec5bd7 6379 2>&1');
|
||||
log('PHASE5-SEC', 'Cross-Project TCP: test-redis-feccbaec5bd7:6379', crossTcp1.output);
|
||||
const crossTcp1Open = crossTcp1.output.includes('open');
|
||||
recordResult('Security / Isolation', 'Cross-Project TCP Lateral Movement (Redis 6379)', 'test-redis-feccbaec5bd7:6379', crossTcp1Open ? 'FINDING' : 'SECURE', crossTcp1.output, crossTcp1Open ? 'Unrestricted intra-tenant cross-project TCP socket connection allowed' : 'TCP connection blocked by NetworkPolicy');
|
||||
|
||||
const crossHttp2 = await tester.exec('wget -qO- -T 3 http://auth-deploy-4c0bca85312b/ | head -n 3');
|
||||
log('PHASE5-SEC', 'Cross-Project HTTP: auth-deploy-4c0bca85312b:80', crossHttp2.output);
|
||||
const crossHttp2Open = crossHttp2.output.includes('<') || crossHttp2.output.includes('html');
|
||||
recordResult('Security / Isolation', 'Cross-Project HTTP Lateral Movement (Auth-deploy:80)', 'auth-deploy-4c0bca85312b:80', crossHttp2Open ? 'FINDING' : 'SECURE', crossHttp2.output, crossHttp2Open ? 'Cross-project HTTP communication succeeded' : 'HTTP blocked');
|
||||
|
||||
// Test 5.3: Infrastructure & Multi-Tenant Boundary Probing
|
||||
// Target: Kubernetes API Server
|
||||
const k8sApiProbe = await tester.exec('curl -k -s -m 3 https://10.43.0.1:443/version || nc -zv -w 3 10.43.0.1 443 2>&1');
|
||||
log('PHASE5-SEC', 'Kubernetes API Server Probe (10.43.0.1:443)', k8sApiProbe.output);
|
||||
recordResult('Security / Cluster Boundary', 'Kubernetes API Server (10.43.0.1:443)', '10.43.0.1:443', 'INFO', k8sApiProbe.output, 'Checks cluster API access controls');
|
||||
|
||||
// Target: Cloud Metadata Service (169.254.169.254)
|
||||
const metaProbe = await tester.exec('curl -s -m 3 http://169.254.169.254/latest/meta-data/ || echo "BLOCKED_OR_UNREACHABLE"');
|
||||
log('PHASE5-SEC', 'Cloud Metadata Service Probe (169.254.169.254)', metaProbe.output);
|
||||
const metaBlocked = metaProbe.output.includes('BLOCKED_OR_UNREACHABLE') || !metaProbe.output.trim();
|
||||
recordResult('Security / SSRF Isolation', 'Cloud Metadata Endpoint (169.254.169.254)', '169.254.169.254', metaBlocked ? 'PASS' : 'CRITICAL_LEAK', metaProbe.output, metaBlocked ? 'Cloud metadata endpoint unreachable' : 'CRITICAL: Metadata service exposed');
|
||||
|
||||
// Target: CoreDNS Prometheus Metrics (10.43.0.10:9153)
|
||||
const dnsMetrics = await tester.exec('curl -s -m 3 http://10.43.0.10:9153/metrics | head -n 5 || echo "BLOCKED"');
|
||||
log('PHASE5-SEC', 'CoreDNS Metrics Probe (10.43.0.10:9153)', dnsMetrics.output);
|
||||
recordResult('Security / Cluster Telemetry', 'CoreDNS Metrics (10.43.0.10:9153)', '10.43.0.10:9153', dnsMetrics.output.includes('coredns_') ? 'INFO' : 'SECURE', dnsMetrics.output);
|
||||
|
||||
// Test 5.4: Subnet Port Sweep & Lateral Discovery
|
||||
log('PHASE5-SEC', 'Scanning Local Subnet (10.42.5.95 - 10.42.5.105)');
|
||||
const scanCmd = `for i in $(seq 97 103); do nc -zv -w 1 10.42.5.$i 6379 2>&1 | grep open || true; nc -zv -w 1 10.42.5.$i 7681 2>&1 | grep open || true; done`;
|
||||
const scanRes = await tester.exec(scanCmd, 15000);
|
||||
log('PHASE5-SEC', 'Subnet Port Sweep Results', scanRes.output);
|
||||
recordResult('Security / Network Recon', 'Internal Subnet Port Sweep', '10.42.5.97-103', 'PASS', scanRes.output, 'Identified active service endpoints on overlay network');
|
||||
|
||||
// Test 5.5: Container Capabilities & Privilege Audit
|
||||
const capCheck = await tester.exec('grep Cap /proc/self/status && whoami');
|
||||
log('PHASE5-SEC', 'Container Privilege & Capabilities', capCheck.output);
|
||||
recordResult('Security / Container Hardening', 'Privilege & Capabilities', 'soc-tester', 'INFO', capCheck.output);
|
||||
|
||||
// Test 5.6: Egress NAT Public IP Discovery
|
||||
const egressIp = await tester.exec('curl -s -m 4 https://api.ipify.org || wget -qO- -T 4 https://api.ipify.org');
|
||||
log('PHASE5-SEC', 'Egress Public IP', egressIp.output);
|
||||
recordResult('Network / Egress NAT', 'Outbound Public IP', 'api.ipify.org', 'PASS', egressIp.output, 'Cluster node egress NAT IP');
|
||||
|
||||
tester.close();
|
||||
gateway.close();
|
||||
|
||||
log('COMPLETE', 'Multi-Microservice QA and Security Test Suite Complete! Results recorded.');
|
||||
console.log(`\nExecution Log saved to: ${LOG_FILE}`);
|
||||
console.log(`Structured Results saved to: ${RESULTS_FILE}`);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,19 @@
|
||||
const { RemoteTerminal } = require('./remote_exec');
|
||||
|
||||
async function main() {
|
||||
const term = new RemoteTerminal();
|
||||
await term.connect();
|
||||
console.log('Connected to soc-tester pod!');
|
||||
|
||||
console.log('--- Probing soc-backend internal port 7681 ---');
|
||||
const res = await term.exec('nc -zv -w 3 soc-backend-d1895c70f6b2 7681 2>&1');
|
||||
console.log('NC:', res.output);
|
||||
|
||||
console.log('--- HTTP GET from soc-tester to soc-backend:7681 ---');
|
||||
const curl = await term.exec('curl -sI http://soc-backend-d1895c70f6b2:7681/ | head -n 5');
|
||||
console.log('CURL HEAD:', curl.output);
|
||||
|
||||
term.close();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,30 @@
|
||||
const { RemoteTerminal } = require('./remote_exec');
|
||||
|
||||
async function main() {
|
||||
const term = new RemoteTerminal();
|
||||
await term.connect();
|
||||
console.log('Connected to soc-tester pod!');
|
||||
|
||||
console.log('--- Test 1: TCP Port Probe on soc-redis ---');
|
||||
const nc = await term.exec('nc -zv -w 3 soc-redis-10f420c389ef 6379 2>&1');
|
||||
console.log('Output:', nc.output);
|
||||
|
||||
console.log('--- Test 2: Raw TCP Redis PING ---');
|
||||
const pingCmd = 'printf "*1\\r\\n\\$4\\r\\nPING\\r\\n" | nc -w 3 soc-redis-10f420c389ef 6379';
|
||||
const ping = await term.exec(pingCmd);
|
||||
console.log('Output:', ping.output);
|
||||
|
||||
console.log('--- Test 3: Raw TCP Redis SET & GET ---');
|
||||
const setGetCmd = 'printf "*3\\r\\n\\$3\\r\\nSET\\r\\n\\$15\\r\\nsoc:mesh:status\\r\\n\\$16\\r\\nGHAYMAH_MESH_OK\\r\\n*2\\r\\n\\$3\\r\\nGET\\r\\n\\$15\\r\\nsoc:mesh:status\\r\\n" | nc -w 3 soc-redis-10f420c389ef 6379';
|
||||
const setGet = await term.exec(setGetCmd);
|
||||
console.log('Output:', setGet.output);
|
||||
|
||||
console.log('--- Test 4: Raw TCP Redis INFO ---');
|
||||
const infoCmd = 'printf "*1\\r\\n\\$4\\r\\nINFO\\r\\n" | nc -w 3 soc-redis-10f420c389ef 6379 | grep -E "redis_version|connected_clients|tcp_port|role"';
|
||||
const info = await term.exec(infoCmd);
|
||||
console.log('Output:', info.output);
|
||||
|
||||
term.close();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,29 @@
|
||||
const url = 'wss://soc-tester-9b6540e2d4ed.hosted.ghaymah.systems/ws';
|
||||
|
||||
async function main() {
|
||||
console.log('Connecting to soc-tester terminal:', url);
|
||||
const ws = new WebSocket(url, ['tty']);
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('WebSocket opened successfully!');
|
||||
ws.send(JSON.stringify({ AuthToken: '' }));
|
||||
ws.send('1' + JSON.stringify({ columns: 120, rows: 40 }));
|
||||
setTimeout(() => {
|
||||
ws.send('0uname -a; hostname; cat /etc/resolv.conf\n');
|
||||
}, 500);
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
let text = typeof event.data === 'string' ? event.data : Buffer.from(event.data).toString('utf8');
|
||||
process.stdout.write(text.startsWith('0') ? text.substring(1) : text);
|
||||
};
|
||||
|
||||
ws.onerror = (err) => console.error('WS error:', err.message || err);
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('\nClosing connection after 5 seconds...');
|
||||
ws.close();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user