Skip to content

Commit e4dcd4a

Browse files
committed
Merge pull request kubernetes#2122 from erictune/moar_attribs
Moar authorization attributes
2 parents 4630948 + 1668c6f commit e4dcd4a

File tree

9 files changed

+374
-60
lines changed

9 files changed

+374
-60
lines changed

cmd/apiserver/apiserver.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ func main() {
145145
}
146146

147147
n := net.IPNet(portalNet)
148+
149+
authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(*authorizationMode)
150+
if err != nil {
151+
glog.Fatalf("Invalid Authorization Config: %v", err)
152+
}
153+
148154
config := &master.Config{
149155
Client: client,
150156
Cloud: cloud,
@@ -161,7 +167,7 @@ func main() {
161167
ReadOnlyPort: *readOnlyPort,
162168
ReadWritePort: *port,
163169
PublicAddress: *publicAddressOverride,
164-
AuthorizationMode: *authorizationMode,
170+
Authorizer: authorizer,
165171
}
166172
m := master.New(config)
167173

cmd/integration/integration.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
3737
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
3838
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
39+
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
3940
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
4041
minionControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller"
4142
replicationControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
@@ -146,7 +147,7 @@ func startComponents(manifestURL string) (apiServerURL string) {
146147
KubeletClient: fakeKubeletClient{},
147148
EnableLogsSupport: false,
148149
APIPrefix: "/api",
149-
AuthorizationMode: "AlwaysAllow",
150+
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
150151

151152
ReadWritePort: portNumber,
152153
ReadOnlyPort: portNumber,

pkg/apiserver/apiserver.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ func (g *APIGroup) InstallREST(mux Mux, paths ...string) {
107107
prefix = strings.TrimRight(prefix, "/")
108108
proxyHandler := &ProxyHandler{prefix + "/proxy/", g.handler.storage, g.handler.codec}
109109
mux.Handle(prefix+"/", http.StripPrefix(prefix, restHandler))
110+
// Note: update GetAttribs() when adding a handler.
110111
mux.Handle(prefix+"/watch/", http.StripPrefix(prefix+"/watch/", watchHandler))
111112
mux.Handle(prefix+"/proxy/", http.StripPrefix(prefix+"/proxy/", proxyHandler))
112113
mux.Handle(prefix+"/redirect/", http.StripPrefix(prefix+"/redirect/", redirectHandler))

pkg/apiserver/authz.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ func (alwaysAllowAuthorizer) Authorize(a authorizer.Attributes) (err error) {
3636
return nil
3737
}
3838

39+
func NewAlwaysAllowAuthorizer() authorizer.Authorizer {
40+
return new(alwaysAllowAuthorizer)
41+
}
42+
3943
// alwaysDenyAuthorizer is an implementation of authorizer.Attributes
4044
// which always says no to an authorization request.
4145
// It is useful in unit tests to force an operation to be forbidden.
@@ -45,6 +49,10 @@ func (alwaysDenyAuthorizer) Authorize(a authorizer.Attributes) (err error) {
4549
return errors.New("Everything is forbidden.")
4650
}
4751

52+
func NewAlwaysDenyAuthorizer() authorizer.Authorizer {
53+
return new(alwaysDenyAuthorizer)
54+
}
55+
4856
const (
4957
ModeAlwaysAllow string = "AlwaysAllow"
5058
ModeAlwaysDeny string = "AlwaysDeny"
@@ -59,9 +67,9 @@ func NewAuthorizerFromAuthorizationConfig(authorizationMode string) (authorizer.
5967
// Keep cases in sync with constant list above.
6068
switch authorizationMode {
6169
case ModeAlwaysAllow:
62-
return new(alwaysAllowAuthorizer), nil
70+
return NewAlwaysAllowAuthorizer(), nil
6371
case ModeAlwaysDeny:
64-
return new(alwaysDenyAuthorizer), nil
72+
return NewAlwaysDenyAuthorizer(), nil
6573
default:
6674
return nil, errors.New("Unknown authorization mode")
6775
}

pkg/apiserver/handlers.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,49 @@ import (
3030
"github.com/golang/glog"
3131
)
3232

33+
// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal
34+
// CRUDdy GET/POST/PUT/DELETE actions on REST objects.
35+
// TODO: find a way to keep this up to date automatically. Maybe dynamically populate list as handlers added to
36+
// master's Mux.
37+
var specialVerbs = map[string]bool{
38+
"proxy": true,
39+
"redirect": true,
40+
"watch": true,
41+
}
42+
43+
// KindFromRequest returns Kind if Kind can be extracted from the request. Otherwise, the empty string.
44+
func KindFromRequest(req http.Request) string {
45+
// TODO: find a way to keep this code's assumptions about paths up to date with changes in the code. Maybe instead
46+
// of directly adding handler's code to the master's Mux, have a function which forces the structure when adding
47+
// them.
48+
parts := splitPath(req.URL.Path)
49+
if len(parts) > 2 && parts[0] == "api" {
50+
if _, ok := specialVerbs[parts[2]]; ok {
51+
if len(parts) > 3 {
52+
return parts[3]
53+
}
54+
} else {
55+
return parts[2]
56+
}
57+
}
58+
return ""
59+
}
60+
61+
// IsReadOnlyReq() is true for any (or at least many) request which has no observable
62+
// side effects on state of apiserver (though there may be internal side effects like
63+
// caching and logging).
64+
func IsReadOnlyReq(req http.Request) bool {
65+
if req.Method == "GET" {
66+
// TODO: add OPTIONS and HEAD if we ever support those.
67+
return true
68+
}
69+
return false
70+
}
71+
3372
// ReadOnly passes all GET requests on to handler, and returns an error on all other requests.
3473
func ReadOnly(handler http.Handler) http.Handler {
3574
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
36-
if req.Method == "GET" {
75+
if IsReadOnlyReq(*req) {
3776
handler.ServeHTTP(w, req)
3877
return
3978
}
@@ -143,6 +182,17 @@ func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attrib
143182
attribs.User = user
144183
}
145184

185+
attribs.ReadOnly = IsReadOnlyReq(*req)
186+
187+
// If a path follows the conventions of the REST object store, then
188+
// we can extract the object Kind. Otherwise, not.
189+
attribs.Kind = KindFromRequest(*req)
190+
191+
// If the request specifies a namespace, then the namespace is filled in.
192+
// Assumes there is no empty string namespace. Unspecified results
193+
// in empty (does not understand defaulting rules.)
194+
attribs.Namespace = req.URL.Query().Get("namespace")
195+
146196
return &attribs
147197
}
148198

pkg/auth/authorizer/interfaces.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,20 @@ import (
2323
// Attributes is an interface used by an Authorizer to get information about a request
2424
// that is used to make an authorization decision.
2525
type Attributes interface {
26+
// The user string which the request was authenticated as, or empty if
27+
// no authentication occured and the request was allowed to proceed.
2628
GetUserName() string
29+
// TODO: add groups, e.g. GetGroups() []string
30+
31+
// When IsReadOnly() == true, the request has no side effects, other than
32+
// caching, logging, and other incidentals.
33+
IsReadOnly() bool
34+
35+
// The namespace of the object, if a request is for a REST object.
36+
GetNamespace() string
37+
38+
// The kind of object, if a request is for a REST object.
39+
GetKind() string
2740
}
2841

2942
// Authorizer makes an authorization decision based on information gained by making
@@ -35,9 +48,24 @@ type Authorizer interface {
3548

3649
// AttributesRecord implements Attributes interface.
3750
type AttributesRecord struct {
38-
User user.Info
51+
User user.Info
52+
ReadOnly bool
53+
Namespace string
54+
Kind string
3955
}
4056

4157
func (a *AttributesRecord) GetUserName() string {
4258
return a.User.GetName()
4359
}
60+
61+
func (a *AttributesRecord) IsReadOnly() bool {
62+
return a.ReadOnly
63+
}
64+
65+
func (a *AttributesRecord) GetNamespace() string {
66+
return a.Namespace
67+
}
68+
69+
func (a *AttributesRecord) GetKind() string {
70+
return a.Kind
71+
}

pkg/master/master.go

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ type Config struct {
6767
APIPrefix string
6868
CorsAllowedOriginList util.StringList
6969
TokenAuthFile string
70-
AuthorizationMode string
71-
AuthorizerForTesting authorizer.Authorizer
70+
Authorizer authorizer.Authorizer
7271

7372
// Number of masters running; all masters must be started with the
7473
// same value for this field. (Numbers > 1 currently untested.)
@@ -104,7 +103,7 @@ type Master struct {
104103
apiPrefix string
105104
corsAllowedOriginList util.StringList
106105
tokenAuthFile string
107-
authorizationzMode string
106+
authorizer authorizer.Authorizer
108107
masterCount int
109108

110109
// "Outputs"
@@ -227,7 +226,7 @@ func New(c *Config) *Master {
227226
apiPrefix: c.APIPrefix,
228227
corsAllowedOriginList: c.CorsAllowedOriginList,
229228
tokenAuthFile: c.TokenAuthFile,
230-
authorizationzMode: c.AuthorizationMode,
229+
authorizer: c.Authorizer,
231230

232231
masterCount: c.MasterCount,
233232
readOnlyServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadOnlyPort))),
@@ -319,19 +318,8 @@ func (m *Master) init(c *Config) {
319318
handler = apiserver.CORS(handler, allowedOriginRegexps, nil, nil, "true")
320319
}
321320

322-
// Install Authorizer
323-
var authorizer authorizer.Authorizer
324-
if c.AuthorizerForTesting != nil {
325-
authorizer = c.AuthorizerForTesting
326-
} else {
327-
var err error
328-
authorizer, err = apiserver.NewAuthorizerFromAuthorizationConfig(m.authorizationzMode)
329-
if err != nil {
330-
glog.Fatal(err)
331-
}
332-
}
333321
attributeGetter := apiserver.NewRequestAttributeGetter(userContexts)
334-
handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, authorizer)
322+
handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, m.authorizer)
335323

336324
// Install Authenticator
337325
if authenticator != nil {

0 commit comments

Comments
 (0)