ipn/ipnlocal: add optional TLS termination on proxied TCP connections

Updates tailscale/corp#7515

Change-Id: Ib250fa20275971563adccfa72db48e0cec02b7a5
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/6280/head
Brad Fitzpatrick 2 years ago committed by Brad Fitzpatrick
parent 56dfdbe190
commit c9d6a9cb4d

@ -107,7 +107,7 @@ func (src *TCPPortHandler) Clone() *TCPPortHandler {
var _TCPPortHandlerCloneNeedsRegeneration = TCPPortHandler(struct { var _TCPPortHandlerCloneNeedsRegeneration = TCPPortHandler(struct {
HTTPS bool HTTPS bool
TCPForward string TCPForward string
TerminateTLS bool TerminateTLS string
}{}) }{})
// Clone makes a deep copy of HTTPHandler. // Clone makes a deep copy of HTTPHandler.

@ -232,15 +232,15 @@ func (v *TCPPortHandlerView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
func (v TCPPortHandlerView) HTTPS() bool { return v.ж.HTTPS } func (v TCPPortHandlerView) HTTPS() bool { return v.ж.HTTPS }
func (v TCPPortHandlerView) TCPForward() string { return v.ж.TCPForward } func (v TCPPortHandlerView) TCPForward() string { return v.ж.TCPForward }
func (v TCPPortHandlerView) TerminateTLS() bool { return v.ж.TerminateTLS } func (v TCPPortHandlerView) TerminateTLS() string { return v.ж.TerminateTLS }
// A compilation failure here means this code must be regenerated, with the command at the top of this file. // A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _TCPPortHandlerViewNeedsRegeneration = TCPPortHandler(struct { var _TCPPortHandlerViewNeedsRegeneration = TCPPortHandler(struct {
HTTPS bool HTTPS bool
TCPForward string TCPForward string
TerminateTLS bool TerminateTLS string
}{}) }{})
// View returns a readonly view of HTTPHandler. // View returns a readonly view of HTTPHandler.

@ -112,11 +112,6 @@ func (b *LocalBackend) HandleInterceptedTCPConn(dport uint16, srcAddr netip.Addr
} }
if backDst := tcph.TCPForward(); backDst != "" { if backDst := tcph.TCPForward(); backDst != "" {
if tcph.TerminateTLS() {
b.logf("TODO(bradfitz): finish")
sendRST()
return
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
backConn, err := b.dialer.SystemDial(ctx, "tcp", backDst) backConn, err := b.dialer.SystemDial(ctx, "tcp", backDst)
cancel() cancel()
@ -134,6 +129,24 @@ func (b *LocalBackend) HandleInterceptedTCPConn(dport uint16, srcAddr netip.Addr
defer conn.Close() defer conn.Close()
defer backConn.Close() defer backConn.Close()
if sni := tcph.TerminateTLS(); sni != "" {
conn = tls.Server(conn, &tls.Config{
GetCertificate: func(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
pair, err := b.GetCertPEM(ctx, sni)
if err != nil {
return nil, err
}
cert, err := tls.X509KeyPair(pair.CertPEM, pair.KeyPEM)
if err != nil {
return nil, err
}
return &cert, nil
},
})
}
// TODO(bradfitz): do the RegisterIPPortIdentity and // TODO(bradfitz): do the RegisterIPPortIdentity and
// UnregisterIPPortIdentity stuff that netstack does // UnregisterIPPortIdentity stuff that netstack does

@ -112,10 +112,11 @@ type TCPPortHandler struct {
// It is mutually exclusive with HTTPS. // It is mutually exclusive with HTTPS.
TCPForward string `json:",omitempty"` TCPForward string `json:",omitempty"`
// TerminateTLS is whether tailscaled should terminate TLS // TerminateTLS, if non-empty, means that tailscaled should terminate the
// connections before forwarding them to TCPForward. It is only // TLS connections before forwarding them to TCPForward, permitting only the
// used if TCPForward is non-empty. (the HTTPS mode ) // SNI name with this value. It is only used if TCPForward is non-empty.
TerminateTLS bool `json:",omitempty"` // (the HTTPS mode uses ServeConfig.Web)
TerminateTLS string `json:",omitempty"`
} }
// HTTPHandler is either a path or a proxy to serve. // HTTPHandler is either a path or a proxy to serve.

Loading…
Cancel
Save