Merge pull request #10713 from thockin/no-localhost-endpoints

Check loopback and link-local multicast endpoints
This commit is contained in:
Saad Ali 2015-08-19 12:48:33 -07:00
commit c1a2c6dee7
10 changed files with 51 additions and 19 deletions

View File

@ -1,7 +1,7 @@
{ {
"swaggerVersion": "1.2", "swaggerVersion": "1.2",
"apiVersion": "", "apiVersion": "",
"basePath": "https://127.0.0.1:6443", "basePath": "https://10.10.10.10:6443",
"resourcePath": "/api", "resourcePath": "/api",
"apis": [ "apis": [
{ {

View File

@ -1,7 +1,7 @@
{ {
"swaggerVersion": "1.2", "swaggerVersion": "1.2",
"apiVersion": "v1", "apiVersion": "v1",
"basePath": "https://127.0.0.1:6443", "basePath": "https://10.10.10.10:6443",
"resourcePath": "/api/v1", "resourcePath": "/api/v1",
"apis": [ "apis": [
{ {
@ -11230,7 +11230,7 @@
"properties": { "properties": {
"ip": { "ip": {
"type": "string", "type": "string",
"description": "IP address of the endpoint" "description": "IP address of the endpoint; may not be loopback (127.0.0.0/8), link-local (169.254.0.0/16), or link-local multicast ((224.0.0.0/24)"
}, },
"targetRef": { "targetRef": {
"$ref": "v1.ObjectReference", "$ref": "v1.ObjectReference",

View File

@ -1,7 +1,7 @@
{ {
"swaggerVersion": "1.2", "swaggerVersion": "1.2",
"apiVersion": "", "apiVersion": "",
"basePath": "https://127.0.0.1:6443", "basePath": "https://10.10.10.10:6443",
"resourcePath": "/version", "resourcePath": "/version",
"apis": [ "apis": [
{ {

View File

@ -270,8 +270,9 @@ func (s *APIServer) Run(_ []string) error {
s.verifyClusterIPFlags() s.verifyClusterIPFlags()
// If advertise-address is not specified, use bind-address. If bind-address // If advertise-address is not specified, use bind-address. If bind-address
// is also unset (or 0.0.0.0), setDefaults() in pkg/master/master.go will // is not usable (unset, 0.0.0.0, or loopback), setDefaults() in
// do the right thing and use the host's default interface. // pkg/master/master.go will do the right thing and use the host's default
// interface.
if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() { if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() {
s.AdvertiseAddress = s.BindAddress s.AdvertiseAddress = s.BindAddress
} }

View File

@ -195,6 +195,9 @@ created. You can manually map the service to your own specific endpoints:
} }
``` ```
NOTE: Endpoint IPs may not be loopback (127.0.0.0/8), link-local
(169.254.0.0/16), or link-local multicast ((224.0.0.0/24).
Accessing a `Service` without a selector works the same as if it had selector. Accessing a `Service` without a selector works the same as if it had selector.
The traffic will be routed to endpoints defined by the user (`1.2.3.4:80` in The traffic will be routed to endpoints defined by the user (`1.2.3.4:80` in
this example). this example).

View File

@ -56,6 +56,7 @@ KUBE_API_VERSIONS="v1" "${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \
--port="${API_PORT}" \ --port="${API_PORT}" \
--etcd-servers="http://${ETCD_HOST}:${ETCD_PORT}" \ --etcd-servers="http://${ETCD_HOST}:${ETCD_PORT}" \
--public-address-override="127.0.0.1" \ --public-address-override="127.0.0.1" \
--advertise-address="10.10.10.10" \
--kubelet-port=${KUBELET_PORT} \ --kubelet-port=${KUBELET_PORT} \
--runtime-config=api/v1 \ --runtime-config=api/v1 \
--service-cluster-ip-range="10.0.0.0/24" >/dev/null 2>&1 & --service-cluster-ip-range="10.0.0.0/24" >/dev/null 2>&1 &

View File

@ -1273,7 +1273,7 @@ type EndpointSubset struct {
type EndpointAddress struct { type EndpointAddress struct {
// The IP of this endpoint. // The IP of this endpoint.
// TODO: This should allow hostname or IP, see #4447. // TODO: This should allow hostname or IP, see #4447.
IP string `json:"ip" description:"IP address of the endpoint"` IP string `json:"ip" description:"IP address of the endpoint; may not be loopback (127.0.0.0/8), link-local (169.254.0.0/16), or link-local multicast ((224.0.0.0/24)"`
// Optional: The kubernetes object related to the entry point. // Optional: The kubernetes object related to the entry point.
TargetRef *ObjectReference `json:"targetRef,omitempty" description:"reference to object providing the endpoint"` TargetRef *ObjectReference `json:"targetRef,omitempty" description:"reference to object providing the endpoint"`

View File

@ -1734,24 +1734,25 @@ func validateEndpointSubsets(subsets []api.EndpointSubset) errs.ValidationErrorL
return allErrs return allErrs
} }
var linkLocalNet *net.IPNet
func validateEndpointAddress(address *api.EndpointAddress) errs.ValidationErrorList { func validateEndpointAddress(address *api.EndpointAddress) errs.ValidationErrorList {
if linkLocalNet == nil {
var err error
_, linkLocalNet, err = net.ParseCIDR("169.254.0.0/16")
if err != nil {
glog.Errorf("Failed to parse link-local CIDR: %v", err)
}
}
allErrs := errs.ValidationErrorList{} allErrs := errs.ValidationErrorList{}
if !util.IsValidIPv4(address.IP) { if !util.IsValidIPv4(address.IP) {
allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "invalid IPv4 address")) allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "invalid IPv4 address"))
return allErrs
} }
if linkLocalNet.Contains(net.ParseIP(address.IP)) { // We disallow some IPs as endpoints. Specifically, loopback addresses are
// nonsensical and link-local addresses tend to be used for node-centric
// purposes (e.g. metadata service).
ip := net.ParseIP(address.IP)
if ip.IsLoopback() {
allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "may not be in the loopback range (127.0.0.0/8)"))
}
if ip.IsLinkLocalUnicast() {
allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "may not be in the link-local range (169.254.0.0/16)")) allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "may not be in the link-local range (169.254.0.0/16)"))
} }
if ip.IsLinkLocalMulticast() {
allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "may not be in the link-local multicast range (224.0.0.0/24)"))
}
return allErrs return allErrs
} }

View File

@ -3948,6 +3948,19 @@ func TestValidateEndpoints(t *testing.T) {
}, },
errorType: "FieldValueRequired", errorType: "FieldValueRequired",
}, },
"Address is loopback": {
endpoints: api.Endpoints{
ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
Subsets: []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}},
Ports: []api.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
},
},
},
errorType: "FieldValueInvalid",
errorDetail: "loopback",
},
"Address is link-local": { "Address is link-local": {
endpoints: api.Endpoints{ endpoints: api.Endpoints{
ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
@ -3961,6 +3974,19 @@ func TestValidateEndpoints(t *testing.T) {
errorType: "FieldValueInvalid", errorType: "FieldValueInvalid",
errorDetail: "link-local", errorDetail: "link-local",
}, },
"Address is link-local multicast": {
endpoints: api.Endpoints{
ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
Subsets: []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{IP: "224.0.0.1"}},
Ports: []api.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
},
},
},
errorType: "FieldValueInvalid",
errorDetail: "link-local multicast",
},
} }
for k, v := range errorCases { for k, v := range errorCases {

View File

@ -286,7 +286,7 @@ func setDefaults(c *Config) {
if c.CacheTimeout == 0 { if c.CacheTimeout == 0 {
c.CacheTimeout = 5 * time.Second c.CacheTimeout = 5 * time.Second
} }
for c.PublicAddress == nil || c.PublicAddress.IsUnspecified() { for c.PublicAddress == nil || c.PublicAddress.IsUnspecified() || c.PublicAddress.IsLoopback() {
// TODO: This should be done in the caller and just require a // TODO: This should be done in the caller and just require a
// valid value to be passed in. // valid value to be passed in.
hostIP, err := util.ChooseHostInterface() hostIP, err := util.ChooseHostInterface()