Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add policy support for named ports #11092

Merged
merged 10 commits into from May 2, 2020
23 changes: 23 additions & 0 deletions api/v1/models/endpoint_status.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions api/v1/models/named_ports.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions api/v1/models/port.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions api/v1/openapi.yaml
Expand Up @@ -1121,6 +1121,9 @@ definitions:
health:
description: Summary overall endpoint & subcomponent health
"$ref": "#/definitions/EndpointHealth"
namedPorts:
description: List of named ports that can be used in Network Policy
"$ref": "#/definitions/NamedPorts"
EndpointState:
description: State of endpoint
type: string
Expand Down Expand Up @@ -2024,6 +2027,9 @@ definitions:
description: Layer 4 port number
type: integer
format: uint16
name:
description: Optional layer 4 port name
type: string
SelectorCache:
description: cache of which identities match selectors in the policy repository
type: array
Expand Down Expand Up @@ -2451,3 +2457,10 @@ definitions:
name:
description: Name assigned to the IP (e.g. Kubernetes pod name)
type: string
NamedPorts:
description: |
List of named Layer 4 port and protocol pairs which will be used in Network
Policy specs.
type: array
items:
"$ref": "#/definitions/Port"
30 changes: 30 additions & 0 deletions api/v1/server/embedded_spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions cilium/cmd/policy_trace.go
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/cilium/cilium/pkg/api"
"github.com/cilium/cilium/pkg/command"
endpointid "github.com/cilium/cilium/pkg/endpoint/id"
"github.com/cilium/cilium/pkg/iana"
"github.com/cilium/cilium/pkg/identity"
"github.com/cilium/cilium/pkg/k8s"
clientset "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned"
Expand Down Expand Up @@ -273,7 +274,7 @@ func getSecIDFromK8s(podName string) (string, error) {
}

// parseL4PortsSlice parses a given `slice` of strings. Each string should be in
// the form of `<port>[/<protocol>]`, where the `<port>` is an integer and
// the form of `<port>[/<protocol>]`, where the `<port>` is an integer or a port name and
// `<protocol>` is an optional layer 4 protocol `tcp` or `udp`. In case
// `protocol` is not present, or is set to `any`, the parsed port will be set to
// `models.PortProtocolAny`.
Expand All @@ -295,13 +296,19 @@ func parseL4PortsSlice(slice []string) ([]*models.Port, error) {
default:
return nil, fmt.Errorf("invalid format %q. Should be <port>[/<protocol>]", v)
}
port := 0
portStr := vSplit[0]
port, err := strconv.Atoi(portStr)
if err != nil {
return nil, fmt.Errorf("invalid port %q: %s", portStr, err)
if !iana.IsSvcName(portStr) {
var err error
port, err = strconv.Atoi(portStr)
if err != nil {
return nil, fmt.Errorf("invalid port %q: %s", portStr, err)
}
portStr = ""
}
l4 := &models.Port{
Port: uint16(port),
Name: portStr,
Protocol: protoStr,
}
rules = append(rules, l4)
Expand Down
17 changes: 10 additions & 7 deletions daemon/cmd/endpoint.go
Expand Up @@ -154,24 +154,24 @@ func NewPutEndpointIDHandler(d *Daemon) PutEndpointIDHandler {
// endpoint metadata. It implements endpoint.MetadataResolverCB.
// The returned pod is deepcopied which means the its fields can be written
// into.
func (d *Daemon) fetchK8sLabelsAndAnnotations(nsName, podName string) (*types.Pod, labels.Labels, labels.Labels, map[string]string, error) {
func (d *Daemon) fetchK8sLabelsAndAnnotations(nsName, podName string) (*types.Pod, []types.ContainerPort, labels.Labels, labels.Labels, map[string]string, error) {
p, err := d.k8sWatcher.GetCachedPod(nsName, podName)
if err != nil {
return nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, err
}
ns, err := d.k8sWatcher.GetCachedNamespace(nsName)
if err != nil {
return nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, err
}

lbls, annotations, err := k8s.GetPodMetadata(ns, p)
containerPorts, lbls, annotations, err := k8s.GetPodMetadata(ns, p)
if err != nil {
return nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, err
}

k8sLbls := labels.Map2Labels(lbls, labels.LabelSourceK8s)
identityLabels, infoLabels := labelsfilter.Filter(k8sLbls)
return p, identityLabels, infoLabels, annotations, nil
return p, containerPorts, identityLabels, infoLabels, annotations, nil
}

func invalidDataError(ep *endpoint.Endpoint, err error) (*endpoint.Endpoint, int, error) {
Expand Down Expand Up @@ -265,11 +265,14 @@ func (d *Daemon) createEndpoint(ctx context.Context, epTemplate *models.Endpoint
}

if ep.K8sNamespaceAndPodNameIsSet() && k8s.IsEnabled() {
pod, identityLabels, info, _, err := d.fetchK8sLabelsAndAnnotations(ep.K8sNamespace, ep.K8sPodName)
pod, cp, identityLabels, info, _, err := d.fetchK8sLabelsAndAnnotations(ep.K8sNamespace, ep.K8sPodName)
if err != nil {
ep.Logger("api").WithError(err).Warning("Unable to fetch kubernetes labels")
} else {
ep.SetPod(pod)
if err := ep.SetNamedPorts(cp); err != nil {
return invalidDataError(ep, fmt.Errorf("Invalid ContainerPorts %v: %s", cp, err))
}
addLabels.MergeLabels(identityLabels)
infoLabels.MergeLabels(info)
}
Expand Down
2 changes: 2 additions & 0 deletions daemon/cmd/ipcache.go
Expand Up @@ -86,6 +86,8 @@ func (ipc *ipCacheDumpListener) OnIPIdentityCacheChange(modType ipcache.CacheMod
Source: string(source.Kubernetes),
Namespace: k8sMeta.Namespace,
Name: k8sMeta.PodName,
// TODO (jrajahalme): Consider if named ports should be
// made visible in the model.
}
}

Expand Down
2 changes: 1 addition & 1 deletion daemon/cmd/state.go
Expand Up @@ -41,7 +41,7 @@ type endpointRestoreState struct {
toClean []*endpoint.Endpoint
}

// validateEndpoint attempts to determine that the endpoint is valid, ie it
// validateEndpoint attempts to determine that the restored endpoint is valid, ie it
// still exists in k8s, its datapath devices are present, and Cilium is
// responsible for its workload, etc.
//
Expand Down
27 changes: 27 additions & 0 deletions pkg/endpoint/api.go
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/cilium/cilium/pkg/option"
"github.com/cilium/cilium/pkg/policy"
"github.com/cilium/cilium/pkg/policy/trafficdirection"
"github.com/cilium/cilium/pkg/u8proto"
)

// GetLabelsModel returns the labels of the endpoint in their representation
Expand Down Expand Up @@ -227,6 +228,7 @@ func (e *Endpoint) GetModelRLocked() *models.Endpoint {
Controllers: controllerMdl,
State: currentState, // TODO: Validate
Health: e.getHealthModel(),
NamedPorts: e.getNamedPortsModel(),
},
}

Expand Down Expand Up @@ -298,6 +300,31 @@ func (e *Endpoint) GetHealthModel() *models.EndpointHealth {
return e.getHealthModel()
}

// getNamedPortsModel returns the endpoint's NamedPorts object.
//
// Must be called with e.Mutex locked.
func (e *Endpoint) getNamedPortsModel() (np models.NamedPorts) {
k8sPorts := e.k8sPorts
np = make(models.NamedPorts, 0, len(k8sPorts))
for name, value := range k8sPorts {
np = append(np, &models.Port{
Name: name,
Port: value.Port,
Protocol: u8proto.U8proto(value.Proto).String(),
})
}
return np
}

// GetNamedPortsModel returns the endpoint's NamedPorts object.
func (e *Endpoint) GetNamedPortsModel() models.NamedPorts {
if err := e.rlockAlive(); err != nil {
return nil
}
defer e.runlock()
return e.getNamedPortsModel()
}

// GetModel returns the API model of endpoint e.
func (e *Endpoint) GetModel() *models.Endpoint {
if e == nil {
Expand Down