fix: site replication issues and add tests (#13861)

- deleting policies was deleting all LDAP
  user mapping, this was a regression introduced
  in #13567

- deleting of policies is properly sent across
  all sites.

- remove unexpected errors instead embed the real
  errors as part of the 500 error response.
This commit is contained in:
Harshavardhana 2021-12-08 11:50:15 -08:00 committed by GitHub
parent 92fdcafb66
commit e82a5c5c54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 215 additions and 49 deletions

View File

@ -85,3 +85,7 @@ jobs:
sudo sysctl net.ipv6.conf.all.disable_ipv6=0 sudo sysctl net.ipv6.conf.all.disable_ipv6=0
sudo sysctl net.ipv6.conf.default.disable_ipv6=0 sudo sysctl net.ipv6.conf.default.disable_ipv6=0
make test-iam make test-iam
- name: Test LDAP for automatic site replication
if: matrix.ldap == 'localhost:389'
run: |
make test-site-replication

View File

@ -57,9 +57,13 @@ test-iam: build ## verify IAM (external IDP, etcd backends)
@CGO_ENABLED=1 go test -race -tags kqueue -v -run TestIAM* ./cmd @CGO_ENABLED=1 go test -race -tags kqueue -v -run TestIAM* ./cmd
test-replication: install ## verify multi site replication test-replication: install ## verify multi site replication
@echo "Running tests for Replication three sites" @echo "Running tests for replicating three sites"
@(env bash $(PWD)/docs/bucket/replication/setup_3site_replication.sh) @(env bash $(PWD)/docs/bucket/replication/setup_3site_replication.sh)
test-site-replication: install ## verify automatic site replication
@echo "Running tests for automatic site replication of IAM"
@(env bash $(PWD)/docs/site-replication/run-multi-site.sh)
verify: ## verify minio various setups verify: ## verify minio various setups
@echo "Verifying build with race" @echo "Verifying build with race"
@GO111MODULE=on CGO_ENABLED=1 go build -race -tags kqueue -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null @GO111MODULE=on CGO_ENABLED=1 go build -race -tags kqueue -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null

View File

@ -113,6 +113,8 @@ func (a adminAPIHandlers) SRInternalBucketOps(w http.ResponseWriter, r *http.Req
var err error var err error
switch operation { switch operation {
default:
err = errInvalidArgument
case madmin.MakeWithVersioningBktOp: case madmin.MakeWithVersioningBktOp:
_, isLockEnabled := r.Form["lockEnabled"] _, isLockEnabled := r.Form["lockEnabled"]
_, isVersioningEnabled := r.Form["versioningEnabled"] _, isVersioningEnabled := r.Form["versioningEnabled"]
@ -128,13 +130,10 @@ func (a adminAPIHandlers) SRInternalBucketOps(w http.ResponseWriter, r *http.Req
err = globalSiteReplicationSys.PeerBucketDeleteHandler(ctx, bucket, false) err = globalSiteReplicationSys.PeerBucketDeleteHandler(ctx, bucket, false)
case madmin.ForceDeleteBucketBktOp: case madmin.ForceDeleteBucketBktOp:
err = globalSiteReplicationSys.PeerBucketDeleteHandler(ctx, bucket, true) err = globalSiteReplicationSys.PeerBucketDeleteHandler(ctx, bucket, true)
default:
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminInvalidArgument), r.URL)
return
} }
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), r.URL) writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return return
} }
@ -160,37 +159,40 @@ func (a adminAPIHandlers) SRInternalReplicateIAMItem(w http.ResponseWriter, r *h
var err error var err error
switch item.Type { switch item.Type {
default:
err = errInvalidArgument
case madmin.SRIAMItemPolicy: case madmin.SRIAMItemPolicy:
var policy *iampolicy.Policy if item.Policy == nil {
if len(item.Policy) > 0 { err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil)
policy, err = iampolicy.ParseConfig(bytes.NewReader(item.Policy)) } else {
if err != nil { policy, perr := iampolicy.ParseConfig(bytes.NewReader(item.Policy))
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) if perr != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, perr), r.URL)
return return
} }
if policy.IsEmpty() {
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil)
} else {
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, policy)
}
} }
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, policy)
case madmin.SRIAMItemSvcAcc: case madmin.SRIAMItemSvcAcc:
err = globalSiteReplicationSys.PeerSvcAccChangeHandler(ctx, *item.SvcAccChange) err = globalSiteReplicationSys.PeerSvcAccChangeHandler(ctx, item.SvcAccChange)
case madmin.SRIAMItemPolicyMapping: case madmin.SRIAMItemPolicyMapping:
err = globalSiteReplicationSys.PeerPolicyMappingHandler(ctx, *item.PolicyMapping) err = globalSiteReplicationSys.PeerPolicyMappingHandler(ctx, item.PolicyMapping)
case madmin.SRIAMItemSTSAcc: case madmin.SRIAMItemSTSAcc:
err = globalSiteReplicationSys.PeerSTSAccHandler(ctx, *item.STSCredential) err = globalSiteReplicationSys.PeerSTSAccHandler(ctx, item.STSCredential)
default:
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminInvalidArgument), r.URL)
return
} }
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), r.URL) writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return return
} }
} }
// SRInternalReplicateBucketItem - PUT /minio/admin/v3/site-replication/bucket-meta // SRInternalReplicateBucketItem - PUT /minio/admin/v3/site-replication/bucket-meta
func (a adminAPIHandlers) SRInternalReplicateBucketItem(w http.ResponseWriter, r *http.Request) { func (a adminAPIHandlers) SRInternalReplicateBucketItem(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "SRInternalReplicateIAMItem") ctx := newContext(r, w, "SRInternalReplicateBucketItem")
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
@ -208,30 +210,33 @@ func (a adminAPIHandlers) SRInternalReplicateBucketItem(w http.ResponseWriter, r
var err error var err error
switch item.Type { switch item.Type {
default:
err = errInvalidArgument
case madmin.SRBucketMetaTypePolicy: case madmin.SRBucketMetaTypePolicy:
var bktPolicy *policy.Policy if item.Policy == nil {
if len(item.Policy) > 0 { err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, nil)
bktPolicy, err = policy.ParseConfig(bytes.NewReader(item.Policy), item.Bucket) } else {
if err != nil { bktPolicy, berr := policy.ParseConfig(bytes.NewReader(item.Policy), item.Bucket)
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) if berr != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, berr), r.URL)
return return
} }
if bktPolicy.IsEmpty() {
err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, nil)
} else {
err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, bktPolicy)
}
} }
err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, bktPolicy)
case madmin.SRBucketMetaTypeTags: case madmin.SRBucketMetaTypeTags:
err = globalSiteReplicationSys.PeerBucketTaggingHandler(ctx, item.Bucket, item.Tags) err = globalSiteReplicationSys.PeerBucketTaggingHandler(ctx, item.Bucket, item.Tags)
case madmin.SRBucketMetaTypeObjectLockConfig: case madmin.SRBucketMetaTypeObjectLockConfig:
err = globalSiteReplicationSys.PeerBucketObjectLockConfigHandler(ctx, item.Bucket, item.ObjectLockConfig) err = globalSiteReplicationSys.PeerBucketObjectLockConfigHandler(ctx, item.Bucket, item.ObjectLockConfig)
case madmin.SRBucketMetaTypeSSEConfig: case madmin.SRBucketMetaTypeSSEConfig:
err = globalSiteReplicationSys.PeerBucketSSEConfigHandler(ctx, item.Bucket, item.SSEConfig) err = globalSiteReplicationSys.PeerBucketSSEConfigHandler(ctx, item.Bucket, item.SSEConfig)
default:
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminInvalidArgument), r.URL)
return
} }
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), r.URL) writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return return
} }
} }

View File

@ -846,12 +846,14 @@ func (store *IAMStoreSys) PolicyNotificationHandler(ctx context.Context, policy
if !pset.Contains(policy) { if !pset.Contains(policy) {
continue continue
} }
_, ok := cache.iamUsersMap[u] if store.getUsersSysType() == MinIOUsersSysType {
if !ok { _, ok := cache.iamUsersMap[u]
// happens when account is deleted or if !ok {
// expired. // happens when account is deleted or
delete(cache.iamUserPolicyMap, u) // expired.
continue delete(cache.iamUserPolicyMap, u)
continue
}
} }
pset.Remove(policy) pset.Remove(policy)
cache.iamUserPolicyMap[u] = newMappedPolicy(strings.Join(pset.ToSlice(), ",")) cache.iamUserPolicyMap[u] = newMappedPolicy(strings.Join(pset.ToSlice(), ","))
@ -886,11 +888,13 @@ func (store *IAMStoreSys) DeletePolicy(ctx context.Context, policy string) error
groups := []string{} groups := []string{}
for u, mp := range cache.iamUserPolicyMap { for u, mp := range cache.iamUserPolicyMap {
pset := mp.policySet() pset := mp.policySet()
if _, ok := cache.iamUsersMap[u]; !ok { if store.getUsersSysType() == MinIOUsersSysType {
// This case can happen when a temporary account is if _, ok := cache.iamUsersMap[u]; !ok {
// deleted or expired - remove it from userPolicyMap. // This case can happen when a temporary account is
delete(cache.iamUserPolicyMap, u) // deleted or expired - remove it from userPolicyMap.
continue delete(cache.iamUserPolicyMap, u)
continue
}
} }
if pset.Contains(policy) { if pset.Contains(policy) {
users = append(users, u) users = append(users, u)

View File

@ -48,8 +48,7 @@ import (
const ( const (
srStatePrefix = minioConfigPrefix + "/site-replication" srStatePrefix = minioConfigPrefix + "/site-replication"
srStateFile = "state.json"
srStateFile = "state.json"
) )
const ( const (
@ -1007,7 +1006,10 @@ func (c *SiteReplicationSys) PeerAddPolicyHandler(ctx context.Context, policyNam
} }
// PeerSvcAccChangeHandler - copies service-account change to local. // PeerSvcAccChangeHandler - copies service-account change to local.
func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change madmin.SRSvcAccChange) error { func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change *madmin.SRSvcAccChange) error {
if change == nil {
return errInvalidArgument
}
switch { switch {
case change.Create != nil: case change.Create != nil:
var sp *iampolicy.Policy var sp *iampolicy.Policy
@ -1069,7 +1071,10 @@ func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change
} }
// PeerPolicyMappingHandler - copies policy mapping to local. // PeerPolicyMappingHandler - copies policy mapping to local.
func (c *SiteReplicationSys) PeerPolicyMappingHandler(ctx context.Context, mapping madmin.SRPolicyMapping) error { func (c *SiteReplicationSys) PeerPolicyMappingHandler(ctx context.Context, mapping *madmin.SRPolicyMapping) error {
if mapping == nil {
return errInvalidArgument
}
err := globalIAMSys.PolicyDBSet(ctx, mapping.UserOrGroup, mapping.Policy, mapping.IsGroup) err := globalIAMSys.PolicyDBSet(ctx, mapping.UserOrGroup, mapping.Policy, mapping.IsGroup)
if err != nil { if err != nil {
return wrapSRErr(err) return wrapSRErr(err)
@ -1078,7 +1083,11 @@ func (c *SiteReplicationSys) PeerPolicyMappingHandler(ctx context.Context, mappi
} }
// PeerSTSAccHandler - replicates STS credential locally. // PeerSTSAccHandler - replicates STS credential locally.
func (c *SiteReplicationSys) PeerSTSAccHandler(ctx context.Context, stsCred madmin.SRSTSCredential) error { func (c *SiteReplicationSys) PeerSTSAccHandler(ctx context.Context, stsCred *madmin.SRSTSCredential) error {
if stsCred == nil {
return errInvalidArgument
}
// Verify the session token of the stsCred // Verify the session token of the stsCred
claims, err := auth.ExtractClaims(stsCred.SessionToken, globalActiveCred.SecretKey) claims, err := auth.ExtractClaims(stsCred.SessionToken, globalActiveCred.SecretKey)
if err != nil { if err != nil {
@ -1089,13 +1098,13 @@ func (c *SiteReplicationSys) PeerSTSAccHandler(ctx context.Context, stsCred madm
mapClaims := claims.Map() mapClaims := claims.Map()
expiry, err := auth.ExpToInt64(mapClaims["exp"]) expiry, err := auth.ExpToInt64(mapClaims["exp"])
if err != nil { if err != nil {
return fmt.Errorf("Expiry claim was not found") return fmt.Errorf("Expiry claim was not found: %v", mapClaims)
} }
// Extract the username and lookup DN and groups in LDAP. // Extract the username and lookup DN and groups in LDAP.
ldapUser, ok := claims.Lookup(ldapUserN) ldapUser, ok := claims.Lookup(ldapUserN)
if !ok { if !ok {
return fmt.Errorf("Could not find LDAP username in claims") return fmt.Errorf("Could not find LDAP username in claims: %v", mapClaims)
} }
// Need to lookup the groups from LDAP. // Need to lookup the groups from LDAP.

View File

@ -0,0 +1,139 @@
#!/usr/bin/env bash
# shellcheck disable=SC2120
exit_1() {
cleanup
exit 1
}
cleanup() {
echo "Cleaning up instances of MinIO"
pkill minio
pkill -9 minio
rm -rf /tmp/minio{1,2,3}
}
cleanup
unset MINIO_KMS_KES_CERT_FILE
unset MINIO_KMS_KES_KEY_FILE
unset MINIO_KMS_KES_ENDPOINT
unset MINIO_KMS_KES_KEY_NAME
export MINIO_BROWSER=off
export MINIO_ROOT_USER="minio"
export MINIO_ROOT_PASSWORD="minio123"
export MINIO_KMS_AUTO_ENCRYPTION=off
export MINIO_PROMETHEUS_AUTH_TYPE=public
export MINIO_KMS_SECRET_KEY=my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw=
export MINIO_IDENTITY_LDAP_SERVER_ADDR="localhost:389"
export MINIO_IDENTITY_LDAP_SERVER_INSECURE="on"
export MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN="cn=admin,dc=min,dc=io"
export MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD="admin"
export MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN="dc=min,dc=io"
export MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER="(uid=%s)"
export MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN="ou=swengg,dc=min,dc=io"
export MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER="(&(objectclass=groupOfNames)(member=%d))"
if [ ! -f ./mc ]; then
wget -O mc https://dl.minio.io/client/mc/release/linux-amd64/mc \
&& chmod +x mc
fi
minio server --address ":9001" /tmp/minio1/{1...4} >/tmp/minio1_1.log 2>&1 &
minio server --address ":9002" /tmp/minio2/{1...4} >/tmp/minio2_1.log 2>&1 &
minio server --address ":9003" /tmp/minio3/{1...4} >/tmp/minio3_1.log 2>&1 &
sleep 10
export MC_HOST_minio1=http://minio:minio123@localhost:9001
export MC_HOST_minio2=http://minio:minio123@localhost:9002
export MC_HOST_minio3=http://minio:minio123@localhost:9003
./mc admin replicate add minio1 minio2 minio3
./mc admin policy set minio1 consoleAdmin user="uid=dillon,ou=people,ou=swengg,dc=min,dc=io"
sleep 5
./mc admin user info minio2 "uid=dillon,ou=people,ou=swengg,dc=min,dc=io"
./mc admin user info minio3 "uid=dillon,ou=people,ou=swengg,dc=min,dc=io"
./mc admin policy add minio1 rw ./docs/site-replication/rw.json
sleep 5
./mc admin policy info minio2 rw >/dev/null 2>&1
./mc admin policy info minio3 rw >/dev/null 2>&1
./mc admin policy remove minio3 rw
sleep 10
./mc admin policy info minio1 rw
if [ $? -eq 0 ]; then
echo "expecting the command to fail, exiting.."
exit_1;
fi
./mc admin policy info minio2 rw
if [ $? -eq 0 ]; then
echo "expecting the command to fail, exiting.."
exit_1;
fi
./mc admin user info minio1 "uid=dillon,ou=people,ou=swengg,dc=min,dc=io"
if [ $? -eq 1 ]; then
echo "policy mapping missing, exiting.."
exit_1;
fi
./mc admin user info minio2 "uid=dillon,ou=people,ou=swengg,dc=min,dc=io"
if [ $? -eq 1 ]; then
echo "policy mapping missing, exiting.."
exit_1;
fi
./mc admin user info minio3 "uid=dillon,ou=people,ou=swengg,dc=min,dc=io"
if [ $? -eq 1 ]; then
echo "policy mapping missing, exiting.."
exit_1;
fi
# LDAP simple user
./mc admin user svcacct add minio2 dillon --access-key testsvc --secret-key testsvc123
if [ $? -eq 1 ]; then
echo "adding svc account failed, exiting.."
exit_1;
fi
sleep 10
./mc admin user svcacct info minio1 testsvc
if [ $? -eq 1 ]; then
echo "svc account not mirrored, exiting.."
exit_1;
fi
./mc admin user svcacct info minio2 testsvc
if [ $? -eq 1 ]; then
echo "svc account not mirrored, exiting.."
exit_1;
fi
./mc admin user svcacct rm minio1 testsvc
if [ $? -eq 1 ]; then
echo "removing svc account failed, exiting.."
exit_1;
fi
sleep 10
./mc admin user svcacct info minio2 testsvc
if [ $? -eq 0 ]; then
echo "svc account found after delete, exiting.."
exit_1;
fi
./mc admin user svcacct info minio3 testsvc
if [ $? -eq 0 ]; then
echo "svc account found after delete, exiting.."
exit_1;
fi
cleanup

View File

@ -0,0 +1 @@
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["admin:*"]},{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::*"]}]}