Refactor config and split them in packages (#8351)
This change is related to larger config migration PR change, this is a first stage change to move our configs to `cmd/config/` - divided into its subsystemsmaster
parent
74008446fe
commit
589e32a4ed
@ -0,0 +1,79 @@ |
|||||||
|
/* |
||||||
|
* 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 cache |
||||||
|
|
||||||
|
import ( |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/minio/minio/cmd/config" |
||||||
|
"github.com/minio/minio/pkg/env" |
||||||
|
) |
||||||
|
|
||||||
|
// Cache ENVs
|
||||||
|
const ( |
||||||
|
EnvCacheDrives = "MINIO_CACHE_DRIVES" |
||||||
|
EnvCacheExclude = "MINIO_CACHE_EXCLUDE" |
||||||
|
EnvCacheExpiry = "MINIO_CACHE_EXPIRY" |
||||||
|
EnvCacheMaxUse = "MINIO_CACHE_MAXUSE" |
||||||
|
EnvCacheEncryptionMasterKey = "MINIO_CACHE_ENCRYPTION_MASTER_KEY" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
cacheEnvDelimiter = ";" |
||||||
|
) |
||||||
|
|
||||||
|
// LookupConfig - extracts cache configuration provided by environment
|
||||||
|
// variables and merge them with provided CacheConfiguration.
|
||||||
|
func LookupConfig(cfg Config) (Config, error) { |
||||||
|
if drives := env.Get(EnvCacheDrives, strings.Join(cfg.Drives, ",")); drives != "" { |
||||||
|
driveList, err := parseCacheDrives(strings.Split(drives, cacheEnvDelimiter)) |
||||||
|
if err != nil { |
||||||
|
return cfg, err |
||||||
|
} |
||||||
|
cfg.Drives = driveList |
||||||
|
} |
||||||
|
|
||||||
|
if excludes := env.Get(EnvCacheExclude, strings.Join(cfg.Exclude, ",")); excludes != "" { |
||||||
|
excludeList, err := parseCacheExcludes(strings.Split(excludes, cacheEnvDelimiter)) |
||||||
|
if err != nil { |
||||||
|
return cfg, err |
||||||
|
} |
||||||
|
cfg.Exclude = excludeList |
||||||
|
} |
||||||
|
|
||||||
|
if expiryStr := env.Get(EnvCacheExpiry, strconv.Itoa(cfg.Expiry)); expiryStr != "" { |
||||||
|
expiry, err := strconv.Atoi(expiryStr) |
||||||
|
if err != nil { |
||||||
|
return cfg, config.ErrInvalidCacheExpiryValue(err) |
||||||
|
} |
||||||
|
cfg.Expiry = expiry |
||||||
|
} |
||||||
|
|
||||||
|
if maxUseStr := env.Get(EnvCacheMaxUse, strconv.Itoa(cfg.MaxUse)); maxUseStr != "" { |
||||||
|
maxUse, err := strconv.Atoi(maxUseStr) |
||||||
|
if err != nil { |
||||||
|
return cfg, config.ErrInvalidCacheMaxUse(err) |
||||||
|
} |
||||||
|
// maxUse should be a valid percentage.
|
||||||
|
if maxUse > 0 && maxUse <= 100 { |
||||||
|
cfg.MaxUse = maxUse |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return cfg, nil |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
/* |
||||||
|
* 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 compress |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/minio/minio/cmd/config" |
||||||
|
"github.com/minio/minio/pkg/env" |
||||||
|
) |
||||||
|
|
||||||
|
// Config represents the compression settings.
|
||||||
|
type Config struct { |
||||||
|
Enabled bool `json:"enabled"` |
||||||
|
Extensions []string `json:"extensions"` |
||||||
|
MimeTypes []string `json:"mime-types"` |
||||||
|
} |
||||||
|
|
||||||
|
// Compression environment variables
|
||||||
|
const ( |
||||||
|
EnvMinioCompress = "MINIO_COMPRESS" |
||||||
|
EnvMinioCompressExtensions = "MINIO_COMPRESS_EXTENSIONS" |
||||||
|
EnvMinioCompressMimeTypes = "MINIO_COMPRESS_MIMETYPES" |
||||||
|
) |
||||||
|
|
||||||
|
// Parses the given compression exclude list `extensions` or `content-types`.
|
||||||
|
func parseCompressIncludes(includes []string) ([]string, error) { |
||||||
|
for _, e := range includes { |
||||||
|
if len(e) == 0 { |
||||||
|
return nil, config.ErrInvalidCompressionIncludesValue(nil).Msg("extension/mime-type (%s) cannot be empty", e) |
||||||
|
} |
||||||
|
} |
||||||
|
return includes, nil |
||||||
|
} |
||||||
|
|
||||||
|
// LookupConfig - lookup compression config.
|
||||||
|
func LookupConfig(cfg Config) (Config, error) { |
||||||
|
const compressEnvDelimiter = "," |
||||||
|
if compress := env.Get(EnvMinioCompress, strconv.FormatBool(cfg.Enabled)); compress != "" { |
||||||
|
cfg.Enabled = strings.EqualFold(compress, "true") |
||||||
|
} |
||||||
|
|
||||||
|
compressExtensions := env.Get(EnvMinioCompressExtensions, strings.Join(cfg.Extensions, ",")) |
||||||
|
compressMimeTypes := env.Get(EnvMinioCompressMimeTypes, strings.Join(cfg.MimeTypes, ",")) |
||||||
|
if compressExtensions != "" || compressMimeTypes != "" { |
||||||
|
if compressExtensions != "" { |
||||||
|
extensions, err := parseCompressIncludes(strings.Split(compressExtensions, compressEnvDelimiter)) |
||||||
|
if err != nil { |
||||||
|
return cfg, fmt.Errorf("%s: Invalid MINIO_COMPRESS_EXTENSIONS value (`%s`)", err, extensions) |
||||||
|
} |
||||||
|
cfg.Extensions = extensions |
||||||
|
} |
||||||
|
if compressMimeTypes != "" { |
||||||
|
contenttypes, err := parseCompressIncludes(strings.Split(compressMimeTypes, compressEnvDelimiter)) |
||||||
|
if err != nil { |
||||||
|
return cfg, fmt.Errorf("%s: Invalid MINIO_COMPRESS_MIMETYPES value (`%s`)", err, contenttypes) |
||||||
|
} |
||||||
|
cfg.MimeTypes = contenttypes |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return cfg, nil |
||||||
|
} |
@ -0,0 +1,201 @@ |
|||||||
|
/* |
||||||
|
* 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 ldap |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/tls" |
||||||
|
"crypto/x509" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"regexp" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/minio/minio/pkg/env" |
||||||
|
ldap "gopkg.in/ldap.v3" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
defaultLDAPExpiry = time.Hour * 1 |
||||||
|
) |
||||||
|
|
||||||
|
// Config contains AD/LDAP server connectivity information.
|
||||||
|
type Config 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
|
||||||
|
|
||||||
|
RootCAs *x509.CertPool `json:"-"` |
||||||
|
|
||||||
|
// Format string for usernames
|
||||||
|
UsernameFormat string `json:"usernameFormat"` |
||||||
|
|
||||||
|
GroupSearchBaseDN string `json:"groupSearchBaseDN"` |
||||||
|
GroupSearchFilter string `json:"groupSearchFilter"` |
||||||
|
GroupNameAttribute string `json:"groupNameAttribute"` |
||||||
|
} |
||||||
|
|
||||||
|
// LDAP keys and envs.
|
||||||
|
const ( |
||||||
|
ServerAddr = "server_addr" |
||||||
|
STSExpiry = "sts_expiry" |
||||||
|
UsernameFormat = "username_format" |
||||||
|
GroupSearchFilter = "group_search_filter" |
||||||
|
GroupNameAttribute = "group_name_attribute" |
||||||
|
GroupSearchBaseDN = "group_search_base_dn" |
||||||
|
|
||||||
|
EnvServerAddr = "MINIO_IDENTITY_LDAP_SERVER_ADDR" |
||||||
|
EnvSTSExpiry = "MINIO_IDENTITY_LDAP_STS_EXPIRY" |
||||||
|
EnvUsernameFormat = "MINIO_IDENTITY_LDAP_USERNAME_FORMAT" |
||||||
|
EnvGroupSearchFilter = "MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER" |
||||||
|
EnvGroupNameAttribute = "MINIO_IDENTITY_LDAP_GROUP_NAME_ATTRIBUTE" |
||||||
|
EnvGroupSearchBaseDN = "MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN" |
||||||
|
) |
||||||
|
|
||||||
|
// Connect connect to ldap server.
|
||||||
|
func (l *Config) Connect() (ldapConn *ldap.Conn, err error) { |
||||||
|
if l == nil { |
||||||
|
// Happens when LDAP is not configured.
|
||||||
|
return |
||||||
|
} |
||||||
|
return ldap.DialTLS("tcp", l.ServerAddr, &tls.Config{RootCAs: l.RootCAs}) |
||||||
|
} |
||||||
|
|
||||||
|
// GetExpiryDuration - return parsed expiry duration.
|
||||||
|
func (l Config) GetExpiryDuration() time.Duration { |
||||||
|
return l.stsExpiryDuration |
||||||
|
} |
||||||
|
|
||||||
|
// Lookup - initializes LDAP config, overrides config, if any ENV values are set.
|
||||||
|
func Lookup(cfg Config, rootCAs *x509.CertPool) (l Config, err error) { |
||||||
|
if cfg.ServerAddr == "" && cfg.IsEnabled { |
||||||
|
return l, errors.New("ldap server cannot initialize with empty LDAP server") |
||||||
|
} |
||||||
|
l.RootCAs = rootCAs |
||||||
|
ldapServer := env.Get(EnvServerAddr, cfg.ServerAddr) |
||||||
|
if ldapServer == "" { |
||||||
|
return l, nil |
||||||
|
} |
||||||
|
l.IsEnabled = true |
||||||
|
l.ServerAddr = ldapServer |
||||||
|
l.stsExpiryDuration = defaultLDAPExpiry |
||||||
|
if v := env.Get(EnvSTSExpiry, cfg.STSExpiryDuration); 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 |
||||||
|
} |
||||||
|
|
||||||
|
if v := env.Get(EnvUsernameFormat, cfg.UsernameFormat); v != "" { |
||||||
|
subs, err := NewSubstituter("username", "test") |
||||||
|
if err != nil { |
||||||
|
return l, err |
||||||
|
} |
||||||
|
if _, err := subs.Substitute(v); err != nil { |
||||||
|
return l, fmt.Errorf("Only username may be substituted in the username format: %s", err) |
||||||
|
} |
||||||
|
l.UsernameFormat = v |
||||||
|
} |
||||||
|
|
||||||
|
grpSearchFilter := env.Get(EnvGroupSearchFilter, cfg.GroupSearchFilter) |
||||||
|
grpSearchNameAttr := env.Get(EnvGroupNameAttribute, cfg.GroupNameAttribute) |
||||||
|
grpSearchBaseDN := env.Get(EnvGroupSearchBaseDN, cfg.GroupSearchBaseDN) |
||||||
|
|
||||||
|
// 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, err := NewSubstituter("username", "test", "usernamedn", "test2") |
||||||
|
if err != nil { |
||||||
|
return l, err |
||||||
|
} |
||||||
|
if _, err := subs.Substitute(grpSearchFilter); err != nil { |
||||||
|
return l, fmt.Errorf("Only username and usernamedn may be substituted in the group search filter string: %s", err) |
||||||
|
} |
||||||
|
l.GroupSearchFilter = grpSearchFilter |
||||||
|
l.GroupNameAttribute = grpSearchNameAttr |
||||||
|
subs, err = NewSubstituter("username", "test", "usernamedn", "test2") |
||||||
|
if err != nil { |
||||||
|
return l, err |
||||||
|
} |
||||||
|
if _, err := subs.Substitute(grpSearchBaseDN); err != nil { |
||||||
|
return l, fmt.Errorf("Only username and usernamedn may be substituted in the base DN string: %s", err) |
||||||
|
} |
||||||
|
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, error) { |
||||||
|
if len(v)%2 != 0 { |
||||||
|
return Substituter{}, errors.New("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}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// 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 |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
/* |
||||||
|
* 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 ldap |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
func TestSubstituter(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
KV []string |
||||||
|
SubstitutableStr string |
||||||
|
SubstitutedStr string |
||||||
|
ErrExpected bool |
||||||
|
}{ |
||||||
|
{ |
||||||
|
KV: []string{"username", "john"}, |
||||||
|
SubstitutableStr: "uid=${username},cn=users,dc=example,dc=com", |
||||||
|
SubstitutedStr: "uid=john,cn=users,dc=example,dc=com", |
||||||
|
ErrExpected: false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
KV: []string{"username", "john"}, |
||||||
|
SubstitutableStr: "uid=${usernamedn},cn=users,dc=example,dc=com", |
||||||
|
ErrExpected: true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
KV: []string{"username"}, |
||||||
|
SubstitutableStr: "uid=${usernamedn},cn=users,dc=example,dc=com", |
||||||
|
ErrExpected: true, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
for i, test := range tests { |
||||||
|
test := test |
||||||
|
t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) { |
||||||
|
subber, err := NewSubstituter(test.KV...) |
||||||
|
if err != nil && !test.ErrExpected { |
||||||
|
t.Errorf("Unexpected failure %s", err) |
||||||
|
} |
||||||
|
gotStr, err := subber.Substitute(test.SubstitutableStr) |
||||||
|
if err != nil && !test.ErrExpected { |
||||||
|
t.Errorf("Unexpected failure %s", err) |
||||||
|
} |
||||||
|
if gotStr != test.SubstitutedStr { |
||||||
|
t.Errorf("Expected %s, got %s", test.SubstitutedStr, gotStr) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -1,180 +0,0 @@ |
|||||||
/* |
|
||||||
* 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" |
|
||||||
"crypto/x509" |
|
||||||
"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
|
|
||||||
rootCAs *x509.CertPool // contains custom CAs for ldaps server.
|
|
||||||
|
|
||||||
// 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{RootCAs: l.rootCAs, InsecureSkipVerify: true}) |
|
||||||
} else { |
|
||||||
ldapConn, err = ldap.DialTLS("tcp", l.ServerAddr, &tls.Config{RootCAs: l.rootCAs}) |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// newLDAPConfigFromEnv loads configuration from the environment
|
|
||||||
func newLDAPConfigFromEnv(rootCAs *x509.CertPool) (l ldapServerConfig, err error) { |
|
||||||
if ldapServer, ok := os.LookupEnv("MINIO_IDENTITY_LDAP_SERVER_ADDR"); ok { |
|
||||||
l.IsEnabled = ok |
|
||||||
l.ServerAddr = ldapServer |
|
||||||
|
|
||||||
// Save root CAs
|
|
||||||
l.rootCAs = rootCAs |
|
||||||
l.SkipTLSVerify = os.Getenv("MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY") == "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 |
|
||||||
} |
|
@ -0,0 +1,85 @@ |
|||||||
|
package color |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"os" |
||||||
|
|
||||||
|
"github.com/fatih/color" |
||||||
|
"github.com/mattn/go-isatty" |
||||||
|
) |
||||||
|
|
||||||
|
// global colors.
|
||||||
|
var ( |
||||||
|
// Check if we stderr, stdout are dumb terminals, we do not apply
|
||||||
|
// ansi coloring on dumb terminals.
|
||||||
|
IsTerminal = func() bool { |
||||||
|
return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd()) |
||||||
|
} |
||||||
|
|
||||||
|
Bold = func() func(a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.Bold).SprintFunc() |
||||||
|
} |
||||||
|
return fmt.Sprint |
||||||
|
}() |
||||||
|
Red = func() func(format string, a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.FgRed).SprintfFunc() |
||||||
|
} |
||||||
|
return fmt.Sprintf |
||||||
|
}() |
||||||
|
Blue = func() func(format string, a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.FgBlue).SprintfFunc() |
||||||
|
} |
||||||
|
return fmt.Sprintf |
||||||
|
}() |
||||||
|
Yellow = func() func(format string, a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.FgYellow).SprintfFunc() |
||||||
|
} |
||||||
|
return fmt.Sprintf |
||||||
|
}() |
||||||
|
CyanBold = func() func(a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
color.New(color.FgCyan, color.Bold).SprintFunc() |
||||||
|
} |
||||||
|
return fmt.Sprint |
||||||
|
}() |
||||||
|
YellowBold = func() func(format string, a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.FgYellow, color.Bold).SprintfFunc() |
||||||
|
} |
||||||
|
return fmt.Sprintf |
||||||
|
}() |
||||||
|
BgYellow = func() func(format string, a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.BgYellow).SprintfFunc() |
||||||
|
} |
||||||
|
return fmt.Sprintf |
||||||
|
}() |
||||||
|
Black = func() func(format string, a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.FgBlack).SprintfFunc() |
||||||
|
} |
||||||
|
return fmt.Sprintf |
||||||
|
}() |
||||||
|
FgRed = func() func(a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.FgRed).SprintFunc() |
||||||
|
} |
||||||
|
return fmt.Sprint |
||||||
|
}() |
||||||
|
BgRed = func() func(format string, a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.BgRed).SprintfFunc() |
||||||
|
} |
||||||
|
return fmt.Sprintf |
||||||
|
}() |
||||||
|
FgWhite = func() func(format string, a ...interface{}) string { |
||||||
|
if IsTerminal() { |
||||||
|
return color.New(color.FgWhite).SprintfFunc() |
||||||
|
} |
||||||
|
return fmt.Sprintf |
||||||
|
}() |
||||||
|
) |
@ -0,0 +1,21 @@ |
|||||||
|
package env |
||||||
|
|
||||||
|
import "os" |
||||||
|
|
||||||
|
// Get retrieves the value of the environment variable named
|
||||||
|
// by the key. If the variable is present in the environment the
|
||||||
|
// value (which may be empty) is returned. Otherwise it returns
|
||||||
|
// the specified default value.
|
||||||
|
func Get(key, defaultValue string) string { |
||||||
|
if v, ok := os.LookupEnv(key); ok { |
||||||
|
return v |
||||||
|
} |
||||||
|
return defaultValue |
||||||
|
} |
||||||
|
|
||||||
|
// Lookup retrieves the value of the environment variable named
|
||||||
|
// by the key. If the variable is present in the environment the
|
||||||
|
// value (which may be empty) is returned and the boolean is true.
|
||||||
|
// Otherwise the returned value will be empty and the boolean will
|
||||||
|
// be false.
|
||||||
|
func Lookup(key string) (string, bool) { return os.LookupEnv(key) } |
Loading…
Reference in new issue