Skip to content

Releases: mechubsec/rustjunosmcp

v0.27.5

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 08 Oct 15:33
v0.27.5
113cd82

v0.27.5

rust-junosmcp v0.27.4

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 07 Oct 16:34
46f3568

Added

  • Official MCP Registry listing. The image now carries
    io.modelcontextprotocol.server.name="io.github.mechubsec/rustjunosmcp",
    which the registry uses to verify image ownership, and the repo ships a
    server.json describing the documented stdio container invocation.

Changed

  • Dependency and CI updates: distroless cc-debian13 base image digest,
    cargo-minor-patch group, and pinned mecmcp reusable workflows.
  • Docs: stdio container invocation and fwconfigsanitizer references.

rust-junosmcp v0.27.3

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 07 Oct 01:35
v0.27.3
062a0ca

rust-junosmcp v0.27.3

v0.27.2

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 30 Sep 20:05
v0.27.2
864ca6c

rust-junosmcp v0.27.2

v0.27.1

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 30 Sep 13:24
50fb7c2

rust-junosmcp v0.27.1

rustjunosmcp v0.27.0

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 07 Oct 15:02
883196e

Security

  • Redact device secrets from tool output. get_junos_config and
    junos_config_diff returned raw device configuration, including Junos
    $9$-style reversibly-encrypted values, PSKs, SNMP communities, and
    RADIUS/TACACS secrets, straight to the calling model (the README also
    wrongly called these values "hashed" — they are symmetrically encrypted
    and recoverable with the device's master key). Redaction is now applied
    at the two tool-specific call sites and, as a last-mile safety net, in a
    single call_tool post-processor covering every tool response, so a
    newly added tool cannot silently skip it. Tool descriptions and the
    README now say output is redacted (MEC-14).

Added

  • confirm_commit — new write tool that sends the confirming commit for
    a commit-confirmed window opened by load_and_commit_config,
    rollback_config, or render_and_apply_j2_template, cancelling the
    pending auto-rollback (MEC-45).

Changed

  • Behaviour change: commit-confirmed is on by default.
    load_and_commit_config, rollback_config (commit=true),
    render_and_apply_j2_template, and apply_junos_change_set now issue
    commit confirmed <window> unless the caller passes
    confirm_timeout_mins: 0. Previously these committed directly with no
    auto-rollback net unless the caller opted in. The window defaults to the
    new --commit-confirm-default-mins flag (default 10 minutes); a per-call
    confirm_timeout_mins overrides it. The opt-out is recorded in the audit
    event as commit_confirmed=false. The response and change-set status both
    report rollback_deadline_unix (MEC-45).
  • --ssh-accept-new-host-keys now gives real TOFU for NETCONF SSH, not
    no-verification-at-all.
    Previously the flag pinned scp's known_hosts
    entries on first contact (HostKeyVerification::AcceptNew) but set NETCONF
    SSH to HostKeyVerification::AcceptAll — an operator reading the flag name
    had no reason to expect that NETCONF connections were left completely
    unverified. NETCONF now uses AcceptNew too: an unknown host's key is
    pinned on first contact, and a host that later presents a different key
    is refused, on both the scp and NETCONF paths, against the same
    known_hosts file (MEC-44).
    Behaviour change for anyone relying on the old flag for key rotation:
    a device that rotates its host key (reimage, RE swap) will now be refused
    on reconnect instead of being silently re-trusted. Re-run
    scripts/scan-known-hosts.sh, or delete the stale line from
    known_hosts, after a legitimate rotation.
  • New --ssh-insecure-accept-any-host-key flag, lab-only, carries the
    old blanket HostKeyVerification::AcceptAll behavior for NETCONF SSH under
    an honestly-named opt-in. Mutually exclusive with
    --ssh-accept-new-host-keys. Logged loudly at startup and recorded as an
    audit event.
  • Container images now publish to ghcr.io/mechubsec/rustjunosmcp —
    the repo moved to the mechubsec organization, and images are renamed to
    match. Older tags were copied from the previous name.

Fixed

  • --ssh-insecure-accept-any-host-key now also disables host-key
    verification for scp (transfer_file / upgrade_junos), not just
    NETCONF SSH.
    The MEC-44 landing (above) left scp on AcceptNew (TOFU)
    under this flag, so a device presenting a changed key was still refused
    over scp despite the flag's name promising to accept any key. Both
    transports now share one SshHostKeyMode end-to-end
    (Strict / AcceptNew / AcceptAll), so --ssh-insecure-accept-any-host-key
    means the same thing on both paths.
  • srx_flow_sessions fails closed on two more summary-parsing gaps
    (MEC-745): a node reporting no error but also no recognised session-count
    element no longer has its missing count silently papered over by the
    other nodes' totals, and an early warning-severity rpc-error on a node
    can no longer mask a later error-severity one on the same node. Both
    previously risked understating the true session count.

Full changes: v0.26.0...v0.27.0

Backfilled GitHub release for an existing tag (2026-10-07).

rustjunosmcp v0.26.0

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 07 Oct 15:02
d434745

Added

  • --allow-direct-commit, off by default. load_and_commit_config, a
    committing render_and_apply_j2_template, rollback_config, and
    upgrade_junos stage, validate, and commit a device change in one call with
    no change set and no second-principal review. Without the flag, all four
    are refused before the device is touched, identically over stdio and HTTP.
    With it, the server logs loudly at startup and every call is audited
    (direct_commit_allowed=true; a refusal is audited too). See
    --allow-direct-commit in the README.

  • approve_junos_change_set now requires a human approver. The mecmcp
    dependency's ChangesetCoordinator::approve_change_set gained an
    approver_actor_type argument and refuses anything but
    mecmcp_audit::ActorType::Human — an agent or an unattributed caller could
    already never propose and approve the same change set, but nothing
    previously stopped it from standing in as the second principal.

  • get_junos_config gains a format parameter (text default, set,
    xml, or json), rendered device-side via the matching Junos
    | display <format> CLI modifier — the same mechanism
    execute_junos_command already passes through untouched. Output flows
    through the same policy check, XML-wrapper stripping, and output-cap
    pipeline as the existing text format; there is no new unredacted path.
    An unrecognized format is rejected before any device connection.

  • The load tools gain a mode parameter (merge default, replace, or
    override) controlling the wire action for <load-configuration>:
    load_and_commit_config, render_and_apply_j2_template, and the
    change-set payload spec (create_junos_change_set's actions[].payload).
    override replaces the entire candidate configuration — the highest
    blast-radius operation this server exposes — and is refused outright on
    load_and_commit_config and render_and_apply_j2_template, which commit
    directly with no second-principal review. It is permitted only through
    create_junos_change_set → approve_junos_change_set →
    apply_junos_change_set, which requires approval by a principal distinct
    from the creator before anything commits (MEC-12). config_format=set
    (configuration-set payloads) has no wire-level override action in Junos;
    that combination is rejected before any RPC is sent regardless of path.

  • Raised MSRV to 1.89 (family-wide decision; enables mecmcp to drop aes pin)

  • Two new read-only SRX tools: srx_flow_sessions, srx_policy_match.
    srx_flow_sessions queries active flow sessions with hard-capped result
    limits and node-aware summaries; srx_policy_match performs deterministic
    5-tuple policy matching against both zone-pair and global policies,
    returning the device's own verdict. Tool count: 37 → 39.

  • Four new read-only SRX tools: srx_list_policies, srx_resolve_address,
    srx_resolve_application, srx_list_nat_rules.
    Security policies by
    zone pair (incl. global policies and optional hit counts), address-book and
    application/application-set resolution (including junos-* predefined
    defaults, with recursive nested-set resolution and explicit cycle
    rejection), and source/destination/static NAT rules. Address-book and
    application resolution are configuration-sourced via a hand-built
    subtree-filtered get-configuration RPC. Names on policies and NAT rules
    are returned unresolved by design — use srx_resolve_address /
    srx_resolve_application to expand them. Tool count: 39 → 43.

  • Release image and tarball are now signed keylessly with cosign via
    GitHub Actions OIDC (no key pair, ever). The Release image workflow signs
    the pushed image by digest; a new Sign release tarball workflow signs the
    LXC tarball once it is attached to a published GitHub release. See the
    README's "Verifying the image signature" and "Downloading a prebuilt release
    tarball instead" sections for cosign verify / verify-blob recipes.

Fixed

  • cSRX devices now work. rustez 0.18 tolerates cSRX rejecting
    <get-route-engine-information/> during fact gathering
    (mechubsec/rustez#54). Previously every tool call on a cSRX device failed
    with [OperationFailed] syntax error.

Full changes: v0.25.0...v0.26.0

Backfilled GitHub release for an existing tag (2026-10-07).

rustjunosmcp v0.25.0

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 07 Oct 15:02
d047a6e

Added

  • execute(operation, arguments) is an additive 37th MCP tool over the
    existing 36 concrete Junos and SRX operations. It accepts only an exact
    concrete operation name; it never repairs names, translates argument keys,
    or supplies defaults. The selected concrete handler receives the original
    argument object, so its existing typed validation remains authoritative.
    Direct calls to all original 36 tools remain supported.

Changed

  • Facade authorization is deliberately dual-scoped. An authenticated HTTP
    token must explicitly grant both execute and the selected concrete tool;
    wildcard scope does not grant execute. The same preflight also applies the
    caller's device scope to every nested device selector before dispatch.

  • execute is conservatively write-capable. It is excluded from wildcard
    tool scope, because it can dispatch a write-capable concrete operation.

  • Facade audit records retain both layers of attribution. HTTP transport
    records the outer execute request, while the concrete handler emits the
    correlated operation record with the same request id; malformed and rejected
    facade calls remain bounded and do not log raw nested arguments.

Full changes: v0.24.1...v0.25.0

Backfilled GitHub release for an existing tag (2026-10-07).

rustjunosmcp v0.24.1

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 07 Oct 15:02
99a04c4

Changed

  • Shipped systemd unit now carries the fleet seccomp posture (mecmcp#354).
    Added a comment documenting why SystemCallErrorNumber=EPERM is load-bearing:
    without it, systemd's default applies and a denied syscall raises SIGSYS, killing
    the process mid-request (mecmcp#351). The comment also clarifies that EPERM denials
    are silent at the systemd layer — seccomp return actions have a precedence order
    and ERRNO outranks LOG, so such denials can only become visible if the application
    stops discarding the errno.

Fixed

  • Flaky test race in timeout_budget_tests. Both tests mutated the same
    process-global CLEANUP_TIMEOUT_SECS and cargo ran them in parallel, so whichever
    test lost the race would read the value the other had just reset, causing spurious
    failures. Added a static mutex that both tests hold for their entire duration to
    serialize them.

Full changes: v0.24.0...v0.24.1

Backfilled GitHub release for an existing tag (2026-10-07).

rustjunosmcp v0.24.0

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 07 Oct 15:02
e2a0b85

Added

  • A crashed commit is now re-probed against the device at startup (#370).
    commit_operation persists the caller's attribution -- including request_id -- immediately before
    the commit RPC is sent, and format_attribution puts that same id into the Junos commit comment as
    request.id=<uuid>. A process that dies between sending the commit and recording its result therefore
    leaves behind a record naming something the device can be asked about. On startup the server now asks:
    for every operation ChangesetCoordinator::load left Indeterminate that carries an attribution, it
    reads the device's commit log and settles the record Committed if the id is there.

    The selection is Indeterminate plus an attribution, not Committing. load rewrites every
    non-terminal operation to Indeterminate before any sweep could observe it, and attribution is written
    nowhere but commit_operation -- so its presence is what separates a crash during the commit from a
    crash during staging, which reaches the same state with no attribution.

    Only a hit settles anything. Junos keeps a bounded commit history, so an entry can age out, and a
    device can simply be unreachable at boot. Neither is evidence the commit did not happen, so both leave
    the record Indeterminate for state resolve. The sweep never writes Failed.

    The commit-log hit settles the commit; the lock flag is cleared only when lock freedom was proven.
    Finding the id in the log proves the commit landed, but not that the candidate lock was released — the
    process may have died between the commit and the unlock. So after finding a hit, the sweep probes lock
    freedom by taking and releasing the lock, matching the abandon path. config_lock_held is cleared only
    when that probe succeeds; if the lock cannot be taken, cannot be confirmed returned, or the probe times
    out or errors, the record is still settled Committed (the commit is proven) but the flag remains set
    and the details note that the lock state could not be verified.

    A confirmed commit is not settled either. Finding the id proves the provisional commit entered the
    log, not that its rollback was ever cancelled; settling it Committed would make the record terminal,
    freeing the device for an apply whose commit would cancel a rollback still armed, and would report as
    permanently live a change the device may yet revert. Junos marks these on the entry's header
    (commit confirmed, rollback in Nmins), so the match groups the log into entries rather than searching
    it flat, and a deadline already on the record is honoured even when the log is ambiguous.

    Known gap: a settle writes the record directly and emits no SSDF result_receipt, so a crash after the
    apply intent was spooled leaves that evidence chain unterminated even once the device has confirmed the
    outcome. Tracked separately.

    The sweep is detached rather than awaited: a candidate on an unreachable device costs 20s, longer than
    the readiness budget the test harness and package smoke allow, so blocking startup on it would turn a
    recoverable record into a server killed and retried on every boot. Nothing is lost by serving first --
    a non-terminal operation already blocks a new one on its device, so these records keep gating writes
    until the sweep settles them. Probing is bounded-concurrent (4 at a time) and the candidate order is
    rotated each run, so a prefix of unreachable devices in the coordinator's stable map order cannot
    starve a reachable candidate behind them -- either within one sweep or across every restart. 20s per
    device, 120s for the sweep. A provisional finding is reported but deliberately not written back: doing so
    would mean writing Indeterminate and the old deadline over a record a confirm_junos_change_set may
    have settled in between, un-confirming a commit the operator did confirm, and mecmcp has no
    compare-and-update for operations to make that check atomic. No rollback deadline is invented either: reconstructing one from the device's clock would be
    the same kind of unobserved claim the rest of this refuses to make. Because the sweep now runs alongside live traffic, the settle
    path re-reads the record and skips it if anything settled it while the probe was in flight; that path
    is safe from the remaining window by construction, since committed is only reached for a record that
    carried no deadline and so writes exactly what a concurrent confirm would have written. The candidate
    rotation is seeded per process rather than from the clock, because a wall-clock modulo is periodic --
    60 records restarted daily lands on 86400 % 60 == 0 and would reselect the same slow prefix forever.

    Verified on hardware (vSRX 24.4R1.9), not just offline: a real change-set commit was confirmed to stamp
    the full untruncated request.id= into the device's commit log; a record flipped to Indeterminate was
    settled Committed from that log after a restart; an id the device had never seen was left untouched;
    and an unreachable device timed out at 20s with the record untouched and the server still starting.

Fixed

  • get_junos_change_set_status now distinguishes a live commit from one the device reverted (#384).
    apply_junos_change_set with confirm_timeout_mins issues a Junos confirmed commit, which the
    device rolls back unless confirm_junos_change_set arrives before the deadline. Status reported
    applied either way. The cause is where Applied is written: apply_change_set sets it as soon as
    staging succeeds, before the commit is issued, and nothing rewrites it afterwards -- so the
    change-set state is settled before the device has been asked to do anything.

    The verdict now comes from the linked operation record, reached through ChangeSetRecord::operation_id:
    awaiting_confirmation while a rollback deadline is still in the future, presumed_auto_reverted
    once it passes with no confirmation recorded, committed once the operation reaches Committed, and
    discarded when one was reconciled by state resolve.

    A recorded deadline is the only evidence this server holds that a confirmed commit was issued, so it
    decides whether a verdict is owed at all, and it outlives a manual resolve. resolve_persisted_operation
    settles the state but leaves rollback_deadline_unix in place, and state resolve ... AS COMMITTED
    does not stop the device: reporting committed there would hide a rollback still counting down.
    AS DISCARDED is an operator saying the change is not on the device, which a timer only reinforces.
    An AS COMMITTED record whose window has since closed reports indeterminate rather than guessing:
    state resolve neither stops the device nor records when it ran, so that shape is either a confirm
    made out of band and then reconciled -- the remedy for a confirming commit whose durable write failed
    -- or a reconciliation made early on a timer that then fired. committed would hide a revert and
    presumed_auto_reverted would contradict a human who looked at the device, so the tool reports the
    uncertainty and sends the reader to the device.
    An operation with no deadline that is not Committed gets no verdict at all -- an ordinary failed
    apply terminalizes as Discarded, and that is not a confirmed-commit outcome.
    The response also carries operation_id, operation_state and rollback_deadline_unix so a reader
    can see the basis. All additions are additive -- no existing field or argument name changed.

    presumed_auto_reverted is a presumption, not an observation. It is the absence of a recorded
    confirmation, and it is wrong if a confirming commit was issued out of band on the device, if the
    server and device clocks disagree, or if confirm_junos_change_set reached the device but its
    durable write failed. Only the device settles it. Likewise committed cannot separate a confirmed
    commit from a plain one, because confirming clears the deadline -- both mean the configuration is
    live, which is the distinction that matters.

    The derivation keys on the deadline rather than on LifecycleState::Committing, because a restart
    rewrites every non-terminal operation to Indeterminate while leaving the deadline intact. Since the
    Junos rollback belongs to the device rather than the session, that window is still open and still
    confirmable; keying on Committing dropped the deadline from the response at exactly the moment an
    operator needed it.

Full changes: v0.23.0...v0.24.0

Backfilled GitHub release for an existing tag (2026-10-07).