Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/client/alpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ type ALPNDialerConfig struct {
// CAs when connection upgrade is required. If not provided, it's assumed
// the proper CAs are already present in TLSConfig.
GetClusterCAs GetClusterCAsFunc
// PROXYHeaderGetter is used if present to get signed PROXY headers to propagate client's IP.
// Used by proxy's web server to make calls on behalf of connected clients.
PROXYHeaderGetter PROXYHeaderGetter
}

// ALPNDialer is a ContextDialer that dials a connection to the Proxy Service
Expand Down Expand Up @@ -132,6 +135,7 @@ func (d *ALPNDialer) DialContext(ctx context.Context, network, addr string) (net
WithInsecureSkipVerify(d.cfg.TLSConfig.InsecureSkipVerify),
WithALPNConnUpgrade(d.cfg.ALPNConnUpgradeRequired),
WithALPNConnUpgradePing(shouldALPNConnUpgradeWithPing(tlsConfig)),
WithPROXYHeaderGetter(d.cfg.PROXYHeaderGetter),
)

conn, err := dialer.DialContext(ctx, network, addr)
Expand Down
23 changes: 3 additions & 20 deletions api/client/alpn_conn_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/pingconn"
"github.com/gravitational/teleport/api/utils/tlsutils"
)

// IsALPNConnUpgradeRequired returns true if a tunnel is required through a HTTP
Expand Down Expand Up @@ -162,35 +163,17 @@ func newALPNConnUpgradeDialer(dialer ContextDialer, tlsConfig *tls.Config, withP
func (d *alpnConnUpgradeDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
logrus.Debugf("ALPN connection upgrade for %v.", addr)

conn, err := d.dialer.DialContext(ctx, network, addr)
tlsConn, err := tlsutils.TLSDial(ctx, d.dialer, network, addr, d.tlsConfig.Clone())
if err != nil {
return nil, trace.Wrap(err)
}

// matching the behavior of tls.Dial
cfg := d.tlsConfig
if cfg == nil {
cfg = &tls.Config{}
}
if cfg.ServerName == "" {
colonPos := strings.LastIndex(addr, ":")
if colonPos == -1 {
colonPos = len(addr)
}
hostname := addr[:colonPos]

cfg = cfg.Clone()
cfg.ServerName = hostname
}

tlsConn := tls.Client(conn, cfg)
upgradeURL := url.URL{
Host: addr,
Scheme: "https",
Path: constants.WebAPIConnUpgrade,
}

conn, err = upgradeConnThroughWebAPI(tlsConn, upgradeURL, d.upgradeType())
conn, err := upgradeConnThroughWebAPI(tlsConn, upgradeURL, d.upgradeType())
if err != nil {
return nil, trace.NewAggregate(tlsConn.Close(), err)
}
Expand Down
4 changes: 4 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ func authConnect(ctx context.Context, params connectParams) (*Client, error) {
WithInsecureSkipVerify(params.cfg.InsecureAddressDiscovery),
WithALPNConnUpgrade(params.cfg.ALPNConnUpgradeRequired),
WithALPNConnUpgradePing(true), // Use Ping protocol for long-lived connections.
WithPROXYHeaderGetter(params.cfg.PROXYHeaderGetter),
)

clt := newClient(params.cfg, dialer, params.tlsConfig)
Expand Down Expand Up @@ -562,6 +563,9 @@ type Config struct {
// will perform necessary tests to decide if connection upgrade is
// required.
ALPNConnUpgradeRequired bool
// PROXYHeaderGetter returns signed PROXY header that is sent to allow Proxy to propagate client's real IP to the
// auth server from the Proxy's web server, when we create user's client for the web session.
PROXYHeaderGetter PROXYHeaderGetter
}

// CheckAndSetDefaults checks and sets default config values.
Expand Down
46 changes: 44 additions & 2 deletions api/client/contextdialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ type dialConfig struct {
// connection upgrade. This is only effective when alpnConnUpgradeRequired
// is true.
alpnConnUpgradeWithPing bool
// proxyHeaderGetter is used if present to get signed PROXY headers to propagate client's IP.
// Used by proxy's web server to make calls on behalf of connected clients.
proxyHeaderGetter PROXYHeaderGetter
}

// WithInsecureSkipVerify specifies if dialing insecure when using an HTTPS proxy.
Expand All @@ -71,6 +74,14 @@ func WithALPNConnUpgradePing(alpnConnUpgradeWithPing bool) DialOption {
}
}

// WithPROXYHeaderGetter provides PROXY headers signer so client's real IP could be propagated.
// Used by proxy's web server to make calls on behalf of connected clients.
func WithPROXYHeaderGetter(proxyHeaderGetter PROXYHeaderGetter) DialProxyOption {
return func(cfg *dialProxyConfig) {
cfg.proxyHeaderGetter = proxyHeaderGetter
}
}

// DialOption allows setting options as functional arguments to api.NewDialer.
type DialOption func(cfg *dialConfig)

Expand All @@ -96,12 +107,37 @@ func newDirectDialer(keepAlivePeriod, dialTimeout time.Duration) *net.Dialer {
}
}

func newProxyURLDialer(proxyURL *url.URL, dialer *net.Dialer, opts ...DialProxyOption) ContextDialer {
func newProxyURLDialer(proxyURL *url.URL, dialer ContextDialer, opts ...DialProxyOption) ContextDialer {
return ContextDialerFunc(func(ctx context.Context, network, addr string) (net.Conn, error) {
return DialProxyWithDialer(ctx, proxyURL, addr, dialer, opts...)
})
}

// NewPROXYHeaderDialer makes a new dialer that can propagate client IP if signed PROXY header getter is present
func NewPROXYHeaderDialer(dialer ContextDialer, headerGetter PROXYHeaderGetter) ContextDialer {
return ContextDialerFunc(func(ctx context.Context, network, addr string) (net.Conn, error) {
conn, err := dialer.DialContext(ctx, network, addr)
if err != nil {
return nil, trace.Wrap(err)
}

if headerGetter != nil {
signedHeader, err := headerGetter()
if err != nil {
conn.Close()
return nil, trace.Wrap(err)
}
_, err = conn.Write(signedHeader)
if err != nil {
conn.Close()
return nil, trace.Wrap(err)
}
}

return conn, nil
})
}

// tracedDialer ensures that the provided ContextDialerFunc is given a context
// which contains tracing information. In the event that a grpc dial occurs without
// a grpc.WithBlock dialing option, the context provided to the dial function will
Expand Down Expand Up @@ -136,9 +172,15 @@ func NewDialer(ctx context.Context, keepAlivePeriod, dialTimeout time.Duration,
// Base direct dialer.
var dialer ContextDialer = netDialer

// Wrap with PROXY header dialer if getter is present.
// Used by Proxy's web server to propagate real client IP when making calls on behalf of connected clients
if cfg.proxyHeaderGetter != nil {
dialer = NewPROXYHeaderDialer(dialer, cfg.proxyHeaderGetter)
}

// Wrap with proxy URL dialer if proxy URL is detected.
if proxyURL := utils.GetProxyURL(addr); proxyURL != nil {
dialer = newProxyURLDialer(proxyURL, netDialer, opts...)
dialer = newProxyURLDialer(proxyURL, dialer, opts...)
}

// Wrap with alpnConnUpgradeDialer if upgrade is required for TLS Routing.
Expand Down
Loading