diff --git a/LICENSE b/LICENSE index 7110856..0f4536d 100644 --- a/LICENSE +++ b/LICENSE @@ -2,12 +2,12 @@ Collimo Copyright (c) 2026 COLLIMO LLC. All rights reserved. This software and its source code are proprietary and confidential. No -licence to use, copy, modify, merge, publish, distribute, sublicense or sell +license to use, copy, modify, merge, publish, distribute, sublicense or sell them, in whole or in part, is granted except by a written agreement with COLLIMO LLC. Collimo is distributed to customers as a built image. Running it is -governed by the terms of the licence under which that image was obtained. +governed by the terms of the license under which that image was obtained. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/PORTAINER.md b/PORTAINER.md index 4ed7f46..34f2743 100644 --- a/PORTAINER.md +++ b/PORTAINER.md @@ -129,7 +129,10 @@ Add these only if you need them: | `POLL_INTERVAL` | `30s` | values below `10s` are clamped | | `REQUEST_TIMEOUT` | `20s` | how long one request to a console may take | | `UNIFI_SITE` | `default` | you renamed your site | +| `COVERAGE_SKIP_TAG` | `nobackup` | a Proxmox VE or XCP-ng tag that leaves a VM out of the last-backup check | +| `COVERAGE_SKIP` | empty | VMs left out of the last-backup check by name, separated by commas; `*` matches any run of characters | | `PORT_SLEEPS` | empty | switch ports whose computer sleeps, as `mac/port` separated by commas: a drop to 100 Mb/s or lower there is expected. The slow-link alert's button writes it | +| `TOOLS_SCAN_NETWORKS` | empty | narrows where the Tools tab's port scan may go, such as `192.168.1.0/24, 10.20.0.0/16`. Empty, any private address (10/8, 172.16/12, 192.168/16); a network outside those is left out, never added | | `UNIFI_OS` | `true` | `false` for a self-hosted standalone controller | | `UNIFI_VERIFY_TLS` | `false` | you installed a trusted cert on the console | | `PROTECT_ENABLED` | `true` | `false` if you do not run Protect | @@ -300,6 +303,60 @@ has not backed up in a while is highlighted on the page (26 hours by default, under Settings → Highlighting) but not alerted on, because a guest that was deleted keeps its old group forever. +#### Veeam Backup & Replication + +B&R 12 or later, read through the REST API the backup server itself serves +on port 9419. In the Veeam console, under **Users and Roles**, add a Windows +or local account with the **Veeam Backup Viewer** role. It must not have MFA: +Veeam's REST API can't sign in an account that has it, and Veeam's own +advice is a service account without. + +``` +SOURCE_6=veeam https://veeam01.lab:9419 name=veeam1 user=LAB\collimo +SOURCE_6_SECRET= +``` + +What it reads: every job's last result and next run, every repository's +size and free space, and every machine Veeam holds backups of with its +newest restore point. Those last ones are one request per machine, so they +are read once an hour. The Viewer role can't read the server's version, so +the panel shows the newest API revision the server takes and the release it +came with. + +Alerts: a job whose last run failed is bad, one with warnings a warning +(`backup_task`); a disabled job is never judged. A repository Veeam reports +offline (`storage_offline`), or nearly full (`storage_full`). + +#### HYCU (preview) + +Read through the REST API the HYCU controller serves on port 8443. HYCU +publishes no API reference, so the fields come from its user guide; if +something reads wrong, the panel and Settings → Diagnostics show what came +back. Give an account the **Viewer** role in the **Infrastructure** group (a +Self-service group sees no targets), then make it an **API key** from the +user menu. With two-factor sign-in on, a key is the only way in. + +``` +SOURCE_7=hycu https://hycu01.lab:8443 name=hycu1 +SOURCE_7_SECRET= +``` + +Or, without a key, `user=` on the line and the password as the secret. +Alerts: a job that ended in error (`backup_task`), a policy HYCU itself +judges out of compliance (`backup_policy`), and a target that is unhealthy +or nearly full. + +#### Last backup per VM + +With any of Proxmox Backup Server, Veeam or HYCU read, the Virtualization +tab lists every VM on vCenter, Proxmox VE and XCP-ng with its last backup, +from whichever of them holds it. Older than two days warns, older than a +week or none at all is critical (`vm_backup`). A VM on a platform no backup +server Collimo reads answers for says **not checked**, never "no backup". +Left out: templates, VMs that haven't run in 30 days, VMs tagged +`COVERAGE_SKIP_TAG` (`nobackup` unless set) in Proxmox VE or XCP-ng, and VMs +named in `COVERAGE_SKIP`, such as `build01, test-*`. + #### Synology DSM In **Control Panel → User & Group**: @@ -374,7 +431,7 @@ temperature; and TrueNAS's alert list. Like vCenter, TrueNAS's own alerts are the alerts (`nas_alert`): each one not dismissed, WARNING as a warning and ERROR or worse as bad. **Dismissing it in TrueNAS clears it here.** -TrueNAS 25.04 marked the REST API this uses as deprecated, in favour of +TrueNAS 25.04 marked the REST API this uses as deprecated, in favor of JSON-RPC over a WebSocket. It still answers through 25.10; if a later release drops it, the slot will say so. @@ -553,6 +610,7 @@ threshold, with a range, and raises `capacity_runway` when that is close. | `CAPACITY_VM_SIZE` | `vcpu=4,mem=16,disk=100` | the VM "how many more fit" counts in, memory and disk in GB | | `CAPACITY_SUMMARY_TO` | empty | who gets the monthly capacity summary on the 1st, through the SMTP server alert email uses; its own list, not `SMTP_TO` | | `CAPACITY_SUMMARY_BY` | empty | a "Prepared by" name on the summary | +| `CAPACITY_CURRENCY` | USD | the currency the VMware exit report quotes in: USD, GBP, EUR, CAD, INR, BRL or AUD (its form can pick another) | | `CAPACITY_HOST` | empty | the host "What to buy" plans in, such as `cores=64,mem=1024` (memory in GB); empty: each cluster's largest | | `CAPACITY_DISK_TBW` | empty | each drive model's rated terabytes written, such as `CT1000P310SSD8=220; Samsung SSD 990 PRO 1TB=600`, for disk wear | | `CAPACITY_HA` | empty | hosts kept spare per cluster, such as `Prod=1,lab=0`: usable capacity is then the cluster less its largest hosts | @@ -711,9 +769,10 @@ it always wins, and the box on Settings is locked. | Name | Default | Meaning | |---|---|---| | `COLLIMO_ROLE` | `standalone` | `collector` at each site, `hub` for the one that shows them all. `standalone` is one site on its own | -| `HUB_URL` | empty | on a hub: the address collectors reach it at. It goes into every enrolment code | -| `HUB_ENROL` | empty | on a collector: the enrolment code made on the hub, used once | +| `HUB_URL` | empty | on a hub: the address collectors reach it at. It goes into every enrollment code | +| `HUB_ENROLL` | empty | on a collector: the enrollment code made on the hub, used once | | `HUB_LINK` | `normal` | `metered` on a cellular or satellite link: the snapshot every 5 minutes instead of every minute | +| `HUB_PROXY` | empty | on a collector: an HTTP proxy for its link to the hub alone. `http://tailscale:1055` with the Tailscale sidecar | A **collector** watches its site as a standalone install does, and sends what it sees to the hub. It only ever calls out to the hub; nothing @@ -725,13 +784,31 @@ unreachable wait on the archive volume and go when it is back. The **hub** polls nothing. It shows every site, sends the notifications for all of them, and keeps each site's archive. It needs an MSP license, or a Business license for several offices, which covers the number of sites the key says. On the hub, **Sites → Add a -site** makes a one-time enrolment code for each new site: paste it into -that site's `HUB_ENROL`. A code works once and lasts a day. Every message +site** makes a one-time enrollment code for each new site: paste it into +that site's `HUB_ENROLL`. A code works once and lasts a day. Every message between a collector and the hub is encrypted to the receiver and signed by the sender, whatever the route. On a hub, set `RETENTION=400d` to keep about 13 months of each site's graphs. The full guide is [The hub](https://docs.collimo.dev/msp/hub/). +### Tailscale + +Add `tailscale` to `COMPOSE_PROFILES` and the stack runs a Tailscale sidecar +that puts Collimo on your tailnet, with no port open on the router: + +| Name | Default | Meaning | +|---|---|---| +| `TS_AUTHKEY` | empty | an auth key from Tailscale's admin console (**Settings → Keys**). Tag it, so the device never expires. Used once, at the first start | +| `TS_HOSTNAME` | `collimo` | this stack's name on the tailnet | + +The dashboard is then at `https://..ts.net` once +HTTPS certificates are on for the tailnet (Tailscale admin → **DNS**), or at +`http://` without them. On a hub, that address is `HUB_URL`. On +a collector, set `HUB_PROXY=http://tailscale:1055` too, so its link reaches +the hub over the tailnet; nothing else Collimo talks to goes through it. The +sidecar runs in userspace: no tun device and no added capabilities. Its +login is kept in the `tailscale-state` volume. + ### Sign-in Off until you turn it on, so a dashboard behind a VPN or Cloudflare Access diff --git a/README.md b/README.md index 5b1837e..4d1feb3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ I line it up. A collimator is the part of an optical instrument that lines the l [![Release](https://img.shields.io/github/v/release/collimohq/collimo-deploy?label=release)](https://github.com/collimohq/collimo-deploy/releases) [![Image](https://img.shields.io/badge/image-ghcr.io%2Fcollimohq%2Fcollimo-blue)](https://github.com/orgs/collimohq/packages) -[![Platforms](https://img.shields.io/badge/platforms-amd64%20%7C%20arm64-lightgrey)](#what-you-need) +[![Platforms](https://img.shields.io/badge/platforms-amd64%20%7C%20arm64-lightgray)](#what-you-need) [![Docs](https://img.shields.io/badge/docs-docs.collimo.dev-informational)](https://docs.collimo.dev) [![Status](https://img.shields.io/badge/status-private%20beta-orange)](#help-and-feedback) @@ -57,7 +57,7 @@ I line it up. A collimator is the part of an optical instrument that lines the l | **Synology DSM** | Model, DSM version, CPU, memory, temperature; volumes and RAID; drives and their SMART verdicts | [Guide](https://docs.collimo.dev/connect/synology/) | | **QNAP** (SNMPv3) | CPU, memory, temperatures and fans; disks and M.2 drives; storage pools | [Guide](https://docs.collimo.dev/connect/qnap/) | | **TrueNAS** | Pools, disk read, write and checksum errors, temperatures, TrueNAS's own alerts | [Guide](https://docs.collimo.dev/connect/truenas/) | -| **Managed switches** (SNMP) | Cisco, Juniper, Aruba, MikroTik and others: ports, speeds, errors, VLANs, temperatures and fans, LLDP neighbours; on Cisco and Juniper also CPU, memory, power supplies, optics and chassis alarms | [Guide](https://docs.collimo.dev/connect/snmp-switches/) | +| **Managed switches** (SNMP) | Cisco, Juniper, Aruba, MikroTik and others: ports, speeds, errors, VLANs, temperatures and fans, LLDP neighbors; on Cisco and Juniper also CPU, memory, power supplies, optics and chassis alarms | [Guide](https://docs.collimo.dev/connect/snmp-switches/) | | **UPS** (SNMP) | On mains or battery, charge, runtime left, load, voltages, battery alarms | [Guide](https://docs.collimo.dev/connect/ups/) | | **PDUs** (SNMP) | APC, Raritan and Eaton: draw against the rating, banks, and each outlet | [Guide](https://docs.collimo.dev/connect/pdu/) | | **Servers** (Redfish) | Dell iDRAC, HPE iLO, Supermicro, Lenovo XCC: health, temperatures, fans, power supplies, drives | [Guide](https://docs.collimo.dev/connect/redfish/) | @@ -69,7 +69,7 @@ No UniFi? Collimo can watch only your servers and storage. See [Watching without - + diff --git a/docker-compose.yml b/docker-compose.yml index defa1a8..5d65934 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,7 +25,7 @@ services: # host. # Each release writes its own version and registry in as the defaults, # so a blank stack runs it. - image: ${COLLIMO_IMAGE:-ghcr.io/collimohq/collimo}:${COLLIMO_VERSION:-0.1.2} + image: ${COLLIMO_IMAGE:-ghcr.io/collimohq/collimo}:${COLLIMO_VERSION:-0.2.0} # Ask the registry on every deploy. A fixed release costs one quick # request; qa moves, and this is how a redeploy picks it up. pull_policy: always @@ -102,12 +102,20 @@ services: SOURCE_7_SECRET: ${SOURCE_7_SECRET:-} SOURCE_8: ${SOURCE_8:-} SOURCE_8_SECRET: ${SOURCE_8_SECRET:-} + # The last-backup check leaves out VMs with this Proxmox VE or XCP-ng + # tag, and VMs named in COVERAGE_SKIP (commas; * matches any run). + COVERAGE_SKIP_TAG: ${COVERAGE_SKIP_TAG:-nobackup} + COVERAGE_SKIP: ${COVERAGE_SKIP:-} # Up-checks: ping, HTTP or a TCP port for anything with an address, # entries separated by ; such as # CHECKS=ping 192.168.1.20 name=printer; http https://nas.lab:5001 name=nas CHECKS: ${CHECKS:-} + # The Tools tab's port scan reaches private addresses only; this can + # narrow it to some of them, such as 192.168.1.0/24. + TOOLS_SCAN_NETWORKS: ${TOOLS_SCAN_NETWORKS:-} + # Sign-in. Off until ADMIN_PASSWORD (free) or OIDC_* / LDAP_* (paid) # are set. See PORTAINER.md. ADMIN_USERNAME: ${ADMIN_USERNAME:-admin} @@ -142,8 +150,13 @@ services: # own. COLLIMO_ROLE: ${COLLIMO_ROLE:-standalone} HUB_URL: ${HUB_URL:-} - HUB_ENROL: ${HUB_ENROL:-} + HUB_ENROLL: ${HUB_ENROLL:-} HUB_LINK: ${HUB_LINK:-normal} + # On a collector with the Tailscale sidecar: http://tailscale:1055, + # for its link to a hub on the tailnet. Nothing else goes through it. + HUB_PROXY: ${HUB_PROXY:-} + # The Tailscale sidecar's name, only so Settings can show it. + TS_HOSTNAME: ${TS_HOSTNAME:-collimo} # Notification channels. Each is off until its variables are set; any # number can be on. Slack, Teams, PagerDuty and the webhook are paid. @@ -222,6 +235,7 @@ services: # the host "What to buy" plans in (empty: each cluster's largest). CAPACITY_SUMMARY_TO: ${CAPACITY_SUMMARY_TO:-} CAPACITY_SUMMARY_BY: ${CAPACITY_SUMMARY_BY:-} + CAPACITY_CURRENCY: ${CAPACITY_CURRENCY:-} CAPACITY_HOST: ${CAPACITY_HOST:-} # Rated endurance per drive model, for disk wear: CT1000P310SSD8=220 CAPACITY_DISK_TBW: ${CAPACITY_DISK_TBW:-} @@ -315,8 +329,10 @@ services: max-file: "3" victoria-metrics: - # Pin to a release tag once you have one you are happy with. - image: victoriametrics/victoria-metrics:latest + # Pinned, like Collimo itself, so a redeploy never pulls a release we + # haven't run. It moves forward with a Collimo release, never back: + # VictoriaMetrics supports upgrading its data, not downgrading it. + image: victoriametrics/victoria-metrics:v1.155.0 container_name: collimo-victoria-metrics restart: unless-stopped command: @@ -458,6 +474,57 @@ services: security_opt: - no-new-privileges:true + # ---- Optional: Tailscale, to reach Collimo on your tailnet ------------ + # + # Starts only with "tailscale" in COMPOSE_PROFILES, and TS_AUTHKEY set to + # an auth key from Tailscale's admin console (Settings → Keys); tag it so + # the device never expires. It joins the tailnet as TS_HOSTNAME and + # serves the dashboard at https://..ts.net, + # with no port open on the router. On a hub, that is HUB_URL. It also + # offers a proxy on :1055, which a collector's link to that hub uses + # through HUB_PROXY=http://tailscale:1055. + # + # Userspace networking: no tun device and no added capabilities. The + # dashboard is served over HTTPS when the tailnet has HTTPS certificates + # turned on (Tailscale admin → DNS), and over plain HTTP on port 80 + # otherwise, which WireGuard still encrypts. + tailscale: + profiles: ["tailscale"] + image: tailscale/tailscale:stable + container_name: collimo-tailscale + hostname: tailscale + restart: unless-stopped + environment: + # Under containerboot's other name for it, so the key is never + # passed to Collimo itself. + TS_AUTH_KEY: ${TS_AUTHKEY:-} + TS_HOSTNAME: ${TS_HOSTNAME:-collimo} + TS_STATE_DIR: /var/lib/tailscale + TS_SOCKET: /var/run/tailscale/tailscaled.sock + TS_USERSPACE: "true" + TS_OUTBOUND_HTTP_PROXY_LISTEN: ":1055" + # containerboot starts tailscaled and logs in; once it is up, the + # dashboard is served on the tailnet, set afresh on every start. Each + # try is timed: with Serve off for the tailnet, tailscale waits for + # someone to turn it on, and the proxy must not wait with it. + entrypoint: ["/bin/sh", "-c"] + command: + - | + /usr/local/bin/containerboot & + boot=$$! + until tailscale status >/dev/null 2>&1; do kill -0 $$boot 2>/dev/null || exit 1; sleep 2; done + # Status answers from the cached map before control does; HTTPS asked + # for then fails at once. Wait up to a minute for the tailnet's + # certificate domain, then serve afresh: HTTPS with it, HTTP without. + i=0; until tailscale status --json 2>/dev/null | grep -q '"CertDomains": \['; do i=$$((i+1)); [ $$i -ge 30 ] && break; sleep 2; done + tailscale serve reset >/dev/null 2>&1 + if [ $$i -lt 30 ]; then timeout 30 tailscale serve --bg --https=443 http://telemetry:8080; else echo 'collimo: HTTPS certificates are off for this tailnet (Tailscale admin -> DNS); serving plain HTTP'; false; fi || timeout 30 tailscale serve --bg --http=80 http://telemetry:8080 || echo 'collimo: could not serve on the tailnet; is Serve allowed for it?' + wait $$boot + volumes: + - tailscale-state:/var/lib/tailscale + security_opt: + - no-new-privileges:true + volumes: vm-data: ledger-data: @@ -465,3 +532,4 @@ volumes: igotify-data: apprise-config: postgres-data: + tailscale-state: diff --git a/sbom/sbom-amd64.cdx.json b/sbom/sbom-amd64.cdx.json index 16def59..1e5e36a 100644 --- a/sbom/sbom-amd64.cdx.json +++ b/sbom/sbom-amd64.cdx.json @@ -1,16 +1,16 @@ { "bomFormat": "CycloneDX", "specVersion": "1.5", - "serialNumber": "urn:uuid:4925c3f0-29b8-40dd-8df3-59663411937b", + "serialNumber": "urn:uuid:3c1f447c-d0b9-4135-a01c-1b327c493b0c", "version": 1, "metadata": { - "timestamp": "2026-10-23T17:54:20Z", + "timestamp": "2026-11-03T15:46:04Z", "component": { "type": "container", "bom-ref": "collimo-image", "name": "collimo", - "version": "0.1.2", - "purl": "pkg:oci/collimo@0.1.2?repository_url=ghcr.io/collimohq/collimo&arch=amd64", + "version": "0.2.0", + "purl": "pkg:oci/collimo@0.2.0?repository_url=ghcr.io/collimohq/collimo&arch=amd64", "properties": [{"name": "base-image", "value": "scratch"}] } }, @@ -19,9 +19,9 @@ "type": "application", "bom-ref": "collimo", "name": "collimo", - "version": "0.1.2", + "version": "0.2.0", "description": "The Collimo collector, a static linux/amd64 binary", - "purl": "pkg:generic/collimohq/collimo@0.1.2?arch=amd64" + "purl": "pkg:generic/collimohq/collimo@0.2.0?arch=amd64" }, { "type": "library", diff --git a/sbom/sbom-arm64.cdx.json b/sbom/sbom-arm64.cdx.json index ee7539d..bd56794 100644 --- a/sbom/sbom-arm64.cdx.json +++ b/sbom/sbom-arm64.cdx.json @@ -1,16 +1,16 @@ { "bomFormat": "CycloneDX", "specVersion": "1.5", - "serialNumber": "urn:uuid:7feed9ab-b3d2-441f-a3a6-dee158bf643e", + "serialNumber": "urn:uuid:9d11f5a8-2e77-4736-9048-ece6648fa4ae", "version": 1, "metadata": { - "timestamp": "2026-10-23T17:54:19Z", + "timestamp": "2026-11-03T15:46:04Z", "component": { "type": "container", "bom-ref": "collimo-image", "name": "collimo", - "version": "0.1.2", - "purl": "pkg:oci/collimo@0.1.2?repository_url=ghcr.io/collimohq/collimo&arch=arm64", + "version": "0.2.0", + "purl": "pkg:oci/collimo@0.2.0?repository_url=ghcr.io/collimohq/collimo&arch=arm64", "properties": [{"name": "base-image", "value": "scratch"}] } }, @@ -19,9 +19,9 @@ "type": "application", "bom-ref": "collimo", "name": "collimo", - "version": "0.1.2", + "version": "0.2.0", "description": "The Collimo collector, a static linux/arm64 binary", - "purl": "pkg:generic/collimohq/collimo@0.1.2?arch=arm64" + "purl": "pkg:generic/collimohq/collimo@0.2.0?arch=arm64" }, { "type": "library", diff --git a/stack.env.example b/stack.env.example index 6f8242d..a2076cd 100644 --- a/stack.env.example +++ b/stack.env.example @@ -2,7 +2,7 @@ # variable, this file's and the rest, from what the stack is running with. # Required: the release to run, such as 0.9.0, or qa for the build under test. -COLLIMO_VERSION=0.1.2 +COLLIMO_VERSION=0.2.0 # false watches only hypervisors and storage, with no UniFi console at all, # and is what to set while trying this stack beside an existing one.
A switch drawn as its front panel
The front panel. Odd ports on top, even below, SFP cages to the right, each lit for its speed. A red outline means errors climbing now; a dashed amber one, slower than that port has run.
The cable modem
The modem. Why it last re-initialised, and whether it rebooted through an outage.
The cable modem
The modem. Why it last re-initialized, and whether it rebooted through an outage.
The outage ledger
The outage ledger. Every outage, its cause, and availability with how much of the window the archive covers.