mirror of https://github.com/k3s-io/k3s
Merge pull request #71981 from micahhausler/automated-cherry-pick-of-#71980-upstream-release-1.13
Automated cherry pick of #71980: Disable proxy to loopback and linklocalpull/58/head
commit
1c51137454
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
|
@ -35,6 +37,11 @@ const (
|
|||
IPv6ZeroCIDR = "::/0"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAddressNotAllowed = errors.New("address not allowed")
|
||||
ErrNoAddresses = errors.New("No addresses for hostname")
|
||||
)
|
||||
|
||||
func IsZeroCIDR(cidr string) bool {
|
||||
if cidr == IPv4ZeroCIDR || cidr == IPv6ZeroCIDR {
|
||||
return true
|
||||
|
@ -42,6 +49,46 @@ func IsZeroCIDR(cidr string) bool {
|
|||
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) {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
|
@ -27,6 +28,74 @@ import (
|
|||
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) {
|
||||
testCases := []struct {
|
||||
service *v1.Service
|
||||
|
|
|
@ -19,6 +19,7 @@ go_library(
|
|||
"//pkg/apis/core/validation:go_default_library",
|
||||
"//pkg/features: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/apis/meta/v1: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/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
|
||||
)
|
||||
|
||||
// nodeStrategy implements behavior for nodes
|
||||
|
@ -217,6 +218,10 @@ func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGet
|
|||
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
|
||||
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/validation: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/meta: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/validation"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
|
||||
)
|
||||
|
||||
// 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{
|
||||
Scheme: scheme,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue