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.

138 lines
5.6 KiB

////////////////////////////////////////////////////////////////////////
// KazooFirewallAgent (c) 2024-2025, RuhNet, All Rights Reserved, except
// for firewall.go, which is copyright PBX.com LLC
// Author: Ruel Tmeizeh, RuhNet www.ruhnet.co
// Dual-Licensed to PBX.com LLC under a non-exclusive nearly-unlimited
// most-rights-granted use/deployment/transfer/modification license.
////////////////////////////////////////////////////////////////////////
package main
import (
"fmt"
//"kazoo_firewall_agent/cache"
"log"
"os"
//"os/signal"
"strings"
//"syscall"
"time"
)
const appnameFull string = "Kazoo Firewall Agent"
const appname string = "kazoo_firewall_agent"
const version string = "1.0"
const serverVersion string = "RuhNet " + appname + " v" + version
const website string = "https://ruhnet.co"
var startupTime time.Time
var myHostname, myHostnameShort string
const banner = `
--⟶____ __ _ _ __
-⟶/ ___\ __ __/ /_ / \ / /__ __/ /_
⟶/ /_/ // /_/ / _ \/ / \/ //__\_ __/
/_/ \_\\ ___/_/ /_/_/ \__/ \__,/_/ %s
_____________________________________________________
`
var ipcache *TTLCache
var done = make(chan bool, 1)
var portProtos []PortProto
type PortProto struct {
Port string
Proto string
}
type AppConfig struct {
ServerType string `json:"server_type" env:"SERVER_TYPE" default:"freeswitch"`
AmqpURI string `json:"amqp_uri" env:"AMQP_URI" default:"amqp://guest:guest@localhost:5672"`
AmqpExch string `json:"amqp_exchange" env:"AMQP_EXCH" default:"call_shield"`
AmqpSubExch string `json:"amqp_sub_exchange" env:"AMQP_SUB_EXCH" default:""`
AmqpPubExch string `json:"amqp_pub_exchange" env:"AMQP_PUB_EXCH" default:""`
AmqpExchType string `json:"amqp_exchange_type" env:"AMQP_EXCH_TYPE" default:"topic"`
AmqpDirectRoutingKey string `json:"amqp_direct_routing_key" env:"AMQP_DIRECT_ROUTING_KEY" default:""` //call_shield.{hostname}.*.*
AmqpSubRoutingKey string `json:"amqp_sub_routing_key" env:"AMQP_SUB_ROUTING_KEY" default:""` //"call_shield.{server_type}.firewall.*"
AmqpPubRoutingKey string `json:"amqp_pub_routing_key" env:"AMQP_PUB_ROUTING_KEY" default:"call_shield.test"`
AmqpWorkers int `json:"amqp_workers" env:"AMQP_WORKERS" default:"4"`
PubMessageFile string `json:"pub_message_file" env:"PUB_MSG_FILE" default:"/tmp/message.json"`
Ports string `json:"ports" env:"PORTS" default:"11000/udp,11000,11001/tcp,16384-32768/udp"`
FilterEvtCat string `json:"filter_event_category" env:"FLT_EVT_CAT" default:"*"` //allow comma separated string for multiple
FilterEvtName string `json:"filter_event_name" env:"FLT_EVT_NAME" default:"*"`
FilterEvtAppName string `json:"filter_event_appname" env:"FLT_EVT_APPNAME" default:"*"`
CacheTimeout int64 `json:"cache_timeout_sec" env:"CACHE_TIMEOUT" default:"43200"` //43200 is 12hr
FirewallType string `json:"firewall_type" env:"FIREWALL_ZONE" default:"firewalld"` //firewalld, iptables
FirewallZone string `json:"firewall_zone" env:"FIREWALL_ZONE" default:"SIPFS"`
LogFile string `json:"log_file" env:"LOG_FILE" default:"/var/log/kazoo_firewall_agent.log"`
LogLevel int `json:"log_level" env:"LOG_LEVEL" default:"5"`
}
func main() {
var err error
startupTime = time.Now()
/////////////////////////////////////////////
// Setup
initConfig()
fmt.Println("Configuration OK, starting " + appname + "...")
logit(5, "Configuration OK, starting "+appname+"...")
/////////////////////////////////////////////
// Logging
appLogFile, err := os.OpenFile(appconf.LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
if err != nil {
log.Println("Could not open log file: " + appconf.LogFile + "\n" + err.Error())
appLogFile, err = os.OpenFile("/tmp/"+appname+".log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
if err != nil {
fmt.Println("Can't open even /tmp log file!\n" + err.Error())
log.Fatal("Can't open even /tmp log file!\n" + err.Error())
}
}
defer appLogFile.Close()
//set other logging to same file
log.SetOutput(appLogFile)
//Startup Banner
fmt.Printf(banner, website)
fmt.Printf(serverVersion + "\n\n")
logit(5, appname+"v"+version+" starting up... ")
logit(5, fmt.Sprintf("Logging with level: %d", appconf.LogLevel))
//Hostname
myHostname, err = os.Hostname()
logit(5, "Detecting my hostname... "+myHostname)
if err != nil {
fmt.Println("Hostname could not be auto-detected from system: " + err.Error())
log.Fatal("Hostname could not be auto-detected from system: " + err.Error())
}
myHostnameShort = strings.SplitN(myHostname, ".", 2)[0]
//fmt.Println()
//create the IP address cache with expiry and function to run when cached entry expires
if shouldAddPermanently() {
ipcache = NewTTL(appconf.CacheTimeout, firewallDeleteCacheOnly)
} else {
ipcache = NewTTL(appconf.CacheTimeout, firewallDelete)
}
if appconf.FirewallType == "firewalld" {
logit(5, "reloading FirewallD...")
err = reload() //reload firewalld to reset firewall state
if err != nil {
fmt.Println("FirewallD could not be reloaded! (Is firewalld running?): " + err.Error())
log.Fatal("FirewallD could not be reloaded. (Is firewalld running?): " + err.Error())
}
err = maybeSetupZone(appconf.FirewallZone) //reload firewalld to reset firewall state
if err != nil {
fmt.Println("Firewall zone '" + appconf.FirewallZone + "' could not be created. (Is firewalld running?): " + err.Error())
log.Fatal("Firewall zone '" + appconf.FirewallZone + "' could not be created. (Is firewalld running?): " + err.Error())
}
}
amqp() //this func will block the main thread, until receiving on the 'done' channel.
}