github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/internal/operator/nodeagent/firewall/centos/centOS.go (about)

     1  package centos
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os/exec"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/caos/orbos/internal/operator/common"
    11  	"github.com/caos/orbos/internal/operator/nodeagent"
    12  	"github.com/caos/orbos/mntr"
    13  )
    14  
    15  func Ensurer(monitor mntr.Monitor, open []string) nodeagent.FirewallEnsurer {
    16  	return nodeagent.FirewallEnsurerFunc(func(desired common.Firewall) (common.FirewallCurrent, func() error, error) {
    17  		ensurers := make([]func() error, 0)
    18  		current := make(common.FirewallCurrent, 0)
    19  
    20  		if desired.Zones == nil {
    21  			desired.Zones = make(map[string]*common.Zone, 0)
    22  		}
    23  
    24  		_, inactiveErr := runCommand(monitor, "systemctl", "is-active", "firewalld")
    25  		_, disabledErr := runCommand(monitor, "systemctl", "is-enabled", "firewalld")
    26  		if inactiveErr != nil || disabledErr != nil {
    27  			monitor.WithFields(
    28  				map[string]interface{}{
    29  					"disabled": strconv.FormatBool(disabledErr != nil),
    30  					"inactive": strconv.FormatBool(inactiveErr != nil),
    31  				},
    32  			).Info("Firewall is inactive or disabled")
    33  			return current, func() error {
    34  				monitor.Info("Enabling and starting firewall")
    35  				if _, err := runCommand(monitor, "systemctl", "enable", "firewalld"); err != nil {
    36  					return err
    37  				}
    38  
    39  				_, err := runCommand(monitor, "systemctl", "start", "firewalld")
    40  				return err
    41  			}, nil
    42  		}
    43  
    44  		// Ensure that all runtime config made in the previous iteration becomes permanent.
    45  		if _, err := runFirewallCommand(monitor, "--runtime-to-permanent"); err != nil {
    46  			return current, nil, err
    47  		}
    48  
    49  		currentFirewall, err := queryCurrentFirewall(monitor)
    50  		if err != nil {
    51  			return current, nil, err
    52  		}
    53  
    54  		for name, _ := range desired.Zones {
    55  			currentZone, ensureFunc, err := ensureZone(monitor, name, desired, currentFirewall, open)
    56  			if err != nil {
    57  				return current, nil, err
    58  			}
    59  			current = append(current, currentZone)
    60  			if ensureFunc != nil {
    61  				ensurers = append(ensurers, ensureFunc)
    62  			}
    63  		}
    64  
    65  		if len(ensurers) == 0 {
    66  			monitor.Debug("Not changing firewall")
    67  			return current, nil, nil
    68  		}
    69  
    70  		current.Sort()
    71  
    72  		return current, func() (err error) {
    73  			monitor.Debug("Ensuring firewall")
    74  			for _, ensurer := range ensurers {
    75  				if err := ensurer(); err != nil {
    76  					return err
    77  				}
    78  			}
    79  			return nil
    80  		}, nil
    81  	})
    82  }
    83  
    84  func ensureZone(monitor mntr.Monitor, zoneName string, desired common.Firewall, currentFW map[string]Zone, open []string) (*common.ZoneDesc, func() error, error) {
    85  	current := &common.ZoneDesc{
    86  		Name:       zoneName,
    87  		Interfaces: []string{},
    88  		Services:   []*common.Service{},
    89  		FW:         []*common.Allowed{},
    90  	}
    91  
    92  	current.Interfaces = currentFW[zoneName].Interfaces.slice
    93  	current.Sources = currentFW[zoneName].Sources.slice
    94  
    95  	ensureMasquerade := getEnsureMasquerade(zoneName, current, desired, currentFW[zoneName])
    96  	addPorts, removePorts := getAddAndRemovePorts(current, desired.Ports(zoneName), open, currentFW[zoneName])
    97  	ensureIfaces, removeIfaces := getEnsureAndRemoveInterfaces(zoneName, current, desired)
    98  	addSources, removeSources := getAddAndRemoveSources(monitor, zoneName, current, desired)
    99  	ensureTarget := getEnsureTarget(currentFW[zoneName])
   100  
   101  	monitor.WithFields(map[string]interface{}{
   102  		"open":  strings.Join(addPorts, ";"),
   103  		"close": strings.Join(removePorts, ";"),
   104  	}).Debug("firewall changes determined")
   105  
   106  	if len(addPorts) == 0 &&
   107  		len(removePorts) == 0 &&
   108  		len(addSources) == 0 &&
   109  		len(removeSources) == 0 &&
   110  		len(ensureIfaces) == 0 &&
   111  		len(removeIfaces) == 0 &&
   112  		len(ensureTarget) == 0 {
   113  		return current, nil, nil
   114  	}
   115  
   116  	zoneNameCopy := zoneName
   117  	return current, func() (err error) {
   118  
   119  		if len(ensureTarget) > 0 {
   120  
   121  			monitor.Debug(fmt.Sprintf("Ensuring part of firewall with %s in zone %s", ensureTarget, zoneNameCopy))
   122  			if err := ensure(monitor, ensureTarget, zoneNameCopy); err != nil {
   123  				return err
   124  			}
   125  
   126  			// this is the only property that needs a firewall reload
   127  			_, err := runFirewallCommand(monitor, "--reload")
   128  			return err
   129  		}
   130  
   131  		if ensureMasquerade != "" {
   132  			monitor.Debug(fmt.Sprintf("Ensuring part of firewall with %s in zone %s", ensureMasquerade, zoneNameCopy))
   133  			if err := ensure(monitor, []string{ensureMasquerade}, zoneNameCopy); err != nil {
   134  				return err
   135  			}
   136  		}
   137  
   138  		monitor.Debug(fmt.Sprintf("Ensuring part of firewall with %s in zone %s", removeIfaces, zoneNameCopy))
   139  		if err := ensure(monitor, removeIfaces, zoneNameCopy); err != nil {
   140  			return err
   141  		}
   142  
   143  		monitor.Debug(fmt.Sprintf("Ensuring part of firewall with %s in zone %s", ensureIfaces, zoneNameCopy))
   144  		if err := ensure(monitor, ensureIfaces, zoneNameCopy); err != nil {
   145  			return err
   146  		}
   147  
   148  		monitor.Debug(fmt.Sprintf("Ensuring part of firewall with %s in zone %s", removeSources, zoneNameCopy))
   149  		if err := ensure(monitor, removeSources, zoneNameCopy); err != nil {
   150  			return err
   151  		}
   152  
   153  		monitor.Debug(fmt.Sprintf("Ensuring part of firewall with %s in zone %s", addSources, zoneNameCopy))
   154  		if err := ensure(monitor, addSources, zoneNameCopy); err != nil {
   155  			return err
   156  		}
   157  
   158  		monitor.Debug(fmt.Sprintf("Ensuring part of firewall with %s in zone %s", removePorts, zoneNameCopy))
   159  		if err := ensure(monitor, removePorts, zoneNameCopy); err != nil {
   160  			return err
   161  		}
   162  
   163  		monitor.Debug(fmt.Sprintf("Ensuring part of firewall with %s in zone %s", addPorts, zoneNameCopy))
   164  		return ensure(monitor, addPorts, zoneNameCopy)
   165  	}, nil
   166  }
   167  
   168  func ensure(monitor mntr.Monitor, changes []string, zone string) error {
   169  	if changes == nil || len(changes) == 0 {
   170  		return nil
   171  	}
   172  
   173  	return changeFirewall(monitor, changes, zone)
   174  }
   175  
   176  func changeFirewall(monitor mntr.Monitor, changes []string, zone string) error {
   177  	if len(changes) == 0 {
   178  		return nil
   179  	}
   180  
   181  	_, err := runFirewallCommand(monitor.Verbose(), append([]string{"--zone", zone}, changes...)...)
   182  	return err
   183  }
   184  
   185  func listFirewall(monitor mntr.Monitor, zone string, arg string) ([]string, error) {
   186  
   187  	out, err := runFirewallCommand(monitor, "--zone", zone, arg)
   188  	return strings.Fields(out), err
   189  }
   190  
   191  func runFirewallCommand(monitor mntr.Monitor, args ...string) (string, error) {
   192  	return runCommand(monitor, "firewall-cmd", args...)
   193  }
   194  
   195  func runCommand(monitor mntr.Monitor, binary string, args ...string) (string, error) {
   196  
   197  	outBuf := new(bytes.Buffer)
   198  	defer outBuf.Reset()
   199  	errBuf := new(bytes.Buffer)
   200  	defer errBuf.Reset()
   201  
   202  	cmd := exec.Command(binary, args...)
   203  	cmd.Stderr = errBuf
   204  	cmd.Stdout = outBuf
   205  
   206  	fullCmd := fmt.Sprintf("'%s'", strings.Join(cmd.Args, "' '"))
   207  	if err := cmd.Run(); err != nil {
   208  		return "", fmt.Errorf(`running %s failed with stderr %s: %w`, fullCmd, errBuf.String(), err)
   209  	}
   210  
   211  	stdout := outBuf.String()
   212  	if monitor.IsVerbose() {
   213  		fmt.Println(fullCmd)
   214  		fmt.Println(stdout)
   215  	}
   216  
   217  	return strings.TrimSuffix(stdout, "\n"), nil
   218  }