mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-30 02:46:30 +02:00
envoy: fix sni/hostname mismatched routing for http2 connection coalescing (#703)
This commit is contained in:
parent
65bb1501fd
commit
1bee3b0df9
5 changed files with 79 additions and 10 deletions
|
@ -24,15 +24,20 @@ func New(workingDir string) *Cluster {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTPClient creates a new *http.Client, with a cookie jar, and a LocalRoundTripper
|
// NewHTTPClient calls NewHTTPClientWithTransport with the default cluster transport.
|
||||||
// which routes traffic to the nginx ingress controller.
|
|
||||||
func (cluster *Cluster) NewHTTPClient() *http.Client {
|
func (cluster *Cluster) NewHTTPClient() *http.Client {
|
||||||
|
return cluster.NewHTTPClientWithTransport(cluster.Transport)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPClientWithTransport creates a new *http.Client, with a cookie jar, and a LocalRoundTripper
|
||||||
|
// which routes traffic to the nginx ingress controller.
|
||||||
|
func (cluster *Cluster) NewHTTPClientWithTransport(transport http.RoundTripper) *http.Client {
|
||||||
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: &loggingRoundTripper{cluster.Transport},
|
Transport: &loggingRoundTripper{transport},
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
return http.ErrUseLastResponse
|
return http.ErrUseLastResponse
|
||||||
},
|
},
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (cluster *Cluster) Setup(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
hostport, err := cluster.getNodeHTTPSAddr(ctx)
|
hostport, err := cluster.GetNodePortAddr(ctx, "ingress-nginx", "ingress-nginx-nodeport")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -68,11 +68,12 @@ func (cluster *Cluster) Setup(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cluster *Cluster) getNodeHTTPSAddr(ctx context.Context) (hostport string, err error) {
|
// GetNodePortAddr returns the node:port address for a NodePort kubernetes service.
|
||||||
|
func (cluster *Cluster) GetNodePortAddr(ctx context.Context, namespace, svcName string) (hostport string, err error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
args := []string{"get", "service", "--namespace", "ingress-nginx", "--output", "json",
|
args := []string{"get", "service", "--namespace", namespace, "--output", "json",
|
||||||
"ingress-nginx-nodeport"}
|
svcName}
|
||||||
err = run(ctx, "kubectl", withArgs(args...), withStdout(&buf))
|
err = run(ctx, "kubectl", withArgs(args...), withStdout(&buf))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error getting service details with kubectl: %w", err)
|
return "", fmt.Errorf("error getting service details with kubectl: %w", err)
|
||||||
|
@ -94,7 +95,7 @@ func (cluster *Cluster) getNodeHTTPSAddr(ctx context.Context) (hostport string,
|
||||||
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
|
|
||||||
args = []string{"get", "pods", "--namespace", "ingress-nginx", "--output", "json"}
|
args = []string{"get", "pods", "--namespace", namespace, "--output", "json"}
|
||||||
var sel []string
|
var sel []string
|
||||||
for k, v := range svcResult.Spec.Selector {
|
for k, v := range svcResult.Spec.Selector {
|
||||||
sel = append(sel, k+"="+v)
|
sel = append(sel, k+"="+v)
|
||||||
|
|
|
@ -178,7 +178,7 @@ local PomeriumDeployment = function(svc) {
|
||||||
ip: '10.96.1.1',
|
ip: '10.96.1.1',
|
||||||
hostnames: [
|
hostnames: [
|
||||||
'openid.localhost.pomerium.io',
|
'openid.localhost.pomerium.io',
|
||||||
'authenticate.localhost.pomerium.io'
|
'authenticate.localhost.pomerium.io',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
initContainers: [{
|
initContainers: [{
|
||||||
|
@ -269,6 +269,28 @@ local PomeriumService = function(svc) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
local PomeriumNodePortServce = function() {
|
||||||
|
apiVersion: 'v1',
|
||||||
|
kind: 'Service',
|
||||||
|
metadata: {
|
||||||
|
namespace: 'default',
|
||||||
|
name: 'pomerium-proxy-nodeport',
|
||||||
|
labels: {
|
||||||
|
app: 'pomerium-proxy',
|
||||||
|
'app.kubernetes.io/part-of': 'pomerium',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
type: 'NodePort',
|
||||||
|
ports: [
|
||||||
|
{ name: 'https', port: 443, protocol: 'TCP', targetPort: 'https', nodePort: 31443 },
|
||||||
|
],
|
||||||
|
selector: {
|
||||||
|
app: 'pomerium-proxy',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
local PomeriumIngress = function() {
|
local PomeriumIngress = function() {
|
||||||
local proxyHosts = [
|
local proxyHosts = [
|
||||||
'forward-authenticate.localhost.pomerium.io',
|
'forward-authenticate.localhost.pomerium.io',
|
||||||
|
@ -392,6 +414,7 @@ local PomeriumForwardAuthIngress = function() {
|
||||||
PomeriumDeployment('cache'),
|
PomeriumDeployment('cache'),
|
||||||
PomeriumService('proxy'),
|
PomeriumService('proxy'),
|
||||||
PomeriumDeployment('proxy'),
|
PomeriumDeployment('proxy'),
|
||||||
|
PomeriumNodePortServce(),
|
||||||
PomeriumIngress(),
|
PomeriumIngress(),
|
||||||
PomeriumForwardAuthIngress(),
|
PomeriumForwardAuthIngress(),
|
||||||
],
|
],
|
||||||
|
|
|
@ -4,11 +4,13 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/pomerium/pomerium/integration/internal/netutil"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -180,3 +182,41 @@ func TestWebsocket(t *testing.T) {
|
||||||
assert.NoError(t, err, "expected no error when reading json from websocket")
|
assert.NoError(t, err, "expected no error when reading json from websocket")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSNIMismatch(t *testing.T) {
|
||||||
|
// Browsers will coalesce connections for the same IP address and TLS certificate
|
||||||
|
// even if the request was made to different domain names. We need to support this
|
||||||
|
// so this test makes a request with an incorrect TLS server name to make sure it
|
||||||
|
// gets routed properly
|
||||||
|
|
||||||
|
ctx := mainCtx
|
||||||
|
ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30)
|
||||||
|
defer clearTimeout()
|
||||||
|
|
||||||
|
hostport, err := testcluster.GetNodePortAddr(ctx, "default", "pomerium-proxy-nodeport")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := testcluster.NewHTTPClientWithTransport(&http.Transport{
|
||||||
|
DialContext: netutil.NewLocalDialer((&net.Dialer{}), map[string]string{
|
||||||
|
"443": hostport,
|
||||||
|
}).DialContext,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
ServerName: "ws-echo.localhost.pomerium.io",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "https://httpdetails.localhost.pomerium.io/ping", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if !assert.NoError(t, err, "unexpected http error") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ func (srv *Server) buildFilterChains(
|
||||||
var chains []*envoy_config_listener_v3.FilterChain
|
var chains []*envoy_config_listener_v3.FilterChain
|
||||||
for _, domain := range allDomains {
|
for _, domain := range allDomains {
|
||||||
// first we match on SNI
|
// first we match on SNI
|
||||||
chains = append(chains, callback(domain, []string{domain}))
|
chains = append(chains, callback(domain, allDomains))
|
||||||
}
|
}
|
||||||
// if there are no SNI matches we match on HTTP host
|
// if there are no SNI matches we match on HTTP host
|
||||||
chains = append(chains, callback("*", allDomains))
|
chains = append(chains, callback("*", allDomains))
|
||||||
|
|
Loading…
Add table
Reference in a new issue