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
91feb345
Commit
91feb345
authored
May 14, 2018
by
Mike Danese
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
implement service account token projection
parent
46d2b471
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
553 additions
and
12 deletions
+553
-12
BUILD
pkg/controller/volume/attachdetach/BUILD
+1
-0
attach_detach_controller.go
...ontroller/volume/attachdetach/attach_detach_controller.go
+7
-0
BUILD
pkg/controller/volume/expand/BUILD
+1
-0
expand_controller.go
pkg/controller/volume/expand/expand_controller.go
+7
-0
BUILD
pkg/controller/volume/persistentvolume/BUILD
+1
-0
volume_host.go
pkg/controller/volume/persistentvolume/volume_host.go
+9
-2
BUILD
pkg/kubelet/BUILD
+4
-0
kubelet.go
pkg/kubelet/kubelet.go
+4
-1
kubelet_test.go
pkg/kubelet/kubelet_test.go
+2
-1
runonce_test.go
pkg/kubelet/runonce_test.go
+1
-1
BUILD
pkg/kubelet/token/BUILD
+40
-0
OWNERS
pkg/kubelet/token/OWNERS
+6
-0
token_manager.go
pkg/kubelet/token/token_manager.go
+147
-0
token_manager_test.go
pkg/kubelet/token/token_manager_test.go
+223
-0
volume_host.go
pkg/kubelet/volume_host.go
+9
-0
BUILD
pkg/volume/BUILD
+1
-0
plugins.go
pkg/volume/plugins.go
+3
-0
BUILD
pkg/volume/projected/BUILD
+3
-0
projected.go
pkg/volume/projected/projected.go
+42
-7
BUILD
pkg/volume/testing/BUILD
+1
-0
testing.go
pkg/volume/testing/testing.go
+7
-0
admission.go
plugin/pkg/admission/serviceaccount/admission.go
+9
-0
admission_test.go
plugin/pkg/admission/serviceaccount/admission_test.go
+25
-0
No files found.
pkg/controller/volume/attachdetach/BUILD
View file @
91feb345
...
...
@@ -25,6 +25,7 @@ go_library(
"//pkg/volume/util/operationexecutor:go_default_library",
"//pkg/volume/util/volumepathhandler:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
...
...
pkg/controller/volume/attachdetach/attach_detach_controller.go
View file @
91feb345
...
...
@@ -24,6 +24,7 @@ import (
"time"
"github.com/golang/glog"
authenticationv1
"k8s.io/api/authentication/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
...
...
@@ -586,6 +587,12 @@ func (adc *attachDetachController) GetConfigMapFunc() func(namespace, name strin
}
}
func
(
adc
*
attachDetachController
)
GetServiceAccountTokenFunc
()
func
(
_
,
_
string
,
_
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
func
(
_
,
_
string
,
_
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
nil
,
fmt
.
Errorf
(
"GetServiceAccountToken unsupported in attachDetachController"
)
}
}
func
(
adc
*
attachDetachController
)
GetExec
(
pluginName
string
)
mount
.
Exec
{
return
mount
.
NewOsExec
()
}
...
...
pkg/controller/volume/expand/BUILD
View file @
91feb345
...
...
@@ -25,6 +25,7 @@ go_library(
"//pkg/volume/util/operationexecutor:go_default_library",
"//pkg/volume/util/volumepathhandler:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
...
...
pkg/controller/volume/expand/expand_controller.go
View file @
91feb345
...
...
@@ -26,6 +26,7 @@ import (
"github.com/golang/glog"
authenticationv1
"k8s.io/api/authentication/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/runtime"
...
...
@@ -295,6 +296,12 @@ func (expc *expandController) GetConfigMapFunc() func(namespace, name string) (*
}
}
func
(
expc
*
expandController
)
GetServiceAccountTokenFunc
()
func
(
_
,
_
string
,
_
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
func
(
_
,
_
string
,
_
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
nil
,
fmt
.
Errorf
(
"GetServiceAccountToken unsupported in expandController"
)
}
}
func
(
expc
*
expandController
)
GetNodeLabels
()
(
map
[
string
]
string
,
error
)
{
return
nil
,
fmt
.
Errorf
(
"GetNodeLabels unsupported in expandController"
)
}
...
...
pkg/controller/volume/persistentvolume/BUILD
View file @
91feb345
...
...
@@ -34,6 +34,7 @@ go_library(
"//pkg/volume/util:go_default_library",
"//pkg/volume/util/recyclerclient:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/storage/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
...
...
pkg/controller/volume/persistentvolume/volume_host.go
View file @
91feb345
...
...
@@ -20,6 +20,7 @@ import (
"fmt"
"net"
authenticationv1
"k8s.io/api/authentication/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
clientset
"k8s.io/client-go/kubernetes"
...
...
@@ -94,18 +95,24 @@ func (ctrl *PersistentVolumeController) GetNodeAllocatable() (v1.ResourceList, e
return
v1
.
ResourceList
{},
nil
}
func
(
adc
*
PersistentVolumeController
)
GetSecretFunc
()
func
(
namespace
,
name
string
)
(
*
v1
.
Secret
,
error
)
{
func
(
ctrl
*
PersistentVolumeController
)
GetSecretFunc
()
func
(
namespace
,
name
string
)
(
*
v1
.
Secret
,
error
)
{
return
func
(
_
,
_
string
)
(
*
v1
.
Secret
,
error
)
{
return
nil
,
fmt
.
Errorf
(
"GetSecret unsupported in PersistentVolumeController"
)
}
}
func
(
adc
*
PersistentVolumeController
)
GetConfigMapFunc
()
func
(
namespace
,
name
string
)
(
*
v1
.
ConfigMap
,
error
)
{
func
(
ctrl
*
PersistentVolumeController
)
GetConfigMapFunc
()
func
(
namespace
,
name
string
)
(
*
v1
.
ConfigMap
,
error
)
{
return
func
(
_
,
_
string
)
(
*
v1
.
ConfigMap
,
error
)
{
return
nil
,
fmt
.
Errorf
(
"GetConfigMap unsupported in PersistentVolumeController"
)
}
}
func
(
ctrl
*
PersistentVolumeController
)
GetServiceAccountTokenFunc
()
func
(
_
,
_
string
,
_
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
func
(
_
,
_
string
,
_
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
nil
,
fmt
.
Errorf
(
"GetServiceAccountToken unsupported in PersistentVolumeController"
)
}
}
func
(
adc
*
PersistentVolumeController
)
GetExec
(
pluginName
string
)
mount
.
Exec
{
return
mount
.
NewOsExec
()
}
...
...
pkg/kubelet/BUILD
View file @
91feb345
...
...
@@ -80,6 +80,7 @@ go_library(
"//pkg/kubelet/stats:go_default_library",
"//pkg/kubelet/status:go_default_library",
"//pkg/kubelet/sysctl:go_default_library",
"//pkg/kubelet/token:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/kubelet/util:go_default_library",
"//pkg/kubelet/util/format:go_default_library",
...
...
@@ -112,6 +113,7 @@ go_library(
"//vendor/github.com/google/cadvisor/events:go_default_library",
"//vendor/github.com/google/cadvisor/info/v1:go_default_library",
"//vendor/github.com/google/cadvisor/info/v2:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
...
...
@@ -190,6 +192,7 @@ go_test(
"//pkg/kubelet/stats:go_default_library",
"//pkg/kubelet/status:go_default_library",
"//pkg/kubelet/status/testing:go_default_library",
"//pkg/kubelet/token:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/kubelet/util/queue:go_default_library",
"//pkg/kubelet/util/sliceutils:go_default_library",
...
...
@@ -281,6 +284,7 @@ filegroup(
"//pkg/kubelet/stats:all-srcs",
"//pkg/kubelet/status:all-srcs",
"//pkg/kubelet/sysctl:all-srcs",
"//pkg/kubelet/token:all-srcs",
"//pkg/kubelet/types:all-srcs",
"//pkg/kubelet/util:all-srcs",
"//pkg/kubelet/volumemanager:all-srcs",
...
...
pkg/kubelet/kubelet.go
View file @
91feb345
...
...
@@ -90,6 +90,7 @@ import (
"k8s.io/kubernetes/pkg/kubelet/stats"
"k8s.io/kubernetes/pkg/kubelet/status"
"k8s.io/kubernetes/pkg/kubelet/sysctl"
"k8s.io/kubernetes/pkg/kubelet/token"
kubetypes
"k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/kubelet/util/format"
"k8s.io/kubernetes/pkg/kubelet/util/manager"
...
...
@@ -778,8 +779,10 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
containerRefManager
,
kubeDeps
.
Recorder
)
tokenManager
:=
token
.
NewManager
(
kubeDeps
.
KubeClient
.
CoreV1
())
klet
.
volumePluginMgr
,
err
=
NewInitializedVolumePluginMgr
(
klet
,
secretManager
,
configMapManager
,
kubeDeps
.
VolumePlugins
,
kubeDeps
.
DynamicPluginProber
)
NewInitializedVolumePluginMgr
(
klet
,
secretManager
,
configMapManager
,
tokenManager
,
kubeDeps
.
VolumePlugins
,
kubeDeps
.
DynamicPluginProber
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
pkg/kubelet/kubelet_test.go
View file @
91feb345
...
...
@@ -61,6 +61,7 @@ import (
"k8s.io/kubernetes/pkg/kubelet/stats"
"k8s.io/kubernetes/pkg/kubelet/status"
statustest
"k8s.io/kubernetes/pkg/kubelet/status/testing"
"k8s.io/kubernetes/pkg/kubelet/token"
kubetypes
"k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/kubelet/util/queue"
kubeletvolume
"k8s.io/kubernetes/pkg/kubelet/volumemanager"
...
...
@@ -325,7 +326,7 @@ func newTestKubeletWithImageList(
var
prober
volume
.
DynamicPluginProber
=
nil
// TODO (#51147) inject mock
kubelet
.
volumePluginMgr
,
err
=
NewInitializedVolumePluginMgr
(
kubelet
,
kubelet
.
secretManager
,
kubelet
.
configMapManager
,
allPlugins
,
prober
)
NewInitializedVolumePluginMgr
(
kubelet
,
kubelet
.
secretManager
,
kubelet
.
configMapManager
,
token
.
NewManager
(
kubelet
.
kubeClient
.
CoreV1
()),
allPlugins
,
prober
)
require
.
NoError
(
t
,
err
,
"Failed to initialize VolumePluginMgr"
)
kubelet
.
mounter
=
&
mount
.
FakeMounter
{}
...
...
pkg/kubelet/runonce_test.go
View file @
91feb345
...
...
@@ -90,7 +90,7 @@ func TestRunOnce(t *testing.T) {
plug
:=
&
volumetest
.
FakeVolumePlugin
{
PluginName
:
"fake"
,
Host
:
nil
}
kb
.
volumePluginMgr
,
err
=
NewInitializedVolumePluginMgr
(
kb
,
fakeSecretManager
,
fakeConfigMapManager
,
[]
volume
.
VolumePlugin
{
plug
},
nil
/* prober */
)
NewInitializedVolumePluginMgr
(
kb
,
fakeSecretManager
,
fakeConfigMapManager
,
nil
,
[]
volume
.
VolumePlugin
{
plug
},
nil
/* prober */
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to initialize VolumePluginMgr: %v"
,
err
)
}
...
...
pkg/kubelet/token/BUILD
0 → 100644
View file @
91feb345
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_library(
name = "go_default_library",
srcs = ["token_manager.go"],
importpath = "k8s.io/kubernetes/pkg/kubelet/token",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["token_manager_test.go"],
embed = [":go_default_library"],
deps = [
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
],
)
pkg/kubelet/token/OWNERS
0 → 100644
View file @
91feb345
approvers:
- mikedanese
reviewers:
- mikedanese
- awly
- tallclair
pkg/kubelet/token/token_manager.go
0 → 100644
View file @
91feb345
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package token implements a manager of serviceaccount tokens for pods running
// on the node.
package
token
import
(
"fmt"
"sync"
"time"
"github.com/golang/glog"
authenticationv1
"k8s.io/api/authentication/v1"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/wait"
corev1
"k8s.io/client-go/kubernetes/typed/core/v1"
)
const
(
maxTTL
=
24
*
time
.
Hour
gcPeriod
=
time
.
Minute
)
// NewManager returns a new token manager.
func
NewManager
(
c
corev1
.
CoreV1Interface
)
*
Manager
{
m
:=
&
Manager
{
getToken
:
func
(
name
,
namespace
string
,
tr
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
c
.
ServiceAccounts
(
namespace
)
.
CreateToken
(
name
,
tr
)
},
cache
:
make
(
map
[
string
]
*
authenticationv1
.
TokenRequest
),
clock
:
clock
.
RealClock
{},
}
go
wait
.
Forever
(
m
.
cleanup
,
gcPeriod
)
return
m
}
// Manager manages service account tokens for pods.
type
Manager
struct
{
// cacheMutex guards the cache
cacheMutex
sync
.
RWMutex
cache
map
[
string
]
*
authenticationv1
.
TokenRequest
// mocked for testing
getToken
func
(
name
,
namespace
string
,
tr
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
clock
clock
.
Clock
}
// GetServiceAccountToken gets a service account token for a pod from cache or
// from the TokenRequest API. This process is as follows:
// * Check the cache for the current token request.
// * If the token exists and does not require a refresh, return the current token.
// * Attempt to refresh the token.
// * If the token is refreshed successfully, save it in the cache and return the token.
// * If refresh fails and the old token is still valid, log an error and return the old token.
// * If refresh fails and the old token is no longer valid, return an error
func
(
m
*
Manager
)
GetServiceAccountToken
(
name
,
namespace
string
,
tr
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
key
:=
keyFunc
(
name
,
namespace
,
tr
)
ctr
,
ok
:=
m
.
get
(
key
)
if
ok
&&
!
m
.
requiresRefresh
(
ctr
)
{
return
ctr
,
nil
}
tr
,
err
:=
m
.
getToken
(
name
,
namespace
,
tr
)
if
err
!=
nil
{
switch
{
case
!
ok
:
return
nil
,
fmt
.
Errorf
(
"failed to fetch token: %v"
,
err
)
case
m
.
expired
(
ctr
)
:
return
nil
,
fmt
.
Errorf
(
"token %s expired and refresh failed: %v"
,
key
,
err
)
default
:
glog
.
Errorf
(
"couldn't update token %s: %v"
,
key
,
err
)
return
ctr
,
nil
}
}
m
.
set
(
key
,
tr
)
return
tr
,
nil
}
func
(
m
*
Manager
)
cleanup
()
{
m
.
cacheMutex
.
Lock
()
defer
m
.
cacheMutex
.
Unlock
()
for
k
,
tr
:=
range
m
.
cache
{
if
m
.
expired
(
tr
)
{
delete
(
m
.
cache
,
k
)
}
}
}
func
(
m
*
Manager
)
get
(
key
string
)
(
*
authenticationv1
.
TokenRequest
,
bool
)
{
m
.
cacheMutex
.
RLock
()
defer
m
.
cacheMutex
.
RUnlock
()
ctr
,
ok
:=
m
.
cache
[
key
]
return
ctr
,
ok
}
func
(
m
*
Manager
)
set
(
key
string
,
tr
*
authenticationv1
.
TokenRequest
)
{
m
.
cacheMutex
.
Lock
()
defer
m
.
cacheMutex
.
Unlock
()
m
.
cache
[
key
]
=
tr
}
func
(
m
*
Manager
)
expired
(
t
*
authenticationv1
.
TokenRequest
)
bool
{
return
m
.
clock
.
Now
()
.
After
(
t
.
Status
.
ExpirationTimestamp
.
Time
)
}
// requiresRefresh returns true if the token is older than 80% of its total
// ttl, or if the token is older than 24 hours.
func
(
m
*
Manager
)
requiresRefresh
(
tr
*
authenticationv1
.
TokenRequest
)
bool
{
if
tr
.
Spec
.
ExpirationSeconds
==
nil
{
glog
.
Errorf
(
"expiration seconds was nil for tr: %#v"
,
tr
)
return
false
}
now
:=
m
.
clock
.
Now
()
exp
:=
tr
.
Status
.
ExpirationTimestamp
.
Time
iat
:=
exp
.
Add
(
-
1
*
time
.
Duration
(
*
tr
.
Spec
.
ExpirationSeconds
)
*
time
.
Second
)
if
now
.
After
(
iat
.
Add
(
maxTTL
))
{
return
true
}
// Require a refresh if within 20% of the TTL from the expiration time.
if
now
.
After
(
exp
.
Add
(
-
1
*
time
.
Duration
((
*
tr
.
Spec
.
ExpirationSeconds
*
20
)
/
100
)
*
time
.
Second
))
{
return
true
}
return
false
}
// keys should be nonconfidential and safe to log
func
keyFunc
(
name
,
namespace
string
,
tr
*
authenticationv1
.
TokenRequest
)
string
{
return
fmt
.
Sprintf
(
"%q/%q/%#v"
,
name
,
namespace
,
tr
.
Spec
)
}
pkg/kubelet/token/token_manager_test.go
0 → 100644
View file @
91feb345
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
token
import
(
"fmt"
"testing"
"time"
authenticationv1
"k8s.io/api/authentication/v1"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/clock"
)
func
TestTokenCachingAndExpiration
(
t
*
testing
.
T
)
{
type
suite
struct
{
clock
*
clock
.
FakeClock
tg
*
fakeTokenGetter
mgr
*
Manager
}
cases
:=
[]
struct
{
name
string
exp
time
.
Duration
f
func
(
t
*
testing
.
T
,
s
*
suite
)
}{
{
name
:
"rotate hour token expires in the last 12 minutes"
,
exp
:
time
.
Hour
,
f
:
func
(
t
*
testing
.
T
,
s
*
suite
)
{
s
.
clock
.
SetTime
(
s
.
clock
.
Now
()
.
Add
(
50
*
time
.
Minute
))
if
_
,
err
:=
s
.
mgr
.
GetServiceAccountToken
(
"a"
,
"b"
,
&
authenticationv1
.
TokenRequest
{});
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
s
.
tg
.
count
!=
2
{
t
.
Fatalf
(
"expected token to be refreshed: call count was %d"
,
s
.
tg
.
count
)
}
},
},
{
name
:
"rotate 24 hour token that expires in 40 hours"
,
exp
:
40
*
time
.
Hour
,
f
:
func
(
t
*
testing
.
T
,
s
*
suite
)
{
s
.
clock
.
SetTime
(
s
.
clock
.
Now
()
.
Add
(
25
*
time
.
Hour
))
if
_
,
err
:=
s
.
mgr
.
GetServiceAccountToken
(
"a"
,
"b"
,
&
authenticationv1
.
TokenRequest
{});
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
s
.
tg
.
count
!=
2
{
t
.
Fatalf
(
"expected token to be refreshed: call count was %d"
,
s
.
tg
.
count
)
}
},
},
{
name
:
"rotate hour token fails, old token is still valid, doesn't error"
,
exp
:
time
.
Hour
,
f
:
func
(
t
*
testing
.
T
,
s
*
suite
)
{
s
.
clock
.
SetTime
(
s
.
clock
.
Now
()
.
Add
(
50
*
time
.
Minute
))
tg
:=
&
fakeTokenGetter
{
err
:
fmt
.
Errorf
(
"err"
),
}
s
.
mgr
.
getToken
=
tg
.
getToken
tr
,
err
:=
s
.
mgr
.
GetServiceAccountToken
(
"a"
,
"b"
,
&
authenticationv1
.
TokenRequest
{})
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
tr
.
Status
.
Token
!=
"foo"
{
t
.
Fatalf
(
"unexpected token: %v"
,
tr
.
Status
.
Token
)
}
},
},
}
for
_
,
c
:=
range
cases
{
t
.
Run
(
c
.
name
,
func
(
t
*
testing
.
T
)
{
clock
:=
clock
.
NewFakeClock
(
time
.
Time
{}
.
Add
(
30
*
24
*
time
.
Hour
))
expSecs
:=
int64
(
c
.
exp
.
Seconds
())
s
:=
&
suite
{
clock
:
clock
,
mgr
:
NewManager
(
nil
),
tg
:
&
fakeTokenGetter
{
tr
:
&
authenticationv1
.
TokenRequest
{
Spec
:
authenticationv1
.
TokenRequestSpec
{
ExpirationSeconds
:
&
expSecs
,
},
Status
:
authenticationv1
.
TokenRequestStatus
{
Token
:
"foo"
,
ExpirationTimestamp
:
metav1
.
Time
{
Time
:
clock
.
Now
()
.
Add
(
c
.
exp
)},
},
},
},
}
s
.
mgr
.
getToken
=
s
.
tg
.
getToken
s
.
mgr
.
clock
=
s
.
clock
if
_
,
err
:=
s
.
mgr
.
GetServiceAccountToken
(
"a"
,
"b"
,
&
authenticationv1
.
TokenRequest
{});
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
s
.
tg
.
count
!=
1
{
t
.
Fatalf
(
"unexpected client call, got: %d, want: 1"
,
s
.
tg
.
count
)
}
if
_
,
err
:=
s
.
mgr
.
GetServiceAccountToken
(
"a"
,
"b"
,
&
authenticationv1
.
TokenRequest
{});
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
s
.
tg
.
count
!=
1
{
t
.
Fatalf
(
"expected token to be served from cache: saw %d"
,
s
.
tg
.
count
)
}
c
.
f
(
t
,
s
)
})
}
}
func
TestRequiresRefresh
(
t
*
testing
.
T
)
{
start
:=
time
.
Now
()
cases
:=
[]
struct
{
now
,
exp
time
.
Time
expectRefresh
bool
}{
{
now
:
start
.
Add
(
10
*
time
.
Minute
),
exp
:
start
.
Add
(
60
*
time
.
Minute
),
expectRefresh
:
false
,
},
{
now
:
start
.
Add
(
50
*
time
.
Minute
),
exp
:
start
.
Add
(
60
*
time
.
Minute
),
expectRefresh
:
true
,
},
{
now
:
start
.
Add
(
25
*
time
.
Hour
),
exp
:
start
.
Add
(
60
*
time
.
Hour
),
expectRefresh
:
true
,
},
{
now
:
start
.
Add
(
70
*
time
.
Minute
),
exp
:
start
.
Add
(
60
*
time
.
Minute
),
expectRefresh
:
true
,
},
}
for
i
,
c
:=
range
cases
{
t
.
Run
(
fmt
.
Sprint
(
i
),
func
(
t
*
testing
.
T
)
{
clock
:=
clock
.
NewFakeClock
(
c
.
now
)
secs
:=
int64
(
c
.
exp
.
Sub
(
start
)
.
Seconds
())
tr
:=
&
authenticationv1
.
TokenRequest
{
Spec
:
authenticationv1
.
TokenRequestSpec
{
ExpirationSeconds
:
&
secs
,
},
Status
:
authenticationv1
.
TokenRequestStatus
{
ExpirationTimestamp
:
metav1
.
Time
{
Time
:
c
.
exp
},
},
}
mgr
:=
NewManager
(
nil
)
mgr
.
clock
=
clock
rr
:=
mgr
.
requiresRefresh
(
tr
)
if
rr
!=
c
.
expectRefresh
{
t
.
Fatalf
(
"unexpected requiresRefresh result, got: %v, want: %v"
,
rr
,
c
.
expectRefresh
)
}
})
}
}
type
fakeTokenGetter
struct
{
count
int
tr
*
authenticationv1
.
TokenRequest
err
error
}
func
(
ftg
*
fakeTokenGetter
)
getToken
(
name
,
namespace
string
,
tr
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
ftg
.
count
++
return
ftg
.
tr
,
ftg
.
err
}
func
TestCleanup
(
t
*
testing
.
T
)
{
cases
:=
[]
struct
{
name
string
relativeExp
time
.
Duration
expectedCacheSize
int
}{
{
name
:
"don't cleanup unexpired tokens"
,
relativeExp
:
-
1
*
time
.
Hour
,
expectedCacheSize
:
0
,
},
{
name
:
"cleanup expired tokens"
,
relativeExp
:
time
.
Hour
,
expectedCacheSize
:
1
,
},
}
for
_
,
c
:=
range
cases
{
t
.
Run
(
c
.
name
,
func
(
t
*
testing
.
T
)
{
clock
:=
clock
.
NewFakeClock
(
time
.
Time
{}
.
Add
(
24
*
time
.
Hour
))
mgr
:=
NewManager
(
nil
)
mgr
.
clock
=
clock
mgr
.
set
(
"key"
,
&
authenticationv1
.
TokenRequest
{
Status
:
authenticationv1
.
TokenRequestStatus
{
ExpirationTimestamp
:
metav1
.
Time
{
Time
:
mgr
.
clock
.
Now
()
.
Add
(
c
.
relativeExp
)},
},
})
mgr
.
cleanup
()
if
got
,
want
:=
len
(
mgr
.
cache
),
c
.
expectedCacheSize
;
got
!=
want
{
t
.
Fatalf
(
"unexpected number of cache entries after cleanup, got: %d, want: %d"
,
got
,
want
)
}
})
}
}
pkg/kubelet/volume_host.go
View file @
91feb345
...
...
@@ -23,6 +23,7 @@ import (
"github.com/golang/glog"
authenticationv1
"k8s.io/api/authentication/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
utilfeature
"k8s.io/apiserver/pkg/util/feature"
...
...
@@ -34,6 +35,7 @@ import (
"k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/mountpod"
"k8s.io/kubernetes/pkg/kubelet/secret"
"k8s.io/kubernetes/pkg/kubelet/token"
"k8s.io/kubernetes/pkg/util/io"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
...
...
@@ -50,6 +52,7 @@ func NewInitializedVolumePluginMgr(
kubelet
*
Kubelet
,
secretManager
secret
.
Manager
,
configMapManager
configmap
.
Manager
,
tokenManager
*
token
.
Manager
,
plugins
[]
volume
.
VolumePlugin
,
prober
volume
.
DynamicPluginProber
)
(
*
volume
.
VolumePluginMgr
,
error
)
{
...
...
@@ -62,6 +65,7 @@ func NewInitializedVolumePluginMgr(
volumePluginMgr
:
volume
.
VolumePluginMgr
{},
secretManager
:
secretManager
,
configMapManager
:
configMapManager
,
tokenManager
:
tokenManager
,
mountPodManager
:
mountPodManager
,
}
...
...
@@ -85,6 +89,7 @@ type kubeletVolumeHost struct {
kubelet
*
Kubelet
volumePluginMgr
volume
.
VolumePluginMgr
secretManager
secret
.
Manager
tokenManager
*
token
.
Manager
configMapManager
configmap
.
Manager
mountPodManager
mountpod
.
Manager
}
...
...
@@ -191,6 +196,10 @@ func (kvh *kubeletVolumeHost) GetConfigMapFunc() func(namespace, name string) (*
return
kvh
.
configMapManager
.
GetConfigMap
}
func
(
kvh
*
kubeletVolumeHost
)
GetServiceAccountTokenFunc
()
func
(
namespace
,
name
string
,
tr
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
kvh
.
tokenManager
.
GetServiceAccountToken
}
func
(
kvh
*
kubeletVolumeHost
)
GetNodeLabels
()
(
map
[
string
]
string
,
error
)
{
node
,
err
:=
kvh
.
kubelet
.
GetNode
()
if
err
!=
nil
{
...
...
pkg/volume/BUILD
View file @
91feb345
...
...
@@ -56,6 +56,7 @@ go_library(
"//pkg/volume/util/fs:go_default_library",
"//pkg/volume/util/recyclerclient:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
...
...
pkg/volume/plugins.go
View file @
91feb345
...
...
@@ -23,6 +23,7 @@ import (
"sync"
"github.com/golang/glog"
authenticationv1
"k8s.io/api/authentication/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
...
...
@@ -336,6 +337,8 @@ type VolumeHost interface {
// Returns a function that returns a configmap.
GetConfigMapFunc
()
func
(
namespace
,
name
string
)
(
*
v1
.
ConfigMap
,
error
)
GetServiceAccountTokenFunc
()
func
(
namespace
,
name
string
,
tr
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
// Returns an interface that should be used to execute any utilities in volume plugins
GetExec
(
pluginName
string
)
mount
.
Exec
...
...
pkg/volume/projected/BUILD
View file @
91feb345
...
...
@@ -28,6 +28,7 @@ go_library(
srcs = ["projected.go"],
importpath = "k8s.io/kubernetes/pkg/volume/projected",
deps = [
"//pkg/features:go_default_library",
"//pkg/util/strings:go_default_library",
"//pkg/volume:go_default_library",
"//pkg/volume/configmap:go_default_library",
...
...
@@ -35,11 +36,13 @@ go_library(
"//pkg/volume/secret:go_default_library",
"//pkg/volume/util:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)
...
...
pkg/volume/projected/projected.go
View file @
91feb345
...
...
@@ -21,18 +21,22 @@ import (
"sort"
"strings"
"github.com/golang/glog
"
authenticationv1
"k8s.io/api/authentication/v1
"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
utilerrors
"k8s.io/apimachinery/pkg/util/errors"
utilfeature
"k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features"
utilstrings
"k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/configmap"
"k8s.io/kubernetes/pkg/volume/downwardapi"
"k8s.io/kubernetes/pkg/volume/secret"
volumeutil
"k8s.io/kubernetes/pkg/volume/util"
"github.com/golang/glog"
)
// ProbeVolumePlugins is the entry point for plugin detection in a package.
...
...
@@ -45,9 +49,10 @@ const (
)
type
projectedPlugin
struct
{
host
volume
.
VolumeHost
getSecret
func
(
namespace
,
name
string
)
(
*
v1
.
Secret
,
error
)
getConfigMap
func
(
namespace
,
name
string
)
(
*
v1
.
ConfigMap
,
error
)
host
volume
.
VolumeHost
getSecret
func
(
namespace
,
name
string
)
(
*
v1
.
Secret
,
error
)
getConfigMap
func
(
namespace
,
name
string
)
(
*
v1
.
ConfigMap
,
error
)
getServiceAccountToken
func
(
namespace
,
name
string
,
tr
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
}
var
_
volume
.
VolumePlugin
=
&
projectedPlugin
{}
...
...
@@ -70,6 +75,7 @@ func (plugin *projectedPlugin) Init(host volume.VolumeHost) error {
plugin
.
host
=
host
plugin
.
getSecret
=
host
.
GetSecretFunc
()
plugin
.
getConfigMap
=
host
.
GetConfigMapFunc
()
plugin
.
getServiceAccountToken
=
host
.
GetServiceAccountTokenFunc
()
return
nil
}
...
...
@@ -236,7 +242,8 @@ func (s *projectedVolumeMounter) collectData() (map[string]volumeutil.FileProjec
errlist
:=
[]
error
{}
payload
:=
make
(
map
[
string
]
volumeutil
.
FileProjection
)
for
_
,
source
:=
range
s
.
source
.
Sources
{
if
source
.
Secret
!=
nil
{
switch
{
case
source
.
Secret
!=
nil
:
optional
:=
source
.
Secret
.
Optional
!=
nil
&&
*
source
.
Secret
.
Optional
secretapi
,
err
:=
s
.
plugin
.
getSecret
(
s
.
pod
.
Namespace
,
source
.
Secret
.
Name
)
if
err
!=
nil
{
...
...
@@ -261,7 +268,7 @@ func (s *projectedVolumeMounter) collectData() (map[string]volumeutil.FileProjec
for
k
,
v
:=
range
secretPayload
{
payload
[
k
]
=
v
}
}
else
if
source
.
ConfigMap
!=
nil
{
case
source
.
ConfigMap
!=
nil
:
optional
:=
source
.
ConfigMap
.
Optional
!=
nil
&&
*
source
.
ConfigMap
.
Optional
configMap
,
err
:=
s
.
plugin
.
getConfigMap
(
s
.
pod
.
Namespace
,
source
.
ConfigMap
.
Name
)
if
err
!=
nil
{
...
...
@@ -286,7 +293,7 @@ func (s *projectedVolumeMounter) collectData() (map[string]volumeutil.FileProjec
for
k
,
v
:=
range
configMapPayload
{
payload
[
k
]
=
v
}
}
else
if
source
.
DownwardAPI
!=
nil
{
case
source
.
DownwardAPI
!=
nil
:
downwardAPIPayload
,
err
:=
downwardapi
.
CollectData
(
source
.
DownwardAPI
.
Items
,
s
.
pod
,
s
.
plugin
.
host
,
s
.
source
.
DefaultMode
)
if
err
!=
nil
{
errlist
=
append
(
errlist
,
err
)
...
...
@@ -295,6 +302,34 @@ func (s *projectedVolumeMounter) collectData() (map[string]volumeutil.FileProjec
for
k
,
v
:=
range
downwardAPIPayload
{
payload
[
k
]
=
v
}
case
source
.
ServiceAccountToken
!=
nil
:
if
!
utilfeature
.
DefaultFeatureGate
.
Enabled
(
features
.
TokenRequestProjection
)
{
errlist
=
append
(
errlist
,
fmt
.
Errorf
(
"pod request ServiceAccountToken projection but the TokenRequestProjection feature was not enabled"
))
continue
}
tp
:=
source
.
ServiceAccountToken
tr
,
err
:=
s
.
plugin
.
getServiceAccountToken
(
s
.
pod
.
Namespace
,
s
.
pod
.
Spec
.
ServiceAccountName
,
&
authenticationv1
.
TokenRequest
{
Spec
:
authenticationv1
.
TokenRequestSpec
{
Audiences
:
[]
string
{
tp
.
Audience
,
},
ExpirationSeconds
:
tp
.
ExpirationSeconds
,
BoundObjectRef
:
&
authenticationv1
.
BoundObjectReference
{
APIVersion
:
"v1"
,
Kind
:
"Pod"
,
Name
:
s
.
pod
.
Name
,
UID
:
s
.
pod
.
UID
,
},
},
})
if
err
!=
nil
{
errlist
=
append
(
errlist
,
err
)
continue
}
payload
[
tp
.
Path
]
=
volumeutil
.
FileProjection
{
Data
:
[]
byte
(
tr
.
Status
.
Token
),
Mode
:
0600
,
}
}
}
return
payload
,
utilerrors
.
NewAggregate
(
errlist
)
...
...
pkg/volume/testing/BUILD
View file @
91feb345
...
...
@@ -22,6 +22,7 @@ go_library(
"//pkg/volume/util/recyclerclient:go_default_library",
"//pkg/volume/util/volumepathhandler:go_default_library",
"//vendor/github.com/stretchr/testify/mock:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
...
...
pkg/volume/testing/testing.go
View file @
91feb345
...
...
@@ -27,6 +27,7 @@ import (
"testing"
"time"
authenticationv1
"k8s.io/api/authentication/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
...
...
@@ -183,6 +184,12 @@ func (f *fakeVolumeHost) GetConfigMapFunc() func(namespace, name string) (*v1.Co
}
}
func
(
f
*
fakeVolumeHost
)
GetServiceAccountTokenFunc
()
func
(
string
,
string
,
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
func
(
namespace
,
name
string
,
tr
*
authenticationv1
.
TokenRequest
)
(
*
authenticationv1
.
TokenRequest
,
error
)
{
return
f
.
kubeClient
.
CoreV1
()
.
ServiceAccounts
(
namespace
)
.
CreateToken
(
name
,
tr
)
}
}
func
(
f
*
fakeVolumeHost
)
GetNodeLabels
()
(
map
[
string
]
string
,
error
)
{
if
f
.
nodeLabels
==
nil
{
f
.
nodeLabels
=
map
[
string
]
string
{
"test-label"
:
"test-value"
}
...
...
plugin/pkg/admission/serviceaccount/admission.go
View file @
91feb345
...
...
@@ -208,6 +208,15 @@ func (s *serviceAccount) Validate(a admission.Attributes) (err error) {
if
hasSecrets
{
return
admission
.
NewForbidden
(
a
,
fmt
.
Errorf
(
"a mirror pod may not reference secrets"
))
}
for
_
,
v
:=
range
pod
.
Spec
.
Volumes
{
if
proj
:=
v
.
Projected
;
proj
!=
nil
{
for
_
,
projSource
:=
range
proj
.
Sources
{
if
projSource
.
ServiceAccountToken
!=
nil
{
return
admission
.
NewForbidden
(
a
,
fmt
.
Errorf
(
"a mirror pod may not use ServiceAccountToken volume projections"
))
}
}
}
}
return
nil
}
...
...
plugin/pkg/admission/serviceaccount/admission_test.go
View file @
91feb345
...
...
@@ -138,6 +138,31 @@ func TestRejectsMirrorPodWithSecretVolumes(t *testing.T) {
}
}
func
TestRejectsMirrorPodWithServiceAccountTokenVolumeProjections
(
t
*
testing
.
T
)
{
pod
:=
&
api
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Annotations
:
map
[
string
]
string
{
kubelet
.
ConfigMirrorAnnotationKey
:
"true"
,
},
},
Spec
:
api
.
PodSpec
{
Volumes
:
[]
api
.
Volume
{
{
VolumeSource
:
api
.
VolumeSource
{
Projected
:
&
api
.
ProjectedVolumeSource
{
Sources
:
[]
api
.
VolumeProjection
{{
ServiceAccountToken
:
&
api
.
ServiceAccountTokenProjection
{}}},
},
},
},
},
},
}
attrs
:=
admission
.
NewAttributesRecord
(
pod
,
nil
,
api
.
Kind
(
"Pod"
)
.
WithVersion
(
"version"
),
"myns"
,
"myname"
,
api
.
Resource
(
"pods"
)
.
WithVersion
(
"version"
),
""
,
admission
.
Create
,
nil
)
err
:=
NewServiceAccount
()
.
Admit
(
attrs
)
if
err
==
nil
{
t
.
Errorf
(
"Expected a mirror pod to be prevented from referencing a ServiceAccountToken volume projection"
)
}
}
func
TestAssignsDefaultServiceAccountAndToleratesMissingAPIToken
(
t
*
testing
.
T
)
{
ns
:=
"myns"
...
...
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