From 2af62fd83e27d490952cf09e9cca755312edfd20 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Thu, 21 Nov 2024 15:49:01 +0100 Subject: [PATCH] Allow specifying an initial firewall ruleset. This is handy for the capi-provider. --- api/v2/types_firewall.go | 30 ++++++++++++++++++++ controllers/firewall/reconcile.go | 46 +++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/api/v2/types_firewall.go b/api/v2/types_firewall.go index 34b18e8..f5f82fb 100644 --- a/api/v2/types_firewall.go +++ b/api/v2/types_firewall.go @@ -74,6 +74,9 @@ type FirewallSpec struct { // EgressRules contains egress rules configured for this firewall. EgressRules []EgressRuleSNAT `json:"egressRules,omitempty"` + // InitialRuleSet is the initial firewall ruleset applied before the firewall-controller starts running. + InitialRuleSet *InitialRuleSet `json:"initialRuleSet,omitempty"` + // Interval on which rule reconciliation by the firewall-controller should happen. Interval string `json:"interval,omitempty"` // DryRun if set to true, firewall rules are not applied. For devel-purposes only. @@ -122,6 +125,33 @@ type FirewallTemplateSpec struct { Spec FirewallSpec `json:"spec,omitempty"` } +// InitialRuleSet is the initial rule set deployed on the firewall. +type InitialRuleSet struct { + Egress []EgressRule + Ingress []IngressRule +} + +type NetworkProtocol string + +const ( + NetworkProtocolTCP = "TCP" + NetworkProtocolUDP = "UDP" +) + +type EgressRule struct { + Comment string + Ports []int32 + Protocol NetworkProtocol + To []string +} + +type IngressRule struct { + Comment string + Ports []int32 + Protocol NetworkProtocol + From []string +} + // EgressRuleSNAT holds a Source-NAT rule type EgressRuleSNAT struct { // NetworkID is the network for which the egress rule will be configured. diff --git a/controllers/firewall/reconcile.go b/controllers/firewall/reconcile.go index 89c07bc..0692abc 100644 --- a/controllers/firewall/reconcile.go +++ b/controllers/firewall/reconcile.go @@ -142,18 +142,42 @@ func (c *controller) createFirewall(r *controllers.Ctx[*v2.Firewall]) (*models.V tags = append(tags, v2.FirewallSetTag(ref.Name)) } + var rules *models.V1FirewallRules + if r.Target.Spec.InitialRuleSet != nil { + rules = &models.V1FirewallRules{} + + for _, rule := range r.Target.Spec.InitialRuleSet.Egress { + rules.Egress = append(rules.Egress, &models.V1FirewallEgressRule{ + Comment: rule.Comment, + Ports: rule.Ports, + Protocol: string(rule.Protocol), + To: rule.To, + }) + } + + for _, rule := range r.Target.Spec.InitialRuleSet.Ingress { + rules.Ingress = append(rules.Ingress, &models.V1FirewallIngressRule{ + Comment: rule.Comment, + From: rule.From, + Ports: rule.Ports, + Protocol: string(rule.Protocol), + }) + } + } + createRequest := &models.V1FirewallCreateRequest{ - Description: "created by firewall-controller-manager", - Name: r.Target.Name, - Hostname: r.Target.Name, - Sizeid: &r.Target.Spec.Size, - Projectid: &r.Target.Spec.Project, - Partitionid: &r.Target.Spec.Partition, - Imageid: &r.Target.Spec.Image, - SSHPubKeys: r.Target.Spec.SSHPublicKeys, - Networks: networks, - UserData: r.Target.Spec.Userdata, - Tags: tags, + Description: "created by firewall-controller-manager", + Name: r.Target.Name, + Hostname: r.Target.Name, + Sizeid: &r.Target.Spec.Size, + Projectid: &r.Target.Spec.Project, + Partitionid: &r.Target.Spec.Partition, + Imageid: &r.Target.Spec.Image, + SSHPubKeys: r.Target.Spec.SSHPublicKeys, + Networks: networks, + UserData: r.Target.Spec.Userdata, + Tags: tags, + FirewallRules: rules, } resp, err := c.c.GetMetal().Firewall().AllocateFirewall(firewall.NewAllocateFirewallParams().WithBody(createRequest).WithContext(r.Ctx), nil)