mirror of https://github.com/minio/minio.git
				
				
				
			
		
			
	
	
		
			179 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
		
		
			
		
	
	
			179 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Licensed under the Apache License, Version 2.0 (the "License");
							 | 
						||
| 
								 | 
							
								 * you may not use this file except in compliance with the License.
							 | 
						||
| 
								 | 
							
								 * You may obtain a copy of the License at
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *     http://www.apache.org/licenses/LICENSE-2.0
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Unless required by applicable law or agreed to in writing, software
							 | 
						||
| 
								 | 
							
								 * distributed under the License is distributed on an "AS IS" BASIS,
							 | 
						||
| 
								 | 
							
								 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
							 | 
						||
| 
								 | 
							
								 * See the License for the specific language governing permissions and
							 | 
						||
| 
								 | 
							
								 * limitations under the License.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package cmd
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"crypto/tls"
							 | 
						||
| 
								 | 
							
									"errors"
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"log"
							 | 
						||
| 
								 | 
							
									"os"
							 | 
						||
| 
								 | 
							
									"regexp"
							 | 
						||
| 
								 | 
							
									"time"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									ldap "gopkg.in/ldap.v3"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const (
							 | 
						||
| 
								 | 
							
									defaultLDAPExpiry = time.Hour * 1
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ldapServerConfig contains server connectivity information.
							 | 
						||
| 
								 | 
							
								type ldapServerConfig struct {
							 | 
						||
| 
								 | 
							
									IsEnabled bool `json:"enabled"`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// E.g. "ldap.minio.io:636"
							 | 
						||
| 
								 | 
							
									ServerAddr string `json:"serverAddr"`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// STS credentials expiry duration
							 | 
						||
| 
								 | 
							
									STSExpiryDuration string        `json:"stsExpiryDuration"`
							 | 
						||
| 
								 | 
							
									stsExpiryDuration time.Duration // contains converted value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Skips TLS verification (for testing, not
							 | 
						||
| 
								 | 
							
									// recommended in production).
							 | 
						||
| 
								 | 
							
									SkipTLSVerify bool `json:"skipTLSverify"`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Format string for usernames
							 | 
						||
| 
								 | 
							
									UsernameFormat string `json:"usernameFormat"`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									GroupSearchBaseDN  string `json:"groupSearchBaseDN"`
							 | 
						||
| 
								 | 
							
									GroupSearchFilter  string `json:"groupSearchFilter"`
							 | 
						||
| 
								 | 
							
									GroupNameAttribute string `json:"groupNameAttribute"`
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (l *ldapServerConfig) Connect() (ldapConn *ldap.Conn, err error) {
							 | 
						||
| 
								 | 
							
									if l == nil {
							 | 
						||
| 
								 | 
							
										// Happens when LDAP is not configured.
							 | 
						||
| 
								 | 
							
										return
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if l.SkipTLSVerify {
							 | 
						||
| 
								 | 
							
										ldapConn, err = ldap.DialTLS("tcp", l.ServerAddr, &tls.Config{InsecureSkipVerify: true})
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										ldapConn, err = ldap.DialTLS("tcp", l.ServerAddr, &tls.Config{})
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// newLDAPConfigFromEnv loads configuration from the environment
							 | 
						||
| 
								 | 
							
								func newLDAPConfigFromEnv() (l ldapServerConfig, err error) {
							 | 
						||
| 
								 | 
							
									if ldapServer, ok := os.LookupEnv("MINIO_IDENTITY_LDAP_SERVER_ADDR"); ok {
							 | 
						||
| 
								 | 
							
										l.IsEnabled = true
							 | 
						||
| 
								 | 
							
										l.ServerAddr = ldapServer
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if v := os.Getenv("MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY"); v == "true" {
							 | 
						||
| 
								 | 
							
											l.SkipTLSVerify = true
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if v := os.Getenv("MINIO_IDENTITY_LDAP_STS_EXPIRY"); v != "" {
							 | 
						||
| 
								 | 
							
											expDur, err := time.ParseDuration(v)
							 | 
						||
| 
								 | 
							
											if err != nil {
							 | 
						||
| 
								 | 
							
												return l, errors.New("LDAP expiry time err:" + err.Error())
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											if expDur <= 0 {
							 | 
						||
| 
								 | 
							
												return l, errors.New("LDAP expiry time has to be positive")
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											l.STSExpiryDuration = v
							 | 
						||
| 
								 | 
							
											l.stsExpiryDuration = expDur
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											l.stsExpiryDuration = defaultLDAPExpiry
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if v := os.Getenv("MINIO_IDENTITY_LDAP_USERNAME_FORMAT"); v != "" {
							 | 
						||
| 
								 | 
							
											subs := newSubstituter("username", "test")
							 | 
						||
| 
								 | 
							
											if _, err := subs.substitute(v); err != nil {
							 | 
						||
| 
								 | 
							
												return l, errors.New("Only username may be substituted in the username format")
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											l.UsernameFormat = v
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										grpSearchFilter := os.Getenv("MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER")
							 | 
						||
| 
								 | 
							
										grpSearchNameAttr := os.Getenv("MINIO_IDENTITY_LDAP_GROUP_NAME_ATTRIBUTE")
							 | 
						||
| 
								 | 
							
										grpSearchBaseDN := os.Getenv("MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Either all group params must be set or none must be set.
							 | 
						||
| 
								 | 
							
										allNotSet := grpSearchFilter == "" && grpSearchNameAttr == "" && grpSearchBaseDN == ""
							 | 
						||
| 
								 | 
							
										allSet := grpSearchFilter != "" && grpSearchNameAttr != "" && grpSearchBaseDN != ""
							 | 
						||
| 
								 | 
							
										if !allNotSet && !allSet {
							 | 
						||
| 
								 | 
							
											return l, errors.New("All group related parameters must be set")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if allSet {
							 | 
						||
| 
								 | 
							
											subs := newSubstituter("username", "test", "usernamedn", "test2")
							 | 
						||
| 
								 | 
							
											if _, err := subs.substitute(grpSearchFilter); err != nil {
							 | 
						||
| 
								 | 
							
												return l, errors.New("Only username and usernamedn may be substituted in the group search filter string")
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											l.GroupSearchFilter = grpSearchFilter
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											l.GroupNameAttribute = grpSearchNameAttr
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											subs = newSubstituter("username", "test", "usernamedn", "test2")
							 | 
						||
| 
								 | 
							
											if _, err := subs.substitute(grpSearchBaseDN); err != nil {
							 | 
						||
| 
								 | 
							
												return l, errors.New("Only username and usernamedn may be substituted in the base DN string")
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											l.GroupSearchBaseDN = grpSearchBaseDN
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// substituter - This type is to allow restricted runtime
							 | 
						||
| 
								 | 
							
								// substitutions of variables in LDAP configuration items during
							 | 
						||
| 
								 | 
							
								// runtime.
							 | 
						||
| 
								 | 
							
								type substituter struct {
							 | 
						||
| 
								 | 
							
									vals map[string]string
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// newSubstituter - sets up the substituter for usage, for e.g.:
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//  subber := newSubstituter("username", "john")
							 | 
						||
| 
								 | 
							
								func newSubstituter(v ...string) substituter {
							 | 
						||
| 
								 | 
							
									if len(v)%2 != 0 {
							 | 
						||
| 
								 | 
							
										log.Fatal("Need an even number of arguments")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									vals := make(map[string]string)
							 | 
						||
| 
								 | 
							
									for i := 0; i < len(v); i += 2 {
							 | 
						||
| 
								 | 
							
										vals[v[i]] = v[i+1]
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return substituter{vals: vals}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// substitute - performs substitution on the given string `t`. Returns
							 | 
						||
| 
								 | 
							
								// an error if there are any variables in the input that do not have
							 | 
						||
| 
								 | 
							
								// values in the substituter. E.g.:
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// subber.substitute("uid=${username},cn=users,dc=example,dc=com")
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// returns "uid=john,cn=users,dc=example,dc=com"
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// whereas:
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// subber.substitute("uid=${usernamedn}")
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// returns an error.
							 | 
						||
| 
								 | 
							
								func (s *substituter) substitute(t string) (string, error) {
							 | 
						||
| 
								 | 
							
									for k, v := range s.vals {
							 | 
						||
| 
								 | 
							
										re := regexp.MustCompile(fmt.Sprintf(`\$\{%s\}`, k))
							 | 
						||
| 
								 | 
							
										t = re.ReplaceAllLiteralString(t, v)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									// Check if all requested substitutions have been made.
							 | 
						||
| 
								 | 
							
									re := regexp.MustCompile(`\$\{.*\}`)
							 | 
						||
| 
								 | 
							
									if re.MatchString(t) {
							 | 
						||
| 
								 | 
							
										return "", errors.New("unsupported substitution requested")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return t, nil
							 | 
						||
| 
								 | 
							
								}
							 |