From 813285beb03ccfdc1e77b82f2e4fb67ffb8fc06d Mon Sep 17 00:00:00 2001 From: rzrbld Date: Fri, 27 Mar 2020 01:10:49 +0300 Subject: [PATCH 1/4] fix security if oauth is enabled --- src/handlers/auth.go | 4 +- src/handlers/buckets.go | 113 +++++++++++++++++++++++---------------- src/handlers/groups.go | 33 ++++++++---- src/handlers/policy.go | 30 +++++++---- src/handlers/probes.go | 9 +--- src/handlers/users.go | 83 +++++++++++++++++----------- src/main.go | 4 +- src/oauth/oauth.go | 6 ++- src/response/response.go | 28 +++++++--- 9 files changed, 195 insertions(+), 115 deletions(-) diff --git a/src/handlers/auth.go b/src/handlers/auth.go index 0469ba4..c401775 100644 --- a/src/handlers/auth.go +++ b/src/handlers/auth.go @@ -14,7 +14,7 @@ var AuthLogout = func(ctx iris.Context) { var AuthRoot = func(ctx iris.Context) { // try to get the user without re-authenticating if gothUser, err := auth.CompleteUserAuth(ctx); err == nil { - ctx.ViewData("", gothUser) + auth.Redirect(ctx) ctx.JSON(iris.Map{"name": gothUser.UserID, "auth": true, "oauth": cnf.OauthEnable}) } else { auth.BeginAuthHandler(ctx) @@ -37,5 +37,5 @@ var AuthCallback = func(ctx iris.Context) { ctx.Writef("%v", err) return } - auth.RedirectOnCallback(ctx) + auth.Redirect(ctx) } diff --git a/src/handlers/buckets.go b/src/handlers/buckets.go index 8f07e3e..a12fa73 100644 --- a/src/handlers/buckets.go +++ b/src/handlers/buckets.go @@ -36,21 +36,30 @@ var BuckListExtended = func(ctx iris.Context) { var BuckMake = func(ctx iris.Context) { var newBucket = ctx.FormValue("newBucket") var newBucketRegion = ctx.FormValue("newBucketRegion") + if newBucketRegion == "" { newBucketRegion = cnf.Region } - err := minioClnt.MakeBucket(newBucket, newBucketRegion) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err := minioClnt.MakeBucket(newBucket, newBucketRegion) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var BuckDelete = func(ctx iris.Context) { var bucketName = ctx.FormValue("bucketName") - err := minioClnt.RemoveBucket(bucketName) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err := minioClnt.RemoveBucket(bucketName) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var BuckGetLifecycle = func(ctx iris.Context) { @@ -65,15 +74,19 @@ var BuckSetLifecycle = func(ctx iris.Context) { var bucketName = ctx.FormValue("bucketName") var lifecycle = ctx.FormValue("lifecycle") - err := minioClnt.SetBucketLifecycle(bucketName, lifecycle) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err := minioClnt.SetBucketLifecycle(bucketName, lifecycle) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var BuckGetEvents = func(ctx iris.Context) { var bucket = ctx.FormValue("bucket") - bn, err := minioClnt.GetBucketNotification(bucket) + bn, err := minioClnt.GetBucketNotification(bucket) var res = resph.BodyResHandler(ctx, err, bn) ctx.JSON(res) } @@ -88,50 +101,58 @@ var BuckSetEvents = func(ctx iris.Context) { var filterPrefix = ctx.FormValue("filterPrefix") var filterSuffix = ctx.FormValue("filterSuffix") - bucketNotify, err := minioClnt.GetBucketNotification(bucket) - - var newNotification = minio.NewNotificationConfig(stsARN) - for _, event := range eventTypes { - switch event { - case "put": - newNotification.AddEvents(minio.ObjectCreatedAll) - case "delete": - newNotification.AddEvents(minio.ObjectRemovedAll) - case "get": - newNotification.AddEvents(minio.ObjectAccessedAll) + if resph.CheckAuthBeforeRequest(ctx) != false { + bucketNotify, err := minioClnt.GetBucketNotification(bucket) + + var newNotification = minio.NewNotificationConfig(stsARN) + for _, event := range eventTypes { + switch event { + case "put": + newNotification.AddEvents(minio.ObjectCreatedAll) + case "delete": + newNotification.AddEvents(minio.ObjectRemovedAll) + case "get": + newNotification.AddEvents(minio.ObjectAccessedAll) + } } - } - if filterPrefix != "" { - newNotification.AddFilterPrefix(filterPrefix) - } - if filterSuffix != "" { - newNotification.AddFilterSuffix(filterSuffix) - } - - switch arrARN[2] { - case "sns": - if bucketNotify.AddTopic(newNotification) { - err = fmt.Errorf("Overlapping Topic configs") + if filterPrefix != "" { + newNotification.AddFilterPrefix(filterPrefix) } - case "sqs": - if bucketNotify.AddQueue(newNotification) { - err = fmt.Errorf("Overlapping Queue configs") + if filterSuffix != "" { + newNotification.AddFilterSuffix(filterSuffix) } - case "lambda": - if bucketNotify.AddLambda(newNotification) { - err = fmt.Errorf("Overlapping lambda configs") + + switch arrARN[2] { + case "sns": + if bucketNotify.AddTopic(newNotification) { + err = fmt.Errorf("Overlapping Topic configs") + } + case "sqs": + if bucketNotify.AddQueue(newNotification) { + err = fmt.Errorf("Overlapping Queue configs") + } + case "lambda": + if bucketNotify.AddLambda(newNotification) { + err = fmt.Errorf("Overlapping lambda configs") + } } - } - err = minioClnt.SetBucketNotification(bucket, bucketNotify) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + err = minioClnt.SetBucketNotification(bucket, bucketNotify) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var BuckRemoveEvents = func(ctx iris.Context) { var bucket = ctx.FormValue("bucket") - err := minioClnt.RemoveAllBucketNotification(bucket) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err := minioClnt.RemoveAllBucketNotification(bucket) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } diff --git a/src/handlers/groups.go b/src/handlers/groups.go index 6b0c1af..f3a6216 100644 --- a/src/handlers/groups.go +++ b/src/handlers/groups.go @@ -11,19 +11,27 @@ import ( var GrSetStatus = func(ctx iris.Context) { var group = ctx.FormValue("group") - var status = madmin.GroupStatus(ctx.FormValue("status")) - err = madmClnt.SetGroupStatus(group, status) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + var status = madmin.GroupStatus(group) + err = madmClnt.SetGroupStatus(group, status) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var GrSetDescription = func(ctx iris.Context) { var group = ctx.FormValue("group") - grp, err := madmClnt.GetGroupDescription(group) - var res = resph.BodyResHandler(ctx, err, grp) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + grp, err := madmClnt.GetGroupDescription(group) + var res = resph.BodyResHandler(ctx, err, grp) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var GrUpdateMembers = func(ctx iris.Context) { @@ -37,9 +45,14 @@ var GrUpdateMembers = func(ctx iris.Context) { ctx.JSON(iris.Map{"error": err.Error()}) } - err = madmClnt.UpdateGroupMembers(gar) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err = madmClnt.UpdateGroupMembers(gar) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } + } var GrList = func(ctx iris.Context) { diff --git a/src/handlers/policy.go b/src/handlers/policy.go index 4bb2b40..02fc7bb 100644 --- a/src/handlers/policy.go +++ b/src/handlers/policy.go @@ -18,18 +18,26 @@ var PolAdd = func(ctx iris.Context) { p.policyName = ctx.FormValue("policyName") p.policyString = ctx.FormValue("policyString") - err = madmClnt.AddCannedPolicy(p.policyName, p.policyString) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err = madmClnt.AddCannedPolicy(p.policyName, p.policyString) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var PolDelete = func(ctx iris.Context) { p := policySet{} p.policyName = ctx.FormValue("policyName") - err = madmClnt.RemoveCannedPolicy(p.policyName) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err = madmClnt.RemoveCannedPolicy(p.policyName) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var PolSet = func(ctx iris.Context) { @@ -45,7 +53,11 @@ var PolSet = func(ctx iris.Context) { ctx.JSON(iris.Map{"error": err.Error()}) } - err = madmClnt.SetPolicy(p.policyName, p.entityName, isGroupBool) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err = madmClnt.SetPolicy(p.policyName, p.entityName, isGroupBool) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } diff --git a/src/handlers/probes.go b/src/handlers/probes.go index d3ee024..19276e6 100644 --- a/src/handlers/probes.go +++ b/src/handlers/probes.go @@ -5,12 +5,7 @@ import ( resph "github.com/rzrbld/adminio-api/response" ) -var Readiness = func(ctx iris.Context) { - var res = resph.DefaultResConstructor(ctx, nil) - ctx.JSON(res) -} - -var Liveness = func(ctx iris.Context) { - var res = resph.DefaultResConstructor(ctx, nil) +var Probes = func(ctx iris.Context) { + var res = resph.DefaultResConstructor(nil) ctx.JSON(res) } diff --git a/src/handlers/users.go b/src/handlers/users.go index 8fe408e..73b0a6a 100644 --- a/src/handlers/users.go +++ b/src/handlers/users.go @@ -18,18 +18,26 @@ var UsrSetStats = func(ctx iris.Context) { us.accessKey = ctx.FormValue("accessKey") us.status = madmin.AccountStatus(ctx.FormValue("status")) - err = madmClnt.SetUserStatus(us.accessKey, us.status) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err = madmClnt.SetUserStatus(us.accessKey, us.status) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var UsrDelete = func(ctx iris.Context) { user := User{} user.accessKey = ctx.FormValue("accessKey") - err = madmClnt.RemoveUser(user.accessKey) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err = madmClnt.RemoveUser(user.accessKey) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var UsrAdd = func(ctx iris.Context) { @@ -37,9 +45,13 @@ var UsrAdd = func(ctx iris.Context) { user.accessKey = ctx.FormValue("accessKey") user.secretKey = ctx.FormValue("secretKey") - err = madmClnt.AddUser(user.accessKey, user.secretKey) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + if resph.CheckAuthBeforeRequest(ctx) != false { + err = madmClnt.AddUser(user.accessKey, user.secretKey) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + ctx.JSON(resph.DefaultAuthError()) + } } var UsrCreateExtended = func(ctx iris.Context) { @@ -51,14 +63,18 @@ var UsrCreateExtended = func(ctx iris.Context) { u.accessKey = ctx.FormValue("accessKey") u.secretKey = ctx.FormValue("secretKey") - err = madmClnt.AddUser(u.accessKey, u.secretKey) - if err != nil { - log.Print(err) - ctx.JSON(iris.Map{"error": err.Error()}) + if resph.CheckAuthBeforeRequest(ctx) != false { + err = madmClnt.AddUser(u.accessKey, u.secretKey) + if err != nil { + log.Print(err) + ctx.JSON(iris.Map{"error": err.Error()}) + } else { + err = madmClnt.SetPolicy(p.policyName, p.entityName, false) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } } else { - err = madmClnt.SetPolicy(p.policyName, p.entityName, false) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + ctx.JSON(resph.DefaultAuthError()) } } @@ -71,22 +87,27 @@ var UsrSet = func(ctx iris.Context) { u.secretKey = ctx.FormValue("secretKey") us.status = madmin.AccountStatus(ctx.FormValue("status")) p.policyName = ctx.FormValue("policyName") - if u.secretKey == "" { - err = madmClnt.SetUserStatus(u.accessKey, us.status) - } else { - err = madmClnt.SetUser(u.accessKey, u.secretKey, us.status) - } - if err != nil { - log.Print(err) - ctx.JSON(iris.Map{"error": err.Error()}) - } else { - if p.policyName == "" { - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + + if resph.CheckAuthBeforeRequest(ctx) != false { + if u.secretKey == "" { + err = madmClnt.SetUserStatus(u.accessKey, us.status) } else { - err = madmClnt.SetPolicy(p.policyName, u.accessKey, false) - var res = resph.DefaultResHandler(ctx, err) - ctx.JSON(res) + err = madmClnt.SetUser(u.accessKey, u.secretKey, us.status) + } + if err != nil { + log.Print(err) + ctx.JSON(iris.Map{"error": err.Error()}) + } else { + if p.policyName == "" { + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } else { + err = madmClnt.SetPolicy(p.policyName, u.accessKey, false) + var res = resph.DefaultResHandler(ctx, err) + ctx.JSON(res) + } } + } else { + ctx.JSON(resph.DefaultAuthError()) } } diff --git a/src/main.go b/src/main.go index ed84b02..507e911 100644 --- a/src/main.go +++ b/src/main.go @@ -66,8 +66,8 @@ func main() { } if cnf.ProbesEnable { - app.Get("/ready", hdl.Readiness) - app.Get("/live", hdl.Liveness) + app.Get("/ready", hdl.Probes) + app.Get("/live", hdl.Probes) } v1auth := app.Party("/auth/", crs).AllowMethods(iris.MethodOptions) diff --git a/src/oauth/oauth.go b/src/oauth/oauth.go index 4820e06..705f7b9 100644 --- a/src/oauth/oauth.go +++ b/src/oauth/oauth.go @@ -126,7 +126,9 @@ func Logout(ctx iris.Context) error { return nil } -func RedirectOnCallback(ctx iris.Context) { +func Redirect(ctx iris.Context) { url := GetState(ctx) - ctx.Redirect(url, iris.StatusTemporaryRedirect) + if url != "" && url != "state" { + ctx.Redirect(url, iris.StatusTemporaryRedirect) + } } diff --git a/src/response/response.go b/src/response/response.go index cc41bb5..ad60f4d 100644 --- a/src/response/response.go +++ b/src/response/response.go @@ -12,32 +12,44 @@ func DefaultResHandler(ctx iris.Context, err error) iris.Map { if cnf.OauthEnable { if gothUser, err := auth.CompleteUserAuth(ctx); err == nil { audit.DefaultAuditLog(gothUser, ctx) - return DefaultResConstructor(ctx, err) + return DefaultResConstructor(err) } else { return iris.Map{"auth": false, "oauth": cnf.OauthEnable} } } else { - return DefaultResConstructor(ctx, err) + return DefaultResConstructor(err) } return nil } +func CheckAuthBeforeRequest(ctx iris.Context) bool { + if cnf.OauthEnable { + if _, err := auth.CompleteUserAuth(ctx); err == nil { + return true + } else { + return false + } + } else { + return true + } +} + func BodyResHandler(ctx iris.Context, err error, body interface{}) interface{} { if cnf.OauthEnable { if gothUser, err := auth.CompleteUserAuth(ctx); err == nil { audit.DefaultAuditLog(gothUser, ctx) - return BodyResConstructor(ctx, err, body) + return BodyResConstructor(err, body) } else { return iris.Map{"auth": false, "oauth": cnf.OauthEnable} } } else { - return BodyResConstructor(ctx, err, body) + return BodyResConstructor(err, body) } return nil } -func BodyResConstructor(ctx iris.Context, err error, body interface{}) interface{} { +func BodyResConstructor(err error, body interface{}) interface{} { var resp interface{} if err != nil { log.Print(err) @@ -48,7 +60,7 @@ func BodyResConstructor(ctx iris.Context, err error, body interface{}) interface return resp } -func DefaultResConstructor(ctx iris.Context, err error) iris.Map { +func DefaultResConstructor(err error) iris.Map { var resp iris.Map if err != nil { log.Print(err) @@ -58,3 +70,7 @@ func DefaultResConstructor(ctx iris.Context, err error) iris.Map { } return resp } + +func DefaultAuthError() interface{} { + return iris.Map{"auth": false, "oauth": cnf.OauthEnable} +} From 019b2eaa822733ea8c61787c5c7e37f377851862 Mon Sep 17 00:00:00 2001 From: rzrbld Date: Fri, 27 Mar 2020 01:13:00 +0300 Subject: [PATCH 2/4] fix documentation --- README.md | 2 +- examples/{policy.xml => lifecycle.xml} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename examples/{policy.xml => lifecycle.xml} (100%) diff --git a/README.md b/README.md index e65f492..fdcf514 100644 --- a/README.md +++ b/README.md @@ -77,4 +77,4 @@ docker run -d \ ### example config - prometheus config for adminio metrics: `examples/prometheus.yml` - - bucket policy: `examples/policy.xml` + - bucket lifecycle: `examples/lifecycle.xml` diff --git a/examples/policy.xml b/examples/lifecycle.xml similarity index 100% rename from examples/policy.xml rename to examples/lifecycle.xml From fcfdfa86e58214e2196ad40ad6845aaeb88387fc Mon Sep 17 00:00:00 2001 From: rzrbld Date: Fri, 27 Mar 2020 01:14:07 +0300 Subject: [PATCH 3/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fdcf514..269272f 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ docker run -d \ | `ADMINIO_METRICS_ENABLE` | enable default iris\golang metrics and bucket sizes metric on /metric/ uri path | false | | `ADMINIO_PROBES_ENABLE` | enable liveness and readiness probes for k8s installations | false | -### Supported oauth providers +### List of supported oauth providers - amazon - auth0 From 500b0ecfb08ccf6d4f338e3581d57e839e51d943 Mon Sep 17 00:00:00 2001 From: rzrbld Date: Fri, 27 Mar 2020 01:25:59 +0300 Subject: [PATCH 4/4] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 269272f..dbed542 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Here is a Web UI for this API - [adminio-ui](https://github.com/rzrbld/adminio-u ### Run full stack demo obtain [docker-compose.yml](https://raw.githubusercontent.com/rzrbld/adminio-ui/master/docker-compose.yml) from [adminio-ui](https://github.com/rzrbld/adminio-ui) repository. And run it: -`` docker-compose -f docker-compose.yml up `` +`docker-compose -f docker-compose.yml up` it will bring up: @@ -17,7 +17,7 @@ it will bring up: - adminio API on 8080 port - adminio UI on 80 port -after that you can go to `` http://localhost `` and try out +after that you can go to `http://localhost` and try out ### Run with docker ``` @@ -34,7 +34,7 @@ docker run -d \ ### Run manually - [start](https://docs.min.io/) minio server - set env variables - - compile and run ./main form `src` folder + - go to `src` folder and compile with `go build main.go`, then run `./main` binary ### Config Env variables | Variable | Description | Default | @@ -56,8 +56,8 @@ docker run -d \ | `ADMINIO_COOKIE_BLOCK_KEY` | block key for session cookies. AES only supports key sizes of 16, 24 or 32 bytes | bnfYuphzxPhJMR823YNezH83fuHuddFC | | `ADMINIO_COOKIE_NAME` | name for the session cookie | adminiosessionid | | `ADMINIO_AUDIT_LOG_ENABLE` | enable audit log, mae sense if oauth is enabled, othervise set to false | false | -| `ADMINIO_METRICS_ENABLE` | enable default iris\golang metrics and bucket sizes metric on /metric/ uri path | false | -| `ADMINIO_PROBES_ENABLE` | enable liveness and readiness probes for k8s installations | false | +| `ADMINIO_METRICS_ENABLE` | enable default iris/golang metrics and bucket sizes metric on `/metric/` uri path | false | +| `ADMINIO_PROBES_ENABLE` | enable liveness and readiness probes for k8s at `/ready/` and `/live/` uri path installations | false | ### List of supported oauth providers