Commit graph

259 commits

Author SHA1 Message Date
f35be66148
fix ICMP ping in Docker by trying raw sockets before unprivileged UDP
The old code only tried unprivileged UDP ICMP (udp4), which relies on
the ping_group_range sysctl — not CAP_NET_RAW. Docker doesn't set that
sysctl, so pings always timed out and fell through to exec. Now tries
raw ICMP sockets (ip4:icmp) first, which properly uses CAP_NET_RAW.
Also sets cap_net_raw+ep on the agent binary in the Dockerfile.
2026-02-12 11:18:21 -06:00
Graham McIntire
cd6fb4e79e
Merge pull request #22 from towerops-app/security-updates
Security fixes
2026-02-12 11:05:43 -06:00
0f8e8fc183
security: validate join reply before entering main event loop
Previously the agent proceeded to the main loop without confirming
the server accepted the channel join. A rejected join (e.g. invalid
token) left the agent running but silently non-functional. Now waits
for the join reply with a 10s timeout, returns error on rejection.

Reader goroutine starts before join send so the reply can be received.
Writer goroutine starts after join confirmation.
2026-02-12 11:02:38 -06:00
13e923d251
security: zero buffer pool contents before reuse
Previously, resetting slice length to 0 left previous protobuf data
(device IDs, OID values, credentials) in the backing array. Now zeros
the full capacity before returning to the pool.
2026-02-12 10:59:54 -06:00
442b019b0b
security: sanitize token from re-exec log line
os.Args logged during self-update re-exec could expose --token value
in plaintext to log aggregators. sanitizeArgs masks the value.
2026-02-12 10:59:08 -06:00
a0516c2bd1
security: use net.JoinHostPort for SSH addresses to support IPv6
fmt.Sprintf("%s:%d") produces invalid addresses for IPv6. Also adds
mockable sshDial variable for testability.
2026-02-12 10:58:28 -06:00
8b65dffc37
security: graceful shutdown on restart event
handleMessage returns bool to signal session end instead of calling
osExit directly. Defers (pool stop, writeCh close, writerWg wait)
now run before exit, preventing in-flight result loss and panics
from writes to closed channels.
2026-02-12 10:56:56 -06:00
87606bb84a
security: fixes 1-5 — payload limits, non-blocking submit, panic recovery, handshake timeout, write error propagation
Fix 1: Limit inbound job payload size to 4MB to prevent OOM from malicious server
Fix 2: Worker pool submit accepts context and returns bool, prevents blocking event loop
Fix 3: Panic recovery in worker pool goroutines prevents permanent worker death
Fix 4: WebSocket handshake timeout (30s) prevents indefinite blocking on slow server
Fix 5: Writer goroutine propagates errors to main loop via writeErrCh
2026-02-12 10:55:24 -06:00
a9990d59bc
fix staticcheck SA6002: use *[]byte in sync.Pool to avoid allocation 2026-02-12 10:37:34 -06:00
e014e9ccd4
harden security: URL sanitization, security docs, credential zeroing utility
- Mask query parameters in log output to prevent credential leakage
- Document InsecureSkipVerify rationale for MikroTik self-signed certs
- Document InsecureIgnoreHostKey rationale for SSH device connections
- Add zeroBytes utility for future byte-slice credential clearing
2026-02-12 10:35:42 -06:00
aa7ca96e95
harden security: file permissions, WebSocket accept validation, backoff jitter
- Self-update: write binary with 0700 permissions instead of 0755
- WebSocket: validate Sec-WebSocket-Accept per RFC 6455 to prevent MITM
- WebSocket: add computeAcceptKey helper with correct RFC 6455 GUID
- Agent: add nextBackoff with 25% jitter to prevent thundering herd
2026-02-12 10:33:17 -06:00
ab2c28c595
harden security: frame limits, HTTPS enforcement, checksum validation, download bounds
- WebSocket: reject frames exceeding 16 MB to prevent OOM
- MikroTik: reject words exceeding 10 MB to prevent OOM
- Self-update: require HTTPS for download URLs
- Self-update: require non-empty SHA256 checksum
- Self-update: use constant-time comparison for checksum verification
- Self-update: bound download size to 100 MB
2026-02-12 10:24:49 -06:00
7ee88dad81
remove legacy Rust build config and artifacts 2026-02-12 10:06:34 -06:00
4e6dd45415
use raw ICMP ping with exec fallback for unprivileged environments 2026-02-12 10:02:54 -06:00
94192f402a
propagate context to job executors for cancellation on disconnect 2026-02-12 09:59:45 -06:00
04d8349a6c
pool protobuf marshal buffers to reduce allocations
Use sync.Pool to reuse 4KB byte slices for protobuf marshaling via
MarshalAppend instead of allocating fresh buffers per result message.
Reduces GC pressure under high-throughput polling.
2026-02-12 09:55:38 -06:00
da8b644f40
batch SNMP results before sending over WebSocket
Accumulate SNMP results and flush in batches of 50 or every 100ms,
whichever comes first. Reduces per-message overhead when thousands
of devices produce results near-simultaneously. Also flushes
remaining results on disconnect/shutdown.
2026-02-12 09:55:08 -06:00
82f5698802
combine WebSocket frame header and payload into single write
Build the entire frame (header + mask key + masked payload) in one
buffer and write it with a single syscall instead of two separate
writes. Removes the failOnNthWrite test that tested the old
two-write implementation detail.
2026-02-12 09:54:31 -06:00
fbaf7a07dc
buffer WebSocket reads to reduce syscalls
Wrap the WebSocket connection in an 8KB bufio.Reader so that small
reads (2-byte frame headers, length extensions) are served from
buffer instead of making individual syscalls.
2026-02-12 09:53:16 -06:00
c57addc6c7
increase channel buffer sizes to handle thousands of devices
Raise write channel from 500 to 10000, SNMP/monitoring result
channels from 1000 to 10000, MikroTik/credential channels to 5000.
Prevents silent data loss when polling thousands of devices.
2026-02-12 09:50:46 -06:00
8b866ae946
add worker pool with bounded concurrency for job dispatch
Replace unbounded goroutine spawning with fixed-size worker pools:
100 workers for SNMP, 20 for MikroTik, 50 for ping. Prevents file
descriptor exhaustion and device overload when handling thousands
of concurrent jobs. Pool provides backpressure when saturated.
2026-02-12 09:50:17 -06:00
07e7ed2745
replace fmt.Sprintf with strconv in hot paths
Use strconv.FormatInt/FormatUint instead of fmt.Sprintf for integer
conversions in snmpValueToString. Use encoding/hex + strings.Builder
instead of fmt.Sprintf loop in formatHex. Use strconv.FormatUint for
WebSocket ref counter. Reduces allocations per OID significantly.
2026-02-12 09:47:42 -06:00
82df96cd59
batch SNMP GETs into chunks of 60 OIDs per PDU
Previously each OID was fetched individually, meaning 1000 OIDs
required 1000 round trips. Now OIDs are batched into groups of 60
(the SNMP max PDU size), reducing round trips by ~50x. Also
pre-sizes the OID value map to avoid repeated rehashing.
2026-02-12 09:41:48 -06:00
92f7c2ed85
fix SNMPv1 walks failing due to unsupported GETBULK
use GETNEXT-based WalkAll for v1 devices instead of BulkWalkAll
2026-02-11 13:04:32 -06:00
01cd109d10
cleanup 2026-02-11 12:42:49 -06:00
289bc3d4f1
fix docker tags by replacing colons in timestamp version
Docker tags cannot contain colons. Use a separate tag-safe format
(colons replaced with hyphens) for Docker tags while keeping the
RFC 3339 timestamp as the version baked into the binary.
2026-02-11 11:48:05 -06:00
297cb8fc91
use UTC timestamp for version instead of git describe 2026-02-11 11:44:19 -06:00
39641b73d4
enable ping in docker container for non-root user
add libcap and setcap cap_net_raw+p on /bin/ping so ICMP works
without container-level capabilities. add NET_RAW cap_add to
docker-compose.example.yml as fallback.
2026-02-11 10:58:30 -06:00
894dcb18e4
skip ping test when raw sockets unavailable 2026-02-11 10:54:15 -06:00
9611cfd620
fix missed errcheck in websocket test 2026-02-11 10:50:00 -06:00
5ad95fac42
fix errcheck lint violations in test files 2026-02-11 10:48:41 -06:00
a36a2b7fd7
increase test coverage and improve logger output 2026-02-11 10:42:19 -06:00
a757bd7615
Optimize CI caching for Blacksmith runners, fix SNMP bulk walk on Mikrotik
- Dockerfile: add BuildKit cache mounts for Go module and build caches,
  pin both stages to Alpine 3.23
- CI: replace BUILDKIT_INLINE_CACHE with registry-based layer caching
  (mode=max) for branch and release builds
- .dockerignore: exclude test files, Rust legacy, nix, and other
  non-build artifacts from Docker context
- SNMP: lower MaxRepetitions from default 50 to 10 to prevent Mikrotik
  routers from timing out during bulk walks
2026-02-11 10:31:10 -06:00
Graham McIntire
86da744e68
Merge pull request #21 from towerops-app/dependabot/github_actions/actions/checkout-6
Bump actions/checkout from 4 to 6
2026-02-11 10:20:14 -06:00
dependabot[bot]
51210a5986
Bump actions/checkout from 4 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 16:20:06 +00:00
Graham McIntire
720ccb40e2
Merge pull request #20 from towerops-app/dependabot/github_actions/actions/upload-artifact-6
Bump actions/upload-artifact from 4 to 6
2026-02-11 10:19:59 -06:00
Graham McIntire
62e31e3d05
Merge pull request #18 from towerops-app/dependabot/github_actions/golangci/golangci-lint-action-9
Bump golangci/golangci-lint-action from 7 to 9
2026-02-11 10:19:40 -06:00
Graham McIntire
91820976ae
Merge pull request #19 from towerops-app/dependabot/github_actions/actions/setup-go-6
Bump actions/setup-go from 5 to 6
2026-02-11 10:19:26 -06:00
Graham McIntire
b03edf8f9b
Merge pull request #17 from towerops-app/dependabot/github_actions/actions/download-artifact-7
Bump actions/download-artifact from 4 to 7
2026-02-11 10:19:14 -06:00
dependabot[bot]
c360705fd4
Bump actions/upload-artifact from 4 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 16:18:57 +00:00
dependabot[bot]
e8ae0769a5
Bump actions/setup-go from 5 to 6
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 16:18:53 +00:00
dependabot[bot]
a72ad8a130
Bump golangci/golangci-lint-action from 7 to 9
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 7 to 9.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v7...v9)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-version: '9'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 16:18:49 +00:00
dependabot[bot]
5757bd4a13
Bump actions/download-artifact from 4 to 7
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 16:18:45 +00:00
Graham McIntire
ee29613047
Merge pull request #16 from towerops-app/golang-rewrite
Full rewrite in golang
2026-02-11 10:18:12 -06:00
58d1af4f84
refactor some code 2026-02-11 10:17:01 -06:00
37f411ec55
cleanup 2026-02-11 10:07:27 -06:00
769ac1f623
Full rewrite in golang 2026-02-11 10:04:30 -06:00
99997e0c1f
Add OID count to SNMP job completion logs
Shows how many OID values were collected and sent back, making it
visible at info level whether SNMP walks returned data or not.
2026-02-11 09:14:36 -06:00
477b5f06cb
Promote job lifecycle logging from debug to info level
Job receipt, start, and completion messages were all at debug level,
making it impossible to tell if the agent was processing jobs without
setting LOG_LEVEL=debug.
2026-02-11 09:09:10 -06:00
81a5d26655
Cleanup from dokku testing 2026-02-11 09:01:18 -06:00