-
Notifications
You must be signed in to change notification settings - Fork 264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Add support for running CHProxy behind another proxy #225
Changes from all commits
c222903
8333f97
f808760
8399420
0fee568
2fcbea2
c15557d
0369e36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
server: | ||
http: | ||
listen_addr: ":8080" | ||
proxy: | ||
header: CF-Connecting-IP | ||
|
||
users: | ||
- name: "dummy" | ||
allowed_networks: ["1.2.3.4"] | ||
to_cluster: "cluster" | ||
to_user: "default" | ||
|
||
clusters: | ||
- name: "cluster" | ||
nodes: ["127.0.1.1:8123"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -105,7 +105,8 @@ func (n Networks) Contains(addr string) bool { | |
|
||
h, _, err := net.SplitHostPort(addr) | ||
if err != nil { | ||
panic(fmt.Sprintf("BUG: unexpected error while parsing RemoteAddr: %s", err)) | ||
// If we only have an IP address. This happens when the proxy middleware is enabled. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if the err comes due to other reason ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you should remove the mention of middleware |
||
h = addr | ||
} | ||
|
||
ip := net.ParseIP(h) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
--- | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
title: Network Proxy | ||
category: Configuration | ||
position: 207 | ||
--- | ||
|
||
By default, `Chproxy` extracts the HTTP requests remote address. `Chproxy` can be configured to run behind another network proxy as well, by including the following configuration: | ||
|
||
```yml | ||
server: | ||
proxy: | ||
enable: true | ||
``` | ||
|
||
When `Chproxy` is run with proxy mode enabled, `Chproxy` will try to parse the following headers to extract the remote address: | ||
|
||
- `X-Forwarded-For` | ||
- `X-Real-IP` | ||
- `Forwarded` | ||
|
||
If multiple remote address are found `Chproxy` assumes the first IP address is the actual remote address. For example in the case where `X-Forwarded-For: 10.0.0.1, 10.3.2.1`, `Chproxy` assumes `10.0.0.1` is the correct address. | ||
|
||
If you have a custom header that contains the real remote address, it is possible to configure `Chproxy` to parse that header instead of the common proxy headers: | ||
|
||
```yml | ||
server: | ||
proxy: | ||
enable: true | ||
header: X-MyCustomHeader | ||
``` | ||
|
||
`Chproxy` assumes the header contains the remote address and doesn't apply any parsing logic to extract the remote address from the header. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,7 +49,7 @@ func TestServe(t *testing.T) { | |
name string | ||
file string | ||
testFn func(t *testing.T) | ||
listenFn func() (net.Listener, chan struct{}) | ||
listenFn func() (*http.Server, chan struct{}) | ||
}{ | ||
{ | ||
"https request", | ||
|
@@ -711,6 +711,29 @@ func TestServe(t *testing.T) { | |
}, | ||
startHTTP, | ||
}, | ||
{ | ||
"http request with default proxy headers", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might miss something but were do you check in this test that the remote addr is modified? Also, you should add the test that when the proxy is disabled, nothing is changed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test works because The proxy disabled setting should be covered by the other tests (as the middleware is always used). |
||
"testdata/https.proxy-enabled.yml", | ||
func(t *testing.T) { | ||
query := "SELECT 1" | ||
req, err := http.NewRequest("GET", "https://127.0.0.1:8443?query="+url.QueryEscape(query), nil) | ||
checkErr(t, err) | ||
req.Close = true | ||
req.SetBasicAuth("default", "qwerty") | ||
req.Header.Add("X-Forwarded-For", "10.0.0.1") | ||
|
||
resp, err := tlsClient.Do(req) | ||
checkErr(t, err) | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
t.Fatalf("unexpected status code: %d; expected: %d", resp.StatusCode, http.StatusOK) | ||
} | ||
|
||
checkResponse(t, resp.Body, expectedOkResp) | ||
}, | ||
startTLS, | ||
}, | ||
} | ||
|
||
// Wait until CHServer starts. | ||
|
@@ -734,10 +757,10 @@ func TestServe(t *testing.T) { | |
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
*configFile = tc.file | ||
ln, done := tc.listenFn() | ||
s, done := tc.listenFn() | ||
defer func() { | ||
if err := ln.Close(); err != nil { | ||
t.Fatalf("unexpected error while closing listener: %s", err) | ||
if err := s.Shutdown(context.Background()); err != nil { | ||
t.Fatalf("unexpected error while closing server: %s", err) | ||
} | ||
<-done | ||
}() | ||
|
@@ -778,7 +801,7 @@ var tlsClient = &http.Client{Transport: &http.Transport{ | |
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, | ||
}} | ||
|
||
func startTLS() (net.Listener, chan struct{}) { | ||
func startTLS() (*http.Server, chan struct{}) { | ||
cfg, err := loadConfig() | ||
if err != nil { | ||
panic(fmt.Sprintf("error while loading config: %s", err)) | ||
|
@@ -794,14 +817,15 @@ func startTLS() (net.Listener, chan struct{}) { | |
tlsCfg := newTLSConfig(cfg.Server.HTTPS) | ||
tln := tls.NewListener(ln, tlsCfg) | ||
h := http.HandlerFunc(serveHTTP) | ||
s := newServer(tln, h, config.TimeoutCfg{}) | ||
go func() { | ||
listenAndServe(tln, h, config.TimeoutCfg{}) | ||
s.Serve(tln) | ||
close(done) | ||
}() | ||
return tln, done | ||
return s, done | ||
} | ||
|
||
func startHTTP() (net.Listener, chan struct{}) { | ||
func startHTTP() (*http.Server, chan struct{}) { | ||
cfg, err := loadConfig() | ||
if err != nil { | ||
panic(fmt.Sprintf("error while loading config: %s", err)) | ||
|
@@ -815,11 +839,12 @@ func startHTTP() (net.Listener, chan struct{}) { | |
panic(fmt.Sprintf("cannot listen for %q: %s", cfg.Server.HTTP.ListenAddr, err)) | ||
} | ||
h := http.HandlerFunc(serveHTTP) | ||
s := newServer(ln, h, config.TimeoutCfg{}) | ||
go func() { | ||
listenAndServe(ln, h, config.TimeoutCfg{}) | ||
s.Serve(ln) | ||
close(done) | ||
}() | ||
return ln, done | ||
return s, done | ||
} | ||
|
||
// TODO randomise port for each instance of the mock | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, you should add the fact that the first ip the list will be chosen