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}