refactor: Simplify configuration management and remove unused features
- Removed the generateRandomSecret function and related JWT secret generation logic. - Eliminated autoRenew and CacheSize fields from ReverseProxyConfig. - Updated applyDefaultsToConfig to streamline default value application. - Enhanced logging for applied default values and configuration loading. - Cleaned up environment variable handling for configuration settings.
- commit
- 3ac0ad3e076500649c4ca426d72f2db21d90c146
- author
- bamen · · 2025-03-29 08:57 UTC
- parent
- 31d3492
- signature
- unsigned
2 files · +291 -369
M internal/common/init_config.go +19 -369
| @@ -7,27 +7,14 @@ import ( | |||
| 7 | 7 | "os" | |
| 8 | 8 | "path/filepath" | |
| 9 | 9 | "runtime" | |
| 10 | - | "strconv" | |
| 11 | 10 | "strings" | |
| 12 | 11 | "sync" | |
| 13 | 12 | ||
| 14 | - | "crypto/rand" | |
| 15 | - | "encoding/hex" | |
| 16 | - | ||
| 17 | 13 | "github.com/bnema/gordon/pkg/docker" | |
| 18 | 14 | "github.com/bnema/gordon/pkg/logger" | |
| 19 | 15 | "github.com/bnema/gordon/pkg/parser" | |
| 20 | 16 | ) | |
| 21 | 17 | ||
| 22 | - | // generateRandomSecret generates a secure random string of the specified byte length, hex-encoded. | |
| 23 | - | func generateRandomSecret(length int) (string, error) { | |
| 24 | - | bytes := make([]byte, length) | |
| 25 | - | if _, err := rand.Read(bytes); err != nil { | |
| 26 | - | return "", err | |
| 27 | - | } | |
| 28 | - | return hex.EncodeToString(bytes), nil | |
| 29 | - | } | |
| 30 | - | ||
| 31 | 18 | var ( | |
| 32 | 19 | globalConfig *Config | |
| 33 | 20 | globalConfigOnce sync.Once | |
| @@ -94,9 +81,7 @@ type ReverseProxyConfig struct { | |||
| 94 | 81 | LetsEncryptMode string `yaml:"letsEncryptMode"` // staging or production | |
| 95 | 82 | SkipCertificates bool `yaml:"skipCertificates"` // Skip Let's Encrypt certificate acquisition when behind a TLS terminating proxy | |
| 96 | 83 | GracePeriod int `yaml:"gracePeriod"` // Shutdown grace period in seconds | |
| 97 | - | AutoRenew bool `yaml:"autoRenew"` // Whether to automatically renew certificates | |
| 98 | 84 | RenewBefore int `yaml:"renewBefore"` // Days before expiry to renew certificates | |
| 99 | - | CacheSize int `yaml:"cacheSize"` // Size of the certificate cache | |
| 100 | 85 | EnableHttpLogs bool `yaml:"enableHttpLogs"` // Whether to enable HTTP request logging | |
| 101 | 86 | EnableRateLimit bool `yaml:"enableRateLimit"` // Whether to enable rate limiting middleware | |
| 102 | 87 | DetectUpstreamProxy bool `yaml:"detectUpstreamProxy"` // Whether to detect and handle upstream TLS termination proxies | |
| @@ -108,21 +93,24 @@ type ReverseProxyConfig struct { | |||
| 108 | 93 | ||
| 109 | 94 | // Default values | |
| 110 | 95 | var ( | |
| 111 | - | sock = "/var/run/docker.sock" | |
| 112 | - | podmansock = "/run/podman/podman.sock" | |
| 113 | - | reverseProxyPort = "443" // Changed to 443 for standard HTTPS | |
| 114 | - | httpPort = "80" // Standard HTTP port | |
| 115 | - | certDir = "/certs" | |
| 116 | - | letsEncryptMode = "staging" | |
| 117 | - | autoRenew = true | |
| 118 | - | proxyEnabled = true // Default to enabled for backward compatibility | |
| 119 | - | renewBefore = 30 // days | |
| 120 | - | cacheSize = 1000 // entries | |
| 121 | - | gracePeriod = 30 // seconds | |
| 122 | - | defaultLogLevel = "info" // Default log level | |
| 123 | - | disableRateLimit = false | |
| 124 | - | detectUpstreamProxy = false // Default to disabled | |
| 125 | - | skipCertificates = false // Default to disabled | |
| 96 | + | sock = "/var/run/docker.sock" | |
| 97 | + | podmansock = "/run/podman/podman.sock" | |
| 98 | + | reverseProxyPort = "443" // Changed to 443 for standard HTTPS | |
| 99 | + | httpPort = "80" // Standard HTTP port | |
| 100 | + | certDir = "/certs" | |
| 101 | + | letsEncryptMode = "staging" | |
| 102 | + | proxyEnabled = true // Default to enabled for backward compatibility | |
| 103 | + | renewBefore = 30 // days | |
| 104 | + | gracePeriod = 30 // seconds | |
| 105 | + | defaultLogLevel = "info" // Default log level | |
| 106 | + | disableRateLimit = false | |
| 107 | + | detectUpstreamProxy = false // Default to disabled | |
| 108 | + | skipCertificates = false // Default to disabled | |
| 109 | + | enableRateLimit = false // Default to disabled | |
| 110 | + | defaultChallengeType = "http-01" | |
| 111 | + | defaultHttpChallengePort = "80" | |
| 112 | + | defaultDnsChallengePropagationTimeout = 60 // seconds | |
| 113 | + | defaultDnsChallengePollingInterval = 5 // seconds | |
| 126 | 114 | ) | |
| 127 | 115 | ||
| 128 | 116 | // applyDefaultsToConfig applies default values to any fields that have zero values | |
| @@ -208,11 +196,6 @@ func applyDefaultsToConfig(config *Config) bool { | |||
| 208 | 196 | logger.Debug("Applied default value for ReverseProxy.LetsEncryptMode", "value", letsEncryptMode) | |
| 209 | 197 | defaultsApplied = true | |
| 210 | 198 | } | |
| 211 | - | if config.ReverseProxy.CacheSize == 0 { | |
| 212 | - | config.ReverseProxy.CacheSize = cacheSize | |
| 213 | - | logger.Debug("Applied default value for ReverseProxy.CacheSize", "value", cacheSize) | |
| 214 | - | defaultsApplied = true | |
| 215 | - | } | |
| 216 | 199 | if config.ReverseProxy.GracePeriod == 0 { | |
| 217 | 200 | config.ReverseProxy.GracePeriod = gracePeriod | |
| 218 | 201 | logger.Debug("Applied default value for ReverseProxy.GracePeriod", "value", gracePeriod) | |
| @@ -266,34 +249,6 @@ func applyDefaultsToConfig(config *Config) bool { | |||
| 266 | 249 | defaultsApplied = true | |
| 267 | 250 | } | |
| 268 | 251 | ||
| 269 | - | // Handle autoRenew | |
| 270 | - | configFilePath = filepath.Join(getConfigDirMustExist(), "config.yml") | |
| 271 | - | if fileExists(configFilePath) { | |
| 272 | - | yamlContent, err := os.ReadFile(configFilePath) | |
| 273 | - | if err == nil { | |
| 274 | - | // Check if "autoRenew" is explicitly mentioned in the config file | |
| 275 | - | if !strings.Contains(string(yamlContent), "autoRenew:") { | |
| 276 | - | config.ReverseProxy.AutoRenew = autoRenew | |
| 277 | - | logger.Debug("Applied default value for ReverseProxy.AutoRenew", "value", autoRenew) | |
| 278 | - | defaultsApplied = true | |
| 279 | - | } else { | |
| 280 | - | logger.Debug("Keeping explicit value for ReverseProxy.AutoRenew", "value", config.ReverseProxy.AutoRenew) | |
| 281 | - | } | |
| 282 | - | } else { | |
| 283 | - | // If we can't read the file for some reason, apply the default | |
| 284 | - | if !config.ReverseProxy.AutoRenew { | |
| 285 | - | config.ReverseProxy.AutoRenew = autoRenew | |
| 286 | - | logger.Debug("Applied default value for ReverseProxy.AutoRenew", "value", autoRenew) | |
| 287 | - | defaultsApplied = true | |
| 288 | - | } | |
| 289 | - | } | |
| 290 | - | } else { | |
| 291 | - | // If config file doesn't exist yet, set the default | |
| 292 | - | config.ReverseProxy.AutoRenew = autoRenew | |
| 293 | - | logger.Debug("Applied default value for ReverseProxy.AutoRenew", "value", autoRenew) | |
| 294 | - | defaultsApplied = true | |
| 295 | - | } | |
| 296 | - | ||
| 297 | 252 | return defaultsApplied | |
| 298 | 253 | } | |
| 299 | 254 | ||
| @@ -447,6 +402,7 @@ func getConfigDir() (string, error) { | |||
| 447 | 402 | return configDir, nil | |
| 448 | 403 | } | |
| 449 | 404 | ||
| 405 | + | // readAndUnmarshalConfig reads and unmarshals a configuration file using the provided filesystem and path. | |
| 450 | 406 | func readAndUnmarshalConfig(fs fs.FS, filePath string, config *Config) error { | |
| 451 | 407 | logger.Debug("readAndUnmarshalConfig: Starting to read and unmarshal config file", "path", filePath) | |
| 452 | 408 | err := parser.ParseYAMLFile(fs, filePath, config) | |
| @@ -458,282 +414,6 @@ func readAndUnmarshalConfig(fs fs.FS, filePath string, config *Config) error { | |||
| 458 | 414 | return nil | |
| 459 | 415 | } | |
| 460 | 416 | ||
| 461 | - | // loadConfigFromEnv loads configuration from environment variables | |
| 462 | - | func loadConfigFromEnv(config *Config, printLogs bool) { | |
| 463 | - | // General Configuration | |
| 464 | - | if val := os.Getenv("GORDON_STORAGE_DIR"); val != "" { | |
| 465 | - | config.General.StorageDir = val | |
| 466 | - | if printLogs { | |
| 467 | - | logger.Info("Using environment variable GORDON_STORAGE_DIR", "value", val) | |
| 468 | - | } | |
| 469 | - | } | |
| 470 | - | // Removed GORDON_TOKEN handling | |
| 471 | - | if val := os.Getenv("GORDON_LOG_LEVEL"); val != "" { | |
| 472 | - | config.General.LogLevel = val | |
| 473 | - | if printLogs { | |
| 474 | - | logger.Info("Using environment variable GORDON_LOG_LEVEL", "value", val) | |
| 475 | - | } | |
| 476 | - | } | |
| 477 | - | ||
| 478 | - | // HTTP Configuration | |
| 479 | - | if val := os.Getenv("GORDON_HTTP_PORT"); val != "" { | |
| 480 | - | config.Http.Port = val | |
| 481 | - | if printLogs { | |
| 482 | - | logger.Info("Using environment variable GORDON_HTTP_PORT", "value", val) | |
| 483 | - | } | |
| 484 | - | } | |
| 485 | - | if val := os.Getenv("GORDON_HTTP_DOMAIN"); val != "" { | |
| 486 | - | config.Http.Domain = val | |
| 487 | - | if printLogs { | |
| 488 | - | logger.Info("Using environment variable GORDON_HTTP_DOMAIN", "value", val) | |
| 489 | - | } | |
| 490 | - | } | |
| 491 | - | if val := os.Getenv("GORDON_HTTP_SUBDOMAIN"); val != "" { | |
| 492 | - | config.Http.SubDomain = val | |
| 493 | - | if printLogs { | |
| 494 | - | logger.Info("Using environment variable GORDON_HTTP_SUBDOMAIN", "value", val) | |
| 495 | - | } | |
| 496 | - | } | |
| 497 | - | if val := os.Getenv("GORDON_HTTP_BACKEND_URL"); val != "" { | |
| 498 | - | config.Http.BackendURL = val | |
| 499 | - | if printLogs { | |
| 500 | - | logger.Info("Using environment variable GORDON_HTTP_BACKEND_URL", "value", val) | |
| 501 | - | } | |
| 502 | - | } | |
| 503 | - | if val := os.Getenv("GORDON_HTTP_HTTPS"); val != "" { | |
| 504 | - | config.Http.Https = strings.ToLower(val) == "true" | |
| 505 | - | if printLogs { | |
| 506 | - | logger.Info("Using environment variable GORDON_HTTP_HTTPS", "value", config.Http.Https) | |
| 507 | - | } | |
| 508 | - | } | |
| 509 | - | ||
| 510 | - | // Admin Configuration | |
| 511 | - | if val := os.Getenv("GORDON_ADMIN_PATH"); val != "" { | |
| 512 | - | config.Admin.Path = val | |
| 513 | - | if printLogs { | |
| 514 | - | logger.Info("Using environment variable GORDON_ADMIN_PATH", "value", val) | |
| 515 | - | } | |
| 516 | - | } | |
| 517 | - | ||
| 518 | - | // Container Engine Configuration | |
| 519 | - | if val := os.Getenv("GORDON_CONTAINER_SOCK"); val != "" { | |
| 520 | - | config.ContainerEngine.Sock = val | |
| 521 | - | if printLogs { | |
| 522 | - | logger.Info("Using environment variable GORDON_CONTAINER_SOCK", "value", val) | |
| 523 | - | } | |
| 524 | - | } | |
| 525 | - | if val := os.Getenv("GORDON_CONTAINER_PODMANSOCK"); val != "" { | |
| 526 | - | config.ContainerEngine.PodmanSock = val | |
| 527 | - | if printLogs { | |
| 528 | - | logger.Info("Using environment variable GORDON_CONTAINER_PODMANSOCK", "value", val) | |
| 529 | - | } | |
| 530 | - | } | |
| 531 | - | if val := os.Getenv("GORDON_CONTAINER_PODMAN"); val != "" { | |
| 532 | - | config.ContainerEngine.Podman = strings.ToLower(val) == "true" | |
| 533 | - | if printLogs { | |
| 534 | - | logger.Info("Using environment variable GORDON_CONTAINER_PODMAN", "value", config.ContainerEngine.Podman) | |
| 535 | - | } | |
| 536 | - | } else { | |
| 537 | - | // Auto-detect Podman if not specified in environment | |
| 538 | - | isPodman, podmanSocket := docker.DetectPodman() | |
| 539 | - | if isPodman { | |
| 540 | - | config.ContainerEngine.Podman = true | |
| 541 | - | config.ContainerEngine.PodmanSock = podmanSocket | |
| 542 | - | if printLogs { | |
| 543 | - | logger.Info("Automatically detected Podman installation", | |
| 544 | - | "using_podman", true, | |
| 545 | - | "socket", podmanSocket) | |
| 546 | - | } | |
| 547 | - | } | |
| 548 | - | ||
| 549 | - | // If we're in a container, check for special socket naming patterns | |
| 550 | - | if docker.IsRunningInContainer() && fileExists(config.ContainerEngine.Sock) { | |
| 551 | - | // Check if socket path contains "podman" which indicates it might be a podman socket | |
| 552 | - | if strings.Contains(config.ContainerEngine.Sock, "podman") { | |
| 553 | - | config.ContainerEngine.Podman = true | |
| 554 | - | if printLogs { | |
| 555 | - | logger.Info("Socket path contains 'podman', enabling Podman mode", "path", config.ContainerEngine.Sock) | |
| 556 | - | } | |
| 557 | - | } | |
| 558 | - | } | |
| 559 | - | } | |
| 560 | - | if val := os.Getenv("GORDON_CONTAINER_NETWORK"); val != "" { | |
| 561 | - | config.ContainerEngine.Network = val | |
| 562 | - | if printLogs { | |
| 563 | - | logger.Info("Using environment variable GORDON_CONTAINER_NETWORK", "value", val) | |
| 564 | - | } | |
| 565 | - | } | |
| 566 | - | ||
| 567 | - | // Use Podman socket if Podman is enabled and no Docker socket is specified | |
| 568 | - | if config.ContainerEngine.Podman && config.ContainerEngine.Sock == "" && config.ContainerEngine.PodmanSock != "" { | |
| 569 | - | config.ContainerEngine.Sock = config.ContainerEngine.PodmanSock | |
| 570 | - | if printLogs { | |
| 571 | - | logger.Info("Setting ContainerEngine.Sock to PodmanSock value", "value", config.ContainerEngine.Sock) | |
| 572 | - | } | |
| 573 | - | } | |
| 574 | - | ||
| 575 | - | // Additional debug info for container sockets | |
| 576 | - | if docker.IsRunningInContainer() { | |
| 577 | - | logger.Debug("Container socket configuration", | |
| 578 | - | "sock", config.ContainerEngine.Sock, | |
| 579 | - | "podmansock", config.ContainerEngine.PodmanSock, | |
| 580 | - | "podman", config.ContainerEngine.Podman, | |
| 581 | - | "sock_exists", fileExists(config.ContainerEngine.Sock), | |
| 582 | - | "podmansock_exists", fileExists(config.ContainerEngine.PodmanSock)) | |
| 583 | - | ||
| 584 | - | // Special handling for container environment to ensure sockets are properly set | |
| 585 | - | // If no socket is configured but default socket path exists, use it | |
| 586 | - | if config.ContainerEngine.Sock == "" && fileExists(sock) { | |
| 587 | - | config.ContainerEngine.Sock = sock | |
| 588 | - | logger.Info("Found default Docker socket in container, using it", "path", sock) | |
| 589 | - | } | |
| 590 | - | ||
| 591 | - | // If podman socket exists but podman mode is not enabled, check if we should enable it | |
| 592 | - | if fileExists(podmansock) && !config.ContainerEngine.Podman { | |
| 593 | - | // If the main socket path contains "podman", enable podman mode | |
| 594 | - | if strings.Contains(config.ContainerEngine.Sock, "podman") { | |
| 595 | - | config.ContainerEngine.Podman = true | |
| 596 | - | logger.Info("Socket path suggests Podman usage, enabling Podman mode", "path", config.ContainerEngine.Sock) | |
| 597 | - | } | |
| 598 | - | } | |
| 599 | - | } | |
| 600 | - | ||
| 601 | - | // Reverse Proxy Configuration | |
| 602 | - | if val := os.Getenv("GORDON_PROXY_PORT"); val != "" { | |
| 603 | - | config.ReverseProxy.Port = val | |
| 604 | - | if printLogs { | |
| 605 | - | logger.Info("Using environment variable GORDON_PROXY_PORT", "value", val) | |
| 606 | - | } | |
| 607 | - | } | |
| 608 | - | if val := os.Getenv("GORDON_PROXY_HTTP_PORT"); val != "" { | |
| 609 | - | config.ReverseProxy.HttpPort = val | |
| 610 | - | if printLogs { | |
| 611 | - | logger.Info("Using environment variable GORDON_PROXY_HTTP_PORT", "value", val) | |
| 612 | - | } | |
| 613 | - | } | |
| 614 | - | ||
| 615 | - | // New environment variables for reverse proxy port control | |
| 616 | - | if val := os.Getenv("GORDON_USE_FALLBACK_BINDING"); val != "" { | |
| 617 | - | // This is handled at runtime in the proxy server but we log it here | |
| 618 | - | if printLogs { | |
| 619 | - | logger.Info("Found environment variable GORDON_USE_FALLBACK_BINDING", "value", val) | |
| 620 | - | } | |
| 621 | - | } | |
| 622 | - | ||
| 623 | - | // Handle GORDON_PROXY_ENABLED environment variable | |
| 624 | - | if val := os.Getenv("GORDON_PROXY_ENABLED"); val != "" { | |
| 625 | - | enabled, err := strconv.ParseBool(val) | |
| 626 | - | if err == nil { | |
| 627 | - | config.ReverseProxy.Enabled = enabled | |
| 628 | - | } | |
| 629 | - | if printLogs { | |
| 630 | - | logger.Info("Using environment variable GORDON_PROXY_ENABLED", "value", config.ReverseProxy.Enabled) | |
| 631 | - | } | |
| 632 | - | } | |
| 633 | - | if val := os.Getenv("GORDON_PROXY_CERT_DIR"); val != "" { | |
| 634 | - | config.ReverseProxy.CertDir = val | |
| 635 | - | if printLogs { | |
| 636 | - | logger.Info("Using environment variable GORDON_PROXY_CERT_DIR", "value", val) | |
| 637 | - | } | |
| 638 | - | } | |
| 639 | - | if val := os.Getenv("GORDON_PROXY_AUTO_RENEW"); val != "" { | |
| 640 | - | config.ReverseProxy.AutoRenew = strings.ToLower(val) == "true" | |
| 641 | - | if printLogs { | |
| 642 | - | logger.Info("Using environment variable GORDON_PROXY_AUTO_RENEW", "value", config.ReverseProxy.AutoRenew) | |
| 643 | - | } | |
| 644 | - | } | |
| 645 | - | if val := os.Getenv("GORDON_PROXY_RENEW_BEFORE"); val != "" { | |
| 646 | - | if i, err := strconv.Atoi(val); err == nil { | |
| 647 | - | config.ReverseProxy.RenewBefore = i | |
| 648 | - | if printLogs { | |
| 649 | - | logger.Info("Using environment variable GORDON_PROXY_RENEW_BEFORE", "value", i) | |
| 650 | - | } | |
| 651 | - | } | |
| 652 | - | } | |
| 653 | - | if val := os.Getenv("GORDON_PROXY_LETSENCRYPT_MODE"); val != "" { | |
| 654 | - | config.ReverseProxy.LetsEncryptMode = val | |
| 655 | - | if printLogs { | |
| 656 | - | logger.Info("Using environment variable GORDON_PROXY_LETSENCRYPT_MODE", "value", val) | |
| 657 | - | } | |
| 658 | - | } | |
| 659 | - | if val := os.Getenv("GORDON_PROXY_EMAIL"); val != "" { | |
| 660 | - | config.ReverseProxy.Email = val | |
| 661 | - | if printLogs { | |
| 662 | - | logger.Info("Using environment variable GORDON_PROXY_EMAIL", "value", val) | |
| 663 | - | } | |
| 664 | - | } | |
| 665 | - | if val := os.Getenv("GORDON_PROXY_CACHE_SIZE"); val != "" { | |
| 666 | - | if i, err := strconv.Atoi(val); err == nil { | |
| 667 | - | config.ReverseProxy.CacheSize = i | |
| 668 | - | if printLogs { | |
| 669 | - | logger.Info("Using environment variable GORDON_PROXY_CACHE_SIZE", "value", i) | |
| 670 | - | } | |
| 671 | - | } | |
| 672 | - | } | |
| 673 | - | if val := os.Getenv("GORDON_PROXY_GRACE_PERIOD"); val != "" { | |
| 674 | - | i, err := strconv.Atoi(val) | |
| 675 | - | if err == nil { | |
| 676 | - | config.ReverseProxy.GracePeriod = i | |
| 677 | - | } | |
| 678 | - | logger.Info("Using environment variable GORDON_PROXY_GRACE_PERIOD", "value", i) | |
| 679 | - | } | |
| 680 | - | ||
| 681 | - | // Handle GORDON_PROXY_ENABLE_HTTP_LOGS environment variable | |
| 682 | - | if val := os.Getenv("GORDON_PROXY_ENABLE_HTTP_LOGS"); val != "" { | |
| 683 | - | enableLogs, err := strconv.ParseBool(val) | |
| 684 | - | if err == nil { | |
| 685 | - | config.ReverseProxy.EnableHttpLogs = enableLogs | |
| 686 | - | } | |
| 687 | - | logger.Info("Using environment variable GORDON_PROXY_ENABLE_HTTP_LOGS", "value", config.ReverseProxy.EnableHttpLogs) | |
| 688 | - | } else if val := os.Getenv("GORDON_PROXY_ENABLE_LOGS"); val != "" { | |
| 689 | - | // For backward compatibility | |
| 690 | - | enableLogs, err := strconv.ParseBool(val) | |
| 691 | - | if err == nil { | |
| 692 | - | config.ReverseProxy.EnableHttpLogs = enableLogs | |
| 693 | - | } | |
| 694 | - | logger.Info("Using environment variable GORDON_PROXY_ENABLE_LOGS (deprecated, use GORDON_PROXY_ENABLE_HTTP_LOGS)", "value", config.ReverseProxy.EnableHttpLogs) | |
| 695 | - | } | |
| 696 | - | ||
| 697 | - | // Handle GORDON_PROXY_ENABLE_RATE_LIMIT environment variable | |
| 698 | - | if val := os.Getenv("GORDON_PROXY_ENABLE_RATE_LIMIT"); val != "" { | |
| 699 | - | enableRateLimit, err := strconv.ParseBool(val) | |
| 700 | - | if err == nil { | |
| 701 | - | config.ReverseProxy.EnableRateLimit = enableRateLimit | |
| 702 | - | } | |
| 703 | - | logger.Info("Using environment variable GORDON_PROXY_ENABLE_RATE_LIMIT", "value", config.ReverseProxy.EnableRateLimit) | |
| 704 | - | } else if val := os.Getenv("GORDON_PROXY_DISABLE_RATE_LIMIT"); val != "" { | |
| 705 | - | // For backward compatibility | |
| 706 | - | disableRateLimit, err := strconv.ParseBool(val) | |
| 707 | - | if err == nil { | |
| 708 | - | // Invert the logic | |
| 709 | - | config.ReverseProxy.EnableRateLimit = !disableRateLimit | |
| 710 | - | } | |
| 711 | - | logger.Info("Using environment variable GORDON_PROXY_DISABLE_RATE_LIMIT (deprecated, use GORDON_PROXY_ENABLE_RATE_LIMIT)", "value", !config.ReverseProxy.EnableRateLimit) | |
| 712 | - | } | |
| 713 | - | ||
| 714 | - | // Handle GORDON_PROXY_DETECT_UPSTREAM environment variable | |
| 715 | - | if val := os.Getenv("GORDON_PROXY_DETECT_UPSTREAM"); val != "" { | |
| 716 | - | detectUpstream, err := strconv.ParseBool(val) | |
| 717 | - | if err == nil { | |
| 718 | - | config.ReverseProxy.DetectUpstreamProxy = detectUpstream | |
| 719 | - | } | |
| 720 | - | if printLogs { | |
| 721 | - | logger.Info("Using environment variable GORDON_PROXY_DETECT_UPSTREAM", "value", config.ReverseProxy.DetectUpstreamProxy) | |
| 722 | - | } | |
| 723 | - | } | |
| 724 | - | ||
| 725 | - | // Handle GORDON_PROXY_SKIP_CERTIFICATES environment variable | |
| 726 | - | if val := os.Getenv("GORDON_PROXY_SKIP_CERTIFICATES"); val != "" { | |
| 727 | - | skipCerts, err := strconv.ParseBool(val) | |
| 728 | - | if err == nil { | |
| 729 | - | config.ReverseProxy.SkipCertificates = skipCerts | |
| 730 | - | } | |
| 731 | - | if printLogs { | |
| 732 | - | logger.Info("Using environment variable GORDON_PROXY_SKIP_CERTIFICATES", "value", config.ReverseProxy.SkipCertificates) | |
| 733 | - | } | |
| 734 | - | } | |
| 735 | - | } | |
| 736 | - | ||
| 737 | 417 | func (config *Config) LoadConfig() (*Config, error) { | |
| 738 | 418 | // If we're in a non-interactive environment, use simplified config | |
| 739 | 419 | nonInteractiveEnv := os.Getenv("GORDON_NONINTERACTIVE") | |
| @@ -781,10 +461,8 @@ func (config *Config) LoadConfig() (*Config, error) { | |||
| 781 | 461 | Port: reverseProxyPort, | |
| 782 | 462 | HttpPort: httpPort, | |
| 783 | 463 | CertDir: certDir, | |
| 784 | - | AutoRenew: autoRenew, | |
| 785 | 464 | RenewBefore: renewBefore, | |
| 786 | 465 | LetsEncryptMode: letsEncryptMode, | |
| 787 | - | CacheSize: cacheSize, | |
| 788 | 466 | GracePeriod: gracePeriod, | |
| 789 | 467 | EnableHttpLogs: true, | |
| 790 | 468 | EnableRateLimit: false, | |
| @@ -920,10 +598,8 @@ func (config *Config) LoadConfig() (*Config, error) { | |||
| 920 | 598 | Port: reverseProxyPort, | |
| 921 | 599 | HttpPort: httpPort, | |
| 922 | 600 | CertDir: certDir, | |
| 923 | - | AutoRenew: autoRenew, | |
| 924 | 601 | RenewBefore: renewBefore, | |
| 925 | 602 | LetsEncryptMode: letsEncryptMode, | |
| 926 | - | CacheSize: cacheSize, | |
| 927 | 603 | GracePeriod: gracePeriod, | |
| 928 | 604 | EnableHttpLogs: true, | |
| 929 | 605 | EnableRateLimit: false, | |
| @@ -1027,32 +703,6 @@ func (config *Config) LoadConfig() (*Config, error) { | |||
| 1027 | 703 | logger.Debug("Log level set from configuration", "level", config.General.LogLevel) | |
| 1028 | 704 | } | |
| 1029 | 705 | ||
| 1030 | - | // --- Generate JWT Secret if missing --- | |
| 1031 | - | if config.General.JwtToken == "" { | |
| 1032 | - | logger.Warn("JWTSecret is missing from configuration, generating a new one...") | |
| 1033 | - | newSecret, err := generateRandomSecret(32) // Generate a 256-bit key (32 bytes) | |
| 1034 | - | if err != nil { | |
| 1035 | - | logger.Error("Failed to generate JWT secret", "error", err) | |
| 1036 | - | // Decide if this is a fatal error or if we can continue without JWT functionality | |
| 1037 | - | // For now, let's return the error as JWT is crucial for API auth | |
| 1038 | - | return nil, fmt.Errorf("failed to generate JWT secret: %w", err) | |
| 1039 | - | } | |
| 1040 | - | config.General.JwtToken = newSecret | |
| 1041 | - | logger.Info("Generated new JWTSecret") | |
| 1042 | - | ||
| 1043 | - | // Save the config immediately to store the new secret | |
| 1044 | - | err = config.SaveConfig() | |
| 1045 | - | if err != nil { | |
| 1046 | - | logger.Error("Failed to save configuration after generating JWT secret", "error", err) | |
| 1047 | - | // Return error as saving failed | |
| 1048 | - | return nil, fmt.Errorf("failed to save config with new JWT secret: %w", err) | |
| 1049 | - | } | |
| 1050 | - | logger.Info("Saved configuration with new JWTSecret") | |
| 1051 | - | } else { | |
| 1052 | - | logger.Debug("JWTSecret found in configuration") | |
| 1053 | - | } | |
| 1054 | - | // --- End JWT Secret Generation --- | |
| 1055 | - | ||
| 1056 | 706 | logger.Debug("LoadConfig: Completed LoadConfig function") | |
| 1057 | 707 | return config, nil | |
| 1058 | 708 | } | |
| @@ -7,27 +7,14 @@ import ( | |||||
| 7 | "os" | 7 | "os" | ||
| 8 | "path/filepath" | 8 | "path/filepath" | ||
| 9 | "runtime" | 9 | "runtime" | ||
| 10 | - | "strconv" | |||
| 11 | "strings" | 10 | "strings" | ||
| 12 | "sync" | 11 | "sync" | ||
| 13 | 12 | ||||
| 14 | - | "crypto/rand" | |||
| 15 | - | "encoding/hex" | |||
| 16 | - | ||||
| 17 | "github.com/bnema/gordon/pkg/docker" | 13 | "github.com/bnema/gordon/pkg/docker" | ||
| 18 | "github.com/bnema/gordon/pkg/logger" | 14 | "github.com/bnema/gordon/pkg/logger" | ||
| 19 | "github.com/bnema/gordon/pkg/parser" | 15 | "github.com/bnema/gordon/pkg/parser" | ||
| 20 | ) | 16 | ) | ||
| 21 | 17 | ||||
| 22 | - | // generateRandomSecret generates a secure random string of the specified byte length, hex-encoded. | |||
| 23 | - | func generateRandomSecret(length int) (string, error) { | |||
| 24 | - | bytes := make([]byte, length) | |||
| 25 | - | if _, err := rand.Read(bytes); err != nil { | |||
| 26 | - | return "", err | |||
| 27 | - | } | |||
| 28 | - | return hex.EncodeToString(bytes), nil | |||
| 29 | - | } | |||
| 30 | - | ||||
| 31 | var ( | 18 | var ( | ||
| 32 | globalConfig *Config | 19 | globalConfig *Config | ||
| 33 | globalConfigOnce sync.Once | 20 | globalConfigOnce sync.Once | ||
| @@ -94,9 +81,7 @@ type ReverseProxyConfig struct { | |||||
| 94 | LetsEncryptMode string `yaml:"letsEncryptMode"` // staging or production | 81 | LetsEncryptMode string `yaml:"letsEncryptMode"` // staging or production | ||
| 95 | SkipCertificates bool `yaml:"skipCertificates"` // Skip Let's Encrypt certificate acquisition when behind a TLS terminating proxy | 82 | SkipCertificates bool `yaml:"skipCertificates"` // Skip Let's Encrypt certificate acquisition when behind a TLS terminating proxy | ||
| 96 | GracePeriod int `yaml:"gracePeriod"` // Shutdown grace period in seconds | 83 | GracePeriod int `yaml:"gracePeriod"` // Shutdown grace period in seconds | ||
| 97 | - | AutoRenew bool `yaml:"autoRenew"` // Whether to automatically renew certificates | |||
| 98 | RenewBefore int `yaml:"renewBefore"` // Days before expiry to renew certificates | 84 | RenewBefore int `yaml:"renewBefore"` // Days before expiry to renew certificates | ||
| 99 | - | CacheSize int `yaml:"cacheSize"` // Size of the certificate cache | |||
| 100 | EnableHttpLogs bool `yaml:"enableHttpLogs"` // Whether to enable HTTP request logging | 85 | EnableHttpLogs bool `yaml:"enableHttpLogs"` // Whether to enable HTTP request logging | ||
| 101 | EnableRateLimit bool `yaml:"enableRateLimit"` // Whether to enable rate limiting middleware | 86 | EnableRateLimit bool `yaml:"enableRateLimit"` // Whether to enable rate limiting middleware | ||
| 102 | DetectUpstreamProxy bool `yaml:"detectUpstreamProxy"` // Whether to detect and handle upstream TLS termination proxies | 87 | DetectUpstreamProxy bool `yaml:"detectUpstreamProxy"` // Whether to detect and handle upstream TLS termination proxies | ||
| @@ -108,21 +93,24 @@ type ReverseProxyConfig struct { | |||||
| 108 | 93 | ||||
| 109 | // Default values | 94 | // Default values | ||
| 110 | var ( | 95 | var ( | ||
| 111 | - | sock = "/var/run/docker.sock" | 96 | + | sock = "/var/run/docker.sock" |
| 112 | - | podmansock = "/run/podman/podman.sock" | 97 | + | podmansock = "/run/podman/podman.sock" |
| 113 | - | reverseProxyPort = "443" // Changed to 443 for standard HTTPS | 98 | + | reverseProxyPort = "443" // Changed to 443 for standard HTTPS |
| 114 | - | httpPort = "80" // Standard HTTP port | 99 | + | httpPort = "80" // Standard HTTP port |
| 115 | - | certDir = "/certs" | 100 | + | certDir = "/certs" |
| 116 | - | letsEncryptMode = "staging" | 101 | + | letsEncryptMode = "staging" |
| 117 | - | autoRenew = true | 102 | + | proxyEnabled = true // Default to enabled for backward compatibility |
| 118 | - | proxyEnabled = true // Default to enabled for backward compatibility | 103 | + | renewBefore = 30 // days |
| 119 | - | renewBefore = 30 // days | 104 | + | gracePeriod = 30 // seconds |
| 120 | - | cacheSize = 1000 // entries | 105 | + | defaultLogLevel = "info" // Default log level |
| 121 | - | gracePeriod = 30 // seconds | 106 | + | disableRateLimit = false |
| 122 | - | defaultLogLevel = "info" // Default log level | 107 | + | detectUpstreamProxy = false // Default to disabled |
| 123 | - | disableRateLimit = false | 108 | + | skipCertificates = false // Default to disabled |
| 124 | - | detectUpstreamProxy = false // Default to disabled | 109 | + | enableRateLimit = false // Default to disabled |
| 125 | - | skipCertificates = false // Default to disabled | 110 | + | defaultChallengeType = "http-01" |
| 111 | + | defaultHttpChallengePort = "80" | |||
| 112 | + | defaultDnsChallengePropagationTimeout = 60 // seconds | |||
| 113 | + | defaultDnsChallengePollingInterval = 5 // seconds | |||
| 126 | ) | 114 | ) | ||
| 127 | 115 | ||||
| 128 | // applyDefaultsToConfig applies default values to any fields that have zero values | 116 | // applyDefaultsToConfig applies default values to any fields that have zero values | ||
| @@ -208,11 +196,6 @@ func applyDefaultsToConfig(config *Config) bool { | |||||
| 208 | logger.Debug("Applied default value for ReverseProxy.LetsEncryptMode", "value", letsEncryptMode) | 196 | logger.Debug("Applied default value for ReverseProxy.LetsEncryptMode", "value", letsEncryptMode) | ||
| 209 | defaultsApplied = true | 197 | defaultsApplied = true | ||
| 210 | } | 198 | } | ||
| 211 | - | if config.ReverseProxy.CacheSize == 0 { | |||
| 212 | - | config.ReverseProxy.CacheSize = cacheSize | |||
| 213 | - | logger.Debug("Applied default value for ReverseProxy.CacheSize", "value", cacheSize) | |||
| 214 | - | defaultsApplied = true | |||
| 215 | - | } | |||
| 216 | if config.ReverseProxy.GracePeriod == 0 { | 199 | if config.ReverseProxy.GracePeriod == 0 { | ||
| 217 | config.ReverseProxy.GracePeriod = gracePeriod | 200 | config.ReverseProxy.GracePeriod = gracePeriod | ||
| 218 | logger.Debug("Applied default value for ReverseProxy.GracePeriod", "value", gracePeriod) | 201 | logger.Debug("Applied default value for ReverseProxy.GracePeriod", "value", gracePeriod) | ||
| @@ -266,34 +249,6 @@ func applyDefaultsToConfig(config *Config) bool { | |||||
| 266 | defaultsApplied = true | 249 | defaultsApplied = true | ||
| 267 | } | 250 | } | ||
| 268 | 251 | ||||
| 269 | - | // Handle autoRenew | |||
| 270 | - | configFilePath = filepath.Join(getConfigDirMustExist(), "config.yml") | |||
| 271 | - | if fileExists(configFilePath) { | |||
| 272 | - | yamlContent, err := os.ReadFile(configFilePath) | |||
| 273 | - | if err == nil { | |||
| 274 | - | // Check if "autoRenew" is explicitly mentioned in the config file | |||
| 275 | - | if !strings.Contains(string(yamlContent), "autoRenew:") { | |||
| 276 | - | config.ReverseProxy.AutoRenew = autoRenew | |||
| 277 | - | logger.Debug("Applied default value for ReverseProxy.AutoRenew", "value", autoRenew) | |||
| 278 | - | defaultsApplied = true | |||
| 279 | - | } else { | |||
| 280 | - | logger.Debug("Keeping explicit value for ReverseProxy.AutoRenew", "value", config.ReverseProxy.AutoRenew) | |||
| 281 | - | } | |||
| 282 | - | } else { | |||
| 283 | - | // If we can't read the file for some reason, apply the default | |||
| 284 | - | if !config.ReverseProxy.AutoRenew { | |||
| 285 | - | config.ReverseProxy.AutoRenew = autoRenew | |||
| 286 | - | logger.Debug("Applied default value for ReverseProxy.AutoRenew", "value", autoRenew) | |||
| 287 | - | defaultsApplied = true | |||
| 288 | - | } | |||
| 289 | - | } | |||
| 290 | - | } else { | |||
| 291 | - | // If config file doesn't exist yet, set the default | |||
| 292 | - | config.ReverseProxy.AutoRenew = autoRenew | |||
| 293 | - | logger.Debug("Applied default value for ReverseProxy.AutoRenew", "value", autoRenew) | |||
| 294 | - | defaultsApplied = true | |||
| 295 | - | } | |||
| 296 | - | ||||
| 297 | return defaultsApplied | 252 | return defaultsApplied | ||
| 298 | } | 253 | } | ||
| 299 | 254 | ||||
| @@ -447,6 +402,7 @@ func getConfigDir() (string, error) { | |||||
| 447 | return configDir, nil | 402 | return configDir, nil | ||
| 448 | } | 403 | } | ||
| 449 | 404 | ||||
| 405 | + | // readAndUnmarshalConfig reads and unmarshals a configuration file using the provided filesystem and path. | |||
| 450 | func readAndUnmarshalConfig(fs fs.FS, filePath string, config *Config) error { | 406 | func readAndUnmarshalConfig(fs fs.FS, filePath string, config *Config) error { | ||
| 451 | logger.Debug("readAndUnmarshalConfig: Starting to read and unmarshal config file", "path", filePath) | 407 | logger.Debug("readAndUnmarshalConfig: Starting to read and unmarshal config file", "path", filePath) | ||
| 452 | err := parser.ParseYAMLFile(fs, filePath, config) | 408 | err := parser.ParseYAMLFile(fs, filePath, config) | ||
| @@ -458,282 +414,6 @@ func readAndUnmarshalConfig(fs fs.FS, filePath string, config *Config) error { | |||||
| 458 | return nil | 414 | return nil | ||
| 459 | } | 415 | } | ||
| 460 | 416 | ||||
| 461 | - | // loadConfigFromEnv loads configuration from environment variables | |||
| 462 | - | func loadConfigFromEnv(config *Config, printLogs bool) { | |||
| 463 | - | // General Configuration | |||
| 464 | - | if val := os.Getenv("GORDON_STORAGE_DIR"); val != "" { | |||
| 465 | - | config.General.StorageDir = val | |||
| 466 | - | if printLogs { | |||
| 467 | - | logger.Info("Using environment variable GORDON_STORAGE_DIR", "value", val) | |||
| 468 | - | } | |||
| 469 | - | } | |||
| 470 | - | // Removed GORDON_TOKEN handling | |||
| 471 | - | if val := os.Getenv("GORDON_LOG_LEVEL"); val != "" { | |||
| 472 | - | config.General.LogLevel = val | |||
| 473 | - | if printLogs { | |||
| 474 | - | logger.Info("Using environment variable GORDON_LOG_LEVEL", "value", val) | |||
| 475 | - | } | |||
| 476 | - | } | |||
| 477 | - | ||||
| 478 | - | // HTTP Configuration | |||
| 479 | - | if val := os.Getenv("GORDON_HTTP_PORT"); val != "" { | |||
| 480 | - | config.Http.Port = val | |||
| 481 | - | if printLogs { | |||
| 482 | - | logger.Info("Using environment variable GORDON_HTTP_PORT", "value", val) | |||
| 483 | - | } | |||
| 484 | - | } | |||
| 485 | - | if val := os.Getenv("GORDON_HTTP_DOMAIN"); val != "" { | |||
| 486 | - | config.Http.Domain = val | |||
| 487 | - | if printLogs { | |||
| 488 | - | logger.Info("Using environment variable GORDON_HTTP_DOMAIN", "value", val) | |||
| 489 | - | } | |||
| 490 | - | } | |||
| 491 | - | if val := os.Getenv("GORDON_HTTP_SUBDOMAIN"); val != "" { | |||
| 492 | - | config.Http.SubDomain = val | |||
| 493 | - | if printLogs { | |||
| 494 | - | logger.Info("Using environment variable GORDON_HTTP_SUBDOMAIN", "value", val) | |||
| 495 | - | } | |||
| 496 | - | } | |||
| 497 | - | if val := os.Getenv("GORDON_HTTP_BACKEND_URL"); val != "" { | |||
| 498 | - | config.Http.BackendURL = val | |||
| 499 | - | if printLogs { | |||
| 500 | - | logger.Info("Using environment variable GORDON_HTTP_BACKEND_URL", "value", val) | |||
| 501 | - | } | |||
| 502 | - | } | |||
| 503 | - | if val := os.Getenv("GORDON_HTTP_HTTPS"); val != "" { | |||
| 504 | - | config.Http.Https = strings.ToLower(val) == "true" | |||
| 505 | - | if printLogs { | |||
| 506 | - | logger.Info("Using environment variable GORDON_HTTP_HTTPS", "value", config.Http.Https) | |||
| 507 | - | } | |||
| 508 | - | } | |||
| 509 | - | ||||
| 510 | - | // Admin Configuration | |||
| 511 | - | if val := os.Getenv("GORDON_ADMIN_PATH"); val != "" { | |||
| 512 | - | config.Admin.Path = val | |||
| 513 | - | if printLogs { | |||
| 514 | - | logger.Info("Using environment variable GORDON_ADMIN_PATH", "value", val) | |||
| 515 | - | } | |||
| 516 | - | } | |||
| 517 | - | ||||
| 518 | - | // Container Engine Configuration | |||
| 519 | - | if val := os.Getenv("GORDON_CONTAINER_SOCK"); val != "" { | |||
| 520 | - | config.ContainerEngine.Sock = val | |||
| 521 | - | if printLogs { | |||
| 522 | - | logger.Info("Using environment variable GORDON_CONTAINER_SOCK", "value", val) | |||
| 523 | - | } | |||
| 524 | - | } | |||
| 525 | - | if val := os.Getenv("GORDON_CONTAINER_PODMANSOCK"); val != "" { | |||
| 526 | - | config.ContainerEngine.PodmanSock = val | |||
| 527 | - | if printLogs { | |||
| 528 | - | logger.Info("Using environment variable GORDON_CONTAINER_PODMANSOCK", "value", val) | |||
| 529 | - | } | |||
| 530 | - | } | |||
| 531 | - | if val := os.Getenv("GORDON_CONTAINER_PODMAN"); val != "" { | |||
| 532 | - | config.ContainerEngine.Podman = strings.ToLower(val) == "true" | |||
| 533 | - | if printLogs { | |||
| 534 | - | logger.Info("Using environment variable GORDON_CONTAINER_PODMAN", "value", config.ContainerEngine.Podman) | |||
| 535 | - | } | |||
| 536 | - | } else { | |||
| 537 | - | // Auto-detect Podman if not specified in environment | |||
| 538 | - | isPodman, podmanSocket := docker.DetectPodman() | |||
| 539 | - | if isPodman { | |||
| 540 | - | config.ContainerEngine.Podman = true | |||
| 541 | - | config.ContainerEngine.PodmanSock = podmanSocket | |||
| 542 | - | if printLogs { | |||
| 543 | - | logger.Info("Automatically detected Podman installation", | |||
| 544 | - | "using_podman", true, | |||
| 545 | - | "socket", podmanSocket) | |||
| 546 | - | } | |||
| 547 | - | } | |||
| 548 | - | ||||
| 549 | - | // If we're in a container, check for special socket naming patterns | |||
| 550 | - | if docker.IsRunningInContainer() && fileExists(config.ContainerEngine.Sock) { | |||
| 551 | - | // Check if socket path contains "podman" which indicates it might be a podman socket | |||
| 552 | - | if strings.Contains(config.ContainerEngine.Sock, "podman") { | |||
| 553 | - | config.ContainerEngine.Podman = true | |||
| 554 | - | if printLogs { | |||
| 555 | - | logger.Info("Socket path contains 'podman', enabling Podman mode", "path", config.ContainerEngine.Sock) | |||
| 556 | - | } | |||
| 557 | - | } | |||
| 558 | - | } | |||
| 559 | - | } | |||
| 560 | - | if val := os.Getenv("GORDON_CONTAINER_NETWORK"); val != "" { | |||
| 561 | - | config.ContainerEngine.Network = val | |||
| 562 | - | if printLogs { | |||
| 563 | - | logger.Info("Using environment variable GORDON_CONTAINER_NETWORK", "value", val) | |||
| 564 | - | } | |||
| 565 | - | } | |||
| 566 | - | ||||
| 567 | - | // Use Podman socket if Podman is enabled and no Docker socket is specified | |||
| 568 | - | if config.ContainerEngine.Podman && config.ContainerEngine.Sock == "" && config.ContainerEngine.PodmanSock != "" { | |||
| 569 | - | config.ContainerEngine.Sock = config.ContainerEngine.PodmanSock | |||
| 570 | - | if printLogs { | |||
| 571 | - | logger.Info("Setting ContainerEngine.Sock to PodmanSock value", "value", config.ContainerEngine.Sock) | |||
| 572 | - | } | |||
| 573 | - | } | |||
| 574 | - | ||||
| 575 | - | // Additional debug info for container sockets | |||
| 576 | - | if docker.IsRunningInContainer() { | |||
| 577 | - | logger.Debug("Container socket configuration", | |||
| 578 | - | "sock", config.ContainerEngine.Sock, | |||
| 579 | - | "podmansock", config.ContainerEngine.PodmanSock, | |||
| 580 | - | "podman", config.ContainerEngine.Podman, | |||
| 581 | - | "sock_exists", fileExists(config.ContainerEngine.Sock), | |||
| 582 | - | "podmansock_exists", fileExists(config.ContainerEngine.PodmanSock)) | |||
| 583 | - | ||||
| 584 | - | // Special handling for container environment to ensure sockets are properly set | |||
| 585 | - | // If no socket is configured but default socket path exists, use it | |||
| 586 | - | if config.ContainerEngine.Sock == "" && fileExists(sock) { | |||
| 587 | - | config.ContainerEngine.Sock = sock | |||
| 588 | - | logger.Info("Found default Docker socket in container, using it", "path", sock) | |||
| 589 | - | } | |||
| 590 | - | ||||
| 591 | - | // If podman socket exists but podman mode is not enabled, check if we should enable it | |||
| 592 | - | if fileExists(podmansock) && !config.ContainerEngine.Podman { | |||
| 593 | - | // If the main socket path contains "podman", enable podman mode | |||
| 594 | - | if strings.Contains(config.ContainerEngine.Sock, "podman") { | |||
| 595 | - | config.ContainerEngine.Podman = true | |||
| 596 | - | logger.Info("Socket path suggests Podman usage, enabling Podman mode", "path", config.ContainerEngine.Sock) | |||
| 597 | - | } | |||
| 598 | - | } | |||
| 599 | - | } | |||
| 600 | - | ||||
| 601 | - | // Reverse Proxy Configuration | |||
| 602 | - | if val := os.Getenv("GORDON_PROXY_PORT"); val != "" { | |||
| 603 | - | config.ReverseProxy.Port = val | |||
| 604 | - | if printLogs { | |||
| 605 | - | logger.Info("Using environment variable GORDON_PROXY_PORT", "value", val) | |||
| 606 | - | } | |||
| 607 | - | } | |||
| 608 | - | if val := os.Getenv("GORDON_PROXY_HTTP_PORT"); val != "" { | |||
| 609 | - | config.ReverseProxy.HttpPort = val | |||
| 610 | - | if printLogs { | |||
| 611 | - | logger.Info("Using environment variable GORDON_PROXY_HTTP_PORT", "value", val) | |||
| 612 | - | } | |||
| 613 | - | } | |||
| 614 | - | ||||
| 615 | - | // New environment variables for reverse proxy port control | |||
| 616 | - | if val := os.Getenv("GORDON_USE_FALLBACK_BINDING"); val != "" { | |||
| 617 | - | // This is handled at runtime in the proxy server but we log it here | |||
| 618 | - | if printLogs { | |||
| 619 | - | logger.Info("Found environment variable GORDON_USE_FALLBACK_BINDING", "value", val) | |||
| 620 | - | } | |||
| 621 | - | } | |||
| 622 | - | ||||
| 623 | - | // Handle GORDON_PROXY_ENABLED environment variable | |||
| 624 | - | if val := os.Getenv("GORDON_PROXY_ENABLED"); val != "" { | |||
| 625 | - | enabled, err := strconv.ParseBool(val) | |||
| 626 | - | if err == nil { | |||
| 627 | - | config.ReverseProxy.Enabled = enabled | |||
| 628 | - | } | |||
| 629 | - | if printLogs { | |||
| 630 | - | logger.Info("Using environment variable GORDON_PROXY_ENABLED", "value", config.ReverseProxy.Enabled) | |||
| 631 | - | } | |||
| 632 | - | } | |||
| 633 | - | if val := os.Getenv("GORDON_PROXY_CERT_DIR"); val != "" { | |||
| 634 | - | config.ReverseProxy.CertDir = val | |||
| 635 | - | if printLogs { | |||
| 636 | - | logger.Info("Using environment variable GORDON_PROXY_CERT_DIR", "value", val) | |||
| 637 | - | } | |||
| 638 | - | } | |||
| 639 | - | if val := os.Getenv("GORDON_PROXY_AUTO_RENEW"); val != "" { | |||
| 640 | - | config.ReverseProxy.AutoRenew = strings.ToLower(val) == "true" | |||
| 641 | - | if printLogs { | |||
| 642 | - | logger.Info("Using environment variable GORDON_PROXY_AUTO_RENEW", "value", config.ReverseProxy.AutoRenew) | |||
| 643 | - | } | |||
| 644 | - | } | |||
| 645 | - | if val := os.Getenv("GORDON_PROXY_RENEW_BEFORE"); val != "" { | |||
| 646 | - | if i, err := strconv.Atoi(val); err == nil { | |||
| 647 | - | config.ReverseProxy.RenewBefore = i | |||
| 648 | - | if printLogs { | |||
| 649 | - | logger.Info("Using environment variable GORDON_PROXY_RENEW_BEFORE", "value", i) | |||
| 650 | - | } | |||
| 651 | - | } | |||
| 652 | - | } | |||
| 653 | - | if val := os.Getenv("GORDON_PROXY_LETSENCRYPT_MODE"); val != "" { | |||
| 654 | - | config.ReverseProxy.LetsEncryptMode = val | |||
| 655 | - | if printLogs { | |||
| 656 | - | logger.Info("Using environment variable GORDON_PROXY_LETSENCRYPT_MODE", "value", val) | |||
| 657 | - | } | |||
| 658 | - | } | |||
| 659 | - | if val := os.Getenv("GORDON_PROXY_EMAIL"); val != "" { | |||
| 660 | - | config.ReverseProxy.Email = val | |||
| 661 | - | if printLogs { | |||
| 662 | - | logger.Info("Using environment variable GORDON_PROXY_EMAIL", "value", val) | |||
| 663 | - | } | |||
| 664 | - | } | |||
| 665 | - | if val := os.Getenv("GORDON_PROXY_CACHE_SIZE"); val != "" { | |||
| 666 | - | if i, err := strconv.Atoi(val); err == nil { | |||
| 667 | - | config.ReverseProxy.CacheSize = i | |||
| 668 | - | if printLogs { | |||
| 669 | - | logger.Info("Using environment variable GORDON_PROXY_CACHE_SIZE", "value", i) | |||
| 670 | - | } | |||
| 671 | - | } | |||
| 672 | - | } | |||
| 673 | - | if val := os.Getenv("GORDON_PROXY_GRACE_PERIOD"); val != "" { | |||
| 674 | - | i, err := strconv.Atoi(val) | |||
| 675 | - | if err == nil { | |||
| 676 | - | config.ReverseProxy.GracePeriod = i | |||
| 677 | - | } | |||
| 678 | - | logger.Info("Using environment variable GORDON_PROXY_GRACE_PERIOD", "value", i) | |||
| 679 | - | } | |||
| 680 | - | ||||
| 681 | - | // Handle GORDON_PROXY_ENABLE_HTTP_LOGS environment variable | |||
| 682 | - | if val := os.Getenv("GORDON_PROXY_ENABLE_HTTP_LOGS"); val != "" { | |||
| 683 | - | enableLogs, err := strconv.ParseBool(val) | |||
| 684 | - | if err == nil { | |||
| 685 | - | config.ReverseProxy.EnableHttpLogs = enableLogs | |||
| 686 | - | } | |||
| 687 | - | logger.Info("Using environment variable GORDON_PROXY_ENABLE_HTTP_LOGS", "value", config.ReverseProxy.EnableHttpLogs) | |||
| 688 | - | } else if val := os.Getenv("GORDON_PROXY_ENABLE_LOGS"); val != "" { | |||
| 689 | - | // For backward compatibility | |||
| 690 | - | enableLogs, err := strconv.ParseBool(val) | |||
| 691 | - | if err == nil { | |||
| 692 | - | config.ReverseProxy.EnableHttpLogs = enableLogs | |||
| 693 | - | } | |||
| 694 | - | logger.Info("Using environment variable GORDON_PROXY_ENABLE_LOGS (deprecated, use GORDON_PROXY_ENABLE_HTTP_LOGS)", "value", config.ReverseProxy.EnableHttpLogs) | |||
| 695 | - | } | |||
| 696 | - | ||||
| 697 | - | // Handle GORDON_PROXY_ENABLE_RATE_LIMIT environment variable | |||
| 698 | - | if val := os.Getenv("GORDON_PROXY_ENABLE_RATE_LIMIT"); val != "" { | |||
| 699 | - | enableRateLimit, err := strconv.ParseBool(val) | |||
| 700 | - | if err == nil { | |||
| 701 | - | config.ReverseProxy.EnableRateLimit = enableRateLimit | |||
| 702 | - | } | |||
| 703 | - | logger.Info("Using environment variable GORDON_PROXY_ENABLE_RATE_LIMIT", "value", config.ReverseProxy.EnableRateLimit) | |||
| 704 | - | } else if val := os.Getenv("GORDON_PROXY_DISABLE_RATE_LIMIT"); val != "" { | |||
| 705 | - | // For backward compatibility | |||
| 706 | - | disableRateLimit, err := strconv.ParseBool(val) | |||
| 707 | - | if err == nil { | |||
| 708 | - | // Invert the logic | |||
| 709 | - | config.ReverseProxy.EnableRateLimit = !disableRateLimit | |||
| 710 | - | } | |||
| 711 | - | logger.Info("Using environment variable GORDON_PROXY_DISABLE_RATE_LIMIT (deprecated, use GORDON_PROXY_ENABLE_RATE_LIMIT)", "value", !config.ReverseProxy.EnableRateLimit) | |||
| 712 | - | } | |||
| 713 | - | ||||
| 714 | - | // Handle GORDON_PROXY_DETECT_UPSTREAM environment variable | |||
| 715 | - | if val := os.Getenv("GORDON_PROXY_DETECT_UPSTREAM"); val != "" { | |||
| 716 | - | detectUpstream, err := strconv.ParseBool(val) | |||
| 717 | - | if err == nil { | |||
| 718 | - | config.ReverseProxy.DetectUpstreamProxy = detectUpstream | |||
| 719 | - | } | |||
| 720 | - | if printLogs { | |||
| 721 | - | logger.Info("Using environment variable GORDON_PROXY_DETECT_UPSTREAM", "value", config.ReverseProxy.DetectUpstreamProxy) | |||
| 722 | - | } | |||
| 723 | - | } | |||
| 724 | - | ||||
| 725 | - | // Handle GORDON_PROXY_SKIP_CERTIFICATES environment variable | |||
| 726 | - | if val := os.Getenv("GORDON_PROXY_SKIP_CERTIFICATES"); val != "" { | |||
| 727 | - | skipCerts, err := strconv.ParseBool(val) | |||
| 728 | - | if err == nil { | |||
| 729 | - | config.ReverseProxy.SkipCertificates = skipCerts | |||
| 730 | - | } | |||
| 731 | - | if printLogs { | |||
| 732 | - | logger.Info("Using environment variable GORDON_PROXY_SKIP_CERTIFICATES", "value", config.ReverseProxy.SkipCertificates) | |||
| 733 | - | } | |||
| 734 | - | } | |||
| 735 | - | } | |||
| 736 | - | ||||
| 737 | func (config *Config) LoadConfig() (*Config, error) { | 417 | func (config *Config) LoadConfig() (*Config, error) { | ||
| 738 | // If we're in a non-interactive environment, use simplified config | 418 | // If we're in a non-interactive environment, use simplified config | ||
| 739 | nonInteractiveEnv := os.Getenv("GORDON_NONINTERACTIVE") | 419 | nonInteractiveEnv := os.Getenv("GORDON_NONINTERACTIVE") | ||
| @@ -781,10 +461,8 @@ func (config *Config) LoadConfig() (*Config, error) { | |||||
| 781 | Port: reverseProxyPort, | 461 | Port: reverseProxyPort, | ||
| 782 | HttpPort: httpPort, | 462 | HttpPort: httpPort, | ||
| 783 | CertDir: certDir, | 463 | CertDir: certDir, | ||
| 784 | - | AutoRenew: autoRenew, | |||
| 785 | RenewBefore: renewBefore, | 464 | RenewBefore: renewBefore, | ||
| 786 | LetsEncryptMode: letsEncryptMode, | 465 | LetsEncryptMode: letsEncryptMode, | ||
| 787 | - | CacheSize: cacheSize, | |||
| 788 | GracePeriod: gracePeriod, | 466 | GracePeriod: gracePeriod, | ||
| 789 | EnableHttpLogs: true, | 467 | EnableHttpLogs: true, | ||
| 790 | EnableRateLimit: false, | 468 | EnableRateLimit: false, | ||
| @@ -920,10 +598,8 @@ func (config *Config) LoadConfig() (*Config, error) { | |||||
| 920 | Port: reverseProxyPort, | 598 | Port: reverseProxyPort, | ||
| 921 | HttpPort: httpPort, | 599 | HttpPort: httpPort, | ||
| 922 | CertDir: certDir, | 600 | CertDir: certDir, | ||
| 923 | - | AutoRenew: autoRenew, | |||
| 924 | RenewBefore: renewBefore, | 601 | RenewBefore: renewBefore, | ||
| 925 | LetsEncryptMode: letsEncryptMode, | 602 | LetsEncryptMode: letsEncryptMode, | ||
| 926 | - | CacheSize: cacheSize, | |||
| 927 | GracePeriod: gracePeriod, | 603 | GracePeriod: gracePeriod, | ||
| 928 | EnableHttpLogs: true, | 604 | EnableHttpLogs: true, | ||
| 929 | EnableRateLimit: false, | 605 | EnableRateLimit: false, | ||
| @@ -1027,32 +703,6 @@ func (config *Config) LoadConfig() (*Config, error) { | |||||
| 1027 | logger.Debug("Log level set from configuration", "level", config.General.LogLevel) | 703 | logger.Debug("Log level set from configuration", "level", config.General.LogLevel) | ||
| 1028 | } | 704 | } | ||
| 1029 | 705 | ||||
| 1030 | - | // --- Generate JWT Secret if missing --- | |||
| 1031 | - | if config.General.JwtToken == "" { | |||
| 1032 | - | logger.Warn("JWTSecret is missing from configuration, generating a new one...") | |||
| 1033 | - | newSecret, err := generateRandomSecret(32) // Generate a 256-bit key (32 bytes) | |||
| 1034 | - | if err != nil { | |||
| 1035 | - | logger.Error("Failed to generate JWT secret", "error", err) | |||
| 1036 | - | // Decide if this is a fatal error or if we can continue without JWT functionality | |||
| 1037 | - | // For now, let's return the error as JWT is crucial for API auth | |||
| 1038 | - | return nil, fmt.Errorf("failed to generate JWT secret: %w", err) | |||
| 1039 | - | } | |||
| 1040 | - | config.General.JwtToken = newSecret | |||
| 1041 | - | logger.Info("Generated new JWTSecret") | |||
| 1042 | - | ||||
| 1043 | - | // Save the config immediately to store the new secret | |||
| 1044 | - | err = config.SaveConfig() | |||
| 1045 | - | if err != nil { | |||
| 1046 | - | logger.Error("Failed to save configuration after generating JWT secret", "error", err) | |||
| 1047 | - | // Return error as saving failed | |||
| 1048 | - | return nil, fmt.Errorf("failed to save config with new JWT secret: %w", err) | |||
| 1049 | - | } | |||
| 1050 | - | logger.Info("Saved configuration with new JWTSecret") | |||
| 1051 | - | } else { | |||
| 1052 | - | logger.Debug("JWTSecret found in configuration") | |||
| 1053 | - | } | |||
| 1054 | - | // --- End JWT Secret Generation --- | |||
| 1055 | - | ||||
| 1056 | logger.Debug("LoadConfig: Completed LoadConfig function") | 706 | logger.Debug("LoadConfig: Completed LoadConfig function") | ||
| 1057 | return config, nil | 707 | return config, nil | ||
| 1058 | } | 708 | } | ||
A internal/common/init_env.go +272 -0
| @@ -0,0 +1,272 @@ | |||
| 1 | + | package common | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "os" | |
| 5 | + | "strconv" | |
| 6 | + | "strings" | |
| 7 | + | ||
| 8 | + | "github.com/bnema/gordon/pkg/docker" | |
| 9 | + | "github.com/bnema/gordon/pkg/logger" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // loadConfigFromEnv loads configuration from environment variables | |
| 13 | + | func loadConfigFromEnv(config *Config, printLogs bool) { | |
| 14 | + | // General Configuration | |
| 15 | + | if val := os.Getenv("GORDON_STORAGE_DIR"); val != "" { | |
| 16 | + | config.General.StorageDir = val | |
| 17 | + | if printLogs { | |
| 18 | + | logger.Info("Using environment variable GORDON_STORAGE_DIR", "value", val) | |
| 19 | + | } | |
| 20 | + | } | |
| 21 | + | // Removed GORDON_TOKEN handling | |
| 22 | + | if val := os.Getenv("GORDON_LOG_LEVEL"); val != "" { | |
| 23 | + | config.General.LogLevel = val | |
| 24 | + | if printLogs { | |
| 25 | + | logger.Info("Using environment variable GORDON_LOG_LEVEL", "value", val) | |
| 26 | + | } | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | // HTTP Configuration | |
| 30 | + | if val := os.Getenv("GORDON_HTTP_PORT"); val != "" { | |
| 31 | + | config.Http.Port = val | |
| 32 | + | if printLogs { | |
| 33 | + | logger.Info("Using environment variable GORDON_HTTP_PORT", "value", val) | |
| 34 | + | } | |
| 35 | + | } | |
| 36 | + | if val := os.Getenv("GORDON_HTTP_DOMAIN"); val != "" { | |
| 37 | + | config.Http.Domain = val | |
| 38 | + | if printLogs { | |
| 39 | + | logger.Info("Using environment variable GORDON_HTTP_DOMAIN", "value", val) | |
| 40 | + | } | |
| 41 | + | } | |
| 42 | + | if val := os.Getenv("GORDON_HTTP_SUBDOMAIN"); val != "" { | |
| 43 | + | config.Http.SubDomain = val | |
| 44 | + | if printLogs { | |
| 45 | + | logger.Info("Using environment variable GORDON_HTTP_SUBDOMAIN", "value", val) | |
| 46 | + | } | |
| 47 | + | } | |
| 48 | + | if val := os.Getenv("GORDON_HTTP_BACKEND_URL"); val != "" { | |
| 49 | + | config.Http.BackendURL = val | |
| 50 | + | if printLogs { | |
| 51 | + | logger.Info("Using environment variable GORDON_HTTP_BACKEND_URL", "value", val) | |
| 52 | + | } | |
| 53 | + | } | |
| 54 | + | if val := os.Getenv("GORDON_HTTP_HTTPS"); val != "" { | |
| 55 | + | config.Http.Https = strings.ToLower(val) == "true" | |
| 56 | + | if printLogs { | |
| 57 | + | logger.Info("Using environment variable GORDON_HTTP_HTTPS", "value", config.Http.Https) | |
| 58 | + | } | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | // Admin Configuration | |
| 62 | + | if val := os.Getenv("GORDON_ADMIN_PATH"); val != "" { | |
| 63 | + | config.Admin.Path = val | |
| 64 | + | if printLogs { | |
| 65 | + | logger.Info("Using environment variable GORDON_ADMIN_PATH", "value", val) | |
| 66 | + | } | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | // Container Engine Configuration | |
| 70 | + | if val := os.Getenv("GORDON_CONTAINER_SOCK"); val != "" { | |
| 71 | + | config.ContainerEngine.Sock = val | |
| 72 | + | if printLogs { | |
| 73 | + | logger.Info("Using environment variable GORDON_CONTAINER_SOCK", "value", val) | |
| 74 | + | } | |
| 75 | + | } | |
| 76 | + | if val := os.Getenv("GORDON_CONTAINER_PODMANSOCK"); val != "" { | |
| 77 | + | config.ContainerEngine.PodmanSock = val | |
| 78 | + | if printLogs { | |
| 79 | + | logger.Info("Using environment variable GORDON_CONTAINER_PODMANSOCK", "value", val) | |
| 80 | + | } | |
| 81 | + | } | |
| 82 | + | if val := os.Getenv("GORDON_CONTAINER_PODMAN"); val != "" { | |
| 83 | + | config.ContainerEngine.Podman = strings.ToLower(val) == "true" | |
| 84 | + | if printLogs { | |
| 85 | + | logger.Info("Using environment variable GORDON_CONTAINER_PODMAN", "value", config.ContainerEngine.Podman) | |
| 86 | + | } | |
| 87 | + | } else { | |
| 88 | + | // Auto-detect Podman if not specified in environment | |
| 89 | + | isPodman, podmanSocket := docker.DetectPodman() | |
| 90 | + | if isPodman { | |
| 91 | + | config.ContainerEngine.Podman = true | |
| 92 | + | config.ContainerEngine.PodmanSock = podmanSocket | |
| 93 | + | if printLogs { | |
| 94 | + | logger.Info("Automatically detected Podman installation", | |
| 95 | + | "using_podman", true, | |
| 96 | + | "socket", podmanSocket) | |
| 97 | + | } | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | // If we're in a container, check for special socket naming patterns | |
| 101 | + | if docker.IsRunningInContainer() && fileExists(config.ContainerEngine.Sock) { | |
| 102 | + | // Check if socket path contains "podman" which indicates it might be a podman socket | |
| 103 | + | if strings.Contains(config.ContainerEngine.Sock, "podman") { | |
| 104 | + | config.ContainerEngine.Podman = true | |
| 105 | + | if printLogs { | |
| 106 | + | logger.Info("Socket path contains 'podman', enabling Podman mode", "path", config.ContainerEngine.Sock) | |
| 107 | + | } | |
| 108 | + | } | |
| 109 | + | } | |
| 110 | + | } | |
| 111 | + | if val := os.Getenv("GORDON_CONTAINER_NETWORK"); val != "" { | |
| 112 | + | config.ContainerEngine.Network = val | |
| 113 | + | if printLogs { | |
| 114 | + | logger.Info("Using environment variable GORDON_CONTAINER_NETWORK", "value", val) | |
| 115 | + | } | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | // Use Podman socket if Podman is enabled and no Docker socket is specified | |
| 119 | + | if config.ContainerEngine.Podman && config.ContainerEngine.Sock == "" && config.ContainerEngine.PodmanSock != "" { | |
| 120 | + | config.ContainerEngine.Sock = config.ContainerEngine.PodmanSock | |
| 121 | + | if printLogs { | |
| 122 | + | logger.Info("Setting ContainerEngine.Sock to PodmanSock value", "value", config.ContainerEngine.Sock) | |
| 123 | + | } | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | // Additional debug info for container sockets | |
| 127 | + | if docker.IsRunningInContainer() { | |
| 128 | + | logger.Debug("Container socket configuration", | |
| 129 | + | "sock", config.ContainerEngine.Sock, | |
| 130 | + | "podmansock", config.ContainerEngine.PodmanSock, | |
| 131 | + | "podman", config.ContainerEngine.Podman, | |
| 132 | + | "sock_exists", fileExists(config.ContainerEngine.Sock), | |
| 133 | + | "podmansock_exists", fileExists(config.ContainerEngine.PodmanSock)) | |
| 134 | + | ||
| 135 | + | // Special handling for container environment to ensure sockets are properly set | |
| 136 | + | // If no socket is configured but default socket path exists, use it | |
| 137 | + | if config.ContainerEngine.Sock == "" && fileExists(sock) { | |
| 138 | + | config.ContainerEngine.Sock = sock | |
| 139 | + | logger.Info("Found default Docker socket in container, using it", "path", sock) | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | // If podman socket exists but podman mode is not enabled, check if we should enable it | |
| 143 | + | if fileExists(podmansock) && !config.ContainerEngine.Podman { | |
| 144 | + | // If the main socket path contains "podman", enable podman mode | |
| 145 | + | if strings.Contains(config.ContainerEngine.Sock, "podman") { | |
| 146 | + | config.ContainerEngine.Podman = true | |
| 147 | + | logger.Info("Socket path suggests Podman usage, enabling Podman mode", "path", config.ContainerEngine.Sock) | |
| 148 | + | } | |
| 149 | + | } | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | // Reverse Proxy Configuration | |
| 153 | + | if val := os.Getenv("GORDON_PROXY_PORT"); val != "" { | |
| 154 | + | config.ReverseProxy.Port = val | |
| 155 | + | if printLogs { | |
| 156 | + | logger.Info("Using environment variable GORDON_PROXY_PORT", "value", val) | |
| 157 | + | } | |
| 158 | + | } | |
| 159 | + | if val := os.Getenv("GORDON_PROXY_HTTP_PORT"); val != "" { | |
| 160 | + | config.ReverseProxy.HttpPort = val | |
| 161 | + | if printLogs { | |
| 162 | + | logger.Info("Using environment variable GORDON_PROXY_HTTP_PORT", "value", val) | |
| 163 | + | } | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | // New environment variables for reverse proxy port control | |
| 167 | + | if val := os.Getenv("GORDON_USE_FALLBACK_BINDING"); val != "" { | |
| 168 | + | // This is handled at runtime in the proxy server but we log it here | |
| 169 | + | if printLogs { | |
| 170 | + | logger.Info("Found environment variable GORDON_USE_FALLBACK_BINDING", "value", val) | |
| 171 | + | } | |
| 172 | + | } | |
| 173 | + | ||
| 174 | + | // Handle GORDON_PROXY_ENABLED environment variable | |
| 175 | + | if val := os.Getenv("GORDON_PROXY_ENABLED"); val != "" { | |
| 176 | + | enabled, err := strconv.ParseBool(val) | |
| 177 | + | if err == nil { | |
| 178 | + | config.ReverseProxy.Enabled = enabled | |
| 179 | + | } | |
| 180 | + | if printLogs { | |
| 181 | + | logger.Info("Using environment variable GORDON_PROXY_ENABLED", "value", config.ReverseProxy.Enabled) | |
| 182 | + | } | |
| 183 | + | } | |
| 184 | + | if val := os.Getenv("GORDON_PROXY_CERT_DIR"); val != "" { | |
| 185 | + | config.ReverseProxy.CertDir = val | |
| 186 | + | if printLogs { | |
| 187 | + | logger.Info("Using environment variable GORDON_PROXY_CERT_DIR", "value", val) | |
| 188 | + | } | |
| 189 | + | } | |
| 190 | + | if val := os.Getenv("GORDON_PROXY_RENEW_BEFORE"); val != "" { | |
| 191 | + | if i, err := strconv.Atoi(val); err == nil { | |
| 192 | + | config.ReverseProxy.RenewBefore = i | |
| 193 | + | if printLogs { | |
| 194 | + | logger.Info("Using environment variable GORDON_PROXY_RENEW_BEFORE", "value", i) | |
| 195 | + | } | |
| 196 | + | } | |
| 197 | + | } | |
| 198 | + | if val := os.Getenv("GORDON_PROXY_LETSENCRYPT_MODE"); val != "" { | |
| 199 | + | config.ReverseProxy.LetsEncryptMode = val | |
| 200 | + | if printLogs { | |
| 201 | + | logger.Info("Using environment variable GORDON_PROXY_LETSENCRYPT_MODE", "value", val) | |
| 202 | + | } | |
| 203 | + | } | |
| 204 | + | if val := os.Getenv("GORDON_PROXY_EMAIL"); val != "" { | |
| 205 | + | config.ReverseProxy.Email = val | |
| 206 | + | if printLogs { | |
| 207 | + | logger.Info("Using environment variable GORDON_PROXY_EMAIL", "value", val) | |
| 208 | + | } | |
| 209 | + | } | |
| 210 | + | if val := os.Getenv("GORDON_PROXY_GRACE_PERIOD"); val != "" { | |
| 211 | + | i, err := strconv.Atoi(val) | |
| 212 | + | if err == nil { | |
| 213 | + | config.ReverseProxy.GracePeriod = i | |
| 214 | + | } | |
| 215 | + | logger.Info("Using environment variable GORDON_PROXY_GRACE_PERIOD", "value", i) | |
| 216 | + | } | |
| 217 | + | ||
| 218 | + | // Handle GORDON_PROXY_ENABLE_HTTP_LOGS environment variable | |
| 219 | + | if val := os.Getenv("GORDON_PROXY_ENABLE_HTTP_LOGS"); val != "" { | |
| 220 | + | enableLogs, err := strconv.ParseBool(val) | |
| 221 | + | if err == nil { | |
| 222 | + | config.ReverseProxy.EnableHttpLogs = enableLogs | |
| 223 | + | } | |
| 224 | + | logger.Info("Using environment variable GORDON_PROXY_ENABLE_HTTP_LOGS", "value", config.ReverseProxy.EnableHttpLogs) | |
| 225 | + | } else if val := os.Getenv("GORDON_PROXY_ENABLE_LOGS"); val != "" { | |
| 226 | + | // For backward compatibility | |
| 227 | + | enableLogs, err := strconv.ParseBool(val) | |
| 228 | + | if err == nil { | |
| 229 | + | config.ReverseProxy.EnableHttpLogs = enableLogs | |
| 230 | + | } | |
| 231 | + | logger.Info("Using environment variable GORDON_PROXY_ENABLE_LOGS (deprecated, use GORDON_PROXY_ENABLE_HTTP_LOGS)", "value", config.ReverseProxy.EnableHttpLogs) | |
| 232 | + | } | |
| 233 | + | ||
| 234 | + | // Handle GORDON_PROXY_ENABLE_RATE_LIMIT environment variable | |
| 235 | + | if val := os.Getenv("GORDON_PROXY_ENABLE_RATE_LIMIT"); val != "" { | |
| 236 | + | enableRateLimit, err := strconv.ParseBool(val) | |
| 237 | + | if err == nil { | |
| 238 | + | config.ReverseProxy.EnableRateLimit = enableRateLimit | |
| 239 | + | } | |
| 240 | + | logger.Info("Using environment variable GORDON_PROXY_ENABLE_RATE_LIMIT", "value", config.ReverseProxy.EnableRateLimit) | |
| 241 | + | } else if val := os.Getenv("GORDON_PROXY_DISABLE_RATE_LIMIT"); val != "" { | |
| 242 | + | // For backward compatibility | |
| 243 | + | disableRateLimit, err := strconv.ParseBool(val) | |
| 244 | + | if err == nil { | |
| 245 | + | // Invert the logic | |
| 246 | + | config.ReverseProxy.EnableRateLimit = !disableRateLimit | |
| 247 | + | } | |
| 248 | + | logger.Info("Using environment variable GORDON_PROXY_DISABLE_RATE_LIMIT (deprecated, use GORDON_PROXY_ENABLE_RATE_LIMIT)", "value", !config.ReverseProxy.EnableRateLimit) | |
| 249 | + | } | |
| 250 | + | ||
| 251 | + | // Handle GORDON_PROXY_DETECT_UPSTREAM environment variable | |
| 252 | + | if val := os.Getenv("GORDON_PROXY_DETECT_UPSTREAM"); val != "" { | |
| 253 | + | detectUpstream, err := strconv.ParseBool(val) | |
| 254 | + | if err == nil { | |
| 255 | + | config.ReverseProxy.DetectUpstreamProxy = detectUpstream | |
| 256 | + | } | |
| 257 | + | if printLogs { | |
| 258 | + | logger.Info("Using environment variable GORDON_PROXY_DETECT_UPSTREAM", "value", config.ReverseProxy.DetectUpstreamProxy) | |
| 259 | + | } | |
| 260 | + | } | |
| 261 | + | ||
| 262 | + | // Handle GORDON_PROXY_SKIP_CERTIFICATES environment variable | |
| 263 | + | if val := os.Getenv("GORDON_PROXY_SKIP_CERTIFICATES"); val != "" { | |
| 264 | + | skipCerts, err := strconv.ParseBool(val) | |
| 265 | + | if err == nil { | |
| 266 | + | config.ReverseProxy.SkipCertificates = skipCerts | |
| 267 | + | } | |
| 268 | + | if printLogs { | |
| 269 | + | logger.Info("Using environment variable GORDON_PROXY_SKIP_CERTIFICATES", "value", config.ReverseProxy.SkipCertificates) | |
| 270 | + | } | |
| 271 | + | } | |
| 272 | + | } | |
| @@ -0,0 +1,272 @@ | |||||
| 1 | + | package common | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "os" | |||
| 5 | + | "strconv" | |||
| 6 | + | "strings" | |||
| 7 | + | ||||
| 8 | + | "github.com/bnema/gordon/pkg/docker" | |||
| 9 | + | "github.com/bnema/gordon/pkg/logger" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | // loadConfigFromEnv loads configuration from environment variables | |||
| 13 | + | func loadConfigFromEnv(config *Config, printLogs bool) { | |||
| 14 | + | // General Configuration | |||
| 15 | + | if val := os.Getenv("GORDON_STORAGE_DIR"); val != "" { | |||
| 16 | + | config.General.StorageDir = val | |||
| 17 | + | if printLogs { | |||
| 18 | + | logger.Info("Using environment variable GORDON_STORAGE_DIR", "value", val) | |||
| 19 | + | } | |||
| 20 | + | } | |||
| 21 | + | // Removed GORDON_TOKEN handling | |||
| 22 | + | if val := os.Getenv("GORDON_LOG_LEVEL"); val != "" { | |||
| 23 | + | config.General.LogLevel = val | |||
| 24 | + | if printLogs { | |||
| 25 | + | logger.Info("Using environment variable GORDON_LOG_LEVEL", "value", val) | |||
| 26 | + | } | |||
| 27 | + | } | |||
| 28 | + | ||||
| 29 | + | // HTTP Configuration | |||
| 30 | + | if val := os.Getenv("GORDON_HTTP_PORT"); val != "" { | |||
| 31 | + | config.Http.Port = val | |||
| 32 | + | if printLogs { | |||
| 33 | + | logger.Info("Using environment variable GORDON_HTTP_PORT", "value", val) | |||
| 34 | + | } | |||
| 35 | + | } | |||
| 36 | + | if val := os.Getenv("GORDON_HTTP_DOMAIN"); val != "" { | |||
| 37 | + | config.Http.Domain = val | |||
| 38 | + | if printLogs { | |||
| 39 | + | logger.Info("Using environment variable GORDON_HTTP_DOMAIN", "value", val) | |||
| 40 | + | } | |||
| 41 | + | } | |||
| 42 | + | if val := os.Getenv("GORDON_HTTP_SUBDOMAIN"); val != "" { | |||
| 43 | + | config.Http.SubDomain = val | |||
| 44 | + | if printLogs { | |||
| 45 | + | logger.Info("Using environment variable GORDON_HTTP_SUBDOMAIN", "value", val) | |||
| 46 | + | } | |||
| 47 | + | } | |||
| 48 | + | if val := os.Getenv("GORDON_HTTP_BACKEND_URL"); val != "" { | |||
| 49 | + | config.Http.BackendURL = val | |||
| 50 | + | if printLogs { | |||
| 51 | + | logger.Info("Using environment variable GORDON_HTTP_BACKEND_URL", "value", val) | |||
| 52 | + | } | |||
| 53 | + | } | |||
| 54 | + | if val := os.Getenv("GORDON_HTTP_HTTPS"); val != "" { | |||
| 55 | + | config.Http.Https = strings.ToLower(val) == "true" | |||
| 56 | + | if printLogs { | |||
| 57 | + | logger.Info("Using environment variable GORDON_HTTP_HTTPS", "value", config.Http.Https) | |||
| 58 | + | } | |||
| 59 | + | } | |||
| 60 | + | ||||
| 61 | + | // Admin Configuration | |||
| 62 | + | if val := os.Getenv("GORDON_ADMIN_PATH"); val != "" { | |||
| 63 | + | config.Admin.Path = val | |||
| 64 | + | if printLogs { | |||
| 65 | + | logger.Info("Using environment variable GORDON_ADMIN_PATH", "value", val) | |||
| 66 | + | } | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | // Container Engine Configuration | |||
| 70 | + | if val := os.Getenv("GORDON_CONTAINER_SOCK"); val != "" { | |||
| 71 | + | config.ContainerEngine.Sock = val | |||
| 72 | + | if printLogs { | |||
| 73 | + | logger.Info("Using environment variable GORDON_CONTAINER_SOCK", "value", val) | |||
| 74 | + | } | |||
| 75 | + | } | |||
| 76 | + | if val := os.Getenv("GORDON_CONTAINER_PODMANSOCK"); val != "" { | |||
| 77 | + | config.ContainerEngine.PodmanSock = val | |||
| 78 | + | if printLogs { | |||
| 79 | + | logger.Info("Using environment variable GORDON_CONTAINER_PODMANSOCK", "value", val) | |||
| 80 | + | } | |||
| 81 | + | } | |||
| 82 | + | if val := os.Getenv("GORDON_CONTAINER_PODMAN"); val != "" { | |||
| 83 | + | config.ContainerEngine.Podman = strings.ToLower(val) == "true" | |||
| 84 | + | if printLogs { | |||
| 85 | + | logger.Info("Using environment variable GORDON_CONTAINER_PODMAN", "value", config.ContainerEngine.Podman) | |||
| 86 | + | } | |||
| 87 | + | } else { | |||
| 88 | + | // Auto-detect Podman if not specified in environment | |||
| 89 | + | isPodman, podmanSocket := docker.DetectPodman() | |||
| 90 | + | if isPodman { | |||
| 91 | + | config.ContainerEngine.Podman = true | |||
| 92 | + | config.ContainerEngine.PodmanSock = podmanSocket | |||
| 93 | + | if printLogs { | |||
| 94 | + | logger.Info("Automatically detected Podman installation", | |||
| 95 | + | "using_podman", true, | |||
| 96 | + | "socket", podmanSocket) | |||
| 97 | + | } | |||
| 98 | + | } | |||
| 99 | + | ||||
| 100 | + | // If we're in a container, check for special socket naming patterns | |||
| 101 | + | if docker.IsRunningInContainer() && fileExists(config.ContainerEngine.Sock) { | |||
| 102 | + | // Check if socket path contains "podman" which indicates it might be a podman socket | |||
| 103 | + | if strings.Contains(config.ContainerEngine.Sock, "podman") { | |||
| 104 | + | config.ContainerEngine.Podman = true | |||
| 105 | + | if printLogs { | |||
| 106 | + | logger.Info("Socket path contains 'podman', enabling Podman mode", "path", config.ContainerEngine.Sock) | |||
| 107 | + | } | |||
| 108 | + | } | |||
| 109 | + | } | |||
| 110 | + | } | |||
| 111 | + | if val := os.Getenv("GORDON_CONTAINER_NETWORK"); val != "" { | |||
| 112 | + | config.ContainerEngine.Network = val | |||
| 113 | + | if printLogs { | |||
| 114 | + | logger.Info("Using environment variable GORDON_CONTAINER_NETWORK", "value", val) | |||
| 115 | + | } | |||
| 116 | + | } | |||
| 117 | + | ||||
| 118 | + | // Use Podman socket if Podman is enabled and no Docker socket is specified | |||
| 119 | + | if config.ContainerEngine.Podman && config.ContainerEngine.Sock == "" && config.ContainerEngine.PodmanSock != "" { | |||
| 120 | + | config.ContainerEngine.Sock = config.ContainerEngine.PodmanSock | |||
| 121 | + | if printLogs { | |||
| 122 | + | logger.Info("Setting ContainerEngine.Sock to PodmanSock value", "value", config.ContainerEngine.Sock) | |||
| 123 | + | } | |||
| 124 | + | } | |||
| 125 | + | ||||
| 126 | + | // Additional debug info for container sockets | |||
| 127 | + | if docker.IsRunningInContainer() { | |||
| 128 | + | logger.Debug("Container socket configuration", | |||
| 129 | + | "sock", config.ContainerEngine.Sock, | |||
| 130 | + | "podmansock", config.ContainerEngine.PodmanSock, | |||
| 131 | + | "podman", config.ContainerEngine.Podman, | |||
| 132 | + | "sock_exists", fileExists(config.ContainerEngine.Sock), | |||
| 133 | + | "podmansock_exists", fileExists(config.ContainerEngine.PodmanSock)) | |||
| 134 | + | ||||
| 135 | + | // Special handling for container environment to ensure sockets are properly set | |||
| 136 | + | // If no socket is configured but default socket path exists, use it | |||
| 137 | + | if config.ContainerEngine.Sock == "" && fileExists(sock) { | |||
| 138 | + | config.ContainerEngine.Sock = sock | |||
| 139 | + | logger.Info("Found default Docker socket in container, using it", "path", sock) | |||
| 140 | + | } | |||
| 141 | + | ||||
| 142 | + | // If podman socket exists but podman mode is not enabled, check if we should enable it | |||
| 143 | + | if fileExists(podmansock) && !config.ContainerEngine.Podman { | |||
| 144 | + | // If the main socket path contains "podman", enable podman mode | |||
| 145 | + | if strings.Contains(config.ContainerEngine.Sock, "podman") { | |||
| 146 | + | config.ContainerEngine.Podman = true | |||
| 147 | + | logger.Info("Socket path suggests Podman usage, enabling Podman mode", "path", config.ContainerEngine.Sock) | |||
| 148 | + | } | |||
| 149 | + | } | |||
| 150 | + | } | |||
| 151 | + | ||||
| 152 | + | // Reverse Proxy Configuration | |||
| 153 | + | if val := os.Getenv("GORDON_PROXY_PORT"); val != "" { | |||
| 154 | + | config.ReverseProxy.Port = val | |||
| 155 | + | if printLogs { | |||
| 156 | + | logger.Info("Using environment variable GORDON_PROXY_PORT", "value", val) | |||
| 157 | + | } | |||
| 158 | + | } | |||
| 159 | + | if val := os.Getenv("GORDON_PROXY_HTTP_PORT"); val != "" { | |||
| 160 | + | config.ReverseProxy.HttpPort = val | |||
| 161 | + | if printLogs { | |||
| 162 | + | logger.Info("Using environment variable GORDON_PROXY_HTTP_PORT", "value", val) | |||
| 163 | + | } | |||
| 164 | + | } | |||
| 165 | + | ||||
| 166 | + | // New environment variables for reverse proxy port control | |||
| 167 | + | if val := os.Getenv("GORDON_USE_FALLBACK_BINDING"); val != "" { | |||
| 168 | + | // This is handled at runtime in the proxy server but we log it here | |||
| 169 | + | if printLogs { | |||
| 170 | + | logger.Info("Found environment variable GORDON_USE_FALLBACK_BINDING", "value", val) | |||
| 171 | + | } | |||
| 172 | + | } | |||
| 173 | + | ||||
| 174 | + | // Handle GORDON_PROXY_ENABLED environment variable | |||
| 175 | + | if val := os.Getenv("GORDON_PROXY_ENABLED"); val != "" { | |||
| 176 | + | enabled, err := strconv.ParseBool(val) | |||
| 177 | + | if err == nil { | |||
| 178 | + | config.ReverseProxy.Enabled = enabled | |||
| 179 | + | } | |||
| 180 | + | if printLogs { | |||
| 181 | + | logger.Info("Using environment variable GORDON_PROXY_ENABLED", "value", config.ReverseProxy.Enabled) | |||
| 182 | + | } | |||
| 183 | + | } | |||
| 184 | + | if val := os.Getenv("GORDON_PROXY_CERT_DIR"); val != "" { | |||
| 185 | + | config.ReverseProxy.CertDir = val | |||
| 186 | + | if printLogs { | |||
| 187 | + | logger.Info("Using environment variable GORDON_PROXY_CERT_DIR", "value", val) | |||
| 188 | + | } | |||
| 189 | + | } | |||
| 190 | + | if val := os.Getenv("GORDON_PROXY_RENEW_BEFORE"); val != "" { | |||
| 191 | + | if i, err := strconv.Atoi(val); err == nil { | |||
| 192 | + | config.ReverseProxy.RenewBefore = i | |||
| 193 | + | if printLogs { | |||
| 194 | + | logger.Info("Using environment variable GORDON_PROXY_RENEW_BEFORE", "value", i) | |||
| 195 | + | } | |||
| 196 | + | } | |||
| 197 | + | } | |||
| 198 | + | if val := os.Getenv("GORDON_PROXY_LETSENCRYPT_MODE"); val != "" { | |||
| 199 | + | config.ReverseProxy.LetsEncryptMode = val | |||
| 200 | + | if printLogs { | |||
| 201 | + | logger.Info("Using environment variable GORDON_PROXY_LETSENCRYPT_MODE", "value", val) | |||
| 202 | + | } | |||
| 203 | + | } | |||
| 204 | + | if val := os.Getenv("GORDON_PROXY_EMAIL"); val != "" { | |||
| 205 | + | config.ReverseProxy.Email = val | |||
| 206 | + | if printLogs { | |||
| 207 | + | logger.Info("Using environment variable GORDON_PROXY_EMAIL", "value", val) | |||
| 208 | + | } | |||
| 209 | + | } | |||
| 210 | + | if val := os.Getenv("GORDON_PROXY_GRACE_PERIOD"); val != "" { | |||
| 211 | + | i, err := strconv.Atoi(val) | |||
| 212 | + | if err == nil { | |||
| 213 | + | config.ReverseProxy.GracePeriod = i | |||
| 214 | + | } | |||
| 215 | + | logger.Info("Using environment variable GORDON_PROXY_GRACE_PERIOD", "value", i) | |||
| 216 | + | } | |||
| 217 | + | ||||
| 218 | + | // Handle GORDON_PROXY_ENABLE_HTTP_LOGS environment variable | |||
| 219 | + | if val := os.Getenv("GORDON_PROXY_ENABLE_HTTP_LOGS"); val != "" { | |||
| 220 | + | enableLogs, err := strconv.ParseBool(val) | |||
| 221 | + | if err == nil { | |||
| 222 | + | config.ReverseProxy.EnableHttpLogs = enableLogs | |||
| 223 | + | } | |||
| 224 | + | logger.Info("Using environment variable GORDON_PROXY_ENABLE_HTTP_LOGS", "value", config.ReverseProxy.EnableHttpLogs) | |||
| 225 | + | } else if val := os.Getenv("GORDON_PROXY_ENABLE_LOGS"); val != "" { | |||
| 226 | + | // For backward compatibility | |||
| 227 | + | enableLogs, err := strconv.ParseBool(val) | |||
| 228 | + | if err == nil { | |||
| 229 | + | config.ReverseProxy.EnableHttpLogs = enableLogs | |||
| 230 | + | } | |||
| 231 | + | logger.Info("Using environment variable GORDON_PROXY_ENABLE_LOGS (deprecated, use GORDON_PROXY_ENABLE_HTTP_LOGS)", "value", config.ReverseProxy.EnableHttpLogs) | |||
| 232 | + | } | |||
| 233 | + | ||||
| 234 | + | // Handle GORDON_PROXY_ENABLE_RATE_LIMIT environment variable | |||
| 235 | + | if val := os.Getenv("GORDON_PROXY_ENABLE_RATE_LIMIT"); val != "" { | |||
| 236 | + | enableRateLimit, err := strconv.ParseBool(val) | |||
| 237 | + | if err == nil { | |||
| 238 | + | config.ReverseProxy.EnableRateLimit = enableRateLimit | |||
| 239 | + | } | |||
| 240 | + | logger.Info("Using environment variable GORDON_PROXY_ENABLE_RATE_LIMIT", "value", config.ReverseProxy.EnableRateLimit) | |||
| 241 | + | } else if val := os.Getenv("GORDON_PROXY_DISABLE_RATE_LIMIT"); val != "" { | |||
| 242 | + | // For backward compatibility | |||
| 243 | + | disableRateLimit, err := strconv.ParseBool(val) | |||
| 244 | + | if err == nil { | |||
| 245 | + | // Invert the logic | |||
| 246 | + | config.ReverseProxy.EnableRateLimit = !disableRateLimit | |||
| 247 | + | } | |||
| 248 | + | logger.Info("Using environment variable GORDON_PROXY_DISABLE_RATE_LIMIT (deprecated, use GORDON_PROXY_ENABLE_RATE_LIMIT)", "value", !config.ReverseProxy.EnableRateLimit) | |||
| 249 | + | } | |||
| 250 | + | ||||
| 251 | + | // Handle GORDON_PROXY_DETECT_UPSTREAM environment variable | |||
| 252 | + | if val := os.Getenv("GORDON_PROXY_DETECT_UPSTREAM"); val != "" { | |||
| 253 | + | detectUpstream, err := strconv.ParseBool(val) | |||
| 254 | + | if err == nil { | |||
| 255 | + | config.ReverseProxy.DetectUpstreamProxy = detectUpstream | |||
| 256 | + | } | |||
| 257 | + | if printLogs { | |||
| 258 | + | logger.Info("Using environment variable GORDON_PROXY_DETECT_UPSTREAM", "value", config.ReverseProxy.DetectUpstreamProxy) | |||
| 259 | + | } | |||
| 260 | + | } | |||
| 261 | + | ||||
| 262 | + | // Handle GORDON_PROXY_SKIP_CERTIFICATES environment variable | |||
| 263 | + | if val := os.Getenv("GORDON_PROXY_SKIP_CERTIFICATES"); val != "" { | |||
| 264 | + | skipCerts, err := strconv.ParseBool(val) | |||
| 265 | + | if err == nil { | |||
| 266 | + | config.ReverseProxy.SkipCertificates = skipCerts | |||
| 267 | + | } | |||
| 268 | + | if printLogs { | |||
| 269 | + | logger.Info("Using environment variable GORDON_PROXY_SKIP_CERTIFICATES", "value", config.ReverseProxy.SkipCertificates) | |||
| 270 | + | } | |||
| 271 | + | } | |||
| 272 | + | } | |||