mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-06-29 22:54:31 +00:00
Compare commits
No commits in common. "84f0a37fdf88d364eba4ac4b0abc2b9fc54b09d7" and "da71515d79d8947db7dfe559bef51d6065eb94a6" have entirely different histories.
84f0a37fdf
...
da71515d79
|
|
@ -1,62 +0,0 @@
|
||||||
# MITM engine migration — Phase 2 bench results (#662)
|
|
||||||
|
|
||||||
- **Date:** 2026-06-18 · ran the Phase-1 Go PoC on gk2 (arm64), `127.0.0.1:8090`,
|
|
||||||
**no DNAT** (zero impact on live R3, which stayed on the mitmproxy workers).
|
|
||||||
|
|
||||||
## Proven on the real arm64 board (with the live `ca-wg` CA)
|
|
||||||
| Check | Result |
|
|
||||||
|-------|--------|
|
|
||||||
| Static arm64 binary | 5.4 MB, `ELF aarch64`, CGO_ENABLED=0 — runs natively on gk2 |
|
|
||||||
| **CA-compat forging** | `curl -x :8090 --cacert ca.pem https://example.com/` → **200**; the forged leaf (signed by the existing `ca-wg` CA) is trusted — R3 clients would trust it, no re-enroll |
|
|
||||||
| **MITM + body inject** | injected `<!-- sbx-ng banner -->` marker present in the HTML |
|
|
||||||
| **204 block** | `https://doubleclick.net/` → **204** (ad_ghost path) |
|
|
||||||
| **JA4 capture** | live: `t0304_c31_ah2_sni=example.com` (TLS1.3 / 31 ciphers / ALPN h2 / SNI) — the `ja4` addon's material is reachable in Go on arm64 |
|
|
||||||
| **Footprint** | **~12 MB RSS** vs Python mitmproxy ~70–117 MB per worker |
|
|
||||||
|
|
||||||
So every **discriminating capability** the analysis flagged (CA-compat, request-204,
|
|
||||||
body-inject, SNI-splice, JA4) works on the actual hardware, at ~1/6th the memory.
|
|
||||||
|
|
||||||
## Gate: "forge + throughput ≥ mitmproxy" — PARTIAL
|
|
||||||
- **Forge:** ✅ proven (CA-compat, cached per host, fast).
|
|
||||||
- **Footprint:** ✅ ~12 MB (far below mitmproxy).
|
|
||||||
- **Throughput / multi-core:** ⚠️ **not cleanly measured.** The instantaneous-CPU
|
|
||||||
sample was cut short by (a) a transient `wg-admin` ssh blip and (b) a
|
|
||||||
`pkill -f sbxmitm` self-match bug (the kill matched its own ssh shell). Multi-core
|
|
||||||
is **structurally guaranteed** — Go runs `GOMAXPROCS=4` with no GIL, vs Python
|
|
||||||
mitmproxy capped ~1 core/worker — but a rigorous throughput-vs-mitmproxy
|
|
||||||
comparison must be done in a **controlled load environment**, NOT by hammering
|
|
||||||
the production board.
|
|
||||||
|
|
||||||
## Phase 2b — controlled multi-core throughput bench (SETTLES the gate)
|
|
||||||
`BenchmarkHandshake` (cmd/sbxmitm/bench_test.go) drives full client↔proxy forged
|
|
||||||
TLS handshakes in parallel at `-cpu=1,2,4,8` (dev box, warm forge cache):
|
|
||||||
|
|
||||||
| Cores | ns/handshake | handshakes/s | scaling |
|
|
||||||
|-------|--------------|--------------|---------|
|
|
||||||
| 1 | 398,895 | ~2,510 | 1.00× |
|
|
||||||
| 2 | 204,116 | ~4,900 | **1.95×** |
|
|
||||||
| 4 | 117,307 | ~8,520 | **3.40×** |
|
|
||||||
| 8 | 86,999 | ~11,490 | 4.58× |
|
|
||||||
|
|
||||||
Near-linear to 2 cores, **3.40× at 4 cores** (gk2's core count) — the Go core's
|
|
||||||
throughput **scales with cores**, whereas a GIL-bound Python mitmproxy worker
|
|
||||||
stays ~1 core regardless. So on gk2's 4 cores the Go core does ~3.4× the handshake
|
|
||||||
throughput of one Python worker; ~2,510 handshakes/s even single-core dwarfs the
|
|
||||||
toolbox's real load (a few clients).
|
|
||||||
|
|
||||||
## Conclusion (Phase 2 + 2b)
|
|
||||||
Migration premise **validated on real hardware**: CA-compat + all L7/TLS
|
|
||||||
discriminators + ~12 MB footprint (arm64) + **multi-core throughput scaling**
|
|
||||||
(3.4× at 4 cores). The big unknowns are answered; what remains is
|
|
||||||
mechanical-but-large porting (Phase 3+) + a gated cutover.
|
|
||||||
|
|
||||||
## Ops note
|
|
||||||
The PoC was localhost-only (`127.0.0.1:8090`), no DNAT, cleaned up (`fuser -k
|
|
||||||
8090/tcp` + binary removed). LESSON: never `pkill -f <name>` over ssh when `<name>`
|
|
||||||
appears in the remote command line — it kills its own shell; use `fuser -k
|
|
||||||
<port>/tcp` or `pgrep | grep -v $$` + kill-by-PID.
|
|
||||||
|
|
||||||
## Next
|
|
||||||
Phase 2 + 2b gates PASSED. → **Phase 3** (hot-path feature parity: port block/
|
|
||||||
inject/strip/splice reading the real data files, parity harness vs the Python
|
|
||||||
addons). Pause for review before committing to the port — see the phased plan.
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
// SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
|
||||||
// Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
|
||||||
//
|
|
||||||
// #662 Phase 2b — controlled multi-core throughput bench. Drives full client↔
|
|
||||||
// proxy TLS handshakes (forge + ClientHello capture) in parallel. Run with
|
|
||||||
// `-cpu=1,2,4,8` to SHOW the scaling Python mitmproxy's GIL cannot do:
|
|
||||||
// go test -run x -bench BenchmarkHandshake -benchmem -cpu=1,2,4,8 ./cmd/sbxmitm
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/pem"
|
|
||||||
"math/big"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func benchCA(b *testing.B) (string, string) {
|
|
||||||
b.Helper()
|
|
||||||
dir := b.TempDir()
|
|
||||||
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
||||||
tmpl := &x509.Certificate{
|
|
||||||
SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "Bench CA"},
|
|
||||||
NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(24 * time.Hour),
|
|
||||||
IsCA: true, KeyUsage: x509.KeyUsageCertSign, BasicConstraintsValid: true,
|
|
||||||
}
|
|
||||||
der, _ := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
|
|
||||||
cp := filepath.Join(dir, "ca.pem")
|
|
||||||
kp := filepath.Join(dir, "key.pem")
|
|
||||||
cf, _ := os.Create(cp)
|
|
||||||
pem.Encode(cf, &pem.Block{Type: "CERTIFICATE", Bytes: der})
|
|
||||||
cf.Close()
|
|
||||||
kder, _ := x509.MarshalPKCS8PrivateKey(key)
|
|
||||||
kf, _ := os.Create(kp)
|
|
||||||
pem.Encode(kf, &pem.Block{Type: "PRIVATE KEY", Bytes: kder})
|
|
||||||
kf.Close()
|
|
||||||
return cp, kp
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkHandshake: steady-state forged-cert TLS handshakes/sec under parallel
|
|
||||||
// load (warm forge cache). req/s should rise ~linearly with -cpu (no GIL).
|
|
||||||
func BenchmarkHandshake(b *testing.B) {
|
|
||||||
cp, kp := benchCA(b)
|
|
||||||
ca, err := loadCA(cp, kp)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
px := &Proxy{ca: ca}
|
|
||||||
if _, err := ca.forge("example.com"); err != nil { // warm cache
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer ln.Close()
|
|
||||||
cfg := px.serverTLSConfig()
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
c, err := ln.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
s := tls.Server(c, cfg)
|
|
||||||
s.Handshake()
|
|
||||||
s.Close()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
pool := x509.NewCertPool()
|
|
||||||
pool.AddCert(ca.cert)
|
|
||||||
addr := ln.Addr().String()
|
|
||||||
ccfg := &tls.Config{ServerName: "example.com", RootCAs: pool, MinVersion: tls.VersionTLS12}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
conn, err := tls.Dial("tcp", addr, ccfg)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user