diff --git a/docs/faq.html b/docs/faq.html index 75c903a..188ec56 100644 --- a/docs/faq.html +++ b/docs/faq.html @@ -151,6 +151,7 @@ sudo journalctl -u peardock -n 80 --no-pager | grep -i 'public key' and copy the full string starting with pd1.. The operator pastes that entire string in Add peer. Public key and HMAC capability are embedded — no Autopass/RocksDB and no seed sharing. Invites can be persistent (default) or limited by TTL / max uses. Revoke peers from Access when devices are lost. + Deep dive: Security model.

Container list empty / “permission denied” on Docker

diff --git a/docs/index.html b/docs/index.html index 6ea8569..679af8e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -132,8 +132,8 @@ Hardening -

Security & threat model

-

Assets, trust boundaries, ACL, residual risks, operator checklist.

+

Security model

+

Admin seed proof, pd1 invites, roles, revoke, threat model, checklist.

Reference diff --git a/docs/operator.html b/docs/operator.html index b28c07d..645113a 100644 --- a/docs/operator.html +++ b/docs/operator.html @@ -237,6 +237,10 @@ PEARDOCK_AUDIT=1 +

+ Full model (handshake elevation, capability format, redeem vs reconnect, revoke): + Security model. +

Registry vault

Credentials encrypted at rest (AES-GCM derived from SERVER_SEED):

diff --git a/docs/security.html b/docs/security.html index 3e74f5a..b26a079 100644 --- a/docs/security.html +++ b/docs/security.html @@ -4,8 +4,8 @@ Security · PearDock Docs - - + + @@ -28,7 +28,7 @@ - + @@ -39,7 +39,7 @@ - + @@ -65,7 +65,7 @@ "TechArticle" ], "name": "Security \u00b7 PearDock Docs", - "description": "PearDock threat model, trust boundaries, controls, residual risks, hardening checklist.", + "description": "PearDock security model: admin seed proof, viewer default, pd1 invites, roles, revoke, threat model, and hardening.", "url": "https://peardock.boats/docs/security", "isPartOf": { "@type": "WebSite", @@ -103,13 +103,243 @@
-

Security & threat model

+

Security model

- Audience: operators deploying PearDock in multi-operator or semi-trusted environments. - Scope: HyperDHT P2P control plane + local Docker Engine socket. + How PearDock authenticates peers: keys in .env, default viewer, + admin seed proof, pd1. invites, roles on every RPC method, and revoke. + Also: threat model summary and hardening checklist.

-

1. Assets

+ + +

1. Overview

+

+ PearDock is a self-hosted P2P Docker control plane. There is no central login. + Anyone who knows the server public key can attempt a HyperDHT dial. + That alone must not mean full Docker control — so the default role is viewer (read-only). +

+
[Desktop client] --Noise/HyperDHT--> [peardock-server] --unix socket--> [dockerd]
+                                          |
+                                          +-- /opt/peardock/.env
+                                          +-- peardock-peers.json
+                                          +-- peardock-vault.json
+                                          +-- peardock-audit.log
+

Three legitimate ways in:

+
+ + + + + + + + + +
Paste in Add peerAccess
Public key only (SERVER_PUBLIC_KEY)Viewer (read-only)
Public key + SERVER_SEEDAdmin (seed never sent on the wire)
Full pd1.… inviteRole in the invite — no seed sharing
+
+

+ Canonical long-form doc in the source tree: + docs/SECURITY_AUTH.md. +

+ +

2. Keys live in .env

+

+ After the first successful start, identity is written to + /opt/peardock/.env (mode 600). Check that file: +

+
sudo grep -E '^(SERVER_PUBLIC_KEY|SERVER_SEED)=' /opt/peardock/.env
+# or: sudo cat /opt/peardock/.env
+
+ + + + + + + + + + + + + + + + +
VariableWhoNotes
SERVER_PUBLIC_KEYSafe to share for read-onlyHow clients dial the server over HyperDHT
SERVER_SEEDAdmins onlyServer identity + HMAC MAC key + vault material. Never give to operators.
+
+

+ From the seed, PearDock derives a MAC key (HKDF). That key signs admin proofs and capability grants. + The seed itself is never transmitted on the wire. + Rotating the seed changes the public key; all clients must re-add the peer. +

+

+ Each desktop client also keeps a stable DHT keypair (peer id). + The server uses that peer id for registration, reconnect, allowlist, and revoke. +

+ +

3. Roles

+

Order: viewer < operator < admin.

+

+ Every RPC method has a minimum role (MethodRoles). Unknown methods default to + admin (fail closed). The UI may hide buttons; the server is authoritative. +

+
+ + + + + + + + + + + + + + + + + + +
RoleTypical powers
viewerList/inspect, logs, stats. No mutate. Cannot manage invites or ACL.
operatorDay-to-day Docker mutate (start/stop, pull, stacks, many Swarm ops, terminals). Still cannot mint invites.
adminEverything + invite/revoke, vault, destructive prune paths, unrestricted CLI if enabled.
+
+

+ Invite management is admin-only: + invitePeer, listInvites, deleteInvite, + revokePeer, unrevokePeer, clearRevokedPeers. +

+ +

4. Handshake elevation

+

Baseline role (usually viewer) is resolved from env / admin keys / peer policy. Then handshake may elevate:

+
    +
  1. adminProof — seed ownership → admin
  2. +
  3. capability — HMAC grant from a pd1. invite (or direct token)
  4. +
  5. Otherwise stay at baseline
  6. +
+
+
!
+
+ Fail closed. If the client presents a capability that is invalid, spent, or expired, + and the peer is not already registered as elevated, the handshake fails. + It does not silently succeed as viewer. +
+
+

+ If a capability fails but this client identity is already registered as operator/admin, + reconnect can use the registered role so restarts keep working. +

+

+ Optional: PEARDOCK_ADMIN_KEYS (fixed admin peer ids), + PEARDOCK_DEFAULT_ROLE, PEARDOCK_PEER_ALLOWLIST=1, + PEARDOCK_INSECURE_OPEN_ADMIN=1 (dev only — everyone is admin). +

+ +

5. Admin seed proof

+

Goal: prove “I know SERVER_SEED” without sending the seed over the network.

+
    +
  1. In Add peer, paste public key + SERVER_SEED (from .env).
  2. +
  3. Client derives the same MAC key and builds an HMAC over + peardock-admin-v1 || nonce || peerId || serverPublicKey.
  4. +
  5. Server verifies with its seed-derived key (constant-time compare).
  6. +
  7. Success → role admin. Failure → ADMIN_PROOF_FAILED.
  8. +
+

+ Best practice: only true admins hold the seed. + Onboard everyone else with invites. Prefer PEARDOCK_ADMIN_KEYS for fixed admin machines + if you do not want to paste the seed on every client. +

+ +

6. pd1 invites & capabilities

+

+ Invites do not use Autopass or RocksDB. They are pure HMAC capability grants + packaged as a self-contained share string. +

+

Capability token

+
base64url(JSON payload) + "." + base64url(HMAC-SHA256(macKey, payload))
+

Payload includes role, jti (unique grant id), exp (or null = never), optional peer bind, and issued-at.

+

pd1 package

+
pd1.<base64url JSON { publicKeyHex, capability, role, jti, ... }>
+

+ Operators paste the full string in Add peer (never truncate). + The client decodes public key + capability and dials with the grant at handshake. +

+

Mint (admin)

+
    +
  1. Connect as admin (key + seed).
  2. +
  3. Access → Create invite (role usually operator).
  4. +
  5. Default: never expires, unlimited uses (persistent). Optionally set TTL / max uses.
  6. +
  7. Copy the full pd1. string and share it (not the seed).
  8. +
+

Redeem (operator)

+
    +
  1. Paste full pd1.… in Add peer.
  2. +
  3. Client dials embedded public key and presents the capability.
  4. +
  5. Server verifies HMAC, then registers the client peer id with the grant role.
  6. +
+
+
i
+
+ Delete invite vs kick a person. + Deleting an invite stops new redemptions of that grant id. + An already-registered operator keeps reconnecting until you + revoke their peer id. +
+
+ +

7. Registration, reconnect, revoke

+

State lives in peardock-peers.json (override with PEARDOCK_PEER_POLICY):

+ +

+ After a successful elevate, reconnect can use the registered role even if that invite jti is later deleted. + To remove access for a lost laptop: Access → revoke that peer. + Unrevoke / clear revoked are admin recovery tools. +

+

+ Common error codes: CAPABILITY_INVALID, CAPABILITY_EXPIRED, + CAPABILITY_SPENT, ADMIN_PROOF_FAILED, PERMISSION_DENIED. + If a saved peer still holds a stale spent capability, paste a new full pd1 invite + or clear the cached grant. +

+ +

8. Workflows

+

Single admin

+
    +
  1. Install server; read keys from /opt/peardock/.env.
  2. +
  3. Add peer with public key + SERVER_SEED → admin.
  4. +
+

Multi-operator (recommended)

+
    +
  1. Admin connects with seed proof.
  2. +
  3. Access → Create invite → share full pd1. string only.
  4. +
  5. Operator pastes invite → operator role; peer registered.
  6. +
  7. Lost device → revoke peer id. Leaked unused invite → delete invite, mint new one.
  8. +
+

Viewer guest

+

Share only SERVER_PUBLIC_KEY. Observe-only.

+ +

9. Threat model (summary)

+

Assets

@@ -119,37 +349,21 @@ - +
SERVER_SEEDCritical: identity and vault key derivation
Docker socket accessCritical: full host container control
Registry passwords (vault)High: encrypted at rest
HMAC capability grants / pd1. invitesMedium: elevated access without seed (TTL / max uses optional)
HMAC capability grants / pd1. invitesMedium: elevated access without seed
Audit logMedium: forensic integrity
Container data / env secretsHigh: via inspect, logs, exec
- -

2. Trust boundaries

-
[Client] --Noise/HyperDHT--> [peardock server] --unix socket--> [dockerd]
-                                  |
-                                  +-- peardock-vault.json (AES-GCM)
-                                  +-- peardock-peers.json
-                                  +-- peardock-audit.log
- - -

3. Adversaries

+

Adversaries

    -
  1. Remote peer with public key only: gets viewer only (read-only lists/inspect/logs).
  2. -
  3. Stolen pd1. invite / capability: limited by TTL / max uses + HMAC; revoke peer after redeem.
  4. -
  5. Compromised client: can use any role the peer holds until revoke.
  6. -
  7. Local host attacker with filesystem: can steal seed and vault if file perms are wrong — keep /opt/peardock/.env mode 600.
  8. -
  9. Malicious container: out of scope for PearDock. Engine isolation applies.
  10. +
  11. Public key only: viewer (read-only).
  12. +
  13. Stolen pd1 invite: can elevate until delete/spend/expire; revoke peer after redeem.
  14. +
  15. Compromised client: holds that role until revoke.
  16. +
  17. Local filesystem on server: can steal seed if .env perms are wrong.
  18. +
  19. Malicious container: out of scope for PearDock; Engine isolation applies.
- -

4. Controls (implemented)

+

Controls

@@ -158,42 +372,47 @@ - + - - - - - - - + + + + +
Transport E2EHyperDHT Noise
Capability ACLviewer / operator / admin + MethodRoles
Default roleviewer; elevate via seed HMAC or pd1. invite
Default roleviewer; elevate via seed HMAC or pd1 invite
Admin proofHMAC-SHA256 from SERVER_SEED (seed never on the wire)
pd1. invitesSelf-contained public key + capability share string (no vault DB)
Peer policyRegister, revoke, capability jti spend, optional allowlist
AuditAppend-only log for privileged methods
Rate limitPer-peer limiter on RPC
Registry secretsAES-256-GCM vault
Browse FSRoot allowlist / default deny
Tunnel targetsLoopback / allowlisted hosts only
pd1 invitesSelf-contained public key + capability (no vault DB)
Peer policyRegister, revoke, jti spend, optional allowlist
Audit / rate limitPrivileged method log; per-peer limiter
Registry vaultAES-256-GCM
Browse FS / tunnelsDefault-deny browse; loopback / allowlisted tunnel targets
- -

5. Residual risks

+

Residual risks

-

6. Operator hardening checklist

+

10. Operator hardening checklist

+

+ Install and multi-op steps: + Operator guide · + Quick start · + RPC & API +

+
Previous← Holesail