Skip to content

Conversation

@vietcgi
Copy link

@vietcgi vietcgi commented Nov 26, 2025

Summary

  • Add TLS encryption support for KumoProxy SOCKS5 server connections
  • Implement RFC 1929 username/password authentication for client access control
  • Add client-side TLS support in kumod for connecting to TLS-enabled proxies
  • Support mTLS (mutual TLS) for client certificate verification

Fixes #451

Changes

Proxy Server (crates/proxy-server):

  • New --tls-certificate, --tls-private-key options for server TLS
  • New --tls-client-ca option for mTLS client verification
  • New --auth-file option for TOML-based user credentials
  • Generic async stream handling (works with both plain TCP and TLS)
  • Constant-time password comparison to prevent timing attacks

KumoMTA Client (crates/kumod):

  • New socks5_proxy_username and socks5_proxy_password fields
  • New socks5_proxy_tls, socks5_proxy_tls_ca, socks5_proxy_tls_insecure fields
  • TLS connector with optional CA verification and insecure mode

Documentation:

  • Added docs for all new make_egress_source options

Test plan

  • Unit tests for auth module (9 tests pass)
  • Manual testing on Linux (Ubuntu 24.04 via multipass):
    • Plain TCP + NOAUTH
    • Plain TCP + Auth (valid/invalid credentials)
    • TLS + NOAUTH
    • TLS + Auth
    • Auth rejection with wrong password
    • TLS mismatch (plain client -> TLS server)
  • cargo check, cargo clippy pass
  • Backward compatible (no auth/TLS required by default)

Copy link
Collaborator

@wez wez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this!

I have some comments about code duplication and suggestions for how to avoid it.
There's a decent amount of work to do to get things into the right shape for this to fit the kumo-way of doing things and be in better shape to maintain in the long term.

One thing that is missing here is integration tests of the new proxy functionality; since we don't have any already, that's not a harsh critique of this PR, but this PR does increase the complexity enough that we really should have integration tests before we go forwards with this.

One of my suggestions is to adopt kumo-server-common and its lua config stuff. A side-effect of that is that it helps to facilitate integration testing. If you follow the pattern that we use in the http and esmtp listeners of logging the endpoint that we're listening to:

pub async fn run(self) -> anyhow::Result<()> {
let connection_gauge = connection_gauge();
let listener = TcpListener::bind(&self.listen)
.await
.with_context(|| format!("failed to bind to {}", self.listen))?;
let addr = listener.local_addr()?;
tracing::info!("smtp listener on {addr:?}");

then you can clone crates/integration-tests/src/tsa.rs (which is a test fixture for an isolated tsa-daemon) as crates/integration-tests/src/proxy.rs and this region of code:

if line.contains("listener on") {
let mut fields: Vec<&str> = line.trim().split(' ').collect();
while fields.len() > 4 {
fields.remove(0);
}
let proto = fields[0];
let addr = fields[3];
let addr: SocketAddr = addr.parse()?;
listeners.insert(proto.to_string(), addr);
}

will detect the listener port. That allows defining integration tests that listen on port 0 to have the kernel pick an isolated port for the purpose of running parallel/concurrent integration tests.

There's more that I could say about integration testing, but I suggest looking at all the other points first, then we can regroup and discuss testing in a bit more detail.

I know that what I've mentioned here in this review is a decent amount of additional work, but these things are necessary in order for things to be maintainable and feel more cohesive with the rest of the kumo functionality!

@@ -0,0 +1,104 @@
//! TLS configuration helpers for the proxy server.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by switching to using kumo-server-common as proposed above, we can then simply reference its make_server_config function directly rather than copying the code into this file.

anyhow::ensure!(
pass.len() < 256,
"username is too long for SOCKS5 protocol"
"password is too long for SOCKS5 protocol"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

@vietcgi
Copy link
Author

vietcgi commented Nov 26, 2025

Hey @wez , thanks for the thorough review. Pushed updates addressing everything:

  • Extracted kumo-tls-helper crate (tls.rs + traits.rs from rfc5321)
  • Lua-based auth via proxy_server_auth_1929 callback - removed the file-based auth.rs
  • kumo.start_proxy_listener with TLS options in Lua, not CLI
  • Using kumo_server_common::tls_helpers::make_server_config() directly
  • Added proxy.rs test fixture following tsa.rs pattern with port 0 detection
  • Kept backwards compat for legacy CLI (--listen, --timeout-seconds, etc.)

Used --policy instead of --proxy-config to match kumod/tsa-daemon conventions.

Integration tests currently just verify startup. Let me know if you want more coverage (actual SOCKS5 connections, auth flows, etc.) before merging.

Copy link
Collaborator

@wez wez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, this is really shaping up nicely!

@vietcgi
Copy link
Author

vietcgi commented Dec 6, 2025

Hey @wez,

Pushed the fixes. Renamed things as requested, swapped --policy to --proxy-config with proper conflicts_with, ditched the TLS CLI options for temp Lua config generation, used declare_event!, fixed the auth flow, and added a bunch of e2e tests. All green.

Copy link
Collaborator

@wez wez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your work on this, I'm excited to see this get merged!

It took me a little while to get back to you on this next round of review because I was building out the new ACL system with this in mind and wanted to have that in a better state before suggesting here how to integrate those changes here.

@vietcgi
Copy link
Author

vietcgi commented Dec 19, 2025

Hey @wez,

I've addressed all your feedback from today's review:

Switched to AuthInfo return type with the new ACL system integration
Using wait_for_source_summary instead of sleep in tests + proper delivery assertions
Consolidated the lua configs into just proxy_with_auth.lua
Reverted the maildir-sink.lua changes
All the small suggestions (MailGenParams::default, error handling cleanup, etc)

I squashed it down to 2 clean commits. Ready for another look when you get a chance!

Copy link
Collaborator

@wez wez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I think we're almost there!

@vietcgi
Copy link
Author

vietcgi commented Dec 24, 2025

Hey @wez , thanks for the thorough review! I've addressed all your feedback:

Bumped the test timeouts to 50s
Added the delivery count assertions in both negative tests
Refactored the policy path logic with the let legacy_file; match pattern
Inlined authenticate_user into perform_auth. You're right, it's cleaner this way

All tests passing. Let me know if there's anything else.

vietcgi and others added 4 commits January 7, 2026 14:15
Move TLS configuration and async stream traits from rfc5321
into a new shared kumo-tls-helper crate for reuse in
proxy-server and other crates.

Refs KumoCorp#451
Add TLS encryption and username/password authentication support for
the KumoProxy SOCKS5 server, with full Lua configuration capabilities.

Proxy Server Changes:
- Add kumo.start_proxy_listener() Lua function with TLS support
- Add proxy_server_auth_rfc1929 event for Lua-based auth validation
- Return AuthInfo from auth for ACL system integration
- Support optional and required authentication modes
- Maintain backwards-compatible legacy CLI mode (--listen, --timeout-seconds)

Client Changes (kumod):
- Add socks5_proxy_username/password fields to egress source
- Add socks5_proxy_tls options for connecting to TLS-enabled proxies

Breaking Changes:
- Cache renamed from rfc5321_rustls_config to rustls_client_config

Fixes KumoCorp#451
Closes: 459
We must only config.put in the success case, otherwise we might
leave a broken lua context in the cache

refs: KumoCorp#459
@wez wez force-pushed the feature/proxy-auth branch from 8f4af96 to 3e535eb Compare January 7, 2026 14:45
Copy link
Collaborator

@wez wez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

I noticed when checking it out and building locally that splice(2) wasn't hooked up, so I added some code that should work to enable that again. It compiles, but I haven't explicitly verified that it it is operating correctly. I force-pushed my updates to your branch.

Could you take a look to verify that splice is operating correctly? This doesn't strictly require an integration test (although I would appreciate it if you feel like making one!). To verify I suppose that you could inspect the log output to see that splice is being used, and then also check to make sure that we're not leaking any additional file descriptors while the service is running and handling connections. I would suggest looking at lsof -np $(pgrep proxy-server) and comparing splice to no-splice mode.

With that verified, the only thing remaining is to write up a doc page for the new kumo.start_proxy_listener function, then add an entry to the changelog for it!

Thank you again for your work and attention here!

@vietcgi
Copy link
Author

vietcgi commented Jan 16, 2026

Hey @wez,

Added the docs and changelog entry. Verified splice logic looks correct.

Ready for merge when you get a chance!

Kevin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add authentication to KumoProxy

2 participants