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
12 changes: 2 additions & 10 deletions lib/srv/alpnproxy/dialer.go → api/client/alpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package alpnproxy
package client

import (
"context"
Expand All @@ -23,16 +23,8 @@ import (
"time"

"github.com/gravitational/trace"

apiclient "github.com/gravitational/teleport/api/client"
)

// ContextDialer represents network dialer interface that uses context
type ContextDialer interface {
// DialContext is a function that dials the specified address
DialContext(ctx context.Context, network, addr string) (net.Conn, error)
}

// ALPNDialerConfig is the config for ALPNDialer.
type ALPNDialerConfig struct {
// KeepAlivePeriod defines period between keep alives.
Expand Down Expand Up @@ -66,7 +58,7 @@ func (d ALPNDialer) DialContext(ctx context.Context, network, addr string) (net.
return nil, trace.BadParameter("missing TLS config")
}

dialer := apiclient.NewDialer(ctx, d.cfg.DialTimeout, d.cfg.DialTimeout, apiclient.WithTLSConfig(d.cfg.TLSConfig))
dialer := NewDialer(ctx, d.cfg.DialTimeout, d.cfg.DialTimeout, WithTLSConfig(d.cfg.TLSConfig))
if d.cfg.ALPNConnUpgradeRequired {
dialer = newALPNConnUpgradeDialer(dialer, &tls.Config{
InsecureSkipVerify: d.cfg.TLSConfig.InsecureSkipVerify,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package alpnproxy
package client

import (
"bufio"
Expand All @@ -30,11 +30,9 @@ import (
"github.com/gravitational/trace"
"github.com/sirupsen/logrus"

"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/lib/srv/alpnproxy/common"
)

// IsALPNConnUpgradeRequired returns true if a tunnel is required through a HTTP
Expand All @@ -57,7 +55,7 @@ func IsALPNConnUpgradeRequired(addr string, insecure bool) bool {
Timeout: defaults.DefaultIOTimeout,
}
tlsConfig := &tls.Config{
NextProtos: []string{string(common.ProtocolReverseTunnel)},
NextProtos: []string{string(constants.ALPNSNIProtocolReverseTunnel)},
InsecureSkipVerify: insecure,
}
testConn, err := tls.DialWithDialer(netDialer, "tcp", addr, tlsConfig)
Expand Down Expand Up @@ -145,12 +143,12 @@ func isALPNConnUpgradeRequiredByEnv(addr, envValue string) bool {
// alpnConnUpgradeDialer makes an "HTTP" upgrade call to the Proxy Service then
// tunnels the connection with this connection upgrade.
type alpnConnUpgradeDialer struct {
dialer apiclient.ContextDialer
dialer ContextDialer
tlsConfig *tls.Config
}

// newALPNConnUpgradeDialer creates a new alpnConnUpgradeDialer.
func newALPNConnUpgradeDialer(dialer apiclient.ContextDialer, tlsConfig *tls.Config) ContextDialer {
func newALPNConnUpgradeDialer(dialer ContextDialer, tlsConfig *tls.Config) ContextDialer {
return &alpnConnUpgradeDialer{
dialer: dialer,
tlsConfig: tlsConfig,
Expand Down Expand Up @@ -187,7 +185,7 @@ func (d alpnConnUpgradeDialer) DialContext(ctx context.Context, network, addr st
err = upgradeConnThroughWebAPI(tlsConn, url.URL{
Host: addr,
Scheme: "https",
Path: teleport.WebAPIConnUpgrade,
Path: constants.WebAPIConnUpgrade,
})
if err != nil {
defer tlsConn.Close()
Expand All @@ -203,7 +201,7 @@ func upgradeConnThroughWebAPI(conn net.Conn, api url.URL) error {
}

// For now, only "alpn" is supported.
req.Header.Add(teleport.WebAPIConnUpgradeHeader, teleport.WebAPIConnUpgradeTypeALPN)
req.Header.Add(constants.WebAPIConnUpgradeHeader, constants.WebAPIConnUpgradeTypeALPN)

// Send the request and check if upgrade is successful.
if err = req.Write(conn); err != nil {
Expand All @@ -219,7 +217,7 @@ func upgradeConnThroughWebAPI(conn net.Conn, api url.URL) error {
if http.StatusNotFound == resp.StatusCode {
return trace.NotImplemented(
"connection upgrade call to %q failed with status code %v. Please upgrade the server and try again.",
teleport.WebAPIConnUpgrade,
constants.WebAPIConnUpgrade,
resp.StatusCode,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package alpnproxy
package client

import (
"context"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"net"
"net/http"
Expand All @@ -31,11 +30,8 @@ import (

"github.com/stretchr/testify/require"

"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/srv/alpnproxy/common"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/fixtures"
)

func TestIsALPNConnUpgradeRequired(t *testing.T) {
Expand All @@ -55,7 +51,7 @@ func TestIsALPNConnUpgradeRequired(t *testing.T) {
},
{
name: "upgrade not required (proto negotiated)",
serverProtos: []string{string(common.ProtocolReverseTunnel)},
serverProtos: []string{string(constants.ALPNSNIProtocolReverseTunnel)},
insecure: true,
expectedResult: false,
},
Expand All @@ -67,7 +63,7 @@ func TestIsALPNConnUpgradeRequired(t *testing.T) {
},
{
name: "upgrade not required (other handshake error)",
serverProtos: []string{string(common.ProtocolReverseTunnel)},
serverProtos: []string{string(constants.ALPNSNIProtocolReverseTunnel)},
insecure: false, // to cause handshake error
expectedResult: false,
},
Expand Down Expand Up @@ -138,7 +134,7 @@ func TestALPNConnUpgradeDialer(t *testing.T) {
pool.AddCert(server.Certificate())

tlsConfig := &tls.Config{RootCAs: pool}
preDialer := apiclient.NewDialer(ctx, 0, 5*time.Second, apiclient.WithTLSConfig(tlsConfig))
preDialer := NewDialer(ctx, 0, 5*time.Second)
dialer := newALPNConnUpgradeDialer(preDialer, tlsConfig)
conn, err := dialer.DialContext(ctx, "tcp", addr.Host)
require.NoError(t, err)
Expand All @@ -158,7 +154,7 @@ func TestALPNConnUpgradeDialer(t *testing.T) {
require.NoError(t, err)

tlsConfig := &tls.Config{InsecureSkipVerify: true}
preDialer := apiclient.NewDialer(ctx, 0, 5*time.Second, apiclient.WithTLSConfig(tlsConfig))
preDialer := NewDialer(ctx, 0, 5*time.Second)
dialer := newALPNConnUpgradeDialer(preDialer, tlsConfig)
_, err = dialer.DialContext(ctx, "tcp", addr.Host)
require.Error(t, err)
Expand Down Expand Up @@ -207,12 +203,7 @@ func mustStartMockALPNServer(t *testing.T, supportedProtos []string) *mockALPNSe
listener.Close()
})

caKey, caCert, err := tlsca.GenerateSelfSignedCA(pkix.Name{
CommonName: "localhost",
}, []string{"localhost"}, defaults.CATTL)
require.NoError(t, err)

cert, err := tls.X509KeyPair(caCert, caKey)
cert, err := tls.X509KeyPair([]byte(fixtures.TLSCACertPEM), []byte(fixtures.TLSCAKeyPEM))
require.NoError(t, err)

m := &mockALPNServer{
Expand All @@ -228,8 +219,8 @@ func mustStartMockALPNServer(t *testing.T, supportedProtos []string) *mockALPNSe
// upgrade request and sends back some data inside the tunnel.
func mockConnUpgradeHandler(t *testing.T, upgradeType string, write []byte) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, teleport.WebAPIConnUpgrade, r.URL.Path)
require.Equal(t, upgradeType, r.Header.Get(teleport.WebAPIConnUpgradeHeader))
require.Equal(t, constants.WebAPIConnUpgrade, r.URL.Path)
require.Equal(t, upgradeType, r.Header.Get(constants.WebAPIConnUpgradeHeader))

hj, ok := w.(http.Hijacker)
require.True(t, ok)
Expand Down
12 changes: 12 additions & 0 deletions api/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,15 @@ const (
// TimeoutGetClusterAlerts is the timeout for grabbing cluster alerts from tctl and tsh
TimeoutGetClusterAlerts = time.Millisecond * 500
)

const (
// WebAPIConnUpgrade is the HTTP web API to make the connection upgrade
// call.
WebAPIConnUpgrade = "/webapi/connectionupgrade"
// WebAPIConnUpgradeHeader is the header used to indicate the requested
// connection upgrade types in the connection upgrade API.
WebAPIConnUpgradeHeader = "Upgrade"
// WebAPIConnUpgradeTypeALPN is a connection upgrade type that specifies
// the upgraded connection should be handled by the ALPN handler.
WebAPIConnUpgradeTypeALPN = "alpn"
)
64 changes: 64 additions & 0 deletions api/fixtures/fixtures.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2023 Gravitational, Inc
//
// 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 fixtures

const (
TLSCACertPEM = `-----BEGIN CERTIFICATE-----
MIIDKjCCAhKgAwIBAgIQJtJDJZZBkg/afM8d2ZJCTjANBgkqhkiG9w0BAQsFADBA
MRUwEwYDVQQKEwxUZWxlcG9ydCBPU1MxJzAlBgNVBAMTHnRlbGVwb3J0LmxvY2Fs
aG9zdC5sb2NhbGRvbWFpbjAeFw0xNzA1MDkxOTQwMzZaFw0yNzA1MDcxOTQwMzZa
MEAxFTATBgNVBAoTDFRlbGVwb3J0IE9TUzEnMCUGA1UEAxMedGVsZXBvcnQubG9j
YWxob3N0LmxvY2FsZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAuKFLaf2iII/xDR+m2Yj6PnUEa+qzqwxsdLUjnunFZaAXG+hZm4Ml80SCiBgI
gTHQlJyLIkTtuRoH5aeMyz1ERUCtii4ZsTqDrjjUybxP4r+4HVX6m34s6hwEr8Fi
fts9pMp4iS3tQguRc28gPdDo/T6VrJTVYUfUUsNDRtIrlB5O9igqqLnuaY9eqGi4
PUx0G0wRYJpRywoj8G0IkpfQTiX+CAC7dt5ws7ZrnGqCNBLGi5bGsaMmptVbsSEp
1TenntF54V1iR49IV5JqDhm1S0HmkleoJzKdc+6sP/xNepz9PJzuF9d9NubTLWgB
sK28YItcmWHdHXD/ODxVaehRjwIDAQABoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYD
VR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAAVU6sNBdj76saHwOxGSdnEqQ
o2tMuR3msSM4F6wFK2UkKepsD7CYIf/PzNSNUqA5JIEUVeMqGyiHuAbU4C655nT1
IyJX1D/+r73sSp5jbIpQm2xoQGZnj6g/Kltw8OSOAw+DsMF/PLVqoWJp07u6ew/m
NxWsJKcZ5k+q4eMxci9mKRHHqsquWKXzQlURMNFI+mGaFwrKM4dmzaR0BEc+ilSx
QqUvQ74smsLK+zhNikmgjlGC5ob9g8XkhVAkJMAh2rb9onDNiRl68iAgczP88mXu
vN/o98dypzsPxXmw6tkDqIRPUAUbh465rlY5sKMmRgXi2rUfl/QV5nbozUo/HQ==
-----END CERTIFICATE-----`
TLSCAKeyPEM = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuKFLaf2iII/xDR+m2Yj6PnUEa+qzqwxsdLUjnunFZaAXG+hZ
m4Ml80SCiBgIgTHQlJyLIkTtuRoH5aeMyz1ERUCtii4ZsTqDrjjUybxP4r+4HVX6
m34s6hwEr8Fifts9pMp4iS3tQguRc28gPdDo/T6VrJTVYUfUUsNDRtIrlB5O9igq
qLnuaY9eqGi4PUx0G0wRYJpRywoj8G0IkpfQTiX+CAC7dt5ws7ZrnGqCNBLGi5bG
saMmptVbsSEp1TenntF54V1iR49IV5JqDhm1S0HmkleoJzKdc+6sP/xNepz9PJzu
F9d9NubTLWgBsK28YItcmWHdHXD/ODxVaehRjwIDAQABAoIBABy4orWrShRMsA/9
k4QVpfAfXf+3tBlwxlJld1QaQ6XqgI3L2FyzyyyLxM6NBo2qhSsJKy+6j0yTOxVD
ukhHkJ5BUH3FbCPA2Yk5uAhl7ft1HZwaqvCTcUM99pCswbjAPFetU5DrfxQeHpNZ
fyd+ny/+E2SUhpkqhmIVlBqpSTQyOywbiEvZ6ZiFmncdHhXaCy3YZsylrKUGPzsJ
jfU2iOE167eTOIjPStsaoCPv9jLSyy2OvuNNudS+Y1qkFz8ZGvPp+HB+Iig+AlAE
7KMzNrIW7PlHTDgUly1cRCl3+84yE2mJ97+hHiEy//HIwVDUpI529i2hMYM/u4qz
Wso/2tkCgYEA2FdE4bmCrZiA9eS8qobwGLE1+MJME4YwfJkynZUHHX93xORPQ66e
WYpN7/xbMvBDa8LZZYVTNVtZ/SkEUaTb5NQW2zXKoIutk1PFBb8NbA0m8Ss/mOJA
d5nUYGr987O9fRh1yP9TksBshHB/5A8U2UG8MFFCNvJTZDPRkuSlMiUCgYEA2nnb
hAJrhY7PaF6jdfimGvvponkUiEbWLppg7/SjgPg+QgqIwuLybryXyOAp+TEnNzgU
ujAjhNtIiyB/B13TDxOgUgWUWPbPvUAWGEvwI9h+RLie1umGHd48G1NR76fwqSf1
y7z3YRnq8vCdz8ywB3o5GO6SH6QkMJBIxfIMlKMCgYA55akOi7oYQT8KD4waSwCI
ayyZhU4cz4W8Yrd0CsUbtNhVvhAked/w8J2JA01Y5Yn1lfDeRX8OQYNkyAxa2Tbs
F4KCafPvYVIzonCQ6B9sclygoEVl4e8E0wtOPnP2O30TtG8ZOpOgK5UfIIhpfUvE
FN6LQ8PntpRwtZl5qW04bQKBgGnHhFxHG64fthZPdA9jY3E/NSCgRSuyOHN59aNY
rG1+RA6PsSXC4iRxlYAB4PCxNs6KjaaUNi5WSaprAnYbnFv5Ya802l20qmJ0C/6Z
jdydLo2xYd6mVHRTrICCd/J0OpZ8LYsGpDPUa6hSjeYVscj9CXYj1IYTYB5PTZzh
k+vHAoGBAJyA+RtBF5m64/TqhZFcesTtnpWaRhQ50xXnNVF3W1eKGPtdTDKOaENA
LJxgC1GdoEz2ilXW802H9QrdKf9GPqxwi2TVzfO6pzWkdZcmbItu+QCCFz+co+r8
+ki49FmlfbR5YVPN+8X40aLQB4xDkCHwRwTkrigzWQhIOv8NAhDA
-----END RSA PRIVATE KEY-----`
)
Loading