Skip to content

Release v0.10.0

Latest

Choose a tag to compare

@eniac111 eniac111 released this 02 Nov 00:30
4db474e

Release v0.10.0

Licensing

  • Bor is now licensed under the AGPL-3.0-only (it was LGPL-3.0-or-later).
    Contributions require a signed Contributor License Agreement, handled on
    the pull request. Vute Tech LTD also distributes Bor Enterprise under a
    commercial license.

New Features

Disk Encryption Policy Type

  • Disk encryption (DiskEncryption, shown as LUKS) is a new policy type
    that manages LUKS2 full-disk encryption across the fleet. A policy names
    the volumes in scope, requires them to be encrypted, enrolls unattended
    unlocking through the TPM 2.0 chip (systemd-cryptenroll, explicit
    PCR profiles) and/or Tang network servers (clevis, pinned signing
    keys the administrator confirmed), and escrows a per-volume recovery
    key
    on the Bor server. Bor never converts a disk: an unencrypted volume
    is reported as non-compliant.
  • The server is never in the boot path. Unlocking is done by the
    initramfs (TPM, Tang or a typed recovery key); a server outage never stops
    a machine from booting. The agent runs cryptsetup, systemd-cryptenroll
    and clevis with fixed arguments and passes key material on stdin or a
    dedicated pipe, never on a command line or in a file, and zeroes it after
    use.
  • Escrow you can trust. Recovery keys are sealed per record with
    AES-256-GCM under a key-encryption key (KEK) read from a file outside the
    database (BOR_ESCROW_KEK_FILE), with the volume, LUKS UUID, record and
    KEK id bound as additional data. Escrow is a two-phase commit (escrow
    first, confirm after the keyslot exists), rotation is crash-resumable, and
    the KEK rotates through BOR_ESCROW_KEK_PREVIOUS_FILES with a background
    rewrap. Without a KEK the feature stays off (fail closed).
  • Help-desk recovery. The new Disk Encryption page shows the fleet
    summary, a volume search and the per-node detail. Revealing a recovery key
    needs the disk_encryption:reveal permission (Super Admin only by
    default), a reason or ticket number, and a fresh re-authentication with
    the password and an authenticator code (step-up; the code is mandatory
    while BOR_ESCROW_REQUIRE_MFA is on, the default). Every reveal and every
    refusal is audited (recoverykey.reveal, recoverykey.reveal_denied). Retired keys stay
    revealable for BOR_ESCROW_RETIRED_RETENTION_DAYS (30) and the keys of
    deleted nodes for BOR_ESCROW_ORPHAN_RETENTION_DAYS (90), then they are
    crypto-shredded.
  • Tang servers are registered under Settings -> Tang Servers: the
    server fetches and verifies the advertisement (JWS over ES512), pins the
    RFC 7638 SHA-256 thumbprints, probes the servers periodically and reports a
    key change (tangserver.check).
  • Inventory and drift. Agents report every LUKS2 volume with its keyslots
    and tokens every 15 minutes; keyslot fingerprints detect drift and cloned
    disks (luks.drift, luks.clone_suspected, luks.tpm_reseal). The node
    drawer gains a disk encryption section and the Compliance page labels the
    LUKS items.
  • Adopt existing machines with bor-agent luks status and bor-agent luks adopt, which inspect the volumes and enroll the Bor protectors with
    an existing passphrase, interactively and under a lock file. New machines
    are provisioned through a one-time bootstrap credential directory.
  • Permissions disk_encryption:view (granted to every role that views
    nodes), disk_encryption:reveal and disk_encryption:rotate (Super
    Admin) and tang_server:* (roles with settings:manage). New Prometheus
    gauges bor_luks_volumes_total, bor_luks_escrow_operations_total,
    bor_luks_recovery_key_reveals_total and
    bor_luks_recovery_keys_overdue_total; the CEF and OCSF exporters map the
    new audit events and recovery_key is redacted everywhere.

Services & Timers Policy Type

  • Services & timers (Systemd) is a new policy type for systemd units
    on nodes. A policy sets, per unit, the enablement (enabled, disabled or
    masked), the activity (running or stopped), overrides for a timer's
    schedule and a service's restart, timeout, environment, resource and
    sandboxing settings through a 90-bor.conf drop-in, raw directives for
    anything else, and a last-run check for timers and services. Several
    bound policies are merged per unit by binding priority; on equal
    priority the more restrictive value wins. The agent drives systemd over
    D-Bus, records each unit's original state and restores it when the unit
    leaves every policy, re-asserts states on a reconcile pass and within
    seconds of a manual change through systemd's signals, and guards every
    file it writes with the tamper watcher.
  • Scheduled tasks. A policy can deploy a script or a program as
    bor-<name>.service with a timer schedule or a boot trigger, sandboxed
    by default, optionally as an unprivileged user. Failed runs are
    non-compliant with the exit status and the tail of the task's journal.
    The bor- prefix is mandatory, so a task can never replace a vendor
    unit.
  • Units of all users. An entry scoped to All users manages the unit
    in every user's systemd --user instance: enablement on the file system
    under /etc/systemd/user exactly as systemctl --global does, drop-ins
    and tasks in the user manager, and the state applied to the sessions that
    are logged in.
  • Preset file. With the preset switch the agent writes
    /etc/systemd/system-preset/10-bor.preset, so the policy's enablement
    decisions also apply to units installed later.
  • Node actions. The Services & timers section of a node lists the units
    it reported and offers Start, Stop and Restart to holders of
    the new systemd:operate permission. The request goes to the agent over
    its policy stream, the row updates with the result, and the server records
    a systemd.unit_action audit event. Protected units are refused and risky
    ones ask for a confirmation.
  • Editor. Unit names come from the inventory agents report; a node
    picker shows what the chosen node does now. Schedule presets, a
    next-runs preview computed by the server's systemd-analyze, two
    templates (desktop hardening, maintenance window), a confirmation for
    units whose stop can cut users off (network, SSH, display managers,
    firewall, time sync, audit, cron) and a protected list the server
    refuses (D-Bus, logind, journald, udev, polkit, the boot targets, the
    agent itself).

SELinux and AppArmor Policy Types

  • SELinux (Selinux) is a new policy type for nodes that run SELinux
    (Fedora, RHEL, SLES 16). A policy sets the enforcement mode (runtime and
    /etc/selinux/config), boolean values, per-domain permissive exceptions,
    the enabled state of policy modules, Bor-deployed CIL modules installed
    as bor-<name> at semodule priority 450, file contexts with an optional
    relabel, and login mappings to confined SELinux users. Nodes without
    SELinux report the policy inapplicable, or non-compliant when the policy
    requires SELinux; a switch prepares disabled nodes for the next reboot.
    The agent batches every module change of a sync into one semodule
    run, records every original value and restores it when the key leaves
    every policy, re-asserts the state on a reconcile pass and within
    seconds of a manual setenforce, setsebool or semodule by following
    the audit records, and guards its files with the tamper watcher.
  • AppArmor (Apparmor) is a new policy type for nodes that run
    AppArmor (Ubuntu, Debian, SLES 15). A policy keeps AppArmor and its
    service on, controls the user namespace restriction of Ubuntu kernels,
    sets profile modes (enforce, complain, disabled) by file name or pattern
    through the parser's disable/ and force-complain/ links without
    touching vendor files, deploys custom profiles as bor-<name> after a
    parser syntax check, adds site-local rules to vendor profiles inside a
    managed block of their local/ file, and adds home directory roots.
    Profiles that would share an executable with an existing profile are
    refused, because the kernel would run it unconfined.
  • Editors. Both editors show, per node or across the fleet, the
    booleans, modules and profiles agents reported, with the server's
    curated list of the switches that matter on desktops and their hardened
    values, one-click templates (process hardening, network homes, kiosk
    accounts, developer workstation; enforce all vendor profiles, user
    namespace stub for an application, confine an application in complain
    mode first, deny network) and confirmations before a permissive mode,
    a disabled profile or a relaxed restriction.
  • Tasks under a profile. A scheduled task of a Services & timers
    policy can run under an AppArmor profile (apparmorProfile, rendered as
    AppArmorProfile=) or an SELinux context (selinuxContext, rendered as
    SELinuxContext=), so a task can use a profile or type that an AppArmor
    or SELinux policy deploys; both editors point at the other.
  • Benchmark controls. Compliance items that correspond to a CIS
    benchmark control (the SELinux mode and active items, the AppArmor
    enabled and profile items) carry the control's number and title in the
    compliance API and as a label on the Compliance page.
  • Node page. A new Mandatory access control section shows what a node
    runs (the kernel security module and its mode, the policy or parser
    version, counts per mode), its booleans or profiles, and a summary of
    the recent denials grouped by domain or profile with one redacted
    example. A userns_create denial offers the ready stub profile. Holders
    of the new mac:operate permission can ask the node for a proposal
    (audit2allow output and its CIL translation, or AppArmor rules
    derived from the denials) to review and paste into a policy; nothing is
    applied by a proposal. The nodes list gains a MAC column and filter.

Software Updates Policy Type

  • Software updates (Updates) is a new policy type that manages the
    distribution's unattended update mechanism and the reboot after
    updates. A policy sets what the node installs on its own (nothing,
    check only, security updates, all updates), the schedule as a drop-in
    of the engine's timer, packages held back, and the reboot rule: never,
    when an update needs it, or after every run, within a daily window,
    only when nobody is logged in unless the reboot has been pending for a
    number of days, with a desktop warning before a reboot that affects
    users. The agent drives dnf-automatic (DNF 4 and 5),
    unattended-upgrades, os-update and rpm-ostree's automatic update
    policy, installs the engine package when it is missing, switches every
    engine's own reboot off and decides reboots itself, records every
    original state and restores it when the node leaves every policy.
  • Node page. A new Software updates section shows the engine and its
    effective mode, who configured it, the last check and run, the running
    and newest installed kernel, whether a reboot is pending and why, and
    the pending updates with their advisories, severities and CVE ids,
    searchable and filterable to security updates.
  • Compliance. Items for the engine, the mode, the schedule, the
    reboot rule, each hold and the engine's last run. A policy's Limits
    add three age items: a security update pending on the node longer than
    the limit, a reboot pending longer than the limit, and package metadata
    older than the limit (updates:security-age, updates:reboot-age,
    updates:check-age), evaluated after every inventory scan. The mode
    and the age items carry the CIS control Ensure updates, patches, and
    additional security software are installed
    (1.2.2.1 in CIS RHEL 9
    v2.0.0 and CIS Ubuntu 24.04 v1.0.0) on the Compliance page.
  • Node actions. The Software updates section of a node offers Check
    for updates
    (refresh the metadata and report again), Install
    updates now
    (run the engine once with the policy's mode and show the
    tail of its output) and Reboot (at once without sessions; forced,
    with a desktop warning and a one-minute delay, with users logged in).
    They need the new updates:operate permission and are audited as
    updates.action.
  • Fleet views. The nodes list has an Updates column and filter
    (security updates pending, reboot pending, no update engine, not
    reported) and the dashboard's fleet overview counts the nodes with
    security updates pending, with a reboot pending and without an engine,
    from a new GET /api/v1/updates/summary.
  • Firmware. Nodes with fwupd list their pending firmware updates in
    the inventory; a policy can enable or disable fwupd-refresh.timer and
    report pending firmware as non-compliant (updates:firmware). Firmware
    is never installed by policy. Snap packages are out of scope.
  • Offline installs on Fedora. With Install offline at the reboot
    the engine's timer only downloads; the agent stores a dnf5 offline
    transaction and the policy's reboot runs it before the desktop comes
    up. A stored transaction counts as a pending reboot and is removed when
    the policy leaves (updates:offline item).
  • Transactional systems. openSUSE MicroOS, Aeon, Kalpa and SLE Micro
    are driven through transactional-update (/etc/transactional-update.conf,
    its timer, zypper locks for holds, /run/reboot-needed as the reboot
    signal); updates.engine in the agent configuration forces an engine.
  • CVE export. The nodes list and the node section export the pending
    updates as CSV (node, groups, engine, package, version, advisory,
    severity, CVE ids) from GET /api/v1/updates/export, and dnf nodes now
    report the CVE ids of their security advisories.
  • Schedule drop-in renamed. The Software updates type writes its
    timer schedule as <timer>.d/90-bor-updates.conf (it was
    90-bor.conf, which the Services & timers type removed as one of its
    own stale drop-ins); an agent upgrade replaces the old file.

KDE Plasma Policy Type Rebuilt

  • Any KDE setting, from a catalogue. The fixed field list of the KDE
    Plasma (Kconfig) type is replaced by generic KConfig entries: any
    configuration file, group path, key and value, with KDE's immutable
    ([$i]), shell-expansion ([$e]), default ([$d]) and locale markers.
    A built-in catalogue of curated settings (session and shell
    restrictions, Plasma desktop, application menu actions, Dolphin, Konsole,
    KRunner, Okular, Ark, KWallet, Kate, Spectacle, KDE PIM, screen lock,
    session, power, appearance, launcher favourites, input and locale,
    privacy, devices, KIO proxy, System Settings and URL rules) documents
    each entry with the file, group and key it writes and the closest Windows
    Group Policy setting. The editor offers a searchable catalogue tree,
    typed value widgets, enforce and expand switches, custom entries, INI
    import, presets (security baseline, shared lab, public kiosk, branding)
    and a rendered-file preview. Policies written by older editors are
    converted when opened and keep working.
  • Settings reported by nodes. Agents on KDE nodes report the settings
    described by the installed KConfigXT schemas and the System Settings
    modules they have, so a node picker in the editor shows what a given node
    actually offers (GET /api/v1/kconfig/catalogue, /api/v1/kconfig/kcms).
    With kconfig.probe_effective the agent reads the written settings back
    inside a logged-in session and reports user overrides of non-enforced
    defaults as non-compliant.
  • Desktop layout and wallpaper. A policy can deploy a wallpaper, a
    Plasma panel layout script and an optional base global theme as a
    generated look-and-feel package, applied once to existing users, live to
    logged-in sessions, or enforced as the global theme so that panels and
    wallpaper return to the policy at every login.
  • Application packs. A Konsole profile (optionally the default and
    locked through Kiosk restrictions), and a list of applications hidden
    from menus and launchers.
  • Scope to Unix groups. A policy can apply to the members of Unix groups
    only: the agent writes a directory per group, a login hook resolves
    membership by GID and puts the matching directories first in
    XDG_CONFIG_DIRS, highest priority first. Group-scoped policies can carry
    their own desktop layout, Konsole profile and hidden applications.
  • Flatpak KDE applications see the managed configuration through a
    system-wide Flatpak override (kconfig.flatpak_override, on by
    default). Show-only module restrictions hide every System Settings
    module that is not listed, based on the modules installed on the node.

Sign-in and MFA

  • The sign-in page asks for the second factor before the password.
    Enter the user name, then the security key or authenticator code, then
    the password. A security key completes the sign-in on its own. Users
    without a second factor go straight from the user name to the password.
    The API behind it is the multi-step flow POST /api/v1/auth/begin and
    /api/v1/auth/step; the former POST /api/v1/auth/login is still
    served for API clients.
  • MFA enrollment at sign-in. With Require MFA for all users on, a
    user without a second factor can now be guided to set up an authenticator
    app or a security key right after entering the password and only then
    gets a session (Set up MFA while signing in, allowed by default; the
    alternative refuses the sign-in).
  • Step-up authentication re-checks the identity before privileged
    actions, starting with revealing a recovery key: the user gives a reason
    or ticket number (stored in the audit log), the password (checked against
    the directory for LDAP accounts) and an authenticator code, and receives
    a single-use token valid for five minutes and for that one action
    (/api/v1/auth/step-up, /step-up/method, /step-up/collect).
  • Precise session revocation. Tokens carry their issue time, so
    disabling a user, changing their password or revoking their sessions
    invalidates exactly the tokens issued before that moment.
  • Ready for single sign-on. User accounts can now be federated to an
    external identity provider: such accounts have no password in Bor, are
    matched by the provider's stable subject and never by email, and their
    groups can be synchronized from the provider. Linking an
    administrator account to a single sign-on identity by email needs a
    prior, expiring approval by another administrator (Allow SSO link on
    the Users tab, shown once a sign-in connection exists). OIDC and SAML
    sign-in and SCIM provisioning are Bor Enterprise features on top of this
    release.
  • Dedicated user and group audit events. user.create, user.update,
    user.delete, user.link_identity, user_group.create,
    user_group.update, user_group.delete, user_group.member_add,
    user_group.member_remove and sso.login_failed complement the generic
    request log.

Compliance Reports (Bor Enterprise)

  • Compliance reports are implemented by the Bor Enterprise server. The
    community server carries only the web UI pages, which show the Reports
    page on a Bor Enterprise server and a Bor Enterprise notice otherwise;
    it has no report tables, permissions or API. Moving
    from the community server to Bor Enterprise of the same version is an
    installation of the Bor Enterprise server and its license; the report
    tables are created on its first start.
  • Reports (Fleet -> Reports) are point-in-time snapshots of the
    fleet's compliance: the nodes in scope with their posture (disk
    encryption, pending security updates, mandatory access control, agent
    version, connection state), the policies in force with their versions,
    the agent's last result per node and policy with the per-setting items
    and their benchmark control labels, the age of every result, stale and
    unreachable markers, and a compliance score. A report never changes
    after it is generated. Generate one for all nodes or for node groups,
    optionally limited to policy types, with a label and notes.
  • Evidence you can hand over. Every report is stored as a canonical
    JSON bundle (bor.report.v1) whose SHA-256 is recorded with the
    report and in the report.generate audit event, so a forwarded audit
    log anchors it outside the database. The hash is re-checked whenever
    the report is opened; a mismatch is shown and audited
    (report.integrity_failure). Downloads: the JSON bundle and CSV files
    for results, items, nodes and controls; every download is audited.
  • Permissions report:view, report:create, report:manage (delete,
    pin, schedules) and report:attest (sign-off, waivers), created by the
    Bor Enterprise server. Viewing goes to every built-in role that can view
    compliance; Org Admin and Auditor can generate; Super Admin can delete;
    Super Admin and Auditor can sign off.
  • Retention. Reports are kept for reports.retention_days (1095 by
    default) and purged hourly (report.purge); reports.stale_after
    (168h) sets the stale and unreachable thresholds; reports.max_results
    and reports.generate_timeout bound one generation.
  • Frameworks. Results and settings map to ISO/IEC 27001:2022 Annex A,
    NIS2 Art. 21(2) and the CIS RHEL 9 v2.0.0 and Ubuntu 24.04 v1.0.0
    benchmarks; CIS controls apply only on their distribution and only
    where the policy's desired state is evidence for them. A Controls
    tab lists them per framework with the results behind each control.
    Every report records the mapping version it was built with.
  • Fresh evidence. A report can first ask the online agents in scope to
    re-check and wait for them (reports.refresh_wait, 60s); the report
    records how many answered.
  • Trend and comparison. The server stores a daily summary of the fleet
    and of every node group (reports.daily_summary); the Reports page and
    the dashboard show the compliance score over 30 days. A Compare tab
    lists new failures, fixes and scope changes since an earlier report.
  • Schedules and delivery. Daily, weekly or monthly reports in a time
    zone, delivered by signed webhook (HMAC-SHA256) or by email with the
    documents attached, through a persistent queue with retries (reports
    configuration section or BOR_REPORT_* variables; read-only settings
    panel with a test email).
  • Waivers, sign-off and legal hold. Accepted exceptions with an
    expiry are recorded in the reports they cover; a person other than the
    generator approves or rejects a report with a signed statement over its
    hash (new permission report:attest, granted to Super Admin and
    Auditor); pinned reports are kept until unpinned.
  • Documents and SIEM. A signed PDF/A-2b document (optionally
    time-stamped by an RFC 3161 authority), an OCSF 1.3.0 Compliance Finding
    export, and live forwarding of compliance status changes as OCSF
    findings when the audit log is forwarded in OCSF. bor-verify also
    verifies the exported sign-offs.
  • Prometheus metrics. bor_compliance_* metrics for stored reports,
    the newest report per trigger, the fleet score, schedule states and
    overdue schedules, the delivery queue and the signing certificate's
    expiry, with suggested alerts in the monitoring guide.

Documentation Site

  • The user and administrator documentation moved into the repository under
    doc/ as an MkDocs site, published at docs.getbor.dev:
    installation, configuration, a page per policy type, operations,
    security and compliance, a permissions reference and developer
    documentation (architecture, building, adding a policy type,
    contributing). The README is slimmed down and points there.

Security

  • Disk encryption is designed for hostile help desks. Reveals need a
    dedicated permission, a fresh re-authentication with MFA, and leave an
    audit trail; the reveal endpoint bypasses the generic request log in
    favour of explicit events and sets Cache-Control: no-store. Tang
    registry URLs are validated with a strict pattern (http or https, host
    name or IPv4 literal, no credentials, query or fragment) and probes dial
    only approved addresses, refusing loopback, unspecified, link-local,
    multicast and broadcast targets (CodeQL go/request-forgery). LUKS2
    header integers are parsed with explicit bit sizes and clamped before
    narrowing.
  • New permissions mac:author and mac:operate. Creating, saving,
    importing or releasing an SELinux or AppArmor policy that carries
    administrator-authored policy text (custom modules, file contexts, login
    mappings, permissive domains, custom profiles, local overrides) requires
    mac:author on top of the policy permissions; refusals are
    access_denied events and every such save and release is audited as
    policy.mac_custom with the names and a SHA-256 of each text. Asking a
    node for an SELinux module proposal or an AppArmor rule proposal from
    the node page requires mac:operate; every request is audited as
    mac.action. Super Admin holds both on upgrade. Nodes can refuse custom
    text with
    selinux.allow_custom_modules: false and
    apparmor.allow_custom_profiles: false, mode changes with
    selinux.allow_mode_change: false, and vendor profile edits stay off
    unless apparmor.allow_vendor_flag_edits: true. Denial records are
    aggregated and redacted on the node (no user ids or names, home
    directories collapsed) and only the latest summary per node is kept.
  • Holds are audited. Creating, saving or releasing a Software updates
    policy that holds packages back from updates is audited as
    policy.updates_hold with the patterns, since a hold keeps a vulnerable
    package on every node of a group. Forced reboots and reboots with users
    logged in are off by default, confirmed in the editor, always preceded
    by a warning, and can be refused per node with updates.allow_reboot: false.
  • New permissions systemd:exec and systemd:operate. Creating,
    saving, importing or releasing a Services & timers policy that runs
    commands on nodes (tasks or raw Exec* directives) requires
    systemd:exec on top of the policy permissions; a draft released by
    someone other than its author is checked too. Every such save and
    release is audited as policy.systemd_exec with the task names and a
    SHA-256 of each script; refusals are access_denied events. Starting,
    stopping or restarting a unit from the node page requires
    systemd:operate and is audited as systemd.unit_action. Super Admin
    holds both on upgrade; grant them to other roles explicitly. Nodes can
    refuse tasks altogether with systemd.allow_tasks: false and actions
    with systemd.allow_actions: false.
  • User-group roles are enforced, and every way of granting them is
    guarded.
    Roles assigned to user groups, which the UI has always offered,
    now grant their permissions to the group's members; previously they had no
    effect. Review your user-group role assignments before upgrading.
    Assigning a role to a user group and adding a member to a user group are
    privilege-escalation-guarded like direct role assignments: membership
    confers every role of the group, so the caller must already hold all of
    those roles' permissions. A user allowed to manage user groups can no
    longer put themselves, or anyone else, into a group carrying permissions
    they do not have.
  • Old inert scoped role assignments are removed. Earlier versions
    accepted "organization" and "group" scoped role assignments but never
    enforced them. The upgrade deletes those rows (they granted nothing) so
    they can never turn into live grants; the API now rejects scoped
    assignments with HTTP 400 and the scope selector is gone from the UI.
  • Denied changes are audited. Refused state-changing requests, including
    privilege-escalation refusals, are recorded as access_denied audit
    events with the actor, the permission or guard involved and the reason.
    Removing a user or user-group role assignment writes a revoke_role event
    naming the user or group and the role. Both honor the audit
    IP-anonymization setting.
  • TOTP enrollment QR codes are rendered by the pquerna/otp library
    the server already used for the codes themselves; the separate
    go-qrcode dependency and its image stack are gone.
  • Services & timers time spans are parsed with a regular expression
    that cannot backtrack, so a crafted value in a policy cannot stall the
    agent.
  • Dependency advisories resolved since v0.9.0: golang.org/x/net
    (HTML parser advisories, now v0.62.0), golang.org/x/crypto,
    fast-uri (four host-confusion and scheme advisories, 3.1.9),
    serialize-javascript (7.1.4) and the npm advisories of October; Go
    1.26 is now the minimum toolchain. The web bundle's third-party license
    notices are tracked in the repository.

Reliability

  • Node group changes reach connected agents at once. Adding a node to
    a group, or removing it, now sends that agent a fresh policy snapshot:
    the group's policies apply (or are withdrawn and their changes undone)
    within seconds instead of at the agent's next reconnect.
  • Restart-safe server state. Pending enrollment tokens and the policy
    stream revision counter are stored in PostgreSQL, so a restart or upgrade
    no longer invalidates outstanding enrollment tokens, and an agent that
    reconnects after a server restart can no longer be served an incomplete
    policy delta. Only a SHA-256 hash of each enrollment token is stored.
  • Agent packages keep the service across upgrades. The rpm and deb
    packages stopped and disabled bor-agent on every upgrade (the
    preremove script ran unconditionally), so an upgraded node stayed
    without an agent after its next reboot. The scripts now disable the
    unit only on removal and restart an enrolled agent on the new binary
    after an upgrade; upgrading from an earlier package re-enables the unit
    once on nodes that hold an agent certificate.

Bug Fixes

  • KDE Plasma policies now take effect on real desktops. The agent
    writes the keys Plasma 5 and 6 actually read (Kiosk action, control
    module and URL restrictions included), merges several bound policies by
    binding priority instead of last-writer-wins, and installs a Plasma
    environment hook (zz-bor.sh, ordered after the distribution's own
    scripts so Fedora's defaults survive) that puts the managed directory
    into XDG_CONFIG_DIRS of every session. An enforced desktop layout
    sticks at every login, and a wallpaper file that is missing or not
    world-readable on the node is reported instead of silently ignored.
    Catalogue entries that Plasma 6 no longer reads are marked as Plasma 5 or
    KDE 4 only.
  • Firewall policies can start firewalld. The ensureEnabled field of
    a Firewall policy, present since 0.8.0 but inert, now makes the agent
    enable and start firewalld.service when it is installed but inactive.
  • PKCS#11 builds. A server built without -tags pkcs11 answers a
    PKCS#11 CA configuration with a clear "not compiled in" error instead of
    a generic failure, and the HSM support itself moved to the maintained
    community fork of crypto11.
  • Deleting a policy binding that does not exist answers 404 instead of 409.
  • The web UI no longer ships unreferenced static assets.

Platform & Dependency Updates

  • Go 1.26 is required to build the server and the agent.
  • Server: google.golang.org/grpc 1.83.2 -> 1.84.0, golang.org/x/crypto
    0.55.0 -> 0.58.0, golang.org/x/net 0.58.0 -> 0.62.0, golang.org/x/sys
    0.47.0 -> 0.49.0, golang.org/x/text 0.41.0 -> 0.43.0,
    google.golang.org/protobuf 1.36.11 -> 1.36.12, go-webauthn/webauthn
    0.16.3 -> 0.18.2, go-ldap/ldap 3.4.14 -> 3.4.15, lib/pq 1.11.2 ->
    1.12.3, prometheus/client_golang 1.23.2 -> 1.25.0. New:
    eclipse-keypont/crypto11 (PKCS#11, build tag pkcs11 only). Removed:
    yeqown/go-qrcode, fogleman/gg, golang/freetype, golang.org/x/image.
  • Agent: godbus/dbus 5.2.2 -> 5.2.4, google.golang.org/grpc 1.83.2 ->
    1.84.0 and the shared golang.org/x/* modules as above.
  • Frontend: PatternFly 6.4 -> 6.7.0, React 19.2.8 -> 19.3.1, react-router
    8.3.1 -> 8.5.0, @simplewebauthn/browser 13.3 -> 14.0, webpack 5.106 ->
    5.112, webpack-cli 7.0.1 -> 7.2.3, globals 15 -> 17, plus the
    transitive fast-uri and serialize-javascript fixes. The KDE catalogue
    is bundled into the UI by a generator (make kconfig-catalogue).
  • CI: actions/download-artifact v8, nfpm installed from its release
    tarball, a GitHub Pages pipeline for the documentation site; Dependabot
    ignores TypeScript major updates.
  • Metrics: new bor_luks_volumes_total, bor_luks_escrow_operations_total,
    bor_luks_recovery_key_reveals_total and
    bor_luks_recovery_keys_overdue_total; see the monitoring guide.

Upgrade Notes

  • Agents must be upgraded to v0.10.0 to enforce the Disk encryption,
    Services & timers, SELinux, AppArmor and Software updates policy types
    and the rebuilt KDE Plasma type; older agents ignore policy types they do
    not understand and report no MAC, updates or LUKS inventory. The deploy
    wizard rolls the upgrade out from the server itself.
  • Database migrations 000030 to 000046 run automatically on startup:
    000030 the disk encryption tables (luks_volumes,
    node_disk_encryption, luks_recovery_keys, tang_servers) and the
    disk_encryption:* and tang_server:* permissions; 000031 the
    persisted enrollment tokens and policy hub revision; 000032 removes the
    inert scoped role bindings and forbids new ones; 000033 the policy owner
    column (existing policies get their creator recorded where the account
    still exists); 000034 federated identities; 000035 externally managed
    user groups; 000036 SSO link approvals; 000037 and 000038 the KDE catalogue, node-reported
    settings and look-and-feel inventory; 000039 the systemd unit inventory;
    000040 and 000041 the systemd:exec and systemd:operate permissions;
    000042 the mandatory access control inventory (node_mac_status,
    node_mac_items, lsm columns on nodes); 000043 and 000044 the
    mac:operate and mac:author permissions; 000045 the Software updates
    inventory (node_updates_status, node_updates_pending); 000046 the
    updates:operate permission. The compliance report tables and
    permissions belong to the Bor Enterprise server and are created by it.
  • New server settings: BOR_ESCROW_KEK_FILE, BOR_ESCROW_KEK_ID,
    BOR_ESCROW_KEK_PREVIOUS_FILES, BOR_ESCROW_REQUIRE_MFA,
    BOR_ESCROW_RETIRED_RETENTION_DAYS and
    BOR_ESCROW_ORPHAN_RETENTION_DAYS. Disk encryption escrow stays off
    until a KEK file is configured. See .env.example and the server
    configuration reference.
  • Agent configuration gains the disk_encryption section (state_file,
    bootstrap_dir, run_dir, tool paths), new kconfig keys
    (data_path, probe_effective, flatpak_override, write_kde5rc),
    the systemd section (system_dir, preset_dir, scripts_dir,
    state_path, job_timeout, reconcile_interval, watch_signals,
    allow_tasks, analyze_binary, user_dir, user_preset_dir,
    allow_actions), the selinux section (config_path, modules_dir,
    module_priority, state_path, reconcile_interval,
    allow_mode_change, allow_custom_modules, follow_audit,
    tool_timeout), the apparmor section (profiles_dir,
    parser_binary, sysctl_path, state_path, backup_dir,
    reconcile_interval, allow_custom_profiles,
    allow_vendor_flag_edits, follow_audit, tool_timeout), the shared
    mac section (inventory_interval, denial_window, collect_denials,
    allow_actions) and the updates section (engine, state_path,
    backup_dir, reconcile_interval, inventory_interval,
    reboot_check_interval, tool_timeout, allow_install,
    allow_reboot); every key has a working default.
  • Agents report the node's kernel security module (lsm, lsm_mode) in
    the heartbeat; nodes running an older agent show no MAC value until
    they are upgraded.
  • Packaging: the agent's deb package now depends on needrestart, the rpm
    package recommends dnf5-plugins and python3-dnf-plugins-core, so a
    node can always tell whether an update needs a reboot; both recommend
    cryptsetup and suggest clevis-luks, clevis-dracut and tpm2-tools
    for disk encryption.
  • The protobuf policy schema gained disk_encryption.proto,
    systemd.proto, selinux.proto, mac.proto and updates.proto (policy
    fields 22 to 26), new stream event types for disk encryption tasks,
    systemd, MAC and updates actions, the lsm heartbeat fields and a much
    larger kconfig.proto; regenerate any external tooling built against
    proto/.
  • Packagers: the build command is unchanged (go build ./cmd/server, with
    -X main.Version=...), but needs Go 1.26. make server-pkcs11 builds
    the HSM variant with cgo.
  • API clients: the web UI signs in through POST /api/v1/auth/begin and
    /api/v1/auth/step; POST /api/v1/auth/login keeps working. Role-binding
    requests without scope_type (or with "global") behave as before; any
    other scope type is rejected with 400. Adding a role or a
    member to a user group answers 403 when it would grant permissions the
    caller does not hold. Deleting a policy binding that does not exist now
    answers 404 instead of 409.