feat(sol): UDP + HTTP listener
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
build.sh
|
||||||
|
dist/
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
.env
|
||||||
|
build.log
|
||||||
|
build/
|
||||||
|
Makefile
|
||||||
35
auth/auth.go
Normal file
35
auth/auth.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Authenticator handles authentication
|
||||||
|
type Authenticator struct {
|
||||||
|
user string
|
||||||
|
password string
|
||||||
|
hash string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new authenticator with the given credentials
|
||||||
|
func New(username, password string) *Authenticator {
|
||||||
|
authString := fmt.Sprintf("%s:%s", username, password)
|
||||||
|
hash := sha256.Sum256([]byte(authString))
|
||||||
|
hashStr := hex.EncodeToString(hash[:])
|
||||||
|
|
||||||
|
return &Authenticator{
|
||||||
|
user: username,
|
||||||
|
password: password,
|
||||||
|
hash: hashStr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify checks if the provided username and password are valid
|
||||||
|
func (a *Authenticator) Verify(username, password string) bool {
|
||||||
|
authString := fmt.Sprintf("%s:%s", username, password)
|
||||||
|
hash := sha256.Sum256([]byte(authString))
|
||||||
|
hashStr := hex.EncodeToString(hash[:])
|
||||||
|
return hashStr == a.hash
|
||||||
|
}
|
||||||
82
config/config.go
Normal file
82
config/config.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Default ports
|
||||||
|
DefaultSOLPort = 9999
|
||||||
|
DefaultAPIPort = 8080
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config holds the application configuration
|
||||||
|
type Config struct {
|
||||||
|
SOLPort int
|
||||||
|
APIPort int
|
||||||
|
APIEnabled bool
|
||||||
|
AuthUser string
|
||||||
|
AuthPass string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load loads configuration from environment variables
|
||||||
|
func Load() *Config {
|
||||||
|
cfg := &Config{
|
||||||
|
SOLPort: getPortFromEnv("SOL_PORT", DefaultSOLPort),
|
||||||
|
APIPort: getPortFromEnv("API_PORT", DefaultAPIPort),
|
||||||
|
APIEnabled: getBoolFromEnv("API_ENABLED", false),
|
||||||
|
AuthUser: os.Getenv("SOL_AUTH_USER"),
|
||||||
|
AuthPass: os.Getenv("SOL_AUTH_PASSWORD"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.AuthUser == "" || cfg.AuthPass == "" {
|
||||||
|
masterlog.Error("Environment variables not set", map[string]interface{}{
|
||||||
|
"variables": "SOL_AUTH_USER, SOL_AUTH_PASSWORD",
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPortFromEnv(envVar string, defaultPort int) int {
|
||||||
|
if portStr := os.Getenv(envVar); portStr != "" {
|
||||||
|
if port, err := strconv.Atoi(portStr); err == nil && port > 0 && port < 65536 {
|
||||||
|
return port
|
||||||
|
} else {
|
||||||
|
masterlog.Error("Invalid environment variable value", map[string]interface{}{
|
||||||
|
"error": err,
|
||||||
|
"variable": envVar,
|
||||||
|
"value": portStr,
|
||||||
|
"default": defaultPort,
|
||||||
|
"type": "port",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultPort
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBoolFromEnv(envVar string, defaultValue bool) bool {
|
||||||
|
if val := os.Getenv(envVar); val != "" {
|
||||||
|
if val == "true" || val == "TRUE" || val == "True" || val == "1" {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
masterlog.Error("Invalid environment variable value", map[string]interface{}{
|
||||||
|
"variable": envVar,
|
||||||
|
"value": val,
|
||||||
|
"type": "boolean",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
masterlog.Error("Invalid environment variable value", map[string]interface{}{
|
||||||
|
"variable": envVar,
|
||||||
|
"value": val,
|
||||||
|
"type": "boolean",
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module wol-sol-agent
|
||||||
|
|
||||||
|
go 1.25.3
|
||||||
|
|
||||||
|
require git.secnex.io/secnex/masterlog v0.1.0
|
||||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
git.secnex.io/secnex/masterlog v0.1.0 h1:74j9CATpfeK0lxpWIQC9ag9083akwG8khi5BwLedD8E=
|
||||||
|
git.secnex.io/secnex/masterlog v0.1.0/go.mod h1:OnDlwEzdkKMnqY+G5O9kHdhoJ6fH1llbVdXpgSc5SdM=
|
||||||
89
magicpacket/magicpacket.go
Normal file
89
magicpacket/magicpacket.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package magicpacket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Magic packet format: 6 bytes of 0xFF followed by 16 repetitions of the MAC address
|
||||||
|
MagicPacketHeader = 6
|
||||||
|
MacAddressRepetitions = 16
|
||||||
|
MacAddressLength = 6
|
||||||
|
// Authentication format for UDP: "AUTH:username:password:" followed by magic packet
|
||||||
|
AuthPrefix = "AUTH:"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsMagicPacket checks if the received data is a valid Wake-on-LAN Magic Packet
|
||||||
|
// Format: 6 bytes of 0xFF followed by 16 repetitions of the MAC address (6 bytes each)
|
||||||
|
func IsMagicPacket(data []byte) bool {
|
||||||
|
// Minimum size: 6 (header) + 16*6 (MAC repetitions) = 102 bytes
|
||||||
|
minSize := MagicPacketHeader + (MacAddressRepetitions * MacAddressLength)
|
||||||
|
if len(data) < minSize {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for 6 bytes of 0xFF at the beginning
|
||||||
|
header := data[:MagicPacketHeader]
|
||||||
|
for _, b := range header {
|
||||||
|
if b != 0xFF {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the MAC address (first 6 bytes after header)
|
||||||
|
macAddr := data[MagicPacketHeader : MagicPacketHeader+MacAddressLength]
|
||||||
|
|
||||||
|
// Check if the MAC address is repeated 16 times
|
||||||
|
for i := 1; i < MacAddressRepetitions; i++ {
|
||||||
|
offset := MagicPacketHeader + (i * MacAddressLength)
|
||||||
|
if offset+MacAddressLength > len(data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
repeatedMac := data[offset : offset+MacAddressLength]
|
||||||
|
if !bytes.Equal(macAddr, repeatedMac) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractAuthFromPacket extracts username and password from an authenticated packet
|
||||||
|
// Format: "AUTH:username:password:" followed by standard magic packet
|
||||||
|
// Returns username, password, magicPacketData, and success status
|
||||||
|
func ExtractAuthFromPacket(data []byte) (string, string, []byte, bool) {
|
||||||
|
dataStr := string(data)
|
||||||
|
|
||||||
|
// Check for authentication prefix
|
||||||
|
if !strings.HasPrefix(dataStr, AuthPrefix) {
|
||||||
|
return "", "", nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract authentication part
|
||||||
|
authEnd := strings.Index(dataStr[len(AuthPrefix):], ":")
|
||||||
|
if authEnd == -1 {
|
||||||
|
return "", "", nil, false
|
||||||
|
}
|
||||||
|
authEnd += len(AuthPrefix)
|
||||||
|
|
||||||
|
// Extract username and password
|
||||||
|
authPart := dataStr[len(AuthPrefix):authEnd]
|
||||||
|
parts := strings.Split(authPart, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return "", "", nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
username := parts[0]
|
||||||
|
password := parts[1]
|
||||||
|
|
||||||
|
// Extract magic packet part (after "AUTH:username:password:")
|
||||||
|
magicPacketStart := authEnd + 1
|
||||||
|
if magicPacketStart >= len(data) {
|
||||||
|
return "", "", nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
magicPacketData := data[magicPacketStart:]
|
||||||
|
|
||||||
|
return username, password, magicPacketData, true
|
||||||
|
}
|
||||||
59
main.go
Normal file
59
main.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"wol-sol-agent/auth"
|
||||||
|
"wol-sol-agent/config"
|
||||||
|
"wol-sol-agent/server"
|
||||||
|
"wol-sol-agent/utils"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := config.Load()
|
||||||
|
pseudonymizer := masterlog.NewPseudonymizerFromString(utils.GetStringFromEnv("MASTERLOG_PSEUDONYMIZER", "high-secure-secret"))
|
||||||
|
masterlog.SetPseudonymizer(pseudonymizer)
|
||||||
|
masterlog.SetLevel(masterlog.Level(utils.GetIntFromEnv("MASTERLOG_LEVEL", int(masterlog.LevelInfo))))
|
||||||
|
masterlog.AddSensitiveFields("AUTH_USER", "AUTH_PASS")
|
||||||
|
masterlog.Info("Authentication enabled")
|
||||||
|
|
||||||
|
authenticator := auth.New(cfg.AuthUser, cfg.AuthPass)
|
||||||
|
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
// Use os.Interrupt which works on all platforms (Windows, Linux, macOS)
|
||||||
|
signal.Notify(sigChan, os.Interrupt)
|
||||||
|
|
||||||
|
udpServer := server.NewUDPServer(cfg.SOLPort, authenticator)
|
||||||
|
udpDone := make(chan bool)
|
||||||
|
go udpServer.Start(udpDone)
|
||||||
|
|
||||||
|
var apiDone chan bool
|
||||||
|
if cfg.APIEnabled {
|
||||||
|
apiServer := server.NewAPIServer(cfg.APIPort, authenticator)
|
||||||
|
apiDone = make(chan bool)
|
||||||
|
go apiServer.Start(apiDone)
|
||||||
|
}
|
||||||
|
|
||||||
|
masterlog.Info("Starting servers", map[string]interface{}{
|
||||||
|
"SOL_PORT": cfg.SOLPort,
|
||||||
|
"API_PORT": cfg.APIPort,
|
||||||
|
"API_ENABLED": cfg.APIEnabled,
|
||||||
|
})
|
||||||
|
|
||||||
|
<-sigChan
|
||||||
|
masterlog.Info("Shutting down", map[string]interface{}{
|
||||||
|
"SOL_PORT": cfg.SOLPort,
|
||||||
|
"API_PORT": cfg.APIPort,
|
||||||
|
"API_ENABLED": cfg.APIEnabled,
|
||||||
|
})
|
||||||
|
|
||||||
|
close(udpDone)
|
||||||
|
if apiDone != nil {
|
||||||
|
close(apiDone)
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
91
server/api.go
Normal file
91
server/api.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"wol-sol-agent/auth"
|
||||||
|
"wol-sol-agent/system"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// APIServer handles HTTP API requests
|
||||||
|
type APIServer struct {
|
||||||
|
port int
|
||||||
|
authenticator *auth.Authenticator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAPIServer creates a new API server
|
||||||
|
func NewAPIServer(port int, authenticator *auth.Authenticator) *APIServer {
|
||||||
|
return &APIServer{
|
||||||
|
port: port,
|
||||||
|
authenticator: authenticator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts the API server
|
||||||
|
func (s *APIServer) Start(done chan bool) {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/shutdown", s.handleShutdown)
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%d", s.port),
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
|
||||||
|
masterlog.Info("API server listening on port", map[string]interface{}{
|
||||||
|
"port": s.port,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start server in goroutine
|
||||||
|
go func() {
|
||||||
|
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
masterlog.Error("API server error", map[string]interface{}{
|
||||||
|
"error": err,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for shutdown signal
|
||||||
|
<-done
|
||||||
|
server.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *APIServer) handleShutdown(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Check authentication
|
||||||
|
username, password, ok := r.BasicAuth()
|
||||||
|
if !ok {
|
||||||
|
w.Header().Set("WWW-Authenticate", `Basic realm="SOL Agent"`)
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
masterlog.Error("Unauthorized API request", map[string]interface{}{
|
||||||
|
"address": r.RemoteAddr,
|
||||||
|
"error": "no auth",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify credentials
|
||||||
|
if !s.authenticator.Verify(username, password) {
|
||||||
|
w.Header().Set("WWW-Authenticate", `Basic realm="SOL Agent"`)
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
masterlog.Error("Unauthorized API request", map[string]interface{}{
|
||||||
|
"address": r.RemoteAddr,
|
||||||
|
"error": "invalid credentials",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
masterlog.Info("Authenticated shutdown request", map[string]interface{}{
|
||||||
|
"address": r.RemoteAddr,
|
||||||
|
})
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(w, "Shutting down system...\n")
|
||||||
|
|
||||||
|
// Shutdown in a goroutine to allow response to be sent
|
||||||
|
go func() {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
system.Shutdown()
|
||||||
|
}()
|
||||||
|
}
|
||||||
107
server/udp.go
Normal file
107
server/udp.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"wol-sol-agent/auth"
|
||||||
|
"wol-sol-agent/magicpacket"
|
||||||
|
"wol-sol-agent/system"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UDPServer handles UDP connections for Magic Packets
|
||||||
|
type UDPServer struct {
|
||||||
|
port int
|
||||||
|
authenticator *auth.Authenticator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUDPServer creates a new UDP server
|
||||||
|
func NewUDPServer(port int, authenticator *auth.Authenticator) *UDPServer {
|
||||||
|
return &UDPServer{
|
||||||
|
port: port,
|
||||||
|
authenticator: authenticator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts the UDP server
|
||||||
|
func (s *UDPServer) Start(done chan bool) {
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", s.port))
|
||||||
|
if err != nil {
|
||||||
|
masterlog.Error("Failed to resolve UDP address", map[string]interface{}{
|
||||||
|
"error": err,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.ListenUDP("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
masterlog.Error("Failed to listen on UDP port", map[string]interface{}{
|
||||||
|
"error": err,
|
||||||
|
"port": s.port,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
masterlog.Info("UDP server listening on port", map[string]interface{}{
|
||||||
|
"port": s.port,
|
||||||
|
})
|
||||||
|
|
||||||
|
buffer := make([]byte, 2048)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||||
|
n, clientAddr, err := conn.ReadFromUDP(buffer)
|
||||||
|
if err != nil {
|
||||||
|
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
masterlog.Error("Error reading UDP packet", map[string]interface{}{
|
||||||
|
"error": err,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
masterlog.Info("Received UDP packet", map[string]interface{}{
|
||||||
|
"bytes": n,
|
||||||
|
"address": clientAddr,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Check authentication and magic packet
|
||||||
|
if s.isValidAuthenticatedMagicPacket(buffer[:n]) {
|
||||||
|
masterlog.Info("Authenticated Magic Packet detected! Shutting down system...", map[string]interface{}{
|
||||||
|
"address": clientAddr,
|
||||||
|
})
|
||||||
|
system.Shutdown()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
masterlog.Error("Invalid or unauthenticated packet", map[string]interface{}{
|
||||||
|
"address": clientAddr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidAuthenticatedMagicPacket checks if the packet contains valid authentication
|
||||||
|
// followed by a valid Magic Packet
|
||||||
|
func (s *UDPServer) isValidAuthenticatedMagicPacket(data []byte) bool {
|
||||||
|
username, password, magicPacketData, ok := magicpacket.ExtractAuthFromPacket(data)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify authentication
|
||||||
|
if !s.authenticator.Verify(username, password) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a valid magic packet
|
||||||
|
return magicpacket.IsMagicPacket(magicPacketData)
|
||||||
|
}
|
||||||
18
system/shutdown.go
Normal file
18
system/shutdown.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Shutdown shuts down the system (Unix/Linux/macOS)
|
||||||
|
func Shutdown() {
|
||||||
|
cmd := exec.Command("shutdown", "-h", "now")
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Fatalf("Failed to shutdown system: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
18
system/shutdown_windows.go
Normal file
18
system/shutdown_windows.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Shutdown shuts down the system (Windows)
|
||||||
|
func Shutdown() {
|
||||||
|
cmd := exec.Command("shutdown", "/s", "/t", "0")
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Fatalf("Failed to shutdown system: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
50
utils/env.go
Normal file
50
utils/env.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetEnv(key string, defaultValue string) string {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if value == "" {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStringFromEnv(key string, defaultValue string) string {
|
||||||
|
return GetEnv(key, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetIntFromEnv(key string, defaultValue int) int {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if value == "" {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
intValue, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return intValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBoolFromEnv(key string, defaultValue bool) bool {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if value == "" {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
if value == "true" || value == "TRUE" || value == "True" || value == "1" {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
masterlog.Error("Invalid environment variable value", map[string]interface{}{
|
||||||
|
"variable": key,
|
||||||
|
"value": value,
|
||||||
|
"type": "boolean",
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user