|
| 1 | +// Copyright The Prometheus Authors |
| 2 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 3 | +// you may not use this file except in compliance with the License. |
| 4 | +// You may obtain a copy of the License at |
| 5 | +// |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 7 | +// |
| 8 | +// Unless required by applicable law or agreed to in writing, software |
| 9 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 10 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 11 | +// See the License for the specific language governing permissions and |
| 12 | +// limitations under the License. |
| 13 | + |
| 14 | +package config |
| 15 | + |
| 16 | +import ( |
| 17 | + "fmt" |
| 18 | + "os" |
| 19 | + "strings" |
| 20 | + |
| 21 | + "go.yaml.in/yaml/v3" |
| 22 | +) |
| 23 | + |
| 24 | +// Config represents the YAML configuration file structure. |
| 25 | +type Config struct { |
| 26 | + AuthModules map[string]AuthModule `yaml:"auth_modules"` |
| 27 | +} |
| 28 | + |
| 29 | +type AuthModule struct { |
| 30 | + Type string `yaml:"type"` |
| 31 | + UserPass *UserPassConfig `yaml:"userpass,omitempty"` |
| 32 | + APIKey string `yaml:"apikey,omitempty"` |
| 33 | + AWS *AWSConfig `yaml:"aws,omitempty"` |
| 34 | + TLS *TLSConfig `yaml:"tls,omitempty"` |
| 35 | + Options map[string]string `yaml:"options,omitempty"` |
| 36 | +} |
| 37 | + |
| 38 | +// AWSConfig contains settings for SigV4 authentication. |
| 39 | +type AWSConfig struct { |
| 40 | + Region string `yaml:"region,omitempty"` |
| 41 | + RoleARN string `yaml:"role_arn,omitempty"` |
| 42 | +} |
| 43 | + |
| 44 | +// TLSConfig allows per-target TLS options. |
| 45 | +type TLSConfig struct { |
| 46 | + CAFile string `yaml:"ca_file,omitempty"` |
| 47 | + CertFile string `yaml:"cert_file,omitempty"` |
| 48 | + KeyFile string `yaml:"key_file,omitempty"` |
| 49 | + InsecureSkipVerify bool `yaml:"insecure_skip_verify,omitempty"` |
| 50 | +} |
| 51 | + |
| 52 | +type UserPassConfig struct { |
| 53 | + Username string `yaml:"username"` |
| 54 | + Password string `yaml:"password"` |
| 55 | +} |
| 56 | + |
| 57 | +// validate ensures every auth module has the required fields according to its type. |
| 58 | +func (c *Config) validate() error { |
| 59 | + for name, am := range c.AuthModules { |
| 60 | + // Validate fields based on auth type |
| 61 | + switch strings.ToLower(am.Type) { |
| 62 | + case "userpass": |
| 63 | + if am.UserPass == nil || am.UserPass.Username == "" || am.UserPass.Password == "" { |
| 64 | + return fmt.Errorf("auth_module %s type userpass requires username and password", name) |
| 65 | + } |
| 66 | + case "apikey": |
| 67 | + if am.APIKey == "" { |
| 68 | + return fmt.Errorf("auth_module %s type apikey requires apikey", name) |
| 69 | + } |
| 70 | + case "aws": |
| 71 | + // No strict validation: region can come from environment/defaults; role_arn is optional. |
| 72 | + case "tls": |
| 73 | + // TLS auth type means client certificate authentication only (no other auth) |
| 74 | + if am.TLS == nil { |
| 75 | + return fmt.Errorf("auth_module %s type tls requires tls configuration section", name) |
| 76 | + } |
| 77 | + if am.TLS.CertFile == "" || am.TLS.KeyFile == "" { |
| 78 | + return fmt.Errorf("auth_module %s type tls requires cert_file and key_file for client certificate authentication", name) |
| 79 | + } |
| 80 | + // Validate that other auth fields are not set when using TLS auth type |
| 81 | + if am.UserPass != nil { |
| 82 | + return fmt.Errorf("auth_module %s type tls cannot have userpass configuration", name) |
| 83 | + } |
| 84 | + if am.APIKey != "" { |
| 85 | + return fmt.Errorf("auth_module %s type tls cannot have apikey", name) |
| 86 | + } |
| 87 | + if am.AWS != nil { |
| 88 | + return fmt.Errorf("auth_module %s type tls cannot have aws configuration", name) |
| 89 | + } |
| 90 | + default: |
| 91 | + return fmt.Errorf("auth_module %s has unsupported type %s", name, am.Type) |
| 92 | + } |
| 93 | + |
| 94 | + // Validate TLS configuration (optional for all auth types, provides transport security) |
| 95 | + if am.TLS != nil { |
| 96 | + // For cert-based auth (type: tls), cert and key are required |
| 97 | + // For other auth types, TLS config is optional and used for transport security |
| 98 | + if strings.ToLower(am.Type) != "tls" { |
| 99 | + // For non-TLS auth types, if cert/key are provided, both must be present |
| 100 | + if (am.TLS.CertFile != "") != (am.TLS.KeyFile != "") { |
| 101 | + return fmt.Errorf("auth_module %s: if providing client certificate, both cert_file and key_file must be specified", name) |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + // Validate file accessibility |
| 106 | + for fileType, path := range map[string]string{ |
| 107 | + "ca_file": am.TLS.CAFile, |
| 108 | + "cert_file": am.TLS.CertFile, |
| 109 | + "key_file": am.TLS.KeyFile, |
| 110 | + } { |
| 111 | + if path == "" { |
| 112 | + continue |
| 113 | + } |
| 114 | + if _, err := os.Stat(path); err != nil { |
| 115 | + return fmt.Errorf("auth_module %s: %s '%s' not accessible: %w", name, fileType, path, err) |
| 116 | + } |
| 117 | + } |
| 118 | + } |
| 119 | + } |
| 120 | + return nil |
| 121 | +} |
| 122 | + |
| 123 | +// LoadConfig reads, parses, and validates the YAML config file. |
| 124 | +func LoadConfig(path string) (*Config, error) { |
| 125 | + data, err := os.ReadFile(path) |
| 126 | + if err != nil { |
| 127 | + return nil, err |
| 128 | + } |
| 129 | + var cfg Config |
| 130 | + if err := yaml.Unmarshal(data, &cfg); err != nil { |
| 131 | + return nil, err |
| 132 | + } |
| 133 | + if err := cfg.validate(); err != nil { |
| 134 | + return nil, err |
| 135 | + } |
| 136 | + return &cfg, nil |
| 137 | +} |
0 commit comments