2021-04-18 15:41:13 -04:00
|
|
|
// 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/>.
|
2018-10-09 17:00:01 -04:00
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
2019-06-20 18:28:33 -04:00
|
|
|
"bytes"
|
2018-10-09 17:00:01 -04:00
|
|
|
"context"
|
2020-03-17 13:36:13 -04:00
|
|
|
"encoding/base64"
|
2020-04-14 14:28:56 -04:00
|
|
|
"encoding/json"
|
2020-06-09 22:19:03 -04:00
|
|
|
"errors"
|
2020-03-17 13:36:13 -04:00
|
|
|
"fmt"
|
2020-11-02 20:52:13 -05:00
|
|
|
"math/rand"
|
2021-10-18 14:21:57 -04:00
|
|
|
"path"
|
2021-12-02 18:43:39 -05:00
|
|
|
"sort"
|
2020-05-11 16:04:11 -04:00
|
|
|
"strings"
|
2020-11-08 00:03:06 -05:00
|
|
|
"sync"
|
2022-04-03 16:08:59 -04:00
|
|
|
"sync/atomic"
|
2020-06-09 22:19:03 -04:00
|
|
|
"time"
|
2018-10-09 17:00:01 -04:00
|
|
|
|
2020-08-05 16:08:40 -04:00
|
|
|
humanize "github.com/dustin/go-humanize"
|
2022-12-06 16:46:50 -05:00
|
|
|
"github.com/minio/madmin-go/v2"
|
2020-07-14 12:38:05 -04:00
|
|
|
"github.com/minio/minio-go/v7/pkg/set"
|
2021-11-26 22:22:40 -05:00
|
|
|
"github.com/minio/minio/internal/arn"
|
2021-06-01 17:59:40 -04:00
|
|
|
"github.com/minio/minio/internal/auth"
|
2021-11-26 22:22:40 -05:00
|
|
|
"github.com/minio/minio/internal/color"
|
2022-08-08 19:16:27 -04:00
|
|
|
"github.com/minio/minio/internal/config"
|
2022-05-25 21:32:53 -04:00
|
|
|
xldap "github.com/minio/minio/internal/config/identity/ldap"
|
|
|
|
"github.com/minio/minio/internal/config/identity/openid"
|
2022-08-08 19:16:27 -04:00
|
|
|
idplugin "github.com/minio/minio/internal/config/identity/plugin"
|
2023-02-27 02:37:00 -05:00
|
|
|
xtls "github.com/minio/minio/internal/config/identity/tls"
|
2022-08-08 19:16:27 -04:00
|
|
|
"github.com/minio/minio/internal/config/policy/opa"
|
|
|
|
polplugin "github.com/minio/minio/internal/config/policy/plugin"
|
|
|
|
xhttp "github.com/minio/minio/internal/http"
|
2022-03-14 12:09:22 -04:00
|
|
|
"github.com/minio/minio/internal/jwt"
|
2021-06-01 17:59:40 -04:00
|
|
|
"github.com/minio/minio/internal/logger"
|
2021-05-30 00:16:42 -04:00
|
|
|
iampolicy "github.com/minio/pkg/iam/policy"
|
2021-10-20 06:22:35 -04:00
|
|
|
etcd "go.etcd.io/etcd/client/v3"
|
2018-10-09 17:00:01 -04:00
|
|
|
)
|
|
|
|
|
2019-09-09 19:12:29 -04:00
|
|
|
// UsersSysType - defines the type of users and groups system that is
|
|
|
|
// active on the server.
|
|
|
|
type UsersSysType string
|
|
|
|
|
|
|
|
// Types of users configured in the server.
|
|
|
|
const (
|
|
|
|
// This mode uses the internal users system in MinIO.
|
|
|
|
MinIOUsersSysType UsersSysType = "MinIOUsersSys"
|
|
|
|
|
|
|
|
// This mode uses users and groups from a configured LDAP
|
|
|
|
// server.
|
|
|
|
LDAPUsersSysType UsersSysType = "LDAPUsersSys"
|
|
|
|
)
|
|
|
|
|
2019-08-02 17:25:00 -04:00
|
|
|
const (
|
|
|
|
statusEnabled = "enabled"
|
|
|
|
statusDisabled = "disabled"
|
|
|
|
)
|
|
|
|
|
2022-05-02 20:56:19 -04:00
|
|
|
const (
|
|
|
|
embeddedPolicyType = "embedded-policy"
|
|
|
|
inheritedPolicyType = "inherited-policy"
|
|
|
|
)
|
|
|
|
|
2018-10-09 17:00:01 -04:00
|
|
|
// IAMSys - config system.
|
|
|
|
type IAMSys struct {
|
2022-04-10 01:19:44 -04:00
|
|
|
// Need to keep them here to keep alignment - ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
|
|
|
// metrics
|
|
|
|
LastRefreshTimeUnixNano uint64
|
|
|
|
LastRefreshDurationMilliseconds uint64
|
|
|
|
TotalRefreshSuccesses uint64
|
|
|
|
TotalRefreshFailures uint64
|
|
|
|
|
2020-11-08 00:03:06 -05:00
|
|
|
sync.Mutex
|
|
|
|
|
2021-11-01 18:03:07 -04:00
|
|
|
iamRefreshInterval time.Duration
|
2023-02-27 02:37:00 -05:00
|
|
|
|
|
|
|
LDAPConfig xldap.Config // only valid if usersSysType is LDAPUsers
|
|
|
|
OpenIDConfig openid.Config // only valid if OpenID is configured
|
|
|
|
STSTLSConfig xtls.Config // only valid if STS TLS is configured
|
2021-11-01 18:03:07 -04:00
|
|
|
|
2019-09-09 19:12:29 -04:00
|
|
|
usersSysType UsersSysType
|
|
|
|
|
2021-11-26 22:22:40 -05:00
|
|
|
rolesMap map[arn.ARN]string
|
|
|
|
|
2019-08-08 18:10:04 -04:00
|
|
|
// Persistence layer for IAM subsystem
|
2021-11-03 22:47:49 -04:00
|
|
|
store *IAMStoreSys
|
2021-03-02 20:08:25 -05:00
|
|
|
|
|
|
|
// configLoaded will be closed and remain so after first load.
|
|
|
|
configLoaded chan struct{}
|
2019-07-24 20:34:23 -04:00
|
|
|
}
|
|
|
|
|
2020-03-17 13:36:13 -04:00
|
|
|
// IAMUserType represents a user type inside MinIO server
|
|
|
|
type IAMUserType int
|
|
|
|
|
|
|
|
const (
|
2022-08-23 14:11:45 -04:00
|
|
|
unknownIAMUserType IAMUserType = iota - 1
|
|
|
|
regUser
|
2020-03-17 13:36:13 -04:00
|
|
|
stsUser
|
2021-07-09 14:17:21 -04:00
|
|
|
svcUser
|
2020-03-17 13:36:13 -04:00
|
|
|
)
|
|
|
|
|
2019-08-02 17:25:00 -04:00
|
|
|
// LoadGroup - loads a specific group from storage, and updates the
|
|
|
|
// memberships cache. If the specified group does not exist in
|
|
|
|
// storage, it is removed from in-memory maps as well - this
|
|
|
|
// simplifies the implementation for group removal. This is called
|
|
|
|
// only via IAM notifications.
|
2021-11-15 17:14:22 -05:00
|
|
|
func (sys *IAMSys) LoadGroup(ctx context.Context, objAPI ObjectLayer, group string) error {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2019-12-10 23:28:22 -05:00
|
|
|
return errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
return sys.store.GroupNotificationHandler(ctx, group)
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
|
2019-06-06 20:46:22 -04:00
|
|
|
// LoadPolicy - reloads a specific canned policy from backend disks or etcd.
|
2021-11-15 17:14:22 -05:00
|
|
|
func (sys *IAMSys) LoadPolicy(ctx context.Context, objAPI ObjectLayer, policyName string) error {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2019-12-10 23:28:22 -05:00
|
|
|
return errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
return sys.store.PolicyNotificationHandler(ctx, policyName)
|
2019-06-06 20:46:22 -04:00
|
|
|
}
|
|
|
|
|
2019-08-13 16:41:06 -04:00
|
|
|
// LoadPolicyMapping - loads the mapped policy for a user or group
|
|
|
|
// from storage into server memory.
|
2022-08-23 14:11:45 -04:00
|
|
|
func (sys *IAMSys) LoadPolicyMapping(ctx context.Context, objAPI ObjectLayer, userOrGroup string, userType IAMUserType, isGroup bool) error {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2019-12-10 23:28:22 -05:00
|
|
|
return errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
return sys.store.PolicyMappingNotificationHandler(ctx, userOrGroup, isGroup, userType)
|
2019-08-13 16:41:06 -04:00
|
|
|
}
|
|
|
|
|
2019-06-06 20:46:22 -04:00
|
|
|
// LoadUser - reloads a specific user from backend disks or etcd.
|
2021-11-15 17:14:22 -05:00
|
|
|
func (sys *IAMSys) LoadUser(ctx context.Context, objAPI ObjectLayer, accessKey string, userType IAMUserType) error {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2019-12-10 23:28:22 -05:00
|
|
|
return errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
return sys.store.UserNotificationHandler(ctx, accessKey, userType)
|
2019-06-06 20:46:22 -04:00
|
|
|
}
|
|
|
|
|
2020-04-24 15:10:09 -04:00
|
|
|
// LoadServiceAccount - reloads a specific service account from backend disks or etcd.
|
2021-11-15 17:14:22 -05:00
|
|
|
func (sys *IAMSys) LoadServiceAccount(ctx context.Context, accessKey string) error {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2020-04-24 15:10:09 -04:00
|
|
|
return errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
return sys.store.UserNotificationHandler(ctx, accessKey, svcUser)
|
2020-04-24 15:10:09 -04:00
|
|
|
}
|
|
|
|
|
2021-11-12 00:03:02 -05:00
|
|
|
// initStore initializes IAM stores
|
|
|
|
func (sys *IAMSys) initStore(objAPI ObjectLayer, etcdClient *etcd.Client) {
|
2023-02-24 21:37:22 -05:00
|
|
|
if sys.LDAPConfig.Enabled() {
|
2022-08-23 14:11:45 -04:00
|
|
|
sys.SetUsersSysType(LDAPUsersSysType)
|
2021-11-03 22:47:49 -04:00
|
|
|
}
|
|
|
|
|
2021-10-20 06:22:35 -04:00
|
|
|
if etcdClient == nil {
|
2022-10-24 20:44:15 -04:00
|
|
|
sys.store = &IAMStoreSys{newIAMObjectStore(objAPI, sys.usersSysType)}
|
2019-08-08 18:10:04 -04:00
|
|
|
} else {
|
2021-11-03 22:47:49 -04:00
|
|
|
sys.store = &IAMStoreSys{newIAMEtcdStore(etcdClient, sys.usersSysType)}
|
2020-04-07 17:26:39 -04:00
|
|
|
}
|
2020-10-19 12:54:40 -04:00
|
|
|
}
|
2019-08-08 18:10:04 -04:00
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
// Initialized checks if IAM is initialized
|
2020-11-08 00:03:06 -05:00
|
|
|
func (sys *IAMSys) Initialized() bool {
|
|
|
|
if sys == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
sys.Lock()
|
|
|
|
defer sys.Unlock()
|
|
|
|
return sys.store != nil
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
// Load - loads all credentials, policies and policy mappings.
|
2021-11-12 00:03:02 -05:00
|
|
|
func (sys *IAMSys) Load(ctx context.Context) error {
|
2022-04-03 16:08:59 -04:00
|
|
|
loadStartTime := time.Now()
|
2021-11-03 22:47:49 -04:00
|
|
|
err := sys.store.LoadIAMCache(ctx)
|
|
|
|
if err != nil {
|
2022-04-03 16:08:59 -04:00
|
|
|
atomic.AddUint64(&sys.TotalRefreshFailures, 1)
|
2021-11-02 16:51:42 -04:00
|
|
|
return err
|
|
|
|
}
|
2022-04-03 16:08:59 -04:00
|
|
|
loadDuration := time.Since(loadStartTime)
|
|
|
|
|
|
|
|
atomic.StoreUint64(&sys.LastRefreshDurationMilliseconds, uint64(loadDuration.Milliseconds()))
|
|
|
|
atomic.StoreUint64(&sys.LastRefreshTimeUnixNano, uint64(loadStartTime.Add(loadDuration).UnixNano()))
|
|
|
|
atomic.AddUint64(&sys.TotalRefreshSuccesses, 1)
|
2021-11-02 16:51:42 -04:00
|
|
|
|
2021-03-02 20:08:25 -05:00
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
default:
|
|
|
|
close(sys.configLoaded)
|
|
|
|
}
|
2021-01-25 23:01:49 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-10-19 12:54:40 -04:00
|
|
|
// Init - initializes config system by reading entries from config/iam
|
2022-02-07 13:39:57 -05:00
|
|
|
func (sys *IAMSys) Init(ctx context.Context, objAPI ObjectLayer, etcdClient *etcd.Client, iamRefreshInterval time.Duration) {
|
2022-12-21 18:52:29 -05:00
|
|
|
bootstrapTrace("IAM initialization started")
|
2022-08-08 19:16:27 -04:00
|
|
|
globalServerConfigMu.RLock()
|
|
|
|
s := globalServerConfig
|
|
|
|
globalServerConfigMu.RUnlock()
|
|
|
|
|
2023-02-26 00:01:37 -05:00
|
|
|
openidConfig, err := openid.LookupConfig(s,
|
2022-10-24 20:44:15 -04:00
|
|
|
NewHTTPTransport(), xhttp.DrainBody, globalSite.Region)
|
2022-08-08 19:16:27 -04:00
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, fmt.Errorf("Unable to initialize OpenID: %w", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize if LDAP is enabled
|
2023-02-24 21:37:22 -05:00
|
|
|
ldapConfig, err := xldap.Lookup(s, globalRootCAs)
|
2022-08-08 19:16:27 -04:00
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, fmt.Errorf("Unable to parse LDAP configuration: %w", err))
|
|
|
|
}
|
|
|
|
|
2023-02-27 02:37:00 -05:00
|
|
|
stsTLSConfig, err := xtls.Lookup(s[config.IdentityTLSSubSys][config.Default])
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, fmt.Errorf("Unable to initialize X.509/TLS STS API: %w", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
if stsTLSConfig.InsecureSkipVerify {
|
|
|
|
logger.LogIf(ctx, fmt.Errorf("CRITICAL: enabling %s is not recommended in a production environment", xtls.EnvIdentityTLSSkipVerify))
|
|
|
|
}
|
|
|
|
|
2022-08-08 19:16:27 -04:00
|
|
|
authNPluginCfg, err := idplugin.LookupConfig(s[config.IdentityPluginSubSys][config.Default],
|
2022-10-24 20:44:15 -04:00
|
|
|
NewHTTPTransport(), xhttp.DrainBody, globalSite.Region)
|
2022-08-08 19:16:27 -04:00
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, fmt.Errorf("Unable to initialize AuthNPlugin: %w", err))
|
|
|
|
}
|
|
|
|
|
2023-02-27 12:55:18 -05:00
|
|
|
setGlobalAuthNPlugin(idplugin.New(GlobalContext, authNPluginCfg))
|
2022-08-08 19:16:27 -04:00
|
|
|
|
2022-12-13 17:28:48 -05:00
|
|
|
authZPluginCfg, err := polplugin.LookupConfig(s, GetDefaultConnSettings(), xhttp.DrainBody)
|
2022-08-08 19:16:27 -04:00
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, fmt.Errorf("Unable to initialize AuthZPlugin: %w", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
if authZPluginCfg.URL == nil {
|
|
|
|
opaCfg, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default],
|
2022-10-24 20:44:15 -04:00
|
|
|
NewHTTPTransport(), xhttp.DrainBody)
|
2022-08-08 19:16:27 -04:00
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, fmt.Errorf("Unable to initialize AuthZPlugin from legacy OPA config: %w", err))
|
|
|
|
} else {
|
|
|
|
authZPluginCfg.URL = opaCfg.URL
|
|
|
|
authZPluginCfg.AuthToken = opaCfg.AuthToken
|
|
|
|
authZPluginCfg.Transport = opaCfg.Transport
|
|
|
|
authZPluginCfg.CloseRespFn = opaCfg.CloseRespFn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setGlobalAuthZPlugin(polplugin.New(authZPluginCfg))
|
|
|
|
|
2021-11-12 00:03:02 -05:00
|
|
|
sys.Lock()
|
|
|
|
defer sys.Unlock()
|
|
|
|
|
2023-02-24 21:37:22 -05:00
|
|
|
sys.LDAPConfig = ldapConfig
|
2023-02-26 00:01:37 -05:00
|
|
|
sys.OpenIDConfig = openidConfig
|
2023-02-27 02:37:00 -05:00
|
|
|
sys.STSTLSConfig = stsTLSConfig
|
|
|
|
|
2021-11-01 18:03:07 -04:00
|
|
|
sys.iamRefreshInterval = iamRefreshInterval
|
|
|
|
|
2021-01-06 16:40:20 -05:00
|
|
|
// Initialize IAM store
|
2021-11-12 00:03:02 -05:00
|
|
|
sys.initStore(objAPI, etcdClient)
|
2021-01-06 16:40:20 -05:00
|
|
|
|
2020-06-09 22:19:03 -04:00
|
|
|
retryCtx, cancel := context.WithCancel(ctx)
|
|
|
|
|
|
|
|
// Indicate to our routine to exit cleanly upon return.
|
|
|
|
defer cancel()
|
|
|
|
|
2020-11-02 20:52:13 -05:00
|
|
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
|
|
2021-11-26 22:22:40 -05:00
|
|
|
// Migrate storage format if needed.
|
2020-11-02 20:52:13 -05:00
|
|
|
for {
|
2021-10-20 06:22:35 -04:00
|
|
|
if etcdClient != nil {
|
2020-06-09 22:19:03 -04:00
|
|
|
// **** WARNING ****
|
|
|
|
// Migrating to encrypted backend on etcd should happen before initialization of
|
|
|
|
// IAM sub-system, make sure that we do not move the above codeblock elsewhere.
|
2021-10-20 06:22:35 -04:00
|
|
|
if err := migrateIAMConfigsEtcdToEncrypted(retryCtx, etcdClient); err != nil {
|
2021-09-03 12:10:48 -04:00
|
|
|
if errors.Is(err, errEtcdUnreachable) {
|
|
|
|
logger.Info("Connection to etcd timed out. Retrying..")
|
|
|
|
continue
|
|
|
|
}
|
2020-06-11 17:11:30 -04:00
|
|
|
logger.LogIf(ctx, fmt.Errorf("Unable to decrypt an encrypted ETCD backend for IAM users and policies: %w", err))
|
|
|
|
logger.LogIf(ctx, errors.New("IAM sub-system is partially initialized, some users may not be available"))
|
2020-06-09 22:19:03 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-11 17:11:30 -04:00
|
|
|
// Migrate IAM configuration, if necessary.
|
2022-08-05 20:53:23 -04:00
|
|
|
if err := saveIAMFormat(retryCtx, sys.store); err != nil {
|
2021-03-18 17:09:55 -04:00
|
|
|
if configRetriableErrors(err) {
|
2020-06-09 22:19:03 -04:00
|
|
|
logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. possible cause (%v)", err)
|
|
|
|
continue
|
|
|
|
}
|
2023-02-01 07:02:22 -05:00
|
|
|
logger.LogIf(ctx, fmt.Errorf("IAM sub-system is partially initialized, unable to write the IAM format: %w", err))
|
2020-06-09 22:19:03 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
break
|
2018-10-12 14:32:18 -04:00
|
|
|
}
|
2020-02-01 20:07:43 -05:00
|
|
|
|
2021-11-26 22:22:40 -05:00
|
|
|
// Load IAM data from storage.
|
2020-12-05 16:00:44 -05:00
|
|
|
for {
|
2021-11-12 00:03:02 -05:00
|
|
|
if err := sys.Load(retryCtx); err != nil {
|
2021-03-18 17:09:55 -04:00
|
|
|
if configRetriableErrors(err) {
|
2020-12-05 16:00:44 -05:00
|
|
|
logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. possible cause (%v)", err)
|
|
|
|
time.Sleep(time.Duration(r.Float64() * float64(5*time.Second)))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err != nil {
|
2023-02-01 07:02:22 -05:00
|
|
|
logger.LogIf(ctx, fmt.Errorf("Unable to initialize IAM sub-system, some users may not be available: %w", err))
|
2020-12-05 16:00:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
2020-03-17 13:36:13 -04:00
|
|
|
|
2022-12-21 18:52:29 -05:00
|
|
|
bootstrapTrace("finishing IAM loading")
|
|
|
|
|
2022-05-30 13:58:37 -04:00
|
|
|
refreshInterval := sys.iamRefreshInterval
|
|
|
|
|
2021-07-21 02:33:12 -04:00
|
|
|
// Set up polling for expired accounts and credentials purging.
|
|
|
|
switch {
|
2023-02-26 00:01:37 -05:00
|
|
|
case sys.OpenIDConfig.ProviderEnabled():
|
2021-07-09 14:17:21 -04:00
|
|
|
go func() {
|
2022-05-30 13:58:37 -04:00
|
|
|
timer := time.NewTimer(refreshInterval)
|
2022-05-17 22:58:47 -04:00
|
|
|
defer timer.Stop()
|
2021-07-09 14:17:21 -04:00
|
|
|
for {
|
2021-11-04 11:16:30 -04:00
|
|
|
select {
|
2022-05-17 22:58:47 -04:00
|
|
|
case <-timer.C:
|
2021-11-04 11:16:30 -04:00
|
|
|
sys.purgeExpiredCredentialsForExternalSSO(ctx)
|
2022-05-17 22:58:47 -04:00
|
|
|
|
2022-05-30 13:58:37 -04:00
|
|
|
timer.Reset(refreshInterval)
|
2021-11-04 11:16:30 -04:00
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
}
|
2021-07-09 14:17:21 -04:00
|
|
|
}
|
|
|
|
}()
|
2023-02-24 21:37:22 -05:00
|
|
|
case sys.LDAPConfig.Enabled():
|
2021-07-21 02:33:12 -04:00
|
|
|
go func() {
|
2022-05-30 13:58:37 -04:00
|
|
|
timer := time.NewTimer(refreshInterval)
|
2022-05-17 22:58:47 -04:00
|
|
|
defer timer.Stop()
|
|
|
|
|
2021-07-21 02:33:12 -04:00
|
|
|
for {
|
2021-11-04 11:16:30 -04:00
|
|
|
select {
|
2022-05-17 22:58:47 -04:00
|
|
|
case <-timer.C:
|
2021-11-04 11:16:30 -04:00
|
|
|
sys.purgeExpiredCredentialsForLDAP(ctx)
|
|
|
|
sys.updateGroupMembershipsForLDAP(ctx)
|
2022-05-17 22:58:47 -04:00
|
|
|
|
2022-05-30 13:58:37 -04:00
|
|
|
timer.Reset(refreshInterval)
|
2021-11-04 11:16:30 -04:00
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
}
|
2021-07-21 02:33:12 -04:00
|
|
|
}
|
|
|
|
}()
|
2021-07-09 14:17:21 -04:00
|
|
|
}
|
|
|
|
|
2021-11-26 22:22:40 -05:00
|
|
|
// Start watching changes to storage.
|
2021-10-18 14:21:57 -04:00
|
|
|
go sys.watch(ctx)
|
2021-11-26 22:22:40 -05:00
|
|
|
|
2022-05-26 20:58:09 -04:00
|
|
|
// Load RoleARNs
|
|
|
|
sys.rolesMap = make(map[arn.ARN]string)
|
|
|
|
|
|
|
|
// From OpenID
|
2023-02-26 00:01:37 -05:00
|
|
|
if riMap := sys.OpenIDConfig.GetRoleInfo(); riMap != nil {
|
2022-05-26 20:58:09 -04:00
|
|
|
sys.validateAndAddRolePolicyMappings(ctx, riMap)
|
|
|
|
}
|
|
|
|
|
|
|
|
// From AuthN plugin if enabled.
|
2022-08-08 19:16:27 -04:00
|
|
|
if authn := newGlobalAuthNPluginFn(); authn != nil {
|
|
|
|
riMap := authn.GetRoleInfo()
|
2022-05-26 20:58:09 -04:00
|
|
|
sys.validateAndAddRolePolicyMappings(ctx, riMap)
|
2021-11-26 22:22:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sys.printIAMRoles()
|
|
|
|
}
|
|
|
|
|
2022-05-26 20:58:09 -04:00
|
|
|
func (sys *IAMSys) validateAndAddRolePolicyMappings(ctx context.Context, m map[arn.ARN]string) {
|
|
|
|
// Validate that policies associated with roles are defined. If
|
|
|
|
// authZ plugin is set, role policies are just claims sent to
|
|
|
|
// the plugin and they need not exist.
|
|
|
|
//
|
|
|
|
// If some mapped policies do not exist, we print some error
|
|
|
|
// messages but continue any way - they can be fixed in the
|
|
|
|
// running server by creating the policies after start up.
|
|
|
|
for arn, rolePolicies := range m {
|
|
|
|
specifiedPoliciesSet := newMappedPolicy(rolePolicies).policySet()
|
|
|
|
validPolicies, _ := sys.store.FilterPolicies(rolePolicies, "")
|
|
|
|
knownPoliciesSet := newMappedPolicy(validPolicies).policySet()
|
|
|
|
unknownPoliciesSet := specifiedPoliciesSet.Difference(knownPoliciesSet)
|
|
|
|
if len(unknownPoliciesSet) > 0 {
|
2022-08-08 19:16:27 -04:00
|
|
|
authz := newGlobalAuthZPluginFn()
|
|
|
|
if authz == nil {
|
2022-05-26 20:58:09 -04:00
|
|
|
// Print a warning that some policies mapped to a role are not defined.
|
|
|
|
errMsg := fmt.Errorf(
|
|
|
|
"The policies \"%s\" mapped to role ARN %s are not defined - this role may not work as expected.",
|
|
|
|
unknownPoliciesSet.ToSlice(), arn.String())
|
|
|
|
logger.LogIf(ctx, errMsg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sys.rolesMap[arn] = rolePolicies
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-26 22:22:40 -05:00
|
|
|
// Prints IAM role ARNs.
|
|
|
|
func (sys *IAMSys) printIAMRoles() {
|
2021-12-02 18:43:39 -05:00
|
|
|
if len(sys.rolesMap) == 0 {
|
2021-11-26 22:22:40 -05:00
|
|
|
return
|
|
|
|
}
|
2021-12-02 18:43:39 -05:00
|
|
|
var arns []string
|
|
|
|
for arn := range sys.rolesMap {
|
|
|
|
arns = append(arns, arn.String())
|
|
|
|
}
|
|
|
|
sort.Strings(arns)
|
2021-11-26 22:22:40 -05:00
|
|
|
msgs := make([]string, 0, len(arns))
|
|
|
|
for _, arn := range arns {
|
|
|
|
msgs = append(msgs, color.Bold(arn))
|
|
|
|
}
|
|
|
|
|
2022-03-03 16:21:16 -05:00
|
|
|
logger.Info(fmt.Sprintf("%s %s", color.Blue("IAM Roles:"), strings.Join(msgs, " ")))
|
2021-10-18 14:21:57 -04:00
|
|
|
}
|
|
|
|
|
2021-10-20 06:22:35 -04:00
|
|
|
// HasWatcher - returns if the IAM system has a watcher to be notified of
|
|
|
|
// changes.
|
|
|
|
func (sys *IAMSys) HasWatcher() bool {
|
2021-11-03 22:47:49 -04:00
|
|
|
return sys.store.HasWatcher()
|
2021-10-20 06:22:35 -04:00
|
|
|
}
|
|
|
|
|
2021-10-18 14:21:57 -04:00
|
|
|
func (sys *IAMSys) watch(ctx context.Context) {
|
2021-11-03 22:47:49 -04:00
|
|
|
watcher, ok := sys.store.IAMStorageAPI.(iamStorageWatcher)
|
2021-10-18 14:21:57 -04:00
|
|
|
if ok {
|
|
|
|
ch := watcher.watch(ctx, iamConfigPrefix)
|
|
|
|
for event := range ch {
|
2022-04-04 14:10:06 -04:00
|
|
|
if err := sys.loadWatchedEvent(ctx, event); err != nil {
|
|
|
|
// we simply log errors
|
|
|
|
logger.LogIf(ctx, fmt.Errorf("Failure in loading watch event: %v", err))
|
|
|
|
}
|
2021-10-18 14:21:57 -04:00
|
|
|
}
|
2021-11-04 11:16:30 -04:00
|
|
|
return
|
|
|
|
}
|
2021-10-18 14:21:57 -04:00
|
|
|
|
2022-03-30 20:02:59 -04:00
|
|
|
var maxRefreshDurationSecondsForLog float64 = 10
|
|
|
|
|
2022-05-17 22:58:47 -04:00
|
|
|
// Load all items periodically
|
|
|
|
timer := time.NewTimer(sys.iamRefreshInterval)
|
|
|
|
defer timer.Stop()
|
2021-11-04 11:16:30 -04:00
|
|
|
for {
|
|
|
|
select {
|
2022-05-17 22:58:47 -04:00
|
|
|
case <-timer.C:
|
2022-03-30 20:02:59 -04:00
|
|
|
refreshStart := time.Now()
|
2021-11-12 00:03:02 -05:00
|
|
|
if err := sys.Load(ctx); err != nil {
|
2022-03-30 20:02:59 -04:00
|
|
|
logger.LogIf(ctx, fmt.Errorf("Failure in periodic refresh for IAM (took %.2fs): %v", time.Since(refreshStart).Seconds(), err))
|
|
|
|
} else {
|
|
|
|
took := time.Since(refreshStart).Seconds()
|
|
|
|
if took > maxRefreshDurationSecondsForLog {
|
|
|
|
// Log if we took a lot of time to load.
|
|
|
|
logger.Info("IAM refresh took %.2fs", took)
|
|
|
|
}
|
2021-10-18 14:21:57 -04:00
|
|
|
}
|
2022-03-30 20:02:59 -04:00
|
|
|
|
2022-05-17 22:58:47 -04:00
|
|
|
timer.Reset(sys.iamRefreshInterval)
|
2021-11-04 11:16:30 -04:00
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
2021-10-18 14:21:57 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
func (sys *IAMSys) loadWatchedEvent(ctx context.Context, event iamWatchEvent) (err error) {
|
2021-10-18 14:21:57 -04:00
|
|
|
usersPrefix := strings.HasPrefix(event.keyPath, iamConfigUsersPrefix)
|
|
|
|
groupsPrefix := strings.HasPrefix(event.keyPath, iamConfigGroupsPrefix)
|
|
|
|
stsPrefix := strings.HasPrefix(event.keyPath, iamConfigSTSPrefix)
|
|
|
|
svcPrefix := strings.HasPrefix(event.keyPath, iamConfigServiceAccountsPrefix)
|
|
|
|
policyPrefix := strings.HasPrefix(event.keyPath, iamConfigPoliciesPrefix)
|
|
|
|
policyDBUsersPrefix := strings.HasPrefix(event.keyPath, iamConfigPolicyDBUsersPrefix)
|
|
|
|
policyDBSTSUsersPrefix := strings.HasPrefix(event.keyPath, iamConfigPolicyDBSTSUsersPrefix)
|
|
|
|
policyDBGroupsPrefix := strings.HasPrefix(event.keyPath, iamConfigPolicyDBGroupsPrefix)
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
ctx, cancel := context.WithTimeout(ctx, defaultContextTimeout)
|
2021-10-18 14:21:57 -04:00
|
|
|
defer cancel()
|
|
|
|
|
2021-11-16 12:28:29 -05:00
|
|
|
switch {
|
|
|
|
case usersPrefix:
|
|
|
|
accessKey := path.Dir(strings.TrimPrefix(event.keyPath, iamConfigUsersPrefix))
|
|
|
|
err = sys.store.UserNotificationHandler(ctx, accessKey, regUser)
|
|
|
|
case stsPrefix:
|
|
|
|
accessKey := path.Dir(strings.TrimPrefix(event.keyPath, iamConfigSTSPrefix))
|
|
|
|
err = sys.store.UserNotificationHandler(ctx, accessKey, stsUser)
|
|
|
|
case svcPrefix:
|
|
|
|
accessKey := path.Dir(strings.TrimPrefix(event.keyPath, iamConfigServiceAccountsPrefix))
|
|
|
|
err = sys.store.UserNotificationHandler(ctx, accessKey, svcUser)
|
|
|
|
case groupsPrefix:
|
|
|
|
group := path.Dir(strings.TrimPrefix(event.keyPath, iamConfigGroupsPrefix))
|
|
|
|
err = sys.store.GroupNotificationHandler(ctx, group)
|
|
|
|
case policyPrefix:
|
|
|
|
policyName := path.Dir(strings.TrimPrefix(event.keyPath, iamConfigPoliciesPrefix))
|
|
|
|
err = sys.store.PolicyNotificationHandler(ctx, policyName)
|
|
|
|
case policyDBUsersPrefix:
|
|
|
|
policyMapFile := strings.TrimPrefix(event.keyPath, iamConfigPolicyDBUsersPrefix)
|
|
|
|
user := strings.TrimSuffix(policyMapFile, ".json")
|
|
|
|
err = sys.store.PolicyMappingNotificationHandler(ctx, user, false, regUser)
|
|
|
|
case policyDBSTSUsersPrefix:
|
|
|
|
policyMapFile := strings.TrimPrefix(event.keyPath, iamConfigPolicyDBSTSUsersPrefix)
|
|
|
|
user := strings.TrimSuffix(policyMapFile, ".json")
|
|
|
|
err = sys.store.PolicyMappingNotificationHandler(ctx, user, false, stsUser)
|
|
|
|
case policyDBGroupsPrefix:
|
|
|
|
policyMapFile := strings.TrimPrefix(event.keyPath, iamConfigPolicyDBGroupsPrefix)
|
|
|
|
user := strings.TrimSuffix(policyMapFile, ".json")
|
|
|
|
err = sys.store.PolicyMappingNotificationHandler(ctx, user, true, regUser)
|
2021-10-18 14:21:57 -04:00
|
|
|
}
|
|
|
|
return err
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2021-12-02 18:43:39 -05:00
|
|
|
// HasRolePolicy - returns if a role policy is configured for IAM.
|
|
|
|
func (sys *IAMSys) HasRolePolicy() bool {
|
|
|
|
return len(sys.rolesMap) > 0
|
2021-11-26 22:22:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetRolePolicy - returns policies associated with a role ARN.
|
2022-04-28 21:27:09 -04:00
|
|
|
func (sys *IAMSys) GetRolePolicy(arnStr string) (arn.ARN, string, error) {
|
|
|
|
roleArn, err := arn.Parse(arnStr)
|
2021-11-26 22:22:40 -05:00
|
|
|
if err != nil {
|
2022-04-28 21:27:09 -04:00
|
|
|
return arn.ARN{}, "", fmt.Errorf("RoleARN parse err: %v", err)
|
2021-11-26 22:22:40 -05:00
|
|
|
}
|
2022-04-28 21:27:09 -04:00
|
|
|
rolePolicy, ok := sys.rolesMap[roleArn]
|
2021-11-26 22:22:40 -05:00
|
|
|
if !ok {
|
2022-04-28 21:27:09 -04:00
|
|
|
return arn.ARN{}, "", fmt.Errorf("RoleARN %s is not defined.", arnStr)
|
2021-11-26 22:22:40 -05:00
|
|
|
}
|
2022-04-28 21:27:09 -04:00
|
|
|
return roleArn, rolePolicy, nil
|
2021-11-26 22:22:40 -05:00
|
|
|
}
|
|
|
|
|
2019-06-06 20:46:22 -04:00
|
|
|
// DeletePolicy - deletes a canned policy from backend or etcd.
|
2021-11-29 17:38:57 -05:00
|
|
|
func (sys *IAMSys) DeletePolicy(ctx context.Context, policyName string, notifyPeers bool) error {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2018-10-09 17:00:01 -04:00
|
|
|
return errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-11-29 17:38:57 -05:00
|
|
|
err := sys.store.DeletePolicy(ctx, policyName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !notifyPeers || sys.HasWatcher() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Notify all other MinIO peers to delete policy
|
2022-02-07 13:39:57 -05:00
|
|
|
for _, nerr := range globalNotificationSys.DeletePolicy(policyName) {
|
2021-11-29 17:38:57 -05:00
|
|
|
if nerr.Err != nil {
|
|
|
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
|
|
logger.LogIf(ctx, nerr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2018-10-16 15:48:19 -04:00
|
|
|
}
|
|
|
|
|
2021-12-11 12:03:39 -05:00
|
|
|
// InfoPolicy - returns the policy definition with some metadata.
|
|
|
|
func (sys *IAMSys) InfoPolicy(policyName string) (*madmin.PolicyInfo, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2021-12-11 12:03:39 -05:00
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
d, err := sys.store.GetPolicyDoc(policyName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pdata, err := json.Marshal(d.Policy)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-09-26 14:23:13 -04:00
|
|
|
}
|
|
|
|
|
2021-12-11 12:03:39 -05:00
|
|
|
return &madmin.PolicyInfo{
|
|
|
|
PolicyName: policyName,
|
|
|
|
Policy: pdata,
|
|
|
|
CreateDate: d.CreateDate,
|
|
|
|
UpdateDate: d.UpdateDate,
|
|
|
|
}, nil
|
2019-09-26 14:23:13 -04:00
|
|
|
}
|
|
|
|
|
2019-06-06 20:46:22 -04:00
|
|
|
// ListPolicies - lists all canned policies.
|
2021-11-15 17:14:22 -05:00
|
|
|
func (sys *IAMSys) ListPolicies(ctx context.Context, bucketName string) (map[string]iampolicy.Policy, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2018-10-16 15:48:19 -04:00
|
|
|
return nil, errServerNotInitialized
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2022-04-19 12:00:19 -04:00
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
return sys.store.ListPolicies(ctx, bucketName)
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2022-04-24 05:36:31 -04:00
|
|
|
// ListPolicyDocs - lists all canned policy docs.
|
|
|
|
func (sys *IAMSys) ListPolicyDocs(ctx context.Context, bucketName string) (map[string]PolicyDoc, error) {
|
|
|
|
if !sys.Initialized() {
|
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
return sys.store.ListPolicyDocs(ctx, bucketName)
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
// SetPolicy - sets a new named policy.
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) SetPolicy(ctx context.Context, policyName string, p iampolicy.Policy) (time.Time, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return time.Time{}, errServerNotInitialized
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err := sys.store.SetPolicy(ctx, policyName, p)
|
2021-11-29 17:38:57 -05:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, err
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if !sys.HasWatcher() {
|
|
|
|
// Notify all other MinIO peers to reload policy
|
2022-02-07 13:39:57 -05:00
|
|
|
for _, nerr := range globalNotificationSys.LoadPolicy(policyName) {
|
2021-11-29 17:38:57 -05:00
|
|
|
if nerr.Err != nil {
|
|
|
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
|
|
logger.LogIf(ctx, nerr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, nil
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2019-07-24 20:34:23 -04:00
|
|
|
// DeleteUser - delete user (only for long-term users not STS users).
|
2021-12-21 05:16:50 -05:00
|
|
|
func (sys *IAMSys) DeleteUser(ctx context.Context, accessKey string, notifyPeers bool) error {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2018-10-09 17:00:01 -04:00
|
|
|
return errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-12-21 05:16:50 -05:00
|
|
|
if err := sys.store.DeleteUser(ctx, accessKey, regUser); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Notify all other MinIO peers to delete user.
|
|
|
|
if notifyPeers && !sys.HasWatcher() {
|
2022-02-07 13:39:57 -05:00
|
|
|
for _, nerr := range globalNotificationSys.DeleteUser(accessKey) {
|
2021-12-21 05:16:50 -05:00
|
|
|
if nerr.Err != nil {
|
|
|
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
|
|
logger.LogIf(ctx, nerr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2020-11-08 00:03:06 -05:00
|
|
|
// CurrentPolicies - returns comma separated policy string, from
|
|
|
|
// an input policy after validating if there are any current
|
|
|
|
// policies which exist on MinIO corresponding to the input.
|
|
|
|
func (sys *IAMSys) CurrentPolicies(policyName string) string {
|
|
|
|
if !sys.Initialized() {
|
2020-10-21 00:28:24 -04:00
|
|
|
return ""
|
|
|
|
}
|
2020-11-08 00:03:06 -05:00
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
policies, _ := sys.store.FilterPolicies(policyName, "")
|
|
|
|
return policies
|
2020-07-19 18:34:01 -04:00
|
|
|
}
|
|
|
|
|
2021-11-29 17:38:57 -05:00
|
|
|
func (sys *IAMSys) notifyForUser(ctx context.Context, accessKey string, isTemp bool) {
|
|
|
|
// Notify all other MinIO peers to reload user.
|
|
|
|
if !sys.HasWatcher() {
|
2022-02-07 13:39:57 -05:00
|
|
|
for _, nerr := range globalNotificationSys.LoadUser(accessKey, isTemp) {
|
2021-11-29 17:38:57 -05:00
|
|
|
if nerr.Err != nil {
|
|
|
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
|
|
logger.LogIf(ctx, nerr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Map policy to parent for STS (#13884)
When STS credentials are created for a user, a unique (hopefully stable) parent
user value exists for the credential, which corresponds to the user for whom the
credentials are created. The access policy is mapped to this parent-user and is
persisted. This helps ensure that all STS credentials of a user have the same
policy assignment at all times.
Before this change, for an OIDC STS credential, when the policy claim changes in
the provider (when not using RoleARNs), the change would not take effect on
existing credentials, but only on new ones.
To support existing STS credentials without parent-user policy mappings, we
lookup the policy in the policy claim value. This behavior should be deprecated
when such support is no longer required, as it can still lead to stale
policy mappings.
Additionally this change also simplifies the implementation for all non-RoleARN
STS credentials. Specifically, for AssumeRole (internal IDP) STS credentials,
policies are picked up from the parent user's policies; for
AssumeRoleWithCertificate STS credentials, policies are picked up from the
parent user mapping created when the STS credential is generated.
AssumeRoleWithLDAP already picks up policies mapped to the virtual parent user.
2021-12-17 03:46:30 -05:00
|
|
|
// SetTempUser - set temporary user credentials, these credentials have an
|
|
|
|
// expiry. The permissions for these STS credentials is determined in one of the
|
|
|
|
// following ways:
|
|
|
|
//
|
|
|
|
// - RoleARN - if a role-arn is specified in the request, the STS credential's
|
|
|
|
// policy is the role's policy.
|
|
|
|
//
|
|
|
|
// - inherited from parent - this is the case for AssumeRole API, where the
|
|
|
|
// parent user is an actual real user with their own (permanent) credentials and
|
|
|
|
// policy association.
|
|
|
|
//
|
|
|
|
// - inherited from "virtual" parent - this is the case for AssumeRoleWithLDAP
|
|
|
|
// where the parent user is the DN of the actual LDAP user. The parent user
|
|
|
|
// itself cannot login, but the policy associated with them determines the base
|
|
|
|
// policy for the STS credential. The policy mapping can be updated by the
|
|
|
|
// administrator.
|
|
|
|
//
|
|
|
|
// - from `Subject.CommonName` field from the STS request for
|
|
|
|
// AssumeRoleWithCertificate. In this case, the policy for the STS credential
|
|
|
|
// has the same name as the value of this field.
|
|
|
|
//
|
|
|
|
// - from special JWT claim from STS request for AssumeRoleWithOIDC API (when
|
|
|
|
// not using RoleARN). The claim value can be a string or a list and refers to
|
|
|
|
// the names of access policies.
|
|
|
|
//
|
|
|
|
// For all except the RoleARN case, the implementation is the same - the policy
|
|
|
|
// for the STS credential is associated with a parent user. For the
|
|
|
|
// AssumeRoleWithCertificate case, the "virtual" parent user is the value of the
|
|
|
|
// `Subject.CommonName` field. For the OIDC (without RoleARN) case the "virtual"
|
|
|
|
// parent is derived as a concatenation of the `sub` and `iss` fields. The
|
|
|
|
// policies applicable to the STS credential are associated with this "virtual"
|
|
|
|
// parent.
|
|
|
|
//
|
|
|
|
// When a policyName is given to this function, the policy association is
|
|
|
|
// created and stored in the IAM store. Thus, it should NOT be given for the
|
|
|
|
// role-arn case (because the role-to-policy mapping is separately stored
|
|
|
|
// elsewhere), the AssumeRole case (because the parent user is real and their
|
|
|
|
// policy is associated via policy-set API) and the AssumeRoleWithLDAP case
|
|
|
|
// (because the policy association is made via policy-set API).
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) SetTempUser(ctx context.Context, accessKey string, cred auth.Credentials, policyName string) (time.Time, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return time.Time{}, errServerNotInitialized
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2022-05-30 13:58:37 -04:00
|
|
|
if newGlobalAuthZPluginFn() != nil {
|
2021-11-03 22:47:49 -04:00
|
|
|
// If OPA is set, we do not need to set a policy mapping.
|
|
|
|
policyName = ""
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err := sys.store.SetTempUser(ctx, accessKey, cred, policyName)
|
2021-11-29 17:38:57 -05:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return time.Time{}, err
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sys.notifyForUser(ctx, cred.AccessKey, true)
|
2022-01-06 18:52:43 -05:00
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, nil
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2021-05-27 13:15:02 -04:00
|
|
|
// ListBucketUsers - list all users who can access this 'bucket'
|
2022-04-19 12:00:19 -04:00
|
|
|
func (sys *IAMSys) ListBucketUsers(ctx context.Context, bucket string) (map[string]madmin.UserInfo, error) {
|
2021-11-03 22:47:49 -04:00
|
|
|
if !sys.Initialized() {
|
|
|
|
return nil, errServerNotInitialized
|
2021-05-27 13:15:02 -04:00
|
|
|
}
|
|
|
|
|
2022-04-19 12:00:19 -04:00
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
return sys.store.GetBucketUsers(bucket)
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
2021-05-27 13:15:02 -04:00
|
|
|
}
|
|
|
|
|
2018-10-13 03:18:43 -04:00
|
|
|
// ListUsers - list all users.
|
2022-04-19 12:00:19 -04:00
|
|
|
func (sys *IAMSys) ListUsers(ctx context.Context) (map[string]madmin.UserInfo, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2018-10-13 03:18:43 -04:00
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
2022-04-19 12:00:19 -04:00
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
return sys.store.GetUsers(), nil
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
2018-10-13 03:18:43 -04:00
|
|
|
}
|
|
|
|
|
2022-04-16 00:26:02 -04:00
|
|
|
// ListLDAPUsers - list LDAP users which has
|
2022-10-04 13:41:47 -04:00
|
|
|
func (sys *IAMSys) ListLDAPUsers(ctx context.Context) (map[string]madmin.UserInfo, error) {
|
2022-04-16 00:26:02 -04:00
|
|
|
if !sys.Initialized() {
|
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
if sys.usersSysType != LDAPUsersSysType {
|
|
|
|
return nil, errIAMActionNotAllowed
|
|
|
|
}
|
|
|
|
|
2022-10-04 13:41:47 -04:00
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
ldapUsers := make(map[string]madmin.UserInfo)
|
|
|
|
for user, policy := range sys.store.GetUsersWithMappedPolicies() {
|
|
|
|
ldapUsers[user] = madmin.UserInfo{
|
|
|
|
PolicyName: policy,
|
|
|
|
Status: madmin.AccountEnabled,
|
|
|
|
}
|
2022-04-16 00:26:02 -04:00
|
|
|
}
|
2022-10-04 13:41:47 -04:00
|
|
|
return ldapUsers, nil
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
2022-04-16 00:26:02 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-07 17:35:09 -05:00
|
|
|
// QueryLDAPPolicyEntities - queries policy associations for LDAP users/groups/policies.
|
|
|
|
func (sys *IAMSys) QueryLDAPPolicyEntities(ctx context.Context, q madmin.PolicyEntitiesQuery) (*madmin.PolicyEntitiesResult, error) {
|
|
|
|
if !sys.Initialized() {
|
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
if sys.usersSysType != LDAPUsersSysType {
|
|
|
|
return nil, errIAMActionNotAllowed
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
2023-02-24 21:37:22 -05:00
|
|
|
pe := sys.store.ListLDAPPolicyMappings(q, sys.LDAPConfig.IsLDAPUserDN, sys.LDAPConfig.IsLDAPGroupDN)
|
2022-11-07 17:35:09 -05:00
|
|
|
pe.Timestamp = UTCNow()
|
|
|
|
return &pe, nil
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-19 17:21:21 -05:00
|
|
|
// IsTempUser - returns if given key is a temporary user.
|
2021-02-25 16:49:59 -05:00
|
|
|
func (sys *IAMSys) IsTempUser(name string) (bool, string, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2021-02-25 16:49:59 -05:00
|
|
|
return false, "", errServerNotInitialized
|
2019-12-19 17:21:21 -05:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
u, found := sys.store.GetUser(name)
|
2019-12-19 17:21:21 -05:00
|
|
|
if !found {
|
2021-02-25 16:49:59 -05:00
|
|
|
return false, "", errNoSuchUser
|
|
|
|
}
|
2022-07-01 16:19:13 -04:00
|
|
|
cred := u.Credentials
|
2021-02-25 16:49:59 -05:00
|
|
|
if cred.IsTemp() {
|
|
|
|
return true, cred.ParentUser, nil
|
2019-12-19 17:21:21 -05:00
|
|
|
}
|
|
|
|
|
2021-02-25 16:49:59 -05:00
|
|
|
return false, "", nil
|
2019-12-19 17:21:21 -05:00
|
|
|
}
|
|
|
|
|
2020-03-17 13:36:13 -04:00
|
|
|
// IsServiceAccount - returns if given key is a service account
|
|
|
|
func (sys *IAMSys) IsServiceAccount(name string) (bool, string, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2020-03-17 13:36:13 -04:00
|
|
|
return false, "", errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
u, found := sys.store.GetUser(name)
|
2020-03-17 13:36:13 -04:00
|
|
|
if !found {
|
|
|
|
return false, "", errNoSuchUser
|
|
|
|
}
|
2022-07-01 16:19:13 -04:00
|
|
|
cred := u.Credentials
|
2020-04-28 15:49:56 -04:00
|
|
|
if cred.IsServiceAccount() {
|
|
|
|
return true, cred.ParentUser, nil
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return false, "", nil
|
|
|
|
}
|
|
|
|
|
2019-08-13 16:41:06 -04:00
|
|
|
// GetUserInfo - get info on a user.
|
2021-11-15 17:14:22 -05:00
|
|
|
func (sys *IAMSys) GetUserInfo(ctx context.Context, name string) (u madmin.UserInfo, err error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2019-08-13 16:41:06 -04:00
|
|
|
return u, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-03-02 20:08:25 -05:00
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
default:
|
2021-11-15 17:14:22 -05:00
|
|
|
sys.store.LoadUser(ctx, name)
|
2021-11-02 16:51:42 -04:00
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
return sys.store.GetUserInfo(name)
|
2019-08-13 16:41:06 -04:00
|
|
|
}
|
|
|
|
|
2022-12-19 13:37:03 -05:00
|
|
|
// QueryPolicyEntities - queries policy associations for builtin users/groups/policies.
|
|
|
|
func (sys *IAMSys) QueryPolicyEntities(ctx context.Context, q madmin.PolicyEntitiesQuery) (*madmin.PolicyEntitiesResult, error) {
|
|
|
|
if !sys.Initialized() {
|
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
pe := sys.store.ListPolicyMappings(q)
|
|
|
|
pe.Timestamp = UTCNow()
|
|
|
|
return &pe, nil
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-13 15:13:23 -05:00
|
|
|
// GetUserPolicies - get policies attached to a user.
|
|
|
|
func (sys *IAMSys) GetUserPolicies(name string) (p []string, err error) {
|
|
|
|
if !sys.Initialized() {
|
|
|
|
return p, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
return sys.store.GetUserPolicies(name)
|
|
|
|
}
|
|
|
|
|
2018-10-16 17:55:23 -04:00
|
|
|
// SetUserStatus - sets current user status, supports disabled or enabled.
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) SetUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) (updatedAt time.Time, err error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errServerNotInitialized
|
2018-10-16 17:55:23 -04:00
|
|
|
}
|
|
|
|
|
2021-01-25 23:01:49 -05:00
|
|
|
if sys.usersSysType != MinIOUsersSysType {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errIAMActionNotAllowed
|
2021-01-25 23:01:49 -05:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err = sys.store.SetUserStatus(ctx, accessKey, status)
|
2021-11-29 17:38:57 -05:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sys.notifyForUser(ctx, accessKey, false)
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, nil
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sys *IAMSys) notifyForServiceAccount(ctx context.Context, accessKey string) {
|
|
|
|
// Notify all other Minio peers to reload the service account
|
|
|
|
if !sys.HasWatcher() {
|
2022-02-07 13:39:57 -05:00
|
|
|
for _, nerr := range globalNotificationSys.LoadServiceAccount(accessKey) {
|
2021-11-29 17:38:57 -05:00
|
|
|
if nerr.Err != nil {
|
|
|
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
|
|
logger.LogIf(ctx, nerr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-16 17:55:23 -04:00
|
|
|
}
|
|
|
|
|
2021-04-15 01:51:14 -04:00
|
|
|
type newServiceAccountOpts struct {
|
|
|
|
sessionPolicy *iampolicy.Policy
|
|
|
|
accessKey string
|
|
|
|
secretKey string
|
2023-01-10 12:57:52 -05:00
|
|
|
comment string
|
2023-02-27 13:10:22 -05:00
|
|
|
expiration *time.Time
|
2021-07-24 14:57:36 -04:00
|
|
|
|
2021-10-05 14:49:33 -04:00
|
|
|
claims map[string]interface{}
|
2021-04-15 01:51:14 -04:00
|
|
|
}
|
|
|
|
|
2020-03-17 13:36:13 -04:00
|
|
|
// NewServiceAccount - create a new service account
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser string, groups []string, opts newServiceAccountOpts) (auth.Credentials, time.Time, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return auth.Credentials{}, time.Time{}, errServerNotInitialized
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
|
|
|
|
2021-08-27 00:57:30 -04:00
|
|
|
if parentUser == "" {
|
2022-07-01 16:19:13 -04:00
|
|
|
return auth.Credentials{}, time.Time{}, errInvalidArgument
|
2021-08-27 00:57:30 -04:00
|
|
|
}
|
|
|
|
|
2020-04-14 14:28:56 -04:00
|
|
|
var policyBuf []byte
|
2021-04-15 01:51:14 -04:00
|
|
|
if opts.sessionPolicy != nil {
|
|
|
|
err := opts.sessionPolicy.Validate()
|
2020-04-14 14:28:56 -04:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return auth.Credentials{}, time.Time{}, err
|
2020-04-14 14:28:56 -04:00
|
|
|
}
|
2021-04-15 01:51:14 -04:00
|
|
|
policyBuf, err = json.Marshal(opts.sessionPolicy)
|
2020-03-23 17:17:18 -04:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return auth.Credentials{}, time.Time{}, err
|
2020-03-23 17:17:18 -04:00
|
|
|
}
|
2020-08-05 16:08:40 -04:00
|
|
|
if len(policyBuf) > 16*humanize.KiByte {
|
2022-07-01 16:19:13 -04:00
|
|
|
return auth.Credentials{}, time.Time{}, fmt.Errorf("Session policy should not exceed 16 KiB characters")
|
2020-03-23 17:17:18 -04:00
|
|
|
}
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
|
|
|
|
2021-08-27 00:57:30 -04:00
|
|
|
// found newly requested service account, to be same as
|
|
|
|
// parentUser, reject such operations.
|
|
|
|
if parentUser == opts.accessKey {
|
2022-07-01 16:19:13 -04:00
|
|
|
return auth.Credentials{}, time.Time{}, errIAMActionNotAllowed
|
2021-08-27 00:57:30 -04:00
|
|
|
}
|
|
|
|
|
2020-03-17 13:36:13 -04:00
|
|
|
m := make(map[string]interface{})
|
|
|
|
m[parentClaim] = parentUser
|
2020-03-23 17:17:18 -04:00
|
|
|
|
2020-04-14 14:28:56 -04:00
|
|
|
if len(policyBuf) > 0 {
|
|
|
|
m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString(policyBuf)
|
2022-05-02 20:56:19 -04:00
|
|
|
m[iamPolicyClaimNameSA()] = embeddedPolicyType
|
2020-03-23 17:17:18 -04:00
|
|
|
} else {
|
2022-05-02 20:56:19 -04:00
|
|
|
m[iamPolicyClaimNameSA()] = inheritedPolicyType
|
2020-03-23 17:17:18 -04:00
|
|
|
}
|
2020-03-17 13:36:13 -04:00
|
|
|
|
2021-10-05 14:49:33 -04:00
|
|
|
// Add all the necessary claims for the service accounts.
|
|
|
|
for k, v := range opts.claims {
|
|
|
|
_, ok := m[k]
|
|
|
|
if !ok {
|
|
|
|
m[k] = v
|
|
|
|
}
|
2021-07-24 14:57:36 -04:00
|
|
|
}
|
|
|
|
|
2022-03-14 12:09:22 -04:00
|
|
|
var accessKey, secretKey string
|
2021-11-03 22:47:49 -04:00
|
|
|
var err error
|
2021-04-15 01:51:14 -04:00
|
|
|
if len(opts.accessKey) > 0 {
|
2022-03-14 12:09:22 -04:00
|
|
|
accessKey, secretKey = opts.accessKey, opts.secretKey
|
2021-04-15 01:51:14 -04:00
|
|
|
} else {
|
2022-03-14 12:09:22 -04:00
|
|
|
accessKey, secretKey, err = auth.GenerateCredentials()
|
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return auth.Credentials{}, time.Time{}, err
|
2022-03-14 12:09:22 -04:00
|
|
|
}
|
2021-04-15 01:51:14 -04:00
|
|
|
}
|
2022-03-14 12:09:22 -04:00
|
|
|
cred, err := auth.CreateNewCredentialsWithMetadata(accessKey, secretKey, m, secretKey)
|
2020-03-17 13:36:13 -04:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return auth.Credentials{}, time.Time{}, err
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
|
|
|
cred.ParentUser = parentUser
|
2021-02-25 16:49:59 -05:00
|
|
|
cred.Groups = groups
|
2021-04-15 17:47:58 -04:00
|
|
|
cred.Status = string(auth.AccountOn)
|
2023-01-10 12:57:52 -05:00
|
|
|
cred.Comment = opts.comment
|
2020-04-28 15:49:56 -04:00
|
|
|
|
2023-02-27 13:10:22 -05:00
|
|
|
if opts.expiration != nil {
|
|
|
|
expirationInUTC := opts.expiration.UTC()
|
|
|
|
if err := validateSvcExpirationInUTC(expirationInUTC); err != nil {
|
|
|
|
return auth.Credentials{}, time.Time{}, err
|
|
|
|
}
|
|
|
|
cred.Expiration = expirationInUTC
|
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err := sys.store.AddServiceAccount(ctx, cred)
|
2021-11-03 22:47:49 -04:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return auth.Credentials{}, time.Time{}, err
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
2021-11-29 17:38:57 -05:00
|
|
|
|
|
|
|
sys.notifyForServiceAccount(ctx, cred.AccessKey)
|
2022-07-01 16:19:13 -04:00
|
|
|
return cred, updatedAt, nil
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
|
|
|
|
2021-04-15 01:51:14 -04:00
|
|
|
type updateServiceAccountOpts struct {
|
|
|
|
sessionPolicy *iampolicy.Policy
|
|
|
|
secretKey string
|
|
|
|
status string
|
2023-01-10 12:57:52 -05:00
|
|
|
comment string
|
2023-02-27 13:10:22 -05:00
|
|
|
expiration *time.Time
|
2021-04-15 01:51:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateServiceAccount - edit a service account
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) UpdateServiceAccount(ctx context.Context, accessKey string, opts updateServiceAccountOpts) (updatedAt time.Time, err error) {
|
2021-04-15 01:51:14 -04:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errServerNotInitialized
|
2021-04-15 01:51:14 -04:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err = sys.store.UpdateServiceAccount(ctx, accessKey, opts)
|
2021-11-29 17:38:57 -05:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, err
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sys.notifyForServiceAccount(ctx, accessKey)
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, nil
|
2021-04-15 01:51:14 -04:00
|
|
|
}
|
|
|
|
|
2020-04-24 15:10:09 -04:00
|
|
|
// ListServiceAccounts - lists all services accounts associated to a specific user
|
2021-04-15 01:51:14 -04:00
|
|
|
func (sys *IAMSys) ListServiceAccounts(ctx context.Context, accessKey string) ([]auth.Credentials, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2020-04-24 15:10:09 -04:00
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2022-04-19 12:00:19 -04:00
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
return sys.store.ListServiceAccounts(ctx, accessKey)
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
2020-04-24 15:10:09 -04:00
|
|
|
}
|
|
|
|
|
2022-04-28 05:39:00 -04:00
|
|
|
// ListTempAccounts - lists all services accounts associated to a specific user
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) ListTempAccounts(ctx context.Context, accessKey string) ([]UserIdentity, error) {
|
2022-04-28 05:39:00 -04:00
|
|
|
if !sys.Initialized() {
|
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
return sys.store.ListTempAccounts(ctx, accessKey)
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-30 16:16:37 -05:00
|
|
|
// GetServiceAccount - wrapper method to get information about a service account
|
2021-04-15 01:51:14 -04:00
|
|
|
func (sys *IAMSys) GetServiceAccount(ctx context.Context, accessKey string) (auth.Credentials, *iampolicy.Policy, error) {
|
2021-11-30 16:16:37 -05:00
|
|
|
sa, embeddedPolicy, err := sys.getServiceAccount(ctx, accessKey)
|
|
|
|
if err != nil {
|
2022-11-22 10:26:33 -05:00
|
|
|
return auth.Credentials{}, nil, err
|
2021-11-30 16:16:37 -05:00
|
|
|
}
|
|
|
|
// Hide secret & session keys
|
2022-07-01 16:19:13 -04:00
|
|
|
sa.Credentials.SecretKey = ""
|
|
|
|
sa.Credentials.SessionToken = ""
|
|
|
|
return sa.Credentials, embeddedPolicy, nil
|
2021-11-30 16:16:37 -05:00
|
|
|
}
|
|
|
|
|
2022-11-22 10:26:33 -05:00
|
|
|
func (sys *IAMSys) getServiceAccount(ctx context.Context, accessKey string) (UserIdentity, *iampolicy.Policy, error) {
|
2022-12-13 11:38:50 -05:00
|
|
|
sa, jwtClaims, err := sys.getAccountWithClaims(ctx, accessKey)
|
2022-11-22 10:26:33 -05:00
|
|
|
if err != nil {
|
|
|
|
if err == errNoSuchAccount {
|
|
|
|
return UserIdentity{}, nil, errNoSuchServiceAccount
|
|
|
|
}
|
|
|
|
return UserIdentity{}, nil, err
|
|
|
|
}
|
|
|
|
if !sa.Credentials.IsServiceAccount() {
|
|
|
|
return UserIdentity{}, nil, errNoSuchServiceAccount
|
|
|
|
}
|
|
|
|
|
2022-12-13 11:38:50 -05:00
|
|
|
var embeddedPolicy *iampolicy.Policy
|
|
|
|
|
|
|
|
pt, ptok := jwtClaims.Lookup(iamPolicyClaimNameSA())
|
|
|
|
sp, spok := jwtClaims.Lookup(iampolicy.SessionPolicyName)
|
|
|
|
if ptok && spok && pt == embeddedPolicyType {
|
|
|
|
policyBytes, err := base64.StdEncoding.DecodeString(sp)
|
|
|
|
if err != nil {
|
|
|
|
return UserIdentity{}, nil, err
|
|
|
|
}
|
|
|
|
embeddedPolicy, err = iampolicy.ParseConfig(bytes.NewReader(policyBytes))
|
|
|
|
if err != nil {
|
|
|
|
return UserIdentity{}, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-22 10:26:33 -05:00
|
|
|
return sa, embeddedPolicy, nil
|
|
|
|
}
|
|
|
|
|
2022-12-13 11:38:50 -05:00
|
|
|
// GetTemporaryAccount - wrapper method to get information about a temporary account
|
|
|
|
func (sys *IAMSys) GetTemporaryAccount(ctx context.Context, accessKey string) (auth.Credentials, *iampolicy.Policy, error) {
|
|
|
|
tmpAcc, embeddedPolicy, err := sys.getTempAccount(ctx, accessKey)
|
|
|
|
if err != nil {
|
|
|
|
return auth.Credentials{}, nil, err
|
|
|
|
}
|
|
|
|
// Hide secret & session keys
|
|
|
|
tmpAcc.Credentials.SecretKey = ""
|
|
|
|
tmpAcc.Credentials.SessionToken = ""
|
|
|
|
return tmpAcc.Credentials, embeddedPolicy, nil
|
|
|
|
}
|
|
|
|
|
2022-11-22 10:26:33 -05:00
|
|
|
func (sys *IAMSys) getTempAccount(ctx context.Context, accessKey string) (UserIdentity, *iampolicy.Policy, error) {
|
2022-12-13 11:38:50 -05:00
|
|
|
tmpAcc, claims, err := sys.getAccountWithClaims(ctx, accessKey)
|
2022-11-22 10:26:33 -05:00
|
|
|
if err != nil {
|
|
|
|
if err == errNoSuchAccount {
|
|
|
|
return UserIdentity{}, nil, errNoSuchTempAccount
|
|
|
|
}
|
|
|
|
return UserIdentity{}, nil, err
|
|
|
|
}
|
|
|
|
if !tmpAcc.Credentials.IsTemp() {
|
|
|
|
return UserIdentity{}, nil, errNoSuchTempAccount
|
|
|
|
}
|
|
|
|
|
2022-12-13 11:38:50 -05:00
|
|
|
var embeddedPolicy *iampolicy.Policy
|
|
|
|
|
|
|
|
sp, spok := claims.Lookup(iampolicy.SessionPolicyName)
|
|
|
|
if spok {
|
|
|
|
policyBytes, err := base64.StdEncoding.DecodeString(sp)
|
|
|
|
if err != nil {
|
|
|
|
return UserIdentity{}, nil, err
|
|
|
|
}
|
|
|
|
embeddedPolicy, err = iampolicy.ParseConfig(bytes.NewReader(policyBytes))
|
|
|
|
if err != nil {
|
|
|
|
return UserIdentity{}, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-22 10:26:33 -05:00
|
|
|
return tmpAcc, embeddedPolicy, nil
|
|
|
|
}
|
|
|
|
|
2022-12-13 11:38:50 -05:00
|
|
|
// getAccountWithClaims - gets information about an account with claims
|
|
|
|
func (sys *IAMSys) getAccountWithClaims(ctx context.Context, accessKey string) (UserIdentity, *jwt.MapClaims, error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-12-13 11:38:50 -05:00
|
|
|
return UserIdentity{}, nil, errServerNotInitialized
|
2020-04-24 15:10:09 -04:00
|
|
|
}
|
|
|
|
|
2022-12-13 11:38:50 -05:00
|
|
|
acc, ok := sys.store.GetUser(accessKey)
|
2022-11-22 10:26:33 -05:00
|
|
|
if !ok {
|
2022-12-13 11:38:50 -05:00
|
|
|
return UserIdentity{}, nil, errNoSuchAccount
|
2020-04-24 15:10:09 -04:00
|
|
|
}
|
2021-04-15 01:51:14 -04:00
|
|
|
|
2023-02-27 13:10:22 -05:00
|
|
|
jwtClaims, err := extractJWTClaims(acc)
|
2022-01-10 17:26:26 -05:00
|
|
|
if err != nil {
|
2023-02-27 13:10:22 -05:00
|
|
|
return UserIdentity{}, nil, err
|
2021-04-15 01:51:14 -04:00
|
|
|
}
|
|
|
|
|
2022-12-13 11:38:50 -05:00
|
|
|
return acc, jwtClaims, nil
|
2020-04-24 15:10:09 -04:00
|
|
|
}
|
|
|
|
|
2021-10-06 19:36:31 -04:00
|
|
|
// GetClaimsForSvcAcc - gets the claims associated with the service account.
|
|
|
|
func (sys *IAMSys) GetClaimsForSvcAcc(ctx context.Context, accessKey string) (map[string]interface{}, error) {
|
|
|
|
if !sys.Initialized() {
|
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
if sys.usersSysType != LDAPUsersSysType {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
sa, ok := sys.store.GetUser(accessKey)
|
2022-07-01 16:19:13 -04:00
|
|
|
if !ok || !sa.Credentials.IsServiceAccount() {
|
2021-10-06 19:36:31 -04:00
|
|
|
return nil, errNoSuchServiceAccount
|
|
|
|
}
|
|
|
|
|
2023-02-27 13:10:22 -05:00
|
|
|
jwtClaims, err := extractJWTClaims(sa)
|
2021-10-06 19:36:31 -04:00
|
|
|
if err != nil {
|
2023-02-27 13:10:22 -05:00
|
|
|
return nil, err
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
2023-02-27 13:10:22 -05:00
|
|
|
|
2021-10-06 19:36:31 -04:00
|
|
|
return jwtClaims.Map(), nil
|
|
|
|
}
|
|
|
|
|
2020-04-24 15:10:09 -04:00
|
|
|
// DeleteServiceAccount - delete a service account
|
2021-12-21 05:16:50 -05:00
|
|
|
func (sys *IAMSys) DeleteServiceAccount(ctx context.Context, accessKey string, notifyPeers bool) error {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2020-04-24 15:10:09 -04:00
|
|
|
return errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
sa, ok := sys.store.GetUser(accessKey)
|
2022-07-01 16:19:13 -04:00
|
|
|
if !ok || !sa.Credentials.IsServiceAccount() {
|
2020-05-01 11:05:14 -04:00
|
|
|
return nil
|
2020-04-24 15:10:09 -04:00
|
|
|
}
|
|
|
|
|
2021-12-21 05:16:50 -05:00
|
|
|
if err := sys.store.DeleteUser(ctx, accessKey, svcUser); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if notifyPeers && !sys.HasWatcher() {
|
2022-02-07 13:39:57 -05:00
|
|
|
for _, nerr := range globalNotificationSys.DeleteServiceAccount(accessKey) {
|
2021-12-21 05:16:50 -05:00
|
|
|
if nerr.Err != nil {
|
|
|
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
|
|
logger.LogIf(ctx, nerr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2020-04-24 15:10:09 -04:00
|
|
|
}
|
|
|
|
|
2021-01-25 23:01:49 -05:00
|
|
|
// CreateUser - create new user credentials and policy, if user already exists
|
|
|
|
// they shall be rewritten with new inputs.
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) CreateUser(ctx context.Context, accessKey string, ureq madmin.AddOrUpdateUserReq) (updatedAt time.Time, err error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errServerNotInitialized
|
2019-05-29 16:18:46 -04:00
|
|
|
}
|
|
|
|
|
2019-09-09 19:12:29 -04:00
|
|
|
if sys.usersSysType != MinIOUsersSysType {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errIAMActionNotAllowed
|
2019-09-09 19:12:29 -04:00
|
|
|
}
|
|
|
|
|
2021-05-05 19:36:39 -04:00
|
|
|
if !auth.IsAccessKeyValid(accessKey) {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, auth.ErrInvalidAccessKeyLength
|
2021-05-05 19:36:39 -04:00
|
|
|
}
|
|
|
|
|
2021-12-23 12:21:21 -05:00
|
|
|
if !auth.IsSecretKeyValid(ureq.SecretKey) {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, auth.ErrInvalidSecretKeyLength
|
2021-11-02 16:51:42 -04:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err = sys.store.AddUser(ctx, accessKey, ureq)
|
2021-11-29 17:38:57 -05:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, err
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sys.notifyForUser(ctx, accessKey, false)
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, nil
|
2021-11-02 16:51:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetUserSecretKey - sets user secret key
|
2021-11-15 17:14:22 -05:00
|
|
|
func (sys *IAMSys) SetUserSecretKey(ctx context.Context, accessKey string, secretKey string) error {
|
2021-11-02 16:51:42 -04:00
|
|
|
if !sys.Initialized() {
|
|
|
|
return errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
if sys.usersSysType != MinIOUsersSysType {
|
|
|
|
return errIAMActionNotAllowed
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
if !auth.IsAccessKeyValid(accessKey) {
|
|
|
|
return auth.ErrInvalidAccessKeyLength
|
2021-01-25 23:01:49 -05:00
|
|
|
}
|
2020-10-19 12:54:40 -04:00
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
if !auth.IsSecretKeyValid(secretKey) {
|
|
|
|
return auth.ErrInvalidSecretKeyLength
|
2021-01-25 23:01:49 -05:00
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
return sys.store.UpdateUserSecretKey(ctx, accessKey, secretKey)
|
2021-01-25 23:01:49 -05:00
|
|
|
}
|
2020-10-19 12:54:40 -04:00
|
|
|
|
2021-07-09 14:17:21 -04:00
|
|
|
// purgeExpiredCredentialsForExternalSSO - validates if local credentials are still valid
|
|
|
|
// by checking remote IDP if the relevant users are still active and present.
|
|
|
|
func (sys *IAMSys) purgeExpiredCredentialsForExternalSSO(ctx context.Context) {
|
2022-04-28 21:27:09 -04:00
|
|
|
parentUsersMap := sys.store.GetAllParentUsers()
|
2021-11-03 22:47:49 -04:00
|
|
|
var expiredUsers []string
|
2022-04-28 21:27:09 -04:00
|
|
|
for parentUser, puInfo := range parentUsersMap {
|
|
|
|
// There are multiple role ARNs for parent user only when there
|
|
|
|
// are multiple openid provider configurations with the same ID
|
|
|
|
// provider. We lookup the provider associated with some one of
|
|
|
|
// the roleARNs to check if the user still exists. If they don't
|
|
|
|
// we can safely remove credentials for this parent user
|
|
|
|
// associated with any of the provider configurations.
|
|
|
|
//
|
|
|
|
// If there is no roleARN mapped to the user, the user may be
|
|
|
|
// coming from a policy claim based openid provider.
|
|
|
|
roleArns := puInfo.roleArns.ToSlice()
|
|
|
|
var roleArn string
|
|
|
|
if len(roleArns) == 0 {
|
|
|
|
logger.LogIf(GlobalContext,
|
|
|
|
fmt.Errorf("parentUser: %s had no roleArns mapped!", parentUser))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
roleArn = roleArns[0]
|
2023-02-26 00:01:37 -05:00
|
|
|
u, err := sys.OpenIDConfig.LookupUser(roleArn, puInfo.subClaimValue)
|
2021-07-09 14:17:21 -04:00
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(GlobalContext, err)
|
|
|
|
continue
|
|
|
|
}
|
2021-11-03 22:47:49 -04:00
|
|
|
// If user is set to "disabled", we will remove them
|
|
|
|
// subsequently.
|
2021-07-09 14:17:21 -04:00
|
|
|
if !u.Enabled {
|
2022-04-28 21:27:09 -04:00
|
|
|
expiredUsers = append(expiredUsers, parentUser)
|
2021-07-09 14:17:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
// We ignore any errors
|
|
|
|
_ = sys.store.DeleteUsers(ctx, expiredUsers)
|
2021-07-09 14:17:21 -04:00
|
|
|
}
|
|
|
|
|
2021-07-21 02:33:12 -04:00
|
|
|
// purgeExpiredCredentialsForLDAP - validates if local credentials are still
|
|
|
|
// valid by checking LDAP server if the relevant users are still present.
|
|
|
|
func (sys *IAMSys) purgeExpiredCredentialsForLDAP(ctx context.Context) {
|
2021-11-03 22:47:49 -04:00
|
|
|
parentUsers := sys.store.GetAllParentUsers()
|
|
|
|
var allDistNames []string
|
2022-04-28 21:27:09 -04:00
|
|
|
for parentUser := range parentUsers {
|
2023-02-24 21:37:22 -05:00
|
|
|
if !sys.LDAPConfig.IsLDAPUserDN(parentUser) {
|
2021-11-03 22:47:49 -04:00
|
|
|
continue
|
2021-07-21 02:33:12 -04:00
|
|
|
}
|
2021-11-03 22:47:49 -04:00
|
|
|
|
2022-04-28 21:27:09 -04:00
|
|
|
allDistNames = append(allDistNames, parentUser)
|
2021-07-21 02:33:12 -04:00
|
|
|
}
|
|
|
|
|
2023-02-24 21:37:22 -05:00
|
|
|
expiredUsers, err := sys.LDAPConfig.GetNonEligibleUserDistNames(allDistNames)
|
2021-07-21 02:33:12 -04:00
|
|
|
if err != nil {
|
|
|
|
// Log and return on error - perhaps it'll work the next time.
|
|
|
|
logger.LogIf(GlobalContext, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
// We ignore any errors
|
|
|
|
_ = sys.store.DeleteUsers(ctx, expiredUsers)
|
2021-07-21 02:33:12 -04:00
|
|
|
}
|
|
|
|
|
2021-07-24 14:57:36 -04:00
|
|
|
// updateGroupMembershipsForLDAP - updates the list of groups associated with the credential.
|
|
|
|
func (sys *IAMSys) updateGroupMembershipsForLDAP(ctx context.Context) {
|
|
|
|
// 1. Collect all LDAP users with active creds.
|
2021-11-03 22:47:49 -04:00
|
|
|
allCreds := sys.store.GetSTSAndServiceAccounts()
|
2021-07-24 14:57:36 -04:00
|
|
|
// List of unique LDAP (parent) user DNs that have active creds
|
2021-11-03 22:47:49 -04:00
|
|
|
var parentUsers []string
|
2021-07-24 14:57:36 -04:00
|
|
|
// Map of LDAP user to list of active credential objects
|
2021-11-03 22:47:49 -04:00
|
|
|
parentUserToCredsMap := make(map[string][]auth.Credentials)
|
2021-07-24 14:57:36 -04:00
|
|
|
// DN to ldap username mapping for each LDAP user
|
2021-11-03 22:47:49 -04:00
|
|
|
parentUserToLDAPUsernameMap := make(map[string]string)
|
|
|
|
for _, cred := range allCreds {
|
2023-02-24 21:37:22 -05:00
|
|
|
if !sys.LDAPConfig.IsLDAPUserDN(cred.ParentUser) {
|
2021-11-03 22:47:49 -04:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Check if this is the first time we are
|
|
|
|
// encountering this LDAP user.
|
|
|
|
if _, ok := parentUserToCredsMap[cred.ParentUser]; !ok {
|
|
|
|
// Try to find the ldapUsername for this
|
|
|
|
// parentUser by extracting JWT claims
|
2022-03-14 12:09:22 -04:00
|
|
|
var (
|
|
|
|
jwtClaims *jwt.MapClaims
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
if cred.SessionToken == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if cred.IsServiceAccount() {
|
|
|
|
jwtClaims, err = auth.ExtractClaims(cred.SessionToken, cred.SecretKey)
|
|
|
|
if err != nil {
|
|
|
|
jwtClaims, err = auth.ExtractClaims(cred.SessionToken, globalActiveCred.SecretKey)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
jwtClaims, err = auth.ExtractClaims(cred.SessionToken, globalActiveCred.SecretKey)
|
|
|
|
}
|
2021-11-03 22:47:49 -04:00
|
|
|
if err != nil {
|
2022-03-14 12:09:22 -04:00
|
|
|
// skip this cred - session token seems invalid
|
2021-11-03 22:47:49 -04:00
|
|
|
continue
|
|
|
|
}
|
2022-03-14 12:09:22 -04:00
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
ldapUsername, ok := jwtClaims.Lookup(ldapUserN)
|
|
|
|
if !ok {
|
|
|
|
// skip this cred - we dont have the
|
|
|
|
// username info needed
|
|
|
|
continue
|
2021-07-24 14:57:36 -04:00
|
|
|
}
|
2021-11-03 22:47:49 -04:00
|
|
|
|
|
|
|
// Collect each new cred.ParentUser into parentUsers
|
|
|
|
parentUsers = append(parentUsers, cred.ParentUser)
|
|
|
|
|
|
|
|
// Update the ldapUsernameMap
|
|
|
|
parentUserToLDAPUsernameMap[cred.ParentUser] = ldapUsername
|
2021-07-24 14:57:36 -04:00
|
|
|
}
|
2021-11-03 22:47:49 -04:00
|
|
|
parentUserToCredsMap[cred.ParentUser] = append(parentUserToCredsMap[cred.ParentUser], cred)
|
|
|
|
|
2021-07-24 14:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 2. Query LDAP server for groups of the LDAP users collected.
|
2023-02-24 21:37:22 -05:00
|
|
|
updatedGroups, err := sys.LDAPConfig.LookupGroupMemberships(parentUsers, parentUserToLDAPUsernameMap)
|
2021-07-24 14:57:36 -04:00
|
|
|
if err != nil {
|
|
|
|
// Log and return on error - perhaps it'll work the next time.
|
|
|
|
logger.LogIf(GlobalContext, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. Update creds for those users whose groups are changed
|
|
|
|
for _, parentUser := range parentUsers {
|
|
|
|
currGroupsSet := updatedGroups[parentUser]
|
|
|
|
currGroups := currGroupsSet.ToSlice()
|
|
|
|
for _, cred := range parentUserToCredsMap[parentUser] {
|
|
|
|
gSet := set.CreateStringSet(cred.Groups...)
|
|
|
|
if gSet.Equals(currGroupsSet) {
|
|
|
|
// No change to groups memberships for this
|
|
|
|
// credential.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
cred.Groups = currGroups
|
2021-11-03 22:47:49 -04:00
|
|
|
if err := sys.store.UpdateUserIdentity(ctx, cred); err != nil {
|
2021-07-24 14:57:36 -04:00
|
|
|
// Log and continue error - perhaps it'll work the next time.
|
|
|
|
logger.LogIf(GlobalContext, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-25 23:01:49 -05:00
|
|
|
// GetUser - get user credentials
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (u UserIdentity, ok bool) {
|
2021-01-25 23:01:49 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return u, false
|
2020-06-11 17:11:30 -04:00
|
|
|
}
|
|
|
|
|
2021-03-02 20:08:25 -05:00
|
|
|
fallback := false
|
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
default:
|
2021-11-15 17:14:22 -05:00
|
|
|
sys.store.LoadUser(ctx, accessKey)
|
2021-03-02 20:08:25 -05:00
|
|
|
fallback = true
|
2021-01-19 13:05:41 -05:00
|
|
|
}
|
2018-10-09 17:00:01 -04:00
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
u, ok = sys.store.GetUser(accessKey)
|
2021-01-19 13:05:41 -05:00
|
|
|
if !ok && !fallback {
|
|
|
|
// accessKey not found, also
|
|
|
|
// IAM store is not in fallback mode
|
|
|
|
// we can try to reload again from
|
|
|
|
// the IAM store and see if credential
|
|
|
|
// exists now. If it doesn't proceed to
|
|
|
|
// fail.
|
2021-11-15 17:14:22 -05:00
|
|
|
sys.store.LoadUser(ctx, accessKey)
|
2022-07-01 16:19:13 -04:00
|
|
|
u, ok = sys.store.GetUser(accessKey)
|
2021-01-19 13:05:41 -05:00
|
|
|
}
|
|
|
|
|
2023-04-15 10:34:02 -04:00
|
|
|
if !ok {
|
|
|
|
if accessKey == globalActiveCred.AccessKey {
|
|
|
|
return newUserIdentity(globalActiveCred), true
|
|
|
|
}
|
|
|
|
}
|
2022-07-01 16:19:13 -04:00
|
|
|
return u, ok && u.Credentials.IsValid()
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2021-11-29 17:38:57 -05:00
|
|
|
// Notify all other MinIO peers to load group.
|
|
|
|
func (sys *IAMSys) notifyForGroup(ctx context.Context, group string) {
|
|
|
|
if !sys.HasWatcher() {
|
2022-02-07 13:39:57 -05:00
|
|
|
for _, nerr := range globalNotificationSys.LoadGroup(group) {
|
2021-11-29 17:38:57 -05:00
|
|
|
if nerr.Err != nil {
|
|
|
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
|
|
logger.LogIf(ctx, nerr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-02 17:25:00 -04:00
|
|
|
// AddUsersToGroup - adds users to a group, creating the group if
|
|
|
|
// needed. No error if user(s) already are in the group.
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) AddUsersToGroup(ctx context.Context, group string, members []string) (updatedAt time.Time, err error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errServerNotInitialized
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
|
2019-09-09 19:12:29 -04:00
|
|
|
if sys.usersSysType != MinIOUsersSysType {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errIAMActionNotAllowed
|
2019-09-09 19:12:29 -04:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err = sys.store.AddUsersToGroup(ctx, group, members)
|
2021-11-29 17:38:57 -05:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, err
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sys.notifyForGroup(ctx, group)
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, nil
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveUsersFromGroup - remove users from group. If no users are
|
|
|
|
// given, and the group is empty, deletes the group as well.
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) RemoveUsersFromGroup(ctx context.Context, group string, members []string) (updatedAt time.Time, err error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errServerNotInitialized
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
|
2019-09-09 19:12:29 -04:00
|
|
|
if sys.usersSysType != MinIOUsersSysType {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errIAMActionNotAllowed
|
2019-09-09 19:12:29 -04:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err = sys.store.RemoveUsersFromGroup(ctx, group, members)
|
2021-11-29 17:38:57 -05:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, err
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sys.notifyForGroup(ctx, group)
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, nil
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetGroupStatus - enable/disabled a group
|
2022-07-01 16:19:13 -04:00
|
|
|
func (sys *IAMSys) SetGroupStatus(ctx context.Context, group string, enabled bool) (updatedAt time.Time, err error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errServerNotInitialized
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
|
2019-09-09 19:12:29 -04:00
|
|
|
if sys.usersSysType != MinIOUsersSysType {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errIAMActionNotAllowed
|
2019-09-09 19:12:29 -04:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err = sys.store.SetGroupStatus(ctx, group, enabled)
|
2021-11-29 17:38:57 -05:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, err
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
sys.notifyForGroup(ctx, group)
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, nil
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetGroupDescription - builds up group description
|
|
|
|
func (sys *IAMSys) GetGroupDescription(group string) (gd madmin.GroupDesc, err error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2020-04-07 17:26:39 -04:00
|
|
|
return gd, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
return sys.store.GetGroupDescription(group)
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
|
2019-09-09 19:12:29 -04:00
|
|
|
// ListGroups - lists groups.
|
2021-11-15 17:14:22 -05:00
|
|
|
func (sys *IAMSys) ListGroups(ctx context.Context) (r []string, err error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2020-04-07 17:26:39 -04:00
|
|
|
return r, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2022-04-19 12:00:19 -04:00
|
|
|
select {
|
|
|
|
case <-sys.configLoaded:
|
|
|
|
return sys.store.ListGroups(ctx)
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
|
2022-08-23 14:11:45 -04:00
|
|
|
// PolicyDBSet - sets a policy for a user or group in the PolicyDB - the user doesn't have to exist since sometimes they are virtuals
|
|
|
|
func (sys *IAMSys) PolicyDBSet(ctx context.Context, name, policy string, userType IAMUserType, isGroup bool) (updatedAt time.Time, err error) {
|
2020-11-08 00:03:06 -05:00
|
|
|
if !sys.Initialized() {
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, errServerNotInitialized
|
2019-07-24 20:34:23 -04:00
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
updatedAt, err = sys.store.PolicyDBSet(ctx, name, policy, userType, isGroup)
|
2021-11-29 17:38:57 -05:00
|
|
|
if err != nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
return
|
2021-11-29 17:38:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Notify all other MinIO peers to reload policy
|
|
|
|
if !sys.HasWatcher() {
|
2022-08-23 14:11:45 -04:00
|
|
|
for _, nerr := range globalNotificationSys.LoadPolicyMapping(name, userType, isGroup) {
|
2021-11-29 17:38:57 -05:00
|
|
|
if nerr.Err != nil {
|
|
|
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
|
|
logger.LogIf(ctx, nerr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-01 16:19:13 -04:00
|
|
|
return updatedAt, nil
|
2019-07-24 20:34:23 -04:00
|
|
|
}
|
|
|
|
|
2022-12-09 16:08:33 -05:00
|
|
|
// PolicyDBUpdateLDAP - adds or removes policies from a user or a group verified
|
|
|
|
// to be in the LDAP directory.
|
|
|
|
func (sys *IAMSys) PolicyDBUpdateLDAP(ctx context.Context, isAttach bool,
|
|
|
|
r madmin.PolicyAssociationReq,
|
|
|
|
) (updatedAt time.Time, addedOrRemoved []string, err error) {
|
|
|
|
if !sys.Initialized() {
|
|
|
|
return updatedAt, nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
var dn string
|
|
|
|
var isGroup bool
|
|
|
|
if r.User != "" {
|
2023-02-24 21:37:22 -05:00
|
|
|
dn, err = sys.LDAPConfig.DoesUsernameExist(r.User)
|
2022-12-09 16:08:33 -05:00
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
return updatedAt, nil, err
|
|
|
|
}
|
|
|
|
if dn == "" {
|
|
|
|
return updatedAt, nil, errNoSuchUser
|
|
|
|
}
|
|
|
|
isGroup = false
|
|
|
|
} else {
|
2023-02-24 21:37:22 -05:00
|
|
|
if exists, err := sys.LDAPConfig.DoesGroupDNExist(r.Group); err != nil {
|
2022-12-09 16:08:33 -05:00
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
return updatedAt, nil, err
|
|
|
|
} else if !exists {
|
|
|
|
return updatedAt, nil, errNoSuchGroup
|
|
|
|
}
|
|
|
|
dn = r.Group
|
|
|
|
isGroup = true
|
|
|
|
}
|
|
|
|
|
|
|
|
userType := stsUser
|
|
|
|
updatedAt, addedOrRemoved, err = sys.store.PolicyDBUpdate(ctx, dn, isGroup,
|
|
|
|
userType, r.Policies, isAttach)
|
|
|
|
if err != nil {
|
|
|
|
return updatedAt, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Notify all other MinIO peers to reload policy
|
|
|
|
if !sys.HasWatcher() {
|
|
|
|
for _, nerr := range globalNotificationSys.LoadPolicyMapping(dn, userType, isGroup) {
|
|
|
|
if nerr.Err != nil {
|
|
|
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
|
|
logger.LogIf(ctx, nerr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return updatedAt, addedOrRemoved, nil
|
|
|
|
}
|
|
|
|
|
2021-03-25 03:38:15 -04:00
|
|
|
// PolicyDBGet - gets policy set on a user or group. If a list of groups is
|
|
|
|
// given, policies associated with them are included as well.
|
|
|
|
func (sys *IAMSys) PolicyDBGet(name string, isGroup bool, groups ...string) ([]string, error) {
|
2021-03-23 18:15:51 -04:00
|
|
|
if !sys.Initialized() {
|
|
|
|
return nil, errServerNotInitialized
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:47:49 -04:00
|
|
|
return sys.store.PolicyDBGet(name, isGroup, groups...)
|
2019-07-24 20:34:23 -04:00
|
|
|
}
|
|
|
|
|
2022-05-02 20:56:19 -04:00
|
|
|
const sessionPolicyNameExtracted = iampolicy.SessionPolicyName + "-extracted"
|
|
|
|
|
2020-03-17 13:36:13 -04:00
|
|
|
// IsAllowedServiceAccount - checks if the given service account is allowed to perform
|
|
|
|
// actions. The permission of the parent user is checked first
|
2021-07-11 20:39:52 -04:00
|
|
|
func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parentUser string) bool {
|
2022-07-26 22:06:55 -04:00
|
|
|
// Verify if the parent claim matches the parentUser.
|
2020-03-17 13:36:13 -04:00
|
|
|
p, ok := args.Claims[parentClaim]
|
|
|
|
if ok {
|
|
|
|
parentInClaim, ok := p.(string)
|
|
|
|
if !ok {
|
|
|
|
// Reject malformed/malicious requests.
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// The parent claim in the session token should be equal
|
|
|
|
// to the parent detected in the backend
|
2021-07-11 20:39:52 -04:00
|
|
|
if parentInClaim != parentUser {
|
2020-03-17 13:36:13 -04:00
|
|
|
return false
|
|
|
|
}
|
2020-03-23 17:17:18 -04:00
|
|
|
} else {
|
|
|
|
// This is needed so a malicious user cannot
|
|
|
|
// use a leaked session key of another user
|
|
|
|
// to widen its privileges.
|
|
|
|
return false
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
|
|
|
|
2022-07-26 22:06:55 -04:00
|
|
|
isOwnerDerived := parentUser == globalActiveCred.AccessKey
|
2020-03-17 13:36:13 -04:00
|
|
|
|
2022-07-26 22:06:55 -04:00
|
|
|
var err error
|
|
|
|
var svcPolicies []string
|
|
|
|
roleArn := args.GetRoleArn()
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case isOwnerDerived:
|
|
|
|
// All actions are allowed by default and no policy evaluation is
|
|
|
|
// required.
|
|
|
|
|
|
|
|
case roleArn != "":
|
|
|
|
arn, err := arn.Parse(roleArn)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(GlobalContext, fmt.Errorf("error parsing role ARN %s: %v", roleArn, err))
|
|
|
|
return false
|
2021-11-18 18:38:54 -05:00
|
|
|
}
|
2022-07-26 22:06:55 -04:00
|
|
|
svcPolicies = newMappedPolicy(sys.rolesMap[arn]).toSlice()
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Check policy for parent user of service account.
|
|
|
|
svcPolicies, err = sys.PolicyDBGet(parentUser, false, args.Groups...)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(GlobalContext, err)
|
2021-11-18 18:38:54 -05:00
|
|
|
return false
|
|
|
|
}
|
2022-07-26 22:06:55 -04:00
|
|
|
|
|
|
|
// Finally, if there is no parent policy, check if a policy claim is
|
|
|
|
// present.
|
|
|
|
if len(svcPolicies) == 0 {
|
|
|
|
policySet, _ := iampolicy.GetPoliciesFromClaims(args.Claims, iamPolicyClaimNameOpenID())
|
|
|
|
svcPolicies = policySet.ToSlice()
|
|
|
|
}
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
|
|
|
|
2022-07-26 22:06:55 -04:00
|
|
|
// Defensive code: Do not allow any operation if no policy is found.
|
|
|
|
if !isOwnerDerived && len(svcPolicies) == 0 {
|
2020-03-17 13:36:13 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-07-26 22:06:55 -04:00
|
|
|
var combinedPolicy iampolicy.Policy
|
|
|
|
// Policies were found, evaluate all of them.
|
|
|
|
if !isOwnerDerived {
|
|
|
|
availablePoliciesStr, c := sys.store.FilterPolicies(strings.Join(svcPolicies, ","), "")
|
|
|
|
if availablePoliciesStr == "" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
combinedPolicy = c
|
|
|
|
}
|
|
|
|
|
2020-03-23 17:17:18 -04:00
|
|
|
parentArgs := args
|
2021-07-11 20:39:52 -04:00
|
|
|
parentArgs.AccountName = parentUser
|
2020-04-14 14:28:56 -04:00
|
|
|
|
|
|
|
saPolicyClaim, ok := args.Claims[iamPolicyClaimNameSA()]
|
|
|
|
if !ok {
|
2020-03-17 13:36:13 -04:00
|
|
|
return false
|
|
|
|
}
|
2020-03-23 17:17:18 -04:00
|
|
|
|
2020-04-14 14:28:56 -04:00
|
|
|
saPolicyClaimStr, ok := saPolicyClaim.(string)
|
|
|
|
if !ok {
|
|
|
|
// Sub policy if set, should be a string reject
|
|
|
|
// malformed/malicious requests.
|
|
|
|
return false
|
|
|
|
}
|
2020-03-23 17:17:18 -04:00
|
|
|
|
2022-05-02 20:56:19 -04:00
|
|
|
if saPolicyClaimStr == inheritedPolicyType {
|
2022-07-26 22:06:55 -04:00
|
|
|
return isOwnerDerived || combinedPolicy.IsAllowed(parentArgs)
|
2020-03-23 17:17:18 -04:00
|
|
|
}
|
2020-03-17 13:36:13 -04:00
|
|
|
|
|
|
|
// Now check if we have a sessionPolicy.
|
2022-05-02 20:56:19 -04:00
|
|
|
spolicy, ok := args.Claims[sessionPolicyNameExtracted]
|
2020-04-14 14:28:56 -04:00
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
2020-03-17 13:36:13 -04:00
|
|
|
|
2020-04-14 14:28:56 -04:00
|
|
|
spolicyStr, ok := spolicy.(string)
|
|
|
|
if !ok {
|
|
|
|
// Sub policy if set, should be a string reject
|
|
|
|
// malformed/malicious requests.
|
|
|
|
return false
|
|
|
|
}
|
2020-03-17 13:36:13 -04:00
|
|
|
|
2020-04-14 14:28:56 -04:00
|
|
|
// Check if policy is parseable.
|
|
|
|
subPolicy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(spolicyStr)))
|
|
|
|
if err != nil {
|
|
|
|
// Log any error in input session policy config.
|
2020-08-13 12:16:01 -04:00
|
|
|
logger.LogIf(GlobalContext, err)
|
2020-04-14 14:28:56 -04:00
|
|
|
return false
|
|
|
|
}
|
2020-03-17 13:36:13 -04:00
|
|
|
|
2021-07-11 20:39:52 -04:00
|
|
|
// This can only happen if policy was set but with an empty JSON.
|
|
|
|
if subPolicy.Version == "" && len(subPolicy.Statements) == 0 {
|
2022-07-26 22:06:55 -04:00
|
|
|
return isOwnerDerived || combinedPolicy.IsAllowed(parentArgs)
|
2021-07-11 20:39:52 -04:00
|
|
|
}
|
|
|
|
|
2020-04-14 14:28:56 -04:00
|
|
|
if subPolicy.Version == "" {
|
|
|
|
return false
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
|
|
|
|
2022-07-26 22:06:55 -04:00
|
|
|
return subPolicy.IsAllowed(parentArgs) && (isOwnerDerived || combinedPolicy.IsAllowed(parentArgs))
|
2020-03-17 13:36:13 -04:00
|
|
|
}
|
|
|
|
|
2020-05-20 14:33:35 -04:00
|
|
|
// IsAllowedSTS is meant for STS based temporary credentials,
|
|
|
|
// which implements claims validation and verification other than
|
|
|
|
// applying policies.
|
2021-02-25 16:49:59 -05:00
|
|
|
func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args, parentUser string) bool {
|
2022-07-26 22:06:55 -04:00
|
|
|
// 1. Determine mapped policies
|
|
|
|
|
|
|
|
isOwnerDerived := parentUser == globalActiveCred.AccessKey
|
2021-11-26 22:22:40 -05:00
|
|
|
var policies []string
|
|
|
|
roleArn := args.GetRoleArn()
|
2022-07-26 22:06:55 -04:00
|
|
|
|
|
|
|
switch {
|
|
|
|
case isOwnerDerived:
|
|
|
|
// All actions are allowed by default and no policy evaluation is
|
|
|
|
// required.
|
|
|
|
|
|
|
|
case roleArn != "":
|
|
|
|
// If a roleARN is present, the role policy is applied.
|
2021-11-26 22:22:40 -05:00
|
|
|
arn, err := arn.Parse(roleArn)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(GlobalContext, fmt.Errorf("error parsing role ARN %s: %v", roleArn, err))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
policies = newMappedPolicy(sys.rolesMap[arn]).toSlice()
|
2022-07-26 22:06:55 -04:00
|
|
|
|
|
|
|
default:
|
|
|
|
// Otherwise, inherit parent user's policy
|
2021-12-20 17:07:16 -05:00
|
|
|
var err error
|
|
|
|
policies, err = sys.store.PolicyDBGet(parentUser, false, args.Groups...)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(GlobalContext, fmt.Errorf("error fetching policies on %s: %v", parentUser, err))
|
|
|
|
return false
|
|
|
|
}
|
2022-07-26 22:06:55 -04:00
|
|
|
|
|
|
|
// Finally, if there is no parent policy, check if a policy claim is
|
|
|
|
// present in the session token.
|
2021-12-20 17:07:16 -05:00
|
|
|
if len(policies) == 0 {
|
Map policy to parent for STS (#13884)
When STS credentials are created for a user, a unique (hopefully stable) parent
user value exists for the credential, which corresponds to the user for whom the
credentials are created. The access policy is mapped to this parent-user and is
persisted. This helps ensure that all STS credentials of a user have the same
policy assignment at all times.
Before this change, for an OIDC STS credential, when the policy claim changes in
the provider (when not using RoleARNs), the change would not take effect on
existing credentials, but only on new ones.
To support existing STS credentials without parent-user policy mappings, we
lookup the policy in the policy claim value. This behavior should be deprecated
when such support is no longer required, as it can still lead to stale
policy mappings.
Additionally this change also simplifies the implementation for all non-RoleARN
STS credentials. Specifically, for AssumeRole (internal IDP) STS credentials,
policies are picked up from the parent user's policies; for
AssumeRoleWithCertificate STS credentials, policies are picked up from the
parent user mapping created when the STS credential is generated.
AssumeRoleWithLDAP already picks up policies mapped to the virtual parent user.
2021-12-17 03:46:30 -05:00
|
|
|
// If there is no parent policy mapping, we fall back to
|
|
|
|
// using policy claim from JWT.
|
|
|
|
policySet, ok := args.GetPolicies(iamPolicyClaimNameOpenID())
|
|
|
|
if !ok {
|
|
|
|
// When claims are set, it should have a policy claim field.
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
policies = policySet.ToSlice()
|
2021-11-26 22:22:40 -05:00
|
|
|
}
|
2022-07-26 22:06:55 -04:00
|
|
|
|
2019-06-20 18:28:33 -04:00
|
|
|
}
|
|
|
|
|
2022-05-19 14:06:55 -04:00
|
|
|
// Defensive code: Do not allow any operation if no policy is found in the session token
|
2022-07-26 22:06:55 -04:00
|
|
|
if !isOwnerDerived && len(policies) == 0 {
|
2022-05-19 14:06:55 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-07-26 22:06:55 -04:00
|
|
|
// 2. Combine the mapped policies into a single combined policy.
|
|
|
|
|
|
|
|
var combinedPolicy iampolicy.Policy
|
|
|
|
if !isOwnerDerived {
|
|
|
|
var err error
|
|
|
|
combinedPolicy, err = sys.store.GetPolicy(strings.Join(policies, ","))
|
2022-11-14 10:15:46 -05:00
|
|
|
if errors.Is(err, errNoSuchPolicy) {
|
2022-07-26 22:06:55 -04:00
|
|
|
for _, pname := range policies {
|
|
|
|
_, err := sys.store.GetPolicy(pname)
|
2022-11-14 10:15:46 -05:00
|
|
|
if errors.Is(err, errNoSuchPolicy) {
|
2022-07-26 22:06:55 -04:00
|
|
|
// all policies presented in the claim should exist
|
|
|
|
logger.LogIf(GlobalContext, fmt.Errorf("expected policy (%s) missing from the JWT claim %s, rejecting the request", pname, iamPolicyClaimNameOpenID()))
|
|
|
|
return false
|
|
|
|
}
|
2021-11-03 22:47:49 -04:00
|
|
|
}
|
2022-07-26 22:06:55 -04:00
|
|
|
logger.LogIf(GlobalContext, fmt.Errorf("all policies were unexpectedly present!"))
|
|
|
|
return false
|
2020-05-11 16:04:11 -04:00
|
|
|
}
|
2022-07-26 22:06:55 -04:00
|
|
|
|
2020-05-11 16:04:11 -04:00
|
|
|
}
|
|
|
|
|
2022-07-26 22:06:55 -04:00
|
|
|
// 3. If an inline session-policy is present, evaluate it.
|
|
|
|
|
2021-07-15 18:27:34 -04:00
|
|
|
// Now check if we have a sessionPolicy.
|
|
|
|
hasSessionPolicy, isAllowedSP := isAllowedBySessionPolicy(args)
|
|
|
|
if hasSessionPolicy {
|
2022-07-26 22:06:55 -04:00
|
|
|
return isAllowedSP && (isOwnerDerived || combinedPolicy.IsAllowed(args))
|
2021-07-15 18:27:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sub policy not set, this is most common since subPolicy
|
|
|
|
// is optional, use the inherited policies.
|
2022-07-26 22:06:55 -04:00
|
|
|
return isOwnerDerived || combinedPolicy.IsAllowed(args)
|
2021-07-15 18:27:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func isAllowedBySessionPolicy(args iampolicy.Args) (hasSessionPolicy bool, isAllowed bool) {
|
|
|
|
hasSessionPolicy = false
|
|
|
|
isAllowed = false
|
|
|
|
|
2019-06-20 18:28:33 -04:00
|
|
|
// Now check if we have a sessionPolicy.
|
2022-05-02 20:56:19 -04:00
|
|
|
spolicy, ok := args.Claims[sessionPolicyNameExtracted]
|
2021-07-15 18:27:34 -04:00
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
2019-06-20 18:28:33 -04:00
|
|
|
|
2021-07-15 18:27:34 -04:00
|
|
|
hasSessionPolicy = true
|
2019-06-20 18:28:33 -04:00
|
|
|
|
2021-07-15 18:27:34 -04:00
|
|
|
spolicyStr, ok := spolicy.(string)
|
|
|
|
if !ok {
|
|
|
|
// Sub policy if set, should be a string reject
|
|
|
|
// malformed/malicious requests.
|
|
|
|
return
|
|
|
|
}
|
2019-06-20 18:28:33 -04:00
|
|
|
|
2021-07-15 18:27:34 -04:00
|
|
|
// Check if policy is parseable.
|
|
|
|
subPolicy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(spolicyStr)))
|
|
|
|
if err != nil {
|
|
|
|
// Log any error in input session policy config.
|
|
|
|
logger.LogIf(GlobalContext, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Policy without Version string value reject it.
|
|
|
|
if subPolicy.Version == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sub policy is set and valid.
|
|
|
|
return hasSessionPolicy, subPolicy.IsAllowed(args)
|
2019-06-20 18:28:33 -04:00
|
|
|
}
|
|
|
|
|
2020-11-27 20:23:57 -05:00
|
|
|
// GetCombinedPolicy returns a combined policy combining all policies
|
|
|
|
func (sys *IAMSys) GetCombinedPolicy(policies ...string) iampolicy.Policy {
|
2021-11-03 22:47:49 -04:00
|
|
|
_, policy := sys.store.FilterPolicies(strings.Join(policies, ","), "")
|
|
|
|
return policy
|
2020-11-27 20:23:57 -05:00
|
|
|
}
|
|
|
|
|
2019-06-20 18:28:33 -04:00
|
|
|
// IsAllowed - checks given policy args is allowed to continue the Rest API.
|
|
|
|
func (sys *IAMSys) IsAllowed(args iampolicy.Args) bool {
|
2018-10-15 15:44:03 -04:00
|
|
|
// If opa is configured, use OPA always.
|
2022-05-30 13:58:37 -04:00
|
|
|
if authz := newGlobalAuthZPluginFn(); authz != nil {
|
|
|
|
ok, err := authz.IsAllowed(args)
|
2019-07-27 23:03:25 -04:00
|
|
|
if err != nil {
|
2020-08-13 12:16:01 -04:00
|
|
|
logger.LogIf(GlobalContext, err)
|
2019-07-27 23:03:25 -04:00
|
|
|
}
|
|
|
|
return ok
|
2018-10-15 15:44:03 -04:00
|
|
|
}
|
|
|
|
|
2019-08-13 16:41:06 -04:00
|
|
|
// Policies don't apply to the owner.
|
|
|
|
if args.IsOwner {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-01-22 04:39:46 -05:00
|
|
|
// If the credential is temporary, perform STS related checks.
|
2021-02-25 16:49:59 -05:00
|
|
|
ok, parentUser, err := sys.IsTempUser(args.AccountName)
|
2020-01-22 04:39:46 -05:00
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if ok {
|
2021-02-25 16:49:59 -05:00
|
|
|
return sys.IsAllowedSTS(args, parentUser)
|
2019-10-23 01:59:13 -04:00
|
|
|
}
|
|
|
|
|
2020-03-17 13:36:13 -04:00
|
|
|
// If the credential is for a service account, perform related check
|
2021-02-25 16:49:59 -05:00
|
|
|
ok, parentUser, err = sys.IsServiceAccount(args.AccountName)
|
2020-03-17 13:36:13 -04:00
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if ok {
|
|
|
|
return sys.IsAllowedServiceAccount(args, parentUser)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Continue with the assumption of a regular user
|
2021-03-26 16:55:42 -04:00
|
|
|
policies, err := sys.PolicyDBGet(args.AccountName, false, args.Groups...)
|
2019-08-13 16:41:06 -04:00
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(policies) == 0 {
|
|
|
|
// No policy found.
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Policies were found, evaluate all of them.
|
2020-11-27 20:23:57 -05:00
|
|
|
return sys.GetCombinedPolicy(policies...).IsAllowed(args)
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
|
2022-08-23 14:11:45 -04:00
|
|
|
// SetUsersSysType - sets the users system type, regular or LDAP.
|
|
|
|
func (sys *IAMSys) SetUsersSysType(t UsersSysType) {
|
|
|
|
sys.usersSysType = t
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetUsersSysType - returns the users system type for this IAM
|
|
|
|
func (sys *IAMSys) GetUsersSysType() UsersSysType {
|
|
|
|
return sys.usersSysType
|
2020-03-22 01:47:26 -04:00
|
|
|
}
|
|
|
|
|
2018-10-09 17:00:01 -04:00
|
|
|
// NewIAMSys - creates new config system object.
|
|
|
|
func NewIAMSys() *IAMSys {
|
|
|
|
return &IAMSys{
|
2021-11-03 22:47:49 -04:00
|
|
|
usersSysType: MinIOUsersSysType,
|
|
|
|
configLoaded: make(chan struct{}),
|
2018-10-09 17:00:01 -04:00
|
|
|
}
|
|
|
|
}
|