Authenticate Status Dashboard reporting with a Zitadel service identity - #28
Merged
Merged
Conversation
Aloento
force-pushed
the
feature/zitadel-service-identity
branch
from
September 23, 2026 18:50
7952165 to
cf83487
Compare
…identity Replace the self-signed HMAC JWT with a Zitadel OIDC service identity token obtained through the machine user JWT Profile flow, delegated to the community `zitadel` crate. - drop status_dashboard.secret and add oidc_issuer, oidc_key_file and oidc_scopes - load the Zitadel machine user key file once at startup, failing closed with the configuration key named when it is missing, unreadable, invalid or of an unsupported type - delegate OIDC discovery, assertion signing and the token request to zitadel::credentials, signing a fresh assertion and requesting a fresh token for every report - send the configured scopes space-joined as one scope parameter (the role scope plus the project audience scope that makes the token audience the project id) - present the assertion instead of HTTP Basic credentials and never cache a token in-process - exit non-zero when a fatal reporter error occurs - replace the jwt, hmac, sha2 and jsonwebtoken dependencies with the zitadel crate and upgrade reqwest to 0.12 so that a single HTTP/TLS stack remains - update the test suite
The default list only carried the role scope, and the audience scope cannot have a default because it carries the project id of the deployment, so a deployment that configured issuer and key file started up and then failed every report with 401. - drop the default of status_dashboard.oidc_scopes and make the key required - reject a scope list without a urn:zitadel:iam:org:project:role: scope or without a urn:zitadel:iam:org:project:id:<projectId>:aud scope in StatusDashboardConfig::oidc_identity(), naming MP_STATUS_DASHBOARD__OIDC_SCOPES and showing a correct list - cover both missing scope classes and the accepted list in the unit and integration tests
Nothing constructs or reads them since the reporter moved to the V2 incident API, and their public visibility kept the dead code lint quiet. - remove the V1 request structure from src/sd.rs and the reporter - drop the serde import the reporter only needed for it
Aloento
force-pushed
the
feature/zitadel-service-identity
branch
from
September 23, 2026 20:50
7ce4cad to
f332d07
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The Status Dashboard backend no longer accepts the local HMAC-SHA256 JWT (stackmon/StatusDashboard-Backend#18), so the reporter authenticates with a Zitadel service identity token obtained through the machine user JWT Profile flow, delegated to the
zitadelcrate.src/oidc.rs: the machine user key file is loaded once at startup (onlytype: serviceaccountis accepted); OIDC discovery, assertion signing and the token request are left tozitadel::credentials. Startup fails closed, naming the offendingMP_STATUS_DASHBOARD__OIDC_*key when a value is missing, unreadable, invalid or of an unsupported type, and the key material never reaches a log line or an error message.src/config.rs:status_dashboard.secretis replaced byoidc_issuer,oidc_key_fileandoidc_scopes, all three required. The scope list is validated at startup and must carry a project role scope (urn:zitadel:iam:org:project:role:<role>) and the project audience scope (urn:zitadel:iam:org:project:id:<projectId>:aud) that makes the token audience the project id the backend verifies.src/sd.rs:build_auth_headersis async and returnsanyhow::Result<HeaderMap>; the HMAC signing path,LocalHmacClaimsand the "no secret, report anonymously" fallback are removed.src/bin/reporter.rs: a fresh token is requested right before every authenticated call (startup component fetch, component cache refresh, incident creation) and is never kept across loop iterations, so a long-running reporter cannot keep using an expired token. A token failure during startup exits non-zero; a failure inside the loop is logged and the loop continues; a fatalmetric_watchererror now propagates out ofmaininstead of exiting with code 0.src/sd.rsandsrc/bin/reporter.rs: the unused V1ComponentStatusstructures are dropped.Cargo.toml:jwt,hmac,sha2andjsonwebtokenare replaced by thezitadelcrate (default-features = false), andreqwestmoves to 0.12 so that a single HTTP/TLS stack remains.The documentation of the new keys, the regenerated config schema and the removal of
specs/anddoc/modules/live in #32, so this PR stays code only.Verification: CI
build(Docker musl image),test(fmt-check, clippy,cargo test) andcoverage(tarpaulin 95% gate) onf332d07.