Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c42b267
fix(sonicwall): optimize field parsing and clean double quotes
JocLRojas Oct 24, 2025
ca1fda5
Upgrade pfSense filter to v2.1.0 with improved syslog parsing, optimi…
JocLRojas Oct 28, 2025
5a12f03
feat(agent): implement TLS support for syslog integrations.
yllada Oct 31, 2025
142e593
fix(account): sanitize password in checkPassword method to prevent in…
mjabascal10 Oct 31, 2025
b28c6d5
feat(date-timezone): add New Zealand and Fiji timezones to the constants
mjabascal10 Oct 31, 2025
0572f0b
feat(date-timezone): add New Zealand and Fiji timezones to the constants
mjabascal10 Oct 31, 2025
a6dd656
fea: add tls connection options and setup steps for syslog integrations
mjabascal10 Oct 31, 2025
8d9be89
fea: add tls connection options and setup steps for syslog integrations
mjabascal10 Oct 31, 2025
8ea7022
fea: add tls connection options and setup steps for syslog integrations
mjabascal10 Oct 31, 2025
d5c7a3e
feat: add tls connection options and setup steps for syslog integrations
mjabascal10 Oct 31, 2025
e3ca85b
feat(agent): validate TLS certificates before enabling TLS for integr…
yllada Oct 31, 2025
0a89a40
feat: add tls connection options and setup steps for syslog integrations
mjabascal10 Nov 3, 2025
08ff88d
feat: improve asset source sorting and display in assets view
mjabascal10 Nov 3, 2025
9c9d5d9
feat(agent): add TLS support for syslog command generation and update…
mjabascal10 Nov 5, 2025
2432a35
feat(agent): add TLS support for syslog command generation and update…
mjabascal10 Nov 5, 2025
23adb5f
feat(agent): refactor PowerShell command generation for TLS certifica…
mjabascal10 Nov 6, 2025
6862ada
feat: add Liquibase changelogs for pfSense and SonicWall filter updates
mjabascal10 Nov 10, 2025
b446b21
Update CHANGELOG.md
Kbayero Nov 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# UTMStack 10.9.1 Release Notes
# UTMStack 10.9.2 Release Notes

-- Dashboard Rendering with Time Filters
Resolved performance issues affecting dashboard responsiveness when applying time-based filters.
- Added new Pacific time zones (New Zealand and Fiji) to the Date Settings section.
- Added TLS connection options and setup steps for secure Syslog integration
- Improved sorting of asset sources in tables, ensuring consistent and predictable order for names, IPs, and combined entries.
– Improved correlation rule handling for pfSense and SonicWall data sources to enhance detection accuracy and event normalization.
6 changes: 6 additions & 0 deletions agent/config/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ var (
MESSAGE_HEADER = "utm_stack_agent_ds"
BatchCapacity = 100

// TLS Configuration for Integrations
IntegrationCertPath = filepath.Join(utils.GetMyPath(), "certs", "integration.crt")
IntegrationKeyPath = filepath.Join(utils.GetMyPath(), "certs", "integration.key")
IntegrationCAPath = filepath.Join(utils.GetMyPath(), "certs", "integration-ca.crt")


// MaxConnectionTime = 120 * time.Second
// SERV_NAME = "UTMStackAgent"
// SERV_LOG = "utmstack_agent.log"
Expand Down
88 changes: 85 additions & 3 deletions agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,88 @@ func main() {
serv.InstallService()
fmt.Println("[OK]")
fmt.Println("UTMStackAgent service installed correctly")

case "enable-integration", "disable-integration":
fmt.Println("Changing integration status ...")
integration := os.Args[2]
proto := os.Args[3]

port, err := modules.ChangeIntegrationStatus(integration, proto, (arg == "enable-integration"))
tlsEnabled := false
for _, arg := range os.Args[4:] {
if arg == "--tls" {
tlsEnabled = true
break
}
}

var port string
var err error

if arg == "enable-integration" && tlsEnabled {
port, err = modules.ChangeIntegrationStatus(integration, proto, true, true)
} else if arg == "enable-integration" {
port, err = modules.ChangeIntegrationStatus(integration, proto, true, false)
} else {
port, err = modules.ChangeIntegrationStatus(integration, proto, false)
}

if err != nil {
fmt.Println("Error trying to change integration status: ", err)
fmt.Println("Error:", err)
os.Exit(1)
}

if arg == "enable-integration" && tlsEnabled {
fmt.Printf("Integration %s %s enabled with TLS on port %s\n", integration, proto, port)
} else if arg == "enable-integration" {
fmt.Printf("Integration %s %s enabled on port %s\n", integration, proto, port)
} else {
fmt.Printf("Integration %s %s disabled (port %s freed)\n", integration, proto, port)
}
time.Sleep(5 * time.Second)

case "load-tls-certs":
if len(os.Args) < 4 {
fmt.Println("Usage: ./utmstack_agent load-tls-certs <certificate_path> <private_key_path> [ca_certificate_path]")
fmt.Println("Example: ./utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key /path/to/ca.crt")
os.Exit(1)
}

userCertPath := os.Args[2]
userKeyPath := os.Args[3]
var userCAPath string
if len(os.Args) > 4 {
userCAPath = os.Args[4]
}

fmt.Println("Loading user TLS certificates ...")

fmt.Print("Validating certificate files ... ")
if err := utils.ValidateIntegrationCertificates(userCertPath, userKeyPath); err != nil {
fmt.Printf("\nError: Invalid certificate files: %v\n", err)
os.Exit(1)
}
fmt.Printf("Action %s %s %s correctly in port %s\n", arg, integration, proto, port)
fmt.Println("[OK]")

fmt.Print("Installing certificates ... ")
src := utils.CertificateFiles{
CertPath: userCertPath,
KeyPath: userKeyPath,
CAPath: userCAPath,
}
dest := utils.CertificateFiles{
CertPath: config.IntegrationCertPath,
KeyPath: config.IntegrationKeyPath,
CAPath: config.IntegrationCAPath,
}
if err := utils.LoadUserCertificatesWithStruct(src, dest); err != nil {
fmt.Printf("\nError loading certificates: %v\n", err)
os.Exit(1)
}
fmt.Println("[OK]")

fmt.Println("TLS certificates loaded successfully!")
time.Sleep(5 * time.Second)

case "change-port":
fmt.Println("Changing integration port ...")
integration := os.Args[2]
Expand All @@ -109,6 +179,7 @@ func main() {
}
fmt.Printf("Port changed correctly from %s to %s\n", old, port)
time.Sleep(5 * time.Second)

case "uninstall":
fmt.Print("Uninstalling UTMStackAgent service ...")

Expand Down Expand Up @@ -160,6 +231,17 @@ func Help() {
fmt.Println(" uninstall Uninstall the UTMStackAgent service")
fmt.Println(" help Display this help message")
fmt.Println()
fmt.Println("TLS Certificate Management:")
fmt.Println(" # Load your own certificates (RECOMMENDED)")
fmt.Println(" ./utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key /path/to/ca.crt")
fmt.Println(" ./utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key # Without CA")
fmt.Println()
fmt.Println("TLS Integration Examples:")
fmt.Println(" ./utmstack_agent enable-integration syslog tcp --tls # Enable with TLS")
fmt.Println(" ./utmstack_agent enable-integration syslog tcp # Enable without TLS (default)")
fmt.Println(" ./utmstack_agent disable-integration syslog tcp # Disable (auto-disables TLS)")
fmt.Println(" ./utmstack_agent check-tls-certs # Check certificate status")
fmt.Println()
fmt.Println("Note:")
fmt.Println(" - Make sure to run commands with appropriate permissions.")
fmt.Println(" - All commands require administrative privileges.")
Expand Down
128 changes: 124 additions & 4 deletions agent/modules/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import (
"net"
"os"
"strings"
"time"

"github.com/utmstack/UTMStack/agent/config"
"github.com/utmstack/UTMStack/agent/utils"
)

type Port struct {
IsListen bool `json:"enabled"`
Port string `json:"value"`
IsListen bool `json:"enabled"`
Port string `json:"value"`
TLSEnabled bool `json:"tls_enabled,omitempty"`
}

type Integration struct {
Expand Down Expand Up @@ -59,7 +61,7 @@ func ConfigureCollectorFirstTime() error {
return WriteCollectorConfig(integrations, config.CollectorFileName)
}

func ChangeIntegrationStatus(logTyp string, proto string, isEnabled bool) (string, error) {
func ChangeIntegrationStatus(logTyp string, proto string, isEnabled bool, tlsOptions ...bool) (string, error) {
var port string
cnf, err := ReadCollectorConfig()
if err != nil {
Expand All @@ -78,9 +80,52 @@ func ChangeIntegrationStatus(logTyp string, proto string, isEnabled bool) (strin
case "tcp":
integration.TCP.IsListen = isEnabled
port = integration.TCP.Port

// Handle TLS configuration if specified
if len(tlsOptions) > 0 && isEnabled {
if tlsOptions[0] {
if !utils.CheckIfPathExist(config.IntegrationCertPath) || !utils.CheckIfPathExist(config.IntegrationKeyPath) {
return "", fmt.Errorf("TLS certificates not found. Please load certificates first")
}
// Enable TLS
integration.TCP.TLSEnabled = true
mod := GetModule(logTyp)
if mod != nil && mod.IsPortListen(proto) {
mod.DisablePort(proto)
time.Sleep(100 * time.Millisecond)
err := mod.EnablePort(proto, true)
if err != nil {
return "", fmt.Errorf("error enabling TLS on running module: %v", err)
}
}
} else {
// Disable TLS
integration.TCP.TLSEnabled = false
mod := GetModule(logTyp)
if mod != nil && mod.IsPortListen(proto) {
mod.DisablePort(proto)
time.Sleep(100 * time.Millisecond)
err := mod.EnablePort(proto, false)
if err != nil {
return "", fmt.Errorf("error disabling TLS on running module: %v", err)
}
}
}
}

// Auto-disable TLS when disabling integration
if !isEnabled {
integration.TCP.TLSEnabled = false
}

case "udp":
integration.UDP.IsListen = isEnabled
port = integration.UDP.Port

// TLS validation for UDP
if len(tlsOptions) > 0 && tlsOptions[0] {
return "", fmt.Errorf("TLS is not supported for UDP protocol. Use TCP for TLS connections")
}
}

cnf.Integrations[logTyp] = integration
Expand Down Expand Up @@ -145,7 +190,11 @@ func WriteCollectorConfig(integrations map[string]Integration, filename string)
for name, integration := range integrations {
fileContent += fmt.Sprintf(" \"%s\": {\n", name)
if integration.TCP.Port != "" {
fileContent += fmt.Sprintf(" \"tcp_port\": {\"enabled\": %t, \"value\": \"%s\"},\n", integration.TCP.IsListen, integration.TCP.Port)
fileContent += fmt.Sprintf(" \"tcp_port\": {\"enabled\": %t, \"value\": \"%s\"", integration.TCP.IsListen, integration.TCP.Port)
if integration.TCP.TLSEnabled {
fileContent += fmt.Sprintf(", \"tls_enabled\": %t", integration.TCP.TLSEnabled)
}
fileContent += "},\n"
}
if integration.UDP.Port != "" {
fileContent += fmt.Sprintf(" \"udp_port\": {\"enabled\": %t, \"value\": \"%s\"},\n", integration.UDP.IsListen, integration.UDP.Port)
Expand Down Expand Up @@ -184,3 +233,74 @@ func WriteCollectorConfigFromModules(mod []Module, filename string) error {
}
return WriteCollectorConfig(integrations, filename)
}

func EnableTLSForIntegration(logTyp string, proto string) (string, error) {
cnf, err := ReadCollectorConfig()
if err != nil {
return "", fmt.Errorf("error reading collector config: %v", err)
}

if valid := config.ValidateModuleType(logTyp); valid == "nil" {
return "", fmt.Errorf("invalid integration: %s", logTyp)
}

integration := cnf.Integrations[logTyp]
var port string

switch proto {
case "tcp":
if integration.TCP.Port == "" {
return "", fmt.Errorf("TCP port not configured for %s", logTyp)
}

port = integration.TCP.Port
integration.TCP.TLSEnabled = true

mod := GetModule(logTyp)
if mod != nil && mod.IsPortListen(proto) {
mod.DisablePort(proto)
time.Sleep(100 * time.Millisecond)
err := mod.EnablePort(proto, true)
if err != nil {
return port, fmt.Errorf("error enabling TLS on running module: %v", err)
}
}
case "udp":
return "", fmt.Errorf("TLS not supported for UDP protocol")
default:
return "", fmt.Errorf("invalid protocol: %s", proto)
}

cnf.Integrations[logTyp] = integration
return port, WriteCollectorConfig(cnf.Integrations, config.CollectorFileName)
}

func DisableTLSForIntegration(logTyp string, proto string) error {
cnf, err := ReadCollectorConfig()
if err != nil {
return fmt.Errorf("error reading collector config: %v", err)
}

integration := cnf.Integrations[logTyp]
switch proto {
case "tcp":
integration.TCP.TLSEnabled = false

mod := GetModule(logTyp)
if mod != nil && mod.IsPortListen(proto) {
mod.DisablePort(proto)
time.Sleep(100 * time.Millisecond)
err := mod.EnablePort(proto, false)
if err != nil {
return fmt.Errorf("error disabling TLS on running module: %v", err)
}
}
case "udp":
return fmt.Errorf("TLS not supported for UDP protocol")
default:
return fmt.Errorf("invalid protocol: %s", proto)
}

cnf.Integrations[logTyp] = integration
return WriteCollectorConfig(cnf.Integrations, config.CollectorFileName)
}
9 changes: 7 additions & 2 deletions agent/modules/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Module interface {
IsPortListen(proto string) bool
SetNewPort(proto string, port string)
GetPort(proto string) string
EnablePort(proto string)
EnablePort(proto string, enableTLS bool) error
DisablePort(proto string)
}

Expand Down Expand Up @@ -88,7 +88,12 @@ func StartModules() {
if changeAllowed {
moCache[index].SetNewPort(proto, port)
if conf[1] {
moCache[index].EnablePort(proto)
enableTLS := proto == "tcp" && cnf.TCP.TLSEnabled

err := moCache[index].EnablePort(proto, enableTLS)
if err != nil {
utils.Logger.ErrorF("error enabling port for %s %s: %v", intType, proto, err)
}
}
} else {
utils.Logger.Info("change in port %s protocol %s not allowed for %s or out range %s-%s", port, proto, intType, config.PortRangeMin, config.PortRangeMax)
Expand Down
12 changes: 9 additions & 3 deletions agent/modules/netflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,20 @@ func GetNetflowModule() *NetflowModule {
return netflowModule
}

func (m *NetflowModule) EnablePort(proto string) {
func (m *NetflowModule) EnablePort(proto string, enableTLS bool) error {
if proto == "udp" && !m.IsEnabled {
// NetFlow over UDP doesn't support TLS
if enableTLS {
utils.Logger.Info("TLS not supported for NetFlow (UDP protocol), ignoring TLS flag")
}

utils.Logger.Info("Server %s listening in port: %s protocol: UDP", m.DataType, config.ProtoPorts[config.DataTypeNetflow].UDP)
m.IsEnabled = true

port, err := strconv.Atoi(config.ProtoPorts[config.DataTypeNetflow].UDP)
if err != nil {
utils.Logger.ErrorF("error converting port to int: %v", err)
return
return err
}

m.Listener, err = net.ListenUDP("udp", &net.UDPAddr{
Expand All @@ -62,7 +67,7 @@ func (m *NetflowModule) EnablePort(proto string) {
})
if err != nil {
utils.Logger.ErrorF("error listening netflow: %v", err)
return
return err
}

m.CTX, m.Cancel = context.WithCancel(context.Background())
Expand Down Expand Up @@ -122,6 +127,7 @@ func (m *NetflowModule) EnablePort(proto string) {
}
}()
}
return nil
}

func (m *NetflowModule) DisablePort(proto string) {
Expand Down
Loading
Loading