Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
184 commits
Select commit Hold shift + click to select a range
feba857
chore(deps): bump actions/upload-artifact from 4.6.2 to 7.0.1 (#14)
dependabot[bot] May 18, 2026
9f7b560
chore(deps): bump actions/download-artifact from 4.3.0 to 8.0.1 (#15)
dependabot[bot] May 18, 2026
8bf2c99
chore(deps): bump sha2 from 0.10.9 to 0.11.0 in /lynx (#16)
dependabot[bot] May 18, 2026
c9f801a
chore(deps): bump x509-parser from 0.16.0 to 0.18.1 in /lynx (#17)
dependabot[bot] May 18, 2026
6bcc0dd
chore(deps): bump tower-http from 0.6.10 to 0.6.11 in /lynx (#18)
dependabot[bot] May 18, 2026
3449261
chore(deps): bump notify from 6.1.1 to 8.2.0 in /lynx (#19)
dependabot[bot] May 18, 2026
dc49870
chore(deps): bump nix from 0.29.0 to 0.31.3 in /lynx (#21)
dependabot[bot] May 18, 2026
4326251
chore(deps): bump redis from 0.27.6 to 1.2.1 in /lynx (#20)
dependabot[bot] May 18, 2026
99942c8
chore(deps): bump tokio-tungstenite from 0.24.0 to 0.29.0 in /lynx (#22)
dependabot[bot] May 18, 2026
47e25c8
chore(deps): bump rand from 0.8.6 to 0.10.1 in /lynx (#25)
dependabot[bot] May 18, 2026
47dedb5
chore(deps): bump rcgen from 0.13.2 to 0.14.8 in /lynx (#24)
dependabot[bot] May 18, 2026
517e510
chore(deps): bump bollard from 0.17.1 to 0.21.0 in /lynx (#23)
dependabot[bot] May 18, 2026
d381ce2
chore(deps): update Cargo.lock for bollard 0.21.0
Jaro-c May 18, 2026
6cd1f73
chore: update dependencies in Cargo.lock
Jaro-c May 18, 2026
16d877e
ci: add CI gates to release workflows
Jaro-c May 18, 2026
0d6e153
fix: update code for rand 0.10, rcgen 0.14, and tungstenite 0.29 API …
Jaro-c May 18, 2026
aa4f457
style: apply rustfmt to config.rs and pki.rs
Jaro-c May 18, 2026
ce92b25
Merge remote-tracking branch 'origin/develop' into develop
Jaro-c May 19, 2026
346e10d
fix: sync release-dashboard.yml with main (remove duplicate jobs from…
Jaro-c May 19, 2026
5275c36
fix(dashboard): resolve installation failures for 1.4.1 release
Jaro-c May 19, 2026
0720ed5
fix(lint): pass RELEASE_VERIFY_KEY to Python as argv instead of hardc…
Jaro-c May 19, 2026
b57419b
feat(dashboard): add nginx TLS termination on port 19443
Jaro-c May 19, 2026
8b6de28
fix(install): complete install/update scripts for dashboard and agent
Jaro-c May 20, 2026
abb74bd
fix(install): robust cleanup, nginx direct run, PSK idempotency in se…
Jaro-c May 20, 2026
ff9d7e0
fix(agent): resolve multiple bugs in setup-agent.sh found during VM t…
Jaro-c May 20, 2026
1b25d17
fix(install): containers auto-start after reboot + persist postgres i…
Jaro-c May 20, 2026
09937a4
fix(nftables): use per-source-IP SSH rate limit to prevent self-lockout
Jaro-c May 20, 2026
a15c3d7
fix(nftables): resolve dual-table conflict causing complete host lockout
Jaro-c May 20, 2026
67b101d
fix(nftables): allow new connections to Netavark container subnets in…
Jaro-c May 20, 2026
bfa5dbf
fix(dashboard): add writable named volume for Next.js cache
Jaro-c May 20, 2026
fed089e
fix(dashboard): force podman-compose project name to lynx-dashboard
Jaro-c May 20, 2026
54245c2
fix(nftables): use 'destroy table' instead of 'flush table' for idemp…
Jaro-c May 20, 2026
f98092b
fix(nftables): allow outbound container traffic in lynx-forward on da…
Jaro-c May 20, 2026
14038e7
fix(dashboard): resync postgres password and fix nginx DNS cache afte…
Jaro-c May 20, 2026
d656a6a
fix(dashboard): wait 15s before pg-pass resync to let startup rotatio…
Jaro-c May 20, 2026
0d1da69
fix(install): flush iptables-nft rules after removing incompatible so…
Jaro-c May 20, 2026
8014d9e
fix(install): suppress BASH_SOURCE[0] unbound variable when script pi…
Jaro-c May 20, 2026
c2e0ca7
fix(dashboard): remove frontend_next_cache volume from compose
Jaro-c May 20, 2026
d9c9cde
fix(dashboard): remove :ro from frontend volume mount
Jaro-c May 20, 2026
be48497
fix(agent-install): fix existing-install detection and BASH_SOURCE[0]…
Jaro-c May 20, 2026
b74d16e
fix(agent): iptables false positive + service must run as root
Jaro-c May 20, 2026
857f28f
fix(agent): use Restart=always so binary-swap exit triggers systemd r…
Jaro-c May 20, 2026
61a236c
fix(ci): bump Cargo.toml version from release tag before building
Jaro-c May 20, 2026
6663f14
chore(agent): bump Cargo.toml version to 1.2.0
Jaro-c May 20, 2026
7408a5d
fix(agent): skip fallback updater when DASHBOARD_URL is not configured
Jaro-c May 20, 2026
2461956
fix(agent): prompt for dashboard-assigned WireGuard IP instead of har…
Jaro-c May 20, 2026
bf54ed7
fix(install): add iptables dep and fix agent WireGuard /32 address
Jaro-c May 20, 2026
82a2b24
fix(install): use graceful stop+rm to preserve netavark iptables cleanup
Jaro-c May 20, 2026
8743192
chore(agent): bump version to 1.2.1
Jaro-c May 20, 2026
807c1d4
fix(agent): apply cargo fmt to pass CI format check
Jaro-c May 20, 2026
a6472d5
fix(agent): allow container network forwarding on all agents
Jaro-c May 20, 2026
80573a5
fix(agent): use direct container IP for PostgreSQL DATABASE_URL
Jaro-c May 20, 2026
efed21b
fix(agent): persist agent-id file when reusing saved ID on reinstall
Jaro-c May 20, 2026
6fd85f0
chore(agent): bump version to 1.2.2
Jaro-c May 20, 2026
bb58701
chore: update Cargo.lock for lynx-agent 1.2.2
Jaro-c May 20, 2026
169aa17
fix(lint): resolve shellcheck SC2034/SC2015 warnings in install scripts
Jaro-c May 20, 2026
37c8cbb
fix(agent): store per-chain checksums for accurate nftables divergenc…
Jaro-c May 20, 2026
27fd170
fix(agent): lockdown immediately when PostgreSQL becomes unreachable
Jaro-c May 20, 2026
9bf4b52
fix(agent): use sqlx runtime query for DB health check to avoid cache…
Jaro-c May 20, 2026
e58ebf4
fix(agent): use fixed subnet and static IP for PostgreSQL container
Jaro-c May 20, 2026
ad5f6b1
fix(agent): fix tenant user creation and subuid range allocation
Jaro-c May 20, 2026
01166ff
fix(agent): add missing rootless Podman deps and neutral cwd for runuser
Jaro-c May 20, 2026
fcbafa4
fix(agent): add container startup recovery for reboot survival
Jaro-c May 20, 2026
5fd0697
fix(dashboard): add security headers to Next.js frontend responses
Jaro-c May 20, 2026
e39670a
fix(dashboard): enforce force_password_change in /auth/me handler
Jaro-c May 20, 2026
63207d7
fix(agent): validate tenant_id/project_id/name to prevent path traversal
Jaro-c May 20, 2026
bb1c1c5
fix(agent): audit log hash chain — include all fields + verify on write
Jaro-c May 20, 2026
4104495
chore(agent): update sqlx cache for audit log integrity query
Jaro-c May 20, 2026
3548b17
chore(dashboard): update sqlx cache for force_password_change in /aut…
Jaro-c May 20, 2026
9784a72
fix(agent): use host-mapped port 127.0.0.1:5434 for DATABASE_URL
Jaro-c May 20, 2026
d0e15d8
fix(agent): suppress clippy too_many_arguments on private hash function
Jaro-c May 20, 2026
3b72da5
fix(tests): add confirm_password to registerSchema test fixtures
Jaro-c May 20, 2026
7ce2460
fix(e2e): use #password locator to avoid strict mode violation
Jaro-c May 20, 2026
7f5602a
fix(dashboard): fix FK violation in agent registration IP allocation
Jaro-c May 20, 2026
436ec20
fix(auth): enforce IP+UA check in /auth/me and /auth/change-password
Jaro-c May 20, 2026
eaea205
fix(dashboard): replace podman CLI with socket API, fix WG IP CIDR su…
Jaro-c May 20, 2026
c57ce0e
fix(dashboard): expose backend port 8080, add persistent X.509 mTLS CA
Jaro-c May 20, 2026
2760dda
fix: three bugs found during scenario 2.1/3.x VM testing
Jaro-c May 20, 2026
84451f6
fix(heartbeat): use semver comparison to prevent agent downgrade
Jaro-c May 20, 2026
6c8a5e4
fix(agent): send heartbeat ACK via WS to prevent lockdown
Jaro-c May 20, 2026
2846455
fix(ws_hub): replace push_command with direct mpsc send for heartbeat…
Jaro-c May 20, 2026
7834ba1
fix(audit): use \"genesis\" sentinel for empty audit chain
Jaro-c May 20, 2026
ac57f7b
fix(nftables): suppress dynamic meter elements from divergence checksum
Jaro-c May 20, 2026
3fe4464
fix(auth): inject TCP peer address as X-Peer-Addr for IP extraction
Jaro-c May 20, 2026
b6a62c2
fix(wg): delegate WireGuard peer ops from backend to local agent via …
Jaro-c May 20, 2026
686098e
fix(dashboard): fire heartbeat_lost alert on unexpected WS disconnect
Jaro-c May 20, 2026
f6f2ff3
fix(dashboard): use https for agent API calls and add graceful shutdown
Jaro-c May 20, 2026
33067d2
fix(dashboard): delay first heartbeat poll to avoid spurious heartbea…
Jaro-c May 20, 2026
2049ae0
fix(dashboard): guard WS cleanup with ptr_eq to prevent stale session…
Jaro-c May 22, 2026
76d0327
fix(dashboard): restore WireGuard peers when local agent reconnects
Jaro-c May 22, 2026
fe26687
fix(dashboard): use destructive permission for vps.reboot, not invali…
Jaro-c May 22, 2026
9289836
fix(dashboard): bind backend 8080 to WG interface only, not 0.0.0.0
Jaro-c May 22, 2026
6c5b099
feat: drop podman-compose + iptables, ship lynx-compose and netavark-…
Jaro-c May 22, 2026
7d43cae
feat: drop openssl host dependency, generate all crypto via Lynx bina…
Jaro-c May 22, 2026
5d9a3b9
fix(install): drop python3-pip, install python3-cryptography via apt
Jaro-c May 22, 2026
2390864
style: cargo fmt across workspace
Jaro-c May 22, 2026
b6c69c5
fix(clippy): replace map_or(true, ...) with is_none_or and extract me…
Jaro-c May 22, 2026
732b302
fix(tests): replay login IP for /auth/me in admin test helper
Jaro-c May 22, 2026
ab14e33
fix(tests): replay session IP on /auth/me across auth.rs tests
Jaro-c May 22, 2026
50410ff
fix(tests): replay session IP on /auth/change-password too
Jaro-c May 22, 2026
3c10693
feat(security): provision DASHBOARD_VERIFY_KEY + CSWSH/SSRF/replay te…
Jaro-c May 22, 2026
ca91107
merge: sync main into develop (clears PR #29 conflicts)
Jaro-c May 22, 2026
bc6e2f6
ci: also run agent/dashboard-server/dashboard-ui/lint-shell on PRs to…
Jaro-c May 22, 2026
7975f3f
chore(dashboard): refresh sqlx cache after merge
Jaro-c May 22, 2026
559784b
fix(ci): suppress audit-urls.py false positives on SSRF test fixtures
Jaro-c May 22, 2026
12bcd9b
fix(fmt): hoist SSRF test URL fixtures to constants
Jaro-c May 22, 2026
f66210c
ci(agent): serialize bin tests with --test-threads=1
Jaro-c May 22, 2026
5acd575
ci(release-agent): match dev workflow — --test-threads=1 for bin tests
Jaro-c May 22, 2026
f9cb0cd
fix(install): embed docker-compose.yml and init SQL in setup-dashboar…
Jaro-c May 29, 2026
bd2b635
fix(install): do not remove iptables package — netavark requires it
Jaro-c May 29, 2026
9e53d75
fix(install): use secret files instead of Podman secrets
Jaro-c May 29, 2026
2a8eac3
fix(install): set up WireGuard before backend start
Jaro-c May 29, 2026
084065b
fix(install): remove stale postgres_data volume before starting postgres
Jaro-c May 29, 2026
217ed5e
fix(install): stop stray containers unconditionally at startup
Jaro-c May 29, 2026
0b4988f
fix(install): embed pg password in init SQL instead of cat in container
Jaro-c May 29, 2026
81781af
fix(install): force remove postgres_data volume before starting postgres
Jaro-c May 29, 2026
f01204a
fix(install): replace initdb.d with direct psql init — idempotent on …
Jaro-c May 29, 2026
dbda9a2
merge: sync main into develop — resolve iptables + secrets conflicts
Jaro-c May 29, 2026
c62c206
fix(install): don't remove iptables + add --no-recreate to lynx-compo…
Jaro-c May 29, 2026
cdb4cb1
merge: sync main into develop (dashboard@1.4.5 squash)
Jaro-c May 29, 2026
d2362b1
fix(install): nginx upstream uses container name lynx-dashboard-frontend
Jaro-c May 29, 2026
497a55a
fix(agent-install): prompt for dashboard credentials before package i…
Jaro-c May 29, 2026
df22348
fix(agent-install): preserve dashboard files during agent cleanup
Jaro-c May 29, 2026
d6d517d
fix(agent-install): collect credentials as first interactive step
Jaro-c May 29, 2026
022a91f
fix(agent-install): auto-configure dashboard WG peer for local agent
Jaro-c May 29, 2026
fd3a8f7
fix(shellcheck): replace A && B || C with if/else in agent install
Jaro-c May 29, 2026
b59e8f5
Merge remote-tracking branch 'origin/main' into develop
Jaro-c May 29, 2026
968d147
fix(install): fix container DNS on dashboard reinstall
Jaro-c May 29, 2026
fd944a0
fix(shellcheck): quote nft chain arguments (SC2086)
Jaro-c May 29, 2026
c912a4e
fix(agent+dashboard): fix DB connection, nftables management plane, r…
Jaro-c May 29, 2026
2e67d77
fix(install): static container IPs and bootstrap nftables alignment
Jaro-c May 29, 2026
52457db
fix(ci): cargo fmt rotation.rs + remove unused shellcheck variables
Jaro-c May 29, 2026
8b018d6
fix(test): update management plane IP test for conditional rendering
Jaro-c May 29, 2026
6717325
merge: sync main into develop (PR #51 squash)
Jaro-c May 29, 2026
00f0912
feat(install): pg_tde AES-256 encryption, lynx-kek+wg-psk credentials…
Jaro-c May 30, 2026
de99deb
fix(install): Percona postgres secret perms + semver release sorting
Jaro-c May 30, 2026
509d77a
fix(dashboard): scheduler semver-max, startup rotation, Podman API pa…
Jaro-c May 30, 2026
6d3bfe8
Merge remote-tracking branch 'origin/main' into develop
Jaro-c May 30, 2026
2063e6b
chore(dashboard): update sqlx cache with standard postgres (not pg_tde)
Jaro-c May 30, 2026
43d9355
Merge remote-tracking branch 'origin/main' into develop
Jaro-c May 30, 2026
aab67eb
fix: §3.x/§9.x/§12.17/§15.x test findings — connectivity and credenti…
Jaro-c May 30, 2026
f7d7bcb
fix(dashboard): PSK rotation via WS + ws_hub command_response passthr…
Jaro-c May 30, 2026
642a100
style: cargo fmt rotation.rs
Jaro-c Jun 1, 2026
8a26273
chore(dashboard): refresh sqlx cache for standard postgres (CI fix fo…
Jaro-c Jun 1, 2026
0f2adc3
Merge branch 'main' into develop
Jaro-c Jun 1, 2026
cbd6c57
chore(dashboard): fix server-specific sqlx cache (force-non-null colu…
Jaro-c Jun 1, 2026
ae9a06b
Merge branch 'main' into develop
Jaro-c Jun 1, 2026
707d6ba
fix(agent): bypass timestamp check for heartbeat_ack to prevent lockd…
Jaro-c Jun 1, 2026
d597e46
fix(agent): update timestamp tests to use non-heartbeat_ack command type
Jaro-c Jun 1, 2026
e454ede
style: cargo fmt agent auth/mod.rs
Jaro-c Jun 1, 2026
c5f4ade
Merge branch 'main' into develop
Jaro-c Jun 1, 2026
54d9f5b
fix(agent): restrict WireGuard inbound to dashboard IP on remote agents
Jaro-c Jun 2, 2026
6a51a9c
style: cargo fmt nftables/mod.rs
Jaro-c Jun 2, 2026
1039402
Merge branch 'main' into develop
Jaro-c Jun 2, 2026
00b5bac
feat(agent): add .prev backup before binary swap and startup health g…
Jaro-c Jun 2, 2026
89169f1
fix(agent): add LockdownReason — only heartbeat_ack clears heartbeat …
Jaro-c Jun 2, 2026
23e45d4
style: cargo fmt agent main.rs
Jaro-c Jun 2, 2026
144f26e
Merge branch 'main' into develop
Jaro-c Jun 2, 2026
cdd30fb
chore(deps-dev): bump @types/node (#60)
dependabot[bot] Jun 2, 2026
b5bd0dd
chore(deps): bump hyper from 1.9.0 to 1.10.1 in /lynx (#59)
dependabot[bot] Jun 2, 2026
a664183
fix(agent): use atomic rename in health guard .prev restore to avoid …
Jaro-c Jun 2, 2026
9b739bc
Merge branch 'main' into develop
Jaro-c Jun 2, 2026
6421ffc
chore(deps): bump uuid from 1.23.1 to 1.23.2 in /lynx (#44)
dependabot[bot] Jun 2, 2026
a27452c
chore(deps): bump lucide-react in /lynx/dashboard/ui (#43)
dependabot[bot] Jun 2, 2026
7e101b6
chore(deps): bump redis from 1.2.1 to 1.2.2 in /lynx (#42)
dependabot[bot] Jun 2, 2026
1633460
chore(deps-dev): bump @biomejs/biome in /lynx/dashboard/ui (#41)
dependabot[bot] Jun 2, 2026
20e4d76
chore(deps): bump axum-test from 20.0.0 to 20.1.0 in /lynx (#39)
dependabot[bot] Jun 2, 2026
a48fb8c
chore(deps): bump shadcn from 4.7.0 to 4.8.0 in /lynx/dashboard/ui (#38)
dependabot[bot] Jun 2, 2026
6ede0cb
chore(deps): bump date-fns from 4.1.0 to 4.3.0 in /lynx/dashboard/ui …
dependabot[bot] Jun 2, 2026
975b221
chore(deps): bump react-resizable-panels in /lynx/dashboard/ui (#36)
dependabot[bot] Jun 2, 2026
a29d658
chore(deps): bump @hookform/resolvers in /lynx/dashboard/ui (#35)
dependabot[bot] Jun 2, 2026
1ed6d3d
chore(deps): bump serde_json from 1.0.149 to 1.0.150 in /lynx (#33)
dependabot[bot] Jun 2, 2026
ee28ae4
chore(deps-dev): bump vitest from 4.1.6 to 4.1.7 in /lynx/dashboard/u…
dependabot[bot] Jun 2, 2026
37ef823
chore(deps): bump @base-ui/react in /lynx/dashboard/ui (#31)
dependabot[bot] Jun 2, 2026
87cc31e
chore(deps): bump react-hook-form in /lynx/dashboard/ui (#10)
dependabot[bot] Jun 2, 2026
78d9f8d
fix(dashboard): use "write" permission in nftables push commands
Jaro-c Jun 2, 2026
00de8a6
Merge remote-tracking branch 'origin/main' into develop
Jaro-c Jun 2, 2026
9dcb2ea
fix(agent): update database-url credential file on db.rotate_password
Jaro-c Jun 2, 2026
7fd97fe
Merge remote-tracking branch 'origin/main' into develop
Jaro-c Jun 2, 2026
30942c6
fix(install): replace iptables flush with nft delete table in setup s…
Jaro-c Jun 2, 2026
d6c0bf0
fix(install): detect existing agent installation before prompting for…
Jaro-c Jun 2, 2026
358b419
test(install): add env var overrides for local test environments
Jaro-c Jun 2, 2026
04f70a7
fix(install): quote $_nft_table variable in nft delete table commands
Jaro-c Jun 2, 2026
69fc553
fix(install): quote $_nft_table in scripts/remove-firewall.sh
Jaro-c Jun 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 107 additions & 74 deletions lynx/agent/setup-agent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,61 @@ if [[ -z "$FREE_DISK_MB" ]] || [[ "$FREE_DISK_MB" -lt 2048 ]]; then
fi
log_ok "Disk: ${FREE_DISK_MB} MB free on / (minimum 2048 MB satisfied)"

# --- Detect existing installation -------------------------------------------

log_section "Checking for existing installation"

existing=false
# Check for agent-specific markers only — /etc/lynx is shared with the dashboard
# on VPSes that host both. /etc/lynx alone does not mean the agent is installed.
if id "$LYNX_AGENT_USER" &>/dev/null || \
systemctl list-unit-files lynx-agent.service 2>/dev/null | grep -q lynx-agent || \
[[ -f "$AGENT_CONF" ]] || podman container exists "$PG_CONTAINER" 2>/dev/null; then
existing=true
fi

if $existing; then
log_warn "Existing agent installation detected."
echo ""
echo -e " ${BOLD}1)${RESET} Abort (default)"
echo -e " ${BOLD}2)${RESET} Update → updates binary, preserves all data"
echo -e " ${BOLD}3)${RESET} Reinstall clean → destroys all agent data"
echo ""
read -rp "Choice [1/2/3]: " choice
choice="${choice:-1}"

case "$choice" in
2)
log_info "Redirecting to update..."
exec "$(dirname "${BASH_SOURCE[0]:-}")/update-agent.sh"
;;
3)
echo ""
log_warn "This will permanently destroy all agent data on this machine."
read -rp "Type 'reinstall lynx-agent' to confirm: " confirm
if [[ "$confirm" != "reinstall lynx-agent" ]]; then
log_error "Confirmation phrase mismatch. Aborting."
exit 1
fi
# Preserve agent ID across reinstalls — dashboard still has the old one registered
_SAVED_AGENT_ID=""
if [[ -f "$LYNX_DIR/agent-id" ]]; then
_SAVED_AGENT_ID=$(cat "$LYNX_DIR/agent-id")
log_info "Preserving Agent ID for reinstall: $_SAVED_AGENT_ID"
fi
_cleanup_existing
;;
*)
log_info "Aborting. No changes made."
exit 0
;;
esac
fi

# --- Collect dashboard bootstrap data ---------------------------------------
# Prompt FIRST — before anything that may consume stdin (systemctl, userdel,
# package installs with debconf Teletype fallback, podman, wg-quick, etc.).
# Prompt after existing-installation decision — before anything that may consume
# stdin (systemctl, userdel, package installs with debconf Teletype fallback,
# podman, wg-quick, etc.).
# Dashboard-sign-pubkey is auto-detected here; it is preserved across agent
# reinstall by _cleanup_existing so the local-agent default still works.

Expand Down Expand Up @@ -279,72 +331,37 @@ _check_remove ufw "$_REASON_FW"
# software that *manages* iptables rules (Docker, ufw, firewalld), not the binary.

if $_incompatible_found; then
if command -v iptables-legacy &>/dev/null; then
iptables-legacy -F 2>/dev/null || true
iptables-legacy -X 2>/dev/null || true
iptables-legacy -t nat -F 2>/dev/null || true
iptables-legacy -t nat -X 2>/dev/null || true
iptables-legacy -t mangle -F 2>/dev/null || true
iptables-legacy -t mangle -X 2>/dev/null || true
fi
# Delete all nftables tables created by Docker / ufw / iptables-nft.
# On Ubuntu 24.04+, iptables is iptables-nft — its tables live in nftables
# ip/ip6 families. Delete them all so only table inet lynx-agent remains.
for _nft_table in \
"ip filter" "ip nat" "ip mangle" "ip raw" "ip security" \
"ip6 filter" "ip6 nat" "ip6 mangle" "ip6 raw" "ip6 security" \
"bridge filter" "arp filter"; do
nft delete table "$_nft_table" 2>/dev/null || true
done
# Legacy iptables kernel module cleanup — only present on older distros,
# not on Ubuntu 24.04+. nft cannot reach legacy xtables tables.
for _ipt in iptables-legacy ip6tables-legacy; do
if command -v "$_ipt" &>/dev/null; then
"$_ipt" -P INPUT ACCEPT 2>/dev/null || true
"$_ipt" -P FORWARD ACCEPT 2>/dev/null || true
"$_ipt" -P OUTPUT ACCEPT 2>/dev/null || true
"$_ipt" -F 2>/dev/null || true
"$_ipt" -X 2>/dev/null || true
"$_ipt" -t nat -F 2>/dev/null || true
"$_ipt" -t nat -X 2>/dev/null || true
"$_ipt" -t mangle -F 2>/dev/null || true
"$_ipt" -t mangle -X 2>/dev/null || true
fi
done
log_ok "Incompatible software removed — residual firewall rules cleared"
else
log_ok "No incompatible software found"
fi

unset _REASON_DOCKER _REASON_CTR _REASON_FW

# --- Detect existing installation -------------------------------------------

log_section "Checking for existing installation"

existing=false
# Check for agent-specific markers only — /etc/lynx is shared with the dashboard
# on VPSes that host both. /etc/lynx alone does not mean the agent is installed.
if id "$LYNX_AGENT_USER" &>/dev/null || \
systemctl list-unit-files lynx-agent.service 2>/dev/null | grep -q lynx-agent || \
[[ -f "$AGENT_CONF" ]] || podman container exists "$PG_CONTAINER" 2>/dev/null; then
existing=true
fi

if $existing; then
log_warn "Existing agent installation detected."
echo ""
echo -e " ${BOLD}1)${RESET} Abort (default)"
echo -e " ${BOLD}2)${RESET} Update → updates binary, preserves all data"
echo -e " ${BOLD}3)${RESET} Reinstall clean → destroys all agent data"
echo ""
read -rp "Choice [1/2/3]: " choice
choice="${choice:-1}"

case "$choice" in
2)
log_info "Redirecting to update..."
exec "$(dirname "${BASH_SOURCE[0]:-}")/update-agent.sh"
;;
3)
echo ""
log_warn "This will permanently destroy all agent data on this machine."
read -rp "Type 'reinstall lynx-agent' to confirm: " confirm
if [[ "$confirm" != "reinstall lynx-agent" ]]; then
log_error "Confirmation phrase mismatch. Aborting."
exit 1
fi
# Preserve agent ID across reinstalls — dashboard still has the old one registered
_SAVED_AGENT_ID=""
if [[ -f "$LYNX_DIR/agent-id" ]]; then
_SAVED_AGENT_ID=$(cat "$LYNX_DIR/agent-id")
log_info "Preserving Agent ID for reinstall: $_SAVED_AGENT_ID"
fi
_cleanup_existing
;;
*)
log_info "Aborting. No changes made."
exit 0
;;
esac
fi

# --- DNS preflight check ----------------------------------------------------

log_section "Checking network connectivity"
Expand Down Expand Up @@ -591,10 +608,16 @@ if ! python3 -c "from cryptography.hazmat.primitives.asymmetric.ed25519 import E
}
fi

log_info "Fetching latest agent release..."
LATEST_AGENT_TAG=$(curl -fsSL \
"https://api.github.com/repos/${GITHUB_REPO}/releases" \
| python3 -c "
if [[ -n "${LYNX_RELEASE_BASE:-}" ]]; then
# Local/test override — skip GitHub API fetch; binary served from LYNX_RELEASE_BASE.
RELEASE_BASE="${LYNX_RELEASE_BASE}"
LATEST_AGENT_TAG="local"
log_info "Using local release base: ${RELEASE_BASE}"
else
log_info "Fetching latest agent release..."
LATEST_AGENT_TAG=$(curl -fsSL \
"https://api.github.com/repos/${GITHUB_REPO}/releases" \
| python3 -c "
import sys, json
releases = json.load(sys.stdin)
tags = [r['tag_name'] for r in releases
Expand All @@ -605,15 +628,13 @@ if tags:
print(max(tags, key=ver))
" 2>/dev/null)

if [[ -z "$LATEST_AGENT_TAG" ]]; then
log_error "No agent release found in ${GITHUB_REPO}"
exit 1
if [[ -z "$LATEST_AGENT_TAG" ]]; then
log_error "No agent release found in ${GITHUB_REPO}"
exit 1
fi
log_ok "Latest release: ${LATEST_AGENT_TAG}"
RELEASE_BASE="https://github.com/${GITHUB_REPO}/releases/download/${LATEST_AGENT_TAG}"
fi
log_ok "Latest release: ${LATEST_AGENT_TAG}"

# LYNX_RELEASE_BASE lets local-host testing point binary downloads at a private
# HTTP server. Production installs use the canonical GitHub release URL.
RELEASE_BASE="${LYNX_RELEASE_BASE:-https://github.com/${GITHUB_REPO}/releases/download/${LATEST_AGENT_TAG}}"
mkdir -p "$BIN_DIR"
chmod 755 "$BIN_DIR"

Expand Down Expand Up @@ -666,6 +687,11 @@ if [[ -n "${_SAVED_AGENT_ID:-}" ]]; then
AGENT_ID="$_SAVED_AGENT_ID"
log_ok "Reusing existing Agent ID: $AGENT_ID"
unset _SAVED_AGENT_ID
elif [[ -n "${LYNX_AGENT_ID:-}" ]]; then
# Test/pre-seeded agent ID (allows registering in dashboard before running script).
AGENT_ID="${LYNX_AGENT_ID}"
unset LYNX_AGENT_ID
log_ok "Using pre-seeded Agent ID: $AGENT_ID"
else
AGENT_ID=$("$BINARY_PATH" gen-uuid-v7)
fi
Expand Down Expand Up @@ -974,7 +1000,14 @@ log_ok "Services installed: lynx-agent-postgres.service, lynx-agent.service"
log_section "Configuring WireGuard tunnel (agent ↔ dashboard)"

# Generate agent keypair
AGENT_PRIV=$(wg genkey)
# LYNX_WG_PRIVKEY allows test environments to pre-seed the WG private key so the
# pubkey can be registered in the dashboard before the script runs.
if [[ -n "${LYNX_WG_PRIVKEY:-}" ]]; then
AGENT_PRIV="${LYNX_WG_PRIVKEY}"
unset LYNX_WG_PRIVKEY
else
AGENT_PRIV=$(wg genkey)
fi
AGENT_PUB=$(printf '%s' "$AGENT_PRIV" | wg pubkey)
log_info "Agent WireGuard public key: ${AGENT_PUB}"
log_info " Register this VPS in the dashboard with the above public key"
Expand Down
25 changes: 14 additions & 11 deletions lynx/dashboard/setup-dashboard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,22 @@ _check_remove ufw "$_REASON_FW"
# software that *manages* iptables rules (Docker, ufw, firewalld), not the binary.

if $_incompatible_found; then
# Flush residual kernel rules left behind by Docker / ufw / iptables.
# On Ubuntu 24.04+, 'iptables' is iptables-nft and flushes nftables ip/ip6
# filter tables (the ones ufw and Docker create). Also flush iptables-legacy
# if present (older distros or explicitly installed).
for _ipt in iptables ip6tables iptables-legacy ip6tables-legacy; do
# Delete all nftables tables created by Docker / ufw / iptables-nft.
# On Ubuntu 24.04+, iptables is iptables-nft — its tables live in nftables
# ip/ip6 families. Delete them all so only table inet lynx-agent remains.
for _nft_table in \
"ip filter" "ip nat" "ip mangle" "ip raw" "ip security" \
"ip6 filter" "ip6 nat" "ip6 mangle" "ip6 raw" "ip6 security" \
"bridge filter" "arp filter"; do
nft delete table "$_nft_table" 2>/dev/null || true
done
# Legacy iptables kernel module cleanup — only present on older distros,
# not on Ubuntu 24.04+. nft cannot reach legacy xtables tables.
for _ipt in iptables-legacy ip6tables-legacy; do
if command -v "$_ipt" &>/dev/null; then
"$_ipt" -P INPUT ACCEPT 2>/dev/null || true
"$_ipt" -P INPUT ACCEPT 2>/dev/null || true
"$_ipt" -P FORWARD ACCEPT 2>/dev/null || true
"$_ipt" -P OUTPUT ACCEPT 2>/dev/null || true
"$_ipt" -P OUTPUT ACCEPT 2>/dev/null || true
"$_ipt" -F 2>/dev/null || true
"$_ipt" -X 2>/dev/null || true
"$_ipt" -t nat -F 2>/dev/null || true
Expand All @@ -286,10 +293,6 @@ if $_incompatible_found; then
"$_ipt" -t mangle -X 2>/dev/null || true
fi
done
# Also nuke any lingering nftables filter tables (ufw/Docker on systems where
# iptables-nft maps to nft tables named 'filter').
nft delete table ip filter 2>/dev/null || true
nft delete table ip6 filter 2>/dev/null || true
log_ok "Incompatible software removed — residual firewall rules cleared"
else
log_ok "No incompatible software found"
Expand Down
33 changes: 21 additions & 12 deletions scripts/remove-firewall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,29 @@ remove_firewall() {
;;
esac

# Flush all iptables rules from memory
for table in filter nat mangle raw security; do
iptables -t "$table" -F 2>/dev/null || true
iptables -t "$table" -X 2>/dev/null || true
iptables -t "$table" -Z 2>/dev/null || true
ip6tables -t "$table" -F 2>/dev/null || true
ip6tables -t "$table" -X 2>/dev/null || true
ip6tables -t "$table" -Z 2>/dev/null || true
# Delete all nftables tables created by ufw / iptables-nft (ip/ip6 families).
# On Ubuntu 24.04+, iptables is iptables-nft — its tables live in nftables.
# Delete entirely so only table inet lynx-agent remains after install.
for _nft_table in \
"ip filter" "ip nat" "ip mangle" "ip raw" "ip security" \
"ip6 filter" "ip6 nat" "ip6 mangle" "ip6 raw" "ip6 security" \
"bridge filter" "arp filter"; do
nft delete table "$_nft_table" 2>/dev/null || true
done

# Reset default policies to ACCEPT
for chain in INPUT FORWARD OUTPUT; do
iptables -P "$chain" ACCEPT 2>/dev/null || true
ip6tables -P "$chain" ACCEPT 2>/dev/null || true
# Legacy iptables kernel module cleanup — older distros only, not Ubuntu 24.04+.
for _ipt in iptables-legacy ip6tables-legacy; do
if command -v "$_ipt" &>/dev/null; then
"$_ipt" -P INPUT ACCEPT 2>/dev/null || true
"$_ipt" -P FORWARD ACCEPT 2>/dev/null || true
"$_ipt" -P OUTPUT ACCEPT 2>/dev/null || true
"$_ipt" -F 2>/dev/null || true
"$_ipt" -X 2>/dev/null || true
"$_ipt" -t nat -F 2>/dev/null || true
"$_ipt" -t nat -X 2>/dev/null || true
"$_ipt" -t mangle -F 2>/dev/null || true
"$_ipt" -t mangle -X 2>/dev/null || true
fi
done

# Remove all config files
Expand Down