code wiki / (root) / nx_cert_autorenew_lib.nx

nx_cert_autorenew_lib.nx source

↩ module page · 80 lines · 4480 B

1// nx_cert_autorenew_lib.nx -- PURE mapping + threshold logic for the per-domain wildcard TLS 2// auto-renewal system (no I/O, no TLS, no fork). Split out (rule 15 DRY / rule 9 single-responsibility) 3// so BOTH the driver (nx_cert_autorenew.nx) and the gate (nx_cert_autorenew_gate.nx) share ONE copy of 4// the domain->path mapping + the WARN/EXPIRED trigger predicate -- the exact logic the gate proves and 5// the driver acts on. Reuses nx_cert_monitor's verdict vocabulary (CM_OK/WARN/EXPIRED/NOCERT + cm_verdict 6// + CM_WARN_DAYS) so the monitor and the renewer can NEVER disagree about "is this cert expiring". 7// 8// The mapping closes the gap that started this whole arc: a per-domain wildcard cert that silently lapsed 9// because nothing renewed it. Given a dotted domain D, the system knows EXACTLY: 10// issue output -> /tmp/new_<D>_fullchain.der + /tmp/new_<D>_ecdsa_key.bin (nx_acme_dns01_issue argv[1]=D) 11// live SNI path -> /volume1/.../certs/le_<label>_fullchain.der + le_<label>_ecdsa_key.bin 12// (label = D's first DNS label; the names nx_sites_daemon loads per ClientHello SNI) 13// license_tier: ORIGINAL (composes nx_cert_monitor's verdict logic; no new crypto/transport) 14import "nx_cert_monitor.nx" // CM_OK/CM_WARN/CM_EXPIRED/CM_NOCERT, CM_WARN_DAYS, cm_verdict, cm_status, cm_name, X509Cert 15 16// ---- single-source infra paths (named, not magic; nx_sites_daemon loads exactly these) ---- 17const CAR_CERT_DIR: *u8 = "/volume1/homes/elderwesto/nishihost/certs/" as *u8 18const CAR_TMP_NEW: *u8 = "/tmp/new_" as *u8 19 20// ---- tiny string helpers (car_-prefixed; no collision with cm_*/x509_*) ---- 21func car_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 22func car_streq(a: *u8, b: *u8) -> i64 { 23 var i: i64 = 0 24 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 25 if b[i] == (0 as u8) { return 1 } 26 return 0 27} 28// append the NUL-terminated string s into out at offset off; returns the new offset (caller terminates). 29func car_put(out: *u8, off: i64, s: *u8) -> i64 { 30 var i: i64 = 0 31 while s[i] != (0 as u8) { out[off] = s[i]; off = off + 1; i = i + 1 } 32 return off 33} 34 35// first DNS label of a dotted domain (chars up to the first '.') -> out (NUL-terminated); returns its len. 36// "nishifamily.com" -> "nishifamily" ; "andelinwest.com" -> "andelinwest". This is the <label> token in 37// le_<label>_fullchain.der -- the exact per-domain cert filename nx_sites_daemon selects by SNI. 38func car_label(domain: *u8, out: *u8) -> i64 { 39 var i: i64 = 0 40 while domain[i] != (0 as u8) { 41 if domain[i] == (0x2e as u8) { out[i] = 0 as u8; return i } // '.' -> end of the first label 42 out[i] = domain[i] 43 i = i + 1 44 } 45 out[i] = 0 as u8 46 return i 47} 48 49// /tmp/new_<domain>_fullchain.der -- the DER the issue organ writes for argv[1]=domain (proven naming). 50func car_issue_der(domain: *u8, out: *u8) -> i64 { 51 var o: i64 = 0 52 o = car_put(out, o, CAR_TMP_NEW); o = car_put(out, o, domain); o = car_put(out, o, "_fullchain.der" as *u8) 53 out[o] = 0 as u8; return o 54} 55// /tmp/new_<domain>_ecdsa_key.bin -- the matching 32-byte P-256 key from the SAME issuance. 56func car_issue_key(domain: *u8, out: *u8) -> i64 { 57 var o: i64 = 0 58 o = car_put(out, o, CAR_TMP_NEW); o = car_put(out, o, domain); o = car_put(out, o, "_ecdsa_key.bin" as *u8) 59 out[o] = 0 as u8; return o 60} 61// /volume1/.../certs/le_<label>_fullchain.der -- the LIVE SNI cert path the daemon loads at startup. 62func car_remote_der(label: *u8, out: *u8) -> i64 { 63 var o: i64 = 0 64 o = car_put(out, o, CAR_CERT_DIR); o = car_put(out, o, "le_" as *u8); o = car_put(out, o, label); o = car_put(out, o, "_fullchain.der" as *u8) 65 out[o] = 0 as u8; return o 66} 67// /volume1/.../certs/le_<label>_ecdsa_key.bin -- the LIVE SNI key path (paired with the fullchain above). 68func car_remote_key(label: *u8, out: *u8) -> i64 { 69 var o: i64 = 0 70 o = car_put(out, o, CAR_CERT_DIR); o = car_put(out, o, "le_" as *u8); o = car_put(out, o, label); o = car_put(out, o, "_ecdsa_key.bin" as *u8) 71 out[o] = 0 as u8; return o 72} 73 74// THE trigger predicate: renew ONLY on WARN (inside the 21d window) or EXPIRED. OK = nothing to do; 75// NOCERT = could not read/parse or host unreachable -> do NOT blindly re-issue (an outage is not an expiry). 76func car_should_renew(verdict: i64) -> i64 { 77 if verdict == CM_WARN { return 1 } 78 if verdict == CM_EXPIRED { return 1 } 79 return 0 80}