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

First draft of forbidden mode for isolated clusters #172

Merged
merged 38 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fbcfbdb
First draft of forbidden mode
majst01 Dec 18, 2023
551079a
feat(cwnp): delete forbidden cwnps instead of apply
vknabel Dec 18, 2023
db2b425
style: golangci-lint
vknabel Dec 19, 2023
fd662cd
refactor(cwnp): extract cwnp validation and deletion
vknabel Dec 19, 2023
7babed6
Merge branch 'master' into isolated-clusters
majst01 Jan 13, 2024
15171b6
feat: bump fwc-manager
vknabel Jan 15, 2024
6035e72
new structure for allowed networks
ulrichSchreiner Jan 15, 2024
55ee37d
set missing recorder
ulrichSchreiner Jan 15, 2024
d79151e
Hint
majst01 Jan 15, 2024
4be5a59
filter IP of loadbalancer when networkisolation is forbidden
ulrichSchreiner Jan 16, 2024
27e2297
fix weird event logging message
ulrichSchreiner Jan 18, 2024
0d8abb2
Only reduce cwnps for accessType forbidden
majst01 Jan 19, 2024
c182db1
Merge branch 'isolated-clusters' of https://github.com/metal-stack/fi…
majst01 Jan 19, 2024
c49c38c
Only reduce cwnps for accessType forbidden
majst01 Jan 19, 2024
dc523cd
Update actions
majst01 Jan 19, 2024
50cad61
Update metal-networker to get block forward
majst01 Jan 22, 2024
56be4e2
Back to released metal-networker
majst01 Jan 22, 2024
6f129de
Change metal table default drop in forward chain to accept after start
majst01 Jan 22, 2024
f2bdf2e
Fix drop/accept forwarding
majst01 Jan 22, 2024
bc963e9
Pin metal-networker
majst01 Jan 23, 2024
28ead28
Merge master
majst01 Jan 23, 2024
ed04054
Updates
majst01 Jan 23, 2024
91d952a
Remove ugly code
majst01 Jan 23, 2024
18bbe78
Prepare cwnp state
majst01 Jan 23, 2024
035975e
resolve review comments
ulrichSchreiner Jan 24, 2024
78ba051
add printer columns for state and message for cwnp
ulrichSchreiner Jan 24, 2024
12d70b1
update real status not a copy of it
ulrichSchreiner Jan 24, 2024
cb21dd1
always update; caller checks if something changed
ulrichSchreiner Jan 24, 2024
c959da9
remove ugly code
ulrichSchreiner Jan 24, 2024
ac01058
make state not required
ulrichSchreiner Jan 24, 2024
3bc65fc
check for old value and only update if new values differ
ulrichSchreiner Jan 24, 2024
6e474ee
Update pkg/nftables/rendering.go
majst01 Jan 29, 2024
564e30e
Add api docs
majst01 Jan 29, 2024
5b42cfb
Update config/crd/bases/metal-stack.io_clusterwidenetworkpolicies.yaml
majst01 Jan 29, 2024
388e808
Update controllers/clusterwidenetworkpolicy_controller.go
majst01 Jan 29, 2024
02b097c
typo
mwennrich Jan 29, 2024
252107a
Pin.
Gerrit91 Jan 30, 2024
368a0cd
Don't forget to save.
Gerrit91 Jan 30, 2024
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
14 changes: 8 additions & 6 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ jobs:

steps:
- name: Log in to the container registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKER_REGISTRY_USER }}
password: ${{ secrets.DOCKER_REGISTRY_TOKEN }}

- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- uses: google-github-actions/auth@v1
with:
Expand All @@ -40,9 +40,10 @@ jobs:
uses: google-github-actions/setup-gcloud@v0

- name: Set up Go 1.21
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: false

- name: Lint
uses: golangci/golangci-lint-action@v3
Expand All @@ -64,7 +65,7 @@ jobs:
make

- name: Push Docker image
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
context: .
push: true
Expand All @@ -89,12 +90,13 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Go 1.21
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: false

- name: Run tests
run: |
Expand Down
98 changes: 96 additions & 2 deletions controllers/clusterwidenetworkpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@ package controllers

import (
"context"
"errors"
"fmt"
"time"

"go4.org/netipx"

"github.com/metal-stack/firewall-controller/v2/pkg/dns"
"github.com/metal-stack/firewall-controller/v2/pkg/helper"
"github.com/metal-stack/firewall-controller/v2/pkg/nftables"

"github.com/go-logr/logr"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/record"

ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -33,7 +38,8 @@ type ClusterwideNetworkPolicyReconciler struct {
FirewallName string
SeedNamespace string

Log logr.Logger
Log logr.Logger
Recorder record.EventRecorder

Interval time.Duration
DnsProxy *dns.DNSProxy
Expand Down Expand Up @@ -91,7 +97,14 @@ func (r *ClusterwideNetworkPolicyReconciler) Reconcile(ctx context.Context, _ ct
if err := r.ShootClient.List(ctx, &services); err != nil {
return ctrl.Result{}, err
}
nftablesFirewall := nftables.NewFirewall(f, &cwnps, &services, r.DnsProxy, r.Log)

validCwnps, err := r.allowedCWNPsOrDelete(ctx, cwnps.Items, f.Spec.NetworkAccessType, f.Spec.AllowedNetworks)
if err != nil {
return ctrl.Result{}, err
}
cwnps.Items = validCwnps

nftablesFirewall := nftables.NewFirewall(f, &cwnps, &services, r.DnsProxy, r.Log, r.Recorder)
if err := r.manageDNSProxy(ctx, f, cwnps, nftablesFirewall); err != nil {
return ctrl.Result{}, err
}
Expand Down Expand Up @@ -181,3 +194,84 @@ func (r *ClusterwideNetworkPolicyReconciler) getReconciliationTicker(scheduleCha
}
}
}

func (r *ClusterwideNetworkPolicyReconciler) allowedCWNPsOrDelete(ctx context.Context, cwnps []firewallv1.ClusterwideNetworkPolicy, accessType firewallv2.NetworkAccessType, allowedNetworks firewallv2.AllowedNetworks) ([]firewallv1.ClusterwideNetworkPolicy, error) {
// FIXME refactor to func and add test, remove illegal rules from further processing
// report as event in case rule is not allowed
majst01 marked this conversation as resolved.
Show resolved Hide resolved

// what to do, if accesstype is baseline but there are allowedNetworks given?
majst01 marked this conversation as resolved.
Show resolved Hide resolved
if accessType != firewallv2.NetworkAccessForbidden {
return cwnps, nil
}

validCWNPs := make([]firewallv1.ClusterwideNetworkPolicy, 0, len(cwnps))
forbiddenCWNPs := make([]firewallv1.ClusterwideNetworkPolicy, 0)

egressSet, err := helper.BuildNetworksIPSet(allowedNetworks.Egress)
if err != nil {
return nil, err
}

ingressSet, err := helper.BuildNetworksIPSet(allowedNetworks.Ingress)
if err != nil {
return nil, err
}

for _, cwnp := range cwnps {
cwnp := cwnp
oke, err := r.validateCWNPEgressTargetPrefix(cwnp, egressSet)
if err != nil {
return nil, err
}
oki, err := r.validateCWNPIngressTargetPrefix(cwnp, ingressSet)
if err != nil {
return nil, err
}
// the CWNP is ok if both ingress/egress match with the allowed networks
ok := oki && oke
majst01 marked this conversation as resolved.
Show resolved Hide resolved

if !ok {
forbiddenCWNPs = append(forbiddenCWNPs, cwnp)
} else {
validCWNPs = append(validCWNPs, cwnp)
}
}
if len(cwnps) != len(validCWNPs) {
var errs []error
for _, cwnp := range forbiddenCWNPs {
cwnp := cwnp
err := r.ShootClient.Delete(ctx, &cwnp)
majst01 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
// TODO: should we acutally fail when single cwnps that won't be applied anyways cannot be deleted?
// Alternatively just log / record event / retry reconcile?
majst01 marked this conversation as resolved.
Show resolved Hide resolved
return nil, fmt.Errorf("failed to delete all forbidden CWNPs: %w", errors.Join(errs...))
}
}
return validCWNPs, nil
}

func (r *ClusterwideNetworkPolicyReconciler) validateCWNPEgressTargetPrefix(cwnp firewallv1.ClusterwideNetworkPolicy, externalSet *netipx.IPSet) (bool, error) {
for _, egress := range cwnp.Spec.Egress {
for _, to := range egress.To {
if ok, err := helper.ValidateCIDR(&cwnp, to.CIDR, externalSet, r.Recorder); !ok {
return false, err
}
}
}
return true, nil
}
majst01 marked this conversation as resolved.
Show resolved Hide resolved

func (r *ClusterwideNetworkPolicyReconciler) validateCWNPIngressTargetPrefix(cwnp firewallv1.ClusterwideNetworkPolicy, externalSet *netipx.IPSet) (bool, error) {
for _, ingress := range cwnp.Spec.Ingress {
for _, from := range ingress.From {
if ok, err := helper.ValidateCIDR(&cwnp, from.CIDR, externalSet, r.Recorder); !ok {
majst01 marked this conversation as resolved.
Show resolved Hide resolved
return false, err
}
}
}
return true, nil
}
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ require (
github.com/google/go-cmp v0.6.0
github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c
github.com/ks2211/go-suricata v0.0.0-20200823200910-986ce1470707
github.com/metal-stack/firewall-controller-manager v0.3.0
github.com/metal-stack/firewall-controller-manager v0.3.2-0.20240115082359-d2ad341a4113
majst01 marked this conversation as resolved.
Show resolved Hide resolved
github.com/metal-stack/metal-go v0.26.2
github.com/metal-stack/metal-lib v0.14.3
github.com/metal-stack/metal-networker v0.41.0
github.com/metal-stack/v v1.0.3
github.com/miekg/dns v1.1.56
github.com/txn2/txeh v1.5.3
github.com/miekg/dns v1.1.58
github.com/txn2/txeh v1.5.5
github.com/vishvananda/netlink v1.2.1-beta.2
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
k8s.io/api v0.26.3
k8s.io/apiextensions-apiserver v0.26.3
k8s.io/apimachinery v0.28.2
Expand Down Expand Up @@ -83,7 +84,7 @@ require (
golang.org/x/term v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.16.0 // indirect
golang.org/x/tools v0.17.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.32.0 // indirect
Expand Down
18 changes: 10 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/metal-stack/firewall-controller-manager v0.3.0 h1:sBCL7iiG17ZO/1TREv2RYiMdX5VddSc92snR8OKcAF8=
github.com/metal-stack/firewall-controller-manager v0.3.0/go.mod h1:KjLZv/BatucZM9DQtBLN04wBGjvxEJRV1C+xDkCWwIE=
github.com/metal-stack/firewall-controller-manager v0.3.2-0.20240115082359-d2ad341a4113 h1:QvFcs5sBI6BGLMidLKFfXDBgBG7idivvck8u8q5PD9Y=
github.com/metal-stack/firewall-controller-manager v0.3.2-0.20240115082359-d2ad341a4113/go.mod h1:KjLZv/BatucZM9DQtBLN04wBGjvxEJRV1C+xDkCWwIE=
github.com/metal-stack/metal-go v0.26.2 h1:KZRV1wtCsj0dMo4GpW2+XemmAkPZAYFjbGe7QhhcH1k=
github.com/metal-stack/metal-go v0.26.2/go.mod h1:olJ3Az7RBh39Q5WFCJOQBd7cJi0xgGYwMTEIFvkDQQY=
github.com/metal-stack/metal-hammer v0.12.0 h1:t6t73RGmDU1IFkHC7dJxu7xDIZZvwmqmu9/0xZVF/L0=
Expand All @@ -184,8 +184,8 @@ github.com/metal-stack/metal-networker v0.41.0 h1:eefp8nzhF6eBDoGjFR8m+chkGUO5QF
github.com/metal-stack/metal-networker v0.41.0/go.mod h1:jdHKFIbPBNHnvies0Tb8DlnPfbKV/HuikxPZH3kC6uA=
github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs=
github.com/metal-stack/v v1.0.3/go.mod h1:YTahEu7/ishwpYKnp/VaW/7nf8+PInogkfGwLcGPdXg=
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
Expand Down Expand Up @@ -259,8 +259,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/txn2/txeh v1.5.3 h1:ZMgc3r+5/AFtE/ayCoICpvxj7xl/CYsZjnIGhozV/Kc=
github.com/txn2/txeh v1.5.3/go.mod h1:qYzGG9kCzeVEI12geK4IlanHWY8X4uy/I3NcW7mk8g4=
github.com/txn2/txeh v1.5.5 h1:UN4e/lCK5HGw/gGAi2GCVrNKg0GTCUWs7gs5riaZlz4=
github.com/txn2/txeh v1.5.5/go.mod h1:qYzGG9kCzeVEI12geK4IlanHWY8X4uy/I3NcW7mk8g4=
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
Expand All @@ -286,6 +286,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand Down Expand Up @@ -382,8 +384,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ func main() {
SeedClient: seedMgr.GetClient(),
ShootClient: shootMgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("ClusterwideNetworkPolicy"),
Recorder: shootMgr.GetEventRecorderFor("FirewallController"),
FirewallName: firewallName,
SeedNamespace: seedNamespace,
}).SetupWithManager(shootMgr); err != nil {
Expand Down
70 changes: 70 additions & 0 deletions pkg/helper/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package helper

import (
"fmt"
"net/netip"

"go4.org/netipx"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/record"
)

const (
forbiddenCIDR = "ForbiddenCIDR"
)

// Create an IPSet from a given list of strings describing networks.
func BuildNetworksIPSet(networks []string) (*netipx.IPSet, error) {
var externalBuilder netipx.IPSetBuilder

for _, externalNetwork := range networks {
parsedExternal, err := netip.ParsePrefix(externalNetwork)
if err != nil {
return nil, fmt.Errorf("failed to parse prefix: %w", err)
}
externalBuilder.AddPrefix(parsedExternal)
}
externalSet, err := externalBuilder.IPSet()
if err != nil {
return nil, fmt.Errorf("failed to create ipset: %w", err)
}
return externalSet, nil
}

func NetworkSetAsString(externalSet *netipx.IPSet) string {
var allowedNetworksStr string
if externalSet != nil {
for i, r := range externalSet.Ranges() {
if i > 0 {
allowedNetworksStr += ","
}
if p, ok := r.Prefix(); ok {
allowedNetworksStr += p.String()
} else {
allowedNetworksStr += r.String()
}
}
}
return allowedNetworksStr
}

func ValidateCIDR(o runtime.Object, cidr string, ipset *netipx.IPSet, rec record.EventRecorder) (bool, error) {
parsedTo, err := netip.ParsePrefix(cidr)
if err != nil {
return false, fmt.Errorf("failed to parse to address: %w", err)
}
if !ipset.ContainsPrefix(parsedTo) {
allowedNetworksStr := NetworkSetAsString(ipset)
if rec != nil {
rec.Eventf(
o,
corev1.EventTypeWarning,
forbiddenCIDR,
"address:%q is outside of the allowed network range:%q, ignoring",
parsedTo.String(), allowedNetworksStr)
}
return false, nil
}
return true, nil
}
Loading
Loading