code wiki / _hdl_build / nx_aw_tlsproxy.nx

nx_aw_tlsproxy.nx source

↩ module page · 143 lines · 7036 B

1// nx_aw_tlsproxy.nx -- SOVEREIGN TLS reverse proxy: terminates HTTPS (TLS 1.3 + the real Let's Encrypt cert, 2// reusing the PROVEN nx_tls13_server_session stack from nx_sites_daemon) on its own port, then forwards the 3// decrypted HTTP request to the LOOPBACK CMS admin (127.0.0.1:8456, plain HTTP) and relays the admin's 4// response back encrypted. This puts the andelinwest lawyer/client admin behind real TLS WITHOUT modifying 5// the live sites_v2 daemon and WITHOUT re-porting the TLS stack into the plain-HTTP admin (operator chose 6// Approach A; standalone for small blast radius + instant DNAT-flip rollback). Live cutover = point the 7// .240:443 DNAT at NX_TP_PORT. Fork-per-connection (bounded). license_tier: ORIGINAL 8// argv[1] (optional) = listen port (default 9443); argv[2] (optional) = backend port (default 8456) 9import "nx_syscalls.nx" 10import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 11import "nx_csprng.nx" 12import "nx_http_server.nx" 13import "nx_tls13_server_session.nx" 14import "nx_tls13_server_session_run.nx" 15import "nx_tls13_server_session_app_data.nx" 16const NX_MAGIC_100000000: i64 = 100000000 17 18const NX_TP_PORT_DEFAULT: i64 = 9443 19const NX_TP_BACKEND_DEFAULT: i64 = 8456 20const NX_TP_CERT: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_fullchain.der" as *u8 21const NX_TP_PRIV: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_ecdsa_key.bin" as *u8 22const NX_TP_MAX_CHILDREN: i64 = 64 23const NX_TP_REC: i64 = 20480 24const NX_TP_RESP: i64 = 262144 25const NX_TP_OUTCAP: i64 = 32768 26const NX_TP_CHUNK: i64 = 15000 27 28func tp_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let c: i64=s[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } return v } 29 30// connect to 127.0.0.1:<port>, send the request, read the full response (until backend closes) into resp. 31func tp_backend(bport: i64, req: *u8, req_n: i64, resp: *u8, cap: i64) -> i64 { 32 let bfd: i64 = sys_socket(2, 1, 0) // AF_INET, SOCK_STREAM 33 if bfd < 0 { return 0 - 1 } 34 let addr: *u8 = sys_mmap(16) 35 addr[0]=2 as u8; addr[1]=0 as u8 36 addr[2]=((bport >> 8) & 0xff) as u8; addr[3]=(bport & 0xff) as u8 37 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8 38 var z: i64 = 8; while z < 16 { addr[z]=0 as u8; z=z+1 } 39 if nx_connect_bounded(bfd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(bfd); return 0 - 1 } 40 sys_write(bfd, req, req_n) 41 var rn: i64 = 0 42 var go: i64 = 1 43 while go == 1 { 44 if rn >= cap { go = 0 } else { 45 let r: i64 = sys_read(bfd, (resp + rn) as *u8, cap - rn) 46 if r > 0 { rn = rn + r } else { go = 0 } 47 } 48 } 49 sys_close(bfd) 50 return rn 51} 52 53func main(argc: i64, argv: *i64) -> i64 { 54 var port: i64 = NX_TP_PORT_DEFAULT 55 var bport: i64 = NX_TP_BACKEND_DEFAULT 56 if argc > 1 { port = tp_atoi(argv[1] as *u8) } 57 if argc > 2 { bport = tp_atoi(argv[2] as *u8) } 58 59 let cert_len_box: *i64 = (sys_mmap(8)) as *i64 60 cert_len_box[0] = 0 61 let cert_der: *u8 = sys_read_file(NX_TP_CERT, cert_len_box) 62 if (cert_der as i64) == 0 { sys_write(1, "TP-CERT-FAIL\n" as *u8, 13); return 2 } 63 let cert_der_len: i64 = cert_len_box[0] 64 if cert_der_len < 100 { sys_write(1, "TP-CERT-SHORT\n" as *u8, 14); return 2 } 65 let priv_len_box: *i64 = (sys_mmap(8)) as *i64 66 priv_len_box[0] = 0 67 let ecdsa_priv: *u8 = sys_read_file(NX_TP_PRIV, priv_len_box) 68 if (ecdsa_priv as i64) == 0 { sys_write(1, "TP-PRIV-FAIL\n" as *u8, 13); return 3 } 69 if priv_len_box[0] != 32 { sys_write(1, "TP-PRIV-LEN\n" as *u8, 12); return 3 } 70 71 let addr_buf: *u8 = sys_mmap(16) 72 nx_http_server_addr_any(addr_buf, port) 73 let lv: *i64 = (sys_mmap(8)) as *i64 74 let lfd: i64 = nx_http_server_listen(addr_buf, 16, lv) 75 if lfd < 0 { sys_write(1, "TP-LISTEN-FAIL\n" as *u8, 15); return 4 } 76 sys_write(1, "nx_aw_tlsproxy: HTTPS -> loopback CMS admin, up\n" as *u8, 47) 77 78 let server_random: *u8 = sys_mmap(32) 79 let server_x25519_priv: *u8 = sys_mmap(32) 80 let rec_buf: *u8 = sys_mmap(NX_TP_REC) 81 let plain: *u8 = sys_mmap(NX_TP_REC) 82 let resp: *u8 = sys_mmap(NX_TP_RESP) 83 let out_rec: *u8 = sys_mmap(NX_TP_OUTCAP) 84 let sock_addr: *u8 = sys_mmap(64) 85 let sock_len: *i64 = (sys_mmap(8)) as *i64 86 let reap_status: *i64 = (sys_mmap(8)) as *i64 87 88 sys_set_socket_timeout(lfd, 5) 89 var served: i64 = 0 90 var live: i64 = 0 91 while served < NX_MAGIC_100000000 { 92 while sys_wait4(0 - 1, reap_status, 1) > 0 { live = live - 1 } 93 sock_len[0] = 16 94 let cfd: i64 = sys_accept_with_addr(lfd, sock_addr, sock_len) 95 if cfd < 0 { continue } 96 if live >= NX_TP_MAX_CHILDREN { if sys_wait4(0 - 1, reap_status, 0) > 0 { live = live - 1 } } 97 let pid: i64 = sys_fork() 98 if pid == 0 { 99 sys_close(lfd) 100 sys_set_socket_timeout(cfd, 10) 101 let nodelay: *u8 = sys_mmap(4) 102 nodelay[0] = 1 as u8; nodelay[1] = 0 as u8; nodelay[2] = 0 as u8; nodelay[3] = 0 as u8 103 sys_setsockopt(cfd, 6, 1, nodelay, 4) 104 nx_csprng_fill(server_random, 32) 105 nx_csprng_fill(server_x25519_priv, 32) 106 let hs_rc: i64 = nx_tls13_server_session_run(cfd, server_random, server_x25519_priv, cert_der, cert_der_len, ecdsa_priv) 107 if hs_rc > 0 { 108 let s: *Tls13ServerSession = hs_rc as *Tls13ServerSession 109 var keep: i64 = 1 110 var nreq: i64 = 0 111 while keep == 1 { 112 if nreq >= 32 { keep = 0 } else { 113 let rec_n: i64 = sys_read(cfd, rec_buf, NX_TP_REC) 114 if rec_n <= 0 { keep = 0 } else { 115 let plain_n: i64 = nx_tls13_server_session_app_recv(s, rec_buf, rec_n, plain, NX_TP_REC) 116 if plain_n <= 0 { keep = 0 } else { 117 let bn: i64 = tp_backend(bport, plain, plain_n, resp, NX_TP_RESP) 118 if bn <= 0 { keep = 0 } else { 119 var off: i64 = 0 120 while off < bn { 121 var ck: i64 = bn - off 122 if ck > NX_TP_CHUNK { ck = NX_TP_CHUNK } 123 let out_n: i64 = nx_tls13_server_session_app_send(s, (resp + off) as *u8, ck, out_rec, NX_TP_OUTCAP) 124 if out_n > 0 { sys_write(cfd, out_rec, out_n) } else { off = bn; keep = 0 } 125 off = off + ck 126 } 127 nreq = nreq + 1 128 } 129 } 130 } 131 } 132 } 133 } 134 sys_close(cfd) 135 sys_exit(0) 136 } 137 sys_close(cfd) 138 if pid > 0 { live = live + 1 } 139 served = served + 1 140 } 141 sys_close(lfd) 142 return 0 143}