-
Notifications
You must be signed in to change notification settings - Fork 38.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use service accounts as clients for controllers #33310
use service accounts as clients for controllers #33310
Conversation
// | ||
// CAUTION: If you update code in this file, you may need to also update code | ||
// in contrib/mesos/pkg/controllermanager/controllermanager.go | ||
package app |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move to a sub-package if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move to a sub-package if possible.
Sure.
I love that you are making progress on this issue. I want to get feedback from @cjcullen and @roberthbailey In follow ons to this PR, David is planning to have each controller of the controller manager talk to the apiserver with a different service account for each sub-process of the controller manager. That seems good to me because it allows for fault isolation of the many pieces of code in the controller manager. The thing David is doing this in this PR is he is making it possible to mint several new service account tokens at kube-controller-manager startup time. CJ, Robert and I had been thinking about having some kind of mapping between Kubernetes service accounts and GCP service accounts. I had thought maybe one way to establish this mapping is to have a controller that sees service accounts made in the kubernetes API, and then does some stuff with the GCP API and provides a GCP-managed credential. It would be harder to do this if you do this PR, because there is no service account object to watch. Is it possible to require that the service accounts for the controller manager exist ahead of time as addon objects, and that the kube-controller-manager mounts all the tokens it needs? That would make upgrades crazy tricky though. |
|
||
watcher, err := b.CoreClient.Secrets(b.Namespace).Watch( | ||
api.ListOptions{ | ||
ResourceVersion: "0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm seeing this pop up more and more… why is it needed? Isn't it implicitly 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm seeing this pop up more and more… why is it needed? Isn't it implicitly 0?
I didn't look it up. I would have expected an implicit ""
, which I would have expected to be "start from now".
Not sure I understand this comment… this PR is creating service account objects, then watching for their API credentials |
Oh, sorry. I skimmed it first and made some assumptions. Okay, my next question is how is the process going to work when someone adds a new control loop to controller manager. If RBAC were the only form of Authz, then in a single PR, they can:
But, if a different authorizer is in use, then it is going to be hard to do that in a single PR. |
I'd like to get to the point of "merge won't pass without RBAC permissions set properly for controller". That would likely be setting an e2e run with RBAC on and the insecure port disabled. Other authorizers would be able to add the permissions at their leisure. Because of the way the controllers work, the forbiddens would simply cause the controller to requeue items until they were able to update them. |
It's a string, so "" is the default, not "0". It is correct to specify "0". On Thu, Sep 22, 2016 at 8:32 PM, David Eads notifications@github.com
|
What is the distinction between "" and "0"? |
"" gets interpreted as unset, which I believe will be interpreted as "give On Fri, Sep 23, 2016 at 2:00 PM, Jordan Liggitt notifications@github.com
|
If that's the case, won't "0" cause a 410 error once resourceVersion 0 is outside the watch window? |
Yes, it will. I thought this was test code, though? On Fri, Sep 23, 2016 at 2:19 PM, Jordan Liggitt notifications@github.com
|
No, not test code. I really thought omitted resourceVersion and resourceVersion "0" were equivalent, and that 0 was special cased in etcd storage and in the watch cache, but I'm not at a computer to verify |
See also kubernetes/pkg/kubectl/cmd/get.go Lines 229 to 233 in 7000e2c
|
If you add a new service accounts called "deployment-controller" in namespace "kube-system", and the deployment controller loop uses that service account, then all the e2es which use non-RBAC authorizers will not pass. So you won't be able to merge that PR. So, is your plan:
|
Pulling the list from the current CI:
Do you know which ones (if any) we'd be able to get to switch to using RBAC? We could also consider things like having the other authorizer whitelist SAs from |
1f0f79e
to
f1e9766
Compare
Comments addressed. |
var clientBuilder controller.ControllerClientBuilder | ||
if len(s.ServiceAccountKeyFile) > 0 { | ||
clientBuilder = controller.SAControllerClientBuilder{ | ||
ClientConfig: kubeconfig, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to remove the credentials from the client config (upstream AnonymousClientConfig
?)
// config returns a complete clientConfig for constructing clients. This is separate in anticipation of composition | ||
// which means that not all clientsets are known here | ||
func (b SAControllerClientBuilder) Config(name string) (*restclient.Config, error) { | ||
clientConfig := *b.ClientConfig |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or remove credentials from the copy here
if err != nil && !apierrors.IsNotFound(err) { | ||
return nil, err | ||
} else if apierrors.IsNotFound(err) { | ||
sa, err = b.CoreClient.ServiceAccounts(b.Namespace).Create( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to ensure the namespace exists as well
case watch.Deleted: | ||
return false, nil | ||
case watch.Error: | ||
return false, fmt.Errorf("error watching") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with high write loads, we could exceed the watch window before we get a token. add a TODO to ListWatchUntil to handle re-establishment, and include the watch error we encountered in the returned error message
case watch.Error: | ||
return false, fmt.Errorf("error watching") | ||
case watch.Added, watch.Modified: | ||
secret := event.Object.(*api.Secret) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
serviceaccount.IsServiceAccountToken(secret, sa)
will do the type/name/uid checks for you
len(secret.Data[api.ServiceAccountTokenKey]) == 0 { | ||
return false, nil | ||
} | ||
clientConfig.BearerToken = string(secret.Data[api.ServiceAccountTokenKey]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
restclient.AddUserAgent(&clientConfig, serviceaccount.MakeUsername(b.Namespace, name))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO or follow-up issue to verify the token is valid? c.f. openshift/origin#3498
@@ -206,20 +220,50 @@ func Run(s *options.CMServer) error { | |||
panic("unreachable") | |||
} | |||
|
|||
func StartControllers(s *options.CMServer, kubeconfig *restclient.Config, stop <-chan struct{}, recorder record.EventRecorder) error { | |||
func StartControllers(s *options.CMServer, kubeconfig *restclient.Config, rootClientBuilder, clientBuilder controller.ControllerClientBuilder, stop <-chan struct{}, recorder record.EventRecorder) error { | |||
client := func(userAgent string) clientset.Interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/userAgent/serviceAccountName/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and sweep what we're passing in as userAgent to make sure we're happy with the names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and sweep what we're passing in as userAgent to make sure we're happy with the names
those haven't changed.
f1e9766
to
3f8927b
Compare
3f8927b
to
59156de
Compare
Jenkins GCI GKE smoke e2e failed for commit 59156deae9e02b96cb781605b339026e6891e706. Full PR test history. The magic incantation to run this job again is |
@liggitt before I rebase, looks ok? |
LGTM |
even assuming rbac, will need a way to coordinate the names used for the service accounts (here in the controllermanager) and the auto-assigned rolebindings giving those permissions (in the apiserver bootstrap) |
59156de
to
8ea2acc
Compare
also needs to give deployers a way to continue using the kubeconfig credentials for all the controllers, for those who have secured setups and authz already in place, and aren't interested in subdividing |
opened #34133 |
@k8s-bot test this [submit-queue is verifying that this PR is safe to merge] |
Automatic merge from submit-queue |
Makes it possible for the controller-manager to use service accounts to run individual controllers. To start, this only enables this feature if this particular controller manager has the power to create service account tokens. Otherwise, the full-powered client is used instead. This is a necessary step on the way to subdividing the authority of controllers.
@kubernetes/sig-auth
@erictune I know you care about this
@ncdc fyi
This change is![Reviewable](https://camo.githubusercontent.com/2d899f4291d07d3cd2fa4aaae1e3b243f164c23fce87d30a589ace0d496a444c/68747470733a2f2f72657669657761626c652e6b756265726e657465732e696f2f7265766965775f627574746f6e2e737667)