v0.0.6 - Tier-1 TCP listener + FIPS deployment documentation
This commit is contained in:
93
documents/qubes_client_examples.md
Normal file
93
documents/qubes_client_examples.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# qubes_client_examples.md
|
||||
|
||||
This document shows minimal caller-qube examples for invoking `nsigner --listen qrexec` through Qubes qrexec.
|
||||
|
||||
For complete protocol details (framing, JSON-RPC, error handling), see `documents/CLIENT_IMPLEMENTATION.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1) Shell example (`qrexec-client-vm` + framed JSON)
|
||||
|
||||
This sends one `get_public_key` request and decodes one framed response.
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
TARGET_QUBE="nsigner-vault"
|
||||
SERVICE="qubes.NsignerRpc"
|
||||
REQ='{"id":"1","method":"get_public_key","params":[]}'
|
||||
|
||||
python3 - "$TARGET_QUBE" "$SERVICE" "$REQ" <<'PY'
|
||||
import json
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
target, service, req_json = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
frame = struct.pack(">I", len(req_json.encode("utf-8"))) + req_json.encode("utf-8")
|
||||
|
||||
p = subprocess.Popen(
|
||||
["qrexec-client-vm", target, service],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
out, _ = p.communicate(frame)
|
||||
if p.returncode != 0:
|
||||
raise SystemExit(f"qrexec-client-vm failed: {p.returncode}")
|
||||
if len(out) < 4:
|
||||
raise SystemExit("short response (missing frame header)")
|
||||
|
||||
n = struct.unpack(">I", out[:4])[0]
|
||||
payload = out[4:4+n]
|
||||
if len(payload) != n:
|
||||
raise SystemExit("short response payload")
|
||||
|
||||
print(json.dumps(json.loads(payload.decode("utf-8")), indent=2))
|
||||
PY
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) Python example (explicit frame helpers over qrexec stdio)
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import struct
|
||||
import subprocess
|
||||
|
||||
|
||||
def frame_encode(obj: dict) -> bytes:
|
||||
payload = json.dumps(obj, separators=(",", ":")).encode("utf-8")
|
||||
return struct.pack(">I", len(payload)) + payload
|
||||
|
||||
|
||||
def frame_decode(buf: bytes) -> dict:
|
||||
if len(buf) < 4:
|
||||
raise ValueError("missing frame header")
|
||||
n = struct.unpack(">I", buf[:4])[0]
|
||||
payload = buf[4:4 + n]
|
||||
if len(payload) != n:
|
||||
raise ValueError("short frame payload")
|
||||
return json.loads(payload.decode("utf-8"))
|
||||
|
||||
|
||||
def call_nsigner_qrexec(target_qube: str, request: dict) -> dict:
|
||||
proc = subprocess.Popen(
|
||||
["qrexec-client-vm", target_qube, "qubes.NsignerRpc"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
out, err = proc.communicate(frame_encode(request))
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(f"qrexec failed ({proc.returncode}): {err.decode('utf-8', 'replace')}")
|
||||
return frame_decode(out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
req = {"id": "1", "method": "get_public_key", "params": []}
|
||||
resp = call_nsigner_qrexec("nsigner-vault", req)
|
||||
print(json.dumps(resp, indent=2))
|
||||
```
|
||||
Reference in New Issue
Block a user