@@ -6,7 +6,9 @@ package config
66import (
77 "fmt"
88 "io"
9+ "net"
910 "os"
11+ "strconv"
1012 "strings"
1113 "time"
1214
3941 } `yaml:"fake-cpu-meter"`
4042 }
4143 Web struct {
42- Config string `yaml:"configFile"`
44+ Config string `yaml:"configFile"`
45+ ListenAddresses []string `yaml:"listenAddresses"`
4346 }
4447
4548 Monitor struct {
@@ -157,7 +160,8 @@ const (
157160
158161 pprofEnabledFlag = "debug.pprof"
159162
160- WebConfigFlag = "web.config-file"
163+ WebConfigFlag = "web.config-file"
164+ WebListenAddressFlag = "web.listen-address"
161165
162166 // Exporters
163167 ExporterStdoutEnabledFlag = "exporter.stdout"
@@ -210,6 +214,9 @@ func DefaultConfig() *Config {
210214 Enabled : ptr .To (false ),
211215 },
212216 },
217+ Web : Web {
218+ ListenAddresses : []string {":28282" },
219+ },
213220 Kube : Kube {
214221 Enabled : ptr .To (false ),
215222 },
@@ -295,6 +302,7 @@ func RegisterFlags(app *kingpin.Application) ConfigUpdaterFn {
295302
296303 enablePprof := app .Flag (pprofEnabledFlag , "Enable pprof debug endpoints" ).Default ("false" ).Bool ()
297304 webConfig := app .Flag (WebConfigFlag , "Web config file path" ).Default ("" ).String ()
305+ webListenAddresses := app .Flag (WebListenAddressFlag , "Web server listen addresses" ).Default (":28282" ).Strings ()
298306
299307 // exporters
300308 stdoutExporterEnabled := app .Flag (ExporterStdoutEnabledFlag , "Enable stdout exporter" ).Default ("false" ).Bool ()
@@ -343,6 +351,10 @@ func RegisterFlags(app *kingpin.Application) ConfigUpdaterFn {
343351 cfg .Web .Config = * webConfig
344352 }
345353
354+ if flagsSet [WebListenAddressFlag ] {
355+ cfg .Web .ListenAddresses = * webListenAddresses
356+ }
357+
346358 if flagsSet [ExporterStdoutEnabledFlag ] {
347359 cfg .Exporter .Stdout .Enabled = stdoutExporterEnabled
348360 }
@@ -378,6 +390,9 @@ func (c *Config) sanitize() {
378390 c .Host .SysFS = strings .TrimSpace (c .Host .SysFS )
379391 c .Host .ProcFS = strings .TrimSpace (c .Host .ProcFS )
380392 c .Web .Config = strings .TrimSpace (c .Web .Config )
393+ for i := range c .Web .ListenAddresses {
394+ c .Web .ListenAddresses [i ] = strings .TrimSpace (c .Web .ListenAddresses [i ])
395+ }
381396
382397 for i := range c .Rapl .Zones {
383398 c .Rapl .Zones [i ] = strings .TrimSpace (c .Rapl .Zones [i ])
@@ -437,6 +452,20 @@ func (c *Config) Validate(skips ...SkipValidation) error {
437452 }
438453 }
439454 }
455+ { // Web listen addresses
456+ if len (c .Web .ListenAddresses ) == 0 {
457+ errs = append (errs , "at least one web listen address must be specified" )
458+ }
459+ for _ , addr := range c .Web .ListenAddresses {
460+ if addr == "" {
461+ errs = append (errs , "web listen address cannot be empty" )
462+ continue
463+ }
464+ if err := validateListenAddress (addr ); err != nil {
465+ errs = append (errs , fmt .Sprintf ("invalid web listen address %q: %s" , addr , err .Error ()))
466+ }
467+ }
468+ }
440469 { // Monitor
441470 if c .Monitor .Interval < 0 {
442471 errs = append (errs , fmt .Sprintf ("invalid monitor interval: %s can't be negative" , c .Monitor .Interval ))
@@ -506,6 +535,37 @@ func canReadFile(path string) error {
506535 return nil
507536}
508537
538+ func validateListenAddress (addr string ) error {
539+ if addr == "" {
540+ return fmt .Errorf ("address cannot be empty" )
541+ }
542+
543+ // Use Go's standard library to parse host:port
544+ _ , port , err := net .SplitHostPort (addr )
545+ if err != nil {
546+ return fmt .Errorf ("invalid address format: %w" , err )
547+ }
548+
549+ // Validate port (host can be empty for listening on all interfaces)
550+ if err := validatePort (port ); err != nil {
551+ return err
552+ }
553+
554+ return nil
555+ }
556+
557+ func validatePort (port string ) error {
558+ portNum , err := strconv .Atoi (port )
559+ if err != nil {
560+ return fmt .Errorf ("port must be numeric, got %s" , port )
561+ }
562+
563+ if portNum < 1 || portNum > 65535 {
564+ return fmt .Errorf ("port must be between 1 and 65535, got %d" , portNum )
565+ }
566+ return nil
567+ }
568+
509569func (c * Config ) String () string {
510570 bytes , err := yaml .Marshal (c )
511571 if err == nil {
0 commit comments