rustsdcmcp

io.github.mechubsec由 GitHub 帳號 mechubsec 發布v0.6.0更新於 Oct 30, 2026

Juniper Security Director Cloud MCP server: scoped, audited change control. Rust.

概覽

AI 產生的概覽

社群版 MCP 伺服器,把 HPE Juniper Security Director Cloud 提供給助理,用於有範圍限制、可稽核的讀取與受變更管控的 SRX 原則操作。

可以做什麼
  • 讀取 SDC 清單與原則:列出並取得裝置、防火牆原則、NAT 原則及通用 SDC 資源,並查看租戶範圍
  • 查看變更與作業狀態:預覽與部署狀態、依裝置的預覽與部署結果、變更集詳情
  • 透過雙主體變更集流程準備、核准並套用原則部署
  • 讀取憑證、授權與範本,並把裝置清單同步進 SDC
  • 可作為本機 stdio 子程序執行(唯讀),或作為僅監聽回送位址的 streamable-HTTP 服務執行並使用具範圍的 bearer 權杖
適用情境
適合透過 Juniper Security Director Cloud 管理 SRX 裝置群的維運人員,讓助理檢視裝置與原則,或草擬原則變更並由第二位主體核准後再套用。定位為實驗或受控部署,而非日常桌面使用。
執行需求
需要本機執行環境:已發佈的 linux/amd64 容器映像、Debian 13 LXC 安裝包,或自行以 Rust 建置(工具鏈固定為 1.99.0)。需要 SDC 租戶 ID 與 SDC API 權杖,權杖透過 credential_env 指定的變數提供(範例中為 SDC_API_TOKEN);設定檔權限須為 0640 或更嚴格;需能透過 HTTPS 存取 SDC API 端點;寫入另需 streamable-HTTP 傳輸與具範圍的 bearer 權杖。
安裝前請注意
一次 SDC 動作可能影響多台受管 SRX 裝置,且部署會刪除 SDC 所擁有階層中、無法從已匯入原則到達的物件。寫入必須經過準備、核准、套用三步,在 stdio 下會被拒絕;萬用字元權杖範圍不授予任何寫入工具。SDC API 權杖與 bearer 權杖屬機密,不得寫入 JSON,並需受檔案權限保護。lab 模式會免除第二核准人,在重要環境中應保持關閉。本專案為非官方社群專案,與 HPE 或 Juniper 無隸屬關係。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 rustsdcmcp,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

[mechub mark]

rustsdcmcp

Async Rust Model Context Protocol server for HPE Juniper Security Director Cloud
a mechub project — sovereign network-security automation

Unofficial / community project. This is an independent community project and does not claim affiliation with or endorsement by Hewlett Packard Enterprise or Juniper Networks. Product names and trademarks are used only to identify the systems with which the software interoperates.


rustsdcmcp exposes HPE Juniper Security Director Cloud (SDC) to MCP clients as a bounded, auditable tool surface. SDC is the HPE Juniper SASE portal for an SRX estate, so this server talks to that management plane rather than to any single firewall. Where rustjunosmcp talks NETCONF to individual SRX devices, rustsdcmcp talks HTTPS REST to the portal that governs them.

That distinction has a large blast radius: one SDC action can affect many managed SRX devices. Strict approval, credential-safe attribution, and bounded I/O therefore protect the management plane rather than merely decorate it.

Current status

rustsdcmcp exposes 80 MCP tools (v0.6.0 and main; v0.3.1 shipped 78; v0.2.0 shipped 74): 60 bounded read tools and 20 change-control tools. The surface covers the part of the SDC API that manages SRX devices and their policy; the rest of the product's API is deliberately out of scope.

Every mutation is reachable only through prepare → independent approval → apply. A wildcard token scope deliberately grants no write tool, so each must be named explicitly when a token is minted.

Live validation against SDC has gone well beyond read-only. On 2026-08-07 a full prepare → approve → apply policy deploy ran against the lab tenant and reached a managed vSRX, which is how the co-management behaviour in #23 was discovered: within hierarchies SDC owns, objects not reachable from a policy SDC imported are deleted on deploy. Interfaces, routing, system, and the management instance were untouched. The full boundary and the observed diff are in docs/operations.md. JobStatus and DeviceStatusEntry are validated against live preview and deploy responses.

On 2026-08-12 the certificate and licence readers, BulkSyncDevices, and the template endpoints were exercised live. Device sync imports into SDC rather than pushing to the device, but reconciles inventory only and does not clear device_config_state: OUT_OF_BAND_CHANGED.

Read-path security properties were last verified live on 2026-08-07 against commit ea81805d2b4b97df9bdd3f70423e047524896a3d with mecmcp v0.5.0. The transport and scope preflight were replaced afterwards in e28d0cc and 369f9bb, so that result no longer speaks for current main; no lab SDC tenant was available to repeat it live. On 2026-10-06, against commit 2036523 with mecmcp v0.26.0, the automated integration suite was re-run instead — cargo test --workspace, 258 passed, 0 failed — which covers: requests with a missing bearer are refused (crates/rustsdcmcp/tests/http_boundary.rs::router_requires_bearer), out-of-scope tenants are refused (crates/rustsdcmcp/tests/http_boundary.rs::out_of_scope_tenant_is_refused), exact tool and tenant scope enforcement with read/write tool disjointness (crates/rustsdcmcp/src/server.rs scope unit tests, crates/rustsdcmcp/tests/tool_contract.rs), and secret-key redaction of responses (crates/rustsdcmcp-core/src/redact.rs unit tests). The remaining properties from the 2026-08-07 live audit have not been re-verified on current main; this run is not a substitute for the live-tenant exercise and does not re-establish one.

Observed response shapes and the remaining endpoint questions are tracked in docs/sdc-api/README.md.

Download

The current release, v0.6.0, attaches a checksummed, cosign-signed LXC install tarball (rustsdcmcp_0.6.0_amd64.tar.gz) as a release asset. Download and verify it, or build from approved source below.

A linux/amd64 container image is published for that tag to ghcr.io/mechubsec/rustsdcmcp:0.6.0 and is publicly pullable with no authentication.

Build from approved source

rust-toolchain.toml pins the build toolchain at 1.99.0; the declared MSRV in Cargo.toml is 1.89. Operators must bind their checkout to the commit they intend to ship before building, testing, or packaging — the release tag is the usual choice:

console
approved_commit=$(git rev-parse v0.6.0^{commit})git checkout --detach "$approved_commit"test "$(git rev-parse HEAD)" = "$approved_commit"cargo build --release --lockedcargo test --workspace --lockedscripts/build-package.shcp examples/sdc.example.json /secure/operator/path/sdc.json

In that configuration, credential_env names the external process variable; the credential itself never belongs in JSON. For a local commit-addressed package, verify the newly built archive and its embedded BUILD-INFO from the approved commit directory—never glob across dist/ because the checksum records only the archive basename:

console
artifact_dir="dist/$approved_commit"mapfile -t archives < <(find "$artifact_dir" -maxdepth 1 -type f -name 'rustsdcmcp_*_amd64.tar.gz' -print)test "${#archives[@]}" -eq 1archive="${archives[0]}"(cd "$artifact_dir" && sha256sum -c "$(basename "$archive").sha256")package_root=$(tar -tzf "$archive" | sed -n '1s#/.*##p')test -n "$package_root"tar -xOf "$archive" "$package_root/BUILD-INFO" | grep -Fx "git_commit=$approved_commit"

Local stdio client

--transport stdio (the default) runs the server as a child process of the MCP client, with no listening socket and no bearer token. examples/mcp.json.example is a .mcp.json entry for that. Copy examples/sdc.example.json to the --device-mapping path, fill in the tenant, and chmod 0600 it: the server refuses a config file more permissive than 0640. --state-file overrides the config's /var/lib/rustsdcmcp/changeset-state.json, which a desktop user cannot normally write.

The credential reaches the server through the variable that credential_env names (SDC_API_TOKEN in the example). ${SDC_API_TOKEN} is expanded from the client's environment, so the token stays out of the JSON file. A client that does not expand ${...} needs the variable exported in the environment it is launched from.

A stdio caller is unauthenticated, so every write tool (prepare, approve, apply) refuses it: stdio is a read-only surface. Change-controlled writes need the streamable-HTTP transport with a scoped bearer token.

Tool listing mode

--tool-mode full|compact (env MCP_TOOL_MODE, default full) controls what tools/list advertises. compact lists only list_sdc_resources, get_sdc_resource, get_sdc_tenant_scope and the change-set tools (get_sdc_change_set, get_sdc_change_set_details, approve_sdc_change_set, apply_sdc_change_set) to keep the client's tool context small. It is listing only: it composes with token scopes and never widens them, every tool stays callable, and the token's tool and tenant scopes still authorize each call.

Docker quick start

For running the published container image, see HOW-TO-SETUP-DOCKER.md.

Debian 13 LXC quick start

For building an LXC from scratch, see HOW-TO-SETUP-LXC.md.

Prerequisites:

  • Debian 13 AMD64; an unprivileged LXC is recommended.
  • 1 vCPU, 512 MiB RAM, 512 MiB swap, and 4 GiB disk for the minimum profile.
  • Working DNS and time synchronization, plus outbound HTTPS to api.sdcloud.juniperclouds.net.
  • Root or equivalent operator access inside the LXC.

Download the signed v0.6.0 release tarball (or build one from approved source — see Build from approved source above) and install the verified package:

bash
set -euo pipefailmapfile -t archives < <(find . -maxdepth 1 -type f -name 'rustsdcmcp_0.6.0_amd64.tar.gz' -print)test "${#archives[@]}" -eq 1archive="${archives[0]}"sha256sum -c "$archive.sha256"package_root=$(tar -tzf "$archive" | sed -n '1s#/.*##p')test -n "$package_root"tar -xzf "$archive"sudo "$package_root/packaging/lxc/install.sh"

Create the configuration and credential file without exposing a credential:

bash
sudo install -o root -g rustsdcmcp -m 0640 \  /etc/rustsdcmcp/sdc.json.example /etc/rustsdcmcp/sdc.jsonsudoedit /etc/rustsdcmcp/sdc.jsonsudo install -o root -g root -m 0600 /dev/null \  /etc/rustsdcmcp/credentials.envsudoedit /etc/rustsdcmcp/credentials.env

sdc.json must retain the HTTPS SDC endpoint, use the desired local tenant alias, and set expected_tenant_id to the operator-obtained SDC tenant ID. The credentials file contains one shell-compatible assignment using the name from credential_env, for example SDC_API_TOKEN=...; never put a real value in the README or JSON.

Create the initial exact 14-tool read-only grant. The redirect destination must already be mode 0600; the output is a one-time bearer token.

console
sudo /usr/local/bin/rustsdcmcp token add \  --tokens-file /etc/rustsdcmcp/tokens.json \  --device-mapping /etc/rustsdcmcp/sdc.json \  --name sdc-read \  --devices production \  --tools get_sdc_tenant_scope,list_sdc_devices,get_sdc_device,list_sdc_firewall_policies,get_sdc_firewall_policy,list_sdc_nat_policies,get_sdc_nat_policy,list_sdc_resources,get_sdc_resource,get_sdc_preview_status,get_sdc_deploy_status,get_sdc_preview_device_result,get_sdc_deploy_device_result,get_sdc_change_set \  --actor-type human > /secure/local/path/rustsdcmcp-sdc-read-token

Start the service and access it through an authenticated SSH tunnel:

console
sudo systemctl enable --now rustsdcmcp.servicesudo systemctl --no-pager --full status rustsdcmcp.servicesudo ss -ltnp 'sport = :30032'ssh -N -L 30032:127.0.0.1:30032 root@your-deployment-host

The expected listener is only 127.0.0.1:30032. While the tunnel is active, the local MCP client uses http://127.0.0.1:30032/mcp. Each installation supplies its own SSH host; the server is never exposed directly.

Security commitments

  • External credentials use restrictive file modes and never belong in JSON.
  • Startup verifies the configured identity against expected_tenant_id.
  • Bearer tokens carry exact tool and tenant scopes.
  • Request, response, and page sizes are bounded.
  • Audit attribution is credential-safe and target values receive HMAC redaction.
  • Mutations require two-principal prepare → approve → apply change control.
  • There is no direct deploy tool and no unauthenticated write path.
  • A wildcard token scope grants no write tool. --tools '*' yields the read surface only; every write tool must be named explicitly when minting a token.

Detailed deployment, recovery, audit-retention, and write-workflow guidance is in docs/operations.md.

--lab-mode

--lab-mode waives the second principal, for a single-operator lab where two-person control is theatre rather than a control. It is off by default and should stay off anywhere the estate matters.

What it does and does not change:

  • The waiver is applied automatically when the change set is created. There is no waive tool, and the flow stays prepare → apply, identical to production.
  • Planning, the plan digest, drift detection, and apply-time revalidation all still run. Lab mode removes the second reviewer, not the change record.
  • No approver is ever fabricated. A waived change set records approver: null alongside approval_waiver: "lab-mode", and carries a waiver digest over (change_set_id, plan_digest, owner, approved_at). It is cryptographically distinguishable from a genuine two-person approval and cannot be relabelled afterwards — which matters if anyone later has to prove which changes had real separation of duties.
  • The server warns loudly at startup whenever it is enabled.

If you want solo write-testing without waiving the control, mint two tokens with different names and use one to prepare and the other to approve: the principal is the token name, and self-approval is refused. That gives one person the complete lifecycle with the control intact, and is the better choice wherever the ceremony has any value.

Enabling it

Add the flag to the service unit. On a package install, use a drop-in rather than editing the shipped unit, so an upgrade does not silently drop it:

console
sudo systemctl edit rustsdcmcp

Replacing ExecStart means restating it in full, so copy the shipped command and append the flag rather than writing a shorter one. Dropping the --audit-* arguments would turn off HMAC target redaction and structured journald auditing as a side effect of enabling lab mode:

ini
[Service]# Clear the shipped ExecStart before replacing it; systemd appends otherwise.ExecStart=ExecStart=/usr/local/bin/rustsdcmcp \    --device-mapping /etc/rustsdcmcp/sdc.json \    --transport streamable-http \    --host 127.0.0.1 \    --port 30032 \    --tokens-file /etc/rustsdcmcp/tokens.json \    --audit-format json \    --audit-journald \    --audit-redact devices=hmac \    --audit-hmac-key-file /etc/rustsdcmcp/audit-hmac.key \    --lab-mode

Check it against packaging/systemd/rustsdcmcp.service before applying it — the shipped arguments are the authority, and this snippet is a copy that can age.

console
sudo systemctl daemon-reload && sudo systemctl restart rustsdcmcp

Confirm it took effect. The two startup records use different spellings — --lab-mode in the warning and lab_mode in the resolved-configuration line — so match both, and read the journal with enough privilege to see a system unit:

console
sudo journalctl -u rustsdcmcp -b | grep -E 'lab.mode'{"level":"WARN","fields":{"message":"--lab-mode: two-person control is DISABLED. …"}}{"level":"INFO","fields":{"message":"change-control configuration resolved","lab_mode":true,…}}

Silence means it is off. An unprivileged journalctl can also print nothing here for lack of access rather than because the flag is unset, which is why the command uses sudo.

A waived change set then reports "state": "approved" with "approver": null and "approval_waiver": "lab-mode" straight out of prepare_sdc_policy_deploy, and apply_sdc_change_set needs no separate approval call.

For a one-off run rather than a service, pass --lab-mode on the command line the same way.

Precedence

--lab-mode is part of mecmcp's shared change-set CLI standard, alongside --state-file and --approval-timeout-secs. For those other two, an explicitly supplied flag wins and sdc.json supplies the value otherwise (changeset_state_file, approval_ttl_secs).

--lab-mode is CLI-only. There is no sdc.json field for it, deliberately: a relaxed security control should have to be typed into the unit an operator can see, not inherited from a configuration file edited months ago. Whether the shared standard permits a product-config fallback is an open upstream question (mecmcp#267); this server takes the conservative reading.

Deployment maturity

The Debian 13 package has been installed and run end to end from the commit-addressed archive built by CI for ea81805d2b4b97df9bdd3f70423e047524896a3d. It starts under the packaged rustsdcmcp.service unit as a non-root account, binds a loopback-only endpoint, enforces the bearer boundary, and passes the startup tenant-scope check against live SDC.

One packaging limit is worth stating plainly: the unit's IPAddressAllow and IPAddressDeny lines take effect only where the host lets systemd attach its cgroup BPF program, which is runtime-dependent. On the validated deployment the installer probe reported NOT ENFORCED, so egress had to be enforced outside the unit; every other sandbox directive still applied. Follow the probe's result on your own host rather than assuming either way. Deployment, recovery, audit-retention, and the per-runtime egress mechanism are in docs/operations.md.

Specific hosts, addresses, and container identifiers are deliberately not published here; each operator supplies their own.

Roadmap

See ROADMAP.md for current coverage, planned expansion, and features blocked on upstream API support. Feature requests are welcome as GitHub issues.

Relationship to mecmcp

mecmcp is the vendor-neutral Rust foundation shared by the mechub MCP server family. This repository consumes it, rather than forking it. main pins all eight shared crates — mecmcp-audit, mecmcp-auth, mecmcp-changeset, mecmcp-redact, mecmcp-runtime, mecmcp-secret, mecmcp-server, and mecmcp-transport — to v0.31.0.

The compatibility blocker is cleared. Earlier revisions of this section said a release was blocked until 59 temporary compatibility declarations were replaced by one coherent upstream release. That happened: the local compat/ layer was deleted in #36 on the move to mecmcp 0.7.2, and the compatibility ledger itself was removed in 369f9bb on the move to 0.8.0. There are no temporary compatibility symbols left, and no ledger to track.

API provenance

The API surface is pinned from Juniper's OpenAPI 3 export, vendored in docs/sdc-api/. It is the authoritative inventory of the supported SDC API surface and its remaining open questions.

Primary references:

Audit forwarding to the event store

The audit trail does not stay on this host. This server follows the family standard — AUDIT-FORWARDING-STANDARD.md.

An audit record that only exists on the machine that produced it is not an audit trail: it is a log file on a box whose operator is the party the record is about.

Emission (in effect now)

--audit-format json \--audit-log-file /var/lib/rustsdcmcp/audit.jsonl

JSON is mandatory. The text format is for reading in a terminal and is not a parse target. The file is the operator-facing artifact and must be rotated — the server never truncates it.

Transport

Records are written directly into SSDF's ssdf.audit as hash-chained rows, per SSDF's merged evidence contract, so that deleting or editing a row is detectable. This is implemented: the server wires an EvidenceService/EvidenceHttpTransport pair at startup when evidence forwarding is configured (crates/rustsdcmcp/src/main.rs), landed as the SSDF evidence pipeline in mecmcp#292.

A cheaper syslog path was designed and rejected: it works, but the records are unchained, and every other link here is tamper-evident by construction — plan digests bind approvals, approvals name a distinct principal, and token_verified_fields separates vouched-for provenance from asserted. An unchained final hop would discard that guarantee exactly where an auditor needs it. The reasoning is recorded in the standard.

Reading the result

token_verified_fields names the provenance fields the token vouched for. The rest of that group — client_name, model_id, session_id — is client-asserted and authenticated by nothing. Do not read them as equivalent.

request_id correlates the transport event, the handler event, and (on Junos) the device commit comment.

License

Licensed under MIT.

來源:README.md,提交 3bb9e57

工具

0
SourceWeft 曾在隔離沙箱中以無憑證方式啟動該軟體包一次,以列出其工具,但未能完成:該軟體包類型無法在沙箱中執行。

版本歷史

4
  1. v0.6.0最新Oct 30, 2026
  2. v0.5.1Oct 29, 2026
  3. v0.4.1Oct 22, 2026
  4. v0.3.1Oct 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分