Skip to content

Commit

Permalink
kube: prevent init failure when proxy listener addr host is unspecifi…
Browse files Browse the repository at this point in the history
…ed (#51839)

* kube: prevent init failure when proxy listener addr host is unspecified

This PR fixes an issue caused by configuring Teleport proxy listener
address without a host `:443` or in ipv6 form `[::]:443`.

The call to `utils.SplitHostPort` was checking if the host was empty
after the split which caused proxies to fail to initialize.

`:443` is equivalent to `0.0.0.0:443`.

Fixes #51838

Signed-off-by: Tiago Silva <[email protected]>

* Update utils.go

Co-authored-by: Zac Bergquist <[email protected]>

---------

Signed-off-by: Tiago Silva <[email protected]>
Co-authored-by: Zac Bergquist <[email protected]>
  • Loading branch information
tigrato and zmb3 authored Feb 4, 2025
1 parent 208791b commit 65afb94
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 3 deletions.
17 changes: 14 additions & 3 deletions lib/kube/grpc/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"crypto/tls"
"net"
"net/http"
"strconv"
"time"

"github.com/gravitational/trace"
Expand All @@ -42,15 +43,25 @@ import (
// multiplexing mode, the Kube proxy is always reachable on the same address as
// the web server using the SNI.
func getWebAddrAndKubeSNI(proxyAddr string) (string, string, error) {
addr, port, err := utils.SplitHostPort(proxyAddr)
// we avoid using utils.SplitHostPort because
// we allow the host to be empty
addr, port, err := net.SplitHostPort(proxyAddr)
if err != nil {
return "", "", trace.Wrap(err)
}
sni := client.GetKubeTLSServerName(addr)

// validate the port
if _, err := strconv.Atoi(port); err != nil {
return "", "", trace.Wrap(err, "invalid port")
}

// if the proxy is an unspecified address (0.0.0.0, ::), use localhost.
if ip := net.ParseIP(addr); ip != nil && ip.IsUnspecified() {
if ip := net.ParseIP(addr); ip != nil && ip.IsUnspecified() || addr == "" {
addr = string(teleport.PrincipalLocalhost)
}

sni := client.GetKubeTLSServerName(addr)

return sni, "https://" + net.JoinHostPort(addr, port), nil
}

Expand Down
119 changes: 119 additions & 0 deletions lib/kube/grpc/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Teleport
* Copyright (C) 2025 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package kubev1

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestGetWebAddrAndkubeSNI(t *testing.T) {
const localKubeSNI = "kube-teleport-proxy-alpn.teleport.cluster.local"
tests := []struct {
name string
proxyAddr string
sni string
host string
assertErr require.ErrorAssertionFunc
}{
{
name: "empty proxy address",
proxyAddr: "",
assertErr: require.Error,
},
{
name: "invalid address format",
proxyAddr: "not-a-valid-addr",
assertErr: require.Error,
},
{
name: "invalid port format",
proxyAddr: ":not-a-valid-port",
assertErr: require.Error,
},
{
name: "valid localhost address",
proxyAddr: "localhost:3000",
sni: localKubeSNI,
host: "https://localhost:3000",
assertErr: require.NoError,
},
{
name: "valid ip address",
proxyAddr: "1.2.3.4:3000",
sni: localKubeSNI,
host: "https://1.2.3.4:3000",
assertErr: require.NoError,
},
{
name: "valid wildcard address",
proxyAddr: "0.0.0.0:3000",
sni: localKubeSNI,
host: "https://localhost:3000",
assertErr: require.NoError,
},
{
name: "specify port only",
proxyAddr: ":3000",
sni: localKubeSNI,
host: "https://localhost:3000",
assertErr: require.NoError,
},
{
name: "double colons in address",
proxyAddr: "::3000",
assertErr: require.Error,
},
{
name: "valid ipv6 address",
proxyAddr: "[::1]:3000",
sni: localKubeSNI,
host: "https://[::1]:3000",
assertErr: require.NoError,
},
{
name: "unspecified ipv6 address",
proxyAddr: "[::]:3000",
sni: localKubeSNI,
host: "https://localhost:3000",
assertErr: require.NoError,
},
{
name: "ipv6 address without port",
proxyAddr: "[::1]",
assertErr: require.Error,
},
{
name: "valid domain address",
proxyAddr: "ci.goteleport.com:3000",
sni: "kube-teleport-proxy-alpn.ci.goteleport.com",
host: "https://ci.goteleport.com:3000",
assertErr: require.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotSNI, gotHost, err := getWebAddrAndKubeSNI(tt.proxyAddr)
tt.assertErr(t, err)
require.Equal(t, tt.sni, gotSNI)
require.Equal(t, tt.host, gotHost)
})
}
}

0 comments on commit 65afb94

Please sign in to comment.