{"action":"created","issue":{"url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/7101","labels_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/7101/labels{/name}","comments_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/7101/comments","events_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/7101/events","html_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7101","id":69784234,"number":7101,"title":"WIP - ServiceAccounts","user":{"login":"liggitt","id":980082,"avatar_url":"https://avatars.githubusercontent.com/u/980082?v=3","gravatar_id":"","url":"https://api.github.com/users/liggitt","html_url":"https://github.com/liggitt","followers_url":"https://api.github.com/users/liggitt/followers","following_url":"https://api.github.com/users/liggitt/following{/other_user}","gists_url":"https://api.github.com/users/liggitt/gists{/gist_id}","starred_url":"https://api.github.com/users/liggitt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/liggitt/subscriptions","organizations_url":"https://api.github.com/users/liggitt/orgs","repos_url":"https://api.github.com/users/liggitt/repos","events_url":"https://api.github.com/users/liggitt/events{/privacy}","received_events_url":"https://api.github.com/users/liggitt/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/labels/cla:%20no","name":"cla: no","color":"e11d21"}],"state":"open","locked":false,"assignee":{"login":"erictune","id":8105590,"avatar_url":"https://avatars.githubusercontent.com/u/8105590?v=3","gravatar_id":"","url":"https://api.github.com/users/erictune","html_url":"https://github.com/erictune","followers_url":"https://api.github.com/users/erictune/followers","following_url":"https://api.github.com/users/erictune/following{/other_user}","gists_url":"https://api.github.com/users/erictune/gists{/gist_id}","starred_url":"https://api.github.com/users/erictune/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/erictune/subscriptions","organizations_url":"https://api.github.com/users/erictune/orgs","repos_url":"https://api.github.com/users/erictune/repos","events_url":"https://api.github.com/users/erictune/events{/privacy}","received_events_url":"https://api.github.com/users/erictune/received_events","type":"User","site_admin":false},"milestone":null,"comments":8,"created_at":"2015-04-21T08:41:27Z","updated_at":"2015-04-24T22:50:21Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/7101","html_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7101","diff_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7101.diff","patch_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7101.patch"},"body":"This is a first pass at ServiceAccount objects and associated tokens.\r\n\r\n### Overview:\r\n\r\n- [x] ServiceAccount API objects\r\n- [x] ServiceAccountToken secret type\r\n- [x] Controller to manage ServiceAccountToken secrets\r\n  - ensures a ServiceAccountToken secret exists for every ServiceAccount\r\n  - ensures ServiceAccountToken secrets are deleted when the corresponding ServiceAccount is deleted\r\n- [x] Controller to ensure a ServiceAccount (probably named \"default\") exists in every namespace\r\n- [x] Admission plugin:\r\n  - set default service account on pods without a specified serviceAccount\r\n  - disallow pods which reference a serviceAccount that does not exist\r\n  - mount the pod's service account's APISecret at a well-known location (`/var/run/secrets/kubernetes.io/serviceaccount`)\r\n- [ ] Think about `ServiceAccount.APISecret` field name\r\n- [ ] Make `ServiceAccount.Secrets` patchable/mergeable (map instead of list?)\r\n- [ ] Authenticator stub that validates service account tokens\r\n- [ ] Real token generator function... probably a signed JWT with claims:\r\n  - iss (issuer, `kubernetes.io/ServiceAccountToken`, etc)\r\n  - sub (subject/username, `serviceaccount:[namespace]:[service account name]`, etc)\r\n  - groups? (could put all service accounts in a namespace in a group)\r\n  - namespace\r\n  - serviceAccountName\r\n  - serviceAccountUID\r\n  - secretName\r\n  - secretUID\r\n- [ ] Authenticator that validates real signed tokens\r\n  - validate well-formedness first\r\n  - validate signature (ensures the token controller was the one that generated it)\r\n  - then looks up secret\r\n    - validates its name/existence name (ensures the token hasn't been revoked)\r\n    - validates secret UID matches claim (ensures the secret hasn't been deleted and recreated)\r\n    - validates the `serviceAccountName`/`serviceAccountUID` annotations on the secret\r\n  - uses `sub`, `serviceAccountUID`, and `groups` claims from token to build `user.Info`\r\n- [ ] Work with @pmorie to update Kubelet to filter secrets for a pod to the ones referenced by the pod's ServiceAccount (might want to expose `/pods/[pod]/secrets` to make this easier)\r\n- [ ] Generate `kubernetes.kubeconfig` in addition to the token? (requires knowing the hostname and ca bundle clients will need to use to contact the API... do we know that?)\r\n- [ ] Add controller and admission tests\r\n- [ ] Work with @pweil- on interaction with securitycontext\r\n\r\n\r\n### Sample usage\r\n\r\n#### To create a service account:\r\n```\r\nserviceaccount.json:\r\n{\r\n\t\"kind\": \"ServiceAccount\",\r\n\t\"metadata\": {\r\n\t\t\"name\": \"myserviceaccount\"\r\n\t}\r\n}\r\n\r\n$ kubectl create -f serviceaccount.json\r\n```\r\n\r\n#### To delete a service account:\r\n```\r\n$ kubectl delete serviceaccount/myserviceaccount\r\n```\r\n\r\n#### To create additional API tokens\r\nA controller loop ensures a secret with an API token exists for each service account. To create additional API tokens for a service account:\r\n```\r\nsecret.json:\r\n{\r\n\t\"kind\": \"Secret\",\r\n\t\"metadata\": {\r\n\t\t\"name\": \"mysecretname\",\r\n\t\t\"annotations\": {\r\n\t\t\t\"serviceAccountName\": \"myserviceaccount\"\r\n\t\t}\r\n\t}\r\n\t\"type\": \"ServiceAccountToken\"\r\n}\r\n\r\n$ kubectl create -f secret.json\r\n```\r\n\r\n#### To delete/invalidate a service account token:\r\n```\r\nkubectl delete secret mysecretname\r\n```\r\n\r\n#### To set a pod's service account\r\n\r\nCreate or update the pod with the `spec.serviceAccount` field set to the name of a `ServiceAccount`. That field is set to the name of the default `ServiceAccount` if empty.\r\n\r\n#### To mount the APISecret in containers\r\n\r\nHa! Trick step. You don't have to do anything... the ServiceAccount admission plugin automatically mounts the pod's `spec.serviceAccount`'s `apiSecret` at `/var/run/secrets/kubernetes.io/serviceaccount`. That means containers can count on an API token existing at `/var/run/secrets/kubernetes.io/serviceaccount/token`"},"comment":{"url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/comments/96089231","html_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7101#issuecomment-96089231","issue_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/7101","id":96089231,"user":{"login":"erictune","id":8105590,"avatar_url":"https://avatars.githubusercontent.com/u/8105590?v=3","gravatar_id":"","url":"https://api.github.com/users/erictune","html_url":"https://github.com/erictune","followers_url":"https://api.github.com/users/erictune/followers","following_url":"https://api.github.com/users/erictune/following{/other_user}","gists_url":"https://api.github.com/users/erictune/gists{/gist_id}","starred_url":"https://api.github.com/users/erictune/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/erictune/subscriptions","organizations_url":"https://api.github.com/users/erictune/orgs","repos_url":"https://api.github.com/users/erictune/repos","events_url":"https://api.github.com/users/erictune/events{/privacy}","received_events_url":"https://api.github.com/users/erictune/received_events","type":"User","site_admin":false},"created_at":"2015-04-24T22:50:21Z","updated_at":"2015-04-24T22:50:21Z","body":"What is the story about updating a token and its dependent processes?  \r\n\r\nI guess the controller could just update the Secret when it is near expiry, and existing pods would keep running with their already mounted data, and newly created pods would use the new secret?  Some other thing could make sure all the pods that used the old secret were re-made.\r\n"}}