Skip to content

Commit 732e0ef

Browse files
authored
ACL for mcs (#123)
This PR sets the initial version of the ACL for mcs, the idea behind this is to start using the principle of least privileges when assigning policies to users when creating users through mcs, currently mcsAdmin policy uses admin:* and s3:* and by default a user with that policy will have access to everything, if want to limit that we can create a policy with least privileges. We need to start validating explicitly if users has acccess to an specific endpoint based on IAM policy actions. In this first version every endpoint (you can see it as a page to), defines a set of well defined admin/s3 actions to work properly, ie: ``` // corresponds to /groups endpoint used by the groups page var groupsActionSet = iampolicy.NewActionSet( iampolicy.ListGroupsAdminAction, iampolicy.AddUserToGroupAdminAction, //iampolicy.GetGroupAdminAction, iampolicy.EnableGroupAdminAction, iampolicy.DisableGroupAdminAction, ) // corresponds to /policies endpoint used by the policies page var iamPoliciesActionSet = iampolicy.NewActionSet( iampolicy.GetPolicyAdminAction, iampolicy.DeletePolicyAdminAction, iampolicy.CreatePolicyAdminAction, iampolicy.AttachPolicyAdminAction, iampolicy.ListUserPoliciesAdminAction, ) ``` With that said, for this initial version, now the sessions endpoint will return a list of authorized pages to be render on the UI, on subsequent prs we will add this verification of authorization via a server middleware.
1 parent e8491d8 commit 732e0ef

23 files changed

+1080
-335
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ assets:
2222

2323
test:
2424
@(go test -race -v github.com/minio/mcs/restapi/...)
25-
@(go test -race -v github.com/minio/mcs/pkg/auth/...)
25+
@(go test -race -v github.com/minio/mcs/pkg/...)
2626

2727
coverage:
2828
@(go test -v -coverprofile=coverage.out github.com/minio/mcs/restapi/... && go tool cover -html=coverage.out && open coverage.html)

README.md

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,30 @@ $ mc admin user add myminio mcs YOURMCSSECRET
1414
$ set -o history
1515
```
1616

17-
2. Create a policy for `mcs`
17+
2. Create a policy for `mcs` with access to everything (for testing and debugging)
1818

1919
```
2020
$ cat > mcsAdmin.json << EOF
2121
{
22-
"Version": "2012-10-17",
23-
"Statement": [
24-
{
25-
"Action": [
26-
"admin:*"
27-
],
28-
"Effect": "Allow",
29-
"Sid": ""
30-
},
31-
{
32-
"Action": [
33-
"s3:*"
34-
],
35-
"Effect": "Allow",
36-
"Resource": [
37-
"arn:aws:s3:::*"
38-
],
39-
"Sid": ""
40-
}
41-
]
22+
"Version": "2012-10-17",
23+
"Statement": [{
24+
"Action": [
25+
"admin:*"
26+
],
27+
"Effect": "Allow",
28+
"Sid": ""
29+
},
30+
{
31+
"Action": [
32+
"s3:*"
33+
],
34+
"Effect": "Allow",
35+
"Resource": [
36+
"arn:aws:s3:::*"
37+
],
38+
"Sid": ""
39+
}
40+
]
4241
}
4342
EOF
4443
$ mc admin policy add myminio mcsAdmin mcsAdmin.json
@@ -50,6 +49,49 @@ $ mc admin policy add myminio mcsAdmin mcsAdmin.json
5049
$ mc admin policy set myminio mcsAdmin user=mcs
5150
```
5251

52+
53+
### Note
54+
Additionally, you can create policies to limit the privileges for `mcs` users, for example, if you want the user to only have access to dashboard, buckets, notifications and watch page, the policy should look like this:
55+
```
56+
{
57+
"Version": "2012-10-17",
58+
"Statement": [{
59+
"Action": [
60+
"admin:ServerInfo",
61+
],
62+
"Effect": "Allow",
63+
"Sid": ""
64+
},
65+
{
66+
"Action": [
67+
"s3:ListenBucketNotification",
68+
"s3:PutBucketNotification",
69+
"s3:GetBucketNotification",
70+
"s3:ListMultipartUploadParts",
71+
"s3:ListBucketMultipartUploads",
72+
"s3:ListBucket",
73+
"s3:HeadBucket",
74+
"s3:GetObject",
75+
"s3:GetBucketLocation",
76+
"s3:AbortMultipartUpload",
77+
"s3:CreateBucket",
78+
"s3:PutObject",
79+
"s3:DeleteObject",
80+
"s3:DeleteBucket",
81+
"s3:PutBucketPolicy",
82+
"s3:DeleteBucketPolicy",
83+
"s3:GetBucketPolicy"
84+
],
85+
"Effect": "Allow",
86+
"Resource": [
87+
"arn:aws:s3:::*"
88+
],
89+
"Sid": ""
90+
}
91+
]
92+
}
93+
```
94+
5395
## Run MCS server
5496
To run the server:
5597

models/session_response.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/acl/endpoints.go

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package acl
18+
19+
import iampolicy "github.com/minio/minio/pkg/iam/policy"
20+
21+
// endpoints definition
22+
var (
23+
configuration = "/configurations-list"
24+
users = "/users"
25+
groups = "/groups"
26+
iamPolicies = "/policies"
27+
dashboard = "/dashboard"
28+
profiling = "/profiling"
29+
trace = "/trace"
30+
logs = "/logs"
31+
watch = "/watch"
32+
notifications = "/notification-endpoints"
33+
buckets = "/buckets"
34+
)
35+
36+
type ConfigurationActionSet struct {
37+
actionTypes iampolicy.ActionSet
38+
actions iampolicy.ActionSet
39+
}
40+
41+
// configurationActionSet contains the list of admin actions required for this endpoint to work
42+
var configurationActionSet = ConfigurationActionSet{
43+
actionTypes: iampolicy.NewActionSet(
44+
iampolicy.AllAdminActions,
45+
),
46+
actions: iampolicy.NewActionSet(
47+
iampolicy.ConfigUpdateAdminAction,
48+
),
49+
}
50+
51+
// logsActionSet contains the list of admin actions required for this endpoint to work
52+
var logsActionSet = ConfigurationActionSet{
53+
actionTypes: iampolicy.NewActionSet(
54+
iampolicy.AllAdminActions,
55+
),
56+
actions: iampolicy.NewActionSet(
57+
iampolicy.ConsoleLogAdminAction,
58+
),
59+
}
60+
61+
// dashboardActionSet contains the list of admin actions required for this endpoint to work
62+
var dashboardActionSet = ConfigurationActionSet{
63+
actionTypes: iampolicy.NewActionSet(
64+
iampolicy.AllAdminActions,
65+
),
66+
actions: iampolicy.NewActionSet(
67+
iampolicy.ServerInfoAdminAction,
68+
),
69+
}
70+
71+
// groupsActionSet contains the list of admin actions required for this endpoint to work
72+
var groupsActionSet = ConfigurationActionSet{
73+
actionTypes: iampolicy.NewActionSet(
74+
iampolicy.AllAdminActions,
75+
),
76+
actions: iampolicy.NewActionSet(
77+
iampolicy.ListGroupsAdminAction,
78+
iampolicy.AddUserToGroupAdminAction,
79+
//iampolicy.GetGroupAdminAction,
80+
iampolicy.EnableGroupAdminAction,
81+
iampolicy.DisableGroupAdminAction,
82+
),
83+
}
84+
85+
// iamPoliciesActionSet contains the list of admin actions required for this endpoint to work
86+
var iamPoliciesActionSet = ConfigurationActionSet{
87+
actionTypes: iampolicy.NewActionSet(
88+
iampolicy.AllAdminActions,
89+
),
90+
actions: iampolicy.NewActionSet(
91+
iampolicy.GetPolicyAdminAction,
92+
iampolicy.DeletePolicyAdminAction,
93+
iampolicy.CreatePolicyAdminAction,
94+
iampolicy.AttachPolicyAdminAction,
95+
iampolicy.ListUserPoliciesAdminAction,
96+
),
97+
}
98+
99+
// profilingActionSet contains the list of admin actions required for this endpoint to work
100+
var profilingActionSet = ConfigurationActionSet{
101+
actionTypes: iampolicy.NewActionSet(
102+
iampolicy.AllAdminActions,
103+
),
104+
actions: iampolicy.NewActionSet(
105+
iampolicy.ProfilingAdminAction,
106+
),
107+
}
108+
109+
// traceActionSet contains the list of admin actions required for this endpoint to work
110+
var traceActionSet = ConfigurationActionSet{
111+
actionTypes: iampolicy.NewActionSet(
112+
iampolicy.AllAdminActions,
113+
),
114+
actions: iampolicy.NewActionSet(
115+
iampolicy.TraceAdminAction,
116+
),
117+
}
118+
119+
// usersActionSet contains the list of admin actions required for this endpoint to work
120+
var usersActionSet = ConfigurationActionSet{
121+
actionTypes: iampolicy.NewActionSet(
122+
iampolicy.AllAdminActions,
123+
),
124+
actions: iampolicy.NewActionSet(
125+
iampolicy.ListUsersAdminAction,
126+
iampolicy.CreateUserAdminAction,
127+
iampolicy.DeleteUserAdminAction,
128+
iampolicy.GetUserAdminAction,
129+
iampolicy.EnableUserAdminAction,
130+
iampolicy.DisableUserAdminAction,
131+
),
132+
}
133+
134+
// watchActionSet contains the list of admin actions required for this endpoint to work
135+
var watchActionSet = ConfigurationActionSet{
136+
actionTypes: iampolicy.NewActionSet(
137+
iampolicy.AllAdminActions,
138+
),
139+
actions: iampolicy.NewActionSet(
140+
iampolicy.ListenBucketNotificationAction,
141+
),
142+
}
143+
144+
// notificationsActionSet contains the list of admin actions required for this endpoint to work
145+
var notificationsActionSet = ConfigurationActionSet{
146+
actionTypes: iampolicy.NewActionSet(
147+
iampolicy.AllActions,
148+
),
149+
actions: iampolicy.NewActionSet(
150+
iampolicy.ListenBucketNotificationAction,
151+
iampolicy.PutBucketNotificationAction,
152+
iampolicy.GetBucketNotificationAction,
153+
),
154+
}
155+
156+
// bucketsActionSet contains the list of admin actions required for this endpoint to work
157+
var bucketsActionSet = ConfigurationActionSet{
158+
actionTypes: iampolicy.NewActionSet(
159+
iampolicy.AllActions,
160+
),
161+
actions: iampolicy.NewActionSet(
162+
// Read access to buckets
163+
iampolicy.ListMultipartUploadPartsAction,
164+
iampolicy.ListBucketMultipartUploadsAction,
165+
iampolicy.ListBucketAction,
166+
iampolicy.HeadBucketAction,
167+
iampolicy.GetObjectAction,
168+
iampolicy.GetBucketLocationAction,
169+
// Write access to buckets
170+
iampolicy.AbortMultipartUploadAction,
171+
iampolicy.CreateBucketAction,
172+
iampolicy.PutObjectAction,
173+
iampolicy.DeleteObjectAction,
174+
iampolicy.DeleteBucketAction,
175+
// Assign bucket policies
176+
iampolicy.PutBucketPolicyAction,
177+
iampolicy.DeleteBucketPolicyAction,
178+
iampolicy.GetBucketPolicyAction,
179+
),
180+
}
181+
182+
// endpointRules contains the mapping between endpoints and ActionSets, additional rules can be added here
183+
var endpointRules = map[string]ConfigurationActionSet{
184+
configuration: configurationActionSet,
185+
users: usersActionSet,
186+
groups: groupsActionSet,
187+
iamPolicies: iamPoliciesActionSet,
188+
dashboard: dashboardActionSet,
189+
profiling: profilingActionSet,
190+
trace: traceActionSet,
191+
logs: logsActionSet,
192+
watch: watchActionSet,
193+
notifications: notificationsActionSet,
194+
buckets: bucketsActionSet,
195+
}
196+
197+
// GetActionsStringFromPolicy extract the admin/s3 actions from a given policy and return them in []string format
198+
//
199+
// ie:
200+
// {
201+
// "Version": "2012-10-17",
202+
// "Statement": [{
203+
// "Action": [
204+
// "admin:ServerInfo",
205+
// "admin:CreatePolicy",
206+
// "admin:GetUser"
207+
// ],
208+
// ...
209+
// },
210+
// {
211+
// "Action": [
212+
// "s3:ListenBucketNotification",
213+
// "s3:PutBucketNotification"
214+
// ],
215+
// ...
216+
// }
217+
// ]
218+
// }
219+
// Will produce an array like: ["admin:ServerInfo", "admin:CreatePolicy", "admin:GetUser", "s3:ListenBucketNotification", "s3:PutBucketNotification"]\
220+
func GetActionsStringFromPolicy(policy *iampolicy.Policy) []string {
221+
var actions []string
222+
for _, statement := range policy.Statements {
223+
// We only care about allowed actions
224+
if statement.Effect.IsAllowed(true) {
225+
for _, action := range statement.Actions.ToSlice() {
226+
actions = append(actions, string(action))
227+
}
228+
}
229+
}
230+
return actions
231+
}
232+
233+
// actionsStringToActionSet convert a given string array to iampolicy.ActionSet structure
234+
// this avoids ending with duplicate actions
235+
func actionsStringToActionSet(actions []string) iampolicy.ActionSet {
236+
actionsSet := iampolicy.ActionSet{}
237+
for _, action := range actions {
238+
actionsSet.Add(iampolicy.Action(action))
239+
}
240+
return actionsSet
241+
}
242+
243+
// GetAuthorizedEndpoints return a list of allowed endpoint based on a provided *iampolicy.Policy
244+
// ie: pages the user should have access based on his current privileges
245+
func GetAuthorizedEndpoints(actions []string) []string {
246+
if len(actions) == 0 {
247+
return []string{}
248+
}
249+
// Prepare new ActionSet structure that will hold all the user actions
250+
userAllowedAction := actionsStringToActionSet(actions)
251+
allowedEndpoints := []string{}
252+
for endpoint, rules := range endpointRules {
253+
// check if user policy matches s3:* or admin:* typesIntersection
254+
endpointActionTypes := rules.actionTypes
255+
typesIntersection := endpointActionTypes.Intersection(userAllowedAction)
256+
if len(typesIntersection) == len(endpointActionTypes.ToSlice()) {
257+
allowedEndpoints = append(allowedEndpoints, endpoint)
258+
continue
259+
}
260+
// check if user policy matches explicitly defined endpoint required actions
261+
endpointRequiredActions := rules.actions
262+
actionsIntersection := endpointRequiredActions.Intersection(userAllowedAction)
263+
if len(actionsIntersection) == len(endpointRequiredActions.ToSlice()) {
264+
allowedEndpoints = append(allowedEndpoints, endpoint)
265+
}
266+
}
267+
return allowedEndpoints
268+
}

0 commit comments

Comments
 (0)