diff --git a/api/v1/firewall_types.go b/api/v1/firewall_types.go index 2395dc4a..e062b29c 100644 --- a/api/v1/firewall_types.go +++ b/api/v1/firewall_types.go @@ -81,6 +81,8 @@ type Data struct { EgressRules []EgressRuleSNAT `json:"egressRules,omitempty"` // FirewallNetworks holds the networks known at the metal-api for this firewall machine FirewallNetworks []FirewallNetwork `json:"firewallNetworks,omitempty"` + // EnableIDS specifies if we need to enable IDS on the firewall machine + EnableIDS bool `json:"enableIDS,omitempty"` } // FirewallStatus defines the observed state of Firewall diff --git a/config/crd/bases/metal-stack.io_firewalls.yaml b/config/crd/bases/metal-stack.io_firewalls.yaml index b8844dd5..97356b41 100644 --- a/config/crd/bases/metal-stack.io_firewalls.yaml +++ b/config/crd/bases/metal-stack.io_firewalls.yaml @@ -72,6 +72,10 @@ spec: - networkid type: object type: array + enableIDS: + description: EnableIDS specifies if we need to enable IDS on the firewall + machine + type: boolean firewallNetworks: description: FirewallNetworks holds the networks known at the metal-api for this firewall machine diff --git a/controllers/firewall_controller.go b/controllers/firewall_controller.go index 7ed22424..53ad5943 100644 --- a/controllers/firewall_controller.go +++ b/controllers/firewall_controller.go @@ -59,7 +59,7 @@ type FirewallReconciler struct { recorder record.EventRecorder Log logr.Logger Scheme *runtime.Scheme - EnableIDS bool + Suricata *suricata.Suricata EnableSignatureCheck bool CAPubKey *rsa.PublicKey policySpecsChecksums map[string][16]byte @@ -145,17 +145,22 @@ func (r *FirewallReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { } log.Info("reconciling network settings") - changed, err = network.ReconcileNetwork(f, log) + kb := network.GetUpdatedKnowledgeBase(f) + changed, err = network.ReconcileNetwork(kb) if changed && err == nil { r.recorder.Event(&f, corev1.EventTypeNormal, "Network settings", "reconcilation succeeded (frr.conf)") } else if changed && err != nil { r.recorder.Event(&f, corev1.EventTypeWarning, "Network settings", fmt.Sprintf("reconcilation failed (frr.conf): %v", err)) } - if err != nil { errors = multierror.Append(errors, err) } + log.Info("reconciling suricata config") + if err := r.Suricata.ReconcileSuricata(kb, f.Spec.EnableIDS); err != nil { + errors = multierror.Append(errors, err) + } + log.Info("reconciling firewall services") if err = r.reconcileFirewallServices(ctx, f); err != nil { errors = multierror.Append(errors, err) @@ -438,9 +443,8 @@ func (r *FirewallReconciler) updateStatus(ctx context.Context, f firewallv1.Fire f.Status.FirewallStats.DeviceStats = deviceStats idsStats := firewallv1.IDSStatsByDevice{} - if r.EnableIDS { // checks the CLI-flag - s := suricata.New() - ss, err := s.InterfaceStats() + if f.Spec.EnableIDS { + ss, err := r.Suricata.InterfaceStats() if err != nil { return err } diff --git a/go.mod b/go.mod index 5fb828a8..fd842965 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/mdlayher/netlink v1.1.1 // indirect github.com/metal-stack/metal-go v0.14.0 github.com/metal-stack/metal-lib v0.7.2 - github.com/metal-stack/metal-networker v0.7.2 + github.com/metal-stack/metal-networker v0.7.1-0.20210708123945-d8907ab9938a github.com/metal-stack/v v1.0.3 github.com/txn2/txeh v1.3.0 github.com/vishvananda/netlink v1.1.0 diff --git a/go.sum b/go.sum index 91e3cf1f..ea80963d 100644 --- a/go.sum +++ b/go.sum @@ -151,7 +151,7 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/dvyukov/go-fuzz v0.0.0-20201127111758-49e582c6c23d/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -543,8 +543,8 @@ github.com/metal-stack/metal-go v0.14.0/go.mod h1:fk411K2KQ6sitmlG21YfbRfBzNaJGL github.com/metal-stack/metal-lib v0.6.9/go.mod h1:r8qhfX72eAzClR/pEaQvdwM//Otx9gegYoOphLPmmQ4= github.com/metal-stack/metal-lib v0.7.2 h1:vXuQnpoXJV4otCwLyB74MiiokaidAoAcNnivkG9/UTI= github.com/metal-stack/metal-lib v0.7.2/go.mod h1:eDBJ88yC8jUk+bAJXpF1Upw6j3lbbgv3UIF0D+llMec= -github.com/metal-stack/metal-networker v0.7.2 h1:jc2BXhCWXNaJtP4XvyW9rXeR5QUJ/CDTRIN9gaN5Gfo= -github.com/metal-stack/metal-networker v0.7.2/go.mod h1:eJkMkv0RmicEHFTk+QOPEHaaXnJCOdbYAe3gS4NTMNg= +github.com/metal-stack/metal-networker v0.7.1-0.20210708123945-d8907ab9938a h1:vfnMxRFa+9gcdgCfUKIhdu5WfuXiwJ4oZi2XQRrV5rE= +github.com/metal-stack/metal-networker v0.7.1-0.20210708123945-d8907ab9938a/go.mod h1:IjlXMdBetE2i81VogBSSQKJFjwrskV6+6drPN/VPJqY= github.com/metal-stack/security v0.4.0/go.mod h1:C7kSrHwRcG+47375RJjhakN1LenbEJF9uQd4I50nZlY= github.com/metal-stack/security v0.5.1/go.mod h1:t7P93F6/iSDR729OS/3x5t69ewBCsHUYqRVaHb5nxjc= github.com/metal-stack/security v0.5.3/go.mod h1:t7P93F6/iSDR729OS/3x5t69ewBCsHUYqRVaHb5nxjc= @@ -983,7 +983,6 @@ golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1062,9 +1061,8 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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= @@ -1214,8 +1212,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k= honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -inet.af/netaddr v0.0.0-20210707202901-70468d781e6c h1:ZNUX2CiFwNbN1VFaD4MQFmC8o5Rxc7BQW1P1K8kMpbE= -inet.af/netaddr v0.0.0-20210707202901-70468d781e6c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls= +inet.af/netaddr v0.0.0-20210403172118-1e1430f727e0 h1:ANl7piXB3SHmhwTNeTO0yl0yf4gO3/aaFjcBCdH9Ftg= +inet.af/netaddr v0.0.0-20210403172118-1e1430f727e0/go.mod h1:I2i9ONCXRZDnG1+7O8fSuYzjcPxHQXrIfzD/IkR87x4= k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI= k8s.io/api v0.18.9 h1:7VDtivqwbvLOf8hmXSd/PDSSbpCBq49MELg84EYBYiQ= k8s.io/api v0.18.9/go.mod h1:9u/h6sUh6FxfErv7QqetX1EB3yBMIYOBXzdcf0Gf0rc= diff --git a/main.go b/main.go index 078282ff..3bda57a9 100644 --- a/main.go +++ b/main.go @@ -24,10 +24,13 @@ import ( "os" "time" - "github.com/metal-stack/firewall-controller/controllers" - "github.com/metal-stack/firewall-controller/controllers/crd" "github.com/metal-stack/metal-lib/pkg/sign" "github.com/metal-stack/v" + + "github.com/metal-stack/firewall-controller/controllers" + "github.com/metal-stack/firewall-controller/controllers/crd" + "github.com/metal-stack/firewall-controller/pkg/suricata" + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -60,18 +63,23 @@ func main() { var ( metricsAddr string enableLeaderElection bool - enableIDS bool enableSignatureCheck bool hostsFile string ) - flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") - flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, + + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") + fs.BoolVar(&enableLeaderElection, "enable-leader-election", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") - flag.BoolVar(&enableIDS, "enable-IDS", true, "Set this to false to exclude IDS.") - flag.StringVar(&hostsFile, "hosts-file", "/etc/hosts", "The hosts file to manipulate for the droptailer.") - flag.BoolVar(&enableSignatureCheck, "enable-signature-check", true, "Set this to false to ignore signature checking.") - flag.Parse() + fs.StringVar(&hostsFile, + "hosts-file", "/etc/hosts", "The hosts file to manipulate for the droptailer.") + fs.BoolVar(&enableSignatureCheck, + "enable-signature-check", true, "Set this to false to ignore signature checking.") + if err := fs.Parse(os.Args[1:]); err != nil { + // Log error but continue program execution + setupLog.Error(err, "error parsing flags") + } ctrl.SetLogger(zap.New(zap.UseDevMode(true))) @@ -158,7 +166,7 @@ func main() { Client: mgr.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("Firewall"), Scheme: mgr.GetScheme(), - EnableIDS: enableIDS, + Suricata: suricata.New(), EnableSignatureCheck: enableSignatureCheck, CAPubKey: caPubKey, }).SetupWithManager(mgr); err != nil { diff --git a/pkg/network/network.go b/pkg/network/network.go index 4e273188..94ae20f5 100644 --- a/pkg/network/network.go +++ b/pkg/network/network.go @@ -6,7 +6,6 @@ import ( "path/filepath" "text/template" - "github.com/go-logr/logr" firewallv1 "github.com/metal-stack/firewall-controller/api/v1" "github.com/metal-stack/metal-go/api/models" "github.com/metal-stack/metal-networker/pkg/netconf" @@ -22,11 +21,12 @@ const ( //go:embed *.tpl var templates embed.FS -// ReconcileNetwork reconciles the network settings for a firewall -// in the current stage it only changes the FRR-Configuration when network prefixes or FRR template changes -func ReconcileNetwork(f firewallv1.Firewall, log logr.Logger) (bool, error) { - kb := netconf.NewKnowledgeBase(MetalKnowledgeBase) +func GetKnowledgeBase() netconf.KnowledgeBase { + return netconf.NewKnowledgeBase(MetalKnowledgeBase) +} +func GetUpdatedKnowledgeBase(f firewallv1.Firewall) netconf.KnowledgeBase { + kb := GetKnowledgeBase() networkMap := map[string]firewallv1.FirewallNetwork{} for _, n := range f.Spec.FirewallNetworks { if n.Networktype == nil { @@ -43,6 +43,12 @@ func ReconcileNetwork(f firewallv1.Firewall, log logr.Logger) (bool, error) { } kb.Networks = newNetworks + return kb +} + +// ReconcileNetwork reconciles the network settings for a firewall +// Changes both the FRR-Configuration and Nftable rules when network prefixes or FRR template changes +func ReconcileNetwork(kb netconf.KnowledgeBase) (changed bool, err error) { tmpFile, err := tmpFile(FrrConfig) if err != nil { return false, fmt.Errorf("error during network reconcilation %v: %w", tmpFile, err) @@ -57,12 +63,12 @@ func ReconcileNetwork(f firewallv1.Firewall, log logr.Logger) (bool, error) { return false, fmt.Errorf("error during network reconcilation: %v: %w", tmpFile, err) } - changed, err := a.Apply(*tpl, tmpFile, FrrConfig, true) + changed, err = a.Apply(*tpl, tmpFile, FrrConfig, true) if err != nil { return changed, fmt.Errorf("error during network reconcilation: %v: %w", tmpFile, err) } - return changed, nil + return } func tmpFile(file string) (string, error) { diff --git a/pkg/suricata/stats.go b/pkg/suricata/stats.go deleted file mode 100644 index 63b47ff9..00000000 --- a/pkg/suricata/stats.go +++ /dev/null @@ -1,53 +0,0 @@ -package suricata - -import ( - "context" - - "github.com/ks2211/go-suricata/client" -) - -// defaultSocket to communicate with suricata -const defaultSocket = "/run/suricata-command.socket" - -type Suricata struct { - socket string -} - -type InterfaceStats map[string]InterFaceStat - -type InterFaceStat struct { - Drop int - InvalidChecksums int - Pkts int -} - -func New() Suricata { - return Suricata{socket: defaultSocket} -} - -func (s *Suricata) InterfaceStats() (*InterfaceStats, error) { - suricata, err := client.CreateSocket(s.socket) - if err != nil { - return nil, err - } - defer suricata.Close() - - ifaces, err := suricata.IFaceListCommand(context.Background()) - if err != nil { - return nil, err - } - result := InterfaceStats{} - for _, iface := range ifaces.Ifaces { - stat, err := suricata.IFaceStatCommand(context.Background(), client.IFaceStatRequest{IFace: iface}) - if err != nil { - return nil, err - } - result[iface] = InterFaceStat{ - Drop: stat.Drop, - InvalidChecksums: stat.InvalidChecksums, - Pkts: stat.Pkts, - } - } - - return &result, nil -} diff --git a/pkg/suricata/suricata.go b/pkg/suricata/suricata.go new file mode 100644 index 00000000..6bb95a5c --- /dev/null +++ b/pkg/suricata/suricata.go @@ -0,0 +1,93 @@ +package suricata + +import ( + "context" + "fmt" + "os/exec" + + "github.com/metal-stack/metal-networker/pkg/netconf" + + "github.com/ks2211/go-suricata/client" +) + +const ( + suricataService = "suricata.service" + systemctlBin = "/bin/systemctl" + + // defaultSocket to communicate with suricata + defaultSocket = "/run/suricata-command.socket" +) + +type Suricata struct { + socket string + enableIDS bool +} + +type InterfaceStats map[string]InterFaceStat + +type InterFaceStat struct { + Drop int + InvalidChecksums int + Pkts int +} + +func New() *Suricata { + return &Suricata{ + socket: defaultSocket, + } +} + +func (s *Suricata) InterfaceStats() (*InterfaceStats, error) { + suricata, err := client.CreateSocket(s.socket) + if err != nil { + return nil, err + } + defer suricata.Close() + + ifaces, err := suricata.IFaceListCommand(context.Background()) + if err != nil { + return nil, err + } + result := InterfaceStats{} + for _, iface := range ifaces.Ifaces { + stat, err := suricata.IFaceStatCommand(context.Background(), client.IFaceStatRequest{IFace: iface}) + if err != nil { + return nil, err + } + result[iface] = InterFaceStat{ + Drop: stat.Drop, + InvalidChecksums: stat.InvalidChecksums, + Pkts: stat.Pkts, + } + } + + return &result, nil +} + +func (s *Suricata) ReconcileSuricata(kb netconf.KnowledgeBase, enableIDS bool) error { + if enableIDS != s.enableIDS { + configurator := netconf.FirewallConfigurator{ + CommonConfigurator: netconf.CommonConfigurator{ + Kb: kb, + }, + EnableIDS: enableIDS, + } + configurator.ConfigureSuricata() + + if err := s.restart(); err != nil { + return fmt.Errorf("failed to restart suricata: %w", err) + } + s.enableIDS = enableIDS + } + + return nil +} + +func (s *Suricata) restart() error { + c := exec.Command(systemctlBin, "restart", suricataService) + err := c.Run() + if err != nil { + return fmt.Errorf("could not reload suricata service, err: %w", err) + } + return nil +}