Add SOCKS5 CONNECT support
This commit is contained in:
@@ -6,6 +6,8 @@ Hermes is a minimal HTTP/1.1 forward proxy written in Rust. It supports streamin
|
||||
|
||||
- Absolute-form HTTP forwarding (`GET http://host/path`)
|
||||
- HTTPS and arbitrary TCP tunneling with `CONNECT host:port`
|
||||
- Optional SOCKS5 CONNECT listener with no-auth and username/password authentication
|
||||
- SOCKS5 IPv4, IPv6, and domain destinations with shared ACL enforcement
|
||||
- Streaming request and response bodies
|
||||
- Upstream connection pooling and configurable timeouts
|
||||
- Ordered allow/deny ACLs using Basic authentication, client IP networks, and destination domains
|
||||
@@ -67,7 +69,9 @@ The Gitea workflow in `.gitea/workflows/build.yaml` builds and publishes `linux/
|
||||
|
||||
```yaml
|
||||
listen_addr: "127.0.0.1:8080"
|
||||
socks5_listen_addr: "127.0.0.1:1080"
|
||||
connect_timeout_ms: 10000
|
||||
socks5_handshake_timeout_ms: 10000
|
||||
upstream_timeout_ms: 30000
|
||||
|
||||
telemetry:
|
||||
@@ -110,7 +114,9 @@ acl:
|
||||
| Field | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `listen_addr` | `127.0.0.1:8080` | Downstream listen address |
|
||||
| `socks5_listen_addr` | `null` | Optional SOCKS5 listen address; disabled when omitted |
|
||||
| `connect_timeout_ms` | `10000` | TCP connection timeout for `CONNECT` and the HTTP connector |
|
||||
| `socks5_handshake_timeout_ms` | `10000` | Timeout for SOCKS5 negotiation and CONNECT request parsing |
|
||||
| `upstream_timeout_ms` | `30000` | Time limit for receiving upstream HTTP response headers |
|
||||
| `telemetry` | see OpenTelemetry below | Logging, resource, and OTLP configuration |
|
||||
| `acl.default_action` | `deny` | Action when no rule matches |
|
||||
@@ -200,6 +206,35 @@ Password verification is synchronous and inexpensive, and digest comparisons are
|
||||
|
||||
**Basic authentication is not encrypted in transit.** A client sending Basic credentials over cleartext HTTP exposes them to the network. Use Basic authentication only on localhost or a trusted private network unless downstream TLS is added. Do not expose Hermes publicly without appropriate network controls.
|
||||
|
||||
## SOCKS5
|
||||
|
||||
Set `socks5_listen_addr` to enable a separate SOCKS5 listener alongside the HTTP proxy:
|
||||
|
||||
```yaml
|
||||
listen_addr: "127.0.0.1:8080"
|
||||
socks5_listen_addr: "127.0.0.1:1080"
|
||||
socks5_handshake_timeout_ms: 10000
|
||||
```
|
||||
|
||||
The listener supports SOCKS5 CONNECT with IPv4, IPv6, and domain destinations. Domain destinations are resolved by Hermes; use `--socks5-hostname` with curl to request proxy-side DNS resolution:
|
||||
|
||||
```sh
|
||||
# Client-side DNS with an IP destination
|
||||
curl --socks5 127.0.0.1:1080 http://198.51.100.10/
|
||||
|
||||
# Proxy-side DNS with a domain destination
|
||||
curl --socks5-hostname 127.0.0.1:1080 http://example.com/
|
||||
|
||||
# Username/password authentication using the configured ACL user
|
||||
curl --socks5-hostname 127.0.0.1:1080 \
|
||||
--proxy-user 'alice:your-password' \
|
||||
http://example.com/
|
||||
```
|
||||
|
||||
SOCKS5 reuses the ACL's salted password hashes and applies `users`, `networks`, and `domains` before upstream dialing. Username/password negotiation is selected when required by the ACL; otherwise no-auth is used. Authentication is cleartext on the network, so restrict the listener to localhost or trusted networks. ACL denial returns SOCKS reply `0x02` before any upstream connection.
|
||||
|
||||
Only CONNECT is supported. SOCKS4, BIND, and UDP ASSOCIATE are not implemented. Docker users can publish the listener with `-p 127.0.0.1:1080:1080` when it is enabled.
|
||||
|
||||
## OpenTelemetry
|
||||
|
||||
Hermes exports traces, metrics, and logs independently to an OpenTelemetry Collector using OTLP/HTTP (protobuf). All settings come from `telemetry` in YAML; `OTEL_*` environment variables are not read.
|
||||
@@ -247,6 +282,7 @@ In another terminal, run Hermes and send a request through it. The collector wil
|
||||
- `proxy.upstream`: upstream HTTP client operation
|
||||
- `proxy.connect`: `CONNECT` handshake and tunnel lifetime
|
||||
- `proxy.upstream_connect`: upstream TCP connection establishment
|
||||
- `socks5.connect`: SOCKS5 negotiation, authorization, upstream connection, and tunnel lifetime
|
||||
|
||||
For plain HTTP, incoming W3C `traceparent` and `tracestate` headers establish the parent context, and a new context is injected upstream. Trace context cannot be propagated inside an encrypted `CONNECT` tunnel. Request spans include the ACL decision, reason, and matched rule name without credentials.
|
||||
|
||||
@@ -264,7 +300,7 @@ Hermes uses standard HTTP instrument names where applicable and adds proxy-speci
|
||||
- `hermes.proxy.tunnel.bytes`
|
||||
- `hermes.proxy.acl.decisions`
|
||||
|
||||
Metric attributes are deliberately bounded. Raw URLs, destination hostnames, usernames, rule names, and client addresses are not metric attributes.
|
||||
Metric attributes are deliberately bounded. Tunnel and ACL metrics include `proxy.protocol` to distinguish HTTP, HTTP CONNECT, and SOCKS5. Raw URLs, destination hostnames, usernames, rule names, and client addresses are not metric attributes.
|
||||
|
||||
### Logs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user