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}