diff --git a/api/v2/types_firewall.go b/api/v2/types_firewall.go index 34b18e8..373107e 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,46 @@ type FirewallTemplateSpec struct { Spec FirewallSpec `json:"spec,omitempty"` } +// InitialRuleSet is the initial rule set deployed on the firewall. +type InitialRuleSet struct { + // Egress rules to be deployed initially on the firewall. + Egress []EgressRule `json:"egress,omitempty"` + // Ingress rules to be deployed initially on the firewall. + Ingress []IngressRule `json:"ingress,omitempty"` +} + +// NetworkProtocol represents the kind of network protocol. +type NetworkProtocol string + +const ( + // NetworkProtocolTCP represents tcp connections. + NetworkProtocolTCP = "TCP" + // NetworkProtocolUDP represents udp connections. + NetworkProtocolUDP = "UDP" +) + +type EgressRule struct { + // Comment provides a human readable description of this rule. + Comment string `json:"comment,omitempty"` + // Ports contains all affected network ports. + Ports []int32 `json:"ports"` + // Protocol constraints the protocol this rule applies to. + Protocol NetworkProtocol `json:"protocol"` + // To source address cidrs this rule applies to. + To []string `json:"to"` +} + +type IngressRule struct { + // Comment provides a human readable description of this rule. + Comment string `json:"comment,omitempty"` + // Ports contains all affected network ports. + Ports []int32 `json:"ports"` + // Protocol constraints the protocol this rule applies to. + Protocol NetworkProtocol `json:"protocol"` + // From source address cidrs this rule applies to. + From []string `json:"from"` +} + // EgressRuleSNAT holds a Source-NAT rule type EgressRuleSNAT struct { // NetworkID is the network for which the egress rule will be configured. diff --git a/api/v2/zz_generated.deepcopy.go b/api/v2/zz_generated.deepcopy.go index b9d9399..802ad3b 100644 --- a/api/v2/zz_generated.deepcopy.go +++ b/api/v2/zz_generated.deepcopy.go @@ -161,6 +161,31 @@ func (in DeviceStatsByDevice) DeepCopy() DeviceStatsByDevice { return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EgressRule) DeepCopyInto(out *EgressRule) { + *out = *in + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]int32, len(*in)) + copy(*out, *in) + } + if in.To != nil { + in, out := &in.To, &out.To + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressRule. +func (in *EgressRule) DeepCopy() *EgressRule { + if in == nil { + return nil + } + out := new(EgressRule) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EgressRuleSNAT) DeepCopyInto(out *EgressRuleSNAT) { *out = *in @@ -633,6 +658,11 @@ func (in *FirewallSpec) DeepCopyInto(out *FirewallSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.InitialRuleSet != nil { + in, out := &in.InitialRuleSet, &out.InitialRuleSet + *out = new(InitialRuleSet) + (*in).DeepCopyInto(*out) + } if in.DNSPort != nil { in, out := &in.DNSPort, &out.DNSPort *out = new(uint) @@ -780,6 +810,60 @@ func (in IDSStatsByDevice) DeepCopy() IDSStatsByDevice { return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressRule) DeepCopyInto(out *IngressRule) { + *out = *in + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]int32, len(*in)) + copy(*out, *in) + } + if in.From != nil { + in, out := &in.From, &out.From + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRule. +func (in *IngressRule) DeepCopy() *IngressRule { + if in == nil { + return nil + } + out := new(IngressRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InitialRuleSet) DeepCopyInto(out *InitialRuleSet) { + *out = *in + if in.Egress != nil { + in, out := &in.Egress, &out.Egress + *out = make([]EgressRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Ingress != nil { + in, out := &in.Ingress, &out.Ingress + *out = make([]IngressRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InitialRuleSet. +func (in *InitialRuleSet) DeepCopy() *InitialRuleSet { + if in == nil { + return nil + } + out := new(InitialRuleSet) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InterfaceStat) DeepCopyInto(out *InterfaceStat) { *out = *in diff --git a/config/crds/firewall.metal-stack.io_firewalldeployments.yaml b/config/crds/firewall.metal-stack.io_firewalldeployments.yaml index 3100ea4..70fa79b 100644 --- a/config/crds/firewall.metal-stack.io_firewalldeployments.yaml +++ b/config/crds/firewall.metal-stack.io_firewalldeployments.yaml @@ -180,6 +180,75 @@ spec: Image is the os image of the firewall. An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string + initialRuleSet: + description: InitialRuleSet is the initial firewall ruleset + applied before the firewall-controller starts running. + properties: + egress: + description: Egress rules to be deployed initially on + the firewall. + items: + properties: + comment: + description: Comment provides a human readable description + of this rule. + type: string + ports: + description: Ports contains all affected network + ports. + items: + format: int32 + type: integer + type: array + protocol: + description: Protocol constraints the protocol this + rule applies to. + type: string + to: + description: To target addresses this rule applies + to. May contain IPs or dns names. + items: + type: string + type: array + required: + - ports + - protocol + - to + type: object + type: array + ingress: + description: Ingress rules to be deployed initially on + the firewall. + items: + properties: + comment: + description: Comment provides a human readable description + of this rule. + type: string + from: + description: From source addresses this rule applies + to. May contain IPs or dns names. + items: + type: string + type: array + ports: + description: Ports contains all affected network + ports. + items: + format: int32 + type: integer + type: array + protocol: + description: Protocol constraints the protocol this + rule applies to. + type: string + required: + - from + - ports + - protocol + type: object + type: array + type: object internalPrefixes: description: |- InternalPrefixes specify prefixes which are considered local to the partition or all regions. This is used for the traffic counters. diff --git a/config/crds/firewall.metal-stack.io_firewalls.yaml b/config/crds/firewall.metal-stack.io_firewalls.yaml index c82118b..0d32283 100644 --- a/config/crds/firewall.metal-stack.io_firewalls.yaml +++ b/config/crds/firewall.metal-stack.io_firewalls.yaml @@ -135,6 +135,71 @@ spec: Image is the os image of the firewall. An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string + initialRuleSet: + description: InitialRuleSet is the initial firewall ruleset applied + before the firewall-controller starts running. + properties: + egress: + description: Egress rules to be deployed initially on the firewall. + items: + properties: + comment: + description: Comment provides a human readable description + of this rule. + type: string + ports: + description: Ports contains all affected network ports. + items: + format: int32 + type: integer + type: array + protocol: + description: Protocol constraints the protocol this rule + applies to. + type: string + to: + description: To target addresses this rule applies to. May + contain IPs or dns names. + items: + type: string + type: array + required: + - ports + - protocol + - to + type: object + type: array + ingress: + description: Ingress rules to be deployed initially on the firewall. + items: + properties: + comment: + description: Comment provides a human readable description + of this rule. + type: string + from: + description: From source addresses this rule applies to. + May contain IPs or dns names. + items: + type: string + type: array + ports: + description: Ports contains all affected network ports. + items: + format: int32 + type: integer + type: array + protocol: + description: Protocol constraints the protocol this rule + applies to. + type: string + required: + - from + - ports + - protocol + type: object + type: array + type: object internalPrefixes: description: |- InternalPrefixes specify prefixes which are considered local to the partition or all regions. This is used for the traffic counters. diff --git a/config/crds/firewall.metal-stack.io_firewallsets.yaml b/config/crds/firewall.metal-stack.io_firewallsets.yaml index ae2878a..bbc7c44 100644 --- a/config/crds/firewall.metal-stack.io_firewallsets.yaml +++ b/config/crds/firewall.metal-stack.io_firewallsets.yaml @@ -172,6 +172,75 @@ spec: Image is the os image of the firewall. An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string + initialRuleSet: + description: InitialRuleSet is the initial firewall ruleset + applied before the firewall-controller starts running. + properties: + egress: + description: Egress rules to be deployed initially on + the firewall. + items: + properties: + comment: + description: Comment provides a human readable description + of this rule. + type: string + ports: + description: Ports contains all affected network + ports. + items: + format: int32 + type: integer + type: array + protocol: + description: Protocol constraints the protocol this + rule applies to. + type: string + to: + description: To target addresses this rule applies + to. May contain IPs or dns names. + items: + type: string + type: array + required: + - ports + - protocol + - to + type: object + type: array + ingress: + description: Ingress rules to be deployed initially on + the firewall. + items: + properties: + comment: + description: Comment provides a human readable description + of this rule. + type: string + from: + description: From source addresses this rule applies + to. May contain IPs or dns names. + items: + type: string + type: array + ports: + description: Ports contains all affected network + ports. + items: + format: int32 + type: integer + type: array + protocol: + description: Protocol constraints the protocol this + rule applies to. + type: string + required: + - from + - ports + - protocol + type: object + type: array + type: object internalPrefixes: description: |- InternalPrefixes specify prefixes which are considered local to the partition or all regions. This is used for the traffic counters. diff --git a/controllers/firewall/reconcile.go b/controllers/firewall/reconcile.go index 89c07bc..d3ba472 100644 --- a/controllers/firewall/reconcile.go +++ b/controllers/firewall/reconcile.go @@ -46,7 +46,7 @@ func (c *controller) Reconcile(r *controllers.Ctx[*v2.Firewall]) error { return err } - // requeueing in order to continue checking progression + // requeuing in order to continue checking progression return controllers.RequeueAfter(10*time.Second, "firewall creation is progressing") case 1: f = fws[0] @@ -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)