Skip to content
This repository has been archived by the owner on Sep 26, 2023. It is now read-only.

Supporting Ingress policies. #15

Merged
merged 4 commits into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020 Google LLC
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ repo: github.com/GoogleCloudPlatform/gke-fqdnnetworkpolicies-golang
resources:
- group: networking
kind: FQDNNetworkPolicy
version: v1alpha1
version: v1alpha2
version: "2"
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A FQDNNetworkPolicy looks a lot like a NetworkPolicy, but you can configure host
in the "to" field:

```
apiVersion: networking.gke.io/v1alpha1
apiVersion: networking.gke.io/v1alpha2
kind: FQDNNetworkPolicy
metadata:
name: example
Expand Down Expand Up @@ -60,7 +60,6 @@ NetworkPolicy.

There are a few functional limitations to FQDNNetworkPolicies:

* Only *egress* rules are supported.
* Only *hostnames* are supported. In particular, you can't configure a FQDNNetworkPolicy with:
* IP addresses or CIDR blocks. Use NetworkPolicies directly for that.
* wildcard hostnames like `*.example.com`.
Expand Down Expand Up @@ -130,6 +129,13 @@ Follow these instructions to install the FQDNNetworkPolicies controller in your
kubectl apply -f https://storage.googleapis.com/fqdnnetworkpolicies-manifests/${VERSION}.yaml
```

## Upgrades

Upgrading in place from the `v1alpha1` API (used in the 0.1 release) to the
`v1alpha2` (introduced in the 0.2 release) is not supported. You'll need to
uninstall the controller, reinstall it, update your FQDNNetworkPolicies to the
`v1alpha2` API and recreate them.

## Uninstall

To uninstall the FQDNNetworkPolicies controller from your GKE cluster, delete the FQDNNetworkPolicies first,
Expand Down Expand Up @@ -181,8 +187,8 @@ You need the following tools installed on your development workstation.
# In one terminal
make follow-manager-logs
# In another terminal
kubectl apply -f config/samples/networking_v1alpha1_fqdnnetworkpolicy_invalid.yaml
kubectl apply -f config/samples/networking_v1alpha1_fqdnnetworkpolicy_valid.yaml
kubectl apply -f config/samples/networking_v1alpha2_fqdnnetworkpolicy_invalid.yaml
kubectl apply -f config/samples/networking_v1alpha2_fqdnnetworkpolicy_valid.yaml
```

1. Explore the Makefile for other available commands, and read the [kubebuilder book](https://book.kubebuilder.io/introduction.html).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 Google LLC
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,7 +28,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1
package v1alpha2

import (
"io/ioutil"
Expand Down Expand Up @@ -59,21 +59,26 @@ func (r *FQDNNetworkPolicy) LoadResource(path string) *FQDNNetworkPolicy {

// GetValidResource returns loads a valid FQDNNetworkPolicy for testing
func (r *FQDNNetworkPolicy) GetValidResource() *FQDNNetworkPolicy {
return r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_valid.yaml")
return r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_valid.yaml")
}

// GetValidIngressResource returns loads a valid FQDNNetworkPolicy with an Ingress policy for testing
func (r *FQDNNetworkPolicy) GetValidIngressResource() *FQDNNetworkPolicy {
return r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_valid_ingress.yaml")
}

func (r *FQDNNetworkPolicy) GetValidNoPortResource() *FQDNNetworkPolicy {
return r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_valid_noport.yaml")
return r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_valid_noport.yaml")
}

func (r *FQDNNetworkPolicy) GetValidNoProtocolResource() *FQDNNetworkPolicy {
return r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_valid_noprotocol.yaml")
return r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_valid_noprotocol.yaml")
}

func (r *FQDNNetworkPolicy) GetValidNonExistentFQDNResource() *FQDNNetworkPolicy {
return r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_valid_nonexistentfqdn.yaml")
return r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_valid_nonexistentfqdn.yaml")
}

func (r *FQDNNetworkPolicy) GetInvalidResource() *FQDNNetworkPolicy {
return r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_invalid.yaml")
return r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_invalid.yaml")
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 Google LLC
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,10 +28,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1
package v1alpha2

import (
networking "k8s.io/api/networking/v1"
v1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand All @@ -55,8 +56,10 @@ type FQDNNetworkPolicySpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

PodSelector metav1.LabelSelector `json:"podSelector"`
Egress []FQDNNetworkPolicyEgressRule `json:"egress"`
PodSelector metav1.LabelSelector `json:"podSelector" protobuf:"bytes,1,opt,name=podSelector"`
Ingress []FQDNNetworkPolicyIngressRule `json:"ingress,omitempty" protobuf:"bytes,2,rep,name=ingress"`
Egress []FQDNNetworkPolicyEgressRule `json:"egress,omitempty" protobuf:"bytes,3,rep,name=egress"`
PolicyTypes []v1.PolicyType `json:"policyTypes,omitempty" protobuf:"bytes,4,rep,name=policyTypes,casttype=PolicyType"`
}

// FQDNNetworkPolicyStatus defines the observed state of FQDNNetworkPolicy
Expand Down Expand Up @@ -99,6 +102,15 @@ type FQDNNetworkPolicyEgressRule struct {
To []FQDNNetworkPolicyPeer `json:"to"`
}

// FQDNNetworkPolicyIngressRule describes a particular set of
// traffic that is allowed into pods matched by a
// FQDNNetworkPolicySpec's podSelector. The traffic must match
// both ports and from.
type FQDNNetworkPolicyIngressRule struct {
Ports []networking.NetworkPolicyPort `json:"ports,omitempty"`
From []FQDNNetworkPolicyPeer `json:"from"`
}

// FQDNNetworkPolicyPeer represents a FQDN that the
// FQDNNetworkPolicy allows connections to.
type FQDNNetworkPolicyPeer struct {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 Google LLC
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,7 +28,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1
package v1alpha2

import (
"golang.org/x/net/idna"
Expand All @@ -53,7 +53,7 @@ func (r *FQDNNetworkPolicy) SetupWebhookWithManager(mgr ctrl.Manager) error {

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!

// +kubebuilder:webhook:path=/mutate-networking-gke-io-v1alpha1-fqdnnetworkpolicy,mutating=true,failurePolicy=fail,groups=networking.gke.io,resources=fqdnnetworkpolicies,verbs=create;update,versions=v1alpha1,name=mfqdnnetworkpolicy.kb.io
// +kubebuilder:webhook:path=/mutate-networking-gke-io-v1alpha2-fqdnnetworkpolicy,mutating=true,failurePolicy=fail,groups=networking.gke.io,resources=fqdnnetworkpolicies,verbs=create;update,versions=v1alpha2,name=mfqdnnetworkpolicy.kb.io

var _ webhook.Defaulter = &FQDNNetworkPolicy{}

Expand All @@ -75,10 +75,24 @@ func (r *FQDNNetworkPolicy) Default() {
}
}
}
for ii, rule := range r.Spec.Ingress {
if rule.Ports != nil {
for ip, port := range rule.Ports {
if *port.Protocol == "" {
fqdnnetworkpolicylog.V(1).Info("No protocol set, defaulting to TCP",
"namespace", r.ObjectMeta.Namespace,
"name", r.ObjectMeta.Name,
"path", field.NewPath("spec").Child("ingress").
Index(ii).Child("ports").Index(ip).String())
*port.Protocol = v1.ProtocolTCP
}
}
}
}
}

// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
// +kubebuilder:webhook:verbs=create;update,path=/validate-networking-gke-io-v1alpha1-fqdnnetworkpolicy,mutating=false,failurePolicy=fail,groups=networking.gke.io,resources=fqdnnetworkpolicies,versions=v1alpha1,name=vfqdnnetworkpolicy.kb.io
// +kubebuilder:webhook:verbs=create;update,path=/validate-networking-gke-io-v1alpha2-fqdnnetworkpolicy,mutating=false,failurePolicy=fail,groups=networking.gke.io,resources=fqdnnetworkpolicies,versions=v1alpha2,name=vfqdnnetworkpolicy.kb.io

var _ webhook.Validator = &FQDNNetworkPolicy{}

Expand Down Expand Up @@ -150,6 +164,30 @@ func (r *FQDNNetworkPolicy) ValidatePorts() field.ErrorList {
}
}
}
for ii, rule := range r.Spec.Ingress {
if rule.Ports != nil {
for ip, port := range rule.Ports {
if port.Port.IntVal < 0 || port.Port.IntVal > 65535 {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("ingress").
Index(ii).Child("ports").Index(ip).Child("port"),
port.Port, "Invalid port. Must be between 0 and 65535."))
}
if port.Port.IntVal == 0 {
fqdnnetworkpolicylog.Info("port not set or set to 0, will match all ports",
"name", r.ObjectMeta.Name,
"namespace", r.ObjectMeta.Namespace,
"resource", field.NewPath("spec").Child("ingress").
Index(ii).Child("ports").Index(ip).Child("port").String())
}
if *port.Protocol != v1.ProtocolTCP && *port.Protocol != v1.ProtocolUDP &&
*port.Protocol != v1.ProtocolSCTP && *port.Protocol != "" {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("ingress").
Index(ii).Child("ports").Index(ip).Child("protocol"),
port.Port, "Invalid protocol. Must be TCP, UDP, or SCTP."))
}
}
}
}
if len(allErrs) == 0 {
return nil
}
Expand Down Expand Up @@ -177,6 +215,23 @@ func (r *FQDNNetworkPolicy) ValidateFQDNs() field.ErrorList {
}
}
}
for ii, rule := range r.Spec.Ingress {
if rule.From != nil {
for ifrom, from := range rule.From {
for ifqdn, fqdn := range from.FQDNs {
var p *idna.Profile
p = idna.New(idna.ValidateForRegistration())
_, err := p.ToASCII(fqdn)
if err != nil {
allErrs = append(allErrs, field.Invalid(
field.NewPath("spec").Child("egress").Index(ii).
Child("to").Index(ifrom).Child("fqdns").Index(ifqdn),
fqdn, err.Error()))
}
}
}
}
}
if len(allErrs) == 0 {
return nil
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 Google LLC
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,7 +28,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1
package v1alpha2

import (
"testing"
Expand All @@ -40,6 +40,10 @@ func TestValidateCreate(t *testing.T) {
t.Error("Valid resource marked as invalid during creation")
}

if r.GetValidIngressResource().ValidateCreate() != nil {
t.Error("Valid resource with Ingress policy marked as invalid during creation")
}

if r.GetValidNoPortResource().ValidateCreate() != nil {
t.Error("Valid resource with no port marked as invalid during creation")
}
Expand All @@ -63,6 +67,10 @@ func TestValidateUpdate(t *testing.T) {
t.Error("Valid resource marked as invalid during update")
}

if r.GetValidIngressResource().ValidateUpdate(&ro) != nil {
t.Error("Valid resource with Ingress policy marked as invalid during update")
}

if r.GetValidNoPortResource().ValidateUpdate(&ro) != nil {
t.Error("Valid resource with no port marked as invalid during update")
}
Expand All @@ -84,6 +92,10 @@ func TestValidateDelete(t *testing.T) {
t.Error("Valid resource marked as invalid during deletion")
}

if r.GetValidIngressResource().ValidateDelete() != nil {
t.Error("Valid resource with Ingress policy marked as invalid during deletion")
}

if r.GetValidNoPortResource().ValidateDelete() != nil {
t.Error("Valid resource with no port marked as invalid during deletion")
}
Expand All @@ -104,18 +116,21 @@ func TestValidatePorts(t *testing.T) {
if r.GetValidResource().ValidatePorts() != nil {
t.Error("Valid resource marked as having invalid ports")
}
if r.GetValidIngressResource().ValidatePorts() != nil {
t.Error("Valid resource with Ingress policy marked as having invalid ports")
}
if r.GetValidNoPortResource().ValidatePorts() != nil {
t.Error("Valid resource with no port marked as having invalid ports")
}
if r.GetValidNoProtocolResource().ValidatePorts() != nil {
t.Error("Valid resource with no protocol marked as having invalid ports")
}

r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_invalid_port.yaml")
r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_invalid_port.yaml")
if r.ValidatePorts() == nil {
t.Error("Resource with invalid ports marked as valid")
}
r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_invalid_protocol.yaml")
r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_invalid_protocol.yaml")
if r.ValidatePorts() == nil {
t.Error("Resource with invalid protocol marked as valid")
}
Expand All @@ -127,22 +142,25 @@ func TestValidateFQDNs(t *testing.T) {
if r.GetValidResource().ValidateFQDNs() != nil {
t.Error("Valid resource marked as having invalid FQDNs")
}
if r.GetValidIngressResource().ValidateFQDNs() != nil {
t.Error("Valid resource with Ingress policy marked as having invalid FQDNs")
}
if r.GetValidNoPortResource().ValidatePorts() != nil {
t.Error("Valid resource with no port marked as having invalid ports")
}
if r.GetValidNoProtocolResource().ValidatePorts() != nil {
t.Error("Valid resource with no protocol marked as having invalid ports")
}

r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_invalid_wildcard.yaml")
r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_invalid_wildcard.yaml")
if r.ValidateFQDNs() == nil {
t.Error("Resource with wildcard marked as valid")
}
r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_invalid_fqdntoolong.yaml")
r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_invalid_fqdntoolong.yaml")
if r.ValidateFQDNs() == nil {
t.Error("Resource with invalid FQDN (too long) marked as valid")
}
r.LoadResource("./config/samples/networking_v1alpha1_fqdnnetworkpolicy_invalid_labeltoolong.yaml")
r.LoadResource("./config/samples/networking_v1alpha2_fqdnnetworkpolicy_invalid_labeltoolong.yaml")
if r.ValidateFQDNs() == nil {
t.Error("Resource with invalid FQDN (label too long) marked as valid")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 Google LLC
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,10 +28,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Package v1alpha1 contains API Schema definitions for the networking v1alpha1 API group
// Package v1alpha2 contains API Schema definitions for the networking v1alpha2 API group
// +kubebuilder:object:generate=true
// +groupName=networking.gke.io
package v1alpha1
package v1alpha2

import (
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -40,7 +40,7 @@ import (

var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "networking.gke.io", Version: "v1alpha1"}
GroupVersion = schema.GroupVersion{Group: "networking.gke.io", Version: "v1alpha2"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
Expand Down
Loading