Skip to content

Commit

Permalink
Fix for traffic accounting (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
majst01 authored Jul 26, 2021
1 parent 686a7e0 commit f70d32b
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.4.0
controller-gen.kubebuilder.io/version: v0.6.1
creationTimestamp: null
name: clusterwidenetworkpolicies.metal-stack.io
spec:
Expand Down Expand Up @@ -74,6 +74,7 @@ spec:
numbers.
x-kubernetes-int-or-string: true
protocol:
default: TCP
description: The protocol (TCP, UDP, or SCTP) which traffic
must match. If not specified, this field defaults to
TCP.
Expand Down Expand Up @@ -174,6 +175,7 @@ spec:
numbers.
x-kubernetes-int-or-string: true
protocol:
default: TCP
description: The protocol (TCP, UDP, or SCTP) which traffic
must match. If not specified, this field defaults to
TCP.
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/metal-stack.io_firewalls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.4.0
controller-gen.kubebuilder.io/version: v0.6.1
creationTimestamp: null
name: firewalls.metal-stack.io
spec:
Expand Down
86 changes: 73 additions & 13 deletions controllers/firewall_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ package controllers

import (
"context"
"crypto/md5" //nolint:gosec
"crypto/rsa"
"encoding/json"
"fmt"
"reflect"
"time"
Expand All @@ -40,14 +42,15 @@ import (

"github.com/hashicorp/go-multierror"

mn "github.com/metal-stack/metal-lib/pkg/net"
networking "k8s.io/api/networking/v1"

firewallv1 "github.com/metal-stack/firewall-controller/api/v1"
"github.com/metal-stack/firewall-controller/pkg/collector"
"github.com/metal-stack/firewall-controller/pkg/network"
"github.com/metal-stack/firewall-controller/pkg/nftables"
"github.com/metal-stack/firewall-controller/pkg/suricata"
"github.com/metal-stack/firewall-controller/pkg/updater"
mn "github.com/metal-stack/metal-lib/pkg/net"
networking "k8s.io/api/networking/v1"
)

// FirewallReconciler reconciles a Firewall object
Expand All @@ -59,6 +62,7 @@ type FirewallReconciler struct {
EnableIDS bool
EnableSignatureCheck bool
CAPubKey *rsa.PublicKey
policySpecsChecksums map[string][16]byte
}

const (
Expand Down Expand Up @@ -123,14 +127,25 @@ func (r *FirewallReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
requeue.RequeueAfter = i
}

var cwnps firewallv1.ClusterwideNetworkPolicyList
if err := r.List(ctx, &cwnps, client.InNamespace(f.Namespace)); err != nil {
return done, err
}
changed, err := r.isSpecsChanged(cwnps)
if err != nil {
return done, err
}

var errors *multierror.Error
log.Info("reconciling nftables rules")
if err = r.reconcileRules(ctx, f, log); err != nil {
errors = multierror.Append(errors, err)
if changed {
log.Info("reconciling nftables rules")
if err = r.reconcileRules(ctx, f, cwnps, log); err != nil {
errors = multierror.Append(errors, err)
}
}

log.Info("reconciling network settings")
changed, err := network.ReconcileNetwork(f, log)
changed, err = network.ReconcileNetwork(f, log)
if changed && err == nil {
r.recorder.Event(&f, corev1.EventTypeNormal, "Network settings", "reconcilation succeeded (frr.conf)")
} else if changed && err != nil {
Expand Down Expand Up @@ -231,22 +246,24 @@ func convert(np networking.NetworkPolicy) (*firewallv1.ClusterwideNetworkPolicy,
}

// reconcileRules reconciles the nftable rules for this firewall
func (r *FirewallReconciler) reconcileRules(ctx context.Context, f firewallv1.Firewall, log logr.Logger) error {
var clusterNPs firewallv1.ClusterwideNetworkPolicyList
if err := r.List(ctx, &clusterNPs, client.InNamespace(f.Namespace)); err != nil {
return err
}

func (r *FirewallReconciler) reconcileRules(ctx context.Context, f firewallv1.Firewall, cwnps firewallv1.ClusterwideNetworkPolicyList, log logr.Logger) error {
var services corev1.ServiceList
if err := r.List(ctx, &services); err != nil {
return err
}

nftablesFirewall := nftables.NewFirewall(&clusterNPs, &services, f.Spec, log)
nftablesFirewall := nftables.NewFirewall(&cwnps, &services, f.Spec, log)
if err := nftablesFirewall.Reconcile(); err != nil {
return err
}

// It's assumed that nftablesFirewall.Reconcile can modify CWNPs
for _, cwnp := range cwnps.Items {
if err := r.updateCWNPChecksum(cwnp); err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -445,6 +462,49 @@ func (r *FirewallReconciler) updateStatus(ctx context.Context, f firewallv1.Fire
return nil
}

func (r *FirewallReconciler) isSpecsChanged(cwnps firewallv1.ClusterwideNetworkPolicyList) (bool, error) {
if r.policySpecsChecksums == nil {
r.policySpecsChecksums = make(map[string][16]byte)
}

for _, cwnp := range cwnps.Items {
nn := types.NamespacedName{
Name: cwnp.Name,
Namespace: cwnp.Namespace,
}
oldSum, exists := r.policySpecsChecksums[nn.String()]
if !exists {
return true, nil
}

j, err := json.Marshal(cwnp.Spec)
if err != nil {
return false, fmt.Errorf("failed to parse '%s' CWNP spec: %w", cwnp.Name, err)
}

currentSum := md5.Sum(j) //nolint:gosec
if exists && !reflect.DeepEqual(oldSum, currentSum) {
return true, nil
}
}

return false, nil
}

func (r *FirewallReconciler) updateCWNPChecksum(cwnp firewallv1.ClusterwideNetworkPolicy) error {
j, err := json.Marshal(cwnp.Spec)
if err != nil {
return fmt.Errorf("failed to parse updated '%s' CWNP spec: %w", cwnp.Name, err)
}

nn := types.NamespacedName{
Name: cwnp.Name,
Namespace: cwnp.Namespace,
}
r.policySpecsChecksums[nn.String()] = md5.Sum(j) //nolint:gosec
return nil
}

// SetupWithManager configures this controller to watch for the CRDs in a specific namespace
func (r *FirewallReconciler) SetupWithManager(mgr ctrl.Manager) error {
r.recorder = mgr.GetEventRecorderFor("FirewallController")
Expand Down
8 changes: 4 additions & 4 deletions pkg/nftables/nftables.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ table ip firewall {
type filter hook forward priority 1; policy drop;
# network traffic accounting for external traffic
ip saddr != @internal_prefixes oifname "vlan{{ .PrivateVrfID }}" counter name external_in comment "count external traffic incomming"
ip daddr != @internal_prefixes iifname "vrf{{ .PrivateVrfID }}" counter name external_out comment "count external traffic outgoing"
ip saddr != @internal_prefixes oifname {"vlan{{ .PrivateVrfID }}", "vrf{{ .PrivateVrfID }}"} counter name external_in comment "count external traffic incomming"
ip daddr != @internal_prefixes iifname {"vlan{{ .PrivateVrfID }}", "vrf{{ .PrivateVrfID }}"} counter name external_out comment "count external traffic outgoing"

# network traffic accounting for internal traffic
ip saddr @internal_prefixes oifname "vlan{{ .PrivateVrfID }}" counter name internal_in comment "count internal traffic incomming"
ip daddr @internal_prefixes iifname "vrf{{ .PrivateVrfID }}" counter name internal_out comment "count internal traffic outgoing"
ip saddr @internal_prefixes oifname {"vlan{{ .PrivateVrfID }}", "vrf{{ .PrivateVrfID }}"} counter name internal_in comment "count internal traffic incomming"
ip daddr @internal_prefixes iifname {"vlan{{ .PrivateVrfID }}", "vrf{{ .PrivateVrfID }}"} counter name internal_out comment "count internal traffic outgoing"

# rate limits
{{- range .RateLimitRules }}
Expand Down
8 changes: 4 additions & 4 deletions pkg/nftables/test_data/more-rules.nftable.v4
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ table ip firewall {
type filter hook forward priority 1; policy drop;

# network traffic accounting for external traffic
ip saddr != @internal_prefixes oifname "vlan42" counter name external_in comment "count external traffic incomming"
ip daddr != @internal_prefixes iifname "vrf42" counter name external_out comment "count external traffic outgoing"
ip saddr != @internal_prefixes oifname {"vlan42", "vrf42"} counter name external_in comment "count external traffic incomming"
ip daddr != @internal_prefixes iifname {"vlan42", "vrf42"} counter name external_out comment "count external traffic outgoing"

# network traffic accounting for internal traffic
ip saddr @internal_prefixes oifname "vlan42" counter name internal_in comment "count internal traffic incomming"
ip daddr @internal_prefixes iifname "vrf42" counter name internal_out comment "count internal traffic outgoing"
ip saddr @internal_prefixes oifname {"vlan42", "vrf42"} counter name internal_in comment "count internal traffic incomming"
ip daddr @internal_prefixes iifname {"vlan42", "vrf42"} counter name internal_out comment "count internal traffic outgoing"

# rate limits
meta iifname "eth0" limit rate over 10 mbytes/second counter name drop_ratelimit drop
Expand Down
8 changes: 4 additions & 4 deletions pkg/nftables/test_data/simple.nftable.v4
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ table ip firewall {
type filter hook forward priority 1; policy drop;

# network traffic accounting for external traffic
ip saddr != @internal_prefixes oifname "vlan42" counter name external_in comment "count external traffic incomming"
ip daddr != @internal_prefixes iifname "vrf42" counter name external_out comment "count external traffic outgoing"
ip saddr != @internal_prefixes oifname {"vlan42", "vrf42"} counter name external_in comment "count external traffic incomming"
ip daddr != @internal_prefixes iifname {"vlan42", "vrf42"} counter name external_out comment "count external traffic outgoing"

# network traffic accounting for internal traffic
ip saddr @internal_prefixes oifname "vlan42" counter name internal_in comment "count internal traffic incomming"
ip daddr @internal_prefixes iifname "vrf42" counter name internal_out comment "count internal traffic outgoing"
ip saddr @internal_prefixes oifname {"vlan42", "vrf42"} counter name internal_in comment "count internal traffic incomming"
ip daddr @internal_prefixes iifname {"vlan42", "vrf42"} counter name internal_out comment "count internal traffic outgoing"

# rate limits
meta iifname "eth0" limit rate over 10 mbytes/second counter name drop_ratelimit drop
Expand Down
8 changes: 4 additions & 4 deletions pkg/nftables/test_data/validated.nftable.v4
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ table ip firewall {
type filter hook forward priority 1; policy drop;

# network traffic accounting for external traffic
ip saddr != @internal_prefixes oifname "vlan42" counter name external_in comment "count external traffic incomming"
ip daddr != @internal_prefixes iifname "vrf42" counter name external_out comment "count external traffic outgoing"
ip saddr != @internal_prefixes oifname {"vlan42", "vrf42"} counter name external_in comment "count external traffic incomming"
ip daddr != @internal_prefixes iifname {"vlan42", "vrf42"} counter name external_out comment "count external traffic outgoing"

# network traffic accounting for internal traffic
ip saddr @internal_prefixes oifname "vlan42" counter name internal_in comment "count internal traffic incomming"
ip daddr @internal_prefixes iifname "vrf42" counter name internal_out comment "count internal traffic outgoing"
ip saddr @internal_prefixes oifname {"vlan42", "vrf42"} counter name internal_in comment "count internal traffic incomming"
ip daddr @internal_prefixes iifname {"vlan42", "vrf42"} counter name internal_out comment "count internal traffic outgoing"

# rate limits

Expand Down

0 comments on commit f70d32b

Please sign in to comment.