code wiki / _hdl_build / nx_aw_mtls_proxy.nx
nx_aw_mtls_proxy.nx source
↩ module page · 157 lines · 8481 B
1// nx_aw_mtls_proxy.nx -- SOVEREIGN mTLS-TERMINATING reverse proxy = the no-JavaScript session carrier.
2// Identical to the PROVEN nx_aw_tlsproxy (terminate TLS 1.3 + the real Let's Encrypt cert -> forward the
3// decrypted HTTP to a loopback backend -> relay back encrypted, fork-per-connection, instant DNAT-flip
4// rollback) EXCEPT it requests a CLIENT cert (mTLS via run_ecdsa_mtls) and, for a VERIFIED client cert,
5// injects the cert Subject CN as X-Nishi-Cert-Identity into the forwarded request -- stripping any client-
6// supplied copy first (the trust boundary, gated by nx_aw_mtls_inject_gate). A no-cert / bad-cert client is
7// forwarded WITHOUT an identity (request-not-require: the backend falls back to its X-Nishi-Session header
8// path = NEVER-LOCKOUT). So a top-level NAVIGATION carries identity at the TLS layer with ZERO JavaScript,
9// ZERO cookies -- the sovereign answer the operator asked for. Live cutover = point .240:443 DNAT at NX_MP_PORT.
10// argv[1] (optional) = listen port (default 9443); argv[2] (optional) = backend port (default 8443)
11// license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
14import "nx_csprng.nx"
15import "nx_http_server.nx"
16import "nx_tls13_server_session.nx"
17import "nx_tls13_server_session_run.nx"
18import "nx_tls13_server_session_run_mtls.nx" // nx_tls13_server_session_run_ecdsa_mtls
19import "nx_tls13_server_session_app_data.nx"
20import "nx_aw_mtls_inject.nx" // mtls_inject_identity (the gated trust boundary)
21const NX_MAGIC_100000000: i64 = 100000000
22
23const NX_MP_PORT_DEFAULT: i64 = 9443
24const NX_MP_BACKEND_DEFAULT: i64 = 8443
25const NX_MP_CERT: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_fullchain.der" as *u8
26const NX_MP_PRIV: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_ecdsa_key.bin" as *u8
27const NX_MP_MAX_CHILDREN: i64 = 64
28const NX_MP_REC: i64 = 20480
29const NX_MP_RESP: i64 = 262144
30const NX_MP_OUTCAP: i64 = 32768
31const NX_MP_CHUNK: i64 = 15000
32const NX_MP_CCCAP: i64 = 4096 // client cert DER buffer
33const NX_MP_INJCAP: i64 = 22528 // injected-request buffer (NX_MP_REC + ample header room)
34
35func mp_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 }
36
37// connect 127.0.0.1:<port>, send req, read the full response (until backend closes) into resp; return n or -1.
38func mp_backend(bport: i64, req: *u8, req_n: i64, resp: *u8, cap: i64) -> i64 {
39 let bfd: i64 = sys_socket(2, 1, 0)
40 if bfd < 0 { return 0 - 1 }
41 let addr: *u8 = sys_mmap(16)
42 addr[0]=2 as u8; addr[1]=0 as u8
43 addr[2]=((bport >> 8) & 0xff) as u8; addr[3]=(bport & 0xff) as u8
44 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8
45 var z: i64 = 8; while z < 16 { addr[z]=0 as u8; z=z+1 }
46 if nx_connect_bounded(bfd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(bfd); return 0 - 1 }
47 sys_write(bfd, req, req_n)
48 var rn: i64 = 0; var go: i64 = 1
49 while go == 1 {
50 if rn >= cap { go = 0 } else {
51 let r: i64 = sys_read(bfd, (resp + rn) as *u8, cap - rn)
52 if r > 0 { rn = rn + r } else { go = 0 }
53 }
54 }
55 sys_close(bfd)
56 return rn
57}
58
59func main(argc: i64, argv: *i64) -> i64 {
60 var port: i64 = NX_MP_PORT_DEFAULT
61 var bport: i64 = NX_MP_BACKEND_DEFAULT
62 if argc > 1 { port = mp_atoi(argv[1] as *u8) }
63 if argc > 2 { bport = mp_atoi(argv[2] as *u8) }
64
65 let cert_len_box: *i64 = (sys_mmap(8)) as *i64; cert_len_box[0] = 0
66 let cert_der: *u8 = sys_read_file(NX_MP_CERT, cert_len_box)
67 if (cert_der as i64) == 0 { sys_write(1, "MP-CERT-FAIL\n" as *u8, 13); return 2 }
68 let cert_der_len: i64 = cert_len_box[0]
69 if cert_der_len < 100 { sys_write(1, "MP-CERT-SHORT\n" as *u8, 14); return 2 }
70 let priv_len_box: *i64 = (sys_mmap(8)) as *i64; priv_len_box[0] = 0
71 let ecdsa_priv: *u8 = sys_read_file(NX_MP_PRIV, priv_len_box)
72 if (ecdsa_priv as i64) == 0 { sys_write(1, "MP-PRIV-FAIL\n" as *u8, 13); return 3 }
73 if priv_len_box[0] != 32 { sys_write(1, "MP-PRIV-LEN\n" as *u8, 12); return 3 }
74
75 let addr_buf: *u8 = sys_mmap(16)
76 nx_http_server_addr_any(addr_buf, port)
77 let lv: *i64 = (sys_mmap(8)) as *i64
78 let lfd: i64 = nx_http_server_listen(addr_buf, 16, lv)
79 if lfd < 0 { sys_write(1, "MP-LISTEN-FAIL\n" as *u8, 15); return 4 }
80 sys_write(1, "nx_aw_mtls_proxy: mTLS HTTPS -> loopback backend, up\n" as *u8, 52)
81
82 let server_random: *u8 = sys_mmap(32)
83 let server_x25519_priv: *u8 = sys_mmap(32)
84 let rec_buf: *u8 = sys_mmap(NX_MP_REC)
85 let plain: *u8 = sys_mmap(NX_MP_REC)
86 let injected: *u8 = sys_mmap(NX_MP_INJCAP)
87 let resp: *u8 = sys_mmap(NX_MP_RESP)
88 let out_rec: *u8 = sys_mmap(NX_MP_OUTCAP)
89 let cc_buf: *u8 = sys_mmap(NX_MP_CCCAP)
90 let cc_len: *i64 = (sys_mmap(8)) as *i64
91 let auth_box: *i64 = (sys_mmap(8)) as *i64
92 let sock_addr: *u8 = sys_mmap(64)
93 let sock_len: *i64 = (sys_mmap(8)) as *i64
94 let reap_status: *i64 = (sys_mmap(8)) as *i64
95
96 sys_set_socket_timeout(lfd, 5)
97 var served: i64 = 0
98 var live: i64 = 0
99 while served < NX_MAGIC_100000000 {
100 while sys_wait4(0 - 1, reap_status, 1) > 0 { live = live - 1 }
101 sock_len[0] = 16
102 let cfd: i64 = sys_accept_with_addr(lfd, sock_addr, sock_len)
103 if cfd < 0 { continue }
104 if live >= NX_MP_MAX_CHILDREN { if sys_wait4(0 - 1, reap_status, 0) > 0 { live = live - 1 } }
105 let pid: i64 = sys_fork()
106 if pid == 0 {
107 sys_close(lfd)
108 sys_set_socket_timeout(cfd, 10)
109 let nodelay: *u8 = sys_mmap(4)
110 nodelay[0] = 1 as u8; nodelay[1] = 0 as u8; nodelay[2] = 0 as u8; nodelay[3] = 0 as u8
111 sys_setsockopt(cfd, 6, 1, nodelay, 4)
112 nx_csprng_fill(server_random, 32)
113 nx_csprng_fill(server_x25519_priv, 32)
114 cc_len[0] = 0; auth_box[0] = 0
115 // mTLS handshake: requests + (if presented) verifies a client cert. auth_box: 0 none / 1 verified / -1 bad.
116 let hs_rc: i64 = nx_tls13_server_session_run_ecdsa_mtls(cfd, server_random, server_x25519_priv, cert_der, cert_der_len, ecdsa_priv, cc_buf, NX_MP_CCCAP, cc_len, auth_box)
117 if hs_rc > 0 {
118 let s: *Tls13ServerSession = hs_rc as *Tls13ServerSession
119 var keep: i64 = 1
120 var nreq: i64 = 0
121 while keep == 1 {
122 if nreq >= 32 { keep = 0 } else {
123 let rec_n: i64 = sys_read(cfd, rec_buf, NX_MP_REC)
124 if rec_n <= 0 { keep = 0 } else {
125 let plain_n: i64 = nx_tls13_server_session_app_recv(s, rec_buf, rec_n, plain, NX_MP_REC)
126 if plain_n <= 0 { keep = 0 } else {
127 // THE CARRIER: strip any spoofed identity; inject the verified CN (auth==1 only).
128 let inj_n: i64 = mtls_inject_identity(plain, plain_n, cc_buf, cc_len[0], auth_box[0], injected, NX_MP_INJCAP)
129 if inj_n < 0 { keep = 0 } else { // fail-closed: never forward an unstripped request
130 let bn: i64 = mp_backend(bport, injected, inj_n, resp, NX_MP_RESP)
131 if bn <= 0 { keep = 0 } else {
132 var off: i64 = 0
133 while off < bn {
134 var ck: i64 = bn - off
135 if ck > NX_MP_CHUNK { ck = NX_MP_CHUNK }
136 let out_n: i64 = nx_tls13_server_session_app_send(s, (resp + off) as *u8, ck, out_rec, NX_MP_OUTCAP)
137 if out_n > 0 { sys_write(cfd, out_rec, out_n) } else { off = bn; keep = 0 }
138 off = off + ck
139 }
140 nreq = nreq + 1
141 }
142 }
143 }
144 }
145 }
146 }
147 }
148 sys_close(cfd)
149 sys_exit(0)
150 }
151 sys_close(cfd)
152 if pid > 0 { live = live + 1 }
153 served = served + 1
154 }
155 sys_close(lfd)
156 return 0
157}