18.104.22.168 Kubernetes Credential Manager
Concourse can be configured to pull credentials from Kubernetes
To configure it, either enable the in-cluster client by setting the following environment variable on the
or set the path to a
Credential lookup rules
When resolving a parameter such as
((foo)), Concourse will lookup for it in the following order in the namespace configured for that team:
Name: PIPELINE_NAME.foo Namespace: concourse-TEAM_NAME Type: Opaque Data ==== value: 32 bytes
Name: foo Namespace: concourse-TEAM_NAME Type: Opaque Data ==== value: 32 bytes
You can also have nested fields, which can be accessed using
. syntax, e.g.
Name: PIPELINE_NAME.foo Namespace: concourse-TEAM_NAME Type: Opaque Data ==== bar: 32 bytes
Name: foo Namespace: concourse-TEAM_NAME Type: Opaque Data ==== bar: 32 bytes
The prefix prepended to the namespace used by Concourse to search for secrets (in the examples above,
concourse-) can be changed by configuring the following in the
If an action is being run in a one-off build, Concourse will not include the pipeline name in the secret that it looks for.
Configuring Kubernetes RBAC
As the Web nodes need to retrieve secrets from namespaces that are not their own, they needs extra permissions to do so.
If you have RBAC enabled, that means creating the necessary Kubernetes objects to identify the Web nodes and give them access to a predefined list of namespaces where the secrets live.
Regardless of how the Kubernetes RBAC-related objects are created, the basic requirement is that
web must be able to read secrets in the namespaces where each teams' secrets live.
For instance, if you have the following teams which you want to read secrets from:
Assuming the following
web node configuration:
web must be able to
get secrets from the following namespaces:
web to interpolate credentials for "team-a" and "team-b", we'd then need to create a few Kubernetes RBAC objects.
Starting with identifying the
web service as an actor, we can use a ServiceAccount for that:
apiVersion: v1 kind: ServiceAccount metadata: name: web labels: app: web
To allow actors to do something, in this case, retrieve secrets from a given namespace, a ClusterRole is then needed.
apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: read-secrets labels: app: web rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get"]
As that role is useless if not bound to an actor, the next step is creating the the object that represents binding the role to the
ServiceAccount that we had created before.
This is accomplished through the RoleBinding object, which is per-namespace (thus, per-team).
Even though in this example we're binding to a
ClusterRole(which is not tied to any namespace), the use of such cluster role is (see
metadata.namespace), making the effective permissions restricted to the namespace applied in the
--- # Role binding for the first team (`team-b`), allowing `web` # to consume secrets from it. # apiVersion: rbac.authorization.k8s.io/v1beta1 kind: RoleBinding metadata: name: web-team-a namespace: myprefix-team-a labels: app: web roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: read-secrets subjects: - kind: ServiceAccount name: web namespace: concourse --- # Role binding for the second team (`team-b`), allowing `web` # to consume secrets from it. # apiVersion: rbac.authorization.k8s.io/v1beta1 kind: RoleBinding metadata: name: web-team-b namespace: myprefix-team-b labels: app: web roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: read-secrets subjects: - kind: ServiceAccount name: web namespace: concourse
To finish the example, we need to associate the
web Pod with the service, granting the pod access to those namespaces through the roles that have been bound to it.
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: web labels: app: web spec: replicas: 1 template: metadata: labels: app: web spec: serviceAccountName: web containers: - name: web image: "concourse/concourse:5.3.0" args: [ web ] env: - name: CONCOURSE_KUBERNETES_NAMESPACE_PREFIX value: "myprefix-" # ...