feat(device-edge): add canonical core channel deployment

This commit is contained in:
Codex
2026-08-11 20:00:58 +03:00
parent 1124c15216
commit 6461e7fca8
8 changed files with 877 additions and 14 deletions
@@ -29,11 +29,11 @@ const runtimeCache = resolve(
const [phase, patchId, ...extra] = process.argv.slice(2);
if (
extra.length
|| !["foundation", "backhaul", "relay"].includes(phase)
|| !["foundation", "backhaul", "relay", "core-channel"].includes(phase)
|| !/^[A-Za-z0-9._-]{1,96}$/.test(patchId || "")
) {
throw new Error(
"usage: build-device-edge-vps-artifact.mjs <foundation|backhaul|relay> <patch-id>",
"usage: build-device-edge-vps-artifact.mjs <foundation|backhaul|relay|core-channel> <patch-id>",
);
}
@@ -72,6 +72,17 @@ const entriesByPhase = {
"services/device-edge-relay/src",
"deployment/device-edge-vps-relay-v1.json",
],
"core-channel": [
"packages/device-protocol-contract/package.json",
"packages/device-protocol-contract/src",
"packages/device-edge-channel-contract/package.json",
"packages/device-edge-channel-contract/src",
"services/device-edge-channel/package.json",
"services/device-edge-channel/src",
"vps/config/nftables-core-channel.conf",
"vps/systemd/nodedc-device-edge-channel.service",
"deployment/device-edge-vps-core-channel-v1.json",
],
};
const entries = entriesByPhase[phase];
const ignoredBasenames = new Set([".DS_Store", ".git", "node_modules"]);
@@ -129,7 +140,11 @@ try {
size: bytes.length,
component: "device-edge-vps",
entries,
publicIngress: phase === "relay" ? "tcp/9921" : "disabled",
publicIngress: phase === "relay"
? "tcp/9921"
: phase === "core-channel"
? "tcp/8443-mtls-only"
: "disabled",
commandTransport: "disabled",
gelios: "untouched",
}, null, 2));
@@ -221,6 +236,34 @@ async function assertBoundary() {
}
}
}
if (phase === "core-channel") {
for (const required of [
"\"runtimeUser\": \"nodedc-channel\"",
"\"trackerIngress\": \"disabled\"",
"User=nodedc-channel",
"node --jitless",
"tcp dport 8443",
"MemoryMax=128M",
"MemorySwapMax=0",
"CPUQuota=50%",
"TasksMax=64",
"LimitNOFILE=1024",
]) {
if (!combined.includes(required)) {
throw new Error(`core_channel_boundary_missing:${required}`);
}
}
for (const forbidden of [
"tcp dport 9921",
"LocalForward",
"tailscale-userspace",
"DEVICE_EDGE_RELAY_UPSTREAM",
]) {
if (combined.includes(forbidden)) {
throw new Error(`core_channel_boundary_violation:${forbidden}`);
}
}
}
}
function canonicalTarScript() {
+315 -9
View File
@@ -44,6 +44,8 @@ BACKHAUL_USER = "nodedc-backhaul"
BACKHAUL_GROUP = "nodedc-backhaul"
RELAY_USER = "nodedc-relay"
RELAY_GROUP = "nodedc-relay"
CHANNEL_USER = "nodedc-channel"
CHANNEL_GROUP = "nodedc-channel"
TAILSCALE_REQUIRED_TAG = "tag:device-edge-vps"
MANAGEMENT_KEY_FINGERPRINT = (
"SHA256:DYYy1E3DaxIQGC0jnsW6SP7gXdBHUy3A1zn4pvgVUEw"
@@ -89,6 +91,14 @@ NFTABLES_CONFIG = Path("/etc/nftables.conf")
TAILSCALE_UNIT = Path("/etc/systemd/system/nodedc-b2-tailscaled.service")
BACKHAUL_UNIT = Path("/etc/systemd/system/nodedc-b2-backhaul.service")
RELAY_UNIT = Path("/etc/systemd/system/nodedc-b2-relay.service")
CHANNEL_UNIT = Path("/etc/systemd/system/nodedc-device-edge-channel.service")
CHANNEL_TRUST_ROOT = Path("/var/lib/nodedc-b2-vps/channel-trust")
CHANNEL_PRIVATE_KEY = CHANNEL_TRUST_ROOT / "edge-private-key.pem"
CHANNEL_CERTIFICATE = CHANNEL_TRUST_ROOT / "edge-certificate.pem"
CHANNEL_CORE_CERTIFICATE = CHANNEL_TRUST_ROOT / "core-certificate.pem"
CHANNEL_RUNTIME_CONFIG = CHANNEL_TRUST_ROOT / "runtime.json"
CHANNEL_HEALTH_PORT = 18222
CHANNEL_PUBLIC_PORT = 8443
FOUNDATION_ENTRIES = (
"vps/config/00-nodedc-b2-vps.conf",
@@ -109,11 +119,23 @@ RELAY_ENTRIES = (
"services/device-edge-relay/src",
"deployment/device-edge-vps-relay-v1.json",
)
CORE_CHANNEL_ENTRIES = (
"packages/device-protocol-contract/package.json",
"packages/device-protocol-contract/src",
"packages/device-edge-channel-contract/package.json",
"packages/device-edge-channel-contract/src",
"services/device-edge-channel/package.json",
"services/device-edge-channel/src",
"vps/config/nftables-core-channel.conf",
"vps/systemd/nodedc-device-edge-channel.service",
"deployment/device-edge-vps-core-channel-v1.json",
)
PHASE_ENTRIES = {
"foundation": FOUNDATION_ENTRIES,
"backhaul": BACKHAUL_ENTRIES,
"relay": RELAY_ENTRIES,
"core-channel": CORE_CHANNEL_ENTRIES,
}
SUPERSEDED_TRANSPORT_PHASES = frozenset({"backhaul", "relay"})
@@ -121,19 +143,19 @@ SUPERSEDED_TRANSPORT_PHASES = frozenset({"backhaul", "relay"})
PHASE_FILE_SHA256 = {
"foundation": {
"vps/config/00-nodedc-b2-vps.conf":
"cc94d0579f85d0af9746b9ce760bc72980f4a22fb59027e1f5f9c7bf3aaebd64",
"2079748f48b2297ecb067e16ae46248e3a981b26331aa0998dc14fdf7454cd4a",
"vps/config/nftables-foundation.conf":
"4d44f902d8d98d1aa8506fca9d9582e700f6424def2b1d667ab2cd5a5ee84934",
"bce5b8c6e2226d47d8d322f8ee9e7158f2d7a553c5e2c4bc2a722d6067f46e28",
"vps/systemd/nodedc-b2-tailscaled.service":
"de147d29bc1759f56533d31058993df55e1200973f2899903bdbf3d13ff579da",
"deployment/device-edge-vps-foundation-v1.json":
"317c98b42520fff3238275908482de7aa611b4ee41c6b1f8062abd2730ab072a",
"5ae007195b17d6cfaeb564abbed6dc9234eb36bf62db23bf2b9ace539c398898",
f"vendor/{NODE_ARCHIVE}": NODE_ARCHIVE_SHA256,
f"vendor/{TAILSCALE_ARCHIVE}": TAILSCALE_ARCHIVE_SHA256,
},
"backhaul": {
"vps/config/backhaul_ssh_config":
"ff1a3575b5a55a56b08a8642aa820d1f8e5f07e0e7698fdd0e01a142452674d4",
"d0df8b70dda025b7c1c3fecd2dafffe60a5bb753650d3bc37db65db626cfc1af",
"vps/systemd/nodedc-b2-backhaul.service":
"64c26cad21cc17675c67ae4a57fc43b129065a8d22fda894648340b310d3aa8c",
"deployment/device-edge-vps-backhaul-v1.json":
@@ -141,7 +163,7 @@ PHASE_FILE_SHA256 = {
},
"relay": {
"vps/config/nftables-relay.conf":
"d99290bf825a3aff2ad8f9dbc6502ba6c32295f0cabd3f71c3d9b94c06373259",
"497485a2fb1b79faa95eb67ca30a89a3b65c3d4d8ddcb14cd1dc9acfeb2bb6f2",
"vps/systemd/nodedc-b2-relay.service":
"12a927a4cb42016229ac438f6e75969e1bf015037f2b5b0a60ab0591bdf424d2",
"services/device-edge-relay/src/runtime.mjs":
@@ -151,6 +173,28 @@ PHASE_FILE_SHA256 = {
"deployment/device-edge-vps-relay-v1.json":
"cb3c2fff4878021783efef4f4a4d1ec31c8e3d8e6325fe33657fec22bec20165",
},
"core-channel": {
"packages/device-protocol-contract/package.json":
"19d0d07da0341e8c2e8d3485400767566b18f6270245f0e9a9681c95013ba3b7",
"packages/device-protocol-contract/src/index.mjs":
"21a8b2b85a807899f946387c7976eaffcdc438b7db5e3d41fc3930f4437f0ec7",
"packages/device-edge-channel-contract/package.json":
"57d5349b5dcef2cacd4f3e4fad010359a65d59f5f903eff07d89f67c497f97c0",
"packages/device-edge-channel-contract/src/index.mjs":
"09d45e6104779212605fed650544794aafe1c2b1ab66f33f07bef4d92f430c80",
"services/device-edge-channel/package.json":
"bdf502be43b62bdd6db05b022a532d93ba954277ac5143d6058d2f27f6a2e9d2",
"services/device-edge-channel/src/runtime.mjs":
"cbb07f7e644a68e9c8c36c1c2ecf0224c06c46c08c49339d62637b10a1495501",
"services/device-edge-channel/src/server.mjs":
"ea891634a18efb9eb44f17b56c95ba97527215c4d0a6147cc2b7bad1d7356e36",
"vps/config/nftables-core-channel.conf":
"1a04a5450042e80b8a20da3c6634dd6bc68693f191a463ba9a62984279d81d0c",
"vps/systemd/nodedc-device-edge-channel.service":
"57c6c0c5eb952e1e196f50c410a8537468af145941b21dd4aad6ff0e8ca56249",
"deployment/device-edge-vps-core-channel-v1.json":
"938f6f7959b78e6be3a6e91a54dca2922fbd813f1a33dca5cebe0dc256a83a14",
},
}
@@ -452,7 +496,7 @@ def current_phase_preflight(phase: str):
die("VPS foundation live/runtime root already exists")
if any(path.exists() for path in (SSHD_DROPIN, TAILSCALE_UNIT, BACKHAUL_UNIT, RELAY_UNIT)):
die("VPS foundation system path already exists")
for port in (1055, 18221, 19921, 9921):
for port in (1055, 18221, CHANNEL_HEALTH_PORT, 19921, 9921, CHANNEL_PUBLIC_PORT):
assert_port_closed(port)
return {"predecessor": "clean-ubuntu-24.04.4"}
@@ -462,6 +506,19 @@ def current_phase_preflight(phase: str):
require_running_tailnet=phase in {"backhaul", "relay"},
expected_key_user=BACKHAUL_USER if phase == "relay" else SERVICE_USER,
)
if phase == "core-channel":
if service_active("nodedc-b2-backhaul.service"):
die("frozen VPS backhaul service must remain inactive")
if service_active("nodedc-b2-relay.service"):
die("frozen VPS relay service must remain inactive")
if CHANNEL_UNIT.exists() or (LIVE_ROOT / CORE_CHANNEL_ENTRIES[-1]).exists():
die("VPS Core channel target path already exists")
if user_exists(CHANNEL_USER):
die("VPS Core channel runtime user already exists")
assert_channel_trust(require_runtime_owner=False)
for port in (CHANNEL_HEALTH_PORT, CHANNEL_PUBLIC_PORT, 9921):
assert_port_closed(port)
return {"predecessor": "accepted-foundation-closed-channel"}
if phase == "backhaul":
for tool in (Path("/usr/bin/ssh"), Path("/usr/bin/nc")):
assert_executable_command_path(
@@ -529,6 +586,8 @@ def backup_targets_for_phase(phase: str):
return common + [SSHD_DROPIN, NFTABLES_CONFIG, TAILSCALE_UNIT]
if phase == "backhaul":
return common + [BACKHAUL_UNIT, BACKHAUL_KNOWN_HOSTS]
if phase == "core-channel":
return common + [CHANNEL_UNIT, NFTABLES_CONFIG, CHANNEL_TRUST_ROOT]
return common + [RELAY_UNIT, NFTABLES_CONFIG]
@@ -568,7 +627,7 @@ def create_backup(patch_id: str, phase: str):
"serviceUserExisted": user_exists(),
"serviceUsersExisted": {
name: user_exists(name)
for name in (SERVICE_USER, BACKHAUL_USER, RELAY_USER)
for name in (SERVICE_USER, BACKHAUL_USER, RELAY_USER, CHANNEL_USER)
},
"services": {
name: {
@@ -582,6 +641,7 @@ def create_backup(patch_id: str, phase: str):
"nodedc-b2-tailscaled.service",
"nodedc-b2-backhaul.service",
"nodedc-b2-relay.service",
"nodedc-device-edge-channel.service",
)
},
}
@@ -740,6 +800,146 @@ def assign_backhaul_trust(account):
os.chmod(BACKHAUL_KNOWN_HOSTS, 0o444)
def certificate_fingerprint(path: Path):
output = run([
"/usr/bin/openssl",
"x509",
"-in",
str(path),
"-noout",
"-fingerprint",
"-sha256",
]).stdout.strip()
prefix = "sha256 Fingerprint="
if not output.lower().startswith(prefix.lower()):
die("VPS certificate fingerprint output is invalid")
fingerprint = output.split("=", 1)[1].upper()
if not re.fullmatch(r"(?:[A-F0-9]{2}:){31}[A-F0-9]{2}", fingerprint):
die("VPS certificate fingerprint is invalid")
return fingerprint
def assert_channel_trust(*, require_runtime_owner: bool):
directory = assert_directory_nonsymlink(
CHANNEL_TRUST_ROOT,
"Core channel trust root",
)
expected_uid = 0
expected_gid = 0
if require_runtime_owner:
account = pwd.getpwnam(CHANNEL_USER)
expected_uid = account.pw_uid
expected_gid = account.pw_gid
if (
directory.st_uid != expected_uid
or directory.st_gid != expected_gid
or (directory.st_mode & 0o777) != 0o700
):
die("Core channel trust root ownership/mode mismatch")
for path, maximum, mode in (
(CHANNEL_PRIVATE_KEY, 32 * 1024, 0o400),
(CHANNEL_CERTIFICATE, 32 * 1024, 0o444),
(CHANNEL_CORE_CERTIFICATE, 32 * 1024, 0o444),
(CHANNEL_RUNTIME_CONFIG, 32 * 1024, 0o444),
):
state = assert_regular_nonsymlink(path, f"Core channel trust {path.name}")
if state.st_size < 1 or state.st_size > maximum:
die(f"Core channel trust file size mismatch: {path.name}")
if (
state.st_uid != expected_uid
or state.st_gid != expected_gid
or (state.st_mode & 0o777) != mode
):
die(f"Core channel trust file ownership/mode mismatch: {path.name}")
private_text = CHANNEL_PRIVATE_KEY.read_text(encoding="ascii")
public_text = (
CHANNEL_CERTIFICATE.read_text(encoding="ascii")
+ CHANNEL_CORE_CERTIFICATE.read_text(encoding="ascii")
)
if "PRIVATE KEY" not in private_text or "PRIVATE KEY" in public_text:
die("Core channel private/public trust boundary mismatch")
for path in (
CHANNEL_CERTIFICATE,
CHANNEL_CORE_CERTIFICATE,
):
if path.read_text(encoding="ascii").count("-----BEGIN CERTIFICATE-----") != 1:
die(f"Core channel certificate cardinality mismatch: {path.name}")
run(["/usr/bin/openssl", "pkey", "-in", str(CHANNEL_PRIVATE_KEY), "-check", "-noout"])
run(["/usr/bin/openssl", "x509", "-in", str(CHANNEL_CERTIFICATE), "-noout"])
run(["/usr/bin/openssl", "x509", "-in", str(CHANNEL_CORE_CERTIFICATE), "-noout"])
run([
"/usr/bin/openssl", "verify", "-purpose", "sslserver",
"-CAfile", str(CHANNEL_CERTIFICATE), str(CHANNEL_CERTIFICATE),
])
run([
"/usr/bin/openssl", "verify", "-purpose", "sslclient",
"-CAfile", str(CHANNEL_CORE_CERTIFICATE), str(CHANNEL_CORE_CERTIFICATE),
])
run([
"/usr/bin/openssl", "x509", "-in", str(CHANNEL_CERTIFICATE),
"-noout", "-checkip", PUBLIC_IPV4,
])
certificate_key = run([
"/usr/bin/openssl", "x509", "-in", str(CHANNEL_CERTIFICATE), "-pubkey", "-noout",
]).stdout.strip()
private_key = run([
"/usr/bin/openssl", "pkey", "-in", str(CHANNEL_PRIVATE_KEY), "-pubout",
]).stdout.strip()
if certificate_key != private_key:
die("Core channel Edge certificate/private key mismatch")
try:
document = json.loads(CHANNEL_RUNTIME_CONFIG.read_text(encoding="utf-8"))
except json.JSONDecodeError:
die("Core channel runtime configuration is invalid JSON")
if set(document) != {
"schemaVersion",
"edgeRegistrationId",
"channelGeneration",
"trustGeneration",
"allowedCoreFingerprints",
}:
die("Core channel runtime configuration key set mismatch")
if document.get("schemaVersion") != "nodedc.device-edge.channel-runtime.v1":
die("Core channel runtime configuration schema mismatch")
for key in ("edgeRegistrationId", "channelGeneration", "trustGeneration"):
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._:-]{0,127}", str(document.get(key, ""))):
die(f"Core channel runtime configuration ref mismatch: {key}")
fingerprints = document.get("allowedCoreFingerprints")
if (
not isinstance(fingerprints, list)
or not 1 <= len(fingerprints) <= 2
or len(fingerprints) != len(set(fingerprints))
or any(
not isinstance(value, str)
or not re.fullmatch(r"(?:[A-F0-9]{2}:){31}[A-F0-9]{2}", value)
for value in fingerprints
)
):
die("Core channel Core identity allowlist mismatch")
if certificate_fingerprint(CHANNEL_CORE_CERTIFICATE) not in fingerprints:
die("Core channel Core trust fingerprint mismatch")
return document
def assign_channel_trust(account):
assert_channel_trust(require_runtime_owner=False)
os.chown(CHANNEL_TRUST_ROOT, account.pw_uid, account.pw_gid)
os.chmod(CHANNEL_TRUST_ROOT, 0o700)
for path, mode in (
(CHANNEL_PRIVATE_KEY, 0o400),
(CHANNEL_CERTIFICATE, 0o444),
(CHANNEL_CORE_CERTIFICATE, 0o444),
(CHANNEL_RUNTIME_CONFIG, 0o444),
):
os.chown(path, account.pw_uid, account.pw_gid)
os.chmod(path, mode)
return assert_channel_trust(require_runtime_owner=True)
def apply_nftables(source: Path):
install_file(source, NFTABLES_CONFIG, 0o644)
run(["/usr/sbin/nft", "-c", "-f", str(NFTABLES_CONFIG)])
@@ -813,6 +1013,19 @@ def apply_relay(payload: Path):
validate_relay_runtime()
def apply_core_channel(payload: Path):
account = ensure_service_user(
CHANNEL_USER,
"/var/lib/nodedc-b2-vps/channel-runtime",
)
assign_channel_trust(account)
install_file(LIVE_ROOT / CORE_CHANNEL_ENTRIES[7], CHANNEL_UNIT, 0o644)
apply_nftables(LIVE_ROOT / CORE_CHANNEL_ENTRIES[6])
systemctl("daemon-reload")
systemctl("enable", "--now", "nodedc-device-edge-channel.service")
validate_core_channel_runtime()
def sshd_effective():
return run(["/usr/sbin/sshd", "-T"]).stdout.lower()
@@ -919,6 +1132,85 @@ def relay_health():
die(f"VPS relay health timeout: {last_error}")
def core_channel_health(*, require_accepted: bool):
last_error = None
for _attempt in range(60):
try:
with urllib.request.urlopen(
f"http://127.0.0.1:{CHANNEL_HEALTH_PORT}/healthz",
timeout=3,
) as response:
payload = json.loads(response.read(65537).decode("utf-8"))
if (
response.status == 200
and payload.get("ok") is True
and (
not require_accepted
or payload.get("channel") == "accepted"
)
):
return payload
last_error = f"channel={payload.get('channel')}"
except Exception as error:
last_error = str(error)
time.sleep(2)
die(f"VPS Core channel health timeout: {last_error}")
def validate_core_channel_runtime():
validate_foundation_runtime(
require_running_tailnet=False,
expected_key_user=SERVICE_USER,
)
source_file_state("core-channel")
assert_channel_trust(require_runtime_owner=True)
if service_active("nodedc-b2-backhaul.service"):
die("frozen VPS backhaul service became active")
if service_active("nodedc-b2-relay.service"):
die("frozen VPS relay service became active")
if not service_active("nodedc-device-edge-channel.service"):
die("VPS Core channel service is not active")
health = core_channel_health(require_accepted=True)
expected = {
"ok": True,
"service": "nodedc-device-edge-channel",
"channel": "accepted",
"trackerIngress": "disabled",
"commandTransport": "disabled",
}
for key, value in expected.items():
if health.get(key) != value:
die(f"VPS Core channel health contract mismatch: {key}")
if not port_is_open(PUBLIC_IPV4, CHANNEL_PUBLIC_PORT, timeout=5):
die("VPS public Core channel listener is unavailable")
assert_port_closed(9921)
nft = run(["/usr/sbin/nft", "list", "table", "inet", "nodedc_b2_vps"]).stdout
if (
"policy drop" not in nft
or "tcp dport 8443" not in nft
or "tcp dport 9921" in nft
):
die("VPS Core channel firewall contract mismatch")
unit = run([
"/usr/bin/systemctl",
"show",
"nodedc-device-edge-channel.service",
"--property=User,Group,NoNewPrivileges,MemoryMax,MemorySwapMax,CPUQuotaPerSecUSec,TasksMax,LimitNOFILE",
]).stdout
for required in (
"User=nodedc-channel",
"Group=nodedc-channel",
"NoNewPrivileges=yes",
"MemoryMax=134217728",
"MemorySwapMax=0",
"TasksMax=64",
"LimitNOFILE=1024",
):
if required not in unit:
die(f"VPS Core channel resource boundary mismatch: {required}")
return health
def validate_relay_runtime():
validate_backhaul_runtime()
source_file_state("relay")
@@ -979,6 +1271,7 @@ def rollback(backup: Path, phase: str):
for service in (
"nodedc-b2-relay.service",
"nodedc-b2-backhaul.service",
"nodedc-device-edge-channel.service",
"nodedc-b2-tailscaled.service",
):
if phase == "foundation" or service != "nodedc-b2-tailscaled.service":
@@ -1004,6 +1297,9 @@ def rollback(backup: Path, phase: str):
if phase == "relay":
if not users_before.get(RELAY_USER, False) and user_exists(RELAY_USER):
run(["/usr/sbin/userdel", RELAY_USER], check=False)
if phase == "core-channel":
if not users_before.get(CHANNEL_USER, False) and user_exists(CHANNEL_USER):
run(["/usr/sbin/userdel", CHANNEL_USER], check=False)
if phase == "foundation" and not metadata.get("serviceUserExisted"):
runtime_state_root = Path("/var/lib/nodedc-b2-vps")
if LIVE_ROOT.exists() and not LIVE_ROOT.is_symlink():
@@ -1052,13 +1348,21 @@ def plan_artifact(artifact_argument: str):
print("public_b2_ingress=disabled")
print("services=nodedc-b2-backhaul")
print(f"backhaul_runtime_identity={BACKHAUL_USER}:private-key-owner")
else:
elif phase == "relay":
print("public_b2_ingress=155.212.211.15:9921/tcp")
print("health=127.0.0.1:18221")
print("private_upstream=127.0.0.1:19921")
print("source_admission=public-ipv4-only")
print("services=nodedc-b2-relay")
print(f"relay_runtime_identity={RELAY_USER}:no-credentials")
else:
print("public_core_channel=155.212.211.15:8443/tcp:tls13-mtls-h2")
print(f"health=127.0.0.1:{CHANNEL_HEALTH_PORT}")
print("public_b2_ingress=disabled")
print("tracker_tcp_9921=closed")
print("services=nodedc-device-edge-channel")
print(f"channel_runtime_identity={CHANNEL_USER}:host-local-private-key")
print("peer_trust=preprovisioned-pinned-self-signed-core-certificate+fingerprint")
print("command_transport=disabled")
print("gelios=untouched")
print("dns=unchanged")
@@ -1088,8 +1392,10 @@ def apply_artifact(artifact_argument: str):
apply_foundation(loaded["payload"])
elif loaded["phase"] == "backhaul":
apply_backhaul(loaded["payload"])
else:
elif loaded["phase"] == "relay":
apply_relay(loaded["payload"])
else:
apply_core_channel(loaded["payload"])
archived = archive_artifact(loaded["artifact"], APPLIED_ROOT)
record = {
@@ -92,7 +92,7 @@ class DeviceEdgeVpsArtifactTest(unittest.TestCase):
def test_builders_are_deterministic_narrow_and_secret_free(self):
self.require_runtime_cache()
for phase in ("foundation", "backhaul", "relay"):
for phase in ("foundation", "backhaul", "relay", "core-channel"):
with self.subTest(phase=phase), tempfile.TemporaryDirectory(
prefix=f"nodedc-vps-{phase}-"
) as directory:
@@ -164,7 +164,7 @@ class DeviceEdgeVpsArtifactTest(unittest.TestCase):
old_inbox = RUNNER.INBOX_ROOT
RUNNER.INBOX_ROOT = inbox
try:
for phase in ("foundation", "backhaul", "relay"):
for phase in ("foundation", "backhaul", "relay", "core-channel"):
result = self.build(
inbox,
phase,
@@ -217,10 +217,55 @@ class DeviceEdgeVpsArtifactTest(unittest.TestCase):
self.assertIn("b2_routes=unchanged", rendered)
self.assertIn("command_transport=disabled", rendered)
def test_core_channel_plan_is_exact_and_keeps_tracker_ingress_closed(self):
with tempfile.TemporaryDirectory(prefix="nodedc-vps-channel-plan-") as directory:
inbox = Path(directory) / "inbox"
inbox.mkdir()
result = self.build(
inbox,
"core-channel",
"device-edge-vps-core-channel-plan-001",
)
self.assertEqual(result.returncode, 0, result.stderr)
artifact = Path(json.loads(result.stdout)["artifact"])
old_inbox = RUNNER.INBOX_ROOT
RUNNER.INBOX_ROOT = inbox
try:
with patch.object(RUNNER, "assert_root"), patch.object(
RUNNER,
"preflight",
return_value={"predecessor": "accepted-foundation-closed-channel"},
), patch("builtins.print") as output:
RUNNER.plan_artifact(str(artifact))
finally:
RUNNER.INBOX_ROOT = old_inbox
rendered = "\n".join(
" ".join(str(arg) for arg in call.args)
for call in output.call_args_list
)
self.assertIn("phase=core-channel", rendered)
self.assertIn(
"predecessor=accepted-foundation-closed-channel",
rendered,
)
self.assertIn(
"public_core_channel=155.212.211.15:8443/tcp:tls13-mtls-h2",
rendered,
)
self.assertIn("tracker_tcp_9921=closed", rendered)
self.assertIn("public_b2_ingress=disabled", rendered)
self.assertIn(
"peer_trust=preprovisioned-pinned-self-signed-core-certificate+fingerprint",
rendered,
)
self.assertIn("command_transport=disabled", rendered)
self.assertIn("gelios=untouched", rendered)
def test_units_and_firewalls_keep_the_required_boundaries(self):
source_root = SCRIPT_DIR.parent.parent / "device-plane"
foundation = (source_root / "vps/config/nftables-foundation.conf").read_text()
relay = (source_root / "vps/config/nftables-relay.conf").read_text()
channel = (source_root / "vps/config/nftables-core-channel.conf").read_text()
sshd = (source_root / "vps/config/00-nodedc-b2-vps.conf").read_text()
backhaul = (source_root / "vps/config/backhaul_ssh_config").read_text()
tailscale_unit = (
@@ -230,11 +275,16 @@ class DeviceEdgeVpsArtifactTest(unittest.TestCase):
backhaul_unit = (
source_root / "vps/systemd/nodedc-b2-backhaul.service"
).read_text()
channel_unit = (
source_root / "vps/systemd/nodedc-device-edge-channel.service"
).read_text()
self.assertIn("policy drop", foundation)
self.assertIn("tcp dport 22", foundation)
self.assertNotIn("tcp dport 9921", foundation)
self.assertIn("tcp dport 9921", relay)
self.assertIn("tcp dport 8443", channel)
self.assertNotIn("tcp dport 9921", channel)
self.assertIn("PasswordAuthentication no", sshd)
self.assertIn("AllowTcpForwarding no", sshd)
self.assertIn("StrictHostKeyChecking yes", backhaul)
@@ -248,6 +298,15 @@ class DeviceEdgeVpsArtifactTest(unittest.TestCase):
self.assertNotIn("User=nodedc-edge", relay_unit)
self.assertIn("DEVICE_EDGE_RELAY_SOURCE_POLICY=public-ipv4-only", relay_unit)
self.assertIn("MemoryMax=192M", relay_unit)
self.assertIn("User=nodedc-channel", channel_unit)
self.assertIn("node --jitless", channel_unit)
self.assertIn("MemoryMax=128M", channel_unit)
self.assertIn("MemorySwapMax=0", channel_unit)
self.assertIn("CPUQuota=50%", channel_unit)
self.assertIn("TasksMax=64", channel_unit)
self.assertIn("LimitNOFILE=1024", channel_unit)
self.assertNotIn("LocalForward", channel_unit)
self.assertNotIn("DEVICE_EDGE_RELAY_UPSTREAM", channel_unit)
def test_runner_has_registered_rollback_and_no_generic_latest(self):
source = RUNNER_PATH.read_text(encoding="utf-8")
@@ -344,6 +403,82 @@ class DeviceEdgeVpsArtifactTest(unittest.TestCase):
"old-source\n",
)
def test_backup_restore_preserves_core_channel_source_trust_and_firewall(self):
with tempfile.TemporaryDirectory(prefix="nodedc-vps-channel-backup-") as directory:
root = Path(directory)
live = root / "live"
backups = root / "backups"
nft = root / "etc/nftables.conf"
channel_unit = root / "etc/nodedc-device-edge-channel.service"
trust = root / "state/channel-trust"
backups.mkdir()
nft.parent.mkdir(parents=True)
trust.mkdir(parents=True)
nft.write_text("foundation-firewall\n", encoding="utf-8")
channel_unit.parent.mkdir(parents=True, exist_ok=True)
channel_unit.write_text("old-channel-unit\n", encoding="utf-8")
(trust / "runtime.json").write_text("old-runtime\n", encoding="utf-8")
for relative in RUNNER.CORE_CHANNEL_ENTRIES:
target = live / relative
if relative.endswith("/src"):
target.mkdir(parents=True)
(target / "server.mjs").write_text("old-channel-source\n", encoding="utf-8")
else:
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(f"old:{relative}\n", encoding="utf-8")
old_live = RUNNER.LIVE_ROOT
old_backups = RUNNER.BACKUP_ROOT
old_nft = RUNNER.NFTABLES_CONFIG
old_unit = RUNNER.CHANNEL_UNIT
old_trust = RUNNER.CHANNEL_TRUST_ROOT
RUNNER.LIVE_ROOT = live
RUNNER.BACKUP_ROOT = backups
RUNNER.NFTABLES_CONFIG = nft
RUNNER.CHANNEL_UNIT = channel_unit
RUNNER.CHANNEL_TRUST_ROOT = trust
completed = subprocess.CompletedProcess([], 0, "table inet old {}\n", "")
try:
with patch.object(RUNNER, "run", return_value=completed), patch.object(
RUNNER,
"service_active",
return_value=False,
), patch.object(
RUNNER,
"systemctl",
return_value=completed,
), patch.object(
RUNNER,
"user_exists",
return_value=False,
):
_backup_id, backup = RUNNER.create_backup(
"channel-unit",
"core-channel",
)
nft.write_text("candidate-firewall\n", encoding="utf-8")
channel_unit.write_text("candidate-channel-unit\n", encoding="utf-8")
(trust / "runtime.json").write_text("candidate-runtime\n", encoding="utf-8")
(live / "services/device-edge-channel/src/server.mjs").write_text(
"candidate-channel-source\n",
encoding="utf-8",
)
RUNNER.restore_backup(backup, "core-channel")
finally:
RUNNER.LIVE_ROOT = old_live
RUNNER.BACKUP_ROOT = old_backups
RUNNER.NFTABLES_CONFIG = old_nft
RUNNER.CHANNEL_UNIT = old_unit
RUNNER.CHANNEL_TRUST_ROOT = old_trust
self.assertEqual(nft.read_text(), "foundation-firewall\n")
self.assertEqual(channel_unit.read_text(), "old-channel-unit\n")
self.assertEqual((trust / "runtime.json").read_text(), "old-runtime\n")
self.assertEqual(
(live / "services/device-edge-channel/src/server.mjs").read_text(),
"old-channel-source\n",
)
if __name__ == "__main__":
unittest.main(verbosity=2)