Merge pull request #15 from vegardengen/14-create-object-for-ports-on-firewall-groups
Handle ports in specification and in load balancer services
This commit is contained in:
@@ -36,6 +36,7 @@ type FirewallGroupSpec struct {
|
|||||||
// ManualAddresses is a list of manual IPs or CIDRs (IPv4 or IPv6)
|
// ManualAddresses is a list of manual IPs or CIDRs (IPv4 or IPv6)
|
||||||
// +optional
|
// +optional
|
||||||
ManualAddresses []string `json:"manualAddresses,omitempty"`
|
ManualAddresses []string `json:"manualAddresses,omitempty"`
|
||||||
|
ManualPorts []string `json:"manualPorts,omitempty"`
|
||||||
|
|
||||||
// AutoIncludeSelector defines which services to extract addresses from
|
// AutoIncludeSelector defines which services to extract addresses from
|
||||||
// +optional
|
// +optional
|
||||||
|
|||||||
@@ -99,6 +99,10 @@ spec:
|
|||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
type: array
|
type: array
|
||||||
|
manualPorts:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
matchServicesInAllNamespaces:
|
matchServicesInAllNamespaces:
|
||||||
type: boolean
|
type: boolean
|
||||||
name:
|
name:
|
||||||
|
|||||||
@@ -21,19 +21,21 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||||
// "sigs.k8s.io/controller-runtime/pkg/source"
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
// "sigs.k8s.io/controller-runtime/pkg/source"
|
||||||
|
|
||||||
goUnifi "github.com/vegardengen/go-unifi/unifi"
|
goUnifi "github.com/vegardengen/go-unifi/unifi"
|
||||||
unifiv1beta1 "github.com/vegardengen/unifi-network-operator/api/v1beta1"
|
unifiv1beta1 "github.com/vegardengen/unifi-network-operator/api/v1beta1"
|
||||||
@@ -69,7 +71,7 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
return reconcile.Result{}, client.IgnoreNotFound(err)
|
return reconcile.Result{}, client.IgnoreNotFound(err)
|
||||||
}
|
}
|
||||||
log.Info(nwObj.Spec.Name)
|
log.Info(nwObj.Spec.Name)
|
||||||
var ipv4, ipv6 []string
|
var ipv4, ipv6, tcpports, udpports []string
|
||||||
|
|
||||||
for _, addressEntry := range nwObj.Spec.ManualAddresses {
|
for _, addressEntry := range nwObj.Spec.ManualAddresses {
|
||||||
ip := net.ParseIP(addressEntry)
|
ip := net.ParseIP(addressEntry)
|
||||||
@@ -99,6 +101,26 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, portEntry := range nwObj.Spec.ManualPorts {
|
||||||
|
port_type := "tcp"
|
||||||
|
port := portEntry
|
||||||
|
if match, _ := regexp.MatchString("(?:tcp|udp)\\/?)\\d+", string(portEntry)); match {
|
||||||
|
fields := strings.Split("/",portEntry)
|
||||||
|
port_type = fields[0]
|
||||||
|
port = fields[1]
|
||||||
|
}
|
||||||
|
if(port_type == "tcp") {
|
||||||
|
if !slices.Contains(tcpports, port) {
|
||||||
|
tcpports = append(tcpports, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(port_type == "udp") {
|
||||||
|
if !slices.Contains(udpports, port) {
|
||||||
|
tcpports = append(udpports, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
var services corev1.ServiceList
|
var services corev1.ServiceList
|
||||||
if nwObj.Spec.MatchServicesInAllNamespaces {
|
if nwObj.Spec.MatchServicesInAllNamespaces {
|
||||||
if err := r.List(ctx, &services); err != nil {
|
if err := r.List(ctx, &services); err != nil {
|
||||||
@@ -112,7 +134,7 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
return reconcile.Result{}, err
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, service := range services.Items {
|
for _, service := range services.Items {
|
||||||
if val, found := service.Annotations["unifi.engen.priv.no/firewall-group"]; found && val == nwObj.Name && service.Status.LoadBalancer.Ingress != nil {
|
if val, found := service.Annotations["unifi.engen.priv.no/firewall-group"]; found && val == nwObj.Name && service.Status.LoadBalancer.Ingress != nil {
|
||||||
for _, ingress := range service.Status.LoadBalancer.Ingress {
|
for _, ingress := range service.Status.LoadBalancer.Ingress {
|
||||||
if ingress.IP != "" {
|
if ingress.IP != "" {
|
||||||
@@ -120,7 +142,23 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
if isIPv6(ip) {
|
if isIPv6(ip) {
|
||||||
ipv6 = append(ipv6, ip)
|
ipv6 = append(ipv6, ip)
|
||||||
} else {
|
} else {
|
||||||
ipv4= append(ipv4, ip)
|
ipv4 = append(ipv4, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if service.Spec.Ports != nil {
|
||||||
|
for _, portSpec := range service.Spec.Ports {
|
||||||
|
log.Info(fmt.Sprintf("portSpec: %+v", portSpec))
|
||||||
|
log.Info(fmt.Sprintf("Port: %s %d", strconv.Itoa(int(portSpec.Port)), portSpec.Port))
|
||||||
|
if(portSpec.Protocol == "TCP") {
|
||||||
|
if !slices.Contains(tcpports, strconv.Itoa(int(portSpec.Port))) {
|
||||||
|
tcpports = append(tcpports, strconv.Itoa(int(portSpec.Port)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(portSpec.Protocol == "UDP") {
|
||||||
|
if !slices.Contains(udpports, strconv.Itoa(int(portSpec.Port))) {
|
||||||
|
udpports = append(udpports, strconv.Itoa(int(portSpec.Port)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +167,7 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
nwObj.Status.ResolvedAddresses = ipv4
|
nwObj.Status.ResolvedAddresses = ipv4
|
||||||
nwObj.Status.ResolvedAddresses = append(nwObj.Status.ResolvedAddresses, ipv6...)
|
nwObj.Status.ResolvedAddresses = append(nwObj.Status.ResolvedAddresses, ipv6...)
|
||||||
currentTime := metav1.Now()
|
currentTime := metav1.Now()
|
||||||
nwObj.Status.LastSyncTime = ¤tTime;
|
nwObj.Status.LastSyncTime = ¤tTime
|
||||||
nwObj.Status.SyncedWithUnifi = true
|
nwObj.Status.SyncedWithUnifi = true
|
||||||
|
|
||||||
err := r.UnifiClient.Reauthenticate()
|
err := r.UnifiClient.Reauthenticate()
|
||||||
@@ -143,8 +181,12 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
}
|
}
|
||||||
ipv4_name := "k8s-" + nwObj.Spec.Name + "-ipv4"
|
ipv4_name := "k8s-" + nwObj.Spec.Name + "-ipv4"
|
||||||
ipv6_name := "k8s-" + nwObj.Spec.Name + "-ipv6"
|
ipv6_name := "k8s-" + nwObj.Spec.Name + "-ipv6"
|
||||||
|
tcpports_name := "k8s-" + nwObj.Spec.Name + "-tcpports"
|
||||||
|
udpports_name := "k8s-" + nwObj.Spec.Name + "-udpports"
|
||||||
ipv4_done := false
|
ipv4_done := false
|
||||||
ipv6_done := false
|
ipv6_done := false
|
||||||
|
tcpports_done := false
|
||||||
|
udpports_done := false
|
||||||
for _, firewall_group := range firewall_groups {
|
for _, firewall_group := range firewall_groups {
|
||||||
if firewall_group.Name == ipv4_name {
|
if firewall_group.Name == ipv4_name {
|
||||||
if len(ipv4) == 0 {
|
if len(ipv4) == 0 {
|
||||||
@@ -216,6 +258,76 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
ipv6_done = true
|
ipv6_done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if firewall_group.Name == tcpports_name {
|
||||||
|
if len(tcpports) == 0 {
|
||||||
|
log.Info(fmt.Sprintf("Delete %s", tcpports_name))
|
||||||
|
err := r.UnifiClient.Client.DeleteFirewallGroup(context.Background(), r.UnifiClient.SiteID, firewall_group.ID)
|
||||||
|
if err != nil {
|
||||||
|
msg := strings.ToLower(err.Error())
|
||||||
|
log.Info(msg)
|
||||||
|
if strings.Contains(msg, "api.err.objectreferredby") {
|
||||||
|
log.Info("Firewall group is in use. Invoking workaround...!")
|
||||||
|
firewall_group.GroupMembers = []string{"127.0.0.1"}
|
||||||
|
firewall_group.Name = firewall_group.Name + "-deleted"
|
||||||
|
_, updateerr := r.UnifiClient.Client.UpdateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||||
|
if updateerr != nil {
|
||||||
|
log.Error(updateerr, "Could neither delete or rename firewall group")
|
||||||
|
return reconcile.Result{}, updateerr
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Error(err, "Could not delete firewall group")
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tcpports_done = true
|
||||||
|
} else {
|
||||||
|
if !reflect.DeepEqual(firewall_group.GroupMembers, tcpports) {
|
||||||
|
firewall_group.GroupMembers = tcpports
|
||||||
|
log.Info(fmt.Sprintf("Updating %s", tcpports_name))
|
||||||
|
_, err := r.UnifiClient.Client.UpdateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Could not update firewall group")
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tcpports_done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if firewall_group.Name == udpports_name {
|
||||||
|
if len(udpports) == 0 {
|
||||||
|
log.Info(fmt.Sprintf("Delete %s", udpports_name))
|
||||||
|
err := r.UnifiClient.Client.DeleteFirewallGroup(context.Background(), r.UnifiClient.SiteID, firewall_group.ID)
|
||||||
|
if err != nil {
|
||||||
|
msg := strings.ToLower(err.Error())
|
||||||
|
log.Info(msg)
|
||||||
|
if strings.Contains(msg, "api.err.objectreferredby") {
|
||||||
|
log.Info("Firewall group is in use. Invoking workaround...!")
|
||||||
|
firewall_group.GroupMembers = []string{"127.0.0.1"}
|
||||||
|
firewall_group.Name = firewall_group.Name + "-deleted"
|
||||||
|
_, updateerr := r.UnifiClient.Client.UpdateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||||
|
if updateerr != nil {
|
||||||
|
log.Error(updateerr, "Could neither delete or rename firewall group")
|
||||||
|
return reconcile.Result{}, updateerr
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Error(err, "Could not delete firewall group")
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
udpports_done = true
|
||||||
|
} else {
|
||||||
|
if !reflect.DeepEqual(firewall_group.GroupMembers, udpports) {
|
||||||
|
firewall_group.GroupMembers = udpports
|
||||||
|
log.Info(fmt.Sprintf("Updating %s", udpports_name))
|
||||||
|
_, err := r.UnifiClient.Client.UpdateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Could not update firewall group")
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
udpports_done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
if firewall_group.Name == ipv4_name+"-deleted" && len(ipv4) > 0 {
|
if firewall_group.Name == ipv4_name+"-deleted" && len(ipv4) > 0 {
|
||||||
firewall_group.Name = ipv4_name
|
firewall_group.Name = ipv4_name
|
||||||
firewall_group.GroupMembers = ipv4
|
firewall_group.GroupMembers = ipv4
|
||||||
@@ -238,6 +350,28 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
}
|
}
|
||||||
ipv6_done = true
|
ipv6_done = true
|
||||||
}
|
}
|
||||||
|
if firewall_group.Name == tcpports_name+"-deleted" && len(tcpports) > 0 {
|
||||||
|
firewall_group.Name = tcpports_name
|
||||||
|
firewall_group.GroupMembers = tcpports
|
||||||
|
log.Info(fmt.Sprintf("Creating %s (from previously deleted)", tcpports_name))
|
||||||
|
_, err := r.UnifiClient.Client.UpdateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Could not update firewall group")
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
tcpports_done = true
|
||||||
|
}
|
||||||
|
if firewall_group.Name == udpports_name+"-deleted" && len(udpports) > 0 {
|
||||||
|
firewall_group.Name = udpports_name
|
||||||
|
firewall_group.GroupMembers = udpports
|
||||||
|
log.Info(fmt.Sprintf("Creating %s (from previously deleted)", udpports_name))
|
||||||
|
_, err := r.UnifiClient.Client.UpdateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Could not update firewall group")
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
udpports_done = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(ipv4) > 0 && !ipv4_done {
|
if len(ipv4) > 0 && !ipv4_done {
|
||||||
log.Info(fmt.Sprintf("Creating %s", ipv4_name))
|
log.Info(fmt.Sprintf("Creating %s", ipv4_name))
|
||||||
@@ -265,12 +399,40 @@ func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
return reconcile.Result{}, err
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := r.Status().Update(ctx, &nwObj); err != nil {
|
if len(tcpports) > 0 && !tcpports_done {
|
||||||
|
log.Info(fmt.Sprintf("Creating %s", tcpports_name))
|
||||||
|
var firewall_group goUnifi.FirewallGroup
|
||||||
|
firewall_group.Name = tcpports_name
|
||||||
|
firewall_group.SiteID = r.UnifiClient.SiteID
|
||||||
|
firewall_group.GroupMembers = tcpports
|
||||||
|
firewall_group.GroupType = "port-group"
|
||||||
|
log.Info(fmt.Sprintf("Trying to apply: %+v", firewall_group))
|
||||||
|
_, err := r.UnifiClient.Client.CreateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Could not create firewall group")
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(udpports) > 0 && !udpports_done {
|
||||||
|
log.Info(fmt.Sprintf("Creating %s", udpports_name))
|
||||||
|
var firewall_group goUnifi.FirewallGroup
|
||||||
|
firewall_group.Name = udpports_name
|
||||||
|
firewall_group.SiteID = r.UnifiClient.SiteID
|
||||||
|
firewall_group.GroupMembers = udpports
|
||||||
|
firewall_group.GroupType = "port-group"
|
||||||
|
log.Info(fmt.Sprintf("Trying to apply: %+v", firewall_group))
|
||||||
|
_, err := r.UnifiClient.Client.CreateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Could not create firewall group")
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := r.Status().Update(ctx, &nwObj); err != nil {
|
||||||
log.Error(err, "unable to update FirewallGroup status")
|
log.Error(err, "unable to update FirewallGroup status")
|
||||||
return reconcile.Result{}, err
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Successfully updated FirewallGroup status with collected IP addresses")
|
log.Info("Successfully updated FirewallGroup status with collected IP addresses and ports")
|
||||||
|
|
||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
}
|
}
|
||||||
@@ -278,43 +440,44 @@ func isIPv6(ip string) bool {
|
|||||||
return strings.Contains(ip, ":")
|
return strings.Contains(ip, ":")
|
||||||
}
|
}
|
||||||
func (r *FirewallGroupReconciler) mapServiceToFirewallGroups(ctx context.Context, obj client.Object) []reconcile.Request {
|
func (r *FirewallGroupReconciler) mapServiceToFirewallGroups(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||||
var requests []reconcile.Request
|
var requests []reconcile.Request
|
||||||
service, ok := obj.(*corev1.Service)
|
service, ok := obj.(*corev1.Service)
|
||||||
if !ok {
|
if !ok {
|
||||||
return requests
|
return requests
|
||||||
}
|
}
|
||||||
|
|
||||||
var allFirewallGroups unifiv1beta1.FirewallGroupList
|
var allFirewallGroups unifiv1beta1.FirewallGroupList
|
||||||
|
|
||||||
if err := r.List(ctx, &allFirewallGroups); err != nil {
|
if err := r.List(ctx, &allFirewallGroups); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fwg := range allFirewallGroups.Items {
|
for _, fwg := range allFirewallGroups.Items {
|
||||||
if fwg.Spec.MatchServicesInAllNamespaces || fwg.Namespace == service.Namespace {
|
if fwg.Spec.MatchServicesInAllNamespaces || fwg.Namespace == service.Namespace {
|
||||||
annotationKey := "unifi.engen.priv.no/firewall-group"
|
annotationKey := "unifi.engen.priv.no/firewall-group"
|
||||||
annotationVal := fwg.Name
|
annotationVal := fwg.Name
|
||||||
if val, ok := service.Annotations[annotationKey]; ok && (annotationVal == "" || val == annotationVal) {
|
if val, ok := service.Annotations[annotationKey]; ok && (annotationVal == "" || val == annotationVal) {
|
||||||
requests = append(requests, ctrl.Request{
|
requests = append(requests, ctrl.Request{
|
||||||
NamespacedName: types.NamespacedName{
|
NamespacedName: types.NamespacedName{
|
||||||
Name: fwg.Name,
|
Name: fwg.Name,
|
||||||
Namespace: fwg.Namespace,
|
Namespace: fwg.Namespace,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return requests
|
return requests
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupWithManager sets up the controller with the Manager.
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
func (r *FirewallGroupReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
func (r *FirewallGroupReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
return ctrl.NewControllerManagedBy(mgr).
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
For(&unifiv1beta1.FirewallGroup{}).
|
For(&unifiv1beta1.FirewallGroup{}).
|
||||||
Named("firewallgroup").
|
Named("firewallgroup").
|
||||||
Watches(
|
Watches(
|
||||||
&corev1.Service{},
|
&corev1.Service{},
|
||||||
handler.EnqueueRequestsFromMapFunc(r.mapServiceToFirewallGroups),
|
handler.EnqueueRequestsFromMapFunc(r.mapServiceToFirewallGroups),
|
||||||
).
|
).
|
||||||
Complete(r)
|
Complete(r)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user