Skip to content

Commit

Permalink
Trust DataPlaneUserSAN from Activator to Queue-Proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
nak3 committed Sep 27, 2023
1 parent d0eaee0 commit e5c4904
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 11 deletions.
2 changes: 1 addition & 1 deletion cmd/activator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func main() {
if tlsEnabled {
logger.Info("Internal Encryption is enabled")
certCache = certificate.NewCertCache(ctx)
transport = pkgnet.NewProxyAutoTLSTransport(env.MaxIdleProxyConns, env.MaxIdleProxyConnsPerHost, &certCache.TLSConf)
transport = pkgnet.NewProxyAutoTLSTransport(env.MaxIdleProxyConns, env.MaxIdleProxyConnsPerHost, certCache.TLSContext())
}

// Start throttler.
Expand Down
68 changes: 68 additions & 0 deletions pkg/activator/certificate/tls_context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright 2023 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package certificate

import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"

"knative.dev/networking/pkg/certificates"
pkgnet "knative.dev/pkg/network"
"knative.dev/serving/pkg/activator/handler"
)

// TLSContext returns DialTLSContextFunc.
func (cr *CertCache) TLSContext() pkgnet.DialTLSContextFunc {
return cr.dialTLSContext
}

// dialTLSContext handles TLS dialer
func (cr *CertCache) dialTLSContext(ctx context.Context, network, addr string) (net.Conn, error) {
return dialTLSContext(ctx, network, addr, cr)
}

// dialTLSContext handles verify SAN before calling DialTLSWithBackOff.
func dialTLSContext(ctx context.Context, network, addr string, cr *CertCache) (net.Conn, error) {
cr.certificatesMux.Lock()
// Clone the certificate Pool such that the one used by the client will be different from the one that will get updated is CA is replaced.
tlsConf := cr.TLSConf.Clone()
tlsConf.RootCAs = tlsConf.RootCAs.Clone()
cr.certificatesMux.Unlock()

revID := handler.RevIDFrom(ctx)
san := certificates.DataPlaneUserName(revID.Namespace)

tlsConf.VerifyConnection = verifySAN(san)
return pkgnet.DialTLSWithBackOff(ctx, network, addr, tlsConf)
}

func verifySAN(san string) func(tls.ConnectionState) error {
return func(cs tls.ConnectionState) error {
if len(cs.PeerCertificates) == 0 {
return errors.New("No PeerCertificates provided")
}
for _, name := range cs.PeerCertificates[0].DNSNames {
if name == san {
return nil
}
}
return fmt.Errorf("SAN %q does not have a matching name in %v", san, cs.PeerCertificates[0].DNSNames)
}
}
56 changes: 56 additions & 0 deletions pkg/activator/certificate/tls_context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package certificate

import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"testing"
)

// TestVerifySAN tests verifySAN.
func TestVerifySAN(t *testing.T) {
tests := []struct {
name string
san string
expErr bool
}{{
name: "first SAN",
san: "knative-knative-serving",
expErr: false,
}, {
name: "second SAN",
san: "data-plane.knative.dev",
expErr: false,
}, {
name: "non existent SAN",
san: "foo",
expErr: true,
}}

// tlsCrt contains two SANs knative-knative-serving and data-plane.knative.dev.
block, _ := pem.Decode(tlsCrt)
if block == nil {
t.Fatal("failed to parse certificate PEM")
}

serverCert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("failed to parse certificate: %v", err)
}

tlsConnectionState := tls.ConnectionState{
PeerCertificates: []*x509.Certificate{serverCert},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := verifySAN(test.san)(tlsConnectionState)
if test.expErr && err == nil {
t.Fatalf("failed to verify SAN")
}
if !test.expErr && err != nil {
t.Fatalf("failed to verify SAN: %v", err)
}
})
}
}
8 changes: 3 additions & 5 deletions vendor/knative.dev/pkg/network/h2c.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,11 @@ func newH2CTransport(disableCompression bool) http.RoundTripper {

// newH2Transport constructs a neew H2 transport. That transport will handles HTTPS traffic
// with TLS config.
func newH2Transport(disableCompression bool, tlsConf *tls.Config) http.RoundTripper {
func newH2Transport(disableCompression bool, tlsContext DialTLSContextFunc) http.RoundTripper {
return &http2.Transport{
DisableCompression: disableCompression,
DialTLS: func(netw, addr string, tlsConf *tls.Config) (net.Conn, error) {
return DialTLSWithBackOff(context.Background(),
netw, addr, tlsConf)
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
return tlsContext(ctx, network, addr)
},
TLSClientConfig: tlsConf,
}
}
12 changes: 7 additions & 5 deletions vendor/knative.dev/pkg/network/transports.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,18 @@ func newHTTPTransport(disableKeepAlives, disableCompression bool, maxIdle, maxId
return transport
}

func newHTTPSTransport(disableKeepAlives, disableCompression bool, maxIdle, maxIdlePerHost int, tlsConf *tls.Config) http.RoundTripper {
type DialTLSContextFunc func(ctx context.Context, network, addr string) (net.Conn, error)

func newHTTPSTransport(disableKeepAlives, disableCompression bool, maxIdle, maxIdlePerHost int, tlsContext DialTLSContextFunc) http.RoundTripper {
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.DialContext = DialWithBackOff
transport.DisableKeepAlives = disableKeepAlives
transport.MaxIdleConns = maxIdle
transport.MaxIdleConnsPerHost = maxIdlePerHost
transport.ForceAttemptHTTP2 = false
transport.DisableCompression = disableCompression
transport.DialTLSContext = tlsContext

transport.TLSClientConfig = tlsConf
return transport
}

Expand All @@ -149,10 +151,10 @@ func NewProberTransport() http.RoundTripper {
}

// NewProxyAutoTLSTransport is same with NewProxyAutoTransport but it has tls.Config to create HTTPS request.
func NewProxyAutoTLSTransport(maxIdle, maxIdlePerHost int, tlsConf *tls.Config) http.RoundTripper {
func NewProxyAutoTLSTransport(maxIdle, maxIdlePerHost int, tlsContext DialTLSContextFunc) http.RoundTripper {
return newAutoTransport(
newHTTPSTransport(false /*disable keep-alives*/, true /*disable auto-compression*/, maxIdle, maxIdlePerHost, tlsConf),
newH2Transport(true /*disable auto-compression*/, tlsConf))
newHTTPSTransport(false /*disable keep-alives*/, true /*disable auto-compression*/, maxIdle, maxIdlePerHost, tlsContext),
newH2Transport(true /*disable auto-compression*/, tlsContext))
}

// NewAutoTransport creates a RoundTripper that can use appropriate transport
Expand Down

0 comments on commit e5c4904

Please sign in to comment.