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
c06867ea
Commit
c06867ea
authored
Oct 07, 2018
by
Darren Shepherd
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Delete bootstrap controller
parent
93898713
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
0 additions
and
1469 deletions
+0
-1469
bootstrap.go
cmd/kube-controller-manager/app/bootstrap.go
+0
-52
controllermanager.go
cmd/kube-controller-manager/app/controllermanager.go
+0
-2
BUILD
pkg/controller/bootstrap/BUILD
+0
-78
bootstrapsigner.go
pkg/controller/bootstrap/bootstrapsigner.go
+0
-307
bootstrapsigner_test.go
pkg/controller/bootstrap/bootstrapsigner_test.go
+0
-165
common_test.go
pkg/controller/bootstrap/common_test.go
+0
-74
doc.go
pkg/controller/bootstrap/doc.go
+0
-20
jws.go
pkg/controller/bootstrap/jws.go
+0
-82
jws_test.go
pkg/controller/bootstrap/jws_test.go
+0
-69
tokencleaner.go
pkg/controller/bootstrap/tokencleaner.go
+0
-203
tokencleaner_test.go
pkg/controller/bootstrap/tokencleaner_test.go
+0
-102
util.go
pkg/controller/bootstrap/util.go
+0
-109
util_test.go
pkg/controller/bootstrap/util_test.go
+0
-206
No files found.
cmd/kube-controller-manager/app/bootstrap.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
app
import
(
"fmt"
"net/http"
"k8s.io/kubernetes/pkg/controller/bootstrap"
)
func
startBootstrapSignerController
(
ctx
ControllerContext
)
(
http
.
Handler
,
bool
,
error
)
{
bsc
,
err
:=
bootstrap
.
NewBootstrapSigner
(
ctx
.
ClientBuilder
.
ClientOrDie
(
"bootstrap-signer"
),
ctx
.
InformerFactory
.
Core
()
.
V1
()
.
Secrets
(),
ctx
.
InformerFactory
.
Core
()
.
V1
()
.
ConfigMaps
(),
bootstrap
.
DefaultBootstrapSignerOptions
(),
)
if
err
!=
nil
{
return
nil
,
true
,
fmt
.
Errorf
(
"error creating BootstrapSigner controller: %v"
,
err
)
}
go
bsc
.
Run
(
ctx
.
Stop
)
return
nil
,
true
,
nil
}
func
startTokenCleanerController
(
ctx
ControllerContext
)
(
http
.
Handler
,
bool
,
error
)
{
tcc
,
err
:=
bootstrap
.
NewTokenCleaner
(
ctx
.
ClientBuilder
.
ClientOrDie
(
"token-cleaner"
),
ctx
.
InformerFactory
.
Core
()
.
V1
()
.
Secrets
(),
bootstrap
.
DefaultTokenCleanerOptions
(),
)
if
err
!=
nil
{
return
nil
,
true
,
fmt
.
Errorf
(
"error creating TokenCleaner controller: %v"
,
err
)
}
go
tcc
.
Run
(
ctx
.
Stop
)
return
nil
,
true
,
nil
}
cmd/kube-controller-manager/app/controllermanager.go
View file @
c06867ea
...
@@ -359,8 +359,6 @@ func NewControllerInitializers(loopMode ControllerLoopMode) map[string]InitFunc
...
@@ -359,8 +359,6 @@ func NewControllerInitializers(loopMode ControllerLoopMode) map[string]InitFunc
controllers
[
"statefulset"
]
=
startStatefulSetController
controllers
[
"statefulset"
]
=
startStatefulSetController
controllers
[
"cronjob"
]
=
startCronJobController
controllers
[
"cronjob"
]
=
startCronJobController
controllers
[
"ttl"
]
=
startTTLController
controllers
[
"ttl"
]
=
startTTLController
controllers
[
"bootstrapsigner"
]
=
startBootstrapSignerController
controllers
[
"tokencleaner"
]
=
startTokenCleanerController
controllers
[
"nodeipam"
]
=
startNodeIpamController
controllers
[
"nodeipam"
]
=
startNodeIpamController
if
loopMode
==
IncludeCloudLoops
{
if
loopMode
==
IncludeCloudLoops
{
controllers
[
"service"
]
=
startServiceController
controllers
[
"service"
]
=
startServiceController
...
...
pkg/controller/bootstrap/BUILD
deleted
100644 → 0
View file @
93898713
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"bootstrapsigner_test.go",
"common_test.go",
"jws_test.go",
"tokencleaner_test.go",
"util_test.go",
],
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
"//pkg/controller:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/informers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/testing:go_default_library",
"//staging/src/k8s.io/client-go/tools/bootstrap/token/api:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"bootstrapsigner.go",
"doc.go",
"jws.go",
"tokencleaner.go",
"util.go",
],
importpath = "k8s.io/kubernetes/pkg/controller/bootstrap",
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/util/metrics:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/client-go/informers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/tools/bootstrap/token/api:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/gopkg.in/square/go-jose.v2:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
pkg/controller/bootstrap/bootstrapsigner.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
bootstrap
import
(
"strings"
"time"
"github.com/golang/glog"
"fmt"
"k8s.io/api/core/v1"
apierrors
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
informers
"k8s.io/client-go/informers/core/v1"
clientset
"k8s.io/client-go/kubernetes"
corelisters
"k8s.io/client-go/listers/core/v1"
bootstrapapi
"k8s.io/client-go/tools/bootstrap/token/api"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
api
"k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/util/metrics"
)
// BootstrapSignerOptions contains options for the BootstrapSigner
type
BootstrapSignerOptions
struct
{
// ConfigMapNamespace is the namespace of the ConfigMap
ConfigMapNamespace
string
// ConfigMapName is the name for the ConfigMap
ConfigMapName
string
// TokenSecretNamespace string is the namespace for token Secrets.
TokenSecretNamespace
string
// ConfigMapResynce is the time.Duration at which to fully re-list configmaps.
// If zero, re-list will be delayed as long as possible
ConfigMapResync
time
.
Duration
// SecretResync is the time.Duration at which to fully re-list secrets.
// If zero, re-list will be delayed as long as possible
SecretResync
time
.
Duration
}
// DefaultBootstrapSignerOptions returns a set of default options for creating a
// BootstrapSigner
func
DefaultBootstrapSignerOptions
()
BootstrapSignerOptions
{
return
BootstrapSignerOptions
{
ConfigMapNamespace
:
api
.
NamespacePublic
,
ConfigMapName
:
bootstrapapi
.
ConfigMapClusterInfo
,
TokenSecretNamespace
:
api
.
NamespaceSystem
,
}
}
// BootstrapSigner is a controller that signs a ConfigMap with a set of tokens.
type
BootstrapSigner
struct
{
client
clientset
.
Interface
configMapKey
string
configMapName
string
configMapNamespace
string
secretNamespace
string
// syncQueue handles synchronizing updates to the ConfigMap. We'll only ever
// have one item (Named <ConfigMapName>) in this queue. We are using it
// serializes and collapses updates as they can come from both the ConfigMap
// and Secrets controllers.
syncQueue
workqueue
.
RateLimitingInterface
secretLister
corelisters
.
SecretLister
secretSynced
cache
.
InformerSynced
configMapLister
corelisters
.
ConfigMapLister
configMapSynced
cache
.
InformerSynced
}
// NewBootstrapSigner returns a new *BootstrapSigner.
func
NewBootstrapSigner
(
cl
clientset
.
Interface
,
secrets
informers
.
SecretInformer
,
configMaps
informers
.
ConfigMapInformer
,
options
BootstrapSignerOptions
)
(
*
BootstrapSigner
,
error
)
{
e
:=
&
BootstrapSigner
{
client
:
cl
,
configMapKey
:
options
.
ConfigMapNamespace
+
"/"
+
options
.
ConfigMapName
,
configMapName
:
options
.
ConfigMapName
,
configMapNamespace
:
options
.
ConfigMapNamespace
,
secretNamespace
:
options
.
TokenSecretNamespace
,
secretLister
:
secrets
.
Lister
(),
secretSynced
:
secrets
.
Informer
()
.
HasSynced
,
configMapLister
:
configMaps
.
Lister
(),
configMapSynced
:
configMaps
.
Informer
()
.
HasSynced
,
syncQueue
:
workqueue
.
NewNamedRateLimitingQueue
(
workqueue
.
DefaultControllerRateLimiter
(),
"bootstrap_signer_queue"
),
}
if
cl
.
CoreV1
()
.
RESTClient
()
.
GetRateLimiter
()
!=
nil
{
if
err
:=
metrics
.
RegisterMetricAndTrackRateLimiterUsage
(
"bootstrap_signer"
,
cl
.
CoreV1
()
.
RESTClient
()
.
GetRateLimiter
());
err
!=
nil
{
return
nil
,
err
}
}
configMaps
.
Informer
()
.
AddEventHandlerWithResyncPeriod
(
cache
.
FilteringResourceEventHandler
{
FilterFunc
:
func
(
obj
interface
{})
bool
{
switch
t
:=
obj
.
(
type
)
{
case
*
v1
.
ConfigMap
:
return
t
.
Name
==
options
.
ConfigMapName
&&
t
.
Namespace
==
options
.
ConfigMapNamespace
default
:
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"object passed to %T that is not expected: %T"
,
e
,
obj
))
return
false
}
},
Handler
:
cache
.
ResourceEventHandlerFuncs
{
AddFunc
:
func
(
_
interface
{})
{
e
.
pokeConfigMapSync
()
},
UpdateFunc
:
func
(
_
,
_
interface
{})
{
e
.
pokeConfigMapSync
()
},
},
},
options
.
ConfigMapResync
,
)
secrets
.
Informer
()
.
AddEventHandlerWithResyncPeriod
(
cache
.
FilteringResourceEventHandler
{
FilterFunc
:
func
(
obj
interface
{})
bool
{
switch
t
:=
obj
.
(
type
)
{
case
*
v1
.
Secret
:
return
t
.
Type
==
bootstrapapi
.
SecretTypeBootstrapToken
&&
t
.
Namespace
==
e
.
secretNamespace
default
:
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"object passed to %T that is not expected: %T"
,
e
,
obj
))
return
false
}
},
Handler
:
cache
.
ResourceEventHandlerFuncs
{
AddFunc
:
func
(
_
interface
{})
{
e
.
pokeConfigMapSync
()
},
UpdateFunc
:
func
(
_
,
_
interface
{})
{
e
.
pokeConfigMapSync
()
},
DeleteFunc
:
func
(
_
interface
{})
{
e
.
pokeConfigMapSync
()
},
},
},
options
.
SecretResync
,
)
return
e
,
nil
}
// Run runs controller loops and returns when they are done
func
(
e
*
BootstrapSigner
)
Run
(
stopCh
<-
chan
struct
{})
{
// Shut down queues
defer
utilruntime
.
HandleCrash
()
defer
e
.
syncQueue
.
ShutDown
()
if
!
controller
.
WaitForCacheSync
(
"bootstrap_signer"
,
stopCh
,
e
.
configMapSynced
,
e
.
secretSynced
)
{
return
}
glog
.
V
(
5
)
.
Infof
(
"Starting workers"
)
go
wait
.
Until
(
e
.
serviceConfigMapQueue
,
0
,
stopCh
)
<-
stopCh
glog
.
V
(
1
)
.
Infof
(
"Shutting down"
)
}
func
(
e
*
BootstrapSigner
)
pokeConfigMapSync
()
{
e
.
syncQueue
.
Add
(
e
.
configMapKey
)
}
func
(
e
*
BootstrapSigner
)
serviceConfigMapQueue
()
{
key
,
quit
:=
e
.
syncQueue
.
Get
()
if
quit
{
return
}
defer
e
.
syncQueue
.
Done
(
key
)
e
.
signConfigMap
()
}
// signConfigMap computes the signatures on our latest cached objects and writes
// back if necessary.
func
(
e
*
BootstrapSigner
)
signConfigMap
()
{
origCM
:=
e
.
getConfigMap
()
if
origCM
==
nil
{
return
}
var
needUpdate
=
false
newCM
:=
origCM
.
DeepCopy
()
// First capture the config we are signing
content
,
ok
:=
newCM
.
Data
[
bootstrapapi
.
KubeConfigKey
]
if
!
ok
{
glog
.
V
(
3
)
.
Infof
(
"No %s key in %s/%s ConfigMap"
,
bootstrapapi
.
KubeConfigKey
,
origCM
.
Namespace
,
origCM
.
Name
)
return
}
// Next remove and save all existing signatures
sigs
:=
map
[
string
]
string
{}
for
key
,
value
:=
range
newCM
.
Data
{
if
strings
.
HasPrefix
(
key
,
bootstrapapi
.
JWSSignatureKeyPrefix
)
{
tokenID
:=
strings
.
TrimPrefix
(
key
,
bootstrapapi
.
JWSSignatureKeyPrefix
)
sigs
[
tokenID
]
=
value
delete
(
newCM
.
Data
,
key
)
}
}
// Now recompute signatures and store them on the new map
tokens
:=
e
.
getTokens
()
for
tokenID
,
tokenValue
:=
range
tokens
{
sig
,
err
:=
computeDetachedSig
(
content
,
tokenID
,
tokenValue
)
if
err
!=
nil
{
utilruntime
.
HandleError
(
err
)
}
// Check to see if this signature is changed or new.
oldSig
,
_
:=
sigs
[
tokenID
]
if
sig
!=
oldSig
{
needUpdate
=
true
}
delete
(
sigs
,
tokenID
)
newCM
.
Data
[
bootstrapapi
.
JWSSignatureKeyPrefix
+
tokenID
]
=
sig
}
// If we have signatures left over we know that some signatures were
// removed. We now need to update the ConfigMap
if
len
(
sigs
)
!=
0
{
needUpdate
=
true
}
if
needUpdate
{
e
.
updateConfigMap
(
newCM
)
}
}
func
(
e
*
BootstrapSigner
)
updateConfigMap
(
cm
*
v1
.
ConfigMap
)
{
_
,
err
:=
e
.
client
.
CoreV1
()
.
ConfigMaps
(
cm
.
Namespace
)
.
Update
(
cm
)
if
err
!=
nil
&&
!
apierrors
.
IsConflict
(
err
)
&&
!
apierrors
.
IsNotFound
(
err
)
{
glog
.
V
(
3
)
.
Infof
(
"Error updating ConfigMap: %v"
,
err
)
}
}
// getConfigMap gets the ConfigMap we are interested in
func
(
e
*
BootstrapSigner
)
getConfigMap
()
*
v1
.
ConfigMap
{
configMap
,
err
:=
e
.
configMapLister
.
ConfigMaps
(
e
.
configMapNamespace
)
.
Get
(
e
.
configMapName
)
// If we can't get the configmap just return nil. The resync will eventually
// sync things up.
if
err
!=
nil
{
if
!
apierrors
.
IsNotFound
(
err
)
{
utilruntime
.
HandleError
(
err
)
}
return
nil
}
return
configMap
}
func
(
e
*
BootstrapSigner
)
listSecrets
()
[]
*
v1
.
Secret
{
secrets
,
err
:=
e
.
secretLister
.
Secrets
(
e
.
secretNamespace
)
.
List
(
labels
.
Everything
())
if
err
!=
nil
{
utilruntime
.
HandleError
(
err
)
return
nil
}
items
:=
[]
*
v1
.
Secret
{}
for
_
,
secret
:=
range
secrets
{
if
secret
.
Type
==
bootstrapapi
.
SecretTypeBootstrapToken
{
items
=
append
(
items
,
secret
)
}
}
return
items
}
// getTokens returns a map of tokenID->tokenSecret. It ensures the token is
// valid for signing.
func
(
e
*
BootstrapSigner
)
getTokens
()
map
[
string
]
string
{
ret
:=
map
[
string
]
string
{}
secretObjs
:=
e
.
listSecrets
()
for
_
,
secret
:=
range
secretObjs
{
tokenID
,
tokenSecret
,
ok
:=
validateSecretForSigning
(
secret
)
if
!
ok
{
continue
}
// Check and warn for duplicate secrets. Behavior here will be undefined.
if
_
,
ok
:=
ret
[
tokenID
];
ok
{
// This should never happen as we ensure a consistent secret name.
// But leave this in here just in case.
glog
.
V
(
1
)
.
Infof
(
"Duplicate bootstrap tokens found for id %s, ignoring on in %s/%s"
,
tokenID
,
secret
.
Namespace
,
secret
.
Name
)
continue
}
// This secret looks good, add it to the list.
ret
[
tokenID
]
=
tokenSecret
}
return
ret
}
pkg/controller/bootstrap/bootstrapsigner_test.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
bootstrap
import
(
"testing"
"github.com/davecgh/go-spew/spew"
"k8s.io/api/core/v1"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/informers"
coreinformers
"k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes/fake"
core
"k8s.io/client-go/testing"
bootstrapapi
"k8s.io/client-go/tools/bootstrap/token/api"
api
"k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/controller"
)
func
init
()
{
spew
.
Config
.
DisableMethods
=
true
}
const
testTokenID
=
"abc123"
func
newBootstrapSigner
()
(
*
BootstrapSigner
,
*
fake
.
Clientset
,
coreinformers
.
SecretInformer
,
coreinformers
.
ConfigMapInformer
,
error
)
{
options
:=
DefaultBootstrapSignerOptions
()
cl
:=
fake
.
NewSimpleClientset
()
informers
:=
informers
.
NewSharedInformerFactory
(
fake
.
NewSimpleClientset
(),
controller
.
NoResyncPeriodFunc
())
secrets
:=
informers
.
Core
()
.
V1
()
.
Secrets
()
configMaps
:=
informers
.
Core
()
.
V1
()
.
ConfigMaps
()
bsc
,
err
:=
NewBootstrapSigner
(
cl
,
secrets
,
configMaps
,
options
)
if
err
!=
nil
{
return
nil
,
nil
,
nil
,
nil
,
err
}
return
bsc
,
cl
,
secrets
,
configMaps
,
nil
}
func
newConfigMap
(
tokenID
,
signature
string
)
*
v1
.
ConfigMap
{
ret
:=
&
v1
.
ConfigMap
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Namespace
:
metav1
.
NamespacePublic
,
Name
:
bootstrapapi
.
ConfigMapClusterInfo
,
ResourceVersion
:
"1"
,
},
Data
:
map
[
string
]
string
{
bootstrapapi
.
KubeConfigKey
:
"payload"
,
},
}
if
len
(
tokenID
)
>
0
{
ret
.
Data
[
bootstrapapi
.
JWSSignatureKeyPrefix
+
tokenID
]
=
signature
}
return
ret
}
func
TestNoConfigMap
(
t
*
testing
.
T
)
{
signer
,
cl
,
_
,
_
,
err
:=
newBootstrapSigner
()
if
err
!=
nil
{
t
.
Fatalf
(
"error creating BootstrapSigner: %v"
,
err
)
}
signer
.
signConfigMap
()
verifyActions
(
t
,
[]
core
.
Action
{},
cl
.
Actions
())
}
func
TestSimpleSign
(
t
*
testing
.
T
)
{
signer
,
cl
,
secrets
,
configMaps
,
err
:=
newBootstrapSigner
()
if
err
!=
nil
{
t
.
Fatalf
(
"error creating BootstrapSigner: %v"
,
err
)
}
cm
:=
newConfigMap
(
""
,
""
)
configMaps
.
Informer
()
.
GetIndexer
()
.
Add
(
cm
)
secret
:=
newTokenSecret
(
testTokenID
,
"tokenSecret"
)
addSecretSigningUsage
(
secret
,
"true"
)
secrets
.
Informer
()
.
GetIndexer
()
.
Add
(
secret
)
signer
.
signConfigMap
()
expected
:=
[]
core
.
Action
{
core
.
NewUpdateAction
(
schema
.
GroupVersionResource
{
Version
:
"v1"
,
Resource
:
"configmaps"
},
api
.
NamespacePublic
,
newConfigMap
(
testTokenID
,
"eyJhbGciOiJIUzI1NiIsImtpZCI6ImFiYzEyMyJ9..QSxpUG7Q542CirTI2ECPSZjvBOJURUW5a7XqFpNI958"
)),
}
verifyActions
(
t
,
expected
,
cl
.
Actions
())
}
func
TestNoSignNeeded
(
t
*
testing
.
T
)
{
signer
,
cl
,
secrets
,
configMaps
,
err
:=
newBootstrapSigner
()
if
err
!=
nil
{
t
.
Fatalf
(
"error creating BootstrapSigner: %v"
,
err
)
}
cm
:=
newConfigMap
(
testTokenID
,
"eyJhbGciOiJIUzI1NiIsImtpZCI6ImFiYzEyMyJ9..QSxpUG7Q542CirTI2ECPSZjvBOJURUW5a7XqFpNI958"
)
configMaps
.
Informer
()
.
GetIndexer
()
.
Add
(
cm
)
secret
:=
newTokenSecret
(
testTokenID
,
"tokenSecret"
)
addSecretSigningUsage
(
secret
,
"true"
)
secrets
.
Informer
()
.
GetIndexer
()
.
Add
(
secret
)
signer
.
signConfigMap
()
verifyActions
(
t
,
[]
core
.
Action
{},
cl
.
Actions
())
}
func
TestUpdateSignature
(
t
*
testing
.
T
)
{
signer
,
cl
,
secrets
,
configMaps
,
err
:=
newBootstrapSigner
()
if
err
!=
nil
{
t
.
Fatalf
(
"error creating BootstrapSigner: %v"
,
err
)
}
cm
:=
newConfigMap
(
testTokenID
,
"old signature"
)
configMaps
.
Informer
()
.
GetIndexer
()
.
Add
(
cm
)
secret
:=
newTokenSecret
(
testTokenID
,
"tokenSecret"
)
addSecretSigningUsage
(
secret
,
"true"
)
secrets
.
Informer
()
.
GetIndexer
()
.
Add
(
secret
)
signer
.
signConfigMap
()
expected
:=
[]
core
.
Action
{
core
.
NewUpdateAction
(
schema
.
GroupVersionResource
{
Version
:
"v1"
,
Resource
:
"configmaps"
},
api
.
NamespacePublic
,
newConfigMap
(
testTokenID
,
"eyJhbGciOiJIUzI1NiIsImtpZCI6ImFiYzEyMyJ9..QSxpUG7Q542CirTI2ECPSZjvBOJURUW5a7XqFpNI958"
)),
}
verifyActions
(
t
,
expected
,
cl
.
Actions
())
}
func
TestRemoveSignature
(
t
*
testing
.
T
)
{
signer
,
cl
,
_
,
configMaps
,
err
:=
newBootstrapSigner
()
if
err
!=
nil
{
t
.
Fatalf
(
"error creating BootstrapSigner: %v"
,
err
)
}
cm
:=
newConfigMap
(
testTokenID
,
"old signature"
)
configMaps
.
Informer
()
.
GetIndexer
()
.
Add
(
cm
)
signer
.
signConfigMap
()
expected
:=
[]
core
.
Action
{
core
.
NewUpdateAction
(
schema
.
GroupVersionResource
{
Version
:
"v1"
,
Resource
:
"configmaps"
},
api
.
NamespacePublic
,
newConfigMap
(
""
,
""
)),
}
verifyActions
(
t
,
expected
,
cl
.
Actions
())
}
pkg/controller/bootstrap/common_test.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
bootstrap
import
(
"testing"
"github.com/davecgh/go-spew/spew"
"k8s.io/api/core/v1"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
core
"k8s.io/client-go/testing"
bootstrapapi
"k8s.io/client-go/tools/bootstrap/token/api"
"k8s.io/kubernetes/pkg/apis/core/helper"
)
func
newTokenSecret
(
tokenID
,
tokenSecret
string
)
*
v1
.
Secret
{
return
&
v1
.
Secret
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Namespace
:
metav1
.
NamespaceSystem
,
Name
:
bootstrapapi
.
BootstrapTokenSecretPrefix
+
tokenID
,
ResourceVersion
:
"1"
,
},
Type
:
bootstrapapi
.
SecretTypeBootstrapToken
,
Data
:
map
[
string
][]
byte
{
bootstrapapi
.
BootstrapTokenIDKey
:
[]
byte
(
tokenID
),
bootstrapapi
.
BootstrapTokenSecretKey
:
[]
byte
(
tokenSecret
),
},
}
}
func
addSecretExpiration
(
s
*
v1
.
Secret
,
expiration
string
)
{
s
.
Data
[
bootstrapapi
.
BootstrapTokenExpirationKey
]
=
[]
byte
(
expiration
)
}
func
addSecretSigningUsage
(
s
*
v1
.
Secret
,
value
string
)
{
s
.
Data
[
bootstrapapi
.
BootstrapTokenUsageSigningKey
]
=
[]
byte
(
value
)
}
func
verifyActions
(
t
*
testing
.
T
,
expected
,
actual
[]
core
.
Action
)
{
for
i
,
a
:=
range
actual
{
if
len
(
expected
)
<
i
+
1
{
t
.
Errorf
(
"%d unexpected actions: %s"
,
len
(
actual
)
-
len
(
expected
),
spew
.
Sdump
(
actual
[
i
:
]))
break
}
e
:=
expected
[
i
]
if
!
helper
.
Semantic
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"Expected
\n\t
%s
\n
got
\n\t
%s"
,
spew
.
Sdump
(
e
),
spew
.
Sdump
(
a
))
continue
}
}
if
len
(
expected
)
>
len
(
actual
)
{
t
.
Errorf
(
"%d additional expected actions"
,
len
(
expected
)
-
len
(
actual
))
for
_
,
a
:=
range
expected
[
len
(
actual
)
:
]
{
t
.
Logf
(
" %s"
,
spew
.
Sdump
(
a
))
}
}
}
pkg/controller/bootstrap/doc.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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 bootstrap provides automatic processes necessary for bootstraping.
// This includes managing and expiring tokens along with signing well known
// configmaps with those tokens.
package
bootstrap
// import "k8s.io/kubernetes/pkg/controller/bootstrap"
pkg/controller/bootstrap/jws.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
bootstrap
import
(
"fmt"
"strings"
jose
"gopkg.in/square/go-jose.v2"
)
// computeDetachedSig takes content and token details and computes a detached
// JWS signature. This is described in Appendix F of RFC 7515. Basically, this
// is a regular JWS with the content part of the signature elided.
func
computeDetachedSig
(
content
,
tokenID
,
tokenSecret
string
)
(
string
,
error
)
{
jwk
:=
&
jose
.
JSONWebKey
{
Key
:
[]
byte
(
tokenSecret
),
KeyID
:
tokenID
,
}
opts
:=
&
jose
.
SignerOptions
{
// Since this is a symmetric key, go-jose doesn't automatically include
// the KeyID as part of the protected header. We have to pass it here
// explicitly.
ExtraHeaders
:
map
[
jose
.
HeaderKey
]
interface
{}{
"kid"
:
tokenID
,
},
}
signer
,
err
:=
jose
.
NewSigner
(
jose
.
SigningKey
{
Algorithm
:
jose
.
HS256
,
Key
:
jwk
},
opts
)
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"can't make a HS256 signer from the given token: %v"
,
err
)
}
jws
,
err
:=
signer
.
Sign
([]
byte
(
content
))
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"can't HS256-sign the given token: %v"
,
err
)
}
fullSig
,
err
:=
jws
.
CompactSerialize
()
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"can't serialize the given token: %v"
,
err
)
}
return
stripContent
(
fullSig
)
}
// stripContent will remove the content part of a compact JWS
//
// The `go-jose` library doesn't support generating signatures with "detached"
// content. To make up for this we take the full compact signature, break it
// apart and put it back together without the content section.
func
stripContent
(
fullSig
string
)
(
string
,
error
)
{
parts
:=
strings
.
Split
(
fullSig
,
"."
)
if
len
(
parts
)
!=
3
{
return
""
,
fmt
.
Errorf
(
"compact JWS format must have three parts"
)
}
return
parts
[
0
]
+
".."
+
parts
[
2
],
nil
}
// DetachedTokenIsValid checks whether a given detached JWS-encoded token matches JWS output of the given content and token
func
DetachedTokenIsValid
(
detachedToken
,
content
,
tokenID
,
tokenSecret
string
)
bool
{
newToken
,
err
:=
computeDetachedSig
(
content
,
tokenID
,
tokenSecret
)
if
err
!=
nil
{
return
false
}
return
detachedToken
==
newToken
}
pkg/controller/bootstrap/jws_test.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
bootstrap
import
(
"testing"
"github.com/stretchr/testify/assert"
)
const
(
content
=
"Hello from the other side. I must have called a thousand times."
secret
=
"my voice is my passcode"
id
=
"joshua"
)
func
TestComputeDetachedSig
(
t
*
testing
.
T
)
{
sig
,
err
:=
computeDetachedSig
(
content
,
id
,
secret
)
assert
.
NoError
(
t
,
err
,
"Error when computing signature: %v"
,
err
)
assert
.
Equal
(
t
,
"eyJhbGciOiJIUzI1NiIsImtpZCI6Impvc2h1YSJ9..VShe2taLd-YTrmWuRkcL_8QTNDHYxQIEBsAYYiIj1_8"
,
sig
,
"Wrong signature. Got: %v"
,
sig
)
// Try with null content
sig
,
err
=
computeDetachedSig
(
""
,
id
,
secret
)
assert
.
NoError
(
t
,
err
,
"Error when computing signature: %v"
,
err
)
assert
.
Equal
(
t
,
"eyJhbGciOiJIUzI1NiIsImtpZCI6Impvc2h1YSJ9..7Ui1ALizW4jXphVUB7xUqC9vLYLL9RZeOFfVLoB7Tgk"
,
sig
,
"Wrong signature. Got: %v"
,
sig
)
// Try with no secret
sig
,
err
=
computeDetachedSig
(
content
,
id
,
""
)
assert
.
NoError
(
t
,
err
,
"Error when computing signature: %v"
,
err
)
assert
.
Equal
(
t
,
"eyJhbGciOiJIUzI1NiIsImtpZCI6Impvc2h1YSJ9..UfkqvDGiIFxrMnFseDj9LYJOLNrvjW8aHhF71mvvAs8"
,
sig
,
"Wrong signature. Got: %v"
,
sig
)
}
func
TestDetachedTokenIsValid
(
t
*
testing
.
T
)
{
// Valid detached JWS token and valid inputs should succeed
sig
:=
"eyJhbGciOiJIUzI1NiIsImtpZCI6Impvc2h1YSJ9..VShe2taLd-YTrmWuRkcL_8QTNDHYxQIEBsAYYiIj1_8"
assert
.
True
(
t
,
DetachedTokenIsValid
(
sig
,
content
,
id
,
secret
),
"Content %q and token
\"
%s:%s
\"
should equal signature: %q"
,
content
,
id
,
secret
,
sig
)
// Invalid detached JWS token and valid inputs should fail
sig2
:=
sig
+
"foo"
assert
.
False
(
t
,
DetachedTokenIsValid
(
sig2
,
content
,
id
,
secret
),
"Content %q and token
\"
%s:%s
\"
should not equal signature: %q"
,
content
,
id
,
secret
,
sig
)
}
pkg/controller/bootstrap/tokencleaner.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
bootstrap
import
(
"fmt"
"time"
"github.com/golang/glog"
"k8s.io/api/core/v1"
apierrors
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
coreinformers
"k8s.io/client-go/informers/core/v1"
clientset
"k8s.io/client-go/kubernetes"
corelisters
"k8s.io/client-go/listers/core/v1"
bootstrapapi
"k8s.io/client-go/tools/bootstrap/token/api"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
api
"k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/util/metrics"
)
// TokenCleanerOptions contains options for the TokenCleaner
type
TokenCleanerOptions
struct
{
// TokenSecretNamespace string is the namespace for token Secrets.
TokenSecretNamespace
string
// SecretResync is the time.Duration at which to fully re-list secrets.
// If zero, re-list will be delayed as long as possible
SecretResync
time
.
Duration
}
// DefaultTokenCleanerOptions returns a set of default options for creating a
// TokenCleaner
func
DefaultTokenCleanerOptions
()
TokenCleanerOptions
{
return
TokenCleanerOptions
{
TokenSecretNamespace
:
api
.
NamespaceSystem
,
}
}
// TokenCleaner is a controller that deletes expired tokens
type
TokenCleaner
struct
{
tokenSecretNamespace
string
client
clientset
.
Interface
// secretLister is able to list/get secrets and is populated by the shared informer passed to NewTokenCleaner.
secretLister
corelisters
.
SecretLister
// secretSynced returns true if the secret shared informer has been synced at least once.
secretSynced
cache
.
InformerSynced
queue
workqueue
.
RateLimitingInterface
}
// NewTokenCleaner returns a new *NewTokenCleaner.
func
NewTokenCleaner
(
cl
clientset
.
Interface
,
secrets
coreinformers
.
SecretInformer
,
options
TokenCleanerOptions
)
(
*
TokenCleaner
,
error
)
{
e
:=
&
TokenCleaner
{
client
:
cl
,
secretLister
:
secrets
.
Lister
(),
secretSynced
:
secrets
.
Informer
()
.
HasSynced
,
tokenSecretNamespace
:
options
.
TokenSecretNamespace
,
queue
:
workqueue
.
NewNamedRateLimitingQueue
(
workqueue
.
DefaultControllerRateLimiter
(),
"token_cleaner"
),
}
if
cl
.
CoreV1
()
.
RESTClient
()
.
GetRateLimiter
()
!=
nil
{
if
err
:=
metrics
.
RegisterMetricAndTrackRateLimiterUsage
(
"token_cleaner"
,
cl
.
CoreV1
()
.
RESTClient
()
.
GetRateLimiter
());
err
!=
nil
{
return
nil
,
err
}
}
secrets
.
Informer
()
.
AddEventHandlerWithResyncPeriod
(
cache
.
FilteringResourceEventHandler
{
FilterFunc
:
func
(
obj
interface
{})
bool
{
switch
t
:=
obj
.
(
type
)
{
case
*
v1
.
Secret
:
return
t
.
Type
==
bootstrapapi
.
SecretTypeBootstrapToken
&&
t
.
Namespace
==
e
.
tokenSecretNamespace
default
:
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"object passed to %T that is not expected: %T"
,
e
,
obj
))
return
false
}
},
Handler
:
cache
.
ResourceEventHandlerFuncs
{
AddFunc
:
e
.
enqueueSecrets
,
UpdateFunc
:
func
(
oldSecret
,
newSecret
interface
{})
{
e
.
enqueueSecrets
(
newSecret
)
},
},
},
options
.
SecretResync
,
)
return
e
,
nil
}
// Run runs controller loops and returns when they are done
func
(
tc
*
TokenCleaner
)
Run
(
stopCh
<-
chan
struct
{})
{
defer
utilruntime
.
HandleCrash
()
defer
tc
.
queue
.
ShutDown
()
glog
.
Infof
(
"Starting token cleaner controller"
)
defer
glog
.
Infof
(
"Shutting down token cleaner controller"
)
if
!
controller
.
WaitForCacheSync
(
"token_cleaner"
,
stopCh
,
tc
.
secretSynced
)
{
return
}
go
wait
.
Until
(
tc
.
worker
,
10
*
time
.
Second
,
stopCh
)
<-
stopCh
}
func
(
tc
*
TokenCleaner
)
enqueueSecrets
(
obj
interface
{})
{
key
,
err
:=
controller
.
KeyFunc
(
obj
)
if
err
!=
nil
{
utilruntime
.
HandleError
(
err
)
return
}
tc
.
queue
.
Add
(
key
)
}
// worker runs a thread that dequeues secrets, handles them, and marks them done.
func
(
tc
*
TokenCleaner
)
worker
()
{
for
tc
.
processNextWorkItem
()
{
}
}
// processNextWorkItem deals with one key off the queue. It returns false when it's time to quit.
func
(
tc
*
TokenCleaner
)
processNextWorkItem
()
bool
{
key
,
quit
:=
tc
.
queue
.
Get
()
if
quit
{
return
false
}
defer
tc
.
queue
.
Done
(
key
)
if
err
:=
tc
.
syncFunc
(
key
.
(
string
));
err
!=
nil
{
tc
.
queue
.
AddRateLimited
(
key
)
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"Sync %v failed with : %v"
,
key
,
err
))
return
true
}
tc
.
queue
.
Forget
(
key
)
return
true
}
func
(
tc
*
TokenCleaner
)
syncFunc
(
key
string
)
error
{
startTime
:=
time
.
Now
()
defer
func
()
{
glog
.
V
(
4
)
.
Infof
(
"Finished syncing secret %q (%v)"
,
key
,
time
.
Since
(
startTime
))
}()
namespace
,
name
,
err
:=
cache
.
SplitMetaNamespaceKey
(
key
)
if
err
!=
nil
{
return
err
}
ret
,
err
:=
tc
.
secretLister
.
Secrets
(
namespace
)
.
Get
(
name
)
if
apierrors
.
IsNotFound
(
err
)
{
glog
.
V
(
3
)
.
Infof
(
"secret has been deleted: %v"
,
key
)
return
nil
}
if
err
!=
nil
{
return
err
}
if
ret
.
Type
==
bootstrapapi
.
SecretTypeBootstrapToken
{
tc
.
evalSecret
(
ret
)
}
return
nil
}
func
(
tc
*
TokenCleaner
)
evalSecret
(
o
interface
{})
{
secret
:=
o
.
(
*
v1
.
Secret
)
if
isSecretExpired
(
secret
)
{
glog
.
V
(
3
)
.
Infof
(
"Deleting expired secret %s/%s"
,
secret
.
Namespace
,
secret
.
Name
)
var
options
*
metav1
.
DeleteOptions
if
len
(
secret
.
UID
)
>
0
{
options
=
&
metav1
.
DeleteOptions
{
Preconditions
:
&
metav1
.
Preconditions
{
UID
:
&
secret
.
UID
}}
}
err
:=
tc
.
client
.
CoreV1
()
.
Secrets
(
secret
.
Namespace
)
.
Delete
(
secret
.
Name
,
options
)
// NotFound isn't a real error (it's already been deleted)
// Conflict isn't a real error (the UID precondition failed)
if
err
!=
nil
&&
!
apierrors
.
IsConflict
(
err
)
&&
!
apierrors
.
IsNotFound
(
err
)
{
glog
.
V
(
3
)
.
Infof
(
"Error deleting Secret: %v"
,
err
)
}
}
}
pkg/controller/bootstrap/tokencleaner_test.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
bootstrap
import
(
"testing"
"time"
"github.com/davecgh/go-spew/spew"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/informers"
coreinformers
"k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes/fake"
core
"k8s.io/client-go/testing"
api
"k8s.io/kubernetes/pkg/apis/core"
)
func
init
()
{
spew
.
Config
.
DisableMethods
=
true
}
func
newTokenCleaner
()
(
*
TokenCleaner
,
*
fake
.
Clientset
,
coreinformers
.
SecretInformer
,
error
)
{
options
:=
DefaultTokenCleanerOptions
()
cl
:=
fake
.
NewSimpleClientset
()
informerFactory
:=
informers
.
NewSharedInformerFactory
(
cl
,
options
.
SecretResync
)
secrets
:=
informerFactory
.
Core
()
.
V1
()
.
Secrets
()
tcc
,
err
:=
NewTokenCleaner
(
cl
,
secrets
,
options
)
if
err
!=
nil
{
return
nil
,
nil
,
nil
,
err
}
return
tcc
,
cl
,
secrets
,
nil
}
func
TestCleanerNoExpiration
(
t
*
testing
.
T
)
{
cleaner
,
cl
,
secrets
,
err
:=
newTokenCleaner
()
if
err
!=
nil
{
t
.
Fatalf
(
"error creating TokenCleaner: %v"
,
err
)
}
secret
:=
newTokenSecret
(
"tokenID"
,
"tokenSecret"
)
secrets
.
Informer
()
.
GetIndexer
()
.
Add
(
secret
)
cleaner
.
evalSecret
(
secret
)
expected
:=
[]
core
.
Action
{}
verifyActions
(
t
,
expected
,
cl
.
Actions
())
}
func
TestCleanerExpired
(
t
*
testing
.
T
)
{
cleaner
,
cl
,
secrets
,
err
:=
newTokenCleaner
()
if
err
!=
nil
{
t
.
Fatalf
(
"error creating TokenCleaner: %v"
,
err
)
}
secret
:=
newTokenSecret
(
"tokenID"
,
"tokenSecret"
)
addSecretExpiration
(
secret
,
timeString
(
-
time
.
Hour
))
secrets
.
Informer
()
.
GetIndexer
()
.
Add
(
secret
)
cleaner
.
evalSecret
(
secret
)
expected
:=
[]
core
.
Action
{
core
.
NewDeleteAction
(
schema
.
GroupVersionResource
{
Version
:
"v1"
,
Resource
:
"secrets"
},
api
.
NamespaceSystem
,
secret
.
ObjectMeta
.
Name
),
}
verifyActions
(
t
,
expected
,
cl
.
Actions
())
}
func
TestCleanerNotExpired
(
t
*
testing
.
T
)
{
cleaner
,
cl
,
secrets
,
err
:=
newTokenCleaner
()
if
err
!=
nil
{
t
.
Fatalf
(
"error creating TokenCleaner: %v"
,
err
)
}
secret
:=
newTokenSecret
(
"tokenID"
,
"tokenSecret"
)
addSecretExpiration
(
secret
,
timeString
(
time
.
Hour
))
secrets
.
Informer
()
.
GetIndexer
()
.
Add
(
secret
)
cleaner
.
evalSecret
(
secret
)
expected
:=
[]
core
.
Action
{}
verifyActions
(
t
,
expected
,
cl
.
Actions
())
}
pkg/controller/bootstrap/util.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
bootstrap
import
(
"regexp"
"time"
"github.com/golang/glog"
"k8s.io/api/core/v1"
bootstrapapi
"k8s.io/client-go/tools/bootstrap/token/api"
)
var
namePattern
=
`^`
+
regexp
.
QuoteMeta
(
bootstrapapi
.
BootstrapTokenSecretPrefix
)
+
`([a-z0-9]{6})$`
var
nameRegExp
=
regexp
.
MustCompile
(
namePattern
)
// getSecretString gets a string value from a secret. If there is an error or
// if the key doesn't exist, an empty string is returned.
func
getSecretString
(
secret
*
v1
.
Secret
,
key
string
)
string
{
data
,
ok
:=
secret
.
Data
[
key
]
if
!
ok
{
return
""
}
return
string
(
data
)
}
// parseSecretName parses the name of the secret to extract the secret ID.
func
parseSecretName
(
name
string
)
(
secretID
string
,
ok
bool
)
{
r
:=
nameRegExp
.
FindStringSubmatch
(
name
)
if
r
==
nil
{
return
""
,
false
}
return
r
[
1
],
true
}
func
validateSecretForSigning
(
secret
*
v1
.
Secret
)
(
tokenID
,
tokenSecret
string
,
ok
bool
)
{
nameTokenID
,
ok
:=
parseSecretName
(
secret
.
Name
)
if
!
ok
{
glog
.
V
(
3
)
.
Infof
(
"Invalid secret name: %s. Must be of form %s<secret-id>."
,
secret
.
Name
,
bootstrapapi
.
BootstrapTokenSecretPrefix
)
return
""
,
""
,
false
}
tokenID
=
getSecretString
(
secret
,
bootstrapapi
.
BootstrapTokenIDKey
)
if
len
(
tokenID
)
==
0
{
glog
.
V
(
3
)
.
Infof
(
"No %s key in %s/%s Secret"
,
bootstrapapi
.
BootstrapTokenIDKey
,
secret
.
Namespace
,
secret
.
Name
)
return
""
,
""
,
false
}
if
nameTokenID
!=
tokenID
{
glog
.
V
(
3
)
.
Infof
(
"Token ID (%s) doesn't match secret name: %s"
,
tokenID
,
nameTokenID
)
return
""
,
""
,
false
}
tokenSecret
=
getSecretString
(
secret
,
bootstrapapi
.
BootstrapTokenSecretKey
)
if
len
(
tokenSecret
)
==
0
{
glog
.
V
(
3
)
.
Infof
(
"No %s key in %s/%s Secret"
,
bootstrapapi
.
BootstrapTokenSecretKey
,
secret
.
Namespace
,
secret
.
Name
)
return
""
,
""
,
false
}
// Ensure this secret hasn't expired. The TokenCleaner should remove this
// but if that isn't working or it hasn't gotten there yet we should check
// here.
if
isSecretExpired
(
secret
)
{
return
""
,
""
,
false
}
// Make sure this secret can be used for signing
okToSign
:=
getSecretString
(
secret
,
bootstrapapi
.
BootstrapTokenUsageSigningKey
)
if
okToSign
!=
"true"
{
return
""
,
""
,
false
}
return
tokenID
,
tokenSecret
,
true
}
// isSecretExpired returns true if the Secret is expired.
func
isSecretExpired
(
secret
*
v1
.
Secret
)
bool
{
expiration
:=
getSecretString
(
secret
,
bootstrapapi
.
BootstrapTokenExpirationKey
)
if
len
(
expiration
)
>
0
{
expTime
,
err2
:=
time
.
Parse
(
time
.
RFC3339
,
expiration
)
if
err2
!=
nil
{
glog
.
V
(
3
)
.
Infof
(
"Unparseable expiration time (%s) in %s/%s Secret: %v. Treating as expired."
,
expiration
,
secret
.
Namespace
,
secret
.
Name
,
err2
)
return
true
}
if
time
.
Now
()
.
After
(
expTime
)
{
glog
.
V
(
3
)
.
Infof
(
"Expired bootstrap token in %s/%s Secret: %v"
,
secret
.
Namespace
,
secret
.
Name
,
expiration
)
return
true
}
}
return
false
}
pkg/controller/bootstrap/util_test.go
deleted
100644 → 0
View file @
93898713
/*
Copyright 2016 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
bootstrap
import
(
"testing"
"time"
"github.com/stretchr/testify/assert"
"k8s.io/api/core/v1"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
bootstrapapi
"k8s.io/client-go/tools/bootstrap/token/api"
)
const
(
givenTokenID
=
"abc123"
givenTokenID2
=
"def456"
givenTokenSecret
=
"tokenSecret"
)
func
timeString
(
delta
time
.
Duration
)
string
{
return
time
.
Now
()
.
Add
(
delta
)
.
Format
(
time
.
RFC3339
)
}
func
TestValidateSecretForSigning
(
t
*
testing
.
T
)
{
cases
:=
[]
struct
{
description
string
tokenID
string
tokenSecret
string
okToSign
string
expiration
string
valid
bool
}{
{
"Signing token with no exp"
,
givenTokenID
,
givenTokenSecret
,
"true"
,
""
,
true
,
},
{
"Signing token with valid exp"
,
givenTokenID
,
givenTokenSecret
,
"true"
,
timeString
(
time
.
Hour
),
true
,
},
{
"Expired signing token"
,
givenTokenID
,
givenTokenSecret
,
"true"
,
timeString
(
-
time
.
Hour
),
false
,
},
{
"Signing token with bad exp"
,
givenTokenID
,
givenTokenSecret
,
"true"
,
"garbage"
,
false
,
},
{
"Signing token without signing bit"
,
givenTokenID
,
givenTokenSecret
,
""
,
"garbage"
,
false
,
},
{
"Signing token with bad signing bit"
,
givenTokenID
,
givenTokenSecret
,
""
,
""
,
false
,
},
{
"Signing token with no ID"
,
""
,
givenTokenSecret
,
"true"
,
""
,
false
,
},
{
"Signing token with no secret"
,
givenTokenID
,
""
,
"true"
,
""
,
false
,
},
}
for
_
,
tc
:=
range
cases
{
secret
:=
&
v1
.
Secret
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Namespace
:
metav1
.
NamespaceSystem
,
Name
:
bootstrapapi
.
BootstrapTokenSecretPrefix
+
givenTokenID
,
ResourceVersion
:
"1"
,
},
Type
:
bootstrapapi
.
SecretTypeBootstrapToken
,
Data
:
map
[
string
][]
byte
{
bootstrapapi
.
BootstrapTokenIDKey
:
[]
byte
(
tc
.
tokenID
),
bootstrapapi
.
BootstrapTokenSecretKey
:
[]
byte
(
tc
.
tokenSecret
),
bootstrapapi
.
BootstrapTokenUsageSigningKey
:
[]
byte
(
tc
.
okToSign
),
bootstrapapi
.
BootstrapTokenExpirationKey
:
[]
byte
(
tc
.
expiration
),
},
}
tokenID
,
tokenSecret
,
ok
:=
validateSecretForSigning
(
secret
)
if
ok
!=
tc
.
valid
{
t
.
Errorf
(
"%s: Unexpected validation failure. Expected %v, got %v"
,
tc
.
description
,
tc
.
valid
,
ok
)
}
if
ok
{
if
tokenID
!=
tc
.
tokenID
{
t
.
Errorf
(
"%s: Unexpected Token ID. Expected %q, got %q"
,
tc
.
description
,
givenTokenID
,
tokenID
)
}
if
tokenSecret
!=
tc
.
tokenSecret
{
t
.
Errorf
(
"%s: Unexpected Token Secret. Expected %q, got %q"
,
tc
.
description
,
givenTokenSecret
,
tokenSecret
)
}
}
}
}
func
TestValidateSecret
(
t
*
testing
.
T
)
{
secret
:=
&
v1
.
Secret
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Namespace
:
metav1
.
NamespaceSystem
,
Name
:
bootstrapapi
.
BootstrapTokenSecretPrefix
+
givenTokenID
,
ResourceVersion
:
"1"
,
},
Type
:
bootstrapapi
.
SecretTypeBootstrapToken
,
Data
:
map
[
string
][]
byte
{
bootstrapapi
.
BootstrapTokenIDKey
:
[]
byte
(
givenTokenID
),
bootstrapapi
.
BootstrapTokenSecretKey
:
[]
byte
(
givenTokenSecret
),
bootstrapapi
.
BootstrapTokenUsageSigningKey
:
[]
byte
(
"true"
),
},
}
tokenID
,
tokenSecret
,
ok
:=
validateSecretForSigning
(
secret
)
if
!
ok
{
t
.
Errorf
(
"Unexpected validation failure."
)
}
if
tokenID
!=
givenTokenID
{
t
.
Errorf
(
"Unexpected Token ID. Expected %q, got %q"
,
givenTokenID
,
tokenID
)
}
if
tokenSecret
!=
givenTokenSecret
{
t
.
Errorf
(
"Unexpected Token Secret. Expected %q, got %q"
,
givenTokenSecret
,
tokenSecret
)
}
}
func
TestBadSecretName
(
t
*
testing
.
T
)
{
secret
:=
&
v1
.
Secret
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Namespace
:
metav1
.
NamespaceSystem
,
Name
:
givenTokenID
,
ResourceVersion
:
"1"
,
},
Type
:
bootstrapapi
.
SecretTypeBootstrapToken
,
Data
:
map
[
string
][]
byte
{
bootstrapapi
.
BootstrapTokenIDKey
:
[]
byte
(
givenTokenID
),
bootstrapapi
.
BootstrapTokenSecretKey
:
[]
byte
(
givenTokenSecret
),
bootstrapapi
.
BootstrapTokenUsageSigningKey
:
[]
byte
(
"true"
),
},
}
_
,
_
,
ok
:=
validateSecretForSigning
(
secret
)
if
ok
{
t
.
Errorf
(
"Token validation should fail with bad name"
)
}
}
func
TestMismatchSecretName
(
t
*
testing
.
T
)
{
secret
:=
&
v1
.
Secret
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Namespace
:
metav1
.
NamespaceSystem
,
Name
:
bootstrapapi
.
BootstrapTokenSecretPrefix
+
givenTokenID2
,
ResourceVersion
:
"1"
,
},
Type
:
bootstrapapi
.
SecretTypeBootstrapToken
,
Data
:
map
[
string
][]
byte
{
bootstrapapi
.
BootstrapTokenIDKey
:
[]
byte
(
givenTokenID
),
bootstrapapi
.
BootstrapTokenSecretKey
:
[]
byte
(
givenTokenSecret
),
bootstrapapi
.
BootstrapTokenUsageSigningKey
:
[]
byte
(
"true"
),
},
}
_
,
_
,
ok
:=
validateSecretForSigning
(
secret
)
if
ok
{
t
.
Errorf
(
"Token validation should fail with mismatched name"
)
}
}
func
TestParseSecretName
(
t
*
testing
.
T
)
{
tokenID
,
ok
:=
parseSecretName
(
"bootstrap-token-abc123"
)
assert
.
True
(
t
,
ok
,
"parseSecretName should accept valid name"
)
assert
.
Equal
(
t
,
"abc123"
,
tokenID
,
"parseSecretName should return token ID"
)
_
,
ok
=
parseSecretName
(
""
)
assert
.
False
(
t
,
ok
,
"parseSecretName should reject blank name"
)
_
,
ok
=
parseSecretName
(
"abc123"
)
assert
.
False
(
t
,
ok
,
"parseSecretName should reject with no prefix"
)
_
,
ok
=
parseSecretName
(
"bootstrap-token-"
)
assert
.
False
(
t
,
ok
,
"parseSecretName should reject no token ID"
)
_
,
ok
=
parseSecretName
(
"bootstrap-token-abc"
)
assert
.
False
(
t
,
ok
,
"parseSecretName should reject short token ID"
)
_
,
ok
=
parseSecretName
(
"bootstrap-token-abc123ghi"
)
assert
.
False
(
t
,
ok
,
"parseSecretName should reject long token ID"
)
_
,
ok
=
parseSecretName
(
"bootstrap-token-ABC123"
)
assert
.
False
(
t
,
ok
,
"parseSecretName should reject invalid token ID"
)
}
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