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 分