Compare commits
2
Commits
150a69b60b
...
701835e9f3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
701835e9f3 | ||
|
|
0aaaa12ba1 |
@@ -7,7 +7,7 @@ NexaFabric is an open-source SDN-like network and security control plane for Pro
|
||||
- FastAPI backend with SQLAlchemy 2, Alembic-ready models, JWT auth, RBAC primitives, audit logging, and provider interfaces.
|
||||
- React + TypeScript frontend with Vite, Tailwind CSS, TanStack Query, React Router, Zustand, dark/light mode, and production-oriented pages.
|
||||
- PostgreSQL, Redis, worker, API, frontend, and reverse proxy through Docker Compose.
|
||||
- Demo seed data for clusters, nodes, workloads, networks, tenants, policies, IPAM, jobs, and audit events.
|
||||
- Optional demo seed data for clusters, nodes, workloads, networks, tenants, policies, IPAM, jobs, and audit events.
|
||||
- Tests, lint/type-check scripts, CI workflow, and operational documentation.
|
||||
|
||||
## Quick Start
|
||||
@@ -22,10 +22,150 @@ Then open:
|
||||
- Frontend: http://localhost:8080
|
||||
- API docs: http://localhost:8080/api/docs
|
||||
|
||||
Demo login:
|
||||
On a fresh database NexaFabric opens the setup wizard first. The wizard creates the first Super Admin user and registers the first Proxmox or demo provider.
|
||||
|
||||
- Email: `admin@nexafabric.local`
|
||||
- Password: `ChangeMe_UseEnvInstead`
|
||||
To start from scratch during testing:
|
||||
|
||||
```bash
|
||||
docker compose down -v
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Demo data is disabled by default. Enable it only for lab screenshots or UI testing:
|
||||
|
||||
```env
|
||||
SEED_DEMO_DATA=true
|
||||
```
|
||||
|
||||
## Proxmox Preparation
|
||||
|
||||
NexaFabric can read inventory from the Proxmox VE API immediately after you add a cluster, but IPAM and flow visibility depend on what Proxmox and the guests expose. Use this checklist before expecting full data in the UI.
|
||||
|
||||
### 1. Create A Dedicated API Token
|
||||
|
||||
In Proxmox VE, create a dedicated user and API token instead of using a personal admin token.
|
||||
|
||||
Recommended UI path:
|
||||
|
||||
1. `Datacenter` -> `Permissions` -> `Users`
|
||||
2. Create a user such as `nexafabric@pve` or another realm you manage.
|
||||
3. `Datacenter` -> `Permissions` -> `API Tokens`
|
||||
4. Add a token such as `nexafabric@pve!control-plane`.
|
||||
5. Keep privilege separation enabled unless you intentionally want the token to inherit all user privileges.
|
||||
|
||||
NexaFabric accepts both token formats:
|
||||
|
||||
```text
|
||||
PVEAPIToken=nexafabric@pve!control-plane=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
nexafabric@pve!control-plane=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
```
|
||||
|
||||
The installer stores the token with the cluster and starts in `read_only` mode by default.
|
||||
|
||||
### 2. Assign Minimum Read Permissions
|
||||
|
||||
For inventory, IP discovery, policy preview, and dashboard data, assign the token read access at the Datacenter level or the narrowest paths that contain your nodes and guests.
|
||||
|
||||
Minimum practical read privileges:
|
||||
|
||||
- `Sys.Audit` for cluster and node inventory.
|
||||
- `VM.Audit` for VM and container inventory/config visibility.
|
||||
- `SDN.Audit` if you use Proxmox SDN zones, VNets, EVPN, or related network objects.
|
||||
|
||||
For future write-enabled firewall orchestration, create a separate token or role and do not reuse the read-only token. Only enable write mode after previews and audit logging have been verified in your environment.
|
||||
|
||||
### 3. Enable QEMU Guest Agent For VM IP Discovery
|
||||
|
||||
For QEMU VMs, Proxmox only exposes guest interface/IP details reliably when the QEMU Guest Agent is installed in the VM and enabled in Proxmox.
|
||||
|
||||
Per VM:
|
||||
|
||||
1. Install the guest agent inside the VM.
|
||||
- Debian/Ubuntu: `apt install qemu-guest-agent`
|
||||
- RHEL/Rocky/Alma: `dnf install qemu-guest-agent`
|
||||
- Windows: install the VirtIO guest tools including the QEMU guest agent.
|
||||
2. Enable and start the service in the guest.
|
||||
- Linux: `systemctl enable --now qemu-guest-agent`
|
||||
3. In Proxmox UI, open the VM:
|
||||
- `Options` -> `QEMU Guest Agent` -> `Enabled`
|
||||
4. Reboot the VM or fully stop/start it if Proxmox does not immediately report the agent.
|
||||
|
||||
NexaFabric uses the Proxmox guest-agent network interface endpoint to discover IPv4 addresses for IPAM. If the guest agent is missing, the VM can still appear in inventory, but IPAM may not learn its IP address.
|
||||
|
||||
### 4. LXC IP Discovery
|
||||
|
||||
For LXC containers, NexaFabric reads static IPs from the Proxmox container network config when available.
|
||||
|
||||
Works best when container interfaces are configured with explicit IPs, for example:
|
||||
|
||||
```text
|
||||
net0: name=eth0,bridge=vmbr0,ip=10.10.10.50/24,gw=10.10.10.1
|
||||
```
|
||||
|
||||
If the container uses DHCP, Proxmox may not always have a stable IP value in config. In that case, use a DHCP lease source, static reservations, or a future NexaFabric node-agent/flow-source integration.
|
||||
|
||||
### 5. Sync Cluster Inventory
|
||||
|
||||
After adding the cluster in NexaFabric:
|
||||
|
||||
1. Open `Clusters`.
|
||||
2. Click the cluster row/name.
|
||||
3. Use `Test` to validate the token.
|
||||
4. Use `Sync` to import nodes, VMs/LXCs, networks, and discoverable IP addresses.
|
||||
5. Open `IPAM` -> `Discover from Proxmox` if you want to rerun IP discovery later.
|
||||
|
||||
Imported IPs are placed into an automatically created `discovered-ipam` network if NexaFabric cannot map them to an existing subnet.
|
||||
|
||||
### 6. Firewall And Policy Requirements
|
||||
|
||||
NexaFabric policy preview does not require Proxmox firewall writes. It compiles NexaFabric policies into provider-specific preview output and records audit events.
|
||||
|
||||
Before enabling real firewall apply workflows:
|
||||
|
||||
- Ensure Proxmox firewall is enabled intentionally at the Datacenter, node, and guest level where you want enforcement.
|
||||
- Keep NexaFabric clusters in `read_only` mode until previews are reviewed.
|
||||
- Use `audit` mode policies first to see what would be allowed or blocked.
|
||||
- Confirm that Proxmox API token permissions match the exact write operations you plan to allow.
|
||||
- Keep backups of Proxmox firewall configuration before enabling automation.
|
||||
|
||||
NexaFabric is designed to read first, simulate second, and only apply after explicit confirmation.
|
||||
|
||||
### 7. Network Flow Visibility
|
||||
|
||||
Proxmox inventory and guest agent data are enough for:
|
||||
|
||||
- Cluster, node, VM, LXC inventory.
|
||||
- Network object visibility.
|
||||
- IPAM discovery for VMs with QEMU Guest Agent.
|
||||
- Static LXC IP discovery.
|
||||
- Policy matching and firewall previews.
|
||||
|
||||
Actual traffic flow visibility, top talkers, byte counters, and per-workload traffic history require an additional telemetry source. Proxmox VE does not provide full flow telemetry for every VM through the basic inventory API.
|
||||
|
||||
Supported or planned options:
|
||||
|
||||
- NexaFabric node agent on Proxmox nodes to read nftables/conntrack or flow counters.
|
||||
- Open vSwitch with sFlow/NetFlow/IPFIX exported to a collector.
|
||||
- Router/firewall flow exports from pfSense, OPNsense, FRR/BGP edge devices, or physical switches.
|
||||
- eBPF or host-level telemetry in future agent builds.
|
||||
|
||||
Until such a source is configured, NexaFabric will show `No flow telemetry collected yet` instead of fake traffic.
|
||||
|
||||
### 8. Troubleshooting Proxmox Integration
|
||||
|
||||
`401 No ticket` or `Provider sync failed` usually means:
|
||||
|
||||
- The API token format is wrong.
|
||||
- The token was copied without the secret value after `=`.
|
||||
- The token lacks the required ACLs.
|
||||
- Privilege separation is enabled but no permissions were assigned to the token.
|
||||
- The wrong realm/user/token ID was used.
|
||||
|
||||
TLS errors usually mean:
|
||||
|
||||
- Proxmox uses a self-signed certificate.
|
||||
- The hostname in `Cluster API URL` does not match the certificate.
|
||||
- Disable `Verify TLS certificate` only for trusted lab systems, or install a valid certificate on Proxmox.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
@@ -39,4 +179,3 @@ nginx/ Reverse proxy example
|
||||
## Safety Model
|
||||
|
||||
NexaFabric never applies firewall changes without a preview, validation, and audit record. The included Proxmox provider is designed around read-only inventory first. Write-enabled orchestration is intentionally routed through explicit dry-run and apply workflows.
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { FormEvent, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Moon, ShieldCheck, Sun } from "lucide-react";
|
||||
import { Activity, LockKeyhole, Moon, Network, ShieldCheck, Sun } from "lucide-react";
|
||||
|
||||
import { login } from "../api/client";
|
||||
import { useTheme } from "../stores/theme";
|
||||
|
||||
export function Login() {
|
||||
const navigate = useNavigate();
|
||||
const [email, setEmail] = useState("admin@nexafabric.local");
|
||||
const [password, setPassword] = useState("ChangeMe_UseEnvInstead");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const { dark, toggle } = useTheme();
|
||||
|
||||
@@ -24,36 +24,93 @@ export function Login() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid min-h-screen place-items-center bg-canvas px-4">
|
||||
<div className="min-h-screen bg-canvas text-slate-900 dark:text-slate-100">
|
||||
<button
|
||||
className="fixed right-4 top-4 rounded-md border border-border bg-panel p-2 text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-800"
|
||||
className="fixed right-4 top-4 z-10 rounded-md border border-border bg-panel p-2 text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-800"
|
||||
onClick={toggle}
|
||||
type="button"
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
{dark ? <Sun size={18} /> : <Moon size={18} />}
|
||||
</button>
|
||||
<form onSubmit={submit} className="w-full max-w-sm rounded-md border border-border bg-panel p-6 shadow-sm">
|
||||
<div className="mb-6 flex items-center gap-3">
|
||||
<div className="rounded-md bg-accent p-2 text-white">
|
||||
<ShieldCheck size={22} />
|
||||
<div className="grid min-h-screen lg:grid-cols-[1.1fr_0.9fr]">
|
||||
<section className="relative hidden overflow-hidden border-r border-border bg-panel lg:block">
|
||||
<div className="absolute inset-0 opacity-40">
|
||||
<div className="absolute left-16 top-24 h-48 w-48 rounded-full border border-accent/40" />
|
||||
<div className="absolute right-20 top-48 h-72 w-72 rounded-full border border-slate-400/30" />
|
||||
<div className="absolute bottom-24 left-1/3 h-56 w-56 rounded-full border border-accent/30" />
|
||||
</div>
|
||||
<div className="relative flex h-full flex-col justify-between p-12">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded-md bg-accent p-3 text-white">
|
||||
<ShieldCheck size={26} />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-xl font-semibold">NexaFabric</h1>
|
||||
<p className="text-sm text-slate-500">Sign in to the control plane</p>
|
||||
<div className="text-2xl font-semibold">NexaFabric</div>
|
||||
<div className="text-sm text-slate-500 dark:text-slate-400">Network and security control plane</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="max-w-xl animate-[slideUp_520ms_ease-out]">
|
||||
<h1 className="mb-4 text-5xl font-semibold leading-tight tracking-normal">
|
||||
Operate Proxmox networks with intent.
|
||||
</h1>
|
||||
<p className="text-base leading-7 text-slate-500 dark:text-slate-400">
|
||||
Centralize inventory, IPAM, segmentation policy, firewall preview, and audit history without patching Proxmox.
|
||||
</p>
|
||||
<div className="mt-8 grid gap-3">
|
||||
{[
|
||||
["Inventory sync", Network],
|
||||
["Policy audit mode", Activity],
|
||||
["Preview before apply", LockKeyhole],
|
||||
].map(([label, Icon]) => {
|
||||
const LucideIcon = Icon as typeof Network;
|
||||
return (
|
||||
<div key={String(label)} className="flex items-center gap-3 rounded-md border border-border bg-canvas/70 p-3 text-sm">
|
||||
<LucideIcon size={18} className="text-accent" />
|
||||
<span>{String(label)}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-slate-500 dark:text-slate-400">Read first. Simulate second. Apply only after confirmation.</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="grid place-items-center px-4 py-12">
|
||||
<form onSubmit={submit} className="w-full max-w-md animate-[fadeIn_420ms_ease-out] rounded-md border border-border bg-panel p-7 shadow-sm">
|
||||
<div className="mb-7">
|
||||
<div className="mb-4 inline-flex rounded-md bg-accent p-3 text-white lg:hidden">
|
||||
<ShieldCheck size={24} />
|
||||
</div>
|
||||
<h1 className="text-2xl font-semibold">Sign in</h1>
|
||||
<p className="mt-1 text-sm text-slate-500 dark:text-slate-400">Use the administrator account created during setup.</p>
|
||||
</div>
|
||||
<label className="mb-4 block text-sm">
|
||||
Email
|
||||
<input className="mt-1 w-full rounded-md border border-border bg-transparent px-3 py-2" value={email} onChange={(event) => setEmail(event.target.value)} />
|
||||
<input
|
||||
className="mt-1 h-11 w-full rounded-md border border-border bg-transparent px-3 outline-none focus:border-accent"
|
||||
value={email}
|
||||
autoComplete="username"
|
||||
onChange={(event) => setEmail(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label className="mb-4 block text-sm">
|
||||
<label className="mb-5 block text-sm">
|
||||
Password
|
||||
<input className="mt-1 w-full rounded-md border border-border bg-transparent px-3 py-2" type="password" value={password} onChange={(event) => setPassword(event.target.value)} />
|
||||
<input
|
||||
className="mt-1 h-11 w-full rounded-md border border-border bg-transparent px-3 outline-none focus:border-accent"
|
||||
type="password"
|
||||
value={password}
|
||||
autoComplete="current-password"
|
||||
onChange={(event) => setPassword(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
{error ? <div className="mb-4 rounded-md border border-danger px-3 py-2 text-sm text-danger">{error}</div> : null}
|
||||
<button className="h-10 w-full rounded-md bg-accent text-sm font-medium text-white">Sign In</button>
|
||||
<button className="h-11 w-full rounded-md bg-accent text-sm font-medium text-white disabled:opacity-50" disabled={!email || !password}>
|
||||
Sign In
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user