Commit graph

302 commits

Author SHA1 Message Date
4573869607
Reduce log noise by changing INFO to DEBUG for routine operations
Changed the following from INFO to DEBUG level:
- Channel join messages
- Job execution start/completion messages
- SNMP operation messages (GET/WALK)
- Device health check results
- MikroTik job execution
- Device poller thread lifecycle
- Credential test messages
- OID collection results

These messages were generating excessive noise during normal operation.
Kept INFO level for:
- Initial connection establishment
- Shutdown/restart messages
- Update operations
- Errors (already at ERROR/WARN level)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-10 16:49:58 -06:00
73b75c8aa4
Disable MIB loading in forked SNMP child processes
Net-SNMP was crashing (signal 11) on every operation despite fork isolation.
The issue was that init_snmp() tries to load MIB files by default, which can
cause crashes if MIB files are missing, corrupted, or incompatible.

Since we only use numeric OIDs (e.g., "1.3.6.1.2.1.1.1.0"), we don't need
MIB name resolution at all. This change explicitly disables MIB loading in
the child process by:

1. Setting MIBS and MIBDIRS environment variables to empty
2. Calling netsnmp_set_mib_directory("") to disable MIB directory search
3. These must happen BEFORE calling init_snmp()

This should fix the persistent SIGSEGV crashes in SNMP operations.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-10 16:39:32 -06:00
7b793b315a
speed up tests: reduce SNMP timeout from 1s to 200ms 2026-02-10 16:19:54 -06:00
57ace0297b
fix clippy needless_return warning 2026-02-10 16:15:37 -06:00
55d001d9f8
Add fork()-based process isolation for SNMP operations
Each SNMP GET/WALK now runs in a forked child process. If libnetsnmp
triggers a SIGSEGV or other fatal signal, only the child dies - the
parent agent logs the crash and continues operating for all other devices.

Key changes:
- C helper: snmp_get_isolated() and snmp_walk_isolated() using
  fork+pipe pattern with 60s alarm watchdog and mutex-serialized forks
- Rust: IsolationMode enum (Fork/Direct) controlled by
  TOWEROPS_SNMP_ISOLATION env var, defaults to Fork
- New CrashRecovered error variant with signal info and logging
- Device poller logs crash recovery events at error level
- Startup logs active isolation mode

Set TOWEROPS_SNMP_ISOLATION=direct to disable isolation for debugging.
2026-02-10 16:12:41 -06:00
54d85c7c06
Remove GitLab CI configuration 2026-02-10 16:03:31 -06:00
ab0db979a1
format 2026-02-10 15:48:51 -06:00
7bffbecd21
Fix function_casts_as_integer warnings in crash handler
Cast function pointers through *const () before converting to
sighandler_t, as required by Rust 1.93+.
2026-02-10 15:44:15 -06:00
6bb33d1f8a
Update CI Rust toolchain to 1.93 to match Docker build 2026-02-10 15:30:15 -06:00
c3e411a565 Fix AMD64 FFI crash by using native AMD64 runners
The AMD64 builds were crashing due to cross-compilation. Blacksmith runners
are ARM64, so when building linux/amd64 on them, Docker uses QEMU emulation
which breaks the C FFI code in native/snmp_helper.c.

Solution:
- AMD64 builds now use GitHub's ubuntu-latest runners (native x86_64)
- ARM64 builds continue using Blacksmith ARM64 runners (native)
- Both architectures now build natively, eliminating FFI segfaults
2026-02-10 15:07:53 -06:00
29b94c09d4
Fix segfault from Alpine version mismatch, add crash handler
The builder (rust:1.93-alpine) uses Alpine 3.23 but the runtime used
Alpine 3.19. The net-snmp-dev headers from 3.23 don't match
net-snmp-libs from 3.19, causing a segfault on the first SNMP call.

Also adds a SIGSEGV/SIGBUS/SIGABRT signal handler that prints a
diagnostic message instead of silently exiting with code 139.
2026-02-10 14:14:07 -06:00
fa46d279a7
Use short SNMP timeouts in tests to avoid 90s waits
Tests against unreachable hosts (192.0.2.1) were waiting 10s * 3 attempts
per request. With cfg(test) overrides (1s timeout, 0 retries), the full
test suite runs in ~3s instead of ~90s.
2026-02-10 14:00:56 -06:00
d91effd3eb
Add integration tests for TLS CryptoProvider configuration
Verifies that ring is properly installed as the default crypto provider
and that all TLS-dependent operations work: rustls ClientConfig, reqwest
client, tokio-rustls connector, and WebSocket TLS with webpki roots.
These tests catch the CryptoProvider panic before it reaches production.
2026-02-10 13:55:32 -06:00
32a60beadb
Fix rustls CryptoProvider panic by explicitly selecting ring
Both ring and aws-lc-rs were enabled transitively (webpki-roots enables
rustls default features), causing rustls to panic at runtime because it
couldn't auto-detect which crypto provider to use.

- Add rustls as direct dep with ring only, disable default features
- Switch russh from aws-lc-rs (default) to ring crypto backend
- Call ring::default_provider().install_default() at startup
- Add macOS build fix: detect Homebrew OpenSSL path for netsnmp's libcrypto
2026-02-10 13:50:33 -06:00
28571b0243
Add webhook notification step to release workflow
After publishing a GitHub Release, notify towerops.net to trigger
mass agent updates via the new webhook endpoint.
2026-02-10 13:40:49 -06:00
9db47e22d1
Remove CAP_NET_RAW requirement from documentation
Since we now use command-line ping (setuid root) instead of surge-ping
library, the agent no longer requires CAP_NET_RAW capability.

Reverted documentation changes from previous commit that added the
capability requirement.
2026-02-10 13:27:08 -06:00
91c407ed5b
Replace surge-ping library with command-line ping
Switched from surge-ping Rust library to system ping command (iputils)
to avoid requiring CAP_NET_RAW capability for ICMP ping.

Changes:
- Use tokio::process::Command to execute /bin/ping or /bin/ping6
- Parse response time from ping output (time=X.XX ms)
- Removed surge-ping and rand dependencies from Cargo.toml
- Added tokio "process" feature

Benefits:
- No special capabilities required (ping binary has setuid root)
- Works in restricted container environments
- Already available in Docker image (iputils package)
- More portable across different container runtimes

The system ping command is already installed in the Docker image
and has setuid root permissions, allowing it to create raw sockets
without requiring the container to have CAP_NET_RAW.
2026-02-10 13:24:32 -06:00
4da510dff1
Add NET_RAW capability requirement for ICMP ping
Agent was crashing with segmentation fault (exit code 139) when
attempting ICMP ping health checks. The surge-ping library requires
CAP_NET_RAW capability to create raw sockets for ICMP.

Changes:
- Added cap_add: NET_RAW to docker-compose.example.yml
- Updated README docker-compose example
- Added troubleshooting section for exit code 139 crashes

Without this capability, the agent crashes immediately after
successful ping execution when the surge-ping library attempts
to clean up raw socket resources.
2026-02-10 13:22:37 -06:00
b0f3b57671
Add restart and self-update handlers, publish standalone binaries
Handle server-initiated restart (exit for Docker restart) and
self-update (download binary, verify SHA256, replace via exec)
commands received over the WebSocket channel. Report architecture
in heartbeat so the server can select the correct binary.

- Add restart and update handlers in websocket_client.rs
- Add self_update() with download, checksum verification, and exec
- Add arch field to AgentHeartbeat protobuf
- Add reqwest and sha2 dependencies
- chown binary in Dockerfile for non-root self-update
- Extract standalone binaries from Docker images in CI
- Create GitHub Releases with binary assets on version tags
2026-02-10 13:06:23 -06:00
0cb673d9c4
Mirror release images to Docker Hub for legacy agent updates
Agents deployed with the old Docker Hub image (gmcintire/towerops-agent)
auto-update via Watchtower. This pushes release builds to both GHCR and
Docker Hub so those agents continue receiving updates.
2026-02-10 11:28:27 -06:00
f8c8c8bb8e
Remove get_local_ip - server determines IP from WebSocket connection 2026-02-10 09:52:25 -06:00
8824eb1ddb
Remove hostname sending 2026-02-10 09:49:15 -06:00
58d53cab1e
Remove TUI feature and all related code
Drop ratatui, crossterm, hostname dependencies and the tui feature flag.
Delete src/tui/ directory, remove event bus plumbing from websocket client,
and simplify connect/init_logger to single implementations.

-1,979 lines of conditional compilation removed.
2026-02-10 09:31:39 -06:00
dd29a783db
Update all dependencies to latest compatible versions 2026-02-10 08:58:22 -06:00
ded4aba536
Add stickydisk caching for cargo git deps and Rust toolchain
- Cache ~/.cargo/git for git-sourced dependencies
- Cache ~/.rustup to avoid re-downloading toolchain each run
- Move format check before cargo check (fail fast on formatting)
2026-02-10 08:55:47 -06:00
da7d7fcba0
Update anyhow 1.0.101, clap 4.5.57, crossterm 0.29.0 2026-02-10 08:55:47 -06:00
Graham McIntire
ea372a8aa1
Merge pull request #11 from towerops-app/dependabot/cargo/regex-1.12.3
Bump regex from 1.12.2 to 1.12.3
2026-02-10 08:55:43 -06:00
dependabot[bot]
22bbf94d25
Bump regex from 1.12.2 to 1.12.3
Bumps [regex](https://github.com/rust-lang/regex) from 1.12.2 to 1.12.3.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.12.2...1.12.3)

---
updated-dependencies:
- dependency-name: regex
  dependency-version: 1.12.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-10 14:49:39 +00:00
dba1c6a79c
Fix arm64 build: use c_char instead of i8 in FFI declarations
On ARM64 Linux, c_char is u8 (unsigned), not i8 (signed).
Using the platform-correct std::ffi::c_char type fixes the
cross-compilation error.
2026-02-10 08:40:15 -06:00
7882e6b726
Fix test compilation: make device_id field public 2026-02-10 08:33:18 -06:00
2850f4f68f
Use iterator instead of index loop in walk result parsing 2026-02-10 08:31:07 -06:00
36f89ddef3
Remove dead code: unused functions, constants, methods, and enum variant 2026-02-10 08:30:11 -06:00
23ccd24210
Fix SNMP client tests for Linux CI environment
- Map "Failed to parse OID" C library errors to InvalidOid variant
- Walk to unreachable host may return empty results instead of error
  on some Linux configurations
2026-02-10 08:26:19 -06:00
62f0e1572a
Add libsnmp-dev to CI test dependencies
The test job needs net-snmp headers to compile the native snmp_helper.c
2026-02-10 08:21:38 -06:00
1192774e30
Use build timestamp version in TUI display
The TUI agent state was using CARGO_PKG_VERSION (0.1.0) instead of
version::current_version() which returns the RFC 3339 build timestamp.
2026-02-10 07:51:52 -06:00
65fbf3bb31
use datestamp instead of creating a version 2026-02-09 13:49:19 -06:00
bd08bb3aad
Remove TOWEROPS_API_URL from docker-compose example 2026-02-09 13:36:29 -06:00
45cea92f8d
Use full path to towerops-agent binary in Procfile 2026-02-09 13:32:40 -06:00
3fa0943ed0
Disable TUI in Dokku (Procfile only, docker-compose unaffected) 2026-02-09 13:31:17 -06:00
2d94f82832
Change Dockerfile to use CMD instead of ENTRYPOINT for Dokku compatibility 2026-02-09 13:29:55 -06:00
1f73dc2d46
Add Procfile with exec and DOKKU_SCALE for worker process 2026-02-09 13:29:14 -06:00
2a1bd414be
Remove Procfile - use Dockerfile ENTRYPOINT directly 2026-02-09 13:27:40 -06:00
37c4fd1d78
Fix Procfile to work with Dockerfile ENTRYPOINT 2026-02-09 13:27:08 -06:00
8ff34d08a6
Remove app.json - use Procfile only for worker process 2026-02-09 13:25:22 -06:00
3bf7d9484a
More dokku testing 2026-02-09 13:25:00 -06:00
47b6c3611b
Morke dokku testing 2026-02-09 13:24:23 -06:00
bf166a123e
add Procfile for dokku staging 2026-02-09 13:21:50 -06:00
71489a709a
copy new native code in to docker build 2026-02-09 12:46:34 -06:00
792825f682
major overhaul to use more C for snmp, may not be stable 2026-02-09 12:31:41 -06:00
cd45e9ff51
feat: add agent-side ICMP ping health monitoring
Implements health monitoring in the agent using ICMP ping, allowing
devices assigned to local (non-cloud) agents to be monitored.

Changes:
- Add surge-ping library for async ICMP ping operations
- Create ping.rs module with ping_device() function (5s timeout)
- Rename JobType::MONITOR to JobType::PING in protobuf
- Add monitoring_check channels for result communication
- Implement execute_ping_job() to handle PING jobs
- Update TUI to display ping result events

The agent now receives PING jobs from Phoenix, executes ICMP pings,
and sends MonitoringCheck results back for storage in the database.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 13:44:09 -06:00