diff --git a/README.md b/README.md index 731ca2d..6317bc1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,19 @@ # source-os -This is the main SociOS Linux SourceOS Repo. + +This is the main SociOS Linux SourceOS repository. + +## Role + +`source-os` is the Linux realization home for the SourceOS control-plane stack. It carries host roles, profiles, images, builders, and Linux-facing integration surfaces that realize the AgentPlane contract on Linux hosts. + +## Current surfaces + +- `docs/repository-layout.md` — repository shape and intent +- `docs/agentplane-integration.md` — contract boundary with AgentPlane and shared standards +- `docs/mesh/` — mesh Linux estate integration planning, path-template mapping, and staged workstreams +- `linux/` — concrete Linux-facing templates for systemd-networkd, NetworkManager, nftables, and helper units +- `profiles/` / `modules/` — Nix realization surfaces + +## Boundary rule + +Shared schemas and canonical vocabulary belong in `SocioProphet/socioprophet-agent-standards`, not here. diff --git a/docs/mesh/README.md b/docs/mesh/README.md new file mode 100644 index 0000000..32a65d6 --- /dev/null +++ b/docs/mesh/README.md @@ -0,0 +1,15 @@ +# Mesh Linux Estate Integration + +This directory carries the Linux-native realization material for the mesh work inside `source-os`. + +Placement rule: +- Linux estate realization lives here. +- Shared schemas and canonical capability vocabulary live in `SocioProphet/socioprophet-agent-standards`. +- Control-plane lifecycle and promotion semantics live in `SocioProphet/agentplane`. + +Contents: +- `mesh_linux_estate_upstream_plan.md` — Linux-native integration plan and role split +- `traffic_class_path_template_matrix.md` — traffic classes, path templates, and Linux policy mapping +- `upstream_workstreams.md` — staged upstream workstream plan + +Companion Linux-facing templates live under `linux/`. diff --git a/docs/mesh/mesh_linux_estate_upstream_plan.md b/docs/mesh/mesh_linux_estate_upstream_plan.md new file mode 100644 index 0000000..860216c --- /dev/null +++ b/docs/mesh/mesh_linux_estate_upstream_plan.md @@ -0,0 +1,79 @@ +# Mesh Linux Estate Upstream Plan + +## Goal + +Integrate the mesh into a Linux estate in a way that is upstream-friendly, operationally sane, and survivable under hostile or degraded networks. + +The design separates four concerns: +1. control-plane provenance and admission +2. transport reachability +3. traffic-class-specific data paths +4. egress and censorship-resistance edge behavior + +## Linux-native constraints + +Release 1 should avoid inventing a bespoke in-kernel anonymous network stack. + +Use existing Linux primitives for what they already do well: +- WireGuard for the encrypted underlay +- rtnetlink / generic netlink / iproute2 for link and route control +- nftables for exit NAT and policy enforcement +- systemd-networkd and NetworkManager as first-class estate front-ends +- systemd units, credentials, and local sockets for lifecycle and secret delivery + +Do not put onion routing, mix scheduling, rendezvous mailboxes, or trust policy into the kernel in Release 1. + +## Privilege split + +Use separate processes instead of one omnipotent daemon: + +### `meshd` +Unprivileged control-plane daemon for identity, enrollment, capability manifests, operator API, and replayable audit data. + +### `meshd-linkd` +Privileged network mutator for WireGuard interfaces, peers, route tables, rules, and fwmarks. + +### `meshd-exitd` +Privileged edge helper for forwarding, nftables NAT/filter policy, and exit-role health/accounting. + +### `mesh-ptd` / bridge helper +Optional user-space censorship-resistance edge for anonymous ingress, bridge integration, or pluggable transports. + +## Estate split + +### Headless servers and relays +Prefer `systemd-networkd` for server roles, exits, relays, and appliances. + +### Workstations and roaming devices +Prefer NetworkManager for laptops and operator-facing devices because it already handles Wi-Fi, roaming, DNS policy, and WireGuard profiles well. + +### Mixed estate rule +Do not force one network manager everywhere. Treat manager choice as a role decision. + +## Release-1 scope + +### In scope +- cryptographic node identity +- signed capability manifests +- direct plus relay-assisted WireGuard underlay +- route policy via fwmark and dedicated route tables +- optional exit role +- Linux templates, units, and packaging notes +- observability and replayable control-plane records + +### Out of scope +- kernel-resident mixnet or onion routing +- global cover traffic by default +- bespoke kernel modules +- full Tor clone inside the Linux realization repo + +## Repo placement in `source-os` + +- `docs/mesh/` — design and rollout docs +- `linux/systemd-networkd/` — networkd templates +- `linux/systemd/` — service units for helper daemons +- `linux/networkmanager/` — NM profile templates +- `linux/nftables/` — exit policy templates +- `linux/packaging/` — packaging notes and future spec stubs + +Shared schemas belong in `SocioProphet/socioprophet-agent-standards`, not here. diff --git a/docs/mesh/traffic_class_path_template_matrix.md b/docs/mesh/traffic_class_path_template_matrix.md new file mode 100644 index 0000000..eb01628 --- /dev/null +++ b/docs/mesh/traffic_class_path_template_matrix.md @@ -0,0 +1,89 @@ +# Traffic-Class / Path-Template Matrix + +The mesh should not pretend that all traffic shares one path discipline. Different traffic classes need different routing semantics. + +## Traffic classes + +### Admission +Purpose: invites, introducer handshakes, first-contact rendezvous, peer metadata provisioning. + +Preferred path: +- wormhole-like or introducer-mediated channel +- short-lived +- never a generic exit path + +Linux expression: +- local `meshd` control socket +- ordinary outbound host networking until enrollment completes + +### Control-plane replication +Purpose: capability manifest updates, revocation, policy distribution, anti-entropy. + +Preferred path: +- direct mesh first +- 2-hop microcascade fallback when observation risk rises + +Linux expression: +- table 100 +- fwmark `0x100` + +### Private service traffic +Purpose: RPC, replication, admin operations, internal APIs. + +Preferred path: +- direct mesh +- relay fallback +- optional microcascade when policy demands operator or jurisdiction separation + +Linux expression: +- table 110 +- fwmark `0x110` + +### Full-tunnel exit traffic +Purpose: public internet egress, remote IP locality, public-Wi-Fi posture. + +Preferred path: +- direct trusted exit for low-risk traffic +- microcascade to exit for medium-risk traffic + +Linux expression: +- table 120 +- fwmark `0x120` +- catch-all routes plus nftables NAT on the exit node + +### Interactive anonymous traffic +Purpose: sessions needing anonymity rather than only transport privacy. + +Preferred path: +- onion rail +- bridge or pluggable-transport entry when required + +Linux expression: +- local proxy or helper process +- table 130 +- fwmark `0x130` + +### Async high-risk traffic +Purpose: delayed messaging, store-and-forward, control replication under active observation. + +Preferred path: +- mix rail only +- not the default for ordinary traffic + +Linux expression: +- user-space app or gateway path first +- table 140 only if emitted as IP + +## Path templates + +- `P0-direct` — single peer path over the WireGuard underlay +- `P1-relay` — one relay for reachability, not anonymity +- `P2-microcascade` — two or three constrained hops from policy-approved nodes +- `P3-onion` — low-latency layered anonymity path +- `P4-mix` — delayed metadata-resistance path for high-risk asynchronous traffic only + +## Default stance + +- servers use `P0` for service traffic and `P2` only where policy requires harder separation +- laptops use `P0` when trusted and `P2` or `P3` when roaming or censored +- exits must not self-select as both client and exit in the same risk context diff --git a/docs/mesh/upstream_workstreams.md b/docs/mesh/upstream_workstreams.md new file mode 100644 index 0000000..44935af --- /dev/null +++ b/docs/mesh/upstream_workstreams.md @@ -0,0 +1,28 @@ +# Upstream Workstreams + +## Workstream A — zero-kernel-delta MVP +Ship entirely in user space. +Use existing WireGuard, existing netlink, existing nftables, and existing network managers. + +This is the default and preferred path. + +## Workstream B — Linux estate integration +Deliver: +- systemd units +- systemd-networkd templates +- NetworkManager profile or dispatcher integration +- nftables policy templates +- packaging notes and future package specs +- SELinux/AppArmor policy later + +## Workstream C — censorship-resistance edge +Deliver separately: +- Tor bridge or pluggable-transport helper +- optional anonymous ingress +- workstation-focused packaging + +## Workstream D — mix rail +Defer until the first three workstreams are stable. + +## Rule of thumb +If a feature can remain in user space without harming correctness or performance materially, keep it in user space. diff --git a/examples/nmcli/harden-existing-wireguard-profile.sh b/examples/nmcli/harden-existing-wireguard-profile.sh new file mode 100644 index 0000000..785bfb1 --- /dev/null +++ b/examples/nmcli/harden-existing-wireguard-profile.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Harden and classify an existing NetworkManager WireGuard profile for full-tunnel exit use. +# Usage: +# ./harden-existing-wireguard-profile.sh wg-mesh + +NAME="${1:-wg-mesh}" + +nmcli connection modify "$NAME" ipv4.route-table 120 ipv6.route-table 120 +nmcli connection modify "$NAME" wireguard.ip4-auto-default-route yes +nmcli connection modify "$NAME" wireguard.ip6-auto-default-route yes +nmcli connection modify "$NAME" +ipv4.routing-rules "priority 10020 fwmark 0x120 table 120" +nmcli connection modify "$NAME" +ipv6.routing-rules "priority 10020 fwmark 0x120 table 120" +nmcli connection modify "$NAME" ipv4.ignore-auto-dns yes ipv6.ignore-auto-dns yes +nmcli connection modify "$NAME" ipv4.dns-priority -32768 ipv6.dns-priority -32768 +nmcli connection modify "$NAME" ipv4.never-default no ipv6.never-default no + +echo "Profile '$NAME' updated. Review with:" +echo " nmcli -f connection.id,ipv4.route-table,ipv6.route-table,wireguard.ip4-auto-default-route,wireguard.ip6-auto-default-route,ipv4.dns-priority,ipv6.dns-priority connection show $NAME" diff --git a/linux/networkmanager/wg-mesh.nmconnection b/linux/networkmanager/wg-mesh.nmconnection new file mode 100644 index 0000000..cd9b01c --- /dev/null +++ b/linux/networkmanager/wg-mesh.nmconnection @@ -0,0 +1,33 @@ +[connection] +id=wg-mesh +type=wireguard +interface-name=wg-mesh +autoconnect=false + +[ipv4] +method=manual +address1=10.77.0.10/32 +route-table=120 +ignore-auto-dns=true +dns-priority=-32768 +never-default=false +routing-rule1=priority 10020 fwmark 0x120 table 120 + +[ipv6] +method=manual +address1=fd77:77::10/128 +route-table=120 +ignore-auto-dns=true +dns-priority=-32768 +never-default=false +routing-rule1=priority 10020 fwmark 0x120 table 120 + +[wireguard] +private-key=__PRIVATE_KEY_BASE64__ +fwmark=0 +ip4-auto-default-route=true +ip6-auto-default-route=true +peer-routes=true +peers=__EXIT_PUBLIC_KEY_BASE64__ endpoint=203.0.113.20:51820 allowed-ips=0.0.0.0/0;::/0 persistent-keepalive=25 + +[proxy] diff --git a/linux/nftables/mesh-exit.nft b/linux/nftables/mesh-exit.nft new file mode 100644 index 0000000..64ce0a3 --- /dev/null +++ b/linux/nftables/mesh-exit.nft @@ -0,0 +1,20 @@ +#!/usr/sbin/nft -f +# Stateful NAT and forwarding policy for an exit node. + +flush table inet mesh_exit +table inet mesh_exit { + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept + iifname "mesh0" oifname "eth0" ip saddr 10.77.0.0/16 accept + iifname "mesh0" oifname "eth0" ip6 saddr fd77:77::/48 accept + iifname "eth0" oifname "mesh0" ct state established,related accept + } + + chain postrouting { + type nat hook postrouting priority srcnat; policy accept; + oifname "eth0" ip saddr 10.77.0.0/16 masquerade + # Add NAT66 only if policy truly requires it. + # oifname "eth0" ip6 saddr fd77:77::/48 masquerade + } +} diff --git a/linux/packaging/README.md b/linux/packaging/README.md new file mode 100644 index 0000000..4de0f66 --- /dev/null +++ b/linux/packaging/README.md @@ -0,0 +1,22 @@ +# Packaging Notes + +Recommended package split: + +- `meshd` +- `meshd-linkd` +- `meshd-exitd` +- `meshctl` +- `meshd-networkd` +- `meshd-networkmanager` +- `meshd-nftables` +- `meshd-selinux` (optional) +- `meshd-apparmor` (optional) +- `meshd-onion` (optional) +- `meshd-mix` (optional) + +Deb/RPM/Nix packaging should converge on the same runtime surfaces: +- `/etc/meshd/` +- `/usr/libexec/` +- `/usr/lib/systemd/system/` +- `/usr/lib/systemd/network/` or `/etc/systemd/network/` +- `/usr/share/doc/meshd/` diff --git a/linux/systemd-networkd/50-mesh0.netdev b/linux/systemd-networkd/50-mesh0.netdev new file mode 100644 index 0000000..3471701 --- /dev/null +++ b/linux/systemd-networkd/50-mesh0.netdev @@ -0,0 +1,31 @@ +# /etc/systemd/network/50-mesh0.netdev +# Conservative example template for a mesh underlay interface. +# Adapt addresses, keys, ports, and routing policy to your estate. + +[NetDev] +Name=mesh0 +Kind=wireguard +Description=Mesh underlay interface + +[WireGuard] +PrivateKey=@mesh0 +ListenPort=51820 +FirewallMark=0x110 +RouteTable=110 +RouteMetric=50 + +[WireGuardPeer] +PublicKey=__PEER1_PUBLIC_KEY_BASE64__ +PresharedKey=@mesh0-peer1-psk +AllowedIPs=10.77.0.2/32, fd77:77::2/128 +Endpoint=198.51.100.10:51820 +PersistentKeepalive=25 + +# Optional full-tunnel peer / exit role on the client side: +# [WireGuardPeer] +# PublicKey=__EXIT_PUBLIC_KEY_BASE64__ +# AllowedIPs=0.0.0.0/0, ::/0 +# Endpoint=203.0.113.20:51820 +# PersistentKeepalive=25 +# RouteTable=120 +# RouteMetric=10 diff --git a/linux/systemd-networkd/50-mesh0.network b/linux/systemd-networkd/50-mesh0.network new file mode 100644 index 0000000..64735be --- /dev/null +++ b/linux/systemd-networkd/50-mesh0.network @@ -0,0 +1,29 @@ +# /etc/systemd/network/50-mesh0.network +# Matching .network file for mesh0. + +[Match] +Name=mesh0 + +[Network] +Address=10.77.0.1/32 +Address=fd77:77::1/128 +LinkLocalAddressing=no +IPv6AcceptRA=no +LLDP=no +ConfigureWithoutCarrier=yes +IgnoreCarrierLoss=yes + +[Route] +Destination=10.90.0.0/16 +Table=110 +Metric=50 + +[RoutingPolicyRule] +FirewallMark=0x110 +Table=110 +Priority=10010 + +[RoutingPolicyRule] +FirewallMark=0x120 +Table=120 +Priority=10020 diff --git a/linux/systemd/meshd-exitd.service b/linux/systemd/meshd-exitd.service new file mode 100644 index 0000000..52cb78c --- /dev/null +++ b/linux/systemd/meshd-exitd.service @@ -0,0 +1,34 @@ +[Unit] +Description=Mesh exit helper +After=network-online.target meshd-linkd.service +Wants=network-online.target +Requires=meshd-linkd.service + +[Service] +Type=simple +User=root +Group=root +ExecStart=/usr/libexec/meshd-exitd --config /etc/meshd/exits.toml --nft /etc/meshd/nftables/mesh-exit.nft +Restart=on-failure +RestartSec=2s + +NoNewPrivileges=yes +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=strict +ProtectHome=yes +ProtectControlGroups=yes +ProtectKernelLogs=yes +ProtectClock=yes +LockPersonality=yes +MemoryDenyWriteExecute=yes +RestrictSUIDSGID=yes +RestrictRealtime=yes +RestrictNamespaces=yes +SystemCallArchitectures=native +RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW + +[Install] +WantedBy=multi-user.target diff --git a/linux/systemd/meshd-linkd.service b/linux/systemd/meshd-linkd.service new file mode 100644 index 0000000..3eefc37 --- /dev/null +++ b/linux/systemd/meshd-linkd.service @@ -0,0 +1,34 @@ +[Unit] +Description=Mesh privileged link helper +After=meshd.service +Requires=meshd.service + +[Service] +Type=notify +User=root +Group=root +RuntimeDirectory=meshd +ExecStart=/usr/libexec/meshd-linkd --rpc unix:/run/meshd/linkd.sock --control unix:/run/meshd/meshd.sock +Restart=on-failure +RestartSec=2s + +NoNewPrivileges=yes +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=strict +ProtectHome=yes +ProtectControlGroups=yes +ProtectKernelLogs=yes +ProtectClock=yes +LockPersonality=yes +MemoryDenyWriteExecute=yes +RestrictSUIDSGID=yes +RestrictRealtime=yes +RestrictNamespaces=yes +SystemCallArchitectures=native +RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW + +[Install] +WantedBy=multi-user.target diff --git a/linux/systemd/meshd.service b/linux/systemd/meshd.service new file mode 100644 index 0000000..a5b1454 --- /dev/null +++ b/linux/systemd/meshd.service @@ -0,0 +1,40 @@ +[Unit] +Description=Mesh control-plane daemon +Documentation=man:meshd(8) +After=network-online.target +Wants=network-online.target + +[Service] +Type=notify +User=meshd +Group=meshd +StateDirectory=meshd +RuntimeDirectory=meshd +ConfigurationDirectory=meshd +ExecStart=/usr/libexec/meshd --config /etc/meshd/meshd.toml --listen unix:/run/meshd/meshd.sock +Restart=on-failure +RestartSec=2s + +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes +PrivateDevices=yes +ProtectControlGroups=yes +ProtectKernelLogs=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +ProtectClock=yes +ProtectHostname=yes +LockPersonality=yes +MemoryDenyWriteExecute=yes +RestrictSUIDSGID=yes +RestrictRealtime=yes +RestrictNamespaces=yes +SystemCallArchitectures=native +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +CapabilityBoundingSet= +AmbientCapabilities= + +[Install] +WantedBy=multi-user.target