1083 Commits
Author SHA1 Message Date
moebiusbandandClaude Opus 5 bd57c7ae46 fix(auth): repair the inert auth configuration schema
The schema string was the inside of a "properties" block: jwts, oidc, ldap and
required sat at the root with no enclosing "type"/"properties". jsonschema
compiled it as a document with only unknown keywords, so every auth section
validated and no option was ever checked.

Wrapped it correctly and dropped the root-level required: ["jwts"]. Init logs
and continues when the jwts section is absent ("Missing JWT configuration: No
JWT token support!"), so enforcing it would abort OIDC- or LDAP-only
deployments that work today. The per-subsection required lists are unchanged.

Validation errors are now printed with err.Error() instead of %#v, which
rendered a jsonschema.ValidationError unreadably.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 16:02:41 +02:00
moebiusbandandClaude Opus 5 668a5bf877 fix(config): update example configurations to match implementation
The shipped examples had drifted from the code that parses them:

- configs/cluster.json was still in the pre-v3 format: no top-level "name",
  "numberOfNodes" instead of the required "nodes" node-list expression, plain
  numbers instead of MetricValue objects for flopRateScalar/flopRateSimd/
  memoryBandwidth, unit strings instead of unit objects, "aggregation": null,
  and hwthread 72 missing from the core topology. It neither validated against
  cluster.schema.json nor decoded into schema.Cluster. Rewritten in the current
  format and extended with the accelerator metrics the GPU subclusters need.
- archive.retention.location is not a field of taskmanager.Retention. That
  decode is not strict, so the key was silently dropped and a "move" policy ran
  with an empty target path. Replaced with target-kind/target-path.
- "ui-file" pointed at ui-config.json while the shipped file is uiConfig.json,
  so ccConfig logged a load error and the UI fell back to defaults.
- Added the options introduced since the examples were written: checkpoint
  file-format/interval/max-wal-size, api-subjects concurrency, db-config,
  nodestate-retention, archive retention/compression, smoothing-window and the
  resample policy/algo defaults.

The config written by -init had no metric-store section, so a server started
right after -init aborted with "missing metricstore configuration".

internal/configexample guards all of this: it resolves "-file" references the
way ccConfig does, strict-decodes every section into the struct that actually
parses it, and validates the cluster configs against the cc-lib schema.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 16:02:32 +02:00
moebiusbandandClaude Opus 5 93eac532c4 feat: default to average downsampling
Average consolidation makes every plotted point the true arithmetic mean of
its interval, which is what the Ganglia-style plot appearance depends on.
LTTB deliberately keeps extremes instead, so a plot downsampled with it
cannot be read as interval means.

resolveResampleAlgo used to return an empty string when neither the user nor
the config had chosen an algorithm, and cc-lib's GetResampler maps that to
LTTB. Route every fallback through config.ResampleAlgo() and ship "average"
as the generated and example default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 12:17:57 +02:00
moebiusbandandClaude Opus 5 769da61570 fix(frontend): drop dead resample trigger/resolutions config
config.ResampleConfig stopped emitting the trigger and resolutions fields,
but MetricPlot still read them. resampleResolutions and resampleMinimum
were therefore always null, which made the array-based resolution branch in
the setScale hook unreachable and the timestep !== resampleMinimum guard
always true. Keep only the policy-based branch and derive the zoom trigger
straight from targetPoints.

configs/config-large.json still carried the removed minimum-points, trigger
and resolutions keys, which DisallowUnknownFields now rejects, so starting
against it aborted. Replace them with the current keys and add a test that
loads the file, so the example configs cannot drift out of the schema
unnoticed again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 12:17:34 +02:00
moebiusbandandClaude Opus 5 a5c1fdf21f fix: forward resample algorithm to metric store for running jobs
Only archived job data and internal-store node-list data honoured the
selected resample algorithm. For running jobs the algorithm was dropped:
MetricDataRepository.LoadData had no such parameter, so the memory store
fell back to an empty string, which cc-lib's GetResampler maps to LTTB.
The external store client was worse - its APIQueryRequest had no
ResampleAlgo field at all, and LoadNodeListData accepted the parameter
without using it.

Add resampleAlgo to the LoadData interface (mirroring LoadNodeListData),
forward it from metricdispatch, and set it on both stores' requests. The
field is tagged omitempty, so the wire format is unchanged when empty -
verify the deployed cc-metric-store accepts it before relying on it there.

The REST job endpoints pass a non-zero resolution and therefore do
resample, so they now request the configured default instead of an empty
string. Add config.ResampleAlgo() for that, since "" is not a neutral
value at this layer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 12:17:03 +02:00
moebiusbandandClaude Opus 5 d40390061d fix: unify resample policy target-points table
The policy -> target-points mapping existed twice with different values:
internal/config (300/600/1000) fed the resampler's MinimumRequiredPoints
threshold, while internal/metricdispatch (200/500/1000) fed the requested
resolution and the frontend target point count.

Because the threshold was larger than the target, the resampler refused to
downsample series whose length fell between the two numbers, silently
dropping the resolution the backend had asked for. With the medium policy
that covered every series between 500 and 600 points.

Move the table into internal/config as the single source of truth (import
direction rules out the reverse, since metricdispatch already imports
config) and keep the 200/500/1000 values, which already drove the requested
resolution. metricdispatch.TargetPointsForPolicy now delegates to it, so
MinimumRequiredPoints equals the target and resampling happens exactly when
a series exceeds it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 12:16:46 +02:00
moebiusbandandClaude Opus 5 fca7b81e3d chore: apply gofumpt formatting
Pre-existing formatting drift picked up by `make fmt`. Whitespace and
comment layout only, no semantic change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 10:28:22 +02:00
moebiusbandandClaude Opus 5 4cfbecd818 Merge branch 'feat/565-add-metric-tooltip' into metric-store-tickets
Resolve conflicts in the generated GraphQL code by regenerating it against
the merged schema. cc-lib v2.13.0 adds Tooltip to schema.MetricConfig and
schema.GlobalMetricListItem, so gqlgen now binds the tooltip field directly
and the hand-written globalMetricListItem/metricConfig resolvers introduced
on the tooltip branch are no longer needed.

Also migrate to the cc-lib v2.13.0 metric container types, which changed
from bare maps to structs carrying array-valued metric groups:

  schema.JobData          map -> {Metrics, Groups}
  schema.ScopedJobStats   map -> {Metrics, Groups}
  job.Statistics          map -> schema.JobStatisticsSet{Metrics, Groups}

Callers index .Metrics, return the zero struct instead of nil, and
deepCopy/DecodeJobStats now also carry the Groups payload through.
archive.GetStatistics returns the full JobStatisticsSet so group
statistics survive the round trip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 10:22:22 +02:00
moebiusband e59ee99474 Upgrade cc-lib. Regenerate GraphQL.
Does not compile
2026-08-27 10:08:02 +02:00
moebiusband b0fb86cd4d Merge branch 'feat/74-assign-user-role-based-on-ldap-group' into metric-store-tickets 2026-08-27 08:13:48 +02:00
Christoph Kluge 053c44d7b5 add tooltips to metricConfig, rendered in metricSelect, rework availability display 2026-07-28 17:36:29 +02:00
Aditya Ujeniya c509a0104b change resampling policy points 2026-07-20 09:43:18 +02:00
moebiusbandandClaude Opus 4.8 3474690adc feat(auth): map OIDC token roles to CC roles via configurable mapping
OIDC roles were translated by a hardcoded switch that only recognised the
literal names user/admin/manager/support (dropping api) and required the
IdP to emit exactly those names.

Add an optional auth.oidc.role-mapping (OIDC role/group claim value -> CC
role) so operators can map their own realm/client role names, including
api. The mapping is the sole source of roles: only mapped names are
honored, unmapped token roles are ignored, and users without any mapped
role receive the base "user" role. Mapping targets are validated once at
startup. Role assignment stays authoritative on every login as before.

Note: deployments relying on the IdP emitting literal CC role names must
now map them explicitly (e.g. "admin": "admin").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 66451678484b
2026-06-19 11:28:20 +02:00
moebiusbandandClaude Opus 4.8 63a82d022b feat(auth): assign elevated LDAP roles via configurable filters
The LDAP sync only ever granted the base "user" role. Add an optional
auth.ldap.role-filters map (role -> LDAP filter) so accounts matching a
filter are granted that elevated role (admin/support/api/manager).

LDAP is authoritative for the configured roles: sync both adds and removes
them to match group membership, while roles not listed (e.g. a manually
granted manager) are preserved. A managed manager that still has assigned
projects is never stripped. With no role-filters configured behaviour is
identical to before.

Roles are reconciled during periodic sync and at login. Sync evaluates
each filter once over the whole base (one search per role, not per user)
and reconciles existing users via a single ListUsers lookup plus the new
UserRepository.UpdateRoles helper.

Closes #74

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: e38526c3259d
2026-06-19 06:36:55 +02:00
moebiusband ffbe171327 Merge branch 'main' into feature/526-average-resample 2026-06-18 07:17:41 +02:00
moebiusbandandClaude Opus 4.8 411bc9b317 feat(web): make footer legal links configurable
Add a "main.footer-links" config option so the footer Imprint and
Privacy Policy links can point at internal pages (default) or external
URLs. External http(s) targets open in a new tab; empty/unset values
fall back to the built-in /imprint and /privacy routes, keeping the
existing ./var/*.tmpl override mechanism intact.

Closes #517

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c5dca72c848f
2026-06-17 17:29:50 +02:00
moebiusbandandClaude Opus 4.8 83d04dff17 feat(auth): replace .env/godotenv secret handling with config-based secrets
Secrets (JWT keys, LDAP sync password, OIDC client id/secret, cross-login
keys) are now configured directly in config.json under the auth section
where they are used. Each secret can still be supplied via its existing
environment variable, which takes precedence over the config value.

The godotenv dependency, the .env file, configs/env-template.txt and the
loadEnvironment() bootstrap step are removed. -init now writes the demo
JWT keys into config.json instead of a .env file.

Closes #283

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3a7cb814c53f
2026-06-17 12:28:17 +02:00
moebiusbandandClaude Opus 4.8 2b01b57495 feat: replace gorilla/sessions with alexedwards/scs/v2
Browser sessions are now server-side, stored in the SQLite database via
scs/sqlite3store (new `sessions` table, DB migration to version 12) instead
of gorilla/sessions client-side cookie storage. Only an opaque random token
is kept in the cookie; session data lives server-side and survives restarts.

Session middleware is wired as a hybrid to avoid buffering large responses:
scs.LoadAndSave on the login/logout write paths, and a non-buffering
read-only LoadSession middleware on the secured/config/frontend read paths
so the large GraphQL /query responses stream unbuffered. JWT-only APIs
(/api, /userapi, /api/metricstore) and static files are left unwrapped.

The session cookie Secure flag is now derived from the server config (set
when cc-backend terminates TLS itself); previously it was effectively never
set. The SESSION_KEY env var is removed as server-side tokens need no
signing secret. The dormant Bearer-JWT branch in the frontend urql client
is removed; the web UI authenticates GraphQL via the session cookie.

Closes #558

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: b51075f43cc7
2026-06-17 07:54:26 +02:00
moebiusband 5d8d4e228e Merge branch 'main' into feature/526-average-resample 2026-06-17 06:30:54 +02:00
Jan EitzingerandGitHub c94f5918f3 Merge pull request #556 from ClusterCockpit/release/v1.5
Fix critical/severe issues in init, startup and shutdown
2026-06-07 07:31:02 +02:00
moebiusbandandClaude Opus 4.8 1b72b0b5ad Fix critical/severe issues in init, startup and shutdown
- auth: do not abort the server when authentication is disabled. auth.Init
  is now always called; with disable-authentication it sets up an ephemeral
  session store (SESSION_KEY not required) and registers no authenticators,
  so the unconditional auth.GetAuthInstance() callers (server init,
  api.New()) always get a valid instance.
- main: run the graceful-shutdown sequence on the startup-error path. runServer
  derives a cancelable context and, on a server-start failure, cancels it and
  waits so the metricstore final checkpoint / WAL rotation, archiver flush and
  taskmanager shutdown actually run before exit.
- server: log the :80 HTTP->HTTPS redirect listener error instead of dropping it.
- archiver: guard Shutdown against being called when Start never ran
  (avoids close(nil) panic / blocking on a nil workerDone).
- nats API: stop worker goroutines on shutdown via a stop channel + idempotent
  Shutdown(); workers and subscription callbacks select on stop and the
  channels are never closed, so no send-on-closed-channel can occur. Wired
  into Server.Shutdown after the NATS client is closed.
- metricstore: make Shutdown idempotent (nil shutdownFunc, early return) and
  release shutdownFuncMu before the checkpoint write.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3c179f9caa8f
2026-06-05 10:16:28 +02:00
Jan EitzingerandGitHub d74ae77c8e Merge pull request #553 from ClusterCockpit/release/v1.5
Release/v1.5
2026-06-04 20:33:22 +02:00
moebiusband 3bef199cbe Regenerate GraphQL 2026-06-04 20:12:27 +02:00
moebiusbandandClaude Opus 4.8 16942f55a0 Fix medium-severity issues from follow-up security audit
Addresses the remaining medium findings from the second-pass audit:

- DoS hardening: bound GraphQL query cost with FixedComplexityLimit, and
  reject non-positive items-per-page / page values so uint64 conversion
  cannot underflow into an unbounded LIMIT/OFFSET. The -1 "load all"
  sentinel stays valid for dashboards; REST now returns 400 for bad input.

- Security headers: add X-Content-Type-Options, X-Frame-Options,
  Referrer-Policy and a conservative CSP (frame-ancestors/object-src/
  base-uri) that hardens against clickjacking and base-tag injection
  without restricting the self-hosted SPA's inline scripts.

- Stored XSS: render job.metaData.message as escaped text instead of
  {@html ...} in Job.root and JobFootprint, preserving line breaks via
  white-space: pre-wrap.

- SQL injection hardening: parameterize the tag-scope IN list and the
  manager project subquery in CountTags instead of interpolating
  user.Username / user.Projects (externally sourced via OIDC/LDAP).

- CSRF defense-in-depth: reject cross-site state-changing requests via
  Sec-Fetch-Site, failing open for non-browser clients, on top of the
  existing SameSite=Lax session cookie.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: de7d47a85c7c
2026-06-04 20:08:41 +02:00
moebiusbandandClaude Opus 4.8 6d86690c76 Fix critical issues from follow-up security audit
A second-pass audit surfaced three severe issues missed by the previous
review, each a sibling code path of a bug class that was only partially
fixed before:

- auth: JWT session login (jwtSession.go) registered its authenticator
  even when CROSS_LOGIN_JWT_HS512_KEY was unset, leaving an empty HMAC
  key. golang-jwt verifies any HS256/HS512 signature against an empty
  key, allowing unauthenticated admin token forgery. Init() now refuses
  to register without a key, with a defense-in-depth empty-key guard in
  the keyfunc.

- repository: metric names from GraphQL ([String!]) were interpolated
  raw into json_extract(footprint, "$.<name>") SQL. SQLite parses
  double-quoted strings as literals, enabling SQL injection by any
  authenticated user. Validate metric names against ^[a-zA-Z0-9_]+$ in
  jobsMetricStatisticsHistogram and buildFloatJSONCondition.

- metricstore: cluster/host line-protocol tags flowed unvalidated into
  path.Join(RootDir, cluster, host) for checkpoint/WAL files, allowing
  arbitrary file write outside the checkpoint root via NATS
  (unauthenticated) or POST /api/write. Reject path-traversal sequences
  in DecodeLine before the tags become path components.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: b57246993ec1
2026-06-04 19:07:20 +02:00
moebiusband 6f7e262f3f Fix issues after security audit
Entire-Checkpoint: bc18358a9343
2026-06-04 18:33:30 +02:00
moebiusband 58ead40112 Merge branch 'main' into release/v1.5 2026-06-04 17:56:41 +02:00
moebiusband 0020f63582 Rebuild Swagger 2026-06-04 17:56:32 +02:00
Christoph Kluge 1ebde74774 Adapt swagger definitions of user update endpoint 2026-06-02 17:58:15 +02:00
Christoph Kluge 40722d72f5 fix name in doc comment 2026-06-02 16:48:23 +02:00
Christoph Kluge f4384668e5 fix name comment 2026-06-02 16:47:29 +02:00
Christoph Kluge e06982db00 reintroduce user update api path 2026-06-02 16:34:46 +02:00
Christoph Kluge 4c59aee304 feat: add subCluster filter to filter component 2026-04-24 11:43:42 +02:00
moebiusband 43807ae12a feat: Also submit projects array via oidc token
Entire-Checkpoint: 2064482d97e1
2026-04-01 13:46:21 +02:00
moebiusband 31a8a11f1b fix: Always request oidc roles from token
Entire-Checkpoint: bfdbffd7aae0
2026-04-01 12:36:37 +02:00
moebiusband 84fe61b3e0 fix: allow all role changes on SyncUser and UpdateUser callback
Entire-Checkpoint: 496bace0120e
2026-04-01 11:09:50 +02:00
moebiusband 1f04e0a1ce fix: oidc role extraction
Entire-Checkpoint: bbe9ad3cf817
2026-04-01 11:03:19 +02:00
moebiusband 641dc0e3b8 Run gofumpt 2026-03-30 16:49:27 +02:00
moebiusband 82c514b11a Ease samesite cookie settings
Entire-Checkpoint: 2fe286e23a4a
2026-03-30 16:10:15 +02:00
moebiusband c267501a1b Reduve noise in tagger logging 2026-03-25 06:53:01 +01:00
Christoph Kluge bd7125a52e review doubleranged filters, fix and improve valeu selection 2026-03-24 15:00:41 +01:00
moebiusband 0c56591e4b Adopt config to use policy based resampler configuration
Entire-Checkpoint: 7536f551d548
2026-03-20 08:03:34 +01:00
moebiusband bf48389aeb Optimize sortby in stats queries
Entire-Checkpoint: 9b5b833472e1
2026-03-20 05:39:22 +01:00
moebiusband 0069c86e81 Replace explicit resampling config with policy based approach
Entire-Checkpoint: f69e38210bb1
2026-03-20 05:34:12 +01:00
moebiusband c0d2d65f96 Introduce average resampler support
Fixes #526

Entire-Checkpoint: 893a1de325b5
2026-03-19 21:16:48 +01:00
Christoph Kluge 10b4fa5a06 change: remove heuristic metricHealth, replace with DB metricHealth
- add metricHealth to single Node view
2026-03-19 15:55:58 +01:00
Christoph Kluge 886791cf8a remove deprecated minRunningFor filter remnants 2026-03-19 14:09:10 +01:00
Christoph Kluge 16ec1e69d9 streamline and unify statsSeries calc and render 2026-03-19 13:30:38 +01:00
moebiusband 22057ff281 Pass reqKey as CacheKey
Entire-Checkpoint: b95ef43221bb
2026-03-19 11:04:32 +01:00
moebiusband 8b0881fb17 Exclude down nodes from HealthCheck
Entire-Checkpoint: 0c3347168c79
2026-03-18 11:20:12 +01:00