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
b9f31ca2
Commit
b9f31ca2
authored
Dec 03, 2015
by
k8s-merge-robot
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #11827 from deads2k/make-permissive-sas
Auto commit by PR queue bot
parents
163b5212
7ae4d4f4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
88 additions
and
30 deletions
+88
-30
serviceaccounts_controller.go
pkg/controller/serviceaccount/serviceaccounts_controller.go
+29
-27
serviceaccounts_controller_test.go
...troller/serviceaccount/serviceaccounts_controller_test.go
+4
-1
admission.go
plugin/pkg/admission/serviceaccount/admission.go
+25
-2
admission_test.go
plugin/pkg/admission/serviceaccount/admission_test.go
+30
-0
No files found.
pkg/controller/serviceaccount/serviceaccounts_controller.go
View file @
b9f31ca2
...
...
@@ -30,7 +30,6 @@ import (
"k8s.io/kubernetes/pkg/controller/framework"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/watch"
)
...
...
@@ -45,8 +44,8 @@ func nameIndexFunc(obj interface{}) ([]string, error) {
// ServiceAccountsControllerOptions contains options for running a ServiceAccountsController
type
ServiceAccountsControllerOptions
struct
{
//
Names is the set of service account name
s to ensure exist in every namespace
Names
sets
.
String
//
ServiceAccounts is the list of service account
s to ensure exist in every namespace
ServiceAccounts
[]
api
.
ServiceAccount
// ServiceAccountResync is the interval between full resyncs of ServiceAccounts.
// If non-zero, all service accounts will be re-listed this often.
...
...
@@ -60,20 +59,24 @@ type ServiceAccountsControllerOptions struct {
}
func
DefaultServiceAccountsControllerOptions
()
ServiceAccountsControllerOptions
{
return
ServiceAccountsControllerOptions
{
Names
:
sets
.
NewString
(
"default"
)}
return
ServiceAccountsControllerOptions
{
ServiceAccounts
:
[]
api
.
ServiceAccount
{
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"default"
}},
},
}
}
// NewServiceAccountsController returns a new *ServiceAccountsController.
func
NewServiceAccountsController
(
cl
client
.
Interface
,
options
ServiceAccountsControllerOptions
)
*
ServiceAccountsController
{
e
:=
&
ServiceAccountsController
{
client
:
cl
,
names
:
options
.
Name
s
,
client
:
cl
,
serviceAccountsToEnsure
:
options
.
ServiceAccount
s
,
}
accountSelector
:=
fields
.
Everything
()
if
len
(
options
.
Name
s
)
==
1
{
if
len
(
options
.
ServiceAccount
s
)
==
1
{
// If we're maintaining a single account, we can scope the accounts we watch to just that name
accountSelector
=
fields
.
SelectorFromSet
(
map
[
string
]
string
{
client
.
ObjectNameField
:
options
.
Names
.
List
()[
0
]
})
accountSelector
=
fields
.
SelectorFromSet
(
map
[
string
]
string
{
client
.
ObjectNameField
:
options
.
ServiceAccounts
[
0
]
.
Name
})
}
e
.
serviceAccounts
,
e
.
serviceAccountController
=
framework
.
NewIndexerInformer
(
&
cache
.
ListWatch
{
...
...
@@ -119,8 +122,8 @@ func NewServiceAccountsController(cl client.Interface, options ServiceAccountsCo
type
ServiceAccountsController
struct
{
stopChan
chan
struct
{}
client
client
.
Interface
names
sets
.
String
client
client
.
Interface
serviceAccountsToEnsure
[]
api
.
ServiceAccount
serviceAccounts
cache
.
Indexer
namespaces
cache
.
Indexer
...
...
@@ -156,24 +159,26 @@ func (e *ServiceAccountsController) serviceAccountDeleted(obj interface{}) {
return
}
// If the deleted service account is one we're maintaining, recreate it
if
e
.
names
.
Has
(
serviceAccount
.
Name
)
{
e
.
createServiceAccountIfNeeded
(
serviceAccount
.
Name
,
serviceAccount
.
Namespace
)
for
_
,
sa
:=
range
e
.
serviceAccountsToEnsure
{
if
sa
.
Name
==
serviceAccount
.
Name
{
e
.
createServiceAccountIfNeeded
(
sa
,
serviceAccount
.
Namespace
)
}
}
}
// namespaceAdded reacts to a Namespace creation by creating a default ServiceAccount object
func
(
e
*
ServiceAccountsController
)
namespaceAdded
(
obj
interface
{})
{
namespace
:=
obj
.
(
*
api
.
Namespace
)
for
_
,
name
:=
range
e
.
names
.
List
()
{
e
.
createServiceAccountIfNeeded
(
name
,
namespace
.
Name
)
for
_
,
sa
:=
range
e
.
serviceAccountsToEnsure
{
e
.
createServiceAccountIfNeeded
(
sa
,
namespace
.
Name
)
}
}
// namespaceUpdated reacts to a Namespace update (or re-list) by creating a default ServiceAccount in the namespace if needed
func
(
e
*
ServiceAccountsController
)
namespaceUpdated
(
oldObj
interface
{},
newObj
interface
{})
{
newNamespace
:=
newObj
.
(
*
api
.
Namespace
)
for
_
,
name
:=
range
e
.
names
.
List
()
{
e
.
createServiceAccountIfNeeded
(
name
,
newNamespace
.
Name
)
for
_
,
sa
:=
range
e
.
serviceAccountsToEnsure
{
e
.
createServiceAccountIfNeeded
(
sa
,
newNamespace
.
Name
)
}
}
...
...
@@ -181,13 +186,13 @@ func (e *ServiceAccountsController) namespaceUpdated(oldObj interface{}, newObj
// * the named ServiceAccount does not already exist
// * the specified namespace exists
// * the specified namespace is in the ACTIVE phase
func
(
e
*
ServiceAccountsController
)
createServiceAccountIfNeeded
(
name
,
namespace
string
)
{
serviceAccount
,
err
:=
e
.
getServiceAccount
(
n
ame
,
namespace
)
func
(
e
*
ServiceAccountsController
)
createServiceAccountIfNeeded
(
sa
api
.
ServiceAccount
,
namespace
string
)
{
existingServiceAccount
,
err
:=
e
.
getServiceAccount
(
sa
.
N
ame
,
namespace
)
if
err
!=
nil
{
glog
.
Error
(
err
)
return
}
if
s
erviceAccount
!=
nil
{
if
existingS
erviceAccount
!=
nil
{
// If service account already exists, it doesn't need to be created
return
}
...
...
@@ -206,16 +211,13 @@ func (e *ServiceAccountsController) createServiceAccountIfNeeded(name, namespace
return
}
e
.
createServiceAccount
(
name
,
namespace
)
e
.
createServiceAccount
(
sa
,
namespace
)
}
// createServiceAccount creates a ServiceAccount with the specified name and namespace
func
(
e
*
ServiceAccountsController
)
createServiceAccount
(
name
,
namespace
string
)
{
serviceAccount
:=
&
api
.
ServiceAccount
{}
serviceAccount
.
Name
=
name
serviceAccount
.
Namespace
=
namespace
_
,
err
:=
e
.
client
.
ServiceAccounts
(
namespace
)
.
Create
(
serviceAccount
)
if
err
!=
nil
&&
!
apierrs
.
IsAlreadyExists
(
err
)
{
// createDefaultServiceAccount creates a default ServiceAccount in the specified namespace
func
(
e
*
ServiceAccountsController
)
createServiceAccount
(
sa
api
.
ServiceAccount
,
namespace
string
)
{
sa
.
Namespace
=
namespace
if
_
,
err
:=
e
.
client
.
ServiceAccounts
(
namespace
)
.
Create
(
&
sa
);
err
!=
nil
&&
!
apierrs
.
IsAlreadyExists
(
err
)
{
glog
.
Error
(
err
)
}
}
...
...
pkg/controller/serviceaccount/serviceaccounts_controller_test.go
View file @
b9f31ca2
...
...
@@ -151,7 +151,10 @@ func TestServiceAccountCreation(t *testing.T) {
for
k
,
tc
:=
range
testcases
{
client
:=
testclient
.
NewSimpleFake
(
defaultServiceAccount
,
managedServiceAccount
)
options
:=
DefaultServiceAccountsControllerOptions
()
options
.
Names
=
sets
.
NewString
(
defaultName
,
managedName
)
options
.
ServiceAccounts
=
[]
api
.
ServiceAccount
{
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
defaultName
}},
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
managedName
}},
}
controller
:=
NewServiceAccountsController
(
client
,
options
)
if
tc
.
ExistingNamespace
!=
nil
{
...
...
plugin/pkg/admission/serviceaccount/admission.go
View file @
b9f31ca2
...
...
@@ -20,6 +20,7 @@ import (
"fmt"
"io"
"math/rand"
"strconv"
"time"
"k8s.io/kubernetes/pkg/admission"
...
...
@@ -39,12 +40,19 @@ import (
// DefaultServiceAccountName is the name of the default service account to set on pods which do not specify a service account
const
DefaultServiceAccountName
=
"default"
// EnforceMountableSecretsAnnotation is a default annotation that indicates that a service account should enforce mountable secrets.
// The value must be true to have this annotation take effect
const
EnforceMountableSecretsAnnotation
=
"kubernetes.io/enforce-mountable-secrets"
// DefaultAPITokenMountPath is the path that ServiceAccountToken secrets are automounted to.
// The token file would then be accessible at /var/run/secrets/kubernetes.io/serviceaccount
const
DefaultAPITokenMountPath
=
"/var/run/secrets/kubernetes.io/serviceaccount"
// PluginName is the name of this admission plugin
const
PluginName
=
"ServiceAccount"
func
init
()
{
admission
.
RegisterPlugin
(
"ServiceAccount"
,
func
(
client
client
.
Interface
,
config
io
.
Reader
)
(
admission
.
Interface
,
error
)
{
admission
.
RegisterPlugin
(
PluginName
,
func
(
client
client
.
Interface
,
config
io
.
Reader
)
(
admission
.
Interface
,
error
)
{
serviceAccountAdmission
:=
NewServiceAccount
(
client
)
serviceAccountAdmission
.
Run
()
return
serviceAccountAdmission
,
nil
...
...
@@ -183,7 +191,7 @@ func (s *serviceAccount) Admit(a admission.Attributes) (err error) {
return
admission
.
NewForbidden
(
a
,
fmt
.
Errorf
(
"service account %s/%s was not found, retry after the service account is created"
,
a
.
GetNamespace
(),
pod
.
Spec
.
ServiceAccountName
))
}
if
s
.
LimitSecretReferences
{
if
s
.
enforceMountableSecrets
(
serviceAccount
)
{
if
err
:=
s
.
limitSecretReferences
(
serviceAccount
,
pod
);
err
!=
nil
{
return
admission
.
NewForbidden
(
a
,
err
)
}
...
...
@@ -203,6 +211,21 @@ func (s *serviceAccount) Admit(a admission.Attributes) (err error) {
return
nil
}
// enforceMountableSecrets indicates whether mountable secrets should be enforced for a particular service account
// A global setting of true will override any flag set on the individual service account
func
(
s
*
serviceAccount
)
enforceMountableSecrets
(
serviceAccount
*
api
.
ServiceAccount
)
bool
{
if
s
.
LimitSecretReferences
{
return
true
}
if
value
,
ok
:=
serviceAccount
.
Annotations
[
EnforceMountableSecretsAnnotation
];
ok
{
enforceMountableSecretCheck
,
_
:=
strconv
.
ParseBool
(
value
)
return
enforceMountableSecretCheck
}
return
false
}
// getServiceAccount returns the ServiceAccount for the given namespace and name if it exists
func
(
s
*
serviceAccount
)
getServiceAccount
(
namespace
string
,
name
string
)
(
*
api
.
ServiceAccount
,
error
)
{
key
:=
&
api
.
ServiceAccount
{
ObjectMeta
:
api
.
ObjectMeta
{
Namespace
:
namespace
}}
...
...
plugin/pkg/admission/serviceaccount/admission_test.go
View file @
b9f31ca2
...
...
@@ -428,6 +428,36 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) {
}
}
func
TestAllowUnreferencedSecretVolumesForPermissiveSAs
(
t
*
testing
.
T
)
{
ns
:=
"myns"
admit
:=
NewServiceAccount
(
nil
)
admit
.
LimitSecretReferences
=
false
admit
.
RequireAPIToken
=
false
// Add the default service account for the ns into the cache
admit
.
serviceAccounts
.
Add
(
&
api
.
ServiceAccount
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
DefaultServiceAccountName
,
Namespace
:
ns
,
Annotations
:
map
[
string
]
string
{
EnforceMountableSecretsAnnotation
:
"true"
},
},
})
pod
:=
&
api
.
Pod
{
Spec
:
api
.
PodSpec
{
Volumes
:
[]
api
.
Volume
{
{
VolumeSource
:
api
.
VolumeSource
{
Secret
:
&
api
.
SecretVolumeSource
{
SecretName
:
"foo"
}}},
},
},
}
attrs
:=
admission
.
NewAttributesRecord
(
pod
,
"Pod"
,
ns
,
"myname"
,
string
(
api
.
ResourcePods
),
""
,
admission
.
Create
,
nil
)
err
:=
admit
.
Admit
(
attrs
)
if
err
==
nil
{
t
.
Errorf
(
"Expected rejection for using a secret the service account does not reference"
)
}
}
func
TestAllowsReferencedImagePullSecrets
(
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