mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-21 05:55:42 +08:00
Fix http proxy basic auth (#13140)
* Fix http proxy basic auth * Update docs about HTTP CONNECT env var formats
This commit is contained in:
@@ -59,8 +59,8 @@ func newDirectDialer(keepAlivePeriod, dialTimeout time.Duration) ContextDialer {
|
||||
func NewDialer(keepAlivePeriod, dialTimeout time.Duration) ContextDialer {
|
||||
return ContextDialerFunc(func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dialer := newDirectDialer(keepAlivePeriod, dialTimeout)
|
||||
if proxyAddr := proxy.GetProxyAddress(addr); proxyAddr != nil {
|
||||
return DialProxyWithDialer(ctx, proxyAddr.Host, addr, dialer)
|
||||
if proxyURL := proxy.GetProxyURL(addr); proxyURL != nil {
|
||||
return DialProxyWithDialer(ctx, proxyURL, addr, dialer)
|
||||
}
|
||||
return dialer.DialContext(ctx, network, addr)
|
||||
})
|
||||
|
||||
+21
-6
@@ -19,6 +19,7 @@ package client
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -28,23 +29,37 @@ import (
|
||||
)
|
||||
|
||||
// DialProxy creates a connection to a server via an HTTP Proxy.
|
||||
func DialProxy(ctx context.Context, proxyAddr, addr string) (net.Conn, error) {
|
||||
return DialProxyWithDialer(ctx, proxyAddr, addr, &net.Dialer{})
|
||||
func DialProxy(ctx context.Context, proxyURL *url.URL, addr string) (net.Conn, error) {
|
||||
return DialProxyWithDialer(ctx, proxyURL, addr, &net.Dialer{})
|
||||
}
|
||||
|
||||
// DialProxyWithDialer creates a connection to a server via an HTTP Proxy using a specified dialer.
|
||||
func DialProxyWithDialer(ctx context.Context, proxyAddr, addr string, dialer ContextDialer) (net.Conn, error) {
|
||||
conn, err := dialer.DialContext(ctx, "tcp", proxyAddr)
|
||||
func DialProxyWithDialer(ctx context.Context, proxyURL *url.URL, addr string, dialer ContextDialer) (net.Conn, error) {
|
||||
if proxyURL == nil {
|
||||
return nil, trace.BadParameter("missing proxy url")
|
||||
}
|
||||
conn, err := dialer.DialContext(ctx, "tcp", proxyURL.Host)
|
||||
if err != nil {
|
||||
log.Warnf("Unable to dial to proxy: %v: %v.", proxyAddr, err)
|
||||
log.Warnf("Unable to dial to proxy: %v: %v.", proxyURL.Host, err)
|
||||
return nil, trace.ConvertSystemError(err)
|
||||
}
|
||||
|
||||
header := make(http.Header)
|
||||
if proxyURL.User != nil {
|
||||
// dont use User.String() because it performs url encoding (rfc 1738),
|
||||
// which we don't want in our header
|
||||
password, _ := proxyURL.User.Password()
|
||||
// empty user/pass is permitted by the spec. The minimum required is a single colon.
|
||||
// see: https://datatracker.ietf.org/doc/html/rfc1945#section-11
|
||||
creds := proxyURL.User.Username() + ":" + password
|
||||
basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(creds))
|
||||
header.Add("Proxy-Authorization", basicAuth)
|
||||
}
|
||||
connectReq := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Opaque: addr},
|
||||
Host: addr,
|
||||
Header: make(http.Header),
|
||||
Header: header,
|
||||
}
|
||||
|
||||
if err := connectReq.Write(conn); err != nil {
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
"golang.org/x/net/http/httpproxy"
|
||||
)
|
||||
|
||||
// GetProxyAddress gets the HTTP proxy address to use for a given address, if any.
|
||||
func GetProxyAddress(dialAddr string) *url.URL {
|
||||
// GetProxyURL gets the HTTP proxy address to use for a given address, if any.
|
||||
func GetProxyURL(dialAddr string) *url.URL {
|
||||
addrURL, err := parse(dialAddr)
|
||||
if err != nil || addrURL == nil {
|
||||
return nil
|
||||
|
||||
@@ -21,8 +21,10 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/http/httpproxy"
|
||||
)
|
||||
@@ -106,22 +108,71 @@ func TestGetProxyAddress(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// used to augment test cases with auth credentials
|
||||
authTests := []struct {
|
||||
info string
|
||||
user string
|
||||
password string
|
||||
}{
|
||||
{info: "no credentials", user: "", password: ""},
|
||||
{info: "plain password", user: "alice", password: "password"},
|
||||
{info: "special characters in password", user: "alice", password: " !@#$%^&*()_+-=[]{};:,.<>/?`~\"\\ abc123"},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(fmt.Sprintf("%v: %v", i, tt.info), func(t *testing.T) {
|
||||
for _, env := range tt.env {
|
||||
t.Setenv(env.name, env.val)
|
||||
}
|
||||
p := GetProxyAddress(tt.targetAddr)
|
||||
if tt.proxyAddr == "" {
|
||||
require.Nil(t, p)
|
||||
} else {
|
||||
for j, authTest := range authTests {
|
||||
t.Run(fmt.Sprintf("%v %v: %v with %v", i, j, tt.info, authTest.info), func(t *testing.T) {
|
||||
for _, env := range tt.env {
|
||||
switch strings.ToLower(env.name) {
|
||||
case "http_proxy", "https_proxy":
|
||||
// add auth test credentials into http(s)_proxy env vars
|
||||
val, err := buildProxyAddr(env.val, authTest.user, authTest.password)
|
||||
require.NoError(t, err)
|
||||
t.Setenv(env.name, val)
|
||||
case "no_proxy":
|
||||
t.Setenv(env.name, env.val)
|
||||
}
|
||||
}
|
||||
p := GetProxyURL(tt.targetAddr)
|
||||
|
||||
// is a proxy expected?
|
||||
if tt.proxyAddr == "" {
|
||||
require.Nil(t, p)
|
||||
return
|
||||
}
|
||||
require.NotNil(t, p)
|
||||
require.Equal(t, tt.proxyAddr, p.Host)
|
||||
}
|
||||
})
|
||||
|
||||
// are auth credentials expected?
|
||||
if authTest.user == "" && authTest.password == "" {
|
||||
require.Nil(t, p.User)
|
||||
return
|
||||
}
|
||||
require.NotNil(t, p.User)
|
||||
require.Equal(t, authTest.user, p.User.Username())
|
||||
password, _ := p.User.Password()
|
||||
require.Equal(t, authTest.password, password)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildProxyAddr(addr, user, pass string) (string, error) {
|
||||
if user == "" && pass == "" {
|
||||
return addr, nil
|
||||
}
|
||||
userInfo := url.UserPassword(user, pass)
|
||||
if strings.HasPrefix(addr, "http") {
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return "", trace.Wrap(err)
|
||||
}
|
||||
u.User = userInfo
|
||||
return u.String(), nil
|
||||
}
|
||||
return fmt.Sprintf("%v@%v", userInfo.String(), addr), nil
|
||||
}
|
||||
|
||||
func TestProxyAwareRoundTripper(t *testing.T) {
|
||||
t.Setenv("HTTP_PROXY", "http://localhost:8888")
|
||||
transport := &http.Transport{
|
||||
|
||||
@@ -71,7 +71,7 @@ Environment="NO_PROXY=localhost,127.0.0.1,192.168.0.0/16,172.16.0.0/12,10.0.0.0/
|
||||
When Teleport builds and establishes the reverse tunnel to the main cluster, it will funnel all traffic through the proxy. Specifically, if using the default configuration, Teleport will tunnel ports `3024` (SSH, reverse tunnel) and `3080` (HTTPS, establishing trust) through the proxy.
|
||||
|
||||
The value of `HTTPS_PROXY` or `HTTP_PROXY` should be in the format
|
||||
`scheme://host:port` where scheme is either `https` or `http` . If the value is
|
||||
`scheme://[user[:password]@]host:port` where scheme is either `https` or `http` . If the value is
|
||||
`host:port` , Teleport will prepend `http` .
|
||||
|
||||
<Admonition
|
||||
|
||||
@@ -181,7 +181,7 @@ func getLocalIP() (string, error) {
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if !ip.IsLoopback() {
|
||||
if !ip.IsLoopback() && ip.IsPrivate() {
|
||||
return ip.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,18 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
||||
type ProxyServer struct {
|
||||
type ProxyHandler struct {
|
||||
sync.Mutex
|
||||
count int
|
||||
}
|
||||
@@ -32,7 +34,7 @@ type ProxyServer struct {
|
||||
// ServeHTTP only accepts the CONNECT verb and will tunnel your connection to
|
||||
// the specified host. Also tracks the number of connections that it proxies for
|
||||
// debugging purposes.
|
||||
func (p *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
func (p *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Validate http connect parameters.
|
||||
if r.Method != http.MethodConnect {
|
||||
trace.WriteError(w, trace.BadParameter("%v not supported", r.Method))
|
||||
@@ -91,8 +93,80 @@ func (p *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Count returns the number of connections that have been proxied.
|
||||
func (p *ProxyServer) Count() int {
|
||||
func (p *ProxyHandler) Count() int {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
return p.count
|
||||
}
|
||||
|
||||
type ProxyAuthorizer struct {
|
||||
next http.Handler
|
||||
sync.Mutex
|
||||
lastError error
|
||||
authDB map[string]string
|
||||
}
|
||||
|
||||
func NewProxyAuthorizer(handler http.Handler, authDB map[string]string) *ProxyAuthorizer {
|
||||
return &ProxyAuthorizer{next: handler, authDB: authDB}
|
||||
}
|
||||
|
||||
func (p *ProxyAuthorizer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
auth := r.Header.Get("Proxy-Authorization")
|
||||
if auth == "" {
|
||||
err := trace.AccessDenied("missing Proxy-Authorization header")
|
||||
p.SetError(err)
|
||||
trace.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
user, password, ok := parseProxyAuth(auth)
|
||||
if !ok {
|
||||
err := trace.AccessDenied("bad Proxy-Authorization header")
|
||||
p.SetError(err)
|
||||
trace.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if p.isAuthorized(user, password) {
|
||||
p.SetError(nil)
|
||||
p.next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
err := trace.AccessDenied("bad credentials")
|
||||
p.SetError(err)
|
||||
trace.WriteError(w, err)
|
||||
}
|
||||
|
||||
func (p *ProxyAuthorizer) SetError(err error) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
p.lastError = err
|
||||
}
|
||||
|
||||
func (p *ProxyAuthorizer) LastError() error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
return p.lastError
|
||||
}
|
||||
|
||||
func (p *ProxyAuthorizer) isAuthorized(user, pass string) bool {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
expectedPass, ok := p.authDB[user]
|
||||
return ok && pass == expectedPass
|
||||
}
|
||||
|
||||
// parse "Proxy-Authorization" header by leveraging the stdlib basic auth parsing for "Authorization" header
|
||||
func parseProxyAuth(proxyAuth string) (user, password string, ok bool) {
|
||||
fakeHeader := make(http.Header)
|
||||
fakeHeader.Add("Authorization", proxyAuth)
|
||||
fakeReq := &http.Request{
|
||||
Header: fakeHeader,
|
||||
}
|
||||
return fakeReq.BasicAuth()
|
||||
}
|
||||
|
||||
func MakeProxyAddr(user, pass, host string) string {
|
||||
userPass := url.UserPassword(user, pass).String()
|
||||
return fmt.Sprintf("%v@%v", userPass, host)
|
||||
}
|
||||
|
||||
@@ -1620,8 +1620,8 @@ func testTwoClustersTunnel(t *testing.T, suite *integrationTestSuite) {
|
||||
|
||||
func twoClustersTunnel(t *testing.T, suite *integrationTestSuite, now time.Time, proxyRecordMode string, execCountSiteA, execCountSiteB int) {
|
||||
// start the http proxy, we need to make sure this was not used
|
||||
ps := &helpers.ProxyServer{}
|
||||
ts := httptest.NewServer(ps)
|
||||
ph := &helpers.ProxyHandler{}
|
||||
ts := httptest.NewServer(ph)
|
||||
defer ts.Close()
|
||||
|
||||
// clear out any proxy environment variables
|
||||
@@ -1677,7 +1677,7 @@ func twoClustersTunnel(t *testing.T, suite *integrationTestSuite, now time.Time,
|
||||
)
|
||||
|
||||
// make sure the direct dialer was used and not the proxy dialer
|
||||
require.Zero(t, ps.Count())
|
||||
require.Zero(t, ph.Count())
|
||||
|
||||
// if we got here, it means two sites are cross-connected. lets execute SSH commands
|
||||
sshPort := a.GetPortSSHInt()
|
||||
@@ -1788,7 +1788,7 @@ func testTwoClustersProxy(t *testing.T, suite *integrationTestSuite) {
|
||||
defer tr.Stop()
|
||||
|
||||
// start the http proxy
|
||||
ps := &helpers.ProxyServer{}
|
||||
ps := &helpers.ProxyHandler{}
|
||||
ts := httptest.NewServer(ps)
|
||||
defer ts.Close()
|
||||
|
||||
|
||||
+88
-10
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/gravitational/teleport/api/breaker"
|
||||
"github.com/gravitational/teleport/integration/helpers"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -206,8 +207,8 @@ func TestALPNSNIProxyTrustedClusterNode(t *testing.T) {
|
||||
// on a single proxy port setup.
|
||||
func TestALPNSNIHTTPSProxy(t *testing.T) {
|
||||
// start the http proxy
|
||||
ps := &helpers.ProxyServer{}
|
||||
ts := httptest.NewServer(ps)
|
||||
ph := &helpers.ProxyHandler{}
|
||||
ts := httptest.NewServer(ph)
|
||||
defer ts.Close()
|
||||
|
||||
// set the http_proxy environment variable
|
||||
@@ -237,15 +238,15 @@ func TestALPNSNIHTTPSProxy(t *testing.T) {
|
||||
require.Eventually(t, waitForClusters(suite.leaf.Tunnel, 1), 10*time.Second, 1*time.Second,
|
||||
"Two clusters do not see each other: tunnels are not working.")
|
||||
|
||||
require.Greater(t, ps.Count(), 0, "proxy did not intercept any connection")
|
||||
require.Greater(t, ph.Count(), 0, "proxy did not intercept any connection")
|
||||
}
|
||||
|
||||
// TestMultiPortHTTPSProxy tests if the reverse tunnel uses http_proxy
|
||||
// on a multiple proxy port setup.
|
||||
func TestMultiPortHTTPSProxy(t *testing.T) {
|
||||
// start the http proxy
|
||||
ps := &helpers.ProxyServer{}
|
||||
ts := httptest.NewServer(ps)
|
||||
ph := &helpers.ProxyHandler{}
|
||||
ts := httptest.NewServer(ph)
|
||||
defer ts.Close()
|
||||
|
||||
// set the http_proxy environment variable
|
||||
@@ -275,7 +276,7 @@ func TestMultiPortHTTPSProxy(t *testing.T) {
|
||||
require.Eventually(t, waitForClusters(suite.leaf.Tunnel, 1), 10*time.Second, 1*time.Second,
|
||||
"Two clusters do not see each other: tunnels are not working.")
|
||||
|
||||
require.Greater(t, ps.Count(), 0, "proxy did not intercept any connection")
|
||||
require.Greater(t, ph.Count(), 0, "proxy did not intercept any connection")
|
||||
}
|
||||
|
||||
// TestAlpnSniProxyKube tests Kubernetes access with custom Kube API mock where traffic is forwarded via
|
||||
@@ -774,8 +775,8 @@ func TestALPNProxyHTTPProxyNoProxyDial(t *testing.T) {
|
||||
defer rc.StopAll()
|
||||
|
||||
// Create and start http_proxy server.
|
||||
ps := &helpers.ProxyServer{}
|
||||
ts := httptest.NewServer(ps)
|
||||
ph := &helpers.ProxyHandler{}
|
||||
ts := httptest.NewServer(ph)
|
||||
defer ts.Close()
|
||||
|
||||
u, err := url.Parse(ts.URL)
|
||||
@@ -797,7 +798,7 @@ func TestALPNProxyHTTPProxyNoProxyDial(t *testing.T) {
|
||||
err = waitForNodeCount(ctx, rc, "root.example.com", 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Zero(t, ps.Count())
|
||||
require.Zero(t, ph.Count())
|
||||
|
||||
// Unset the no_proxy=127.0.0.1 env variable. After that a new node
|
||||
// should take into account the http_proxy address and connection should go through the http_proxy.
|
||||
@@ -807,5 +808,82 @@ func TestALPNProxyHTTPProxyNoProxyDial(t *testing.T) {
|
||||
err = waitForNodeCount(ctx, rc, "root.example.com", 2)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotZero(t, ps.Count())
|
||||
require.NotZero(t, ph.Count())
|
||||
}
|
||||
|
||||
// TestALPNProxyHTTPProxyBasicAuthDial tests if a node joining to root cluster
|
||||
// takes into account http_proxy with basic auth credentials in the address
|
||||
func TestALPNProxyHTTPProxyBasicAuthDial(t *testing.T) {
|
||||
lib.SetInsecureDevMode(true)
|
||||
defer lib.SetInsecureDevMode(false)
|
||||
|
||||
rcAddr, err := getLocalIP()
|
||||
require.NoError(t, err)
|
||||
|
||||
rc := helpers.NewInstance(helpers.InstanceConfig{
|
||||
ClusterName: "root.example.com",
|
||||
HostID: uuid.New().String(),
|
||||
NodeName: rcAddr,
|
||||
Log: utils.NewLoggerForTests(),
|
||||
Ports: helpers.SingleProxyPortSetup(),
|
||||
})
|
||||
username := mustGetCurrentUser(t).Username
|
||||
rc.AddUser(username, []string{username})
|
||||
|
||||
rcConf := service.MakeDefaultConfig()
|
||||
rcConf.DataDir = t.TempDir()
|
||||
rcConf.Auth.Enabled = true
|
||||
rcConf.Auth.NetworkingConfig.SetProxyListenerMode(types.ProxyListenerMode_Multiplex)
|
||||
rcConf.Auth.Preference.SetSecondFactor("off")
|
||||
rcConf.Proxy.Enabled = true
|
||||
rcConf.Proxy.DisableWebInterface = true
|
||||
rcConf.SSH.Enabled = false
|
||||
rcConf.CircuitBreakerConfig = breaker.NoopBreakerConfig()
|
||||
|
||||
err = rc.CreateEx(t, nil, rcConf)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = rc.Start()
|
||||
require.NoError(t, err)
|
||||
defer rc.StopAll()
|
||||
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30))
|
||||
defer cancel()
|
||||
|
||||
validUser := "aladdin"
|
||||
validPass := "open sesame"
|
||||
|
||||
// Create and start http_proxy server.
|
||||
ph := &helpers.ProxyHandler{}
|
||||
authorizer := helpers.NewProxyAuthorizer(ph, map[string]string{validUser: validPass})
|
||||
ts := httptest.NewServer(authorizer)
|
||||
defer ts.Close()
|
||||
|
||||
proxyURL, err := url.Parse(ts.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
rcProxyAddr := net.JoinHostPort(rcAddr, rc.GetPortWeb())
|
||||
|
||||
// proxy url is just the host with no auth credentials
|
||||
t.Setenv("http_proxy", proxyURL.Host)
|
||||
_, err = rc.StartNode(makeNodeConfig("first-root-node", rcProxyAddr))
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, authorizer.LastError(), trace.AccessDenied("missing Proxy-Authorization header"))
|
||||
require.Zero(t, ph.Count())
|
||||
|
||||
// proxy url is user:password@host with incorrect password
|
||||
t.Setenv("http_proxy", helpers.MakeProxyAddr(validUser, "incorrectPassword", proxyURL.Host))
|
||||
_, err = rc.StartNode(makeNodeConfig("second-root-node", rcProxyAddr))
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, authorizer.LastError(), trace.AccessDenied("bad credentials"))
|
||||
require.Zero(t, ph.Count())
|
||||
|
||||
// proxy url is user:password@host with correct password
|
||||
t.Setenv("http_proxy", helpers.MakeProxyAddr(validUser, validPass, proxyURL.Host))
|
||||
_, err = rc.StartNode(makeNodeConfig("third-root-node", rcProxyAddr))
|
||||
require.NoError(t, err)
|
||||
err = waitForNodeCount(ctx, rc, "root.example.com", 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, authorizer.LastError())
|
||||
require.NotZero(t, ph.Count())
|
||||
}
|
||||
|
||||
@@ -1078,7 +1078,7 @@ func (proxy *ProxyClient) loadTLS(clusterName string) (*tls.Config, error) {
|
||||
return tlsConfig.Clone(), nil
|
||||
}
|
||||
|
||||
// ConnectToAuthServiceThroughALPNSNIProxy uses ALPN proxy service to connect to remove/local auth
|
||||
// ConnectToAuthServiceThroughALPNSNIProxy uses ALPN proxy service to connect to remote/local auth
|
||||
// service and returns auth client. For routing purposes, TLS ServerName is set to destination auth service
|
||||
// cluster name with ALPN values set to teleport-auth protocol.
|
||||
func (proxy *ProxyClient) ConnectToAuthServiceThroughALPNSNIProxy(ctx context.Context, clusterName string) (auth.ClientI, error) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
@@ -161,7 +162,7 @@ func (d directDial) DialTimeout(ctx context.Context, network, address string, ti
|
||||
|
||||
type proxyDial struct {
|
||||
// proxyHost is the HTTPS proxy address.
|
||||
proxyHost string
|
||||
proxyURL *url.URL
|
||||
// insecure is whether to skip certificate validation.
|
||||
insecure bool
|
||||
// tlsRoutingEnabled indicates that proxy is running in TLSRouting mode.
|
||||
@@ -189,7 +190,7 @@ func (d proxyDial) DialTimeout(ctx context.Context, network, address string, tim
|
||||
defer cancel()
|
||||
ctx = timeoutCtx
|
||||
}
|
||||
conn, err := apiclient.DialProxy(ctx, d.proxyHost, address)
|
||||
conn, err := apiclient.DialProxy(ctx, d.proxyURL, address)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
@@ -216,7 +217,7 @@ func (d proxyDial) DialTimeout(ctx context.Context, network, address string, tim
|
||||
// SSH connection.
|
||||
func (d proxyDial) Dial(ctx context.Context, network string, addr string, config *ssh.ClientConfig) (*tracessh.Client, error) {
|
||||
// Build a proxy connection first.
|
||||
pconn, err := apiclient.DialProxy(ctx, d.proxyHost, addr)
|
||||
pconn, err := apiclient.DialProxy(ctx, d.proxyURL, addr)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
@@ -283,7 +284,7 @@ func WithInsecureSkipTLSVerify(insecure bool) DialerOptionFunc {
|
||||
// server directly.
|
||||
func DialerFromEnvironment(addr string, opts ...DialerOptionFunc) Dialer {
|
||||
// Try and get proxy addr from the environment.
|
||||
proxyAddr := apiproxy.GetProxyAddress(addr)
|
||||
proxyURL := apiproxy.GetProxyURL(addr)
|
||||
|
||||
var options dialerOptions
|
||||
for _, opt := range opts {
|
||||
@@ -292,7 +293,7 @@ func DialerFromEnvironment(addr string, opts ...DialerOptionFunc) Dialer {
|
||||
|
||||
// If no proxy settings are in environment return regular ssh dialer,
|
||||
// otherwise return a proxy dialer.
|
||||
if proxyAddr == nil {
|
||||
if proxyURL == nil {
|
||||
log.Debugf("No proxy set in environment, returning direct dialer.")
|
||||
return directDial{
|
||||
tlsConfig: options.tlsConfig,
|
||||
@@ -300,9 +301,9 @@ func DialerFromEnvironment(addr string, opts ...DialerOptionFunc) Dialer {
|
||||
insecure: options.insecureSkipTLSVerify,
|
||||
}
|
||||
}
|
||||
log.Debugf("Found proxy %q in environment, returning proxy dialer.", proxyAddr)
|
||||
log.Debugf("Found proxy %q in environment, returning proxy dialer.", proxyURL)
|
||||
return proxyDial{
|
||||
proxyHost: proxyAddr.Host,
|
||||
proxyURL: proxyURL,
|
||||
insecure: options.insecureSkipTLSVerify,
|
||||
tlsRoutingEnabled: options.tlsRoutingEnabled,
|
||||
tlsConfig: options.tlsConfig,
|
||||
|
||||
Reference in New Issue
Block a user