mirror of
https://github.com/minio/minio.git
synced 2025-01-04 03:23:22 -05:00
0c855638de
At server startup, LDAP configuration is validated against the LDAP server. If the LDAP server is down at that point, we need to cleanly disable LDAP configuration. Previously, LDAP would remain configured but error out in strange ways because initialization did not complete without errors.
308 lines
7.9 KiB
Go
308 lines
7.9 KiB
Go
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
//
|
|
// This file is part of MinIO Object Storage stack
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package ldap
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"errors"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/minio/madmin-go/v3"
|
|
"github.com/minio/minio/internal/config"
|
|
"github.com/minio/pkg/v2/ldap"
|
|
)
|
|
|
|
const (
|
|
defaultLDAPExpiry = time.Hour * 1
|
|
|
|
minLDAPExpiry time.Duration = 15 * time.Minute
|
|
maxLDAPExpiry time.Duration = 365 * 24 * time.Hour
|
|
)
|
|
|
|
// Config contains AD/LDAP server connectivity information.
|
|
type Config struct {
|
|
LDAP ldap.Config
|
|
|
|
stsExpiryDuration time.Duration // contains converted value
|
|
}
|
|
|
|
// Enabled returns if LDAP is enabled.
|
|
func (l *Config) Enabled() bool {
|
|
return l.LDAP.Enabled
|
|
}
|
|
|
|
// Clone returns a cloned copy of LDAP config.
|
|
func (l *Config) Clone() Config {
|
|
if l == nil {
|
|
return Config{}
|
|
}
|
|
cfg := Config{
|
|
LDAP: l.LDAP.Clone(),
|
|
stsExpiryDuration: l.stsExpiryDuration,
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
// LDAP keys and envs.
|
|
const (
|
|
ServerAddr = "server_addr"
|
|
SRVRecordName = "srv_record_name"
|
|
LookupBindDN = "lookup_bind_dn"
|
|
LookupBindPassword = "lookup_bind_password"
|
|
UserDNSearchBaseDN = "user_dn_search_base_dn"
|
|
UserDNSearchFilter = "user_dn_search_filter"
|
|
GroupSearchFilter = "group_search_filter"
|
|
GroupSearchBaseDN = "group_search_base_dn"
|
|
TLSSkipVerify = "tls_skip_verify"
|
|
ServerInsecure = "server_insecure"
|
|
ServerStartTLS = "server_starttls"
|
|
|
|
EnvServerAddr = "MINIO_IDENTITY_LDAP_SERVER_ADDR"
|
|
EnvSRVRecordName = "MINIO_IDENTITY_LDAP_SRV_RECORD_NAME"
|
|
EnvTLSSkipVerify = "MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY"
|
|
EnvServerInsecure = "MINIO_IDENTITY_LDAP_SERVER_INSECURE"
|
|
EnvServerStartTLS = "MINIO_IDENTITY_LDAP_SERVER_STARTTLS"
|
|
EnvUsernameFormat = "MINIO_IDENTITY_LDAP_USERNAME_FORMAT"
|
|
EnvUserDNSearchBaseDN = "MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN"
|
|
EnvUserDNSearchFilter = "MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER"
|
|
EnvGroupSearchFilter = "MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER"
|
|
EnvGroupSearchBaseDN = "MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN"
|
|
EnvLookupBindDN = "MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN"
|
|
EnvLookupBindPassword = "MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD"
|
|
)
|
|
|
|
var removedKeys = []string{
|
|
"sts_expiry",
|
|
"username_format",
|
|
"username_search_filter",
|
|
"username_search_base_dn",
|
|
"group_name_attribute",
|
|
}
|
|
|
|
// DefaultKVS - default config for LDAP config
|
|
var (
|
|
DefaultKVS = config.KVS{
|
|
config.KV{
|
|
Key: config.Enable,
|
|
Value: "",
|
|
},
|
|
config.KV{
|
|
Key: ServerAddr,
|
|
Value: "",
|
|
},
|
|
config.KV{
|
|
Key: SRVRecordName,
|
|
Value: "",
|
|
},
|
|
config.KV{
|
|
Key: UserDNSearchBaseDN,
|
|
Value: "",
|
|
},
|
|
config.KV{
|
|
Key: UserDNSearchFilter,
|
|
Value: "",
|
|
},
|
|
config.KV{
|
|
Key: GroupSearchFilter,
|
|
Value: "",
|
|
},
|
|
config.KV{
|
|
Key: GroupSearchBaseDN,
|
|
Value: "",
|
|
},
|
|
config.KV{
|
|
Key: TLSSkipVerify,
|
|
Value: config.EnableOff,
|
|
},
|
|
config.KV{
|
|
Key: ServerInsecure,
|
|
Value: config.EnableOff,
|
|
},
|
|
config.KV{
|
|
Key: ServerStartTLS,
|
|
Value: config.EnableOff,
|
|
},
|
|
config.KV{
|
|
Key: LookupBindDN,
|
|
Value: "",
|
|
},
|
|
config.KV{
|
|
Key: LookupBindPassword,
|
|
Value: "",
|
|
},
|
|
}
|
|
)
|
|
|
|
// Enabled returns if LDAP config is enabled.
|
|
func Enabled(kvs config.KVS) bool {
|
|
return kvs.Get(ServerAddr) != ""
|
|
}
|
|
|
|
// Lookup - initializes LDAP config, overrides config, if any ENV values are set.
|
|
func Lookup(s config.Config, rootCAs *x509.CertPool) (l Config, err error) {
|
|
l = Config{}
|
|
|
|
// Purge all removed keys first
|
|
kvs := s[config.IdentityLDAPSubSys][config.Default]
|
|
if len(kvs) > 0 {
|
|
for _, k := range removedKeys {
|
|
kvs.Delete(k)
|
|
}
|
|
s[config.IdentityLDAPSubSys][config.Default] = kvs
|
|
}
|
|
|
|
if err := s.CheckValidKeys(config.IdentityLDAPSubSys, removedKeys); err != nil {
|
|
return l, err
|
|
}
|
|
|
|
getCfgVal := func(cfgParam string) string {
|
|
// As parameters are already validated, we skip checking
|
|
// if the config param was found.
|
|
val, _, _ := s.ResolveConfigParam(config.IdentityLDAPSubSys, config.Default, cfgParam, false)
|
|
return val
|
|
}
|
|
|
|
ldapServer := getCfgVal(ServerAddr)
|
|
if ldapServer == "" {
|
|
return l, nil
|
|
}
|
|
l.LDAP = ldap.Config{
|
|
RootCAs: rootCAs,
|
|
ServerAddr: ldapServer,
|
|
SRVRecordName: getCfgVal(SRVRecordName),
|
|
}
|
|
|
|
// Parse explicitly set enable=on/off flag.
|
|
isEnableFlagExplicitlySet := false
|
|
if v := getCfgVal(config.Enable); v != "" {
|
|
isEnableFlagExplicitlySet = true
|
|
l.LDAP.Enabled, err = config.ParseBool(v)
|
|
if err != nil {
|
|
return l, err
|
|
}
|
|
}
|
|
|
|
l.stsExpiryDuration = defaultLDAPExpiry
|
|
|
|
// LDAP connection configuration
|
|
if v := getCfgVal(ServerInsecure); v != "" {
|
|
l.LDAP.ServerInsecure, err = config.ParseBool(v)
|
|
if err != nil {
|
|
return l, err
|
|
}
|
|
}
|
|
if v := getCfgVal(ServerStartTLS); v != "" {
|
|
l.LDAP.ServerStartTLS, err = config.ParseBool(v)
|
|
if err != nil {
|
|
return l, err
|
|
}
|
|
}
|
|
if v := getCfgVal(TLSSkipVerify); v != "" {
|
|
l.LDAP.TLSSkipVerify, err = config.ParseBool(v)
|
|
if err != nil {
|
|
return l, err
|
|
}
|
|
}
|
|
|
|
// Lookup bind user configuration
|
|
l.LDAP.LookupBindDN = getCfgVal(LookupBindDN)
|
|
l.LDAP.LookupBindPassword = getCfgVal(LookupBindPassword)
|
|
|
|
// User DN search configuration
|
|
l.LDAP.UserDNSearchFilter = getCfgVal(UserDNSearchFilter)
|
|
l.LDAP.UserDNSearchBaseDistName = getCfgVal(UserDNSearchBaseDN)
|
|
|
|
// Group search params configuration
|
|
l.LDAP.GroupSearchFilter = getCfgVal(GroupSearchFilter)
|
|
l.LDAP.GroupSearchBaseDistName = getCfgVal(GroupSearchBaseDN)
|
|
|
|
// If enable flag was not explicitly set, we treat it as implicitly set at
|
|
// this point as necessary configuration is available.
|
|
if !isEnableFlagExplicitlySet && !l.LDAP.Enabled {
|
|
l.LDAP.Enabled = true
|
|
}
|
|
// Validate and test configuration.
|
|
valResult := l.LDAP.Validate()
|
|
if !valResult.IsOk() {
|
|
// Set to false if configuration fails to validate.
|
|
l.LDAP.Enabled = false
|
|
return l, valResult
|
|
}
|
|
|
|
return l, nil
|
|
}
|
|
|
|
// GetConfigList - returns a list of LDAP configurations.
|
|
func (l *Config) GetConfigList(s config.Config) ([]madmin.IDPListItem, error) {
|
|
ldapConfigs, err := s.GetAvailableTargets(config.IdentityLDAPSubSys)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// For now, ldapConfigs will only have a single entry for the default
|
|
// configuration.
|
|
|
|
var res []madmin.IDPListItem
|
|
for _, cfg := range ldapConfigs {
|
|
res = append(res, madmin.IDPListItem{
|
|
Type: "ldap",
|
|
Name: cfg,
|
|
Enabled: l.Enabled(),
|
|
})
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
// ErrProviderConfigNotFound - represents a non-existing provider error.
|
|
var ErrProviderConfigNotFound = errors.New("provider configuration not found")
|
|
|
|
// GetConfigInfo - returns config details for an LDAP configuration.
|
|
func (l *Config) GetConfigInfo(s config.Config, cfgName string) ([]madmin.IDPCfgInfo, error) {
|
|
// For now only a single LDAP config is supported.
|
|
if cfgName != madmin.Default {
|
|
return nil, ErrProviderConfigNotFound
|
|
}
|
|
kvsrcs, err := s.GetResolvedConfigParams(config.IdentityLDAPSubSys, cfgName, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := make([]madmin.IDPCfgInfo, 0, len(kvsrcs))
|
|
for _, kvsrc := range kvsrcs {
|
|
// skip default values.
|
|
if kvsrc.Src == config.ValueSourceDef {
|
|
continue
|
|
}
|
|
res = append(res, madmin.IDPCfgInfo{
|
|
Key: kvsrc.Key,
|
|
Value: kvsrc.Value,
|
|
IsCfg: true,
|
|
IsEnv: kvsrc.Src == config.ValueSourceEnv,
|
|
})
|
|
}
|
|
|
|
// sort the structs by the key
|
|
sort.Slice(res, func(i, j int) bool {
|
|
return res[i].Key < res[j].Key
|
|
})
|
|
|
|
return res, nil
|
|
}
|