feat(proto): credentials auth and default-password state core#471
Conversation
7e15c6d to
a83f4b4
Compare
🔐 Codex Security Review
Review SummaryOverall Risk: HIGH Findings[HIGH] DEFAULT_PASSWORD Miners Cannot Be Resolved for Password Remediation
NotesNo cryptostealing or pool-hijack behavior was found in the changed hunks. The protobuf additions use new field numbers and preserve reserved fields; generated outputs appear to be included. Generated by Codex Security Review | |
674ba1f to
9296418
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9f5d9fe814
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
a0633c2 to
4f8863d
Compare
5d857b3 to
a6cd903
Compare
Switch the proto plugin from Ed25519 key-based auth to username/password credentials (default admin/proto), bumping the driver to API version v2. The server is capability-driven, so dropping CapabilityAsymmetricAuth routes proto through the existing username/password pairing, encrypted credential persistence, and reconnect paths. Devices paired under v1 (no stored creds) fall back to the factory defaults. Default-password handling: rigs on the factory password report telemetry normally (reads are not gated), and are surfaced as a distinct DEFAULT_PASSWORD pairing state via the positive default_password_active flag on GET /api/v1/system/status — plumbed through DeviceMetrics. The telemetry poll reconciles the state with an in-memory transition guard; UpdateMinerPassword clears it back to PAIRED. The frontend shows the device's metrics alongside a "change password" remediation prompt. Phases 2 (fleet-node creds-only) and 3 (remove key-auth code/columns) follow in separate PRs; this phase leaves the dormant asymmetric-auth code in place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- telemetry: use a checked type assertion in reconcileDefaultPasswordState (golangci-lint forcetypeassert). - asicrs: set default_password_active on the DeviceMetrics initializer in device.rs (new proto field); fixes asicrs and plugin-contract builds. - command: a credential-persistence failure after an on-device password change now fails the command instead of reporting false success, so Fleet never keeps stale credentials silently. (Codex security finding) - fleetmanagement: treat DEFAULT_PASSWORD devices as paired-like for snapshot status/metadata and telemetry enrichment so their telemetry is shown. (Codex security finding) - tests: collectPairedDeviceIdentifiers covers DEFAULT_PASSWORD; update the password-persistence device-type doc test for Proto. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…egression test Address the full scope of the Codex review findings: - fleetmanagement: centralize the paired-like check (isPairedLikeStatus / isPairedLikePairingStatus) covering PAIRED and DEFAULT_PASSWORD, used by snapshot status/metadata and telemetry/group/rack enrichment. - CSV export: DEFAULT_PASSWORD devices now export their telemetry values (telemetry is not gated by the default password); only AUTHENTICATION_NEEDED blanks values. Status stays "Needs attention" and the issue column shows "Password change required". - command: add TestExecuteCommand_UpdateMinerPassword_PersistFailureFailsCommand — a regression test proving a persist failure after the on-device change fails the command instead of silently succeeding. - tests: DEFAULT_PASSWORD cases for the CSV status/issues/value helpers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address three reliability/auth review findings: - Presence-aware default-password signal: default_password_active is now an optional bool (SDK + V2 metrics carry *bool). The plugin sets it only on a successful /system/status read; the telemetry poll skips reconciliation when it is unset, so a transient probe failure or an older plugin can no longer demote a DEFAULT_PASSWORD device back to PAIRED. - Pair-time remediation state: PairDevice reports the default-password flag, and the pairer records DEFAULT_PASSWORD with a non-ACTIVE initial status immediately instead of marking a factory-password rig fully paired/active until first poll. - Reject empty pairing passwords: Client.Authenticate and the server secret-bundle creation reject empty passwords; the plugin's secret extraction no longer upgrades an explicit empty password to the factory default (nil bundle still falls back). asicrs DeviceMetrics/DeviceInfo set the optional field to None. Adds regression tests for the nil-signal, pair-time DEFAULT_PASSWORD, and empty-password paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The static sqlc queries still scoped to PAIRED + AUTHENTICATION_NEEDED, unlike the dynamic rollups, so DEFAULT_PASSWORD devices were inconsistently handled: - GetMinerStateCountsByDeviceIDs (site/building stats) + InsertMinerStateSnapshot / GetTotalMinerStateSnapshots now bucket DEFAULT_PASSWORD as broken/needs-attention, matching the dynamic CountMinersByState logic, instead of dropping it from every state bucket while still counting it present. - Reconciliation lookups (GetPairedDeviceByMACAddress / ...ByMACAddresses / ...BySerialNumber) include DEFAULT_PASSWORD, so a default-password device that moves IP/subnet is matched to its existing row on rediscovery instead of failing on a duplicate insert or splitting state. - GetAllPairedDeviceIdentifiers (telemetry poll seed) includes DEFAULT_PASSWORD, so those devices keep polling after a restart and can reconcile back to PAIRED. - GetTotalPairedDevices / GetAvailableModels / GetAvailableFirmwareVersions / GetKnownSubnets include DEFAULT_PASSWORD for fleet-view consistency. Adds integration tests for the by-device-IDs state counts and MAC reconciliation of a DEFAULT_PASSWORD device. Auth-needed-only counts and the status mutation are intentionally unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Condense the explanatory comments added across the credentials-auth work to concise WHY-only notes, matching the repo's minimal-comment style. No behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route AUTHENTICATION_NEEDED / DEFAULT_PASSWORD checks through the pairingRemediation helpers instead of inline enum comparisons, share the "Password change required" label, and drop a duplicated sleeping-state computation in StatusModal. reconcileDefaultPasswordState now writes on the first determined reading so a device whose password changed while the server was down is corrected on the next poll. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DEFAULT_PASSWORD does not suppress telemetry; only AUTHENTICATION_NEEDED does. Clarify that the two states differ in both telemetry behavior and remediation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
main merged 000087_notification_history_keyset_index and 000088_seed_notification_permissions while this branch was open, colliding on 000087. Bump to the next free version so golang-migrate stops failing on the duplicate file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4948b2b to
33f98ff
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 33f98ff719
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
🤖 Re: Codex security review: This is addressed in the stacked child PR #480 rather than in the #471 diff: #480 adds paired-like miner resolution for DEFAULT_PASSWORD devices via I am leaving #471 unchanged here so the stack stays as currently split: #471 carries the core DEFAULT_PASSWORD/auth state, and #480 carries the remediation/telemetry resolver hardening. |
Summary
Switches the proto miner plugin from Ed25519 key-based authentication to username/password credentials with factory defaults of
admin/proto. This foundation PR also introduces the server-sideDEFAULT_PASSWORDpairing state and telemetry signal so the fleet can represent a rig that is paired and reporting metrics but still needs password rotation.Stack: this PR is the base of the split stack. #480 is stacked on this PR and hardens the server-side command, telemetry, SQL, cache, plugin, and fleet-node boundaries for
DEFAULT_PASSWORD; #482 is stacked on #480 and contains the hand-written Fleet UI remediation UX; #477 is also stacked on #480 and handles phase-2 fleet-node enrollment cleanup. The diff here is relative tomain; UI affordances and normal-action gating are intentionally out of scope and live in #482. Phase 3 remains a future cleanup PR for deleting dormant key-auth code and schema.How it works
The server continues dispatching miners by
DriverName, but the proto driver now advertisesbasic_authinstead of asymmetric auth. Pairing builds aUsernamePasswordsecret, encrypts it through the existing credentials path, and reuses those credentials when reconnecting to the miner. If no credentials are supplied, the driver returns the proto defaults and the pairer tries those so factory-default rigs can be adopted without key provisioning.The proto plugin client lazily logs in to the rig REST API, caches the access token, and retries once after a
401to handle token expiry or out-of-band credential changes. The public status endpoint reportsdefault_password_active; telemetry carries that bit back onDeviceMetrics, and the server reconciles it intoPAIRING_STATUS_DEFAULT_PASSWORD. That state is paired-like for telemetry, snapshots, rollups, CSV, and enrichment, while password-change success clears it back toPAIRED.Diagrams
flowchart TD P["Pairer"] --> C{"proto driver capabilities"} C -->|"basic_auth"| S["UsernamePassword secret"] S --> E["encrypted miner_credentials"] S --> D["proto plugin client"] D --> L["POST /api/v1/auth/login"] L --> T["cached access token"] D --> R["GET /api/v1/system/status"] R --> M["DeviceMetrics.default_password_active"] M --> Q["device_pairing.DEFAULT_PASSWORD"]Areas of the code involved
plugin/proto/internal/driverbasic_authcapability, default credentials, and credential bundle validationplugin/proto/pkg/protoandplugin/proto/internal/deviceserver/sdk/v1and generated SDK outputsDeviceMetrics.default_password_activeand capability plumbingproto/fleetmanagement/v1andserver/migrations/000089_*PAIRING_STATUS_DEFAULT_PASSWORDand DB enum migrationserver/internal/domain/telemetryserver/internal/domain/commandserver/internal/domain/fleetmanagement,stores/sqlstores,buildings,sites,collectionDEFAULT_PASSWORDas paired-like for visibility/enrichment/CSVserver/fake-proto-rigandserver/docker-compose.yamlclient/src/protoFleet/api/generated/fleetmanagement/v1/fleetmanagement_pb.tsKey technical decisions & trade-offs
protodriver nameTesting & validation
cd client && npm run lintcd plugin/proto && ../../bin/go test ./...partially passed:internal/device,internal/driver,pkg/proto, andtests/unitpassed;plugin/proto/testsfailed because Docker closed the image-build connection locally.cd server && ../bin/go test ./internal/domain/plugins ./internal/domain/telemetry ./internal/domain/commandpartially passed:pluginsandtelemetrypassed; the broadercommandpackage hit local Postgres authentication failures for DB-backed tests.git diff --checkNot fully covered locally: Docker/testcontainers and DB-backed integration tests need a working local Docker/Postgres setup and should be covered by CI or a local
just devsmoke before merge.