mirror of
https://github.com/minio/minio.git
synced 2024-12-25 06:35:56 -05:00
Convert service account add/update expiration to cond values (#19024)
In order to force some users allowed to create or update a service account to provide an expiration satifying the user policy conditions.
This commit is contained in:
parent
0e177a44e0
commit
4fa06aefc6
@ -27,6 +27,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/klauspost/compress/zip"
|
"github.com/klauspost/compress/zip"
|
||||||
@ -774,28 +775,6 @@ func (a adminAPIHandlers) UpdateServiceAccount(w http.ResponseWriter, r *http.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Permission checks:
|
|
||||||
//
|
|
||||||
// 1. Any type of account (i.e. access keys (previously/still called service
|
|
||||||
// accounts), STS accounts, internal IDP accounts, etc) with the
|
|
||||||
// policy.UpdateServiceAccountAdminAction permission can update any service
|
|
||||||
// account.
|
|
||||||
//
|
|
||||||
// 2. We would like to let a user update their own access keys, however it
|
|
||||||
// is currently blocked pending a re-design. Users are still able to delete
|
|
||||||
// and re-create them.
|
|
||||||
if !globalIAMSys.IsAllowed(policy.Args{
|
|
||||||
AccountName: cred.AccessKey,
|
|
||||||
Groups: cred.Groups,
|
|
||||||
Action: policy.UpdateServiceAccountAdminAction,
|
|
||||||
ConditionValues: getConditionValues(r, "", cred),
|
|
||||||
IsOwner: owner,
|
|
||||||
Claims: cred.Claims,
|
|
||||||
}) {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
password := cred.SecretKey
|
password := cred.SecretKey
|
||||||
reqBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
|
reqBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -816,6 +795,31 @@ func (a adminAPIHandlers) UpdateServiceAccount(w http.ResponseWriter, r *http.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
condValues := getConditionValues(r, "", cred)
|
||||||
|
addExpirationToCondValues(updateReq.NewExpiration, condValues)
|
||||||
|
|
||||||
|
// Permission checks:
|
||||||
|
//
|
||||||
|
// 1. Any type of account (i.e. access keys (previously/still called service
|
||||||
|
// accounts), STS accounts, internal IDP accounts, etc) with the
|
||||||
|
// policy.UpdateServiceAccountAdminAction permission can update any service
|
||||||
|
// account.
|
||||||
|
//
|
||||||
|
// 2. We would like to let a user update their own access keys, however it
|
||||||
|
// is currently blocked pending a re-design. Users are still able to delete
|
||||||
|
// and re-create them.
|
||||||
|
if !globalIAMSys.IsAllowed(policy.Args{
|
||||||
|
AccountName: cred.AccessKey,
|
||||||
|
Groups: cred.Groups,
|
||||||
|
Action: policy.UpdateServiceAccountAdminAction,
|
||||||
|
ConditionValues: condValues,
|
||||||
|
IsOwner: owner,
|
||||||
|
Claims: cred.Claims,
|
||||||
|
}) {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var sp *policy.Policy
|
var sp *policy.Policy
|
||||||
if len(updateReq.NewPolicy) > 0 {
|
if len(updateReq.NewPolicy) > 0 {
|
||||||
sp, err = policy.ParseConfig(bytes.NewReader(updateReq.NewPolicy))
|
sp, err = policy.ParseConfig(bytes.NewReader(updateReq.NewPolicy))
|
||||||
@ -2417,6 +2421,13 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addExpirationToCondValues(exp *time.Time, condValues map[string][]string) {
|
||||||
|
if exp == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
condValues["DurationSeconds"] = []string{strconv.FormatInt(int64(exp.Sub(time.Now()).Seconds()), 10)}
|
||||||
|
}
|
||||||
|
|
||||||
func commonAddServiceAccount(r *http.Request) (context.Context, auth.Credentials, newServiceAccountOpts, madmin.AddServiceAccountReq, string, APIError) {
|
func commonAddServiceAccount(r *http.Request) (context.Context, auth.Credentials, newServiceAccountOpts, madmin.AddServiceAccountReq, string, APIError) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
@ -2472,13 +2483,16 @@ func commonAddServiceAccount(r *http.Request) (context.Context, auth.Credentials
|
|||||||
claims: make(map[string]interface{}),
|
claims: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
condValues := getConditionValues(r, "", cred)
|
||||||
|
addExpirationToCondValues(createReq.Expiration, condValues)
|
||||||
|
|
||||||
// Check if action is allowed if creating access key for another user
|
// Check if action is allowed if creating access key for another user
|
||||||
// Check if action is explicitly denied if for self
|
// Check if action is explicitly denied if for self
|
||||||
if !globalIAMSys.IsAllowed(policy.Args{
|
if !globalIAMSys.IsAllowed(policy.Args{
|
||||||
AccountName: cred.AccessKey,
|
AccountName: cred.AccessKey,
|
||||||
Groups: cred.Groups,
|
Groups: cred.Groups,
|
||||||
Action: policy.CreateServiceAccountAdminAction,
|
Action: policy.CreateServiceAccountAdminAction,
|
||||||
ConditionValues: getConditionValues(r, "", cred),
|
ConditionValues: condValues,
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
Claims: cred.Claims,
|
Claims: cred.Claims,
|
||||||
DenyOnly: (targetUser == cred.AccessKey || targetUser == cred.ParentUser),
|
DenyOnly: (targetUser == cred.AccessKey || targetUser == cred.ParentUser),
|
||||||
|
@ -208,6 +208,7 @@ func TestIAMInternalIDPServerSuite(t *testing.T) {
|
|||||||
suite.TestServiceAccountOpsByAdmin(c)
|
suite.TestServiceAccountOpsByAdmin(c)
|
||||||
suite.TestServiceAccountPrivilegeEscalationBug(c)
|
suite.TestServiceAccountPrivilegeEscalationBug(c)
|
||||||
suite.TestServiceAccountOpsByUser(c)
|
suite.TestServiceAccountOpsByUser(c)
|
||||||
|
suite.TestServiceAccountDurationSecondsCondition(c)
|
||||||
suite.TestAddServiceAccountPerms(c)
|
suite.TestAddServiceAccountPerms(c)
|
||||||
suite.TearDownSuite(c)
|
suite.TearDownSuite(c)
|
||||||
},
|
},
|
||||||
@ -904,6 +905,93 @@ func (s *TestSuiteIAM) TestServiceAccountOpsByUser(c *check) {
|
|||||||
c.mustNotCreateSvcAccount(ctx, globalActiveCred.AccessKey, userAdmClient)
|
c.mustNotCreateSvcAccount(ctx, globalActiveCred.AccessKey, userAdmClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TestSuiteIAM) TestServiceAccountDurationSecondsCondition(c *check) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
bucket := getRandomBucketName()
|
||||||
|
err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("bucket creat error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create policy, user and associate policy
|
||||||
|
policy := "mypolicy"
|
||||||
|
policyBytes := []byte(fmt.Sprintf(`{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Deny",
|
||||||
|
"Action": [
|
||||||
|
"admin:CreateServiceAccount",
|
||||||
|
"admin:UpdateServiceAccount"
|
||||||
|
],
|
||||||
|
"Condition": {"NumericGreaterThan": {"svc:DurationSeconds": "3600"}}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"s3:PutObject",
|
||||||
|
"s3:GetObject",
|
||||||
|
"s3:ListBucket"
|
||||||
|
],
|
||||||
|
"Resource": [
|
||||||
|
"arn:aws:s3:::%s/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`, bucket))
|
||||||
|
err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("policy add error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
accessKey, secretKey := mustGenerateCredentials(c)
|
||||||
|
err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Unable to set user: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.adm.SetPolicy(ctx, policy, accessKey, false)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Unable to set policy: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an madmin client with user creds
|
||||||
|
userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
|
||||||
|
Creds: credentials.NewStaticV4(accessKey, secretKey, ""),
|
||||||
|
Secure: s.secure,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Err creating user admin client: %v", err)
|
||||||
|
}
|
||||||
|
userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
|
||||||
|
|
||||||
|
distantExpiration := time.Now().Add(30 * time.Minute)
|
||||||
|
cr, err := userAdmClient.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
|
||||||
|
TargetUser: accessKey,
|
||||||
|
AccessKey: "svc-accesskey",
|
||||||
|
SecretKey: "svc-secretkey",
|
||||||
|
Expiration: &distantExpiration,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Unable to create svc acc: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.assertSvcAccS3Access(ctx, s, cr, bucket)
|
||||||
|
|
||||||
|
closeExpiration := time.Now().Add(2 * time.Hour)
|
||||||
|
_, err = userAdmClient.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
|
||||||
|
TargetUser: accessKey,
|
||||||
|
AccessKey: "svc-accesskey",
|
||||||
|
SecretKey: "svc-secretkey",
|
||||||
|
Expiration: &closeExpiration,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
c.Fatalf("Creating a svc acc with distant expiration should fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TestSuiteIAM) TestServiceAccountOpsByAdmin(c *check) {
|
func (s *TestSuiteIAM) TestServiceAccountOpsByAdmin(c *check) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
2
go.mod
2
go.mod
@ -54,7 +54,7 @@ require (
|
|||||||
github.com/minio/madmin-go/v3 v3.0.45
|
github.com/minio/madmin-go/v3 v3.0.45
|
||||||
github.com/minio/minio-go/v7 v7.0.66
|
github.com/minio/minio-go/v7 v7.0.66
|
||||||
github.com/minio/mux v1.9.0
|
github.com/minio/mux v1.9.0
|
||||||
github.com/minio/pkg/v2 v2.0.9-0.20240130164631-ac3f1be23e94
|
github.com/minio/pkg/v2 v2.0.9-0.20240209124402-7990a27fd79d
|
||||||
github.com/minio/selfupdate v0.6.0
|
github.com/minio/selfupdate v0.6.0
|
||||||
github.com/minio/sha256-simd v1.0.1
|
github.com/minio/sha256-simd v1.0.1
|
||||||
github.com/minio/simdjson-go v0.4.5
|
github.com/minio/simdjson-go v0.4.5
|
||||||
|
2
go.sum
2
go.sum
@ -456,6 +456,8 @@ github.com/minio/mux v1.9.0 h1:dWafQFyEfGhJvK6AwLOt83bIG5bxKxKJnKMCi0XAaoA=
|
|||||||
github.com/minio/mux v1.9.0/go.mod h1:1pAare17ZRL5GpmNL+9YmqHoWnLmMZF9C/ioUCfy0BQ=
|
github.com/minio/mux v1.9.0/go.mod h1:1pAare17ZRL5GpmNL+9YmqHoWnLmMZF9C/ioUCfy0BQ=
|
||||||
github.com/minio/pkg/v2 v2.0.9-0.20240130164631-ac3f1be23e94 h1:nBZkkmq9HPfs2ZUDcRqPahe2js1mvMUR/7cBfW17Jik=
|
github.com/minio/pkg/v2 v2.0.9-0.20240130164631-ac3f1be23e94 h1:nBZkkmq9HPfs2ZUDcRqPahe2js1mvMUR/7cBfW17Jik=
|
||||||
github.com/minio/pkg/v2 v2.0.9-0.20240130164631-ac3f1be23e94/go.mod h1:yayUTo82b0RK+e97hGb1naC787mOtUEyDs3SIcwSyHI=
|
github.com/minio/pkg/v2 v2.0.9-0.20240130164631-ac3f1be23e94/go.mod h1:yayUTo82b0RK+e97hGb1naC787mOtUEyDs3SIcwSyHI=
|
||||||
|
github.com/minio/pkg/v2 v2.0.9-0.20240209124402-7990a27fd79d h1:xGtyFgqwGy7Lc/i5udOKKeqsyRpQPlKQY2Pf4RiUDtk=
|
||||||
|
github.com/minio/pkg/v2 v2.0.9-0.20240209124402-7990a27fd79d/go.mod h1:yayUTo82b0RK+e97hGb1naC787mOtUEyDs3SIcwSyHI=
|
||||||
github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU=
|
github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU=
|
||||||
github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
|
github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
|
||||||
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||||
|
Loading…
Reference in New Issue
Block a user