mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 05:57:25 +00:00
Disable proxy to loopback and linklocal
This commit is contained in:
parent
c49896f924
commit
9740b6a6e1
@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
@ -35,6 +37,11 @@ const (
|
|||||||
IPv6ZeroCIDR = "::/0"
|
IPv6ZeroCIDR = "::/0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrAddressNotAllowed = errors.New("address not allowed")
|
||||||
|
ErrNoAddresses = errors.New("No addresses for hostname")
|
||||||
|
)
|
||||||
|
|
||||||
func IsZeroCIDR(cidr string) bool {
|
func IsZeroCIDR(cidr string) bool {
|
||||||
if cidr == IPv4ZeroCIDR || cidr == IPv6ZeroCIDR {
|
if cidr == IPv4ZeroCIDR || cidr == IPv6ZeroCIDR {
|
||||||
return true
|
return true
|
||||||
@ -42,6 +49,46 @@ func IsZeroCIDR(cidr string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsProxyableIP checks if a given IP address is permitted to be proxied
|
||||||
|
func IsProxyableIP(ip string) error {
|
||||||
|
netIP := net.ParseIP(ip)
|
||||||
|
if netIP == nil {
|
||||||
|
return ErrAddressNotAllowed
|
||||||
|
}
|
||||||
|
return isProxyableIP(netIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isProxyableIP(ip net.IP) error {
|
||||||
|
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast() {
|
||||||
|
return ErrAddressNotAllowed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolver is an interface for net.Resolver
|
||||||
|
type Resolver interface {
|
||||||
|
LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProxyableHostname checks if the IP addresses for a given hostname are permitted to be proxied
|
||||||
|
func IsProxyableHostname(ctx context.Context, resolv Resolver, hostname string) error {
|
||||||
|
resp, err := resolv.LookupIPAddr(ctx, hostname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp) == 0 {
|
||||||
|
return ErrNoAddresses
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range resp {
|
||||||
|
if err := isProxyableIP(host.IP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func IsLocalIP(ip string) (bool, error) {
|
func IsLocalIP(ip string) (bool, error) {
|
||||||
addrs, err := net.InterfaceAddrs()
|
addrs, err := net.InterfaceAddrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -27,6 +28,74 @@ import (
|
|||||||
fake "k8s.io/kubernetes/pkg/proxy/util/testing"
|
fake "k8s.io/kubernetes/pkg/proxy/util/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestIsProxyableIP(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
ip string
|
||||||
|
want error
|
||||||
|
}{
|
||||||
|
{"127.0.0.1", ErrAddressNotAllowed},
|
||||||
|
{"127.0.0.2", ErrAddressNotAllowed},
|
||||||
|
{"169.254.169.254", ErrAddressNotAllowed},
|
||||||
|
{"169.254.1.1", ErrAddressNotAllowed},
|
||||||
|
{"224.0.0.0", ErrAddressNotAllowed},
|
||||||
|
{"10.0.0.1", nil},
|
||||||
|
{"192.168.0.1", nil},
|
||||||
|
{"172.16.0.1", nil},
|
||||||
|
{"8.8.8.8", nil},
|
||||||
|
{"::1", ErrAddressNotAllowed},
|
||||||
|
{"fe80::", ErrAddressNotAllowed},
|
||||||
|
{"ff02::", ErrAddressNotAllowed},
|
||||||
|
{"ff01::", ErrAddressNotAllowed},
|
||||||
|
{"2600::", nil},
|
||||||
|
{"1", ErrAddressNotAllowed},
|
||||||
|
{"", ErrAddressNotAllowed},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range testCases {
|
||||||
|
got := IsProxyableIP(testCases[i].ip)
|
||||||
|
if testCases[i].want != got {
|
||||||
|
t.Errorf("case %d: expected %v, got %v", i, testCases[i].want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dummyResolver struct {
|
||||||
|
ips []string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *dummyResolver) LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
resp := []net.IPAddr{}
|
||||||
|
for _, ipString := range r.ips {
|
||||||
|
resp = append(resp, net.IPAddr{IP: net.ParseIP(ipString)})
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsProxyableHostname(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
hostname string
|
||||||
|
ips []string
|
||||||
|
want error
|
||||||
|
}{
|
||||||
|
{"k8s.io", []string{}, ErrNoAddresses},
|
||||||
|
{"k8s.io", []string{"8.8.8.8"}, nil},
|
||||||
|
{"k8s.io", []string{"169.254.169.254"}, ErrAddressNotAllowed},
|
||||||
|
{"k8s.io", []string{"127.0.0.1", "8.8.8.8"}, ErrAddressNotAllowed},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range testCases {
|
||||||
|
resolv := dummyResolver{ips: testCases[i].ips}
|
||||||
|
got := IsProxyableHostname(context.Background(), &resolv, testCases[i].hostname)
|
||||||
|
if testCases[i].want != got {
|
||||||
|
t.Errorf("case %d: expected %v, got %v", i, testCases[i].want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestShouldSkipService(t *testing.T) {
|
func TestShouldSkipService(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
service *v1.Service
|
service *v1.Service
|
||||||
|
@ -19,6 +19,7 @@ go_library(
|
|||||||
"//pkg/apis/core/validation:go_default_library",
|
"//pkg/apis/core/validation:go_default_library",
|
||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
"//pkg/kubelet/client:go_default_library",
|
"//pkg/kubelet/client:go_default_library",
|
||||||
|
"//pkg/proxy/util:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||||
|
@ -40,6 +40,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
|
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// nodeStrategy implements behavior for nodes
|
// nodeStrategy implements behavior for nodes
|
||||||
@ -217,6 +218,10 @@ func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGet
|
|||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := proxyutil.IsProxyableHostname(ctx, &net.Resolver{}, info.Hostname); err != nil {
|
||||||
|
return nil, nil, errors.NewBadRequest(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise, return the requested scheme and port, and the proxy transport
|
// Otherwise, return the requested scheme and port, and the proxy transport
|
||||||
return &url.URL{Scheme: schemeReq, Host: net.JoinHostPort(info.Hostname, portReq)}, proxyTransport, nil
|
return &url.URL{Scheme: schemeReq, Host: net.JoinHostPort(info.Hostname, portReq)}, proxyTransport, nil
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ go_library(
|
|||||||
"//pkg/apis/core/helper/qos:go_default_library",
|
"//pkg/apis/core/helper/qos:go_default_library",
|
||||||
"//pkg/apis/core/validation:go_default_library",
|
"//pkg/apis/core/validation:go_default_library",
|
||||||
"//pkg/kubelet/client:go_default_library",
|
"//pkg/kubelet/client:go_default_library",
|
||||||
|
"//pkg/proxy/util:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
@ -47,6 +47,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apis/core/helper/qos"
|
"k8s.io/kubernetes/pkg/apis/core/helper/qos"
|
||||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
|
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// podStrategy implements behavior for Pods
|
// podStrategy implements behavior for Pods
|
||||||
@ -290,6 +291,10 @@ func ResourceLocation(getter ResourceGetter, rt http.RoundTripper, ctx context.C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := proxyutil.IsProxyableIP(pod.Status.PodIP); err != nil {
|
||||||
|
return nil, nil, errors.NewBadRequest(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
loc := &url.URL{
|
loc := &url.URL{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user