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 }