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  }