code wiki / _hdl_build / nx_deploy_secure.nx
nx_deploy_secure.nx source
↩ module page · 92 lines · 4822 B
1// nx_deploy_secure.nx -- ZERO-PERSISTENT-PLAINTEXT unattended deploy (vault backlog #5). One
2// sovereign orchestrator: derive the machine-bound key -> open the encrypted vault -> hand the
3// credential to the team's SSH deployer -> SHRED every ephemeral (overwrite + unlink). The operator
4// runs ONE command, never types or stages the password, and no plaintext credential survives on
5// disk past the deploy. Chains the gated modules via fork/execve/wait4 (NishiLang orchestration).
6//
7// Pre-staged by the caller: /tmp/nxput.payload (the page) + /tmp/nxput.dst (remote write cmd).
8// Requires: /tmp/nx_machine_key.elf, _offc/nx_vault.elf, /tmp/nx_ssh_putpage.elf (bootstrap-built).
9// license_tier: ORIGINAL
10import "nx_syscalls.nx"
11import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
12const AT_MAGIC_4096: i64 = 4096
13
14const AT_FDCWD: i64 = 0 - 100
15
16func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
17// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
18// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
19// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
20// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
21func _pn(v: i64) -> i64 { nxi_out(v); return 0 }
22
23// fork + (optional argv1) execve + wait4 -> WEXITSTATUS
24func ds_run1(path: *u8, arg1: *u8, arg2: *u8) -> i64 {
25 let pid: i64 = sys_fork()
26 if pid == 0 {
27 let argv: *i64 = sys_mmap(64) as *i64
28 argv[0] = path as i64
29 var ai: i64 = 1
30 if (arg1 as i64) != 0 { argv[ai] = arg1 as i64; ai = ai + 1 }
31 if (arg2 as i64) != 0 { argv[ai] = arg2 as i64; ai = ai + 1 }
32 argv[ai] = 0
33 let envp: *i64 = sys_mmap(16) as *i64
34 envp[0] = 0
35 sys_execve(path, argv, envp)
36 sys_exit(127)
37 }
38 let st: *i64 = sys_mmap(16) as *i64
39 sys_wait4(pid, st, 0)
40 return (st[0] >> 8) & 0xff
41}
42// SHRED: overwrite the file with zeros (best-effort vs disk caching) then unlink. A file that does
43// not exist is fine (rc ignored). Honest note: on CoW/journaled FS overwrite-in-place is not a
44// guaranteed wipe -- tmpfs (/tmp on most Linux) is RAM-backed, so unlink + memory reuse is the real
45// erase here; recorded in the vault design doc.
46func ds_shred(path: *u8) -> i64 {
47 let fd: i64 = sys_openat_wr(path, 0x180)
48 if fd >= 0 {
49 let z: *u8 = sys_mmap(AT_MAGIC_4096)
50 var i: i64 = 0
51 while i < AT_MAGIC_4096 { z[i] = 0 as u8; i = i + 1 }
52 sys_write(fd, z, AT_MAGIC_4096)
53 sys_close(fd)
54 }
55 // unlinkat: empirically 263 (x86) works, rv64 35 does NOT translate (gap filed) -- probe-verified
56 __syscall(263, AT_FDCWD, path, 0, 0, 0, 0)
57 return 0
58}
59func ds_cleanup() -> i64 {
60 ds_shred("/tmp/nxpass" as *u8)
61 ds_shred("/tmp/nxsecret.out" as *u8)
62 ds_shred("/tmp/nxpw" as *u8)
63 return 0
64}
65func main() -> i64 {
66 _p("=== SECURE DEPLOY: machine-key -> vault-open -> SSH deploy -> shred (zero persistent plaintext) ===\n" as *u8)
67 // 1. derive the machine-bound passphrase (writes /tmp/nxpass)
68 if ds_run1("/tmp/nx_machine_key.elf" as *u8, 0 as *u8, 0 as *u8) != 0 { _p(" machine-key derive failed\n" as *u8); ds_cleanup(); sys_exit(1); return 1 }
69 // 2. open the vault -> /tmp/nxsecret.out (authenticated; fails closed)
70 let vpath: *u8 = sys_mmap(256)
71 let home: *u8 = "/home/elderwesto/.nishi/secrets/nas.nv" as *u8
72 var h: i64 = 0
73 while home[h] != (0 as u8) { vpath[h] = home[h]; h = h + 1 }
74 vpath[h] = 0 as u8
75 if ds_run1("_offc/nx_vault.elf" as *u8, "open" as *u8, vpath) != 0 { _p(" vault open failed (wrong machine or tampered) -- FAILS CLOSED\n" as *u8); ds_cleanup(); sys_exit(1); return 1 }
76 // 3. hand the recovered credential to the deployer (it reads /tmp/nxpw). Brief RAM-backed
77 // ephemeral only; shredded in step 5.
78 let box: *i64 = sys_mmap(16) as *i64
79 let sec: *u8 = sys_read_file("/tmp/nxsecret.out" as *u8, box)
80 if (sec as i64) == 0 { _p(" no recovered secret\n" as *u8); ds_cleanup(); sys_exit(1); return 1 }
81 let fd: i64 = sys_openat_wr("/tmp/nxpw" as *u8, 0x180)
82 sys_write(fd, sec, box[0])
83 sys_close(fd)
84 // 4. deploy via the team's sovereign SSH putpage (reads /tmp/nxpw + /tmp/nxput.payload + .dst)
85 let drc: i64 = ds_run1("/tmp/nx_ssh_putpage.elf" as *u8, 0 as *u8, 0 as *u8)
86 // 5. SHRED every plaintext ephemeral regardless of deploy outcome
87 ds_cleanup()
88 if drc == 0 { _p(" DEPLOY OK + all plaintext ephemerals shredded (you never typed or stored the password)\n" as *u8); sys_exit(0); return 0 }
89 _p(" deploy returned nonzero (ephemerals still shredded) rc=" as *u8); _pn(drc); _p("\n" as *u8)
90 sys_exit(1)
91 return 1
92}