github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/config/identity/ldap/config.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package ldap 19 20 import ( 21 "crypto/x509" 22 "errors" 23 "sort" 24 "time" 25 26 "github.com/minio/madmin-go/v3" 27 "github.com/minio/minio/internal/config" 28 "github.com/minio/pkg/v2/ldap" 29 ) 30 31 const ( 32 defaultLDAPExpiry = time.Hour * 1 33 34 minLDAPExpiry time.Duration = 15 * time.Minute 35 maxLDAPExpiry time.Duration = 365 * 24 * time.Hour 36 ) 37 38 // Config contains AD/LDAP server connectivity information. 39 type Config struct { 40 LDAP ldap.Config 41 42 stsExpiryDuration time.Duration // contains converted value 43 } 44 45 // Enabled returns if LDAP is enabled. 46 func (l *Config) Enabled() bool { 47 return l.LDAP.Enabled 48 } 49 50 // Clone returns a cloned copy of LDAP config. 51 func (l *Config) Clone() Config { 52 if l == nil { 53 return Config{} 54 } 55 cfg := Config{ 56 LDAP: l.LDAP.Clone(), 57 stsExpiryDuration: l.stsExpiryDuration, 58 } 59 return cfg 60 } 61 62 // LDAP keys and envs. 63 const ( 64 ServerAddr = "server_addr" 65 SRVRecordName = "srv_record_name" 66 LookupBindDN = "lookup_bind_dn" 67 LookupBindPassword = "lookup_bind_password" 68 UserDNSearchBaseDN = "user_dn_search_base_dn" 69 UserDNSearchFilter = "user_dn_search_filter" 70 GroupSearchFilter = "group_search_filter" 71 GroupSearchBaseDN = "group_search_base_dn" 72 TLSSkipVerify = "tls_skip_verify" 73 ServerInsecure = "server_insecure" 74 ServerStartTLS = "server_starttls" 75 76 EnvServerAddr = "MINIO_IDENTITY_LDAP_SERVER_ADDR" 77 EnvSRVRecordName = "MINIO_IDENTITY_LDAP_SRV_RECORD_NAME" 78 EnvTLSSkipVerify = "MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY" 79 EnvServerInsecure = "MINIO_IDENTITY_LDAP_SERVER_INSECURE" 80 EnvServerStartTLS = "MINIO_IDENTITY_LDAP_SERVER_STARTTLS" 81 EnvUsernameFormat = "MINIO_IDENTITY_LDAP_USERNAME_FORMAT" 82 EnvUserDNSearchBaseDN = "MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN" 83 EnvUserDNSearchFilter = "MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER" 84 EnvGroupSearchFilter = "MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER" 85 EnvGroupSearchBaseDN = "MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN" 86 EnvLookupBindDN = "MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN" 87 EnvLookupBindPassword = "MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD" 88 ) 89 90 var removedKeys = []string{ 91 "sts_expiry", 92 "username_format", 93 "username_search_filter", 94 "username_search_base_dn", 95 "group_name_attribute", 96 } 97 98 // DefaultKVS - default config for LDAP config 99 var ( 100 DefaultKVS = config.KVS{ 101 config.KV{ 102 Key: config.Enable, 103 Value: "", 104 }, 105 config.KV{ 106 Key: ServerAddr, 107 Value: "", 108 }, 109 config.KV{ 110 Key: SRVRecordName, 111 Value: "", 112 }, 113 config.KV{ 114 Key: UserDNSearchBaseDN, 115 Value: "", 116 }, 117 config.KV{ 118 Key: UserDNSearchFilter, 119 Value: "", 120 }, 121 config.KV{ 122 Key: GroupSearchFilter, 123 Value: "", 124 }, 125 config.KV{ 126 Key: GroupSearchBaseDN, 127 Value: "", 128 }, 129 config.KV{ 130 Key: TLSSkipVerify, 131 Value: config.EnableOff, 132 }, 133 config.KV{ 134 Key: ServerInsecure, 135 Value: config.EnableOff, 136 }, 137 config.KV{ 138 Key: ServerStartTLS, 139 Value: config.EnableOff, 140 }, 141 config.KV{ 142 Key: LookupBindDN, 143 Value: "", 144 }, 145 config.KV{ 146 Key: LookupBindPassword, 147 Value: "", 148 }, 149 } 150 ) 151 152 // Enabled returns if LDAP config is enabled. 153 func Enabled(kvs config.KVS) bool { 154 return kvs.Get(ServerAddr) != "" 155 } 156 157 // Lookup - initializes LDAP config, overrides config, if any ENV values are set. 158 func Lookup(s config.Config, rootCAs *x509.CertPool) (l Config, err error) { 159 l = Config{} 160 161 // Purge all removed keys first 162 kvs := s[config.IdentityLDAPSubSys][config.Default] 163 if len(kvs) > 0 { 164 for _, k := range removedKeys { 165 kvs.Delete(k) 166 } 167 s[config.IdentityLDAPSubSys][config.Default] = kvs 168 } 169 170 if err := s.CheckValidKeys(config.IdentityLDAPSubSys, removedKeys); err != nil { 171 return l, err 172 } 173 174 getCfgVal := func(cfgParam string) string { 175 // As parameters are already validated, we skip checking 176 // if the config param was found. 177 val, _, _ := s.ResolveConfigParam(config.IdentityLDAPSubSys, config.Default, cfgParam, false) 178 return val 179 } 180 181 ldapServer := getCfgVal(ServerAddr) 182 if ldapServer == "" { 183 return l, nil 184 } 185 l.LDAP = ldap.Config{ 186 Enabled: true, 187 RootCAs: rootCAs, 188 ServerAddr: ldapServer, 189 SRVRecordName: getCfgVal(SRVRecordName), 190 } 191 192 // Parse explicitly enable=on/off flag. If not set, defaults to `true` 193 // because ServerAddr is set. 194 if v := getCfgVal(config.Enable); v != "" { 195 l.LDAP.Enabled, err = config.ParseBool(v) 196 if err != nil { 197 return l, err 198 } 199 } 200 201 l.stsExpiryDuration = defaultLDAPExpiry 202 203 // LDAP connection configuration 204 if v := getCfgVal(ServerInsecure); v != "" { 205 l.LDAP.ServerInsecure, err = config.ParseBool(v) 206 if err != nil { 207 return l, err 208 } 209 } 210 if v := getCfgVal(ServerStartTLS); v != "" { 211 l.LDAP.ServerStartTLS, err = config.ParseBool(v) 212 if err != nil { 213 return l, err 214 } 215 } 216 if v := getCfgVal(TLSSkipVerify); v != "" { 217 l.LDAP.TLSSkipVerify, err = config.ParseBool(v) 218 if err != nil { 219 return l, err 220 } 221 } 222 223 // Lookup bind user configuration 224 l.LDAP.LookupBindDN = getCfgVal(LookupBindDN) 225 l.LDAP.LookupBindPassword = getCfgVal(LookupBindPassword) 226 227 // User DN search configuration 228 l.LDAP.UserDNSearchFilter = getCfgVal(UserDNSearchFilter) 229 l.LDAP.UserDNSearchBaseDistName = getCfgVal(UserDNSearchBaseDN) 230 231 // Group search params configuration 232 l.LDAP.GroupSearchFilter = getCfgVal(GroupSearchFilter) 233 l.LDAP.GroupSearchBaseDistName = getCfgVal(GroupSearchBaseDN) 234 235 // Validate and test configuration. 236 valResult := l.LDAP.Validate() 237 if !valResult.IsOk() { 238 return l, valResult 239 } 240 241 return l, nil 242 } 243 244 // GetConfigList - returns a list of LDAP configurations. 245 func (l *Config) GetConfigList(s config.Config) ([]madmin.IDPListItem, error) { 246 ldapConfigs, err := s.GetAvailableTargets(config.IdentityLDAPSubSys) 247 if err != nil { 248 return nil, err 249 } 250 251 // For now, ldapConfigs will only have a single entry for the default 252 // configuration. 253 254 var res []madmin.IDPListItem 255 for _, cfg := range ldapConfigs { 256 res = append(res, madmin.IDPListItem{ 257 Type: "ldap", 258 Name: cfg, 259 Enabled: l.Enabled(), 260 }) 261 } 262 263 return res, nil 264 } 265 266 // ErrProviderConfigNotFound - represents a non-existing provider error. 267 var ErrProviderConfigNotFound = errors.New("provider configuration not found") 268 269 // GetConfigInfo - returns config details for an LDAP configuration. 270 func (l *Config) GetConfigInfo(s config.Config, cfgName string) ([]madmin.IDPCfgInfo, error) { 271 // For now only a single LDAP config is supported. 272 if cfgName != madmin.Default { 273 return nil, ErrProviderConfigNotFound 274 } 275 kvsrcs, err := s.GetResolvedConfigParams(config.IdentityLDAPSubSys, cfgName, true) 276 if err != nil { 277 return nil, err 278 } 279 280 res := make([]madmin.IDPCfgInfo, 0, len(kvsrcs)) 281 for _, kvsrc := range kvsrcs { 282 // skip default values. 283 if kvsrc.Src == config.ValueSourceDef { 284 continue 285 } 286 res = append(res, madmin.IDPCfgInfo{ 287 Key: kvsrc.Key, 288 Value: kvsrc.Value, 289 IsCfg: true, 290 IsEnv: kvsrc.Src == config.ValueSourceEnv, 291 }) 292 } 293 294 // sort the structs by the key 295 sort.Slice(res, func(i, j int) bool { 296 return res[i].Key < res[j].Key 297 }) 298 299 return res, nil 300 }