Skip to content

Commit d50ed81

Browse files
authored
Merge pull request #110 from SenseUnit/custom_tls
TLS options
2 parents 1d8369d + 4490fd8 commit d50ed81

File tree

4 files changed

+303
-121
lines changed

4 files changed

+303
-121
lines changed

README.md

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,7 @@ dumbproxy can select upstream proxy dynamically invoking `getProxy` JS function
295295

296296
Note that this option can be repeated multiple times, same as `-proxy` option for chaining of proxies. These two options can be used together and order of chaining will be as they come in command line. For generalization purposes we can say that `-proxy` option is equivalent to `-js-proxy-router` option with script which returns just one static proxy.
297297

298-
`getProxy` function is invoked with the [same parameters](#access-filter-by-js-script) as the `access` function. But unlike `access` function it is expected to return proxy URL in string format *scheme://[user:password@]host:port* or empty string `""` if no additional upstream proxy needed (i.e. direct connection if there are no other proxy dialers defined in chain).
299-
300-
Supported proxy schemes are:
301-
* `http` - regular HTTP proxy with the CONNECT method support.
302-
* `https` - HTTP proxy over TLS connection.
303-
* `socks5`, `socks5h` - SOCKS5 proxy with hostname resolving via remote proxy.
304-
* `set-src-hints` - not an actual proxy, but a signal to use different source IP address hints for this connection. It's useful to route traffic across multiple network interfaces, including VPN connections. URL has to have one query parameter `hints` with a comma-separated list of IP addresses. See `-ip-hints` command line option for more details. Example: `set-src-hints://?hints=10.2.0.2`
298+
`getProxy` function is invoked with the [same parameters](#access-filter-by-js-script) as the `access` function. But unlike `access` function it is expected to return proxy URL in string format *scheme://[user:password@]host:port* or empty string `""` if no additional upstream proxy needed (i.e. direct connection if there are no other proxy dialers defined in chain). See [supported upstream proxy schemes](#supported-upstream-proxy-schemes) for details.
305299

306300
Example:
307301

@@ -321,6 +315,24 @@ function getProxy(req, dst, username) {
321315
>
322316
> This shouldn't be much of concern, though, if `getProxy` function doesn't use dst.resolvedHost and returns consistent values across invocations with the rest of inputs having same values.
323317
318+
## Supported upstream proxy schemes
319+
320+
Supported proxy schemes are:
321+
322+
* `http` - regular HTTP proxy with the CONNECT method support. Examples: `http://example.org:3128`.
323+
* `https` - HTTP proxy over TLS connection. Examples: `https://user:[email protected]`, `https://example.org?cert=cert.pem&key=key.pem`. This method also supports additional parameters passed in query string:
324+
* `cafile` - file with CA certificates in PEM format used to verify TLS peer.
325+
* `sni` - override value of ServerName Indication extension.
326+
* `peername` - expect specified name in peer certificate. Empty string relaxes any name constraints.
327+
* `cert` - file with user certificate for mutual TLS authentication. Should be used in conjunction with `key`.
328+
* `key` - file with private key matching user certificate specified with `cert` option.
329+
* `ciphers` - colon-separated list of enabled TLS ciphersuites.
330+
* `curves` - colon-separated list of enabled TLS key exchange curves.
331+
* `min-tls-version` - minimum TLS version.
332+
* `max-tls-version` - maximum TLS version.
333+
* `socks5`, `socks5h` - SOCKS5 proxy with hostname resolving via remote proxy. Example: `socks5://127.0.0.1:9050`.
334+
* `set-src-hints` - not an actual proxy, but a signal to use different source IP address hints for this connection. It's useful to route traffic across multiple network interfaces, including VPN connections. URL has to have one query parameter `hints` with a comma-separated list of IP addresses. See `-ip-hints` command line option for more details. Example: `set-src-hints://?hints=10.2.0.2`
335+
324336
## Synopsis
325337

326338
```
@@ -354,8 +366,6 @@ Usage of /home/user/go/bin/dumbproxy:
354366
restrict autocert domains to this comma-separated list
355367
-bind-address string
356368
HTTP proxy listen address. Set empty value to use systemd socket activation. (default ":8080")
357-
-proxyproto
358-
listen proxy protocol
359369
-bind-pprof string
360370
enables pprof debug endpoints
361371
-bind-reuseport
@@ -372,6 +382,8 @@ Usage of /home/user/go/bin/dumbproxy:
372382
enable TLS and use certificate
373383
-ciphers string
374384
colon-separated list of enabled ciphers
385+
-curves string
386+
colon-separated list of enabled key exchange curves
375387
-deny-dst-addr value
376388
comma-separated list of CIDR prefixes of forbidden IP addresses (default 127.0.0.0/8, 0.0.0.0/32, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, ::1/128, ::/128, fe80::/10)
377389
-disable-http2
@@ -400,16 +412,20 @@ Usage of /home/user/go/bin/dumbproxy:
400412
key for TLS certificate
401413
-list-ciphers
402414
list ciphersuites
415+
-list-curves
416+
list key exchange curves
403417
-max-tls-version value
404418
maximum TLS version accepted by server (default TLS13)
405419
-min-tls-version value
406-
minimal TLS version accepted by server (default TLS12)
420+
minimum TLS version accepted by server (default TLS12)
407421
-passwd string
408422
update given htpasswd file and add/set password for username. Username and password can be passed as positional arguments or requested interactively
409423
-passwd-cost int
410424
bcrypt password cost (for -passwd mode) (default 4)
411425
-proxy value
412426
upstream proxy URL. Can be repeated multiple times to chain proxies. Examples: socks5h://127.0.0.1:9050; https://user:[email protected]:443
427+
-proxyproto
428+
listen proxy protocol
413429
-req-header-timeout duration
414430
amount of time allowed to read request headers (default 30s)
415431
-user-ip-hints

dialer/upstream.go

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,105 @@ import (
1616
"sync"
1717

1818
xproxy "golang.org/x/net/proxy"
19+
20+
"github.com/SenseUnit/dumbproxy/tlsutil"
1921
)
2022

2123
type HTTPProxyDialer struct {
22-
address string
23-
tls bool
24-
userinfo *url.Userinfo
25-
next Dialer
24+
address string
25+
tlsConfig *tls.Config
26+
userinfo *url.Userinfo
27+
next Dialer
2628
}
2729

28-
func NewHTTPProxyDialer(address string, tls bool, userinfo *url.Userinfo, next LegacyDialer) *HTTPProxyDialer {
30+
func NewHTTPProxyDialer(address string, tlsConfig *tls.Config, userinfo *url.Userinfo, next LegacyDialer) *HTTPProxyDialer {
2931
return &HTTPProxyDialer{
30-
address: address,
31-
tls: tls,
32-
next: MaybeWrapWithContextDialer(next),
33-
userinfo: userinfo,
32+
address: address,
33+
tlsConfig: tlsConfig,
34+
next: MaybeWrapWithContextDialer(next),
35+
userinfo: userinfo,
3436
}
3537
}
3638

3739
func HTTPProxyDialerFromURL(u *url.URL, next xproxy.Dialer) (xproxy.Dialer, error) {
3840
host := u.Hostname()
3941
port := u.Port()
40-
tls := false
42+
params, err := url.ParseQuery(u.RawQuery)
43+
if err != nil {
44+
return nil, fmt.Errorf("unable to parse query string of proxy specification URL %q: %w", u.String(), err)
45+
}
4146

47+
var tlsConfig *tls.Config
4248
switch strings.ToLower(u.Scheme) {
4349
case "http":
4450
if port == "" {
4551
port = "80"
4652
}
4753
case "https":
48-
tls = true
4954
if port == "" {
5055
port = "443"
5156
}
57+
tlsConfig = &tls.Config{
58+
ServerName: host,
59+
}
60+
if params.Has("cafile") {
61+
roots, err := tlsutil.LoadCAfile(params.Get("cafile"))
62+
if err != nil {
63+
return nil, err
64+
}
65+
tlsConfig.RootCAs = roots
66+
}
67+
if params.Has("sni") {
68+
tlsConfig.ServerName = params.Get("sni")
69+
tlsConfig.InsecureSkipVerify = true
70+
tlsConfig.VerifyConnection = tlsutil.ExpectPeerName(host, tlsConfig.RootCAs)
71+
}
72+
if params.Has("peername") {
73+
tlsConfig.InsecureSkipVerify = true
74+
tlsConfig.VerifyConnection = tlsutil.ExpectPeerName(params.Get("peername"), tlsConfig.RootCAs)
75+
}
76+
if params.Has("cert") {
77+
cert, err := tls.LoadX509KeyPair(params.Get("cert"), params.Get("key"))
78+
if err != nil {
79+
return nil, err
80+
}
81+
tlsConfig.Certificates = []tls.Certificate{cert}
82+
}
83+
if params.Has("ciphers") {
84+
cipherList, err := tlsutil.ParseCipherList(params.Get("ciphers"))
85+
if err != nil {
86+
return nil, err
87+
}
88+
tlsConfig.CipherSuites = cipherList
89+
}
90+
if params.Has("curves") {
91+
curveList, err := tlsutil.ParseCurveList(params.Get("curves"))
92+
if err != nil {
93+
return nil, err
94+
}
95+
tlsConfig.CurvePreferences = curveList
96+
}
97+
if params.Has("min-tls-version") {
98+
ver, err := tlsutil.ParseVersion(params.Get("min-tls-version"))
99+
if err != nil {
100+
return nil, err
101+
}
102+
tlsConfig.MinVersion = ver
103+
}
104+
if params.Has("max-tls-version") {
105+
ver, err := tlsutil.ParseVersion(params.Get("max-tls-version"))
106+
if err != nil {
107+
return nil, err
108+
}
109+
tlsConfig.MaxVersion = ver
110+
}
52111
default:
53112
return nil, errors.New("unsupported proxy type")
54113
}
55114

56115
address := net.JoinHostPort(host, port)
57116

58-
return NewHTTPProxyDialer(address, tls, u.User, next), nil
117+
return NewHTTPProxyDialer(address, tlsConfig, u.User, next), nil
59118
}
60119

61120
func (d *HTTPProxyDialer) Dial(network, address string) (net.Conn, error) {
@@ -72,14 +131,8 @@ func (d *HTTPProxyDialer) DialContext(ctx context.Context, network, address stri
72131
if err != nil {
73132
return nil, fmt.Errorf("proxy dialer is unable to make connection: %w", err)
74133
}
75-
if d.tls {
76-
hostname, _, err := net.SplitHostPort(d.address)
77-
if err != nil {
78-
hostname = address
79-
}
80-
conn = tls.Client(conn, &tls.Config{
81-
ServerName: hostname,
82-
})
134+
if d.tlsConfig != nil {
135+
conn = tls.Client(conn, d.tlsConfig)
83136
}
84137

85138
stopGuardEvent := make(chan struct{})

0 commit comments

Comments
 (0)