Daemon that listens for AMQP messages to add IP addresses and ports to FirewallD. IP addresses expire and are removed automatically after a configurable timeout.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

239 lines
5.9 KiB

package main
import (
"errors"
"os/exec"
"strconv"
"strings"
"time"
// "kazoo_firewall_agent/cache"
)
func firewall(action, ipaddr string, inputports interface{}) (err error) {
switch p := inputports.(type) {
case []interface{}:
for _, porti := range p {
switch port := porti.(type) {
case string:
firewallAction(action, ipaddr, port)
case float64:
firewallAction(action, ipaddr, strconv.Itoa(int(port)))
case int64:
firewallAction(action, ipaddr, strconv.Itoa(int(port)))
case int:
firewallAction(action, ipaddr, strconv.Itoa(port))
}
}
case []string:
for _, port := range p {
firewallAction(action, ipaddr, port)
}
case float64:
firewallAction(action, ipaddr, strconv.Itoa(int(p)))
case string:
splitPorts := strings.Split(p, ",")
for _, port := range splitPorts {
firewallAction(action, ipaddr, port)
}
case nil:
firewallAction(action, ipaddr, "")
default:
}
return nil
}
func parsePortProto(p string) PortProto {
var pp PortProto
pparsed := strings.Split(p, "/") //"8443/tcp" --> [8443, tcp]
if len(pparsed) < 1 {
return pp
}
pp.Port = pparsed[0]
pp.Proto = "tcp"
if len(pparsed) > 1 {
switch pparsed[1] {
case "tcp":
pp.Proto = "tcp"
case "TCP":
pp.Proto = "tcp"
case "udp":
pp.Proto = "udp"
case "UDP":
pp.Proto = "udp"
default:
}
}
return pp
}
func firewallAction(action, ipaddr string, portstring string) (err error) {
switch action {
case "firewall_add":
err = firewallAdd(ipaddr, portstring)
case "firewall_remove":
err = firewallDelete(ipaddr, portstring)
}
return err
}
func firewallAdd(ipaddr, portstring string) error {
var pp PortProto
pp = parsePortProto(portstring)
zone := determineZone(ipaddr, pp)
logit(5, "Firewall adding to zone '"+zone+"': "+ipaddr+":"+pp.Port+"/"+pp.Proto)
//ipcache.Set(ipaddr+":"+pp.Port+"/"+pp.Proto, zone, time.Duration(appconf.CacheTimeout) * time.Second)
if len(pp.Port) > 0 {
err := maybeAddPort(zone, pp, false)
if err != nil {
logit(3, err.Error())
return err
}
}
//if it's already cached, then don't add it, just refresh the expiry time and return
_, alreadyExists := ipcache.Get(ipaddr)
if alreadyExists {
ipcache.Set(ipaddr, zone, time.Duration(appconf.CacheTimeout)*time.Second)
return nil
}
fwOutput, err := exec.Command("firewall-cmd", "--zone="+zone, "--add-source="+ipaddr).CombinedOutput()
if err != nil {
logit(3, "Error executing firewall-cmd: "+err.Error()+" OUTPUT: "+string(fwOutput))
return errors.New("Error executing firewall-cmd: " + err.Error())
}
ipcache.Set(ipaddr, zone, time.Duration(appconf.CacheTimeout)*time.Second)
return nil
}
func firewallDelete(ipaddr, portstring string) error {
var pp PortProto
pp = parsePortProto(portstring)
zone := determineZone(ipaddr, pp)
logit(5, "Firewall removing from zone '"+zone+"': "+ipaddr)
fwOutput, err := exec.Command("firewall-cmd", "--zone="+zone, "--remove-source="+ipaddr).CombinedOutput()
if err != nil {
logit(3, "Error executing firewall-cmd: "+err.Error()+" OUTPUT: "+string(fwOutput))
return errors.New("Error executing firewall-cmd: " + err.Error())
}
ipcache.Remove(ipaddr)
//ipcache.Remove(ipaddr+":"+pp.Port+"/"+pp.Proto)
return nil
}
func maybeAddPort(zone string, pp PortProto, permanent bool) error {
var found bool
ports, err := getZonePorts(zone)
if err != nil {
return err
}
for _, port := range ports {
if port == pp.Port+"/"+pp.Proto {
found = true
return nil
}
}
if !found {
return addZonePort(zone, pp.Port+"/"+pp.Proto, permanent)
}
return nil
}
func addZonePort(zone, port string, permanent bool) error {
cmd := exec.Command("firewall-cmd", "--zone="+zone, "--add-port="+port)
if permanent {
cmd = exec.Command("firewall-cmd", "--zone="+zone, "--add-port="+port, "--permanent")
}
output, err := cmd.CombinedOutput()
if err != nil {
return errors.New(string(output) + " | " + err.Error())
}
ipcache.Remove("zone_" + zone)
return nil
}
func getZonePorts(zone string) ([]string, error) {
logit(7, "Get ports in zone: '"+zone+"'...")
ports, found := ipcache.Get("zone_" + zone) //check cache first
if found {
logit(7, "Ports for zone: '"+zone+"' are in cache; returning.")
return strings.Split(ports, " "), nil
}
logit(7, "Ports for zone: '"+zone+"' are not cached; getting from firewalld.")
cmd := exec.Command("firewall-cmd", "--zone="+zone, "--list-ports")
output, err := cmd.CombinedOutput()
outputArr := strings.Split(string(output), " ")
ipcache.Set("zone_"+zone, string(output), time.Duration(appconf.CacheTimeout)*time.Second)
return outputArr, err
}
func maybeSetupZone(zone string) error {
cmd := exec.Command("firewall-cmd", "--zone="+zone, "--list-ports")
output, err := cmd.CombinedOutput()
if err != nil {
logit(3, "Error querying zone '"+zone+"': "+err.Error()+" : "+string(output)+" : Zone may not exist. Trying to create...")
cmd = exec.Command("firewall-cmd", "--new-zone="+zone, "--permanent")
output, err = cmd.CombinedOutput()
if err != nil {
logit(3, "Error creating zone '"+zone+"': "+err.Error()+" : "+string(output))
return err
}
}
err = maybeAddDefaultPorts(zone)
if err != nil {
return err
}
return reload()
}
func maybeAddDefaultPorts(zone string) error {
var ports []string
if len(appconf.Ports) > 0 {
ports = strings.Split(appconf.Ports, ",")
for _, port := range ports {
pp := parsePortProto(port)
err := maybeAddPort(zone, pp, true)
if err != nil {
logit(3, err.Error())
return err
}
}
}
return nil
}
func reload() error {
cmd := exec.Command("firewall-cmd", "--reload")
output, err := cmd.CombinedOutput()
if err != nil {
logit(3, "Unable to reload firewalld! Output: "+string(output)+"Error: "+err.Error())
}
logit(5, "Reload firewalld: "+string(output))
return err
}
func determineZone(ipaddr string, pp PortProto) string {
var zone string
logit(7, "Determine zone for ipaddr '"+ipaddr+"'...")
//do stuff
zone = appconf.FirewallZone
return zone
}