Skip to content

Commit 6358cda

Browse files
committed
Exec based credential provider proposal
1 parent e993791 commit 6358cda

File tree

1 file changed

+168
-33
lines changed

1 file changed

+168
-33
lines changed

keps/sig-cloud-provider/20191004-out-of-tree-credential-providers.md

Lines changed: 168 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@
22
title: Out-of-Tree Credential Providers
33
authors:
44
- "@mcrute"
5+
- "@nckturner"
56
owning-sig: sig-cloud-provider
67
participating-sigs:
78
- sig-node
89
- sig-auth
910
reviewers:
1011
- "@andrewsykim"
1112
- "@cheftako"
12-
- "@nckturner"
13+
- "@tallclair"
14+
- "@mikedanese"
1315
approvers:
14-
- TBD
16+
- "@andrewsykim"
17+
- "@cheftako"
18+
- "@tallclair"
19+
- "@mikedanese"
1520
editor: TBD
1621
creation-date: 2019-10-04
17-
last-updated: 2019-10-16
18-
status: provisional
22+
last-updated: 2019-12-10
23+
status: implementable
1924
---
2025

2126
# Out-of-Tree Credential Providers
@@ -29,9 +34,13 @@ status: provisional
2934
- [Goals](#goals)
3035
- [Non-Goals](#non-goals)
3136
- [Proposal](#proposal)
32-
- [API Server Proxy](#api-server-proxy)
33-
- [Sidecar Credential Daemon](#sidecar-credential-daemon)
34-
- [External Non-Daemon Executable](#external-non-daemon-executable)
37+
- [External Credential Provider](#external-credential-provider)
38+
- [Example](#example)
39+
- [Alternatives Considered](#alternatives-considered)
40+
- [API Server Proxy](#api-server-proxy)
41+
- [Sidecar Credential Daemon](#sidecar-credential-daemon)
42+
- [Bound Service Account Token Flow](#bound-service-account-token-flow)
43+
- [Pushing Credential Management into the CRI](#pushing-credential-management-into-the-cri)
3544
- [Risks and Mitigations](#risks-and-mitigations)
3645
- [Design Details](#design-details)
3746
- [Test Plan](#test-plan)
@@ -44,74 +53,200 @@ status: provisional
4453

4554
## Release Signoff Checklist
4655

47-
- [ ] kubernetes/enhancements issue in release milestone, which links to KEP (this should be a link to the KEP location in kubernetes/enhancements, not the initial KEP PR)
48-
- [ ] KEP approvers have set the KEP status to `implementable`
49-
- [ ] Design details are appropriately documented
50-
- [ ] Test plan is in place, giving consideration to SIG Architecture and SIG Testing input
51-
- [ ] Graduation criteria is in place
56+
- [x] kubernetes/enhancements issue in release milestone, which links to KEP (this should be a link to the KEP location in kubernetes/enhancements, not the initial KEP PR)
57+
- [x] KEP approvers have set the KEP status to `implementable`
58+
- [x] Design details are appropriately documented
59+
- [x] Test plan is in place, giving consideration to SIG Architecture and SIG Testing input
60+
- [x] Graduation criteria is in place
5261
- [ ] "Implementation History" section is up-to-date for milestone
5362
- [ ] User-facing documentation has been created in [kubernetes/website], for publication to [kubernetes.io]
5463
- [ ] Supporting documentation e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes
5564

5665
## Summary
5766

58-
Replace the existing in-tree container image registry (registry) credential providers with an external and pluggable credential provider mechanism and remove in-tree credential providers.
67+
This KEP replaces the existing in-tree container image registry credential providers with an external and pluggable credential provider mechanism and removes the in-tree credential providers.
5968

6069
## Motivation
6170

62-
kubelet uses cloud provider specific SDKs to obtain credentials when pulling container images from cloud provider specific registries. The use of cloud provider specific SDKs from within the main Kubernetes tree is deprecated by [KEP-0002](https://github.com/kubernetes/enhancements/blob/master/keps/sig-cloud-provider/20180530-cloud-controller-manager.md) and all existing uses need to be migrated out-of-tree. This KEP supports that migration process by removing this SDK usage.
63-
64-
In addition to supporting cloud provider migration this KEP supports allowing users to support multiple container registries across different cloud providers by introducing a pluggable interface to cloud specific credential providers such that a single user could run multiple credential providers for different clouds at the same time. Currently this functionality is gated by having only a single, active cloud provider within the API server and kubelet.
71+
Kubelet uses cloud provider specific SDKs to obtain credentials when pulling container images from cloud provider specific registries. The use of cloud provider specific SDKs from within the main Kubernetes tree is deprecated by [KEP-0002](https://github.com/kubernetes/enhancements/blob/master/keps/sig-cloud-provider/20180530-cloud-controller-manager.md) and all existing uses need to be migrated out-of-tree. This KEP supports that migration process by removing this SDK usage.
6572

6673
### Goals
6774

68-
* Develop/test/release an API for kubelet to obtain registry credentials from the API server
75+
* Develop/test/release an interface for kubelet to obtain registry credentials from a cloud provider specific binary
6976
* Update/test/release the credential acquisition logic within kubelet
7077
* Build user documentation for out-of-tree credential providers
71-
* Migrate existing in-tree credential providers to new credential provider interface
72-
* Remove in-tree credential provider code from kuberentes core
78+
* Support migration from existing in-tree credential providers to the new credential provider interface, along with dynamic roll back.
79+
* Remove in-tree credential provider code from Kubernetes core
7380

7481
### Non-Goals
7582

76-
* Broad removal of cloud SDK usage, this effort falls under the [KEP for removing in-tree providers](https://github.com/kubernetes/enhancements/blob/master/keps/sig-cloud-provider/2019-01-25-removing-in-tree-providers.md).
83+
* Broad removal of cloud SDK usage falls under the [KEP for removing in-tree providers](https://github.com/kubernetes/enhancements/blob/master/keps/sig-cloud-provider/2019-01-25-removing-in-tree-providers.md).
84+
* Continuing to support projects that import the credential provider package.
7785

7886
## Proposal
7987

80-
### API Server Proxy
81-
82-
The API server will act as a proxy to an external container registry credential provider that may support multiple cloud providers. The credential provider service will return container runtime compatible responses of the type currently used by the credential provider infrastructure within the kubelet along with credential expiration information to allow the API server to cache credential responses for a period of time.
88+
### External Credential Provider
89+
90+
An executable capable of providing container registry credentials will be pre-installed on each node so that it exists when kubelet starts running. This binary will be executed by the kubelet to obtain container registry credentials in a format compatible with container runtimes. Credential responses may be cached within the kubelet.
91+
92+
This architecture is similar to the approach taken by the exec based credential plugin architecture already present in client-go and CNI, and is a well understood pattern. The API types are modeled after the ExecConfig and ExecCredential in client-go which define exec based credential retrieval for similar use cases.
93+
94+
A `RegistryCredentialConfig` and `RegistryCredentialProvider` configuration API type (similar to [clientauthentication](https://github.com/kubernetes/kubernetes/tree/0273d43ae9486e9d0be292c01de2dd4143522b86/staging/src/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1)) will be added to Kubernetes:
95+
96+
```go
97+
type RegistryCredentialConfig struct {
98+
metav1.TypeMeta `json:",inline"`
99+
100+
Providers []RegistryCredentialProvider `json:"providers"`
101+
}
102+
103+
// RegistryCredentialProvider is used by the kubelet container runtime to match the
104+
// image property string (from the container spec) with exec-based credential provider
105+
// plugins that provide container registry credentials.
106+
type RegistryCredentialProvider struct {
107+
metav1.TypeMeta `json:",inline"`
108+
109+
// ImageMatchers is a list of strings used to match against the image property
110+
// (sometimes called "registry path") to determine which images to provide
111+
// credentials for. If one of the strings matches the image property, then the
112+
// RegistryCredentialProvider will be used by kubelet to provide credentials
113+
// for the image pull.
114+
115+
// The image property of a container supports the same syntax as the docker
116+
// command does, including private registries and tags. A registry path is
117+
// similar to a URL, but does not contain a protocol specifier (https://).
118+
//
119+
// Each ImageMatcher string is a pattern which can optionally contain
120+
// a port and a path, similar to the image spec. Globs can be used in the
121+
// hostname (but not the port or the path).
122+
//
123+
// Globs are supported as subdomains (*.k8s.io) or (k8s.*.io), and
124+
// top-level-domains (k8s.*). Matching partial subdomains is also supported
125+
// (app*.k8s.io). Each glob can only match a single subdomain segment, so
126+
// *.io does not match *.k8s.io.
127+
//
128+
// The image property matches when it has the same number of parts as the
129+
// ImageMatcher string, and each part matches. Additionally the path of
130+
// ImageMatcher must be a prefix of the target URL. If the ImageMatcher
131+
// contains a port, then the port must match as well.
132+
ImageMatchers []string `json:"imageMatchers"`
133+
134+
// Exec specifies a custom exec-based plugin. This type is defined in
135+
// https://github.com/kubernetes/client-go/blob/62f256057db7571c5ed1aba47eea291f72dd557a/tools/clientcmd/api/types.go#L184
136+
Exec clientcmd.ExecConfig
137+
}
138+
```
139+
140+
The RegistryCredentialConfig will be encoded in YAML and located in a file on disk. The exact path of the credential provider configuration file will be passed to kubelet via a new configuration option `RegistryCredentialConfigPath`.
141+
142+
We will create new types `RegistryCredentialPluginRequest` and `RegistryCredentialPluginResponse` which will define the interface between the plugin and the kubelet runtime. After the kubelet matches the image property string to a RegistryCredentialProvider, the kubelet will exec the plugin binary, and pass the JSON encoded request to the plugin via stdin. This includes the image that is to be pulled. The plugin will report back the response, which includes the credentials that kubelet needs to pull the image.
143+
144+
In the in-tree implementation, the docker keyring, which has N credential providers, returns an `[]AuthConfig` on a Lookup(image string) call. This struct will be populated by the plugin rather than the in-tree provider.
145+
146+
```go
147+
// RegistryCredentialPluginRequest is passed to the plugin via stdin, and includes the image that will be pulled by kubelet.
148+
type RegistryCredentialPluginRequest struct {
149+
// Image is used when passed to registry credential providers as part of an
150+
// image pull
151+
Image string `json:"image"`
152+
}
153+
154+
// RegistryCredentialPluginResponse holds credentials for the kubelet runtime
155+
// to use for image pulls. It is returned from the plugin as stdout.
156+
type RegistryCredentialPluginResponse struct {
157+
metav1.TypeMeta `json:",inline"`
158+
159+
// +optional
160+
ExpirationTimestamp *metav1.Time `json:"expirationTimestamp,omitempty"`
161+
162+
// +optional
163+
Username *string `json:"username,omitempty"`
164+
// +optional
165+
Password *string `json:"password,omitempty"`
166+
167+
// IdentityToken is used to authenticate the user and get
168+
// an access token for the registry.
169+
// +optional
170+
IdentityToken *string `json:"identitytoken,omitempty"`
171+
172+
// RegistryToken is a bearer token to be sent to a registry
173+
// +optional
174+
RegistryToken *string `json:"registrytoken,omitempty"`
175+
}
176+
177+
```
178+
179+
### Example
180+
181+
A registry credential provider configuration for Amazon ECR could look like the following:
182+
183+
```yaml
184+
kind: credentialprovider
185+
apiVersion: v1alpha1
186+
providers:
187+
-
188+
imageMatchers:
189+
- *.dkr.ecr.*.amazonaws.com
190+
- *.dkr.ecr.*.amazonaws.com.cn
191+
exec:
192+
command: ecr-creds
193+
args: token
194+
apiVersion: v1alpha1
195+
```
196+
197+
Where ecr-creds is a binary that vends ecr credentials. This would execute the binary `ecr-creds` with the argument `token` for the image `012345678910.dkr.ecr.us-east-1.amazonaws.com/my-image`.
198+
199+
### Alternatives Considered
200+
201+
#### API Server Proxy
202+
203+
The API server would act as a proxy to an external container registry credential provider that may support multiple cloud providers. The credential provider service will return container runtime compatible responses of the type currently used by the credential provider infrastructure within the kubelet along with credential expiration information to allow the API server to cache credential responses for a period of time.
83204

84205
This limits the cloud-specific privileges required for each node for the purpose of fetching credentials. Centralized caching helps to avoid cloud-specific rate limits for credential acquisition by consolidating that credential acquisition within the API server.
85206

86-
### Sidecar Credential Daemon
207+
We chose not to follow this approach because although less privileges on each node and centralized caching are good, we have not seen enough evidence that these features are commonly requested by users. Also, it is outside the stated goals of this KEP. Lastly, taking the time to design such a system would probably take long enough to push back the date that we could extract the in-tree cloud providers completely from Kubernetes.
208+
209+
#### Sidecar Credential Daemon
87210

88-
Each node will run a sidecar credential daemon that can obtain cloud-specific container registry credentials and may support multiple cloud providers. This service will be available to the kubelet on the local host and will return container runtime responses compatible with those currently used by the credential provider infrastructure within kubelet. Each daemon will perform its own caching of credentials for the node on which it runs.
211+
Each node would run a sidecar credential daemon that can obtain cloud-specific container registry credentials and may support multiple cloud providers. This service will be available to the kubelet on the local host and will return container runtime responses compatible with those currently used by the credential provider infrastructure within kubelet. Each daemon will perform its own caching of credentials for the node on which it runs.
89212

90-
This architecture is similar to the approach taken by CSI with node plugins and is a well understood deployment pattern.It limits fleet-wide cacheability of credentials.
213+
The added complexity of running a daemon over executing a binary made this option less desirable to us. If a daemon implementation is necessary for a cloud provider, the binary can talk to one to retrieve credentials upon each execution.
91214

92-
### External Non-Daemon Executable
215+
#### Bound Service Account Token Flow
93216

94-
An executable capable of providing container registry credentials will be installed on each node. This binary will be executed by the kubelet to obtain container registry credentials in a format compatible with container runtimes. Credential responses may be cached within the kubelet.
217+
Suggested in https://github.com/kubernetes/kubernetes/issues/68810, an image pull flow built on bound service account tokens would provide kubelet with credentials to pull images for pods running as a specific service account.
95218

96-
This Architecture is similar to the approach taken by CNI and is a well understood deployment pattern. It limits fleet-wide cacheability of credentials.
219+
This approach might be better suited as a future enhancement to either the credential provider or ImagePullSecrets, but is out of scope for extracting the cloud provider specific code.
220+
221+
#### Pushing Credential Management into the CRI
222+
223+
Another possibility is moving the credential management logic into the CRI, so that Kubelet doesn't provide any credentials for image pulls. Similarly, this approach is also out of scope for extracting cloud provider code because it would be a more significant redesign but should be considered for a future enhancement.
97224

98225
### Risks and Mitigations
99226

100-
TODO
227+
This is a critical feature of kubelet and pods cannot start if it does not work correctly. This functionality will be labeled alpha and hidden behind a feature gate in v1.18. It will use DynamicKubeletConfig so that it can be disabled during runtime if any problems occur.
101228

102229
## Design Details
103230

104231
### Test Plan
105232

106-
TODO
233+
* Unit tests for image matching logic.
234+
* E2E tests for image pulls from cloud providers.
107235

108236
### Graduation Criteria
109237

110-
TODO
238+
Successful Alpha Criteria
239+
* Multiple plugin implentations created.
240+
* One E2E test implemented.
111241

112242
### Upgrade / Downgrade Strategy
113243

114-
TODO
244+
Upgrading
245+
* Add any cloud provider plugin binaries for image repositories that you use to your worker nodes.
246+
* Enable this feature in kubelet with a feature flag.
247+
248+
Downgrading
249+
* Disable this feature in kubelet with a feature flag.
115250

116251
### Version Skew Strategy
117252

0 commit comments

Comments
 (0)