package config import ( "errors" "fmt" "net" "os" "strconv" "strings" "git.ipao.vip/rogee/go-sip/internal/tenant" ) // AgentEnvironment is deployment-owned and is inspected without opening any // durable file, media adapter, credential or network connection. type AgentEnvironment struct { AgentID string CellID string DispatcherID string Listen string SessionPath string RecoveryRoot string DispatcherEndpoint string DispatcherServerName string CAFile string CertFile string KeyFile string PeerFingerprints map[string]struct{} MockScenarioFile string MockAppliedSIPFile string } func LoadAgentEnvironment(mode string) (AgentEnvironment, error) { if mode != "mock" { return AgentEnvironment{}, errors.New("Agent accepts only isolated Mock mode") } get := func(name string) (string, error) { value := os.Getenv(name) if strings.TrimSpace(value) == "" { return "", fmt.Errorf("%s is required", name) } return value, nil } var settings AgentEnvironment for _, field := range []struct { name string value *string }{ {"AGENT_ID", &settings.AgentID}, {"CELL_ID", &settings.CellID}, {"DISPATCHER_ID", &settings.DispatcherID}, {"AGENT_GRPC_LISTEN", &settings.Listen}, {"AGENT_SESSION_PATH", &settings.SessionPath}, {"AGENT_RECOVERY_ROOT", &settings.RecoveryRoot}, {"DISPATCHER_GRPC_ENDPOINT", &settings.DispatcherEndpoint}, {"DISPATCHER_GRPC_SERVER_NAME", &settings.DispatcherServerName}, {"MTLS_CA_FILE", &settings.CAFile}, {"MTLS_CERT_FILE", &settings.CertFile}, {"MTLS_KEY_FILE", &settings.KeyFile}, {"AGENT_MOCK_SCENARIO_FILE", &settings.MockScenarioFile}, {"AGENT_MOCK_APPLIED_SIP_FILE", &settings.MockAppliedSIPFile}, } { value, err := get(field.name) if err != nil { return AgentEnvironment{}, err } *field.value = value } if tenant.ValidateDispatcherID(settings.DispatcherID) != nil { return AgentEnvironment{}, errors.New("DISPATCHER_ID must be a canonical UUID v4") } if !localGRPCAddress(settings.Listen, true) { return AgentEnvironment{}, errors.New("AGENT_GRPC_LISTEN must be an isolated local Mock address") } if !localGRPCAddress(settings.DispatcherEndpoint, false) { return AgentEnvironment{}, errors.New("DISPATCHER_GRPC_ENDPOINT must be an isolated local Mock address") } rawFingerprints, err := get("MTLS_PEER_CERT_FINGERPRINTS") if err != nil { return AgentEnvironment{}, err } settings.PeerFingerprints, err = ParseCertificateFingerprints(rawFingerprints) if err != nil || len(settings.PeerFingerprints) == 0 { return AgentEnvironment{}, errors.New("MTLS_PEER_CERT_FINGERPRINTS is invalid") } return settings, nil } func localGRPCAddress(raw string, allowZeroPort bool) bool { host, port, err := net.SplitHostPort(raw) if err != nil { return false } portNumber, err := strconv.Atoi(port) if err != nil || portNumber < 0 || portNumber > 65535 || (!allowZeroPort && portNumber == 0) { return false } if strings.EqualFold(host, "localhost") { return true } ip := net.ParseIP(host) return ip != nil && ip.IsLoopback() }