feat: implement HTTPS mTLS tunnel with auth handshake

Add HTTPS default tunnel endpoint and outbound connector using Rustls
with mTLS and an additional auth/session token.

- Add rustls, tokio-rustls, hyper, and related dependencies
- Implement TLS config builders (server/client) with cert validation
- Implement HTTPS tunnel listener with mTLS accept and auth handshake
- Implement HTTPS tunnel connector with server cert validation
- Add auth token loading from CLI value or file
- Add security gate state machine for tracking auth progression
- Fail closed for missing/invalid TLS config, cert mismatch, auth failures
- Port conflicts fail without fallback
- Reconnect revalidates all security gates
- 67 tests covering all validation assertions
This commit is contained in:
c4ch3c4d3
2026-06-03 18:29:15 -06:00
parent 03dab7c2c8
commit 393100de5a
7 changed files with 1955 additions and 19 deletions
+118
View File
@@ -0,0 +1,118 @@
/// Error types for the HTTPS mTLS tunnel.
///
/// All errors are designed to be actionable and fail-closed — they never
/// silently degrade to an insecure path.
use thiserror::Error;
/// Errors that occur when loading or configuring TLS material.
#[derive(Debug, Error)]
pub enum TlsError {
#[error("missing {0}")]
Missing(&'static str),
#[error("invalid {0}: {1}")]
Invalid(&'static str, String),
#[error("certificate identity mismatch: {0}")]
IdentityMismatch(String),
#[error("certificate expired: {0}")]
#[allow(dead_code)]
Expired(String),
#[error("certificate verification failed: {0}")]
VerificationFailed(String),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("rustls error: {0}")]
#[allow(dead_code)]
Rustls(String),
}
/// Errors that occur during application-level authentication.
#[derive(Debug, Error)]
pub enum AuthError {
#[error("auth token missing")]
Missing,
#[error("auth token invalid")]
Invalid,
#[error("auth token expired")]
#[allow(dead_code)]
Expired,
#[error("too many auth attempts")]
#[allow(dead_code)]
TooManyAttempts,
}
/// Errors that occur during tunnel lifecycle operations.
#[derive(Debug, Error)]
pub enum TunnelError {
#[error("TLS error: {0}")]
Tls(#[from] TlsError),
#[error("auth error: {0}")]
Auth(#[from] AuthError),
#[error("bind error on {0}: {1}")]
BindError(String, String),
#[error("connection refused: {0}")]
ConnectionRefused(String),
#[error("connection closed")]
ConnectionClosed,
#[error("port {0} already in use")]
PortInUse(u16),
#[error("shutdown requested")]
#[allow(dead_code)]
Shutdown,
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("protocol error: {0}")]
Protocol(String),
#[error("timeout: {0}")]
#[allow(dead_code)]
Timeout(String),
}
/// Errors returned by the connector side.
#[allow(dead_code)]
#[derive(Debug, Error)]
pub enum ConnectorError {
#[error("TLS handshake failed: {0}")]
TlsHandshake(String),
#[error("server certificate verification failed: {0}")]
ServerCertVerification(String),
#[error("server certificate identity mismatch for {0}: {1}")]
ServerIdentityMismatch(String, String),
#[error("server certificate expired")]
ServerCertExpired,
#[error("auth rejected: {0}")]
AuthRejected(String),
#[error("connection failed: {0}")]
ConnectionFailed(String),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("protocol error: {0}")]
Protocol(String),
#[error("timeout: {0}")]
Timeout(String),
}