| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,115 @@ |
| 0 |
+package authorizer |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "path" |
|
| 4 |
+ "strings" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 7 |
+ |
|
| 8 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+type DefaultAuthorizationAttributes struct {
|
|
| 12 |
+ Verb string |
|
| 13 |
+ Resource string |
|
| 14 |
+ ResourceName string |
|
| 15 |
+ RequestAttributes interface{}
|
|
| 16 |
+ NonResourceURL bool |
|
| 17 |
+ URL string |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func (a DefaultAuthorizationAttributes) RuleMatches(rule authorizationapi.PolicyRule) (bool, error) {
|
|
| 21 |
+ if a.IsNonResourceURL() {
|
|
| 22 |
+ if a.nonResourceMatches(rule) {
|
|
| 23 |
+ if a.verbMatches(rule.Verbs) {
|
|
| 24 |
+ return true, nil |
|
| 25 |
+ } |
|
| 26 |
+ } |
|
| 27 |
+ |
|
| 28 |
+ return false, nil |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ if a.verbMatches(rule.Verbs) {
|
|
| 32 |
+ allowedResourceTypes := authorizationapi.ExpandResources(rule.Resources) |
|
| 33 |
+ |
|
| 34 |
+ if a.resourceMatches(allowedResourceTypes) {
|
|
| 35 |
+ if a.nameMatches(rule.ResourceNames) {
|
|
| 36 |
+ return true, nil |
|
| 37 |
+ } |
|
| 38 |
+ } |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ return false, nil |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+func (a DefaultAuthorizationAttributes) verbMatches(verbs util.StringSet) bool {
|
|
| 45 |
+ return verbs.Has(authorizationapi.VerbAll) || verbs.Has(strings.ToLower(a.GetVerb())) |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+func (a DefaultAuthorizationAttributes) resourceMatches(allowedResourceTypes util.StringSet) bool {
|
|
| 49 |
+ return allowedResourceTypes.Has(authorizationapi.ResourceAll) || allowedResourceTypes.Has(strings.ToLower(a.GetResource())) |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// nameMatches checks to see if the resourceName of the action is in a the specified whitelist. An empty whitelist indicates that any name is allowed. |
|
| 53 |
+// An empty string in the whitelist should only match the action's resourceName if the resourceName itself is empty string. This behavior allows for the |
|
| 54 |
+// combination of a whitelist for gets in the same rule as a list that won't have a resourceName. I don't recommend writing such a rule, but we do |
|
| 55 |
+// handle it like you'd expect: white list is respected for gets while not preventing the list you explicitly asked for. |
|
| 56 |
+func (a DefaultAuthorizationAttributes) nameMatches(allowedResourceNames util.StringSet) bool {
|
|
| 57 |
+ if len(allowedResourceNames) == 0 {
|
|
| 58 |
+ return true |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ return allowedResourceNames.Has(a.GetResourceName()) |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+func (a DefaultAuthorizationAttributes) GetVerb() string {
|
|
| 65 |
+ return a.Verb |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// nonResourceMatches take the remainer of a URL and attempts to match it against a series of explicitly allowed steps that can end in a wildcard |
|
| 69 |
+func (a DefaultAuthorizationAttributes) nonResourceMatches(rule authorizationapi.PolicyRule) bool {
|
|
| 70 |
+ for allowedNonResourcePath := range rule.NonResourceURLs {
|
|
| 71 |
+ // if the allowed resource path ends in a wildcard, check to see if the URL starts with it |
|
| 72 |
+ if strings.HasSuffix(allowedNonResourcePath, "*") {
|
|
| 73 |
+ if strings.HasPrefix(a.GetURL(), allowedNonResourcePath[0:len(allowedNonResourcePath)-1]) {
|
|
| 74 |
+ return true |
|
| 75 |
+ } |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ // if we have an exact match, return true |
|
| 79 |
+ if a.GetURL() == allowedNonResourcePath {
|
|
| 80 |
+ return true |
|
| 81 |
+ } |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ return false |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+// splitPath returns the segments for a URL path. |
|
| 88 |
+func splitPath(thePath string) []string {
|
|
| 89 |
+ thePath = strings.Trim(path.Clean(thePath), "/") |
|
| 90 |
+ if thePath == "" {
|
|
| 91 |
+ return []string{}
|
|
| 92 |
+ } |
|
| 93 |
+ return strings.Split(thePath, "/") |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+func (a DefaultAuthorizationAttributes) GetResource() string {
|
|
| 97 |
+ return a.Resource |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func (a DefaultAuthorizationAttributes) GetResourceName() string {
|
|
| 101 |
+ return a.ResourceName |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+func (a DefaultAuthorizationAttributes) GetRequestAttributes() interface{} {
|
|
| 105 |
+ return a.RequestAttributes |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+func (a DefaultAuthorizationAttributes) IsNonResourceURL() bool {
|
|
| 109 |
+ return a.NonResourceURL |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+func (a DefaultAuthorizationAttributes) GetURL() string {
|
|
| 113 |
+ return a.URL |
|
| 114 |
+} |
| 0 | 115 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,53 @@ |
| 0 |
+package authorizer |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net/http" |
|
| 4 |
+ "strings" |
|
| 5 |
+ |
|
| 6 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 7 |
+ kapiserver "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type openshiftAuthorizationAttributeBuilder struct {
|
|
| 11 |
+ contextMapper kapi.RequestContextMapper |
|
| 12 |
+ infoResolver *kapiserver.APIRequestInfoResolver |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+func NewAuthorizationAttributeBuilder(contextMapper kapi.RequestContextMapper, infoResolver *kapiserver.APIRequestInfoResolver) AuthorizationAttributeBuilder {
|
|
| 16 |
+ return &openshiftAuthorizationAttributeBuilder{contextMapper, infoResolver}
|
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+func (a *openshiftAuthorizationAttributeBuilder) GetAttributes(req *http.Request) (AuthorizationAttributes, error) {
|
|
| 20 |
+ // any url that starts with an API prefix and is more than one step long is considered to be a resource URL. |
|
| 21 |
+ // That means that /api is non-resource, /api/v1beta1 is resource, /healthz is non-resource, and /swagger/anything is non-resource |
|
| 22 |
+ urlSegments := splitPath(req.URL.Path) |
|
| 23 |
+ isResourceURL := (len(urlSegments) > 1) && a.infoResolver.APIPrefixes.Has(urlSegments[0]) |
|
| 24 |
+ |
|
| 25 |
+ if !isResourceURL {
|
|
| 26 |
+ return DefaultAuthorizationAttributes{
|
|
| 27 |
+ Verb: strings.ToLower(req.Method), |
|
| 28 |
+ NonResourceURL: true, |
|
| 29 |
+ URL: req.URL.Path, |
|
| 30 |
+ }, nil |
|
| 31 |
+ } |
|
| 32 |
+ |
|
| 33 |
+ requestInfo, err := a.infoResolver.GetAPIRequestInfo(req) |
|
| 34 |
+ if err != nil {
|
|
| 35 |
+ return nil, err |
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ // TODO reconsider special casing this. Having the special case hereallow us to fully share the kube |
|
| 39 |
+ // APIRequestInfoResolver without any modification or customization. |
|
| 40 |
+ if (requestInfo.Resource == "projects") && (len(requestInfo.Name) > 0) {
|
|
| 41 |
+ requestInfo.Namespace = requestInfo.Name |
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ return DefaultAuthorizationAttributes{
|
|
| 45 |
+ Verb: requestInfo.Verb, |
|
| 46 |
+ Resource: requestInfo.Resource, |
|
| 47 |
+ ResourceName: requestInfo.Name, |
|
| 48 |
+ RequestAttributes: nil, |
|
| 49 |
+ NonResourceURL: false, |
|
| 50 |
+ URL: req.URL.Path, |
|
| 51 |
+ }, nil |
|
| 52 |
+} |
| ... | ... |
@@ -2,42 +2,15 @@ package authorizer |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
- "net/http" |
|
| 6 |
- "path" |
|
| 7 |
- "strings" |
|
| 8 | 5 |
|
| 9 | 6 |
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
| 10 |
- kapiserver "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" |
|
| 11 | 7 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" |
| 12 | 8 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
| 13 | 9 |
kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors" |
| 14 | 10 |
|
| 15 |
- authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 16 | 11 |
"github.com/openshift/origin/pkg/authorization/rulevalidation" |
| 17 | 12 |
) |
| 18 | 13 |
|
| 19 |
-type Authorizer interface {
|
|
| 20 |
- Authorize(ctx kapi.Context, a AuthorizationAttributes) (allowed bool, reason string, err error) |
|
| 21 |
- GetAllowedSubjects(ctx kapi.Context, attributes AuthorizationAttributes) ([]string, []string, error) |
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 |
-type AuthorizationAttributeBuilder interface {
|
|
| 25 |
- GetAttributes(request *http.Request) (AuthorizationAttributes, error) |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-type AuthorizationAttributes interface {
|
|
| 29 |
- GetVerb() string |
|
| 30 |
- // GetResource returns the resource type. If IsNonResourceURL() is true, then GetResource() is "". |
|
| 31 |
- GetResource() string |
|
| 32 |
- GetResourceName() string |
|
| 33 |
- // GetRequestAttributes is of type interface{} because different verbs and different Authorizer/AuthorizationAttributeBuilder pairs may have different contract requirements.
|
|
| 34 |
- GetRequestAttributes() interface{}
|
|
| 35 |
- // IsNonResourceURL returns true if this is not an action performed against the resource API |
|
| 36 |
- IsNonResourceURL() bool |
|
| 37 |
- // GetURL returns the URL split on '/'s |
|
| 38 |
- GetURL() string |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 | 14 |
type openshiftAuthorizer struct {
|
| 42 | 15 |
masterAuthorizationNamespace string |
| 43 | 16 |
ruleResolver rulevalidation.AuthorizationRuleResolver |
| ... | ... |
@@ -47,75 +20,39 @@ func NewAuthorizer(masterAuthorizationNamespace string, ruleResolver rulevalidat |
| 47 | 47 |
return &openshiftAuthorizer{masterAuthorizationNamespace, ruleResolver}
|
| 48 | 48 |
} |
| 49 | 49 |
|
| 50 |
-type DefaultAuthorizationAttributes struct {
|
|
| 51 |
- Verb string |
|
| 52 |
- Resource string |
|
| 53 |
- ResourceName string |
|
| 54 |
- RequestAttributes interface{}
|
|
| 55 |
- NonResourceURL bool |
|
| 56 |
- URL string |
|
| 57 |
-} |
|
| 58 |
- |
|
| 59 |
-type openshiftAuthorizationAttributeBuilder struct {
|
|
| 60 |
- contextMapper kapi.RequestContextMapper |
|
| 61 |
- infoResolver *kapiserver.APIRequestInfoResolver |
|
| 62 |
-} |
|
| 63 |
- |
|
| 64 |
-func NewAuthorizationAttributeBuilder(contextMapper kapi.RequestContextMapper, infoResolver *kapiserver.APIRequestInfoResolver) AuthorizationAttributeBuilder {
|
|
| 65 |
- return &openshiftAuthorizationAttributeBuilder{contextMapper, infoResolver}
|
|
| 66 |
-} |
|
| 67 |
- |
|
| 68 |
-func doesApplyToUser(ruleUsers, ruleGroups util.StringSet, user user.Info) bool {
|
|
| 69 |
- if ruleUsers.Has(user.GetName()) {
|
|
| 70 |
- return true |
|
| 71 |
- } |
|
| 50 |
+func (a *openshiftAuthorizer) Authorize(ctx kapi.Context, passedAttributes AuthorizationAttributes) (bool, string, error) {
|
|
| 51 |
+ attributes := coerceToDefaultAuthorizationAttributes(passedAttributes) |
|
| 72 | 52 |
|
| 73 |
- for _, currGroup := range user.GetGroups() {
|
|
| 74 |
- if ruleGroups.Has(currGroup) {
|
|
| 75 |
- return true |
|
| 76 |
- } |
|
| 77 |
- } |
|
| 53 |
+ // keep track of errors in case we are unable to authorize the action. |
|
| 54 |
+ // It is entirely possible to get an error and be able to continue determine authorization status in spite of it. |
|
| 55 |
+ // This is most common when a bound role is missing, but enough roles are still present and bound to authorize the request. |
|
| 56 |
+ errs := []error{}
|
|
| 78 | 57 |
|
| 79 |
- return false |
|
| 80 |
-} |
|
| 81 |
-func contains(list []string, token string) bool {
|
|
| 82 |
- for _, curr := range list {
|
|
| 83 |
- if curr == token {
|
|
| 84 |
- return true |
|
| 85 |
- } |
|
| 58 |
+ masterContext := kapi.WithNamespace(ctx, a.masterAuthorizationNamespace) |
|
| 59 |
+ globalAllowed, globalReason, err := a.authorizeWithNamespaceRules(masterContext, attributes) |
|
| 60 |
+ if globalAllowed {
|
|
| 61 |
+ return true, globalReason, nil |
|
| 86 | 62 |
} |
| 87 |
- return false |
|
| 88 |
-} |
|
| 89 |
-func (a *openshiftAuthorizer) getAllowedSubjectsFromNamespaceBindings(ctx kapi.Context, passedAttributes AuthorizationAttributes) (util.StringSet, util.StringSet, error) {
|
|
| 90 |
- attributes := coerceToDefaultAuthorizationAttributes(passedAttributes) |
|
| 91 |
- |
|
| 92 |
- roleBindings, err := a.ruleResolver.GetRoleBindings(ctx) |
|
| 93 | 63 |
if err != nil {
|
| 94 |
- return nil, nil, err |
|
| 64 |
+ errs = append(errs, err) |
|
| 95 | 65 |
} |
| 96 | 66 |
|
| 97 |
- users := util.StringSet{}
|
|
| 98 |
- groups := util.StringSet{}
|
|
| 99 |
- for _, roleBinding := range roleBindings {
|
|
| 100 |
- role, err := a.ruleResolver.GetRole(roleBinding) |
|
| 67 |
+ namespace, _ := kapi.NamespaceFrom(ctx) |
|
| 68 |
+ if len(namespace) != 0 {
|
|
| 69 |
+ namespaceAllowed, namespaceReason, err := a.authorizeWithNamespaceRules(ctx, attributes) |
|
| 70 |
+ if namespaceAllowed {
|
|
| 71 |
+ return true, namespaceReason, nil |
|
| 72 |
+ } |
|
| 101 | 73 |
if err != nil {
|
| 102 |
- return nil, nil, err |
|
| 74 |
+ errs = append(errs, err) |
|
| 103 | 75 |
} |
| 76 |
+ } |
|
| 104 | 77 |
|
| 105 |
- for _, rule := range role.Rules {
|
|
| 106 |
- matches, err := attributes.RuleMatches(rule) |
|
| 107 |
- if err != nil {
|
|
| 108 |
- return nil, nil, err |
|
| 109 |
- } |
|
| 110 |
- |
|
| 111 |
- if matches {
|
|
| 112 |
- users.Insert(roleBinding.Users.List()...) |
|
| 113 |
- groups.Insert(roleBinding.Groups.List()...) |
|
| 114 |
- } |
|
| 115 |
- } |
|
| 78 |
+ if len(errs) > 0 {
|
|
| 79 |
+ return false, "", kerrors.NewAggregate(errs) |
|
| 116 | 80 |
} |
| 117 | 81 |
|
| 118 |
- return users, groups, nil |
|
| 82 |
+ return false, "denied by default", nil |
|
| 119 | 83 |
} |
| 120 | 84 |
|
| 121 | 85 |
func (a *openshiftAuthorizer) GetAllowedSubjects(ctx kapi.Context, attributes AuthorizationAttributes) ([]string, []string, error) {
|
| ... | ... |
@@ -140,39 +77,36 @@ func (a *openshiftAuthorizer) GetAllowedSubjects(ctx kapi.Context, attributes Au |
| 140 | 140 |
return users.List(), groups.List(), nil |
| 141 | 141 |
} |
| 142 | 142 |
|
| 143 |
-func (a *openshiftAuthorizer) Authorize(ctx kapi.Context, passedAttributes AuthorizationAttributes) (bool, string, error) {
|
|
| 143 |
+func (a *openshiftAuthorizer) getAllowedSubjectsFromNamespaceBindings(ctx kapi.Context, passedAttributes AuthorizationAttributes) (util.StringSet, util.StringSet, error) {
|
|
| 144 | 144 |
attributes := coerceToDefaultAuthorizationAttributes(passedAttributes) |
| 145 | 145 |
|
| 146 |
- // keep track of errors in case we are unable to authorize the action. |
|
| 147 |
- // It is entirely possible to get an error and be able to continue determine authorization status in spite of it. |
|
| 148 |
- // This is most common when a bound role is missing, but enough roles are still present and bound to authorize the request. |
|
| 149 |
- errs := []error{}
|
|
| 150 |
- |
|
| 151 |
- masterContext := kapi.WithNamespace(ctx, a.masterAuthorizationNamespace) |
|
| 152 |
- globalAllowed, globalReason, err := a.authorizeWithNamespaceRules(masterContext, attributes) |
|
| 153 |
- if globalAllowed {
|
|
| 154 |
- return true, globalReason, nil |
|
| 155 |
- } |
|
| 146 |
+ roleBindings, err := a.ruleResolver.GetRoleBindings(ctx) |
|
| 156 | 147 |
if err != nil {
|
| 157 |
- errs = append(errs, err) |
|
| 148 |
+ return nil, nil, err |
|
| 158 | 149 |
} |
| 159 | 150 |
|
| 160 |
- namespace, _ := kapi.NamespaceFrom(ctx) |
|
| 161 |
- if len(namespace) != 0 {
|
|
| 162 |
- namespaceAllowed, namespaceReason, err := a.authorizeWithNamespaceRules(ctx, attributes) |
|
| 163 |
- if namespaceAllowed {
|
|
| 164 |
- return true, namespaceReason, nil |
|
| 165 |
- } |
|
| 151 |
+ users := util.StringSet{}
|
|
| 152 |
+ groups := util.StringSet{}
|
|
| 153 |
+ for _, roleBinding := range roleBindings {
|
|
| 154 |
+ role, err := a.ruleResolver.GetRole(roleBinding) |
|
| 166 | 155 |
if err != nil {
|
| 167 |
- errs = append(errs, err) |
|
| 156 |
+ return nil, nil, err |
|
| 168 | 157 |
} |
| 169 |
- } |
|
| 170 | 158 |
|
| 171 |
- if len(errs) > 0 {
|
|
| 172 |
- return false, "", kerrors.NewAggregate(errs) |
|
| 159 |
+ for _, rule := range role.Rules {
|
|
| 160 |
+ matches, err := attributes.RuleMatches(rule) |
|
| 161 |
+ if err != nil {
|
|
| 162 |
+ return nil, nil, err |
|
| 163 |
+ } |
|
| 164 |
+ |
|
| 165 |
+ if matches {
|
|
| 166 |
+ users.Insert(roleBinding.Users.List()...) |
|
| 167 |
+ groups.Insert(roleBinding.Groups.List()...) |
|
| 168 |
+ } |
|
| 169 |
+ } |
|
| 173 | 170 |
} |
| 174 | 171 |
|
| 175 |
- return false, "denied by default", nil |
|
| 172 |
+ return users, groups, nil |
|
| 176 | 173 |
} |
| 177 | 174 |
|
| 178 | 175 |
// authorizeWithNamespaceRules returns isAllowed, reason, and error. If an error is returned, isAllowed and reason are still valid. This seems strange |
| ... | ... |
@@ -214,358 +148,16 @@ func coerceToDefaultAuthorizationAttributes(passedAttributes AuthorizationAttrib |
| 214 | 214 |
return attributes |
| 215 | 215 |
} |
| 216 | 216 |
|
| 217 |
-func (a DefaultAuthorizationAttributes) RuleMatches(rule authorizationapi.PolicyRule) (bool, error) {
|
|
| 218 |
- if a.IsNonResourceURL() {
|
|
| 219 |
- if a.nonResourceMatches(rule) {
|
|
| 220 |
- if a.verbMatches(rule.Verbs) {
|
|
| 221 |
- return true, nil |
|
| 222 |
- } |
|
| 223 |
- } |
|
| 224 |
- |
|
| 225 |
- return false, nil |
|
| 226 |
- } |
|
| 227 |
- |
|
| 228 |
- if a.verbMatches(rule.Verbs) {
|
|
| 229 |
- allowedResourceTypes := authorizationapi.ExpandResources(rule.Resources) |
|
| 230 |
- |
|
| 231 |
- if a.resourceMatches(allowedResourceTypes) {
|
|
| 232 |
- if a.nameMatches(rule.ResourceNames) {
|
|
| 233 |
- return true, nil |
|
| 234 |
- } |
|
| 235 |
- } |
|
| 236 |
- } |
|
| 237 |
- |
|
| 238 |
- return false, nil |
|
| 239 |
-} |
|
| 240 |
- |
|
| 241 |
-func (a DefaultAuthorizationAttributes) verbMatches(verbs util.StringSet) bool {
|
|
| 242 |
- return verbs.Has(authorizationapi.VerbAll) || verbs.Has(strings.ToLower(a.GetVerb())) |
|
| 243 |
-} |
|
| 244 |
- |
|
| 245 |
-func (a DefaultAuthorizationAttributes) resourceMatches(allowedResourceTypes util.StringSet) bool {
|
|
| 246 |
- return allowedResourceTypes.Has(authorizationapi.ResourceAll) || allowedResourceTypes.Has(strings.ToLower(a.GetResource())) |
|
| 247 |
-} |
|
| 248 |
- |
|
| 249 |
-// nameMatches checks to see if the resourceName of the action is in a the specified whitelist. An empty whitelist indicates that any name is allowed. |
|
| 250 |
-// An empty string in the whitelist should only match the action's resourceName if the resourceName itself is empty string. This behavior allows for the |
|
| 251 |
-// combination of a whitelist for gets in the same rule as a list that won't have a resourceName. I don't recommend writing such a rule, but we do |
|
| 252 |
-// handle it like you'd expect: white list is respected for gets while not preventing the list you explicitly asked for. |
|
| 253 |
-func (a DefaultAuthorizationAttributes) nameMatches(allowedResourceNames util.StringSet) bool {
|
|
| 254 |
- if len(allowedResourceNames) == 0 {
|
|
| 217 |
+func doesApplyToUser(ruleUsers, ruleGroups util.StringSet, user user.Info) bool {
|
|
| 218 |
+ if ruleUsers.Has(user.GetName()) {
|
|
| 255 | 219 |
return true |
| 256 | 220 |
} |
| 257 | 221 |
|
| 258 |
- return allowedResourceNames.Has(a.GetResourceName()) |
|
| 259 |
-} |
|
| 260 |
- |
|
| 261 |
-func (a DefaultAuthorizationAttributes) GetVerb() string {
|
|
| 262 |
- return a.Verb |
|
| 263 |
-} |
|
| 264 |
- |
|
| 265 |
-// nonResourceMatches take the remainer of a URL and attempts to match it against a series of explicitly allowed steps that can end in a wildcard |
|
| 266 |
-func (a DefaultAuthorizationAttributes) nonResourceMatches(rule authorizationapi.PolicyRule) bool {
|
|
| 267 |
- for allowedNonResourcePath := range rule.NonResourceURLs {
|
|
| 268 |
- // if the allowed resource path ends in a wildcard, check to see if the URL starts with it |
|
| 269 |
- if strings.HasSuffix(allowedNonResourcePath, "*") {
|
|
| 270 |
- if strings.HasPrefix(a.GetURL(), allowedNonResourcePath[0:len(allowedNonResourcePath)-1]) {
|
|
| 271 |
- return true |
|
| 272 |
- } |
|
| 273 |
- } |
|
| 274 |
- |
|
| 275 |
- // if we have an exact match, return true |
|
| 276 |
- if a.GetURL() == allowedNonResourcePath {
|
|
| 222 |
+ for _, currGroup := range user.GetGroups() {
|
|
| 223 |
+ if ruleGroups.Has(currGroup) {
|
|
| 277 | 224 |
return true |
| 278 | 225 |
} |
| 279 | 226 |
} |
| 280 | 227 |
|
| 281 | 228 |
return false |
| 282 | 229 |
} |
| 283 |
- |
|
| 284 |
-// splitPath returns the segments for a URL path. |
|
| 285 |
-func splitPath(thePath string) []string {
|
|
| 286 |
- thePath = strings.Trim(path.Clean(thePath), "/") |
|
| 287 |
- if thePath == "" {
|
|
| 288 |
- return []string{}
|
|
| 289 |
- } |
|
| 290 |
- return strings.Split(thePath, "/") |
|
| 291 |
-} |
|
| 292 |
- |
|
| 293 |
-func (a DefaultAuthorizationAttributes) GetResource() string {
|
|
| 294 |
- return a.Resource |
|
| 295 |
-} |
|
| 296 |
- |
|
| 297 |
-func (a DefaultAuthorizationAttributes) GetResourceName() string {
|
|
| 298 |
- return a.ResourceName |
|
| 299 |
-} |
|
| 300 |
- |
|
| 301 |
-func (a DefaultAuthorizationAttributes) GetRequestAttributes() interface{} {
|
|
| 302 |
- return a.RequestAttributes |
|
| 303 |
-} |
|
| 304 |
- |
|
| 305 |
-func (a DefaultAuthorizationAttributes) IsNonResourceURL() bool {
|
|
| 306 |
- return a.NonResourceURL |
|
| 307 |
-} |
|
| 308 |
- |
|
| 309 |
-func (a DefaultAuthorizationAttributes) GetURL() string {
|
|
| 310 |
- return a.URL |
|
| 311 |
-} |
|
| 312 |
- |
|
| 313 |
-func (a *openshiftAuthorizationAttributeBuilder) GetAttributes(req *http.Request) (AuthorizationAttributes, error) {
|
|
| 314 |
- // any url that starts with an API prefix and is more than one step long is considered to be a resource URL. |
|
| 315 |
- // That means that /api is non-resource, /api/v1beta1 is resource, /healthz is non-resource, and /swagger/anything is non-resource |
|
| 316 |
- urlSegments := splitPath(req.URL.Path) |
|
| 317 |
- isResourceURL := (len(urlSegments) > 1) && a.infoResolver.APIPrefixes.Has(urlSegments[0]) |
|
| 318 |
- |
|
| 319 |
- if !isResourceURL {
|
|
| 320 |
- return DefaultAuthorizationAttributes{
|
|
| 321 |
- Verb: strings.ToLower(req.Method), |
|
| 322 |
- NonResourceURL: true, |
|
| 323 |
- URL: req.URL.Path, |
|
| 324 |
- }, nil |
|
| 325 |
- } |
|
| 326 |
- |
|
| 327 |
- requestInfo, err := a.infoResolver.GetAPIRequestInfo(req) |
|
| 328 |
- if err != nil {
|
|
| 329 |
- return nil, err |
|
| 330 |
- } |
|
| 331 |
- |
|
| 332 |
- // TODO reconsider special casing this. Having the special case hereallow us to fully share the kube |
|
| 333 |
- // APIRequestInfoResolver without any modification or customization. |
|
| 334 |
- if (requestInfo.Resource == "projects") && (len(requestInfo.Name) > 0) {
|
|
| 335 |
- requestInfo.Namespace = requestInfo.Name |
|
| 336 |
- } |
|
| 337 |
- |
|
| 338 |
- return DefaultAuthorizationAttributes{
|
|
| 339 |
- Verb: requestInfo.Verb, |
|
| 340 |
- Resource: requestInfo.Resource, |
|
| 341 |
- ResourceName: requestInfo.Name, |
|
| 342 |
- RequestAttributes: nil, |
|
| 343 |
- NonResourceURL: false, |
|
| 344 |
- URL: req.URL.Path, |
|
| 345 |
- }, nil |
|
| 346 |
-} |
|
| 347 |
- |
|
| 348 |
-func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy {
|
|
| 349 |
- return &authorizationapi.Policy{
|
|
| 350 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 351 |
- Name: authorizationapi.PolicyName, |
|
| 352 |
- Namespace: masterNamespace, |
|
| 353 |
- CreationTimestamp: util.Now(), |
|
| 354 |
- UID: util.NewUUID(), |
|
| 355 |
- }, |
|
| 356 |
- LastModified: util.Now(), |
|
| 357 |
- Roles: map[string]authorizationapi.Role{
|
|
| 358 |
- "cluster-admin": {
|
|
| 359 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 360 |
- Name: "cluster-admin", |
|
| 361 |
- Namespace: masterNamespace, |
|
| 362 |
- }, |
|
| 363 |
- Rules: []authorizationapi.PolicyRule{
|
|
| 364 |
- {
|
|
| 365 |
- Verbs: util.NewStringSet(authorizationapi.VerbAll), |
|
| 366 |
- Resources: util.NewStringSet(authorizationapi.ResourceAll), |
|
| 367 |
- }, |
|
| 368 |
- {
|
|
| 369 |
- Verbs: util.NewStringSet(authorizationapi.VerbAll), |
|
| 370 |
- NonResourceURLs: util.NewStringSet(authorizationapi.NonResourceAll), |
|
| 371 |
- }, |
|
| 372 |
- }, |
|
| 373 |
- }, |
|
| 374 |
- "admin": {
|
|
| 375 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 376 |
- Name: "admin", |
|
| 377 |
- Namespace: masterNamespace, |
|
| 378 |
- }, |
|
| 379 |
- Rules: []authorizationapi.PolicyRule{
|
|
| 380 |
- {
|
|
| 381 |
- Verbs: util.NewStringSet("get", "list", "watch", "create", "update", "delete"),
|
|
| 382 |
- Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.PermissionGrantingGroupName, authorizationapi.KubeExposedGroupName), |
|
| 383 |
- }, |
|
| 384 |
- {
|
|
| 385 |
- Verbs: util.NewStringSet("get", "list", "watch"),
|
|
| 386 |
- Resources: util.NewStringSet(authorizationapi.PolicyOwnerGroupName, authorizationapi.KubeAllGroupName), |
|
| 387 |
- }, |
|
| 388 |
- }, |
|
| 389 |
- }, |
|
| 390 |
- "edit": {
|
|
| 391 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 392 |
- Name: "edit", |
|
| 393 |
- Namespace: masterNamespace, |
|
| 394 |
- }, |
|
| 395 |
- Rules: []authorizationapi.PolicyRule{
|
|
| 396 |
- {
|
|
| 397 |
- Verbs: util.NewStringSet("get", "list", "watch", "create", "update", "delete"),
|
|
| 398 |
- Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeExposedGroupName), |
|
| 399 |
- }, |
|
| 400 |
- {
|
|
| 401 |
- Verbs: util.NewStringSet("get", "list", "watch"),
|
|
| 402 |
- Resources: util.NewStringSet(authorizationapi.KubeAllGroupName), |
|
| 403 |
- }, |
|
| 404 |
- }, |
|
| 405 |
- }, |
|
| 406 |
- "view": {
|
|
| 407 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 408 |
- Name: "view", |
|
| 409 |
- Namespace: masterNamespace, |
|
| 410 |
- }, |
|
| 411 |
- Rules: []authorizationapi.PolicyRule{
|
|
| 412 |
- {
|
|
| 413 |
- Verbs: util.NewStringSet("get", "list", "watch"),
|
|
| 414 |
- Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeAllGroupName), |
|
| 415 |
- }, |
|
| 416 |
- }, |
|
| 417 |
- }, |
|
| 418 |
- "basic-user": {
|
|
| 419 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 420 |
- Name: "view-self", |
|
| 421 |
- Namespace: masterNamespace, |
|
| 422 |
- }, |
|
| 423 |
- Rules: []authorizationapi.PolicyRule{
|
|
| 424 |
- {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("users"), ResourceNames: util.NewStringSet("~")},
|
|
| 425 |
- {Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projects")},
|
|
| 426 |
- }, |
|
| 427 |
- }, |
|
| 428 |
- "cluster-status": {
|
|
| 429 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 430 |
- Name: "cluster-status", |
|
| 431 |
- Namespace: masterNamespace, |
|
| 432 |
- }, |
|
| 433 |
- Rules: []authorizationapi.PolicyRule{
|
|
| 434 |
- {
|
|
| 435 |
- Verbs: util.NewStringSet("get"),
|
|
| 436 |
- NonResourceURLs: util.NewStringSet("/healthz", "/version", "/api", "/osapi"),
|
|
| 437 |
- }, |
|
| 438 |
- }, |
|
| 439 |
- }, |
|
| 440 |
- "system:deployer": {
|
|
| 441 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 442 |
- Name: "system:deployer", |
|
| 443 |
- Namespace: masterNamespace, |
|
| 444 |
- }, |
|
| 445 |
- Rules: []authorizationapi.PolicyRule{
|
|
| 446 |
- {
|
|
| 447 |
- Verbs: util.NewStringSet(authorizationapi.VerbAll), |
|
| 448 |
- Resources: util.NewStringSet(authorizationapi.ResourceAll), |
|
| 449 |
- }, |
|
| 450 |
- }, |
|
| 451 |
- }, |
|
| 452 |
- "system:component": {
|
|
| 453 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 454 |
- Name: "system:component", |
|
| 455 |
- Namespace: masterNamespace, |
|
| 456 |
- }, |
|
| 457 |
- Rules: []authorizationapi.PolicyRule{
|
|
| 458 |
- {
|
|
| 459 |
- Verbs: util.NewStringSet(authorizationapi.VerbAll), |
|
| 460 |
- Resources: util.NewStringSet(authorizationapi.ResourceAll), |
|
| 461 |
- }, |
|
| 462 |
- }, |
|
| 463 |
- }, |
|
| 464 |
- "system:delete-tokens": {
|
|
| 465 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 466 |
- Name: "system:delete-tokens", |
|
| 467 |
- Namespace: masterNamespace, |
|
| 468 |
- }, |
|
| 469 |
- Rules: []authorizationapi.PolicyRule{
|
|
| 470 |
- {
|
|
| 471 |
- Verbs: util.NewStringSet("delete"),
|
|
| 472 |
- Resources: util.NewStringSet("oauthaccesstoken", "oauthauthorizetoken"),
|
|
| 473 |
- }, |
|
| 474 |
- }, |
|
| 475 |
- }, |
|
| 476 |
- }, |
|
| 477 |
- } |
|
| 478 |
-} |
|
| 479 |
- |
|
| 480 |
-func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyBinding {
|
|
| 481 |
- return &authorizationapi.PolicyBinding{
|
|
| 482 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 483 |
- Name: masterNamespace, |
|
| 484 |
- Namespace: masterNamespace, |
|
| 485 |
- CreationTimestamp: util.Now(), |
|
| 486 |
- UID: util.NewUUID(), |
|
| 487 |
- }, |
|
| 488 |
- LastModified: util.Now(), |
|
| 489 |
- PolicyRef: kapi.ObjectReference{Namespace: masterNamespace},
|
|
| 490 |
- RoleBindings: map[string]authorizationapi.RoleBinding{
|
|
| 491 |
- "system:component-binding": {
|
|
| 492 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 493 |
- Name: "system:component-binding", |
|
| 494 |
- Namespace: masterNamespace, |
|
| 495 |
- }, |
|
| 496 |
- RoleRef: kapi.ObjectReference{
|
|
| 497 |
- Name: "system:component", |
|
| 498 |
- Namespace: masterNamespace, |
|
| 499 |
- }, |
|
| 500 |
- Users: util.NewStringSet("system:openshift-client", "system:kube-client"),
|
|
| 501 |
- }, |
|
| 502 |
- "system:deployer-binding": {
|
|
| 503 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 504 |
- Name: "system:deployer-binding", |
|
| 505 |
- Namespace: masterNamespace, |
|
| 506 |
- }, |
|
| 507 |
- RoleRef: kapi.ObjectReference{
|
|
| 508 |
- Name: "system:deployer", |
|
| 509 |
- Namespace: masterNamespace, |
|
| 510 |
- }, |
|
| 511 |
- Users: util.NewStringSet("system:openshift-deployer"),
|
|
| 512 |
- }, |
|
| 513 |
- "cluster-admin-binding": {
|
|
| 514 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 515 |
- Name: "cluster-admin-binding", |
|
| 516 |
- Namespace: masterNamespace, |
|
| 517 |
- }, |
|
| 518 |
- RoleRef: kapi.ObjectReference{
|
|
| 519 |
- Name: "cluster-admin", |
|
| 520 |
- Namespace: masterNamespace, |
|
| 521 |
- }, |
|
| 522 |
- Users: util.NewStringSet("system:admin"),
|
|
| 523 |
- }, |
|
| 524 |
- "basic-user-binding": {
|
|
| 525 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 526 |
- Name: "basic-user-binding", |
|
| 527 |
- Namespace: masterNamespace, |
|
| 528 |
- }, |
|
| 529 |
- RoleRef: kapi.ObjectReference{
|
|
| 530 |
- Name: "basic-user", |
|
| 531 |
- Namespace: masterNamespace, |
|
| 532 |
- }, |
|
| 533 |
- Groups: util.NewStringSet("system:authenticated"),
|
|
| 534 |
- }, |
|
| 535 |
- "insecure-cluster-admin-binding": {
|
|
| 536 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 537 |
- Name: "insecure-cluster-admin-binding", |
|
| 538 |
- Namespace: masterNamespace, |
|
| 539 |
- }, |
|
| 540 |
- RoleRef: kapi.ObjectReference{
|
|
| 541 |
- Name: "cluster-admin", |
|
| 542 |
- Namespace: masterNamespace, |
|
| 543 |
- }, |
|
| 544 |
- // TODO until we decide to enforce policy, simply allow every one access |
|
| 545 |
- Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
|
|
| 546 |
- }, |
|
| 547 |
- "system:delete-tokens-binding": {
|
|
| 548 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 549 |
- Name: "system:delete-tokens-binding", |
|
| 550 |
- Namespace: masterNamespace, |
|
| 551 |
- }, |
|
| 552 |
- RoleRef: kapi.ObjectReference{
|
|
| 553 |
- Name: "system:delete-tokens", |
|
| 554 |
- Namespace: masterNamespace, |
|
| 555 |
- }, |
|
| 556 |
- Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
|
|
| 557 |
- }, |
|
| 558 |
- "cluster-status-binding": {
|
|
| 559 |
- ObjectMeta: kapi.ObjectMeta{
|
|
| 560 |
- Name: "cluster-status-binding", |
|
| 561 |
- Namespace: masterNamespace, |
|
| 562 |
- }, |
|
| 563 |
- RoleRef: kapi.ObjectReference{
|
|
| 564 |
- Name: "cluster-status", |
|
| 565 |
- Namespace: masterNamespace, |
|
| 566 |
- }, |
|
| 567 |
- Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
|
|
| 568 |
- }, |
|
| 569 |
- }, |
|
| 570 |
- } |
|
| 571 |
-} |
| 572 | 230 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,233 @@ |
| 0 |
+package authorizer |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 4 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 5 |
+ |
|
| 6 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func GetBootstrapPolicy(masterNamespace string) *authorizationapi.Policy {
|
|
| 10 |
+ return &authorizationapi.Policy{
|
|
| 11 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 12 |
+ Name: authorizationapi.PolicyName, |
|
| 13 |
+ Namespace: masterNamespace, |
|
| 14 |
+ CreationTimestamp: util.Now(), |
|
| 15 |
+ UID: util.NewUUID(), |
|
| 16 |
+ }, |
|
| 17 |
+ LastModified: util.Now(), |
|
| 18 |
+ Roles: map[string]authorizationapi.Role{
|
|
| 19 |
+ "cluster-admin": {
|
|
| 20 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 21 |
+ Name: "cluster-admin", |
|
| 22 |
+ Namespace: masterNamespace, |
|
| 23 |
+ }, |
|
| 24 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 25 |
+ {
|
|
| 26 |
+ Verbs: util.NewStringSet(authorizationapi.VerbAll), |
|
| 27 |
+ Resources: util.NewStringSet(authorizationapi.ResourceAll), |
|
| 28 |
+ }, |
|
| 29 |
+ {
|
|
| 30 |
+ Verbs: util.NewStringSet(authorizationapi.VerbAll), |
|
| 31 |
+ NonResourceURLs: util.NewStringSet(authorizationapi.NonResourceAll), |
|
| 32 |
+ }, |
|
| 33 |
+ }, |
|
| 34 |
+ }, |
|
| 35 |
+ "admin": {
|
|
| 36 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 37 |
+ Name: "admin", |
|
| 38 |
+ Namespace: masterNamespace, |
|
| 39 |
+ }, |
|
| 40 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 41 |
+ {
|
|
| 42 |
+ Verbs: util.NewStringSet("get", "list", "watch", "create", "update", "delete"),
|
|
| 43 |
+ Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.PermissionGrantingGroupName, authorizationapi.KubeExposedGroupName), |
|
| 44 |
+ }, |
|
| 45 |
+ {
|
|
| 46 |
+ Verbs: util.NewStringSet("get", "list", "watch"),
|
|
| 47 |
+ Resources: util.NewStringSet(authorizationapi.PolicyOwnerGroupName, authorizationapi.KubeAllGroupName), |
|
| 48 |
+ }, |
|
| 49 |
+ }, |
|
| 50 |
+ }, |
|
| 51 |
+ "edit": {
|
|
| 52 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 53 |
+ Name: "edit", |
|
| 54 |
+ Namespace: masterNamespace, |
|
| 55 |
+ }, |
|
| 56 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 57 |
+ {
|
|
| 58 |
+ Verbs: util.NewStringSet("get", "list", "watch", "create", "update", "delete"),
|
|
| 59 |
+ Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeExposedGroupName), |
|
| 60 |
+ }, |
|
| 61 |
+ {
|
|
| 62 |
+ Verbs: util.NewStringSet("get", "list", "watch"),
|
|
| 63 |
+ Resources: util.NewStringSet(authorizationapi.KubeAllGroupName), |
|
| 64 |
+ }, |
|
| 65 |
+ }, |
|
| 66 |
+ }, |
|
| 67 |
+ "view": {
|
|
| 68 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 69 |
+ Name: "view", |
|
| 70 |
+ Namespace: masterNamespace, |
|
| 71 |
+ }, |
|
| 72 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 73 |
+ {
|
|
| 74 |
+ Verbs: util.NewStringSet("get", "list", "watch"),
|
|
| 75 |
+ Resources: util.NewStringSet(authorizationapi.OpenshiftExposedGroupName, authorizationapi.KubeAllGroupName), |
|
| 76 |
+ }, |
|
| 77 |
+ }, |
|
| 78 |
+ }, |
|
| 79 |
+ "basic-user": {
|
|
| 80 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 81 |
+ Name: "view-self", |
|
| 82 |
+ Namespace: masterNamespace, |
|
| 83 |
+ }, |
|
| 84 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 85 |
+ {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("users"), ResourceNames: util.NewStringSet("~")},
|
|
| 86 |
+ {Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projects")},
|
|
| 87 |
+ }, |
|
| 88 |
+ }, |
|
| 89 |
+ "cluster-status": {
|
|
| 90 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 91 |
+ Name: "cluster-status", |
|
| 92 |
+ Namespace: masterNamespace, |
|
| 93 |
+ }, |
|
| 94 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 95 |
+ {
|
|
| 96 |
+ Verbs: util.NewStringSet("get"),
|
|
| 97 |
+ NonResourceURLs: util.NewStringSet("/healthz", "/version", "/api", "/osapi"),
|
|
| 98 |
+ }, |
|
| 99 |
+ }, |
|
| 100 |
+ }, |
|
| 101 |
+ "system:deployer": {
|
|
| 102 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 103 |
+ Name: "system:deployer", |
|
| 104 |
+ Namespace: masterNamespace, |
|
| 105 |
+ }, |
|
| 106 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 107 |
+ {
|
|
| 108 |
+ Verbs: util.NewStringSet(authorizationapi.VerbAll), |
|
| 109 |
+ Resources: util.NewStringSet(authorizationapi.ResourceAll), |
|
| 110 |
+ }, |
|
| 111 |
+ }, |
|
| 112 |
+ }, |
|
| 113 |
+ "system:component": {
|
|
| 114 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 115 |
+ Name: "system:component", |
|
| 116 |
+ Namespace: masterNamespace, |
|
| 117 |
+ }, |
|
| 118 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 119 |
+ {
|
|
| 120 |
+ Verbs: util.NewStringSet(authorizationapi.VerbAll), |
|
| 121 |
+ Resources: util.NewStringSet(authorizationapi.ResourceAll), |
|
| 122 |
+ }, |
|
| 123 |
+ }, |
|
| 124 |
+ }, |
|
| 125 |
+ "system:delete-tokens": {
|
|
| 126 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 127 |
+ Name: "system:delete-tokens", |
|
| 128 |
+ Namespace: masterNamespace, |
|
| 129 |
+ }, |
|
| 130 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 131 |
+ {
|
|
| 132 |
+ Verbs: util.NewStringSet("delete"),
|
|
| 133 |
+ Resources: util.NewStringSet("oauthaccesstoken", "oauthauthorizetoken"),
|
|
| 134 |
+ }, |
|
| 135 |
+ }, |
|
| 136 |
+ }, |
|
| 137 |
+ }, |
|
| 138 |
+ } |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+func GetBootstrapPolicyBinding(masterNamespace string) *authorizationapi.PolicyBinding {
|
|
| 142 |
+ return &authorizationapi.PolicyBinding{
|
|
| 143 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 144 |
+ Name: masterNamespace, |
|
| 145 |
+ Namespace: masterNamespace, |
|
| 146 |
+ CreationTimestamp: util.Now(), |
|
| 147 |
+ UID: util.NewUUID(), |
|
| 148 |
+ }, |
|
| 149 |
+ LastModified: util.Now(), |
|
| 150 |
+ PolicyRef: kapi.ObjectReference{Namespace: masterNamespace},
|
|
| 151 |
+ RoleBindings: map[string]authorizationapi.RoleBinding{
|
|
| 152 |
+ "system:component-binding": {
|
|
| 153 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 154 |
+ Name: "system:component-binding", |
|
| 155 |
+ Namespace: masterNamespace, |
|
| 156 |
+ }, |
|
| 157 |
+ RoleRef: kapi.ObjectReference{
|
|
| 158 |
+ Name: "system:component", |
|
| 159 |
+ Namespace: masterNamespace, |
|
| 160 |
+ }, |
|
| 161 |
+ Users: util.NewStringSet("system:openshift-client", "system:kube-client"),
|
|
| 162 |
+ }, |
|
| 163 |
+ "system:deployer-binding": {
|
|
| 164 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 165 |
+ Name: "system:deployer-binding", |
|
| 166 |
+ Namespace: masterNamespace, |
|
| 167 |
+ }, |
|
| 168 |
+ RoleRef: kapi.ObjectReference{
|
|
| 169 |
+ Name: "system:deployer", |
|
| 170 |
+ Namespace: masterNamespace, |
|
| 171 |
+ }, |
|
| 172 |
+ Users: util.NewStringSet("system:openshift-deployer"),
|
|
| 173 |
+ }, |
|
| 174 |
+ "cluster-admin-binding": {
|
|
| 175 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 176 |
+ Name: "cluster-admin-binding", |
|
| 177 |
+ Namespace: masterNamespace, |
|
| 178 |
+ }, |
|
| 179 |
+ RoleRef: kapi.ObjectReference{
|
|
| 180 |
+ Name: "cluster-admin", |
|
| 181 |
+ Namespace: masterNamespace, |
|
| 182 |
+ }, |
|
| 183 |
+ Users: util.NewStringSet("system:admin"),
|
|
| 184 |
+ }, |
|
| 185 |
+ "basic-user-binding": {
|
|
| 186 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 187 |
+ Name: "basic-user-binding", |
|
| 188 |
+ Namespace: masterNamespace, |
|
| 189 |
+ }, |
|
| 190 |
+ RoleRef: kapi.ObjectReference{
|
|
| 191 |
+ Name: "basic-user", |
|
| 192 |
+ Namespace: masterNamespace, |
|
| 193 |
+ }, |
|
| 194 |
+ Groups: util.NewStringSet("system:authenticated"),
|
|
| 195 |
+ }, |
|
| 196 |
+ "insecure-cluster-admin-binding": {
|
|
| 197 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 198 |
+ Name: "insecure-cluster-admin-binding", |
|
| 199 |
+ Namespace: masterNamespace, |
|
| 200 |
+ }, |
|
| 201 |
+ RoleRef: kapi.ObjectReference{
|
|
| 202 |
+ Name: "cluster-admin", |
|
| 203 |
+ Namespace: masterNamespace, |
|
| 204 |
+ }, |
|
| 205 |
+ // TODO until we decide to enforce policy, simply allow every one access |
|
| 206 |
+ Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
|
|
| 207 |
+ }, |
|
| 208 |
+ "system:delete-tokens-binding": {
|
|
| 209 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 210 |
+ Name: "system:delete-tokens-binding", |
|
| 211 |
+ Namespace: masterNamespace, |
|
| 212 |
+ }, |
|
| 213 |
+ RoleRef: kapi.ObjectReference{
|
|
| 214 |
+ Name: "system:delete-tokens", |
|
| 215 |
+ Namespace: masterNamespace, |
|
| 216 |
+ }, |
|
| 217 |
+ Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
|
|
| 218 |
+ }, |
|
| 219 |
+ "cluster-status-binding": {
|
|
| 220 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 221 |
+ Name: "cluster-status-binding", |
|
| 222 |
+ Namespace: masterNamespace, |
|
| 223 |
+ }, |
|
| 224 |
+ RoleRef: kapi.ObjectReference{
|
|
| 225 |
+ Name: "cluster-status", |
|
| 226 |
+ Namespace: masterNamespace, |
|
| 227 |
+ }, |
|
| 228 |
+ Groups: util.NewStringSet("system:authenticated", "system:unauthenticated"),
|
|
| 229 |
+ }, |
|
| 230 |
+ }, |
|
| 231 |
+ } |
|
| 232 |
+} |
| 0 | 233 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 0 |
+package authorizer |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net/http" |
|
| 4 |
+ |
|
| 5 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type Authorizer interface {
|
|
| 9 |
+ Authorize(ctx kapi.Context, a AuthorizationAttributes) (allowed bool, reason string, err error) |
|
| 10 |
+ GetAllowedSubjects(ctx kapi.Context, attributes AuthorizationAttributes) ([]string, []string, error) |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+type AuthorizationAttributeBuilder interface {
|
|
| 14 |
+ GetAttributes(request *http.Request) (AuthorizationAttributes, error) |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+type AuthorizationAttributes interface {
|
|
| 18 |
+ GetVerb() string |
|
| 19 |
+ // GetResource returns the resource type. If IsNonResourceURL() is true, then GetResource() is "". |
|
| 20 |
+ GetResource() string |
|
| 21 |
+ GetResourceName() string |
|
| 22 |
+ // GetRequestAttributes is of type interface{} because different verbs and different Authorizer/AuthorizationAttributeBuilder pairs may have different contract requirements.
|
|
| 23 |
+ GetRequestAttributes() interface{}
|
|
| 24 |
+ // IsNonResourceURL returns true if this is not an action performed against the resource API |
|
| 25 |
+ IsNonResourceURL() bool |
|
| 26 |
+ // GetURL returns the URL path being requested, including the leading '/' |
|
| 27 |
+ GetURL() string |
|
| 28 |
+} |