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

create clusterwidenetworkpolicy from AccessList.SourceRanges #67

Merged
merged 11 commits into from
Feb 8, 2021
Merged
15 changes: 13 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ manager: generate fmt vet
-o bin/manager main.go

# Run against the configured Kubernetes cluster in ~/.kube/config
run: generate fmt vet manifests
run: generate fmt vet manifests install-crd-cwnp
go run ./main.go -partition-id sample-partition -tenant sample-tenant -controlplane-kubeconfig "./kubeconfig"

# Install CRDs into a cluster
Expand All @@ -53,7 +53,7 @@ uninstall: manifests
kustomize build config/crd | kubectl --kubeconfig kubeconfig delete -f -

# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
deploy: manifests secret
deploy: install-crd-cwnp manifests secret kind-load-image
cd config/manager && kustomize edit set image controller=${IMG}:${VERSION}
kustomize build config/default | kubectl apply -f -

Expand Down Expand Up @@ -154,3 +154,14 @@ helm:
helm package charts/postgreslet-support/
helm dependency build charts/postgreslet/
helm package charts/postgreslet/

test-cwnp:
./hack/test-cwnp.sh

install-crd-cwnp:
kubectl apply -f https://raw.githubusercontent.com/metal-stack/firewall-controller/master/config/crd/bases/metal-stack.io_clusterwidenetworkpolicies.yaml
kubectl create ns firewall --dry-run=client --save-config -o yaml | kubectl apply -f -

uninstall-crd-cwnp:
kubectl delete ns firewall
kubectl delete -f https://raw.githubusercontent.com/metal-stack/firewall-controller/master/config/crd/bases/metal-stack.io_clusterwidenetworkpolicies.yaml
55 changes: 52 additions & 3 deletions api/v1/postgres_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@ limitations under the License.
package v1

import (
"fmt"
"time"

firewall "github.com/metal-stack/firewall-controller/api/v1"
"inet.af/netaddr"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
Expand Down Expand Up @@ -80,8 +86,7 @@ type PostgresSpec struct {

// AccessList defines the type of restrictions to access the database
type AccessList struct {
// SourceRanges defines a list of prefixes in CIDR Notation e.g. 1.2.3.0/24
// FIXME implement validation if source is a parsable CIDR
// SourceRanges defines a list of prefixes in CIDR Notation e.g. 1.2.3.0/24 or fdaa::/104
SourceRanges []string `json:"sourceRanges,omitempty"`
}

Expand Down Expand Up @@ -152,18 +157,62 @@ type PostgresList struct {
Items []Postgres `json:"items"`
}

// HasSourceRanges returns true if SourceRanges are set
func (p *Postgres) HasSourceRanges() bool {
return p.Spec.AccessList != nil && p.Spec.AccessList.SourceRanges != nil
}

// IsBeingDeleted returns true if the deletion-timestamp is set
func (p *Postgres) IsBeingDeleted() bool {
return !p.ObjectMeta.DeletionTimestamp.IsZero()
}

// ToCWNP returns CRD ClusterwideNetworkPolicy derived from CRD Postgres
func (p *Postgres) ToCWNP(port int) (*firewall.ClusterwideNetworkPolicy, error) {
portObj := intstr.FromInt(port)
tcp := corev1.ProtocolTCP
ports := []networkingv1.NetworkPolicyPort{
{Port: &portObj, Protocol: &tcp},
}

// When SourceRanges of Postgres aren't set, ipblocks will be left blank,
// which implies denying all accecces.
ipblocks := []networkingv1.IPBlock{}
if p.HasSourceRanges() {
for _, src := range p.Spec.AccessList.SourceRanges {
parsedSrc, err := netaddr.ParseIPPrefix(src)
if err != nil {
return nil, fmt.Errorf("unable to parse source range %s: %w", src, err)
}
ipblock := networkingv1.IPBlock{
CIDR: parsedSrc.String(),
}
ipblocks = append(ipblocks, ipblock)
}
}

policy := &firewall.ClusterwideNetworkPolicy{}
// todo: Use the exported const.
policy.Namespace = "firewall"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a const somewhere in the firewall package that we can use instead of this hardcoded string? If this changes in the control plane cluster / firewall controller, this cwnp might not be loaded.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

policy.Name = p.ToPeripheralResourceName()
policy.Spec.Ingress = []firewall.IngressRule{
{Ports: ports, From: ipblocks},
}

return policy, nil
}

func (p *Postgres) ToKey() *types.NamespacedName {
return &types.NamespacedName{
Namespace: p.Namespace,
Name: p.Name,
}
}

func (p *Postgres) ToPeripheralResourceName() string {
return p.Spec.ProjectID + "--" + string(p.UID)
}

// Name of the label referencing the owning Postgres resource in the control cluster
const LabelName string = "postgres.database.fits.cloud/uuid"

Expand All @@ -172,7 +221,7 @@ func (p *Postgres) ToZalandoPostgres() *ZalandoPostgres {
return &ZalandoPostgres{
TypeMeta: ZalandoPostgresTypeMeta,
ObjectMeta: metav1.ObjectMeta{
Name: p.Spec.ProjectID + "--" + string(p.UID), // todo: "." used to work but not anymore. Make sure the rule is well-documented.
Name: p.ToPeripheralResourceName(),
Namespace: projectID, // todo: Check if the projectID is too long for zalando operator.
Labels: map[string]string{LabelName: string(p.UID)},
},
Expand Down
3 changes: 1 addition & 2 deletions config/crd/bases/database.fits.cloud_postgres.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ spec:
properties:
sourceRanges:
description: SourceRanges defines a list of prefixes in CIDR Notation
e.g. 1.2.3.0/24 FIXME implement validation if source is a parsable
CIDR
e.g. 1.2.3.0/24 or fdaa::/104
items:
type: string
type: array
Expand Down
23 changes: 23 additions & 0 deletions config/samples/_test_cwnp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: v1
kind: Namespace
metadata:
name: database
---
apiVersion: database.fits.cloud/v1
kind: Postgres
metadata:
namespace: database
name: sample-name-a
spec:
accessList:
sourceRanges:
- 1.2.3.4/24
backup:
s3BucketURL: ""
numberOfInstances: 2
partitionID: sample-partition
projectID: projectid-a
size:
storageSize: 1Gi
tenant: sample-tenant
version: "12"
3 changes: 3 additions & 0 deletions config/samples/database_v1_postgres.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ metadata:
namespace: database
name: sample-name-b
spec:
accessList:
sourceRanges:
- 1.2.3.4/24
backup:
s3BucketURL: ""
numberOfInstances: 2
Expand Down
50 changes: 49 additions & 1 deletion controllers/postgres_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ import (
"github.com/go-logr/logr"
zalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
corev1 "k8s.io/api/core/v1"

firewall "github.com/metal-stack/firewall-controller/api/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

pg "github.com/fi-ts/postgres-controller/api/v1"
"github.com/fi-ts/postgres-controller/pkg/operatormanager"
Expand Down Expand Up @@ -79,6 +83,11 @@ func (r *PostgresReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {

// Delete
if instance.IsBeingDeleted() {
if err := r.deleteCWNP(ctx, instance); client.IgnoreNotFound(err) != nil { // todo: remove ignorenotfound
return ctrl.Result{}, err
}
log.Info("corresponding CRD ClusterwideNetworkPolicy deleted")

log.Info("deleting owned zalando postgresql")

if err := r.deleteZPostgresqlByLabels(ctx, matchingLabels, namespace); err != nil {
Expand Down Expand Up @@ -146,7 +155,11 @@ func (r *PostgresReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
log.Info("zalando postgresql updated", "ns/name", namespacedName)
}

// todo: Check the port. The default port of postgres is used.
// Update status will be handled by the StatusReconciler, based on the Zalando Status
if err := r.createOrUpdateCWNP(ctx, instance, 5432); err != nil {
return ctrl.Result{}, fmt.Errorf("unable to create or update corresponding CRD ClusterwideNetworkPolicy: %W", err)
}

return ctrl.Result{}, nil
}
Expand Down Expand Up @@ -241,6 +254,10 @@ func (r *PostgresReconciler) deleteZPostgresqlByLabels(ctx context.Context, matc
}

func patchRawZ(out *zalando.Postgresql, in *pg.Postgres) {
if in.HasSourceRanges() {
out.Spec.AllowedSourceRanges = in.Spec.AccessList.SourceRanges
}

out.Spec.NumberOfInstances = in.Spec.NumberOfInstances

// todo: Check if the validation should be performed here.
Expand Down Expand Up @@ -271,7 +288,38 @@ func patchRawZ(out *zalando.Postgresql, in *pg.Postgres) {
}
}()

// todo: in.Spec.Backup, in.Spec.AccessList
// todo: in.Spec.Backup
}

// todo: Change to `controllerutl.CreateOrPatch`
// createOrUpdateCWNP will create an ingress firewall rule on the firewall in front of the k8s cluster
// based on the spec.AccessList.SourceRanges given.
eberlep marked this conversation as resolved.
Show resolved Hide resolved
func (r *PostgresReconciler) createOrUpdateCWNP(ctx context.Context, in *pg.Postgres, port int) error {
policy, err := in.ToCWNP(port)
if err != nil {
return fmt.Errorf("unable to convert instance to CRD ClusterwideNetworkPolicy: %w", err)
}

// placeholder of the object with the specified namespaced name
key := &firewall.ClusterwideNetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: policy.Name, Namespace: policy.Namespace}}
if _, err := controllerutil.CreateOrUpdate(ctx, r.Service, key, func() error {
key.Spec.Ingress = policy.Spec.Ingress
return nil
}); err != nil {
return fmt.Errorf("unable to deploy CRD ClusterwideNetworkPolicy: %w", err)
}

return nil
}

func (r *PostgresReconciler) deleteCWNP(ctx context.Context, in *pg.Postgres) error {
policy := &firewall.ClusterwideNetworkPolicy{}
policy.Namespace = "firewall"
eberlep marked this conversation as resolved.
Show resolved Hide resolved
policy.Name = in.ToPeripheralResourceName()
if err := r.Service.Delete(ctx, policy); err != nil {
return fmt.Errorf("unable to delete CRD ClusterwideNetworkPolicy %v: %w", policy.Name, err)
}
return nil
}

// Returns *only one* Zalndo Postgresql resource with the given label, returns an error if not unique.
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ go 1.15

require (
github.com/go-logr/logr v0.3.0
github.com/go-logr/zapr v0.3.0 // indirect
github.com/metal-stack/firewall-controller v1.0.1
github.com/metal-stack/v v1.0.2
github.com/onsi/ginkgo v1.14.2
github.com/onsi/gomega v1.10.5
github.com/zalando/postgres-operator v1.5.0
inet.af/netaddr v0.0.0-20210115183222-bffc12a571f6
k8s.io/api v0.19.4
k8s.io/apiextensions-apiserver v0.18.6
k8s.io/apiextensions-apiserver v0.18.9
k8s.io/apimachinery v0.19.4
k8s.io/client-go v11.0.0+incompatible
sigs.k8s.io/controller-runtime v0.6.4
Expand Down
Loading