How to use Kubernetes External Secrets Operator with Private Container Registry
We show how to streamline the management of pull secrets of private repositories such as GitLab with a combination of External Secrets Operator and Doppler.
Most images we deploy to a Kubernetes cluster come from a private container registry.
Typically this registry is provided by the cloud platform that manages the Kubernetes cluster, and they are well integrated, so
you do not need to make extra efforts to make it work. But sometimes, the cloud platform does not have an integrated repository, or the company policy does not allow to use of the one provided by the platform. In that case, you have to provide
imagePullSecret
in your Kubernetes manifest. The Kubernetes documentation has a good description of how to do this. However, that method can be further streamlined
with the External Secrets Operator and its templating capability, so you do not need to manage the secret, the dockerconfig json file and its base64 encoding manually. In the following, we show how
to combine the use of Doppler security operation platform and the External Secret Operator to deploy images to Kubernetes from the Gitlab private container registry.
In the following, we assume that:
- you set up your Doppler projects, environments (they call it configs), and its command line client
doppler
, - you set up the Gitlab container registry and pushed an image there,
- you have a Kubernetes cluster configured to be accessible using
kubectl
andhelm
, - you have a namespace in Kubernetes called
test
where you want to deploy, - and finally, your shell is configured to not save commands to the history that starts with space
Setting up External Secrets Operator with Doppler
First - if you have not already - you should install the External Secrets Operator using helm
:
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets \
external-secrets/external-secrets \
-n external-secrets \
--create-namespace \
--set installCRDs=true
Then you create a SecretStore
or ClusterSecretStore
custom resource configured to use Doppler.
We prefer SecretStore
since it is a namespaced resource, allowing better separation and access control.
The SecretStore
needs a Kubernetes Secret
that contains a Doppler access token. Every Doppler access token is
scoped to a specific project and config (environment) combination. You can create one named by, for example, external-secret-operator
using doppler's CLI:
❯ doppler --project <project name> --config <config name> configs tokens create external-secret-operator --plain
dp.st.XXXX
Using the returned token, you can create a Kubernetes secret that holds this token:
❯ kubectl create secret generic doppler-auth-token --namespace test --from-literal dopplerToken="dp.st.XXXX"
(be aware of the extra space at the beginning of the command not to save it to history)
Then to create a SecretStore
:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: doppler-secret-store
namespace: test
spec:
provider:
doppler:
auth:
secretRef:
dopplerToken:
name: doppler-auth-token
key: dopplerToken
Sync Gitlab Docker Registry Credentials from Doppler to Kubernetes
First, you create a deploy token on Gitlab. You can do that under the Project page / Settings / Repository / Deploy Tokens. The scope should be read_repository
. Put the created access token username and password to Doppler under GITLAB_DEPLOY_TOKEN_USERNAME
and GITLAB_DEPLOY_TOKEN_PASSWORD
, respectively. Further, add the following secrets to Doppler:
GITLAB_REGISTRY_HOST
: the hostname of the Gitlab Private Repository. Typicallyregistry.gitlab.com
.GITLAB_REGISTRY_EMAIL
: use your GitLab email address
The following manifest creates an ExternalSecret
named registry-credetial
that uses the previously defined Doppler secrets and the SecretStore
to create and sync a Kubernetes Secret named gitlab-registry-credential
with properly formatted .dockerconfigjson
data using Go template. You can adjust the refreshInterval
during development to lower value to get synced faster.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: registry-credential
namespace: test
spec:
refreshInterval: 3600s
secretStoreRef:
kind: SecretStore
name: doppler-secret-store
target:
name: gitlab-registry-credential
template:
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: |
{
"auths": {
"{{ .registry }}": {
"username": "{{ .username }}",
"password": "{{ .password }}",
"email": "{{ .registryEmail }}",
"auth": "{{ list .username .password | join ":" | b64enc }}"
}
}
}
data:
- secretKey: registry
remoteRef:
key: GITLAB_REGISTRY_HOST
- secretKey: registryEmail
remoteRef:
key: GITLAB_REGISTRY_EMAIL
- secretKey: username
remoteRef:
key: GITLAB_DEPLOY_TOKEN_USERNAME
- secretKey: password
remoteRef:
key: GITLAB_DEPLOY_TOKEN_PASSWORD
Using Secret to deploy from a private repository
The following manifest show how to create a Deployment
that uses the previously defined gitlab-registry-credential
Kubernetes secret as a imagePullSecrets
. Important to note that the imagePullSecret
name is the target secret name of the ExternalSecret
, not the external secret itself.
apiVersion: apps/v1
kind: Deployment
metadata:
name: pionlab-server
namespace: test
spec:
replicas: 1
selector:
matchLabels:
app: pionlab-server
template:
metadata:
labels:
app: pionlab-server
spec:
containers:
- name: pionlab-server
image: registry.gitlab.com/pionlab/pionlab:latest
imagePullPolicy: Always
args:
- ./scripts/runserver.sh
envFrom:
- secretRef:
name: django-environment-vars
imagePullSecrets:
- name: gitlab-registry-credential
You can find other ways to utilize Doppler configs as environment variables for deployment in Doppler documentation.
This article was originally posted on the Pionlab Blog: pionlab.com/blog/k8s-external-secrets-priva..