mirror of https://github.com/k3s-io/k3s
Handle custom kubelet port in agent tunnel
The kubelet port can be overridden by users; we shouldn't assume its always 10250 Signed-off-by: Brad Davidson <brad.davidson@rancher.com>pull/6250/head
parent
3f5c88e4a3
commit
3a829ae860
|
@ -7,7 +7,9 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
agentconfig "github.com/k3s-io/k3s/pkg/agent/config"
|
agentconfig "github.com/k3s-io/k3s/pkg/agent/config"
|
||||||
|
@ -22,6 +24,7 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
|
@ -31,10 +34,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type agentTunnel struct {
|
type agentTunnel struct {
|
||||||
client kubernetes.Interface
|
client kubernetes.Interface
|
||||||
cidrs cidranger.Ranger
|
cidrs cidranger.Ranger
|
||||||
ports map[string]bool
|
ports map[string]bool
|
||||||
mode string
|
mode string
|
||||||
|
kubeletPort string
|
||||||
}
|
}
|
||||||
|
|
||||||
// explicit interface check
|
// explicit interface check
|
||||||
|
@ -85,6 +89,9 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er
|
||||||
close(apiServerReady)
|
close(apiServerReady)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Allow the kubelet port, as published via our node object
|
||||||
|
go tunnel.setKubeletPort(ctx, apiServerReady)
|
||||||
|
|
||||||
switch tunnel.mode {
|
switch tunnel.mode {
|
||||||
case daemonconfig.EgressSelectorModeCluster:
|
case daemonconfig.EgressSelectorModeCluster:
|
||||||
// In Cluster mode, we allow the cluster CIDRs, and any connections to the node's IPs for pods using host network.
|
// In Cluster mode, we allow the cluster CIDRs, and any connections to the node's IPs for pods using host network.
|
||||||
|
@ -135,6 +142,23 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setKubeletPort retrieves the configured kubelet port from our node object
|
||||||
|
func (a *agentTunnel) setKubeletPort(ctx context.Context, apiServerReady <-chan struct{}) {
|
||||||
|
<-apiServerReady
|
||||||
|
|
||||||
|
wait.PollImmediateWithContext(ctx, time.Second, util.DefaultAPIServerReadyTimeout, func(ctx context.Context) (bool, error) {
|
||||||
|
nodeName := os.Getenv("NODE_NAME")
|
||||||
|
node, err := a.client.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Tunnel authorizer failed to get Kubelet Port: %v", err)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
a.kubeletPort = strconv.FormatInt(int64(node.Status.DaemonEndpoints.KubeletEndpoint.Port), 10)
|
||||||
|
logrus.Infof("Tunnel authorizer set Kubelet Port %s", a.kubeletPort)
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (a *agentTunnel) clusterAuth(config *daemonconfig.Node) {
|
func (a *agentTunnel) clusterAuth(config *daemonconfig.Node) {
|
||||||
// In Cluster mode, we add static entries for the Node IPs and Cluster CIDRs
|
// In Cluster mode, we add static entries for the Node IPs and Cluster CIDRs
|
||||||
for _, ip := range config.AgentConfig.NodeIPs {
|
for _, ip := range config.AgentConfig.NodeIPs {
|
||||||
|
@ -304,7 +328,7 @@ func (a *agentTunnel) authorized(ctx context.Context, proto, address string) boo
|
||||||
logrus.Debugf("Tunnel authorizer checking dial request for %s", address)
|
logrus.Debugf("Tunnel authorizer checking dial request for %s", address)
|
||||||
host, port, err := net.SplitHostPort(address)
|
host, port, err := net.SplitHostPort(address)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if proto == "tcp" && daemonconfig.KubeletReservedPorts[port] && (host == "127.0.0.1" || host == "::1") {
|
if a.isKubeletPort(proto, host, port) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
@ -359,3 +383,8 @@ func (a *agentTunnel) connect(rootCtx context.Context, waitGroup *sync.WaitGroup
|
||||||
|
|
||||||
return cancel
|
return cancel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isKubeletPort returns true if the connection is to a reserved TCP port on a loopback address.
|
||||||
|
func (a *agentTunnel) isKubeletPort(proto, host, port string) bool {
|
||||||
|
return proto == "tcp" && (host == "127.0.0.1" || host == "::1") && (port == a.kubeletPort || port == daemonconfig.StreamServerPort)
|
||||||
|
}
|
||||||
|
|
|
@ -32,16 +32,8 @@ const (
|
||||||
EgressSelectorModePod = "pod"
|
EgressSelectorModePod = "pod"
|
||||||
CertificateRenewDays = 90
|
CertificateRenewDays = 90
|
||||||
StreamServerPort = "10010"
|
StreamServerPort = "10010"
|
||||||
KubeletPort = "10250"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// These ports can always be accessed via the tunnel server, at the loopback address.
|
|
||||||
// Other addresses and ports are only accessible via the tunnel on newer agents, when used by a pod.
|
|
||||||
var KubeletReservedPorts = map[string]bool{
|
|
||||||
StreamServerPort: true,
|
|
||||||
KubeletPort: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Docker bool
|
Docker bool
|
||||||
ContainerRuntimeEndpoint string
|
ContainerRuntimeEndpoint string
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -77,15 +78,21 @@ type TunnelServer struct {
|
||||||
var _ cidranger.RangerEntry = &tunnelEntry{}
|
var _ cidranger.RangerEntry = &tunnelEntry{}
|
||||||
|
|
||||||
type tunnelEntry struct {
|
type tunnelEntry struct {
|
||||||
cidr net.IPNet
|
kubeletPort string
|
||||||
nodeName string
|
nodeName string
|
||||||
node bool
|
cidr net.IPNet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *tunnelEntry) Network() net.IPNet {
|
func (n *tunnelEntry) Network() net.IPNet {
|
||||||
return n.cidr
|
return n.cidr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some ports can always be accessed via the tunnel server, at the loopback address.
|
||||||
|
// Other addresses and ports are only accessible via the tunnel on newer agents, when used by a pod.
|
||||||
|
func (n *tunnelEntry) IsReservedPort(port string) bool {
|
||||||
|
return n.kubeletPort != "" && (port == n.kubeletPort || port == config.StreamServerPort)
|
||||||
|
}
|
||||||
|
|
||||||
// ServeHTTP handles either CONNECT requests, or websocket requests to the remotedialer server
|
// ServeHTTP handles either CONNECT requests, or websocket requests to the remotedialer server
|
||||||
func (t *TunnelServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
|
func (t *TunnelServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
|
||||||
logrus.Debugf("Tunnel server handing %s %s request for %s from %s", req.Proto, req.Method, req.URL, req.RemoteAddr)
|
logrus.Debugf("Tunnel server handing %s %s request for %s from %s", req.Proto, req.Method, req.URL, req.RemoteAddr)
|
||||||
|
@ -134,7 +141,8 @@ func (t *TunnelServer) onChangeNode(nodeName string, node *v1.Node) (*v1.Node, e
|
||||||
t.cidrs.Remove(*n)
|
t.cidrs.Remove(*n)
|
||||||
} else {
|
} else {
|
||||||
logrus.Debugf("Tunnel server egress proxy updating Node %s IP %v", nodeName, n)
|
logrus.Debugf("Tunnel server egress proxy updating Node %s IP %v", nodeName, n)
|
||||||
t.cidrs.Insert(&tunnelEntry{cidr: *n, nodeName: nodeName, node: true})
|
kubeletPort := strconv.FormatInt(int64(node.Status.DaemonEndpoints.KubeletEndpoint.Port), 10)
|
||||||
|
t.cidrs.Insert(&tunnelEntry{cidr: *n, nodeName: nodeName, kubeletPort: kubeletPort})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,7 +230,7 @@ func (t *TunnelServer) dialBackend(ctx context.Context, addr string) (net.Conn,
|
||||||
if nets, err := t.cidrs.ContainingNetworks(ip); err == nil && len(nets) > 0 {
|
if nets, err := t.cidrs.ContainingNetworks(ip); err == nil && len(nets) > 0 {
|
||||||
if n, ok := nets[0].(*tunnelEntry); ok {
|
if n, ok := nets[0].(*tunnelEntry); ok {
|
||||||
nodeName = n.nodeName
|
nodeName = n.nodeName
|
||||||
if n.node && config.KubeletReservedPorts[port] {
|
if n.IsReservedPort(port) {
|
||||||
toKubelet = true
|
toKubelet = true
|
||||||
useTunnel = true
|
useTunnel = true
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue