Skip to content
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@
/shard.lock

/http_proxy

# Local MITM certificates/keys generated for manual testing
/.mitm-certs/
/mitm-ca.*
/mitm.*
65 changes: 65 additions & 0 deletions MITM_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# MITM Production Readiness Checklist

Status key:

- [x] Done
- [ ] Not done
- [~] Partial

## Security

- [ ] Store CA private key outside repository with strict permissions (`600`) and dedicated service account.
- [ ] Add CA key rotation procedure and documented migration plan.
- [ ] Add encrypted CA key / passphrase support.
- [~] Keep sensitive logging disabled by default (`debug: false`), but add redaction for auth/cookies/tokens when debug is enabled.
- [ ] Add host allowlist/denylist policy for MITM interception.

## Certificate Generation

- [x] Dynamic per-host certificate generation is implemented.
- [x] In-process generator is implemented in `src/http/proxy/server/certificate_generator.cr`.
- [x] Certificate cache directory support exists (`certificate_cache_dir`).
- [x] Basic SAN generation exists for DNS/IP hostnames.
- [ ] Serial number strategy should be strengthened (currently timestamp-based).
- [ ] Add cert cache lifecycle controls (TTL/max size/pruning).

## Protocol Behavior

- [x] CONNECT MITM flow for HTTP/1.1 is implemented.
- [x] ALPN is constrained to HTTP/1.1 in server contexts.
- [~] Non-HTTP streams are detected and aborted in MITM path; consider explicit passthrough/tunnel fallback policy.
- [ ] Define and document policy for HTTP/2/HTTP/3 handling in production mode.

## Reliability & Resilience

- [x] Mutex-protected per-host context cache prevents concurrent generation races.
- [ ] Add bounded concurrency / connection limits.
- [ ] Add timeout and retry policy for upstream requests and TLS handshakes.
- [ ] Add graceful shutdown behavior for active MITM sessions under load.
- [ ] Add robust error taxonomy and user-facing failure modes.

## Observability

- [x] Debug output is gated behind `MITMConfig#debug`.
- [ ] Replace ad-hoc debug prints with structured logs for production diagnostics.
- [ ] Add metrics (handshake failures, generation latency, cache hits/misses, upstream status rates).
- [ ] Add health/readiness check endpoint or command.

## Testing

- [ ] Unit tests for certificate generator (key generation, CSR, signing, SAN DNS/IP).
- [ ] Integration tests for browser trust flow (Firefox/Chromium with local CA).
- [ ] Negative tests (invalid CA files, malformed CONNECT targets, broken cert cache files).
- [ ] Load/stress tests for concurrent CONNECT traffic and cert generation.

## Operations

- [~] Runtime sample exists (`samples/server_mitm.cr`) and README usage is documented.
- [ ] Add deployment profile (systemd/service config, restart policy, file permissions).
- [ ] Add operational runbook (bootstrap CA, trust install, rotation, incident recovery).
- [ ] Add secure secret management guidance for CA private key.

## Current Assessment

- Overall maturity: **Advanced MVP / Beta**
- Not yet production-ready due to outstanding hardening, testing depth, and operational controls.
56 changes: 55 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,60 @@ server = HTTP::Proxy::Server.new(handlers: [
end
```

#### HTTPS MITM (MVP, CONNECT HTTP/1.1 only)

```crystal
server = HTTP::Proxy::Server.new
server.mitm = HTTP::Proxy::Server::MITMConfig.new(
ca_certificate_path: "./certs/mitm-ca.crt",
ca_private_key_path: "./certs/mitm-ca.key",
certificate_cache_dir: "./.mitm-certs",
debug: false,
)

address = server.bind_tcp("127.0.0.1", 8080)
puts "Listening on http://#{address}"
server.listen
```

Notes:

- This enables HTTPS interception for `CONNECT` requests.
- The certificate authority (CA) used to sign MITM certs must be trusted by clients.
- This MVP is intended for HTTP/1.1 traffic over CONNECT.

Certificate files:

- `mitm-ca.crt` / `mitm-ca.key`: local CA (trust anchor used to sign dynamic leaf certs).
- `./.mitm-certs/*.crt` / `./.mitm-certs/*.key`: generated per-host leaf certificates.

Static mode is still available:

- `mitm.crt` / `mitm.key`: a single leaf certificate and key presented by the proxy.

Firefox setup:

- Import `mitm-ca.crt` in **Authorities** and trust it for websites.
- Do **not** import `mitm.crt` into Authorities.

Server setup:

- Dynamic mode (recommended): pass `mitm-ca.crt` as `ca_certificate_path` and `mitm-ca.key` as `ca_private_key_path`.
- Static mode: pass `mitm.crt` as `certificate_chain_path` and `mitm.key` as `private_key_path`.

Run dynamic MITM sample:

```bash
crystal run samples/server_mitm.cr
```

Useful flags:

- `--ca-cert ./mitm-ca.crt`
- `--ca-key ./mitm-ca.key`
- `--cache-dir ./.mitm-certs`
- `--debug` (enable verbose MITM request/response output)

### Client

#### Make request with proxy
Expand Down Expand Up @@ -112,7 +166,7 @@ puts response.body
* [x] HTTPS Proxy: CONNECT support
* [x] Make context.request & context.response writable
* [x] Basic Authentication
* [ ] MITM HTTPS Proxy
* [x] MITM HTTPS Proxy (MVP, CONNECT HTTP/1.1)

### Proxy client

Expand Down
30 changes: 30 additions & 0 deletions mitm-ca.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFITCCAwmgAwIBAgIUKE5Bbf3Q2Y6N9QR1x4yEZPa0pFIwDQYJKoZIhvcNAQEL
BQAwGDEWMBQGA1UEAwwNTG9jYWwgTUlUTSBDQTAeFw0yNjAyMjcxNTM5MjhaFw0z
NjAyMjUxNTM5MjhaMBgxFjAUBgNVBAMMDUxvY2FsIE1JVE0gQ0EwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQDb8uaLhVtvrSso3j/G0HJBiz5zMl6zUBnz
zLuv0Dhqpyia4t/6qKu8jco4+iW978bjx4LvsNPzM/jq6Mc0I/BTH6opP/ckPGfL
IK7VYPoaWbQMFy0NpEmesgZgyb62TiSCuWtlBNDRYOyqriTcH4xMk9M1PKtx0GBp
cmphX+b0o/VHzXJ3WcxNnLn3k/vTmSGjzBV3eHE47A2iZVR0ukUm/GJOz3i+zaWq
CimY7VGUEuOK+iSf/zcXE+NcUkofdw3ZuLsRrBZZyQeh+lGRD7YU6OYsczuzb3XJ
AECIte83Ko03KkLxyoD+rYlT+CVb0S4O0w5TycGxLa5RzzCXmu0ZVoEtz9bw6VoD
eiBiI+FGlNFxgSi6WtRCsZ11Uw9Z/Pry2Ot62k18U9rS5lQhTOqplfEmksKIEsTK
7RDABEPeP8PPASrVr6BuSzgWq2FbVVu41tZ/TWwXwY9KsbhYTe9Sr8HbvWoT8mQS
UAe/lZWvCZvP8vp0fnHIj5Ci+2nGLy42p0Ft6ON7GEfbqQV63bKWZjP6jtgF7SNs
Tb7cMCyam4J5f181Uds0qMw0IdobUmG2LN64CNAVjF+8d+VRobkO8sKVYxIAAO+G
JeOTOtRSMvLD2+SQQ27XQBx4lMNeOo6fFs3jkcG7LQ8okXwo4lc9StVKWexh1WhP
APVBZs9DLQIDAQABo2MwYTAdBgNVHQ4EFgQUr4xEWjgXxTQgiArF09zoj4GIxw4w
HwYDVR0jBBgwFoAUr4xEWjgXxTQgiArF09zoj4GIxw4wDwYDVR0TAQH/BAUwAwEB
/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAMfKybOvRZalGj8p
IZxm/QERRYbk8mTuc9j/KZ4B6Nrr7DLKi5Dsx/dqBRYkdsQEJzxDw2RESYW+HxlS
BtLTpJyO4Mj9oBzaN0MFfKk8gAgNEeT7hbdKwiCoug26r/+LOfnhddlw1gayB8JF
Egu4x67EGWwP2gZJRJYY1/fjr8F39TByqhBWTzbmpq6INNu9hIfBSq+libPwnTNL
dBvmeY9mo7441HScXeGW8JJnf1sg3vGwC+3qDVFZilvV33/FRehb3g78yR8VTyp7
9dEi0y/PqFOLOQFvvgwKo6g8yruoCPGeMLW99za6Xts79/tpj1QYIqsJb31ESxEc
ZBEqPXuRJOPjs2mGw3bnoYYlTrKB50gxezN1nOd4uvXohqKmoZ/S4Oc6fI8VuaHc
SfC9PJQ9AeW/DMgvBdo3dIc2KTd0536iW6RwybMKg7KGM7J39LRjbIriM1hU9wzV
KJW5wsUqENu7S+Vy2IUk8uZ4YJGPAbVbOSV2mpN03DJebpVeY6u5IoIj1LqDPZ/L
SXxKrlPSMCkrHfUoJWSIFuXglVYQnS0vXk7NyFbtT4tkb+7QO7XprJA9DD+oLpSK
GRH53WVAZL8JbCA5EifM5Ff24qCFHgzfe5fvNqUfF3qy5CyRmxugtEUzmAkJJfo/
Y8ZH/T0v0jhMXQgbLRMr2t/Gz/xT
-----END CERTIFICATE-----
52 changes: 52 additions & 0 deletions mitm-ca.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
2XDQweSmPKqwgTMSvbabrXiI0Ekm
-----END PRIVATE KEY-----
2 changes: 1 addition & 1 deletion samples/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ require "../src/http_proxy"
require "option_parser"

host = "127.0.0.1"
port = 8081
port = 8080

OptionParser.parse do |opts|
opts.on("-h HOST", "--host HOST", "define host to run server") do |opt|
Expand Down
65 changes: 65 additions & 0 deletions samples/server_mitm.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require "../src/http_proxy"
require "option_parser"

host = "127.0.0.1"
port = 8080
ca_certificate_path = "./mitm-ca.crt"
ca_private_key_path = "./mitm-ca.key"
certificate_cache_dir = "./.mitm-certs"
upstream_insecure = false
debug = false

OptionParser.parse do |opts|
opts.banner = "Usage: crystal run samples/server_mitm.cr -- [arguments]"

opts.on("-h HOST", "--host HOST", "define host to run server") do |opt|
host = opt
end

opts.on("-p PORT", "--port PORT", "define port to run server") do |opt|
port = opt.to_i
end

opts.on("--ca-cert PATH", "path to MITM CA certificate PEM (dynamic per-host cert mode)") do |opt|
ca_certificate_path = opt
end

opts.on("--ca-key PATH", "path to MITM CA private key PEM (dynamic per-host cert mode)") do |opt|
ca_private_key_path = opt
end

opts.on("--cache-dir PATH", "directory to store generated host certificates") do |opt|
certificate_cache_dir = opt
end

opts.on("--upstream-insecure", "disable upstream TLS verification") do
upstream_insecure = true
end

opts.on("--debug", "enable verbose MITM request/response debug output") do
debug = true
end
end

server = HTTP::Proxy::Server.new(handlers: [
HTTP::LogHandler.new,
])

server.mitm = HTTP::Proxy::Server::MITMConfig.new(
ca_certificate_path: ca_certificate_path,
ca_private_key_path: ca_private_key_path,
certificate_cache_dir: certificate_cache_dir,
upstream_insecure: upstream_insecure,
debug: debug,
)

address = server.bind_tcp(host, port)
puts "Listening on http://#{address}"
puts "MITM mode enabled (CONNECT HTTP/1.1 MVP)"
puts "Certificate mode: dynamic per-host"
puts "CA certificate: #{ca_certificate_path}"
puts "CA private key: #{ca_private_key_path}"
puts "Certificate cache dir: #{certificate_cache_dir}"
puts "Upstream TLS verification: #{upstream_insecure ? "DISABLED" : "ENABLED"}"
puts "MITM debug output: #{debug ? "ENABLED" : "DISABLED"}"
server.listen
2 changes: 0 additions & 2 deletions spec/client_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ describe HTTP::Proxy::Client do

(client.proxy?).should be_true
(response.status_code).should eq(407) # 407 Proxy Authentication Required


ensure
wants_close.send(nil)
end
Expand Down
48 changes: 48 additions & 0 deletions src/ext/openssl/lib_crypro.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
require "openssl/lib_crypto"

lib LibCrypto
alias EVP_PKEY = Void*
alias EVP_PKEY_CTX = Void*
alias X509_REQ = Void*
alias ASN1_INTEGER = Void*
alias ASN1_TIME = Void*

EVP_PKEY_RSA = 6

fun evp_pkey_ctx_new_id = EVP_PKEY_CTX_new_id(id : Int32, e : Void*) : EVP_PKEY_CTX
fun evp_pkey_ctx_free = EVP_PKEY_CTX_free(ctx : EVP_PKEY_CTX)
fun evp_pkey_keygen_init = EVP_PKEY_keygen_init(ctx : EVP_PKEY_CTX) : Int32
fun evp_pkey_ctx_ctrl_str = EVP_PKEY_CTX_ctrl_str(ctx : EVP_PKEY_CTX, type : UInt8*, value : UInt8*) : Int32
fun evp_pkey_keygen = EVP_PKEY_keygen(ctx : EVP_PKEY_CTX, ppkey : EVP_PKEY*) : Int32
fun evp_pkey_free = EVP_PKEY_free(pkey : EVP_PKEY)

fun bio_new_file = BIO_new_file(filename : UInt8*, mode : UInt8*) : Bio*
fun bio_free_all = BIO_free_all(bio : Bio*) : Int32

fun pem_read_bio_private_key = PEM_read_bio_PrivateKey(bp : Bio*, x : EVP_PKEY*, cb : Void*, u : Void*) : EVP_PKEY
fun pem_write_bio_private_key = PEM_write_bio_PrivateKey(bp : Bio*, x : EVP_PKEY, enc : Void*, kstr : UInt8*, klen : Int32, cb : Void*, u : Void*) : Int32

fun pem_read_bio_x509 = PEM_read_bio_X509(bp : Bio*, x : X509*, cb : Void*, u : Void*) : X509
fun pem_write_bio_x509_req = PEM_write_bio_X509_REQ(bp : Bio*, x : X509_REQ) : Int32
fun pem_write_bio_x509 = PEM_write_bio_X509(bp : Bio*, x : X509) : Int32

fun x509_req_new = X509_REQ_new : X509_REQ
fun x509_req_free = X509_REQ_free(req : X509_REQ)
fun x509_req_set_version = X509_REQ_set_version(req : X509_REQ, version : Long) : Int32
fun x509_req_set_subject_name = X509_REQ_set_subject_name(req : X509_REQ, name : X509_NAME) : Int32
fun x509_req_set_pubkey = X509_REQ_set_pubkey(req : X509_REQ, pkey : EVP_PKEY) : Int32
fun x509_req_sign = X509_REQ_sign(req : X509_REQ, pkey : EVP_PKEY, md : EVP_MD) : Int32
fun x509_req_get_subject_name = X509_REQ_get_subject_name(req : X509_REQ) : X509_NAME
fun x509_req_get_pubkey = X509_REQ_get_pubkey(req : X509_REQ) : EVP_PKEY

fun x509_set_version = X509_set_version(x : X509, version : Long) : Int32
fun x509_set_issuer_name = X509_set_issuer_name(x : X509, name : X509_NAME) : Int32
fun x509_set_pubkey = X509_set_pubkey(x : X509, pkey : EVP_PKEY) : Int32
fun x509_sign = X509_sign(x : X509, pkey : EVP_PKEY, md : EVP_MD) : Int32
fun x509_get_serial_number = X509_get_serialNumber(x : X509) : ASN1_INTEGER
fun x509_getm_not_before = X509_getm_notBefore(x : X509) : ASN1_TIME
fun x509_getm_not_after = X509_getm_notAfter(x : X509) : ASN1_TIME

fun asn1_integer_set = ASN1_INTEGER_set(a : ASN1_INTEGER, v : Long) : Int32
fun x509_gmtime_adj = X509_gmtime_adj(s : ASN1_TIME, adj : Long) : ASN1_TIME
end
5 changes: 4 additions & 1 deletion src/http/proxy/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ require "socket"
require "wait_group"
require "./server/handler"
require "./server/context"
require "./server/mitm_config"
{% if !flag?(:without_openssl) %}
require "openssl"
{% end %}
Expand All @@ -28,6 +29,8 @@ class HTTP::Proxy::Server
# Returns `true` if this server is listening on its sockets.
getter? listening : Bool = false

property mitm : MITMConfig?

# Creates a new HTTP Proxy server
def initialize
handler = build_middleware
Expand Down Expand Up @@ -61,7 +64,7 @@ class HTTP::Proxy::Server
end

private def build_middleware(handler : (Context ->)? = nil)
proxy_handler = Handler.new
proxy_handler = Handler.new(-> { @mitm })
proxy_handler.next = handler if handler
proxy_handler
end
Expand Down
Loading
Loading