config: add support for TCP proxy chaining (#5053)

Add a distinction between TCP routes depending on whether the To URL(s)
have the scheme tcp://. For routes with a TCP upstream, configure Envoy
to terminate CONNECT requests and open a TCP tunnel to the upstream
service (this is the current behavior). For routes without a TCP
upstream, configure Envoy to proxy CONNECT requests to the upstream.

This new mode can allow an upstream proxy server to terminate a CONNECT
request and open its own TCP tunnel to the final destination server.
(Note that this will typically require setting the preserve_host_header
option as well.)

Note that this requires Envoy 1.30 or later.
This commit is contained in:
Kenneth Jenkins 2024-04-24 16:35:18 -07:00 committed by GitHub
parent 05e077fe04
commit 498c3aa108
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 52 additions and 8 deletions

View file

@ -50,6 +50,8 @@ func Test_PolicyValidate(t *testing.T) {
{"bad kube service account token file", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://internal-host-name"), KubernetesServiceAccountTokenFile: "testdata/missing.token"}, true},
{"good kube service account token", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://internal-host-name"), KubernetesServiceAccountToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1OTY1MDk4MjIsImV4cCI6MTYyODA0NTgyMiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.H0I6ccQrL6sKobsKQj9dqNcLw_INhU9_xJsVyCkgkiY"}, false},
{"bad kube service account token and file", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://internal-host-name"), KubernetesServiceAccountToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1OTY1MDk4MjIsImV4cCI6MTYyODA0NTgyMiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.H0I6ccQrL6sKobsKQj9dqNcLw_INhU9_xJsVyCkgkiY", KubernetesServiceAccountTokenFile: "testdata/kubeserviceaccount.token"}, true},
{"TCP To URLs", Policy{From: "tcp+https://httpbin.corp.example:4000", To: mustParseWeightedURLs(t, "tcp://one.example.com:5000", "tcp://two.example.com:5000")}, false},
{"mix of TCP and non-TCP To URLs", Policy{From: "tcp+https://httpbin.corp.example:4000", To: mustParseWeightedURLs(t, "https://example.com", "tcp://example.com:5000")}, true},
}
for _, tt := range tests {
@ -340,3 +342,30 @@ func TestPolicy_SortOrder(t *testing.T) {
})
}
}
func TestPolicy_IsTCP(t *testing.T) {
p1 := Policy{From: "https://example.com"}
assert.False(t, p1.IsTCP())
p2 := Policy{From: "tcp+https://example.com"}
assert.True(t, p2.IsTCP())
}
func TestPolicy_IsTCPUpstream(t *testing.T) {
p1 := Policy{
From: "tcp+https://example.com:1234",
To: mustParseWeightedURLs(t, "https://one.example.com", "https://two.example.com"),
}
assert.False(t, p1.IsTCPUpstream())
p2 := Policy{
From: "tcp+https://example.com:1234",
To: mustParseWeightedURLs(t, "tcp://one.example.com:4000", "tcp://two.example.com:4000"),
}
assert.True(t, p2.IsTCPUpstream())
p3 := Policy{
From: "tcp+https://example.com:1234",
}
assert.False(t, p3.IsTCPUpstream())
}