Cluster auth providers
How convoy builds the connection to a target cluster and how the source controllers authenticate.
The package internal/clusterconn makes a rest.Config for the target cluster of a Cluster.
The cluster controller, the release controller and the observer use it. The source controllers
do not use it. Each source kind has its own credential code. All of them read credentials with
the package internal/auth. For the auth types of each kind, see
Credentials.
Connector#
Connector.Connect selects the connection by spec.connectionMode:
| Mode | Result |
|---|---|
inCluster | The rest.Config of the convoy process itself. |
agentless | The authenticator in the registry for spec.agentless.auth.type makes the config. |
agent | ErrUnsupported. Not built |
| Other value | ErrUnsupported |
For inCluster, the config is the config that the manager built at start. That is the
in-cluster service account config, or the --kubeconfig file when that flag is set. Thus with
--kubeconfig, an inCluster Cluster targets the cluster of that file.
Connector.DynamicClients calls Connect and then makes a dynamic client and a RESTMapper from
discovery. It does this at each call. There is no cache of clients or credentials. The deployer,
the hook runner and the observer use DynamicClients. The cluster prober uses Connect and then
reads the server version and the API groups.
Authenticator registry#
clusterconn.Registry maps an auth type to an Authenticator. Both managers make the same
registry with one entry:
clusterAuth := clusterconn.Registry{
corev1alpha1.ClusterAuthTypeKubeconfig: auth.KubeconfigAuthenticator{},
}KubeconfigAuthenticator reads the Secret spec.agentless.auth.kubeconfig.secretRef in the
namespace of the Cluster. It parses the key with clientcmd.RESTConfigFromKubeConfig. The
kubeconfig gives the server, the CA and the credentials. The authenticator ignores the server and
the CA fields of the Cluster.
Error classes#
Connect returns errors in two classes:
| Error | Cause | Cluster Ready reason | Retry |
|---|---|---|---|
clusterconn.ErrUnsupported | Mode agent, an unknown mode, or an auth type that is not in the registry | Unsupported | No. The cluster controller returns no error. |
clusterconn.ErrProbeFailed | The authenticator failed, for example a missing Secret or key, or spec.agentless is empty | ProbeFailed | Yes, with the rate limiter of the queue |
The prober adds ErrProbeFailed to discovery errors. An error without a class also gives reason
ProbeFailed. The release controller applies only to a Cluster with status.reachable: true.
If the deployer returns clusterconn.ErrUnsupported, the Applied reason is Unsupported and
convoy does not retry. Other deploy errors give DeployFailed and a retry.
Secret and ConfigMap resolution#
auth.SecretResolver reads Secrets and ConfigMaps with a direct GET to the host cluster. It
does not use an informer cache. Each reference is local to the namespace of the object that
contains it.
ResolveSecretKeyreads one key fromSecret.data.ResolveConfigMapKeyreads one key fromConfigMap.data, then fromConfigMap.binaryData.- A missing name, object or key gives an error that names the object and the key.
Source authentication#
Each source kind has its own resolveAuth or authMethod function, with its own
ErrUnsupported and ErrFetchFailed errors. ErrUnsupported gives reason Unsupported and no
retry. Other errors give reason VerifyFailed and a retry.
| Kind | Type | Credential on the wire |
|---|---|---|
GitRepository | basic | HTTP basic auth with username and the password from the Secret |
GitRepository | token | HTTP basic auth with user git and the token as password |
GitRepository | sshKey | SSH public key auth with user git, with host key verification |
HelmRegistry | basic | HTTP basic auth on each request |
HelmRegistry | token | Authorization: Bearer <token> on each request |
OCIRegistry | basic | Registry login with username and the password |
OCIRegistry | token | Registry login with user convoy and the token as password |
All three kinds accept none. Each other type returns ErrUnsupported. A selected type without
its configuration block also returns ErrUnsupported. A Secret read error gives ErrFetchFailed
for Helm and OCI. For Git, the error has no class, so the result is also VerifyFailed and a
retry.
The verify step differs between the kinds:
- Git runs
ls-remote. It verifies the address and the credentials. - Helm downloads
index.yamlwith the credentials. - OCI sends
GET https://<host>/v2/. A200or a401passes. Thus the verify step does not verify the OCI credentials. convoy verifies the credentials at the first pull.
For Helm, the fetch sends the credentials also to the chart URL from index.yaml. That URL can
be on a different host than the registry.
SSH host keys#
sshKey auth requires knownHostsRef. There is no mode that skips the host key verification.
knownHostsCallback parses the known_hosts data in memory and writes no file:
- It accepts plaintext host entries, more than one key type for each host, and
@revokedentries. - It rejects hashed entries (
|1|…) withErrUnsupported. - It skips
@cert-authorityentries. CA-based host verification is not supported. - Data with no usable entry gives
ErrUnsupported.
At connection time, the callback compares the presented key with the entries of the host. A
@revoked match fails. A host with entries but no matching key fails with a host key mismatch. A
host with no entry fails.
TLS#
For Git, Helm and OCI, convoy reads only tls.insecureSkipVerify. It does not read
tls.caPublicCertRef. A source with a private CA must use a certificate that the system trust
store accepts, or skip the verification.
Planned providers#
These Cluster auth types are in the API but not in the registry: token, clientCert,
execPlugin and oidc. A Cluster with one of them has reason Unsupported. Not built
The planned execPlugin design calls a separate credential provider workload over the network.
It does not start a local process. Not built
In the code
convoy v0.9.0
internal/clusterconn.(*Connector).Connectinternal/clusterconn/connector.go:64internal/clusterconn.(*Connector).DynamicClientsinternal/clusterconn/connector.go:96internal/auth.KubeconfigAuthenticator.RESTConfiginternal/auth/kubeconfig.go:37internal/auth.ResolveSecretKeyinternal/auth/keyref.go:28internal/auth.ResolveConfigMapKeyinternal/auth/keyref.go:45internal/controller/gitrepository.authMethodinternal/controller/gitrepository/authenticator.go:48internal/controller/gitrepository.knownHostsCallbackinternal/controller/gitrepository/authenticator.go:126internal/controller/helmregistry.(*helmFetcher).resolveAuthinternal/controller/helmregistry/fetcher.go:192internal/controller/ociregistry.(*ociFetcher).resolveAuthinternal/controller/ociregistry/fetcher.go:107internal/controller/cluster.computeStatusinternal/controller/cluster/reconcile.go:45