259 lines
11 KiB
Markdown
259 lines
11 KiB
Markdown
# RedFlag
|
||
|
||
**Self-hosted update management for operators who own their stack.**
|
||
|
||
`v0.2.0.7` — May 2026 · MIT License
|
||
|
||
---
|
||
|
||
The update manager is part of your attack surface. Most homelab tooling ignores this. RedFlag doesn't.
|
||
|
||
Every command the server issues is Ed25519-signed. Agents verify the signature, check the nonce, validate the timestamp, and reject anything they've seen before. The signing key never leaves your server. Hardware binding means a stolen agent config doesn't work on a different machine. You can read the security model in the code, not in marketing copy.
|
||
|
||
It also just manages your updates — across Linux and Windows, including Docker containers running on those hosts — from a single dashboard, with a human approval step before anything gets installed.
|
||
|
||
ConnectWise charges $50/agent/month. RedFlag doesn't.
|
||
|
||
---
|
||
|
||
|  |  |  |
|
||
|---|---|---|
|
||
|
||
<details>
|
||
<summary>More screenshots</summary>
|
||
|
||
|  |  |  |
|
||
|---|---|---|
|
||
|
||
|  |  |  |
|
||
|---|---|---|
|
||
|
||
</details>
|
||
|
||
---
|
||
|
||
## Quick Start
|
||
|
||
### Server
|
||
|
||
```bash
|
||
git clone https://github.com/Fimeg/RedFlag.git
|
||
cd RedFlag
|
||
cp config/.env.bootstrap.example config/.env
|
||
docker-compose build && docker-compose up -d
|
||
```
|
||
|
||
Open `http://localhost:31336`, complete the setup wizard, then restart:
|
||
|
||
```bash
|
||
docker-compose down && docker-compose up -d
|
||
```
|
||
|
||
### Agent
|
||
|
||
Get a registration token from **Settings → Token Management**, then:
|
||
|
||
**Linux / macOS:**
|
||
```bash
|
||
curl -sfL https://your-server.com/install | sudo bash -s -- your-registration-token
|
||
```
|
||
|
||
**Windows:**
|
||
```powershell
|
||
iwr https://your-server.com/install.ps1 | iex your-registration-token
|
||
```
|
||
|
||
---
|
||
|
||
## What It Manages
|
||
|
||
| Platform | Package Managers / Scanners |
|
||
|---|---|
|
||
| Linux | APT, DNF, Docker (socket) |
|
||
| Windows | Winget, Windows Update (COM), Docker (socket) |
|
||
|
||
Agents run at the OS level and query the Docker socket directly — there's no separate container agent. Agents are pull-based: they check in every 5 minutes and execute what the server has approved. The server never initiates a connection.
|
||
|
||
---
|
||
|
||
## Trust Model
|
||
|
||
Agents register with a one-time token plus a hardware fingerprint. The server stores the fingerprint; future check-ins that don't match the registered machine are rejected. This prevents config copying between hosts.
|
||
|
||
On first connect, the agent fetches and caches the server's Ed25519 public key (TOFU). Every subsequent command is verified against it. Keys have TTL-based rotation — agents pre-cache new keys before the old ones expire, so rotation is zero-downtime.
|
||
|
||
Every command includes a signed nonce with a 10-minute validity window. The agent tracks executed nonces and rejects replays, including from an attacker who intercepted a valid command.
|
||
|
||
Agent-server communication runs over HTTPS. The Ed25519 signing model is a defense-in-depth layer on top of that — commands can't be forged or replayed even if traffic is somehow intercepted or TLS is terminated at a proxy. The signing model doesn't assume the transport is trustworthy. Cert pinning and enforced TLS verification are on the roadmap.
|
||
|
||
Before a package is installed: the agent fetches the expected SHA-256 from the server, downloads the artifact, verifies the hash. Mismatch blocks the install. At approval time, OSV.dev is queried for known vulnerabilities in npm and PyPI packages.
|
||
|
||
**Refresh-token rotation.** Each renewal mints a new refresh token and marks the old one consumed. Replaying a consumed token whose successor was also consumed means theft — the server revokes the entire token family and logs a security event. Agent crash-before-save is covered by accept-previous-once grace: a consumed token whose successor is still unconsumed gets a fresh one, not a revocation.
|
||
|
||
**Machine-bound renewal.** The renewal endpoint now checks `X-Machine-ID` against the registered host, exactly as command endpoints do. A stolen `config.json` cannot mint access tokens from an unregistered machine — a mismatch returns 403 with a logged `machine_id_mismatch` security event. The agent surfaces this as a critical event, not a quiet backoff.
|
||
|
||
**Security Health** is surfaced as a dashboard panel on each agent — signing status, nonce protection, machine binding violations, command validation — so the posture is visible without digging through logs.
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
```
|
||
┌─────────────────┐
|
||
│ Web Dashboard │ React + TypeScript
|
||
│ Port: 31336 │
|
||
└────────┬────────┘
|
||
│ HTTPS + JWT + Machine Binding
|
||
┌────────▼────────┐
|
||
│ Server (Go) │ PostgreSQL · Ed25519 Signing Service
|
||
│ Port: 31337 │
|
||
└────────┬────────┘
|
||
│ Pull-based (agents check in, not the reverse)
|
||
├──────────────────┐
|
||
┌────────▼────────┐ ┌──────▼──────────┐
|
||
│ Linux Agent │ │ Windows Agent │
|
||
│ │ │ │
|
||
│ APT / DNF │ │ Winget / WUA │
|
||
│ Docker socket │ │ Docker socket │
|
||
└─────────────────┘ └─────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Features
|
||
|
||
- **Approval workflow** — updates queue for human review before anything runs
|
||
- **Maintenance windows** — day/time gates on when installs can proceed
|
||
- **Upstream tracking** — polls GitHub, Gitea, GitLab, Bitbucket for new releases; flags EOL drift
|
||
- **Drift detection** — knows what should be installed vs. what is, bridges the gap into update packages
|
||
- **Agent self-update** — SHA-256 → signature → atomic binary swap → service restart, reconciled server-side
|
||
- **Dependency dry-run** — checks before installing, not after
|
||
- **Idempotent installer** — re-running won't create duplicate agents
|
||
- **Proxy support** — HTTP/HTTPS/SOCKS5 for restricted networks
|
||
- **Native services** — systemd on Linux, Windows Services on Windows
|
||
- **Full audit trail** — all operations logged with context, nothing sanitized
|
||
|
||
---
|
||
|
||
## Status
|
||
|
||
**Working in production:**
|
||
- Linux and Windows agent registration and update management
|
||
- APT, DNF, Winget, Windows Update, Docker image scanning
|
||
- Dry-run dependency checking
|
||
- Real-time heartbeat and rapid polling
|
||
- Ed25519 key rotation and replay protection
|
||
- Supply chain hash verification and OSV.dev checks
|
||
- Maintenance windows
|
||
- Upstream version tracking (GitHub, Gitea, GitLab, Bitbucket, Repology, endoflife.date)
|
||
- Agent self-update with rollback
|
||
|
||
**Known issues:**
|
||
- Winget detection occasionally misses packages (Windows API limitation)
|
||
- Some Windows Updates reappear after installation (Windows Update quirk, not ours)
|
||
- No AUR, Snap, Flatpak, or Homebrew support yet
|
||
- macOS agent binaries not yet signed
|
||
- Mobile dashboard is usable, not optimized
|
||
|
||
---
|
||
|
||
## Updating
|
||
|
||
```bash
|
||
git pull && docker-compose down && docker-compose build --no-cache && docker-compose up -d
|
||
```
|
||
|
||
Agent self-update runs from the dashboard. Requires a real service manager (`systemd` on Linux, SCM on Windows). Container-only agent deployments can't self-update through this path — redeploy with the new image instead.
|
||
|
||
If a self-update times out, the previous binary is preserved at `<binary>.bak` on the agent host. Restore manually and restart the service.
|
||
|
||
<details>
|
||
<summary>Nuclear option (full reset)</summary>
|
||
|
||
```bash
|
||
docker-compose down -v --remove-orphans && \
|
||
rm config/.env && \
|
||
docker-compose build --no-cache && \
|
||
cp config/.env.bootstrap.example config/.env && \
|
||
docker-compose up -d
|
||
```
|
||
|
||
This wipes all data including the database. Re-register agents afterward with new tokens.
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Upgrading from pre-v0.1.20</summary>
|
||
|
||
Old installations used different paths. Clean reinstall is the supported migration path.
|
||
|
||
Remove old artifacts if present:
|
||
```bash
|
||
sudo rm -rf /etc/aggregator/ /usr/local/bin/aggregator-agent /var/lib/aggregator/
|
||
```
|
||
|
||
Then install fresh with the standard one-liner.
|
||
|
||
</details>
|
||
|
||
---
|
||
|
||
## Philosophy
|
||
|
||
RedFlag follows ETHOS:
|
||
|
||
- **Honest** — what you see is what you get
|
||
- **Transparent** — errors logged with full context, nothing sanitized
|
||
- **Secure** — hardware binding, cryptographic verification, local-only logging
|
||
- **Open standards** — no vendor lock-in, no cloud dependency, no telemetry
|
||
|
||
The maintainer runs this on their own infrastructure. Releases are versioned, migrations are idempotent. If something breaks, the error shows up in full — not sanitized into a generic failure message.
|
||
|
||
Built for operators who'd rather own the problem than outsource it.
|
||
|
||
---
|
||
|
||
## Changelog
|
||
|
||
**v0.2.0.7 (May 2026)**
|
||
- Refresh-token rotation with accept-previous-once crash-recovery grace
|
||
- Machine-bound renewal path closes stolen-config.json replay attack
|
||
- Typed sentinel errors for auth failures in agent polling loop
|
||
- Loud terminal-state detection: revoked refresh tokens and machine-ID mismatches surface as critical events, not silent retries
|
||
- No-more-unsigned-binaries: signing-disabled path removed from orchestrator; unsigned fallback removed from download handler
|
||
|
||
**v0.2.0.6 (May 2026)**
|
||
- Agent cold-start trust root: signed release manifest verified before first binary execution
|
||
- Supply-chain hash registry (Layer 1): expected SHA-256 stored server-side, verified by agents before install
|
||
- Drift → UpdatePackage bridge: drifted bindings automatically create pending update packages
|
||
- Upstream tracking UI with release-source adapters (GitHub, Gitea, GitLab, Bitbucket)
|
||
- Attention panel: surfaces offline agents, failed updates, EOL drift, upstream movement
|
||
|
||
**v0.2.0.0 (May 2026)**
|
||
- Maintenance windows for scheduling/gating installs
|
||
- OSV.dev supply chain checks at approval time (npm/PyPI)
|
||
- Ed25519 key rotation with TTL-based auto-refresh
|
||
- Command signing v3: agent_id binding prevents relay attacks
|
||
- Replay protection via signed nonces (10-minute window)
|
||
- Semver-aware version comparison
|
||
- Binary path traversal protection
|
||
- Machine ID rebind endpoint for hardware migration
|
||
- 170+ tests across 18 packages
|
||
- Command lifecycle v2: `received` state, disk-persisted dedup, authenticated binary download
|
||
|
||
**v0.1.27 (December 2025)**
|
||
- Hardware binding with machine fingerprinting
|
||
- Ed25519 signing for all commands and updates
|
||
- Error transparency system (ETHOS #1)
|
||
- Circuit breakers and retry logic
|
||
- Agent auto-update fully implemented
|
||
- Rate limiting (60 req/min, configurable)
|
||
- Command deduplication and idempotency
|
||
|
||
---
|
||
|
||
## License
|
||
|
||
MIT — see [LICENSE](LICENSE).
|
||
|
||
**Third-party:** Windows Update integration based on [windowsupdate](https://github.com/ceshihao/windowsupdate) (Apache 2.0).
|