Skip to content

Releases: BxnnyG/matrixctrl

MatrixCtrl 0.1.35

Choose a tag to compare

@github-actions github-actions released this 06 Aug 10:10

Changed

  • The pod is now Guaranteed QoS, so the panel is no longer among the first
    processes killed when the node runs out of memory. requests now equal limits for
    both containers, which moves oom_score_adj from 997 to -997.

    This came out of measuring 0.1.34's trigger instead of assuming it. MatrixCtrl was
    killed while holding 14 MB against a 512Mi limit: the kernel logged
    constraint=CONSTRAINT_NONE, global_oom, meaning the whole node was exhausted — by
    an unrelated 18 GB process — not the container. There was no memory problem to fix.
    What the log did show is that kubelet derives the kill order from the memory
    request, so a 128Mi request against a 512Mi limit put the admin panel near the
    front of the queue.

    It creates no memory; it changes who is killed instead. The reservation is about 2%
    of the node, and the request/limit gap was buying nothing at 81Mi steady state.

    Note that QoS is a pod property: both containers need requests == limits or
    the class stays Burstable.

  • Memory and CPU limits are unchanged. Lowering the 512Mi ceiling to match real
    usage was rejected — the peak during a Helm render has never been measured, and that
    would trade a rare collateral kill for a self-inflicted one.

MatrixCtrl 0.1.34

Choose a tag to compare

@github-actions github-actions released this 06 Aug 09:46

Fixed

  • A slow-starting MAS no longer locks you out of your own panel. The container was
    OOMKilled and restarted before MAS was serving; discovery returned a proxy error
    page, the single OIDC init attempt failed, and the panel showed a username/password
    box for eleven hours while MAS was healthy seconds later. OIDC now retries in the
    background with capped backoff and never gives up on its own — giving up after N
    attempts is the same lockout on a delay.
  • The retry rebuilds from the effective startup config, not from the database.
    Reusing ReloadOIDC would have been a silent no-op here: it reads the DB, startup
    prefers env, and this deployment is env-configured. The logs would have claimed a
    recovery was running while nothing changed.
  • The connect-OIDC setup flow wins over an in-flight retry — a person acting
    deliberately outranks a background loop.

Added

  • /api/v1/auth/oidc/available reports retrying alongside enabled. "This install
    uses local login" and "Matrix login exists but its issuer is down" look identical on
    screen and lead to opposite actions. The login page shows the distinction and polls
    until it can switch back on its own, with no reload.

Security

  • A transient IdP failure used to re-open the local password login on a public URL
    indefinitely, because bootstrap login is only disabled while OIDC is configured.
    That window now closes by itself. The endpoint reports only that a retry is running,
    never the discovery error — it is unauthenticated by necessity.

MatrixCtrl 0.1.33

Choose a tag to compare

@github-actions github-actions released this 05 Aug 21:28

Added

  • Release notes for the version you are about to install, shown on the upgrade
    page beside the button that starts it. Not decoration: 26.8.0's notes say "Upgrade
    Element Web to v1.12.25" and "Upgrade Synapse to v1.158.0" — exactly the upgrades
    the pinned image tags were silently preventing. The screen now says both what the
    version brings and, from the pin warning, what a pin will stop it bringing.
  • "Upgrade auf X" arrives with X selected. The version travels from the list as a
    search parameter instead of being picked twice.
  • Notes are cached per version and the cache is bounded — published notes do not
    change, and GitHub's unauthenticated limit is 60 requests an hour.
  • "Could not be fetched" and "no notes published" are different messages, because
    they lead to different conclusions.

Security

  • The version becomes a URL path segment and is validated against a strict pattern,
    refused rather than escaped.
  • Rendered markdown links only follow http(s). A javascript: URL in third-party
    text must not become clickable in an admin panel.

Fixed

  • The documented local deploy shipped the wrong image. The chart's committed
    default is image.tag: "latest" — CI rewrites it to the exact version only when it
    packages a released chart — so a deploy from the working tree rendered :latest
    and ran whatever stale build containerd held. The first attempt at this release
    deployed 0.1.32 while helm list said APP VERSION 0.1.33 and rollout status
    said success. PROZESS §4 now passes
    --set image.tag and ends the read-back with the container's own startup line,
    which reads the artefact rather than a declaration about it.

MatrixCtrl 0.1.32

Choose a tag to compare

@github-actions github-actions released this 05 Aug 17:37

Added

  • The upgrade log says what the rollout is waiting for. It used to be a clock:
    Waiting for Helm rollout… (30s elapsed), fifteen times, while one pod sat in
    Init:CrashLoopBackOff with the explanation in its own logs. Now each tick names
    the failing pod, its container, the reason, and the container's error text.
  • Pods that are merely starting are counted rather than narrated, and an unchanged
    diagnosis is not repeated — the useful line must not become wallpaper.
  • Image tags pinned behind the chart are reported before the rollout starts. On
    the instance this was built for, four components were behind: MAS 1.15.0 against
    1.22.0, Synapse v1.151 against v1.158, Element Web v1.12.14 against v1.12.25,
    Element Admin 0.1.11 against 0.1.12. Chart upgrades had been updating templates
    while keeping old images, and nothing said so.
  • The MAS pin is what made the 26.8.0 upgrade fail: chart 26.8.0 writes
    database.password_file, MAS 1.15 does not know the field, so it connected with no
    password at all.

Deliberately not done

  • Pins are reported, not fixed. Unpinning is an upgrade decision with
    consequences — a seven-minor-version MAS jump with database migrations — and it
    belongs to the operator.
  • Only tags older than the chart's are reported. Running ahead is a choice, and
    anything not confidently orderable is left alone: a wrong "you are behind" costs an
    upgrade nobody needed.

MatrixCtrl 0.1.31

Choose a tag to compare

@github-actions github-actions released this 05 Aug 16:04

Fixed

  • MAS asked "Continue to <ULID>?" instead of "Continue to MatrixCtrl?" on the
    consent screen. The generator already writes client_name; instances registered by
    an earlier version do not have it, and there was no way to add it through the
    product.
  • Registration is now reconcilable rather than one-shot. Connecting OIDC used to
    answer 409 Conflict for ever after the first time, so any field the generator
    learned to write later could only reach fresh installs — every existing one was
    stranded with hand-edited YAML as the only route. The setup page now reports what
    the stored client is missing and offers to complete it.
  • The reconcile never regenerates the client ID or secret, never overwrites a value
    that is already set, and refuses a fragment it cannot parse rather than replacing
    it.

Changed

  • A code comment claiming client_name was undocumented and might not render is
    replaced by the verification: MAS 1.15's published config schema lists
    ClientConfig.client_name. Config is also the only durable place for it — a
    database edit does not survive mas-cli config sync.

MatrixCtrl 0.1.30

Choose a tag to compare

@github-actions github-actions released this 05 Aug 11:37

Answers six of the seven findings from an external security review. The seventh —
the ClusterRole being cluster-admin in all but name — is deliberately separate: it
is the most likely to break upgrades and needs its own verification.

Security

  • The session JWT no longer travels in a URL. The OIDC callback handed it over
    as /auth/callback?token=<jwt>, and chi's request logger writes the full URL —
    400 of the last 400 log lines carried one, so the token was written to the
    application log by the very request that delivered it. It is now a one-time code
    in the URL fragment
    : fragments are never sent to a server, so there is nothing to
    log and no Referer leak, and the code is single-use with a one-minute life, so the
    copy left in browser history is spent.
  • ?token= is accepted only on a genuine WebSocket upgrade, judged from the
    request's own headers. It used to work on every route, which made any log line or
    link carrying one a usable session.
  • A failed crypto/rand now stops the process. It used to fall back to a
    time-seeded string — and that fallback was reachable from the path that
    persists the JWT secret, so a bad first boot would have written
    matrixctrl-fallback-<unix-nanos> into the database as the permanent signing key,
    derivable from the pod start time Kubernetes publishes.
  • Login throttling with per-IP and per-user counters, progressive backoff and a
    lockout. Counted in Postgres, because an in-memory counter would make "restart the
    pod and try again" the attack.
  • The container runs as non-root (65532) with a read-only root filesystem, no
    capabilities and no privilege escalation. The ESS chart it manages already held
    its own workloads to this standard.
  • The CORS wildcard is gone. The frontend is served by this same binary on the
    same origin, so nothing needed it.
  • RevokeSession now checks the signing method, matching ValidateToken in the
    same file.

Fixed

  • The login backoff shifted without a bound: past ~62 failures the delay overflowed
    to zero, so the most persistent attacker would have waited the least. Reachable,
    because the counter keeps growing after a lockout expires. Found by a test.
  • The switch to non-root would have broken config saving on every existing install —
    the config repo was owned by root from earlier versions. A one-shot chown
    initContainer fixes ownership; fsGroup was unavailable because it would also
    apply to the Postgres sidecar's volume, and Postgres refuses to start when its data
    directory is group-accessible.

MatrixCtrl 0.1.29

Choose a tag to compare

@github-actions github-actions released this 05 Aug 11:03

Added

  • User write actions: lock, unlock, deactivate, reactivate, grant/revoke admin,
    set password — each behind a confirmation that states what it actually does.
  • The dialogs carry the consequence, not "are you sure?", because every one of
    these verbs is narrower than it sounds: locking does not end existing sessions,
    unlock does not reactivate, reactivate does not unlock, and revoking admin leaves
    existing admin sessions intact. An operator locking a compromised account needs to
    know the attacker is still connected.
  • Only the actions that fit the account's actual state are offered — no "unlock" on
    an account that is not locked.
  • Self-lockout is refused. MatrixCtrl admits only MAS admins, so locking or
    deactivating yourself, or revoking your own admin, would close the door you need to
    reopen it. Refused too when the acting identity cannot be resolved: not being able
    to tell is not permission.
  • ConfirmDialog moved into the shared primitives from the one route that had it
    inline, and now closes on Escape.

Security

  • Deactivation never erases. MAS defaults to asking the homeserver to GDPR-erase
    the account; MatrixCtrl always sends skip_erase: true and says so in the dialog.
  • Passwords cannot reach the audit table — the audit middleware records no request
    bodies. Because of that the endpoints are verb-in-path (/grant-admin,
    /revoke-admin), so the trail says which way the change went without logging
    anything that must not be logged.

MatrixCtrl 0.1.28

Choose a tag to compare

@github-actions github-actions released this 04 Aug 19:15

Added

  • Phase 2 starts: a user list. Accounts from the Matrix Authentication Service,
    searchable, filterable by state, with cursor paging. Until now the answer to "show
    me the users" was "go use element-admin".
  • Locked and deactivated stay distinct — separate timestamps, separate states,
    separate wording. Locked is reversible and usually temporary; deactivated is the
    account being gone, and an operator deciding what to do needs to know which.
  • The page states that it reads MAS, which is authoritative for accounts under
    MSC3861, rather than implying it lists every user Synapse has ever seen.
  • Bootstrap mode explains that the feature needs MAS credentials instead of showing
    an empty list that reads as "no users".

Changed

  • MAS admin access moved out of internal/auth into internal/mas, shared with the
    login path. The admin token is now cached with its lifetime instead of minted per
    call — the old behaviour doubled the request count for every page — and a 401
    drops the cache and retries once, so a rotated secret costs one retry rather than
    every request until the process restarts.

Not in this release

  • Writes: lock, deactivate, set-admin, set-password. Each is destructive in a
    different way and needs confirmation plus audit entries.

MatrixCtrl 0.1.27

Choose a tag to compare

@github-actions github-actions released this 04 Aug 18:34

Added

  • Calls / RTC can now check the ports from outside, on an explicit click. E19
    recorded inbound reachability as a permanent unknown — true from inside, and it
    quietly implied nothing could be done. One request to an outside vantage point
    answered in seconds what three days of inside-out measurement could not.
  • A control decides whether the result is believable: a port known to be open on
    an unrelated host. A blocked or broken checker reports everything as closed, and
    acting on that means reconfiguring a router that was already correct. Without the
    control, every result is unknown and the action says to change nothing.
  • The result names the distinction that cost three days: a port forward (DNAT) is
    not the same as a firewall rule allowing the port.
  • Untestable UDP ports are counted and stated rather than dropped — the most
    important port on an RTC deployment is UDP, and free checkers speak TCP.

Privacy

  • This is the only code in MatrixCtrl that leaves the cluster. It is POST, never
    runs on a page load or a timer, names both third-party hosts in the UI before the
    click, and stores nothing.

MatrixCtrl 0.1.26

Choose a tag to compare

@github-actions github-actions released this 04 Aug 17:52

Added

  • Fields changed by hand are now visible. E21 checks the patches a hook
    declares; it could not see an edit no hook knows about — which is the case P1-11
    was opened for, where an Ingress carried ingressClassName: disabled applied by
    hand and Helm's three-way merge preserved it through every upgrade in silence.
  • The mechanism is metadata.managedFields: the API server records which manager
    set which field, so this is read rather than inferred. No manifest rendering, no
    curated list of fields to watch — a curated list only ever finds what someone
    already thought of.
  • Two levels, because they are two statements: a hand-edit no hook maintains
    will never be restored by anything and is loud; one a hook maintains means someone
    bypassed the product and is quiet.
  • Metadata-only listing, so the scan costs kilobytes rather than most of a megabyte
    per poll. Ownership lives in metadata; the spec is never fetched.

Fixed

  • kubectl rollout restart's restartedAt stamp and ESS's own matrix-tools are
    not reported. On the production cluster they were three of eight findings — enough
    noise to teach an operator to skim past the two that mattered.