Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
0b9f1322
Unverified
Commit
0b9f1322
authored
Feb 22, 2019
by
Kubernetes Prow Robot
Committed by
GitHub
Feb 22, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #70302 from tallclair/authzcache
Don't cache rediculous subject access reviews
parents
b96378c0
d512173c
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
96 additions
and
51 deletions
+96
-51
webhook.go
...k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go
+25
-5
webhook_test.go
...o/apiserver/plugin/pkg/authorizer/webhook/webhook_test.go
+71
-46
No files found.
staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go
View file @
0b9f1322
...
@@ -39,7 +39,11 @@ var (
...
@@ -39,7 +39,11 @@ var (
groupVersions
=
[]
schema
.
GroupVersion
{
authorization
.
SchemeGroupVersion
}
groupVersions
=
[]
schema
.
GroupVersion
{
authorization
.
SchemeGroupVersion
}
)
)
const
retryBackoff
=
500
*
time
.
Millisecond
const
(
retryBackoff
=
500
*
time
.
Millisecond
// The maximum length of requester-controlled attributes to allow caching.
maxControlledAttrCacheSize
=
10000
)
// Ensure Webhook implements the authorizer.Authorizer interface.
// Ensure Webhook implements the authorizer.Authorizer interface.
var
_
authorizer
.
Authorizer
=
(
*
WebhookAuthorizer
)(
nil
)
var
_
authorizer
.
Authorizer
=
(
*
WebhookAuthorizer
)(
nil
)
...
@@ -193,10 +197,12 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision auth
...
@@ -193,10 +197,12 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision auth
return
w
.
decisionOnError
,
""
,
err
return
w
.
decisionOnError
,
""
,
err
}
}
r
.
Status
=
result
.
Status
r
.
Status
=
result
.
Status
if
r
.
Status
.
Allowed
{
if
shouldCache
(
attr
)
{
w
.
responseCache
.
Add
(
string
(
key
),
r
.
Status
,
w
.
authorizedTTL
)
if
r
.
Status
.
Allowed
{
}
else
{
w
.
responseCache
.
Add
(
string
(
key
),
r
.
Status
,
w
.
authorizedTTL
)
w
.
responseCache
.
Add
(
string
(
key
),
r
.
Status
,
w
.
unauthorizedTTL
)
}
else
{
w
.
responseCache
.
Add
(
string
(
key
),
r
.
Status
,
w
.
unauthorizedTTL
)
}
}
}
}
}
switch
{
switch
{
...
@@ -262,3 +268,17 @@ func (t *subjectAccessReviewClient) Create(subjectAccessReview *authorization.Su
...
@@ -262,3 +268,17 @@ func (t *subjectAccessReviewClient) Create(subjectAccessReview *authorization.Su
err
:=
t
.
w
.
RestClient
.
Post
()
.
Body
(
subjectAccessReview
)
.
Do
()
.
Into
(
result
)
err
:=
t
.
w
.
RestClient
.
Post
()
.
Body
(
subjectAccessReview
)
.
Do
()
.
Into
(
result
)
return
result
,
err
return
result
,
err
}
}
// shouldCache determines whether it is safe to cache the given request attributes. If the
// requester-controlled attributes are too large, this may be a DoS attempt, so we skip the cache.
func
shouldCache
(
attr
authorizer
.
Attributes
)
bool
{
controlledAttrSize
:=
int64
(
len
(
attr
.
GetNamespace
()))
+
int64
(
len
(
attr
.
GetVerb
()))
+
int64
(
len
(
attr
.
GetAPIGroup
()))
+
int64
(
len
(
attr
.
GetAPIVersion
()))
+
int64
(
len
(
attr
.
GetResource
()))
+
int64
(
len
(
attr
.
GetSubresource
()))
+
int64
(
len
(
attr
.
GetName
()))
+
int64
(
len
(
attr
.
GetPath
()))
return
controlledAttrSize
<
maxControlledAttrCacheSize
}
staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook_test.go
View file @
0b9f1322
...
@@ -28,6 +28,7 @@ import (
...
@@ -28,6 +28,7 @@ import (
"os"
"os"
"path/filepath"
"path/filepath"
"reflect"
"reflect"
"strings"
"testing"
"testing"
"text/template"
"text/template"
"time"
"time"
...
@@ -542,41 +543,6 @@ func TestWebhook(t *testing.T) {
...
@@ -542,41 +543,6 @@ func TestWebhook(t *testing.T) {
}
}
}
}
type
webhookCacheTestCase
struct
{
attr
authorizer
.
AttributesRecord
allow
bool
statusCode
int
expectedErr
bool
expectedAuthorized
bool
expectedCalls
int
}
func
testWebhookCacheCases
(
t
*
testing
.
T
,
serv
*
mockService
,
wh
*
WebhookAuthorizer
,
tests
[]
webhookCacheTestCase
)
{
for
i
,
test
:=
range
tests
{
serv
.
called
=
0
serv
.
allow
=
test
.
allow
serv
.
statusCode
=
test
.
statusCode
authorized
,
_
,
err
:=
wh
.
Authorize
(
test
.
attr
)
if
test
.
expectedErr
&&
err
==
nil
{
t
.
Errorf
(
"%d: Expected error"
,
i
)
continue
}
else
if
!
test
.
expectedErr
&&
err
!=
nil
{
t
.
Errorf
(
"%d: unexpected error: %v"
,
i
,
err
)
continue
}
if
test
.
expectedAuthorized
!=
(
authorized
==
authorizer
.
DecisionAllow
)
{
t
.
Errorf
(
"%d: expected authorized=%v, got %v"
,
i
,
test
.
expectedAuthorized
,
authorized
)
}
if
test
.
expectedCalls
!=
serv
.
called
{
t
.
Errorf
(
"%d: expected %d calls, got %d"
,
i
,
test
.
expectedCalls
,
serv
.
called
)
}
}
}
// TestWebhookCache verifies that error responses from the server are not
// TestWebhookCache verifies that error responses from the server are not
// cached, but successful responses are.
// cached, but successful responses are.
func
TestWebhookCache
(
t
*
testing
.
T
)
{
func
TestWebhookCache
(
t
*
testing
.
T
)
{
...
@@ -595,27 +561,86 @@ func TestWebhookCache(t *testing.T) {
...
@@ -595,27 +561,86 @@ func TestWebhookCache(t *testing.T) {
aliceAttr
:=
authorizer
.
AttributesRecord
{
User
:
&
user
.
DefaultInfo
{
Name
:
"alice"
}}
aliceAttr
:=
authorizer
.
AttributesRecord
{
User
:
&
user
.
DefaultInfo
{
Name
:
"alice"
}}
bobAttr
:=
authorizer
.
AttributesRecord
{
User
:
&
user
.
DefaultInfo
{
Name
:
"bob"
}}
bobAttr
:=
authorizer
.
AttributesRecord
{
User
:
&
user
.
DefaultInfo
{
Name
:
"bob"
}}
aliceRidiculousAttr
:=
authorizer
.
AttributesRecord
{
User
:
&
user
.
DefaultInfo
{
Name
:
"alice"
},
ResourceRequest
:
true
,
Verb
:
strings
.
Repeat
(
"v"
,
2000
),
APIGroup
:
strings
.
Repeat
(
"g"
,
2000
),
APIVersion
:
strings
.
Repeat
(
"a"
,
2000
),
Resource
:
strings
.
Repeat
(
"r"
,
2000
),
Name
:
strings
.
Repeat
(
"n"
,
2000
),
}
bobRidiculousAttr
:=
authorizer
.
AttributesRecord
{
User
:
&
user
.
DefaultInfo
{
Name
:
"bob"
},
ResourceRequest
:
true
,
Verb
:
strings
.
Repeat
(
"v"
,
2000
),
APIGroup
:
strings
.
Repeat
(
"g"
,
2000
),
APIVersion
:
strings
.
Repeat
(
"a"
,
2000
),
Resource
:
strings
.
Repeat
(
"r"
,
2000
),
Name
:
strings
.
Repeat
(
"n"
,
2000
),
}
type
webhookCacheTestCase
struct
{
name
string
attr
authorizer
.
AttributesRecord
allow
bool
statusCode
int
expectedErr
bool
expectedAuthorized
bool
expectedCalls
int
}
tests
:=
[]
webhookCacheTestCase
{
tests
:=
[]
webhookCacheTestCase
{
// server error and 429's retry
// server error and 429's retry
{
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
500
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
5
},
{
name
:
"server errors retry"
,
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
500
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
5
},
{
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
429
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
5
},
{
name
:
"429s retry"
,
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
429
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
5
},
// regular errors return errors but do not retry
// regular errors return errors but do not retry
{
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
404
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
1
},
{
name
:
"404 doesnt retry"
,
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
404
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
1
},
{
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
403
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
1
},
{
name
:
"403 doesnt retry"
,
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
403
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
1
},
{
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
401
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
1
},
{
name
:
"401 doesnt retry"
,
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
401
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
1
},
// successful responses are cached
// successful responses are cached
{
attr
:
aliceAttr
,
allow
:
true
,
statusCode
:
200
,
expectedErr
:
false
,
expectedAuthorized
:
true
,
expectedCalls
:
1
},
{
name
:
"alice successful request"
,
attr
:
aliceAttr
,
allow
:
true
,
statusCode
:
200
,
expectedErr
:
false
,
expectedAuthorized
:
true
,
expectedCalls
:
1
},
// later requests within the cache window don't hit the backend
// later requests within the cache window don't hit the backend
{
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
500
,
expectedErr
:
false
,
expectedAuthorized
:
true
,
expectedCalls
:
0
},
{
name
:
"alice cached request"
,
attr
:
aliceAttr
,
allow
:
false
,
statusCode
:
500
,
expectedErr
:
false
,
expectedAuthorized
:
true
,
expectedCalls
:
0
},
// a request with different attributes doesn't hit the cache
// a request with different attributes doesn't hit the cache
{
attr
:
bobAttr
,
allow
:
false
,
statusCode
:
500
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
5
},
{
name
:
"bob failed request"
,
attr
:
bobAttr
,
allow
:
false
,
statusCode
:
500
,
expectedErr
:
true
,
expectedAuthorized
:
false
,
expectedCalls
:
5
},
// successful response for other attributes is cached
// successful response for other attributes is cached
{
attr
:
bobAttr
,
allow
:
true
,
statusCode
:
200
,
expectedErr
:
false
,
expectedAuthorized
:
tru
e
,
expectedCalls
:
1
},
{
name
:
"bob unauthorized request"
,
attr
:
bobAttr
,
allow
:
false
,
statusCode
:
200
,
expectedErr
:
false
,
expectedAuthorized
:
fals
e
,
expectedCalls
:
1
},
// later requests within the cache window don't hit the backend
// later requests within the cache window don't hit the backend
{
attr
:
bobAttr
,
allow
:
false
,
statusCode
:
500
,
expectedErr
:
false
,
expectedAuthorized
:
true
,
expectedCalls
:
0
},
{
name
:
"bob unauthorized cached request"
,
attr
:
bobAttr
,
allow
:
false
,
statusCode
:
500
,
expectedErr
:
false
,
expectedAuthorized
:
false
,
expectedCalls
:
0
},
// ridiculous unauthorized requests are not cached.
{
name
:
"ridiculous unauthorized request"
,
attr
:
bobRidiculousAttr
,
allow
:
false
,
statusCode
:
200
,
expectedErr
:
false
,
expectedAuthorized
:
false
,
expectedCalls
:
1
},
// later ridiculous requests within the cache window still hit the backend
{
name
:
"ridiculous unauthorized request again"
,
attr
:
bobRidiculousAttr
,
allow
:
false
,
statusCode
:
200
,
expectedErr
:
false
,
expectedAuthorized
:
false
,
expectedCalls
:
1
},
// ridiculous authorized requests are not cached.
{
name
:
"ridiculous authorized request"
,
attr
:
aliceRidiculousAttr
,
allow
:
true
,
statusCode
:
200
,
expectedErr
:
false
,
expectedAuthorized
:
true
,
expectedCalls
:
1
},
// later ridiculous requests within the cache window still hit the backend
{
name
:
"ridiculous authorized request again"
,
attr
:
aliceRidiculousAttr
,
allow
:
true
,
statusCode
:
200
,
expectedErr
:
false
,
expectedAuthorized
:
true
,
expectedCalls
:
1
},
}
}
testWebhookCacheCases
(
t
,
serv
,
wh
,
tests
)
for
i
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
serv
.
called
=
0
serv
.
allow
=
test
.
allow
serv
.
statusCode
=
test
.
statusCode
authorized
,
_
,
err
:=
wh
.
Authorize
(
test
.
attr
)
if
test
.
expectedErr
&&
err
==
nil
{
t
.
Fatalf
(
"%d: Expected error"
,
i
)
}
else
if
!
test
.
expectedErr
&&
err
!=
nil
{
t
.
Fatalf
(
"%d: unexpected error: %v"
,
i
,
err
)
}
if
test
.
expectedAuthorized
!=
(
authorized
==
authorizer
.
DecisionAllow
)
{
t
.
Errorf
(
"%d: expected authorized=%v, got %v"
,
i
,
test
.
expectedAuthorized
,
authorized
)
}
if
test
.
expectedCalls
!=
serv
.
called
{
t
.
Errorf
(
"%d: expected %d calls, got %d"
,
i
,
test
.
expectedCalls
,
serv
.
called
)
}
})
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment